Skip to content

Commit

Permalink
Short status (#419)
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Cross <martinidc1992@gmail.com>
  • Loading branch information
martini1992 and Martin Cross committed Jul 21, 2023
1 parent 07f26c1 commit dffe697
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 52 deletions.
4 changes: 4 additions & 0 deletions docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ gui:
expandFocusedSidePanel: false
# Determines which screen mode will be used on startup
screenMode: "normal" # one of 'normal' | 'half' | 'fullscreen'
# Determines the style of the container status and container health display in the
# containers panel. "long": full words (default), "short": one or two characters,
# "icon": unicode emoji.
containerStatusHealthStyle: "long"
logs:
timestamps: false
since: '60m' # set to '' to show all logs
Expand Down
22 changes: 14 additions & 8 deletions pkg/config/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ type GuiConfig struct {

// ScreenMode allow user to specify which screen mode will be used on startup
ScreenMode string `yaml:"screenMode,omitempty"`

// Determines the style of the container status and container health display in the
// containers panel. "long": full words (default), "short": one or two characters,
// "icon": unicode emoji.
ContainerStatusHealthStyle string `yaml:"containerStatusHealthStyle"`
}

// CommandTemplatesConfig determines what commands actually get called when we
Expand Down Expand Up @@ -358,14 +363,15 @@ func GetDefaultConfig() UserConfig {
InactiveBorderColor: []string{"default"},
OptionsTextColor: []string{"blue"},
},
ShowAllContainers: false,
ReturnImmediately: false,
WrapMainPanel: true,
LegacySortContainers: false,
SidePanelWidth: 0.3333,
ShowBottomLine: true,
ExpandFocusedSidePanel: false,
ScreenMode: "normal",
ShowAllContainers: false,
ReturnImmediately: false,
WrapMainPanel: true,
LegacySortContainers: false,
SidePanelWidth: 0.3333,
ShowBottomLine: true,
ExpandFocusedSidePanel: false,
ScreenMode: "normal",
ContainerStatusHealthStyle: "long",
},
ConfirmOnQuit: false,
Logs: LogsConfig{
Expand Down
4 changes: 3 additions & 1 deletion pkg/gui/containers_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]

return true
},
GetTableCells: presentation.GetContainerDisplayStrings,
GetTableCells: func(container *commands.Container) []string {
return presentation.GetContainerDisplayStrings(&gui.Config.UserConfig.Gui, container)
},
}
}

Expand Down
77 changes: 67 additions & 10 deletions pkg/gui/presentation/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
dockerTypes "github.com/docker/docker/api/types"
"github.com/fatih/color"
"github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/samber/lo"
)

