Skip to content

Commit 53fd88b

Browse files
committed
color: text/auto: support more table coloring
No per-cell/row/column support, but can set cell text/fore and back colors, and set table bg color to continue to end of line. Support `target:` prefices on the auto specification, to let fg: and bg: be set explicitly instead of being only ordering-dependent. Only use prefices for setting the cell colors. Support `!solid` to turn off solid. Support `fullwidth` and `!fullwidth` (and `toeol` alias). Debugged to be sure this coloring works in xterm and gnome-terminal.
1 parent 418f1bc commit 53fd88b

File tree

5 files changed

+138
-11
lines changed

5 files changed

+138
-11
lines changed

auto/auto.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,40 @@ func init() {
7272
}
7373

7474
func setColorsOrDecorationsFromSections(tt *texttable.TextTable, sections []string) {
75+
// These 3 control the implicit targetting of sections, border fg then border bg, and decorations.
76+
// Setting the cell colors (text) explicitly requires using the string prefix forms.
7577
doneFG, doneBG, doneDecoration := false, false, false
7678
for _, section := range sections {
79+
section = strings.ToLower(section)
7780
var (
7881
c color.Color
7982
err error
8083
red, green, blue uint64
8184
haveColor bool
85+
colorForce string
8286
)
8387
haveColor = false
88+
89+
if strings.HasPrefix(section, "fg:") {
90+
colorForce = "FG"
91+
section = section[3:]
92+
} else if strings.HasPrefix(section, "bg:") {
93+
colorForce = "BG"
94+
section = section[3:]
95+
} else if strings.HasPrefix(section, "text:") {
96+
colorForce = "CELLFG"
97+
section = section[5:]
98+
} else if strings.HasPrefix(section, "cell:") {
99+
colorForce = "CELLFG"
100+
section = section[5:]
101+
} else if strings.HasPrefix(section, "cellfg:") {
102+
colorForce = "CELLFG"
103+
section = section[7:]
104+
} else if strings.HasPrefix(section, "cellbg:") {
105+
colorForce = "CELLBG"
106+
section = section[7:]
107+
}
108+
84109
if c, err = color.ByHTMLNamedColor(section); err == nil {
85110
haveColor = true
86111
} else if m := reColorHex.FindStringSubmatch(section); m != nil {
@@ -97,11 +122,37 @@ func setColorsOrDecorationsFromSections(tt *texttable.TextTable, sections []stri
97122
}
98123
} else if section == "solid" {
99124
tt.SetBGSolid(true)
125+
} else if section == "!solid" {
126+
tt.SetBGSolid(false)
127+
} else if section == "fullwidth" || section == "toeol" {
128+
tt.SetColorToEOL(true)
129+
} else if section == "!fullwidth" || section == "!toeol" {
130+
tt.SetColorToEOL(false)
100131
} else if !doneDecoration {
101132
tt.SetDecorationNamed(section)
102133
doneDecoration = true
103134
}
104135
if haveColor {
136+
switch colorForce {
137+
case "FG":
138+
tt.SetFGColor(c)
139+
doneFG = true
140+
continue
141+
case "BG":
142+
tt.SetBGColor(c)
143+
doneBG = true
144+
continue
145+
case "CELLFG":
146+
tt.SetCellFGColor(c)
147+
continue
148+
case "CELLBG":
149+
tt.SetCellBGColor(c)
150+
continue
151+
case "":
152+
default:
153+
panic("BUG unexpected colorForce value: " + colorForce)
154+
}
155+
105156
if doneFG && doneBG {
106157
continue
107158
} else if doneFG {

texttable/decoration/emit.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type emitter struct {
2626
escStart string
2727
escStop string
2828
escCellStart string
29+
escClearEOL string
30+
noResetEOL bool
2931
}
3032

3133
// ForColumnWidths returns an emitter object with methods for getting
@@ -62,6 +64,14 @@ func (e *emitter) SetANSIEscapes(escStart, escStop, escCellStart string) {
6264
} else if e.escStart != "" {
6365
e.escCellStart = e.escStart
6466
}
67+
// clr_eol / el
68+
// without this, terminals are inconsistent in whether or not to stop the color at newline;
69+
// eg, in gnome-terminal, we get color to right margin on all lines except the first.
70+
e.escClearEOL = "\x1B[K"
71+
}
72+
73+
func (e *emitter) SetNoResetEOL(onoff bool) {
74+
e.noResetEOL = onoff
6575
}
6676

6777
func (e emitter) commonTemplateLine(left, horiz, cross, right string) string {
@@ -82,7 +92,14 @@ func (e emitter) commonTemplateLine(left, horiz, cross, right string) string {
8292
} else {
8393
fields = append(fields, right)
8494
}
85-
fields = append(fields, e.escStop)
95+
if e.noResetEOL {
96+
// Without e.escStop here too, and with LineBottom appending e.escStop when e.noResetEOL,
97+
// we get perfection in xterm but gnome-terminal carries the background over for one more line, which seems like a bug.
98+
// So force a stop at the end of the lines.
99+
fields = append(fields, e.escClearEOL+e.escStop)
100+
} else {
101+
fields = append(fields, e.escStop)
102+
}
86103
fields = append(fields, e.eol)
87104
return strings.Join(fields, "")
88105
}
@@ -133,6 +150,10 @@ func (e emitter) BodyDividers() DividerSet {
133150

134151
func (e emitter) commonRenderedLine(ds DividerSet, cellStrs []WidthString, colAligns []align.Alignment) string {
135152
fields := make([]string, 0, len(e.colWidths)*2+1)
153+
eolReset := e.escStop
154+
if e.noResetEOL {
155+
eolReset = e.escClearEOL + e.escStop
156+
}
136157
if ds.Left != "" {
137158
fields = append(fields, e.escStart+ds.Left+e.escCellStart)
138159
}
@@ -145,9 +166,9 @@ func (e emitter) commonRenderedLine(ds DividerSet, cellStrs []WidthString, colAl
145166
}
146167
}
147168
if ds.Right != "" && ds.Inner != "" {
148-
fields[len(fields)-1] = e.escStart + ds.Right + e.escStop
169+
fields[len(fields)-1] = e.escStart + ds.Right + eolReset
149170
} else if ds.Right != "" {
150-
fields = append(fields, e.escStart+ds.Right+e.escStop)
171+
fields = append(fields, e.escStart+ds.Right+eolReset)
151172
} else if ds.Inner != "" {
152173
fields = fields[:len(fields)-1]
153174
}

texttable/render.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,12 @@ func (t *TextTable) RenderTo(w io.Writer) error {
131131
colorON := t.colorBegin()
132132
colorCELL := t.colorCellBegin()
133133
colorOFF := t.colorEnd()
134-
if colorON != "" {
134+
if colorON != "" || colorCELL != color.ResetColor {
135135
emitter.SetANSIEscapes(colorON, colorOFF, colorCELL)
136136
}
137+
if t.bgflags&colorToEOL != 0 {
138+
emitter.SetNoResetEOL(true)
139+
}
137140

138141
if headers != nil {
139142
if _, err := io.WriteString(w, emitter.LineHeaderTop()); err != nil {
@@ -220,15 +223,30 @@ func (t *TextTable) colorBegin() string {
220223
if t.bgcolor != nil {
221224
io.WriteString(b, t.bgcolor.AnsiEscapeBG())
222225
}
226+
if t.fgcolor == nil && t.bgcolor == nil && (t.cellfgcolor != nil || t.cellbgcolor != nil) {
227+
io.WriteString(b, color.ResetColor)
228+
}
223229
return b.String()
224230
}
225231

226232
func (t *TextTable) colorCellBegin() string {
227-
if t.bgcolor == nil {
233+
if t.bgcolor == nil && t.cellfgcolor == nil && t.cellbgcolor == nil {
228234
return color.ResetColor
229235
}
230-
if t.bgflags&colorBGSolid != 0 {
231-
return color.ResetColor + t.bgcolor.AnsiEscapeBG()
236+
if t.cellbgcolor == nil && t.bgflags&colorBGSolid != 0 {
237+
if t.cellfgcolor == nil {
238+
return color.ResetColor + t.bgcolor.AnsiEscapeBG()
239+
}
240+
return t.cellfgcolor.AnsiEscapeFG() + t.bgcolor.AnsiEscapeBG()
241+
}
242+
if t.cellbgcolor != nil {
243+
if t.cellfgcolor == nil {
244+
return color.ResetColor + t.cellbgcolor.AnsiEscapeBG()
245+
}
246+
return t.cellfgcolor.AnsiEscapeFG() + t.cellbgcolor.AnsiEscapeBG()
247+
}
248+
if t.cellfgcolor != nil {
249+
return color.ResetColor + t.cellfgcolor.AnsiEscapeFG()
232250
}
233251
return color.ResetColor
234252
}

texttable/style.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,37 @@ func (t *TextTable) SetBGSolid(onoff bool) *TextTable {
8585
}
8686
return t
8787
}
88+
89+
// SetColorToEOL causes the table to not reset color at the end of the table lines
90+
func (t *TextTable) SetColorToEOL(onoff bool) *TextTable {
91+
if onoff {
92+
t.bgflags |= colorToEOL
93+
} else {
94+
t.bgflags &= ^colorToEOL
95+
}
96+
return t
97+
}
98+
99+
// SetCellFGColor sets the color for the foreground color of cells
100+
func (t *TextTable) SetCellFGColor(c color.Color) *TextTable {
101+
t.cellfgcolor = &c
102+
return t
103+
}
104+
105+
// SetCellBGColor sets the color for the background color of cells
106+
func (t *TextTable) SetCellBGColor(c color.Color) *TextTable {
107+
t.cellbgcolor = &c
108+
return t
109+
}
110+
111+
// RemoveCellFGColor removes the color for the foreground of cells
112+
func (t *TextTable) RemoveCellFGColor(c color.Color) *TextTable {
113+
t.cellfgcolor = nil
114+
return t
115+
}
116+
117+
// RemoveCellBGColor removes the color for the background of cells
118+
func (t *TextTable) RemoveCellBGColor(c color.Color) *TextTable {
119+
t.cellbgcolor = nil
120+
return t
121+
}

texttable/wrap.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type colorFlags uint8
1414

1515
const (
1616
colorBGSolid colorFlags = 1 << iota
17+
colorToEOL
1718
)
1819

1920
// A TextTable wraps a tabular.Table to act as the render control for
@@ -22,10 +23,12 @@ const (
2223
type TextTable struct {
2324
tabular.Table
2425

25-
decor decoration.Decoration
26-
fgcolor *color.Color
27-
bgcolor *color.Color
28-
bgflags colorFlags
26+
decor decoration.Decoration
27+
fgcolor *color.Color
28+
bgcolor *color.Color
29+
cellfgcolor *color.Color
30+
cellbgcolor *color.Color
31+
bgflags colorFlags
2932
}
3033

3134
// Wrap returns a TextTable rendering object for the given tabular.Table

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy