Manually specify the closing SGR for a span

This commit is contained in:
Kovid Goyal 2023-03-23 15:48:29 +05:30
parent 6590be84a2
commit de9edb6ff5
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 55 additions and 64 deletions

View File

@ -329,7 +329,7 @@ func SGRFromCSI(csi string) (ans SGR) {
type Span struct { type Span struct {
Offset, Size int // in bytes Offset, Size int // in bytes
SGR SGR opening_sgr, closing_sgr SGR
} }
func NewSpan(offset, size int) *Span { func NewSpan(offset, size int) *Span {
@ -354,102 +354,93 @@ func (self *ColorVal) Set(val any) {
} }
func (self *Span) SetForeground(val any) *Span { func (self *Span) SetForeground(val any) *Span {
self.SGR.Foreground.Set(val) self.opening_sgr.Foreground.Set(val)
return self return self
} }
func (self *Span) SetBackground(val any) *Span { func (self *Span) SetBackground(val any) *Span {
self.SGR.Background.Set(val) self.opening_sgr.Background.Set(val)
return self return self
} }
func (self *Span) SetUnderlineColor(val any) *Span { func (self *Span) SetUnderlineColor(val any) *Span {
self.SGR.Underline_color.Set(val) self.opening_sgr.Underline_color.Set(val)
return self return self
} }
func (self *Span) SetItalic(val bool) *Span { func (self *Span) SetItalic(val bool) *Span {
self.SGR.Italic.Set(val) self.opening_sgr.Italic.Set(val)
return self return self
} }
func (self *Span) SetBold(val bool) *Span { func (self *Span) SetBold(val bool) *Span {
self.SGR.Bold.Set(val) self.opening_sgr.Bold.Set(val)
return self return self
} }
func (self *Span) SetReverse(val bool) *Span { func (self *Span) SetReverse(val bool) *Span {
self.SGR.Reverse.Set(val) self.opening_sgr.Reverse.Set(val)
return self return self
} }
func (self *Span) SetDim(val bool) *Span { func (self *Span) SetDim(val bool) *Span {
self.SGR.Dim.Set(val) self.opening_sgr.Dim.Set(val)
return self return self
} }
func (self *Span) SetStrikethrough(val bool) *Span { func (self *Span) SetStrikethrough(val bool) *Span {
self.SGR.Strikethrough.Set(val) self.opening_sgr.Strikethrough.Set(val)
return self return self
} }
func (self *Span) SetUnderlineStyle(val UnderlineStyle) *Span { func (self *Span) SetUnderlineStyle(val UnderlineStyle) *Span {
self.SGR.Underline_style.Is_set = true self.opening_sgr.Underline_style.Is_set = true
self.SGR.Underline_style.Val = val self.opening_sgr.Underline_style.Val = val
return self return self
} }
type defaulting_val interface { func (self *Span) SetClosingForeground(val any) *Span {
DefaultCSI() string self.closing_sgr.Foreground.Set(val)
return self
} }
func append_default_csi(x defaulting_val, ans []byte) []byte { func (self *Span) SetClosingBackground(val any) *Span {
val := x.DefaultCSI() self.closing_sgr.Background.Set(val)
if val != "" { return self
ans = append(ans, val...)
ans = append(ans, ';')
}
return ans
} }
func (self *Span) ClosingCSI() string { func (self *Span) SetClosingUnderlineColor(val any) *Span {
ans := make([]byte, 0, 16) self.closing_sgr.Underline_color.Set(val)
w := func(x string) { return self
if x != "" { }
ans = append(append(ans, x...), ';')
} func (self *Span) SetClosingItalic(val bool) *Span {
} self.closing_sgr.Italic.Set(val)
if self.SGR.Bold.Is_set { return self
w(self.SGR.Bold.AsCSI("1", "221")) }
}
if self.SGR.Dim.Is_set { func (self *Span) SetClosingBold(val bool) *Span {
w(self.SGR.Dim.AsCSI("2", "222")) self.closing_sgr.Bold.Set(val)
} return self
if self.SGR.Italic.Is_set { }
w(self.SGR.Italic.AsCSI("3", "23")) func (self *Span) SetClosingReverse(val bool) *Span {
} self.closing_sgr.Reverse.Set(val)
if self.SGR.Reverse.Is_set { return self
w(self.SGR.Reverse.AsCSI("7", "27")) }
} func (self *Span) SetClosingDim(val bool) *Span {
if self.SGR.Strikethrough.Is_set { self.closing_sgr.Dim.Set(val)
w(self.SGR.Strikethrough.AsCSI("9", "29")) return self
} }
wc := func(cval ColorVal, base int) { func (self *Span) SetClosingStrikethrough(val bool) *Span {
if cval.Is_set { self.closing_sgr.Strikethrough.Set(val)
cval.Is_default = true return self
w(cval.AsCSI(base)) }
} func (self *Span) SetClosingUnderlineStyle(val UnderlineStyle) *Span {
} self.closing_sgr.Underline_style.Is_set = true
wc(self.SGR.Foreground, 30) self.opening_sgr.Underline_style.Val = val
wc(self.SGR.Background, 40) return self
wc(self.SGR.Underline_color, 50)
if len(ans) > 0 {
ans = ans[:len(ans)-1]
ans = append(ans, 'm')
}
return utils.UnsafeBytesToString(ans)
} }
// Insert formatting into text at the specified offsets, overriding any existing formatting, and restoring // Insert formatting into text at the specified offsets, overriding any existing formatting, and restoring
// existing formatting after the replaced sections. // existing formatting after the replaced sections.
func InsertFormatting(text string, spans ...*Span) string { func InsertFormatting(text string, spans ...*Span) string {
spans = utils.Filter(spans, func(s *Span) bool { return !s.SGR.IsEmpty() }) spans = utils.Filter(spans, func(s *Span) bool { return !s.opening_sgr.IsEmpty() })
if len(spans) == 0 { if len(spans) == 0 {
return text return text
} }
@ -470,7 +461,7 @@ func InsertFormatting(text string, spans ...*Span) string {
in_span = spans[0] in_span = spans[0]
spans = spans[1:] spans = spans[1:]
if in_span.Size > 0 { if in_span.Size > 0 {
write_csi(in_span.SGR.AsCSI()) write_csi(in_span.opening_sgr.AsCSI())
} else { } else {
in_span = nil in_span = nil
} }
@ -478,7 +469,7 @@ func InsertFormatting(text string, spans ...*Span) string {
} }
close_span := func() { close_span := func() {
write_csi(in_span.ClosingCSI()) write_csi(in_span.closing_sgr.AsCSI())
write_csi(overall_sgr_state.AsCSI()) write_csi(overall_sgr_state.AsCSI())
in_span = nil in_span = nil
} }
@ -515,7 +506,7 @@ func InsertFormatting(text string, spans ...*Span) string {
if in_span == nil { if in_span == nil {
write_csi(csi) write_csi(csi)
} else { } else {
sgr.ApplyMask(in_span.SGR) sgr.ApplyMask(in_span.opening_sgr)
csi := sgr.AsCSI() csi := sgr.AsCSI()
write_csi(csi) write_csi(csi)
} }

View File

@ -21,16 +21,16 @@ func TestInsertFormatting(t *testing.T) {
test( test(
"\x1b[44m abcd \x1b[49m", "\x1b[44m abcd \x1b[49m",
"\x1b[44m a\x1b[33;41mbc\x1b[39;49m\x1b[44md \x1b[49m", "\x1b[44m a\x1b[33;41mbc\x1b[39;49m\x1b[44md \x1b[49m",
NewSpan(2, 2).SetForeground(3).SetBackground(1), NewSpan(2, 2).SetForeground(3).SetBackground(1).SetClosingForeground(nil).SetClosingBackground(nil),
) )
test( test(
"abcd", "abcd",
"a\x1b[92mbcd\x1b[39m", "a\x1b[92mbcd\x1b[39m",
NewSpan(1, 11).SetForeground(10), NewSpan(1, 11).SetForeground(10).SetClosingForeground(nil),
) )
test( test(
"AB\x1b[1mC\x1b[221mDE", "AB\x1b[1mC\x1b[221mDE",
"A\x1b[37mB\x1b[1mC\x1b[221mDE\x1b[39m\x1b[221m", "A\x1b[37mB\x1b[1mC\x1b[221mDE\x1b[39m\x1b[221m",
NewSpan(1, 11).SetForeground(7), NewSpan(1, 11).SetForeground(7).SetClosingForeground(nil),
) )
} }