func GetContainerDisplayStrings(container *commands.Container) []string {
func GetContainerDisplayStrings(guiConfig *config.GuiConfig, container *commands.Container) []string {
return []string{
getContainerDisplayStatus(container),
getContainerDisplaySubstatus(container),
getContainerDisplayStatus(guiConfig, container),
getContainerDisplaySubstatus(guiConfig, container),
container.Name,
getDisplayCPUPerc(container),
utils.ColoredString(displayPorts(container), color.FgYellow),
Expand Down Expand Up @@ -52,12 +53,44 @@ func displayPorts(c *commands.Container) string {
}

// getContainerDisplayStatus returns the colored status of the container
func getContainerDisplayStatus(c *commands.Container) string {
return utils.ColoredString(c.Container.State, getContainerColor(c))
func getContainerDisplayStatus(guiConfig *config.GuiConfig, c *commands.Container) string {
shortStatusMap := map[string]string{
"paused": "P",
"exited": "X",
"created": "C",
"removing": "RM",
"restarting": "RS",
"running": "R",
"dead": "D",
}

iconStatusMap := map[string]rune{
"paused": '◫',
"exited": '⨯',
"created": '+',
"removing": '−',
"restarting": '⟳',
"running": '▶',
"dead": '!',
}

var containerState string
switch guiConfig.ContainerStatusHealthStyle {
case "short":
containerState = shortStatusMap[c.Container.State]
case "icon":
containerState = string(iconStatusMap[c.Container.State])
case "long":
fallthrough
default:
containerState = c.Container.State
}

return utils.ColoredString(containerState, getContainerColor(c))
}

// GetDisplayStatus returns the exit code if the container has exited, and the health status if the container is running (and has a health check)
func getContainerDisplaySubstatus(c *commands.Container) string {
func getContainerDisplaySubstatus(guiConfig *config.GuiConfig, c *commands.Container) string {
if !c.DetailsLoaded() {
return ""
}
Expand All @@ -68,13 +101,13 @@ func getContainerDisplaySubstatus(c *commands.Container) string {
fmt.Sprintf("(%s)", strconv.Itoa(c.Details.State.ExitCode)), getContainerColor(c),
)
case "running":
return getHealthStatus(c)
return getHealthStatus(guiConfig, c)
default:
return ""
}
}

func getHealthStatus(c *commands.Container) string {
func getHealthStatus(guiConfig *config.GuiConfig, c *commands.Container) string {
if !c.DetailsLoaded() {
return ""
}
Expand All @@ -88,8 +121,32 @@ func getHealthStatus(c *commands.Container) string {
if c.Details.State.Health == nil {
return ""
}
healthStatus := c.Details.State.Health.Status
if healthStatusColor, ok := healthStatusColorMap[healthStatus]; ok {

shortHealthStatusMap := map[string]string{
"healthy": "H",
"unhealthy": "U",
"starting": "S",
}

iconHealthStatusMap := map[string]rune{
"healthy": '✔',
"unhealthy": '?',
"starting": '…',
}

var healthStatus string
switch guiConfig.ContainerStatusHealthStyle {
case "short":
healthStatus = shortHealthStatusMap[c.Details.State.Health.Status]
case "icon":
healthStatus = string(iconHealthStatusMap[c.Details.State.Health.Status])
case "long":
fallthrough
default:
healthStatus = c.Details.State.Health.Status
}

if healthStatusColor, ok := healthStatusColorMap[c.Details.State.Health.Status]; ok {
return utils.ColoredString(fmt.Sprintf("(%s)", healthStatus), healthStatusColor)
}
return ""
Expand Down
21 changes: 17 additions & 4 deletions pkg/gui/presentation/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@ package presentation
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/utils"
)

func GetServiceDisplayStrings(service *commands.Service) []string {
func GetServiceDisplayStrings(guiConfig *config.GuiConfig, service *commands.Service) []string {
if service.Container == nil {
var containerState string
switch guiConfig.ContainerStatusHealthStyle {
case "short":
containerState = "n"
case "icon":
containerState = "."
case "long":
fallthrough
default:
containerState = "none"
}

return []string{
utils.ColoredString("none", color.FgBlue),
utils.ColoredString(containerState, color.FgBlue),
"",
service.Name,
"",
Expand All @@ -20,8 +33,8 @@ func GetServiceDisplayStrings(service *commands.Service) []string {

container := service.Container
return []string{
getContainerDisplayStatus(container),
getContainerDisplaySubstatus(container),
getContainerDisplayStatus(guiConfig, container),
getContainerDisplaySubstatus(guiConfig, container),
service.Name,
getDisplayCPUPerc(container),
utils.ColoredString(displayPorts(container), color.FgYellow),
Expand Down
4 changes: 3 additions & 1 deletion pkg/gui/services_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {

return a.Name < b.Name
},
GetTableCells: presentation.GetServiceDisplayStrings,
GetTableCells: func(service *commands.Service) []string {
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
},
Hide: func() bool {
return !gui.DockerCommand.InDockerComposeProject
},
Expand Down
55 changes: 27 additions & 28 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/mattn/go-runewidth"

// "github.com/jesseduffield/yaml"

Expand Down Expand Up @@ -41,10 +42,10 @@ func SplitLines(multilineString string) []string {
// WithPadding pads a string as much as you want
func WithPadding(str string, padding int) string {
uncoloredStr := Decolorise(str)
if padding < len(uncoloredStr) {
if padding < runewidth.StringWidth(uncoloredStr) {
return str
}
return str + strings.Repeat(" ", padding-len(uncoloredStr))
return str + strings.Repeat(" ", padding-runewidth.StringWidth(uncoloredStr))
}

// ColoredString takes a string and a colour attribute and returns a colored
Expand Down Expand Up @@ -143,54 +144,52 @@ func Max(x, y int) int {
}

// RenderTable takes an array of string arrays and returns a table containing the values
func RenderTable(stringArrays [][]string) (string, error) {
if len(stringArrays) == 0 {
func RenderTable(rows [][]string) (string, error) {
if len(rows) == 0 {
return "", nil
}
if !displayArraysAligned(stringArrays) {
if !displayArraysAligned(rows) {
return "", errors.New("Each item must return the same number of strings to display")
}

padWidths := getPadWidths(stringArrays)
paddedDisplayStrings := getPaddedDisplayStrings(stringArrays, padWidths)
columnPadWidths := getPadWidths(rows)
paddedDisplayRows := getPaddedDisplayStrings(rows, columnPadWidths)

return strings.Join(paddedDisplayStrings, "\n"), nil
return strings.Join(paddedDisplayRows, "\n"), nil
}

// Decolorise strips a string of color
func Decolorise(str string) string {
re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`)
re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mK]`)
return re.ReplaceAllString(str, "")
}

func getPadWidths(stringArrays [][]string) []int {
if len(stringArrays[0]) <= 1 {
func getPadWidths(rows [][]string) []int {
if len(rows[0]) <= 1 {
return []int{}
}
padWidths := make([]int, len(stringArrays[0])-1)
for i := range padWidths {
for _, strings := range stringArrays {
uncoloredString := Decolorise(strings[i])
if len(uncoloredString) > padWidths[i] {
padWidths[i] = len(uncoloredString)
columnPadWidths := make([]int, len(rows[0])-1)
for i := range columnPadWidths {
for _, cells := range rows {
uncoloredCell := Decolorise(cells[i])

if runewidth.StringWidth(uncoloredCell) > columnPadWidths[i] {
columnPadWidths[i] = runewidth.StringWidth(uncoloredCell)
}
}
}
return padWidths
return columnPadWidths
}

func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string {
paddedDisplayStrings := make([]string, len(stringArrays))
for i, stringArray := range stringArrays {
if len(stringArray) == 0 {
continue
}
for j, padWidth := range padWidths {
paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " "
func getPaddedDisplayStrings(rows [][]string, columnPadWidths []int) []string {
paddedDisplayRows := make([]string, len(rows))
for i, cells := range rows {
for j, columnPadWidth := range columnPadWidths {
paddedDisplayRows[i] += WithPadding(cells[j], columnPadWidth) + " "
}
paddedDisplayStrings[i] += stringArray[len(padWidths)]
paddedDisplayRows[i] += cells[len(columnPadWidths)]
}
return paddedDisplayStrings
return paddedDisplayRows
}

// displayArraysAligned returns true if every string array returned from our
Expand Down

0 comments on commit dffe697

Please sign in to comment.