mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
configurable graphs
This commit is contained in:
parent
5bf1a40d2f
commit
28b488b740
7 changed files with 282 additions and 122 deletions
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/docker/docker/client"
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/xerrors"
|
||||
|
|
@ -28,10 +29,21 @@ type Container struct {
|
|||
DisplayString string
|
||||
Client *client.Client
|
||||
OSCommand *OSCommand
|
||||
Config *config.AppConfig
|
||||
Log *logrus.Entry
|
||||
Stats ContainerCliStat
|
||||
StatHistory []ContainerCliStat
|
||||
CLIStats ContainerCliStat // for realtime we use the CLI, for long-term we use the client
|
||||
StatHistory []RecordedStats
|
||||
Details Details
|
||||
MonitoringStats bool
|
||||
}
|
||||
type RecordedStats struct {
|
||||
ClientStats ContainerStats
|
||||
DerivedStats DerivedStats
|
||||
}
|
||||
|
||||
type DerivedStats struct {
|
||||
CPUPercentage float64
|
||||
MemoryPercentage float64
|
||||
}
|
||||
|
||||
type Details struct {
|
||||
|
|
@ -246,7 +258,7 @@ func (c *Container) GetDisplayStrings(isFocused bool) []string {
|
|||
|
||||
// GetDisplayCPUPerc colors the cpu percentage based on how extreme it is
|
||||
func (c *Container) GetDisplayCPUPerc() string {
|
||||
stats := c.GetStats()
|
||||
stats := c.CLIStats
|
||||
|
||||
if stats.CPUPerc == "" {
|
||||
return ""
|
||||
|
|
@ -341,11 +353,3 @@ func (c *Container) Attach() (*exec.Cmd, error) {
|
|||
func (c *Container) Top() (types.ContainerProcessList, error) {
|
||||
return c.Client.ContainerTop(context.Background(), c.ID, []string{})
|
||||
}
|
||||
|
||||
// GetStats gets the container's most recent stats
|
||||
func (c *Container) GetStats() ContainerCliStat {
|
||||
if len(c.StatHistory) == 0 {
|
||||
return ContainerCliStat{}
|
||||
}
|
||||
return c.StatHistory[len(c.StatHistory)-1]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/carlosms/asciigraph"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"github.com/mcuadros/go-lookup"
|
||||
)
|
||||
|
||||
// ContainerStats autogenerated at https://mholt.github.io/json-to-go/
|
||||
|
|
@ -143,6 +148,7 @@ func (s *ContainerStats) CalculateContainerCPUPercentage() float64 {
|
|||
|
||||
// CalculateContainerMemoryUsage calculates the memory usage of the container as a percent of total available memory
|
||||
func (s *ContainerStats) CalculateContainerMemoryUsage() float64 {
|
||||
|
||||
value := float64(s.MemoryStats.Usage*100) / float64(s.MemoryStats.Limit)
|
||||
if math.IsNaN(value) {
|
||||
return 0
|
||||
|
|
@ -151,44 +157,34 @@ func (s *ContainerStats) CalculateContainerMemoryUsage() float64 {
|
|||
}
|
||||
|
||||
// RenderStats returns a string containing the rendered stats of the container
|
||||
func (s *ContainerStats) RenderStats(viewWidth int, cpuUsageHistory []float64, memoryUsageHistory []float64) (string, error) {
|
||||
memoryGraph := asciigraph.Plot(
|
||||
memoryUsageHistory,
|
||||
asciigraph.Height(10),
|
||||
asciigraph.Width(viewWidth-10),
|
||||
asciigraph.Min(0),
|
||||
asciigraph.Max(100),
|
||||
asciigraph.Caption(
|
||||
fmt.Sprintf(
|
||||
"%.2f%% Memory (%s/%s)",
|
||||
memoryUsageHistory[len(memoryUsageHistory)-1],
|
||||
utils.FormatBinaryBytes(s.MemoryStats.Usage),
|
||||
utils.FormatBinaryBytes(int(s.MemoryStats.Limit)),
|
||||
),
|
||||
),
|
||||
)
|
||||
func (c *Container) RenderStats(viewWidth int) (string, error) {
|
||||
history := c.StatHistory
|
||||
if len(history) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
currentStats := history[len(history)-1]
|
||||
|
||||
cpuGraph := asciigraph.Plot(
|
||||
cpuUsageHistory,
|
||||
asciigraph.Height(10),
|
||||
asciigraph.Width(viewWidth-10),
|
||||
asciigraph.Min(0),
|
||||
asciigraph.Max(100),
|
||||
asciigraph.Caption(fmt.Sprintf("%.2f%% CPU", cpuUsageHistory[len(cpuUsageHistory)-1])),
|
||||
)
|
||||
graphSpecs := c.Config.UserConfig.Stats.Graphs
|
||||
graphs := make([]string, len(graphSpecs))
|
||||
for i, spec := range graphSpecs {
|
||||
graph, err := c.PlotGraph(spec, viewWidth-10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
graphs[i] = utils.ColoredString(graph, utils.GetColorAttribute(spec.Color))
|
||||
}
|
||||
|
||||
pidsCount := fmt.Sprintf("PIDs: %d", s.PidsStats.Current)
|
||||
dataReceived := fmt.Sprintf("Traffic received: %s", utils.FormatDecimalBytes(s.Networks.Eth0.RxBytes))
|
||||
dataSent := fmt.Sprintf("Traffic sent: %s", utils.FormatDecimalBytes(s.Networks.Eth0.TxBytes))
|
||||
pidsCount := fmt.Sprintf("PIDs: %d", currentStats.ClientStats.PidsStats.Current)
|
||||
dataReceived := fmt.Sprintf("Traffic received: %s", utils.FormatDecimalBytes(currentStats.ClientStats.Networks.Eth0.RxBytes))
|
||||
dataSent := fmt.Sprintf("Traffic sent: %s", utils.FormatDecimalBytes(currentStats.ClientStats.Networks.Eth0.TxBytes))
|
||||
|
||||
originalJSON, err := json.MarshalIndent(s, "", " ")
|
||||
originalJSON, err := json.MarshalIndent(currentStats, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
contents := fmt.Sprintf("\n\n%s\n\n%s\n\n%s\n\n%s\n%s\n\n%s",
|
||||
utils.ColoredString(cpuGraph, color.FgCyan),
|
||||
utils.ColoredString(memoryGraph, color.FgGreen),
|
||||
contents := fmt.Sprintf("\n\n%s\n\n%s\n\n%s\n%s\n\n%s",
|
||||
utils.ColoredString(strings.Join(graphs, "\n\n"), color.FgGreen),
|
||||
pidsCount,
|
||||
dataReceived,
|
||||
dataSent,
|
||||
|
|
@ -197,3 +193,68 @@ func (s *ContainerStats) RenderStats(viewWidth int, cpuUsageHistory []float64, m
|
|||
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
// PlotGraph returns the plotted graph based on the graph spec and the stat history
|
||||
func (c *Container) PlotGraph(spec config.GraphConfig, width int) (string, error) {
|
||||
data := make([]float64, len(c.StatHistory))
|
||||
for i, stats := range c.StatHistory {
|
||||
value, err := lookup.LookupString(stats, spec.StatPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
floatValue, err := getFloat(value.Interface())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
data[i] = floatValue
|
||||
}
|
||||
|
||||
return asciigraph.Plot(
|
||||
data,
|
||||
asciigraph.Height(spec.Height),
|
||||
asciigraph.Width(width),
|
||||
asciigraph.Min(spec.Min),
|
||||
asciigraph.Max(spec.Max),
|
||||
asciigraph.Caption(spec.Caption),
|
||||
), nil
|
||||
}
|
||||
|
||||
// from Dave C's answer at https://stackoverflow.com/questions/20767724/converting-unknown-interface-to-float64-in-golang
|
||||
func getFloat(unk interface{}) (float64, error) {
|
||||
floatType := reflect.TypeOf(float64(0))
|
||||
stringType := reflect.TypeOf("")
|
||||
|
||||
switch i := unk.(type) {
|
||||
case float64:
|
||||
return i, nil
|
||||
case float32:
|
||||
return float64(i), nil
|
||||
case int64:
|
||||
return float64(i), nil
|
||||
case int32:
|
||||
return float64(i), nil
|
||||
case int:
|
||||
return float64(i), nil
|
||||
case uint64:
|
||||
return float64(i), nil
|
||||
case uint32:
|
||||
return float64(i), nil
|
||||
case uint:
|
||||
return float64(i), nil
|
||||
case string:
|
||||
return strconv.ParseFloat(i, 64)
|
||||
default:
|
||||
v := reflect.ValueOf(unk)
|
||||
v = reflect.Indirect(v)
|
||||
if v.Type().ConvertibleTo(floatType) {
|
||||
fv := v.Convert(floatType)
|
||||
return fv.Float(), nil
|
||||
} else if v.Type().ConvertibleTo(stringType) {
|
||||
sv := v.Convert(stringType)
|
||||
s := sv.String()
|
||||
return strconv.ParseFloat(s, 64)
|
||||
} else {
|
||||
return math.NaN(), fmt.Errorf("Can't convert %v to float64", v.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/acarl005/stripansi"
|
||||
"github.com/docker/docker/api/types"
|
||||
|
|
@ -53,9 +54,13 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localize
|
|||
}, nil
|
||||
}
|
||||
|
||||
// MonitorContainerStats monitors a stream of container stats and updates the containers as each new stats object is received
|
||||
func (c *DockerCommand) MonitorContainerStats() {
|
||||
go c.MonitorCLIContainerStats()
|
||||
go c.MonitorClientContainerStats()
|
||||
}
|
||||
|
||||
// MonitorCLIContainerStats monitors a stream of container stats and updates the containers as each new stats object is received
|
||||
func (c *DockerCommand) MonitorCLIContainerStats() {
|
||||
command := `docker stats --all --no-trunc --format '{{json .}}'`
|
||||
cmd := c.OSCommand.RunCustomCommand(command)
|
||||
|
||||
|
|
@ -80,7 +85,7 @@ func (c *DockerCommand) MonitorContainerStats() {
|
|||
c.ContainerMutex.Lock()
|
||||
for _, container := range c.Containers {
|
||||
if container.ID == stats.ID {
|
||||
container.StatHistory = append(container.StatHistory, stats)
|
||||
container.CLIStats = stats
|
||||
}
|
||||
}
|
||||
c.ContainerMutex.Unlock()
|
||||
|
|
@ -91,6 +96,52 @@ func (c *DockerCommand) MonitorContainerStats() {
|
|||
return
|
||||
}
|
||||
|
||||
func (c *DockerCommand) MonitorClientContainerStats() {
|
||||
// periodically loop through running containers and see if we need to create a monitor goroutine for any
|
||||
// every second we check if we need to spawn a new goroutine
|
||||
for range time.Tick(time.Second) {
|
||||
for _, container := range c.Containers {
|
||||
if !container.MonitoringStats {
|
||||
go c.createClientStatMonitor(container)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DockerCommand) createClientStatMonitor(container *Container) {
|
||||
container.MonitoringStats = true
|
||||
stream, err := c.Client.ContainerStats(context.Background(), container.ID, true)
|
||||
if err != nil {
|
||||
c.ErrorChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
defer stream.Body.Close()
|
||||
|
||||
scanner := bufio.NewScanner(stream.Body)
|
||||
for scanner.Scan() {
|
||||
data := scanner.Bytes()
|
||||
var stats ContainerStats
|
||||
json.Unmarshal(data, &stats)
|
||||
|
||||
recordedStats := RecordedStats{
|
||||
ClientStats: stats,
|
||||
DerivedStats: DerivedStats{
|
||||
CPUPercentage: stats.CalculateContainerCPUPercentage(),
|
||||
MemoryPercentage: stats.CalculateContainerMemoryUsage(),
|
||||
},
|
||||
}
|
||||
|
||||
c.ContainerMutex.Lock()
|
||||
// TODO: for now we never truncate the recorded stats, and we should
|
||||
container.StatHistory = append(container.StatHistory, recordedStats)
|
||||
c.ContainerMutex.Unlock()
|
||||
}
|
||||
|
||||
container.MonitoringStats = false
|
||||
return
|
||||
}
|
||||
|
||||
// GetContainersAndServices returns a slice of docker containers
|
||||
func (c *DockerCommand) GetContainersAndServices() error {
|
||||
c.ServiceMutex.Lock()
|
||||
|
|
@ -147,7 +198,7 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
|||
c.ContainerMutex.Lock()
|
||||
defer c.ContainerMutex.Unlock()
|
||||
|
||||
currentContainers := c.Containers
|
||||
existingContainers := c.Containers
|
||||
|
||||
containers, err := c.Client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
if err != nil {
|
||||
|
|
@ -157,26 +208,35 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
|||
ownContainers := make([]*Container, len(containers))
|
||||
|
||||
for i, container := range containers {
|
||||
ownContainers[i] = &Container{
|
||||
ID: container.ID,
|
||||
Name: strings.TrimLeft(container.Names[0], "/"),
|
||||
ServiceName: container.Labels["com.docker.compose.service"],
|
||||
ServiceID: container.Labels["com.docker.compose.config-hash"],
|
||||
ProjectName: container.Labels["com.docker.compose.project"],
|
||||
ContainerNumber: container.Labels["com.docker.compose.container"],
|
||||
Container: container,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
}
|
||||
var newContainer *Container
|
||||
|
||||
// bring across old stats information
|
||||
for _, currentContainer := range currentContainers {
|
||||
if currentContainer.ID == container.ID {
|
||||
ownContainers[i].StatHistory = currentContainer.StatHistory // might need to use pointers here in case we're deep copying everything
|
||||
ownContainers[i].Details = currentContainer.Details
|
||||
// check if we already data stored against the container
|
||||
for _, existingContainer := range existingContainers {
|
||||
if existingContainer.ID == container.ID {
|
||||
newContainer = existingContainer
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// initialise the container if it's completely new
|
||||
if newContainer == nil {
|
||||
newContainer = &Container{
|
||||
ID: container.ID,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
Config: c.Config,
|
||||
}
|
||||
}
|
||||
|
||||
newContainer.Container = container
|
||||
newContainer.Name = strings.TrimLeft(container.Names[0], "/")
|
||||
newContainer.ServiceName = container.Labels["com.docker.compose.service"]
|
||||
newContainer.ServiceID = container.Labels["com.docker.compose.config-hash"]
|
||||
newContainer.ProjectName = container.Labels["com.docker.compose.project"]
|
||||
newContainer.ContainerNumber = container.Labels["com.docker.compose.container"]
|
||||
|
||||
ownContainers[i] = newContainer
|
||||
}
|
||||
|
||||
return ownContainers, nil
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ type UserConfig struct {
|
|||
CommandTemplates CommandTemplatesConfig `yaml:"commandTemplates,omitempty"`
|
||||
OS OSConfig `yaml:"oS,omitempty"`
|
||||
Update UpdateConfig `yaml:"update,omitempty"`
|
||||
Stats StatsConfig `yaml:"stats,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeConfig struct {
|
||||
|
|
@ -114,6 +115,20 @@ type UpdateConfig struct {
|
|||
Method string `yaml:"method,omitempty"`
|
||||
}
|
||||
|
||||
// GraphConfig specifies how to make a graph of recorded container stats
|
||||
type GraphConfig struct {
|
||||
Min float64 `yaml:"min,omitempty"`
|
||||
Max float64 `yaml:"max,omitempty"`
|
||||
Height int `yaml:"height,omitempty"`
|
||||
Caption string `yaml:"caption,omitempty"`
|
||||
StatPath string `yaml:"statPath,omitempty"`
|
||||
Color string `yaml:"color,omitempty"`
|
||||
}
|
||||
|
||||
type StatsConfig struct {
|
||||
Graphs []GraphConfig
|
||||
}
|
||||
|
||||
// GetDefaultConfig returns the application default configuration
|
||||
func GetDefaultConfig() UserConfig {
|
||||
return UserConfig{
|
||||
|
|
@ -139,6 +154,26 @@ func GetDefaultConfig() UserConfig {
|
|||
Update: UpdateConfig{
|
||||
Method: "never",
|
||||
},
|
||||
Stats: StatsConfig{
|
||||
Graphs: []GraphConfig{
|
||||
{
|
||||
Min: 0,
|
||||
Max: 100,
|
||||
Height: 10,
|
||||
Caption: "CPU (%)",
|
||||
StatPath: "DerivedStats.CPUPercentage",
|
||||
Color: "blue",
|
||||
},
|
||||
{
|
||||
Min: 0,
|
||||
Max: 100,
|
||||
Height: 10,
|
||||
Caption: "Memory (%)",
|
||||
StatPath: "DerivedStats.MemoryPercentage",
|
||||
Color: "green",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package gui
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/fatih/color"
|
||||
|
|
@ -123,46 +123,22 @@ func (gui *Gui) renderContainerStats(mainView *gocui.View, container *commands.C
|
|||
mainView.Autoscroll = false
|
||||
mainView.Title = "Stats"
|
||||
|
||||
stream, err := gui.DockerCommand.Client.ContainerStats(context.Background(), container.ID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.T.NewTask(func(stop chan struct{}) {
|
||||
defer stream.Body.Close()
|
||||
|
||||
cpuUsageHistory := []float64{}
|
||||
memoryUsageHistory := []float64{}
|
||||
scanner := bufio.NewScanner(stream.Body)
|
||||
for scanner.Scan() {
|
||||
data := scanner.Bytes()
|
||||
var stats commands.ContainerStats
|
||||
json.Unmarshal(data, &stats)
|
||||
|
||||
cpuUsageHistory = append(cpuUsageHistory, stats.CalculateContainerCPUPercentage())
|
||||
if len(cpuUsageHistory) >= 20 {
|
||||
cpuUsageHistory = cpuUsageHistory[1:]
|
||||
}
|
||||
|
||||
memoryUsageHistory = append(memoryUsageHistory, stats.CalculateContainerMemoryUsage())
|
||||
if len(memoryUsageHistory) >= 20 {
|
||||
memoryUsageHistory = memoryUsageHistory[1:]
|
||||
}
|
||||
|
||||
width, _ := mainView.Size()
|
||||
|
||||
contents, err := stats.RenderStats(width, cpuUsageHistory, memoryUsageHistory)
|
||||
if err != nil {
|
||||
gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
tickChan := time.NewTicker(time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
default:
|
||||
}
|
||||
case <-tickChan.C:
|
||||
width, _ := mainView.Size()
|
||||
|
||||
gui.reRenderString(gui.g, "main", contents)
|
||||
contents, err := container.RenderStats(width)
|
||||
if err != nil {
|
||||
gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
gui.reRenderString(gui.g, "main", contents)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,36 +2,14 @@ package gui
|
|||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
)
|
||||
|
||||
// GetAttribute gets the gocui color attribute from the string
|
||||
func (gui *Gui) GetAttribute(key string) gocui.Attribute {
|
||||
colorMap := map[string]gocui.Attribute{
|
||||
"default": gocui.ColorDefault,
|
||||
"black": gocui.ColorBlack,
|
||||
"red": gocui.ColorRed,
|
||||
"green": gocui.ColorGreen,
|
||||
"yellow": gocui.ColorYellow,
|
||||
"blue": gocui.ColorBlue,
|
||||
"magenta": gocui.ColorMagenta,
|
||||
"cyan": gocui.ColorCyan,
|
||||
"white": gocui.ColorWhite,
|
||||
"bold": gocui.AttrBold,
|
||||
"reverse": gocui.AttrReverse,
|
||||
"underline": gocui.AttrUnderline,
|
||||
}
|
||||
value, present := colorMap[key]
|
||||
if present {
|
||||
return value
|
||||
}
|
||||
return gocui.ColorWhite
|
||||
}
|
||||
|
||||
// GetColor bitwise OR's a list of attributes obtained via the given keys
|
||||
func (gui *Gui) GetColor(keys []string) gocui.Attribute {
|
||||
var attribute gocui.Attribute
|
||||
for _, key := range keys {
|
||||
attribute = attribute | gui.GetAttribute(key)
|
||||
attribute = attribute | utils.GetGocuiAttribute(key)
|
||||
}
|
||||
return attribute
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
|
@ -296,3 +297,48 @@ func ApplyTemplate(str string, object interface{}) string {
|
|||
template.Must(template.New("").Parse(str)).Execute(&buf, object)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// GetGocuiAttribute gets the gocui color attribute from the string
|
||||
func GetGocuiAttribute(key string) gocui.Attribute {
|
||||
colorMap := map[string]gocui.Attribute{
|
||||
"default": gocui.ColorDefault,
|
||||
"black": gocui.ColorBlack,
|
||||
"red": gocui.ColorRed,
|
||||
"green": gocui.ColorGreen,
|
||||
"yellow": gocui.ColorYellow,
|
||||
"blue": gocui.ColorBlue,
|
||||
"magenta": gocui.ColorMagenta,
|
||||
"cyan": gocui.ColorCyan,
|
||||
"white": gocui.ColorWhite,
|
||||
"bold": gocui.AttrBold,
|
||||
"reverse": gocui.AttrReverse,
|
||||
"underline": gocui.AttrUnderline,
|
||||
}
|
||||
value, present := colorMap[key]
|
||||
if present {
|
||||
return value
|
||||
}
|
||||
return gocui.ColorWhite
|
||||
}
|
||||
|
||||
// GetColorAttribute gets the color attribute from the string
|
||||
func GetColorAttribute(key string) color.Attribute {
|
||||
colorMap := map[string]color.Attribute{
|
||||
"default": color.FgWhite,
|
||||
"black": color.FgBlack,
|
||||
"red": color.FgRed,
|
||||
"green": color.FgGreen,
|
||||
"yellow": color.FgYellow,
|
||||
"blue": color.FgBlue,
|
||||
"magenta": color.FgMagenta,
|
||||
"cyan": color.FgCyan,
|
||||
"white": color.FgWhite,
|
||||
"bold": color.Bold,
|
||||
"underline": color.Underline,
|
||||
}
|
||||
value, present := colorMap[key]
|
||||
if present {
|
||||
return value
|
||||
}
|
||||
return color.FgWhite
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue