mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-13 09:57:51 +08:00
e89fe48870
* Add compatibility test with old implementation * Add color type * Add basic style structs and utilities * Add structs for styled segments and texts * Add default style transformers to reimplement $edit:styled~ * Add builtins to manipulate styled segments and texts * Rename style 'underline' -> 'underlined' * Fix test case * Add conversion from styled text to ansi sequences * Return errors rather than throwing * Validate the type of boolean options * Delegate old to new styled function * Rebase for new test framework api and expand test cases * Remove old builtin function $edit:styled~ * Use strings to represent colors * Convert bool pointers to simple bool values * Validate color strings * Do no longer expose builtin style transformers * Fix confusion about pointers * Make outputs more stable * Expand tests * Use pointers instead of passing setter functions * Unexport and rename color check * Use the empty string for default colors * Expand tests * Simplify styled transformers Now there are three transformers for each boolean style attribute that allow setting, unsetting and toggling the corresponding attribute. * Rework and add doc comments
109 lines
2.4 KiB
Go
109 lines
2.4 KiB
Go
package styled
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/elves/elvish/eval/vals"
|
|
)
|
|
|
|
// Segment is a string that has some style applied to it.
|
|
type Segment struct {
|
|
Style
|
|
Text string
|
|
}
|
|
|
|
func (Segment) Kind() string { return "styled-segment" }
|
|
|
|
// Repr returns the representation of this Segment. The string can be used to
|
|
// construct an identical Segment. Unset or default attributes are skipped. If
|
|
// the Segment represents an unstyled string only this string is returned.
|
|
func (s Segment) Repr(indent int) string {
|
|
buf := new(bytes.Buffer)
|
|
addIfNotEqual := func(key string, val, cmp interface{}) {
|
|
if val != cmp {
|
|
fmt.Fprintf(buf, "&%s=%s ", key, vals.Repr(val, 0))
|
|
}
|
|
}
|
|
|
|
addIfNotEqual("fg-color", s.Foreground, "")
|
|
addIfNotEqual("bg-color", s.Background, "")
|
|
addIfNotEqual("bold", s.Bold, false)
|
|
addIfNotEqual("dim", s.Dim, false)
|
|
addIfNotEqual("italic", s.Italic, false)
|
|
addIfNotEqual("underlined", s.Underlined, false)
|
|
addIfNotEqual("blink", s.Blink, false)
|
|
addIfNotEqual("inverse", s.Inverse, false)
|
|
|
|
if buf.Len() == 0 {
|
|
return s.Text
|
|
}
|
|
|
|
return fmt.Sprintf("(styled-segment %s %s)", s.Text, strings.TrimSpace(buf.String()))
|
|
}
|
|
|
|
func (s Segment) IterateKeys(fn func(v interface{}) bool) {
|
|
vals.Feed(fn, "text", "fg-color", "bg-color", "bold", "dim", "italic", "underlined", "blink", "inverse")
|
|
}
|
|
|
|
// Index provides access to the attributes of the Segment.
|
|
func (s Segment) Index(k interface{}) (v interface{}, ok bool) {
|
|
switch k {
|
|
case "text":
|
|
v = s.Text
|
|
case "fg-color":
|
|
v = s.Foreground
|
|
case "bg-color":
|
|
v = s.Background
|
|
case "bold":
|
|
v = s.Bold
|
|
case "dim":
|
|
v = s.Dim
|
|
case "italic":
|
|
v = s.Italic
|
|
case "underlined":
|
|
v = s.Underlined
|
|
case "blink":
|
|
v = s.Blink
|
|
case "inverse":
|
|
v = s.Inverse
|
|
}
|
|
|
|
if v == "" {
|
|
v = "default"
|
|
}
|
|
|
|
return v, v != nil
|
|
}
|
|
|
|
// Concat implements Segment+string, Segment+Segment and Segment+Text.
|
|
func (s Segment) Concat(v interface{}) (interface{}, error) {
|
|
switch rhs := v.(type) {
|
|
case string:
|
|
return Text{
|
|
s,
|
|
Segment{Text: rhs},
|
|
}, nil
|
|
case *Segment:
|
|
return Text{s, *rhs}, nil
|
|
case *Text:
|
|
return Text(append([]Segment{s}, *rhs...)), nil
|
|
}
|
|
|
|
return nil, vals.ErrConcatNotImplemented
|
|
}
|
|
|
|
// RConcat implements string+Segment.
|
|
func (s Segment) RConcat(v interface{}) (interface{}, error) {
|
|
switch lhs := v.(type) {
|
|
case string:
|
|
return Text{
|
|
Segment{Text: lhs},
|
|
s,
|
|
}, nil
|
|
}
|
|
|
|
return nil, vals.ErrConcatNotImplemented
|
|
}
|