src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/ui/style.go (about)

     1  package ui
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  // NoColor can be set to true to suppress foreground and background colors when
     9  // writing text to the terminal.
    10  var NoColor bool = false
    11  
    12  // Style specifies how something (mostly a string) shall be displayed.
    13  type Style struct {
    14  	Fg         Color
    15  	Bg         Color
    16  	Bold       bool
    17  	Dim        bool
    18  	Italic     bool
    19  	Underlined bool
    20  	Blink      bool
    21  	Inverse    bool
    22  }
    23  
    24  // SGRValues returns an array of the individual SGR values for the style.
    25  func (s Style) SGRValues() []string {
    26  	var sgr []string
    27  
    28  	addIf := func(b bool, code string) {
    29  		if b {
    30  			sgr = append(sgr, code)
    31  		}
    32  	}
    33  	addIf(s.Bold, "1")
    34  	addIf(s.Dim, "2")
    35  	addIf(s.Italic, "3")
    36  	addIf(s.Underlined, "4")
    37  	addIf(s.Blink, "5")
    38  	addIf(s.Inverse, "7")
    39  	if s.Fg != nil && !NoColor {
    40  		sgr = append(sgr, s.Fg.fgSGR())
    41  	}
    42  	if s.Bg != nil && !NoColor {
    43  		sgr = append(sgr, s.Bg.bgSGR())
    44  	}
    45  	return sgr
    46  }
    47  
    48  // SGR returns, for the Style, a string that can be included in an ANSI X3.64 SGR sequence.
    49  func (s Style) SGR() string {
    50  	return strings.Join(s.SGRValues(), ";")
    51  }
    52  
    53  // MergeFromOptions merges all recognized values from a map to the current
    54  // Style.
    55  func (s *Style) MergeFromOptions(options map[string]any) error {
    56  	assignColor := func(val any, colorField *Color) string {
    57  		if val == "default" {
    58  			*colorField = nil
    59  			return ""
    60  		} else if s, ok := val.(string); ok {
    61  			color := parseColor(s)
    62  			if color != nil {
    63  				*colorField = color
    64  				return ""
    65  			}
    66  		}
    67  		return "valid color string"
    68  	}
    69  	assignBool := func(val any, attrField *bool) string {
    70  		if b, ok := val.(bool); ok {
    71  			*attrField = b
    72  		} else {
    73  			return "bool value"
    74  		}
    75  		return ""
    76  	}
    77  
    78  	for k, v := range options {
    79  		var need string
    80  
    81  		switch k {
    82  		case "fg-color":
    83  			need = assignColor(v, &s.Fg)
    84  		case "bg-color":
    85  			need = assignColor(v, &s.Bg)
    86  		case "bold":
    87  			need = assignBool(v, &s.Bold)
    88  		case "dim":
    89  			need = assignBool(v, &s.Dim)
    90  		case "italic":
    91  			need = assignBool(v, &s.Italic)
    92  		case "underlined":
    93  			need = assignBool(v, &s.Underlined)
    94  		case "blink":
    95  			need = assignBool(v, &s.Blink)
    96  		case "inverse":
    97  			need = assignBool(v, &s.Inverse)
    98  
    99  		default:
   100  			return fmt.Errorf("unrecognized option '%s'", k)
   101  		}
   102  
   103  		if need != "" {
   104  			return fmt.Errorf("value for option '%s' must be a %s", k, need)
   105  		}
   106  	}
   107  
   108  	return nil
   109  }