Add support for text styling using --color

Close #1663
This commit is contained in:
Junegunn Choi 2020-10-25 19:29:37 +09:00
parent 03c4f04246
commit 11841f688b
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
12 changed files with 502 additions and 347 deletions

View File

@ -15,6 +15,13 @@ CHANGELOG
sleep 0.01
done'
```
- Extended color specification: supports text styles
- `regular` / `bold` / `dim` / `underline` / `italic` / `reverse` / `blink`
```sh
rg --line-number --no-heading --color=always "" |
fzf --ansi --prompt "Rg: " \
--color fg+:italic,hl:underline:-1,hl+:reverse:-1,prompt:reverse
```
- To indicate if `--multi` mode is enabled, fzf will print the number of
selected items even when no item is selected
```sh

View File

@ -267,11 +267,9 @@ Enable processing of ANSI color codes
.BI "--tabstop=" SPACES
Number of spaces for a tab character (default: 8)
.TP
.BI "--color=" "[BASE_SCHEME][,COLOR:ANSI]"
.BI "--color=" "[BASE_SCHEME][,COLOR_NAME[:ANSI_COLOR][:ANSI_ATTRIBUTES]]..."
Color configuration. The name of the base color scheme is followed by custom
color mappings. Ansi color code of -1 denotes terminal default
foreground/background color. You can also specify 24-bit color in \fB#rrggbb\fR
format.
color mappings.
.RS
.B BASE SCHEME:
@ -282,7 +280,7 @@ format.
\fB16 \fRColor scheme for 16-color terminal
\fBbw \fRNo colors (equivalent to \fB--no-color\fR)
.B COLOR:
.B COLOR NAMES:
\fBfg \fRText
\fBbg \fRBackground
\fBpreview-fg \fRPreview window text
@ -300,10 +298,24 @@ format.
\fBspinner \fRStreaming input indicator
\fBheader \fRHeader
.B ANSI COLORS:
\fB-1 \fRDefault terminal foreground/background color
\fB \fR(or the original color of the text)
\fB0 ~ 15 \fR16 base colors
\fB16 ~ 255 \fRANSI 256 colors
\fB#rrggbb \fR24-bit colors
.B ANSI ATTRIBUTES: (Only applies to foreground colors)
\fBregular \fRClears previously set attributes; should precede the other ones
\fBbold\fR
\fBunderline\fR
\fBitalic\fR
\fBreverse\fR
.B EXAMPLES:
\fB# Seoul256 theme with 8-bit colors
# (https://github.com/junegunn/seoul256.vim)
# (https://github.com/junegunn/seoul256.vim)
fzf --color='bg:237,bg+:236,info:143,border:240,spinner:108' \\
--color='hl:65,fg:252,header:65,fg+:252' \\
--color='pointer:161,marker:168,prompt:110,hl+:108'

View File

@ -66,7 +66,7 @@ func Run(opts *Options, revision string) {
var lineAnsiState, prevLineAnsiState *ansiState
if opts.Ansi {
if opts.Theme != nil {
if opts.Theme.Colored {
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
prevLineAnsiState = lineAnsiState
trimmed, offsets, newState := extractColor(string(data), lineAnsiState, nil)
@ -102,7 +102,7 @@ func Run(opts *Options, revision string) {
} else {
chunkList = NewChunkList(func(item *Item, data []byte) bool {
tokens := Tokenize(string(data), opts.Delimiter)
if opts.Ansi && opts.Theme != nil && len(tokens) > 1 {
if opts.Ansi && opts.Theme.Colored && len(tokens) > 1 {
var ansiState *ansiState
if prevLineAnsiState != nil {
ansiStateDup := *prevLineAnsiState

View File

@ -590,11 +590,8 @@ func parseTiebreak(str string) []criterion {
}
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
if theme != nil {
dupe := *theme
return &dupe
}
return nil
dupe := *theme
return &dupe
}
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
@ -619,54 +616,76 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
continue
}
pair := strings.Split(str, ":")
if len(pair) != 2 {
components := strings.Split(str, ":")
if len(components) < 2 {
fail()
}
var ansi tui.Color
if rrggbb.MatchString(pair[1]) {
ansi = tui.HexToColor(pair[1])
} else {
ansi32, err := strconv.Atoi(pair[1])
if err != nil || ansi32 < -1 || ansi32 > 255 {
fail()
cattr := tui.NewColorAttr()
for _, component := range components[1:] {
switch component {
case "regular":
cattr.Attr = tui.AttrRegular
case "bold", "strong":
cattr.Attr |= tui.Bold
case "dim":
cattr.Attr |= tui.Dim
case "italic":
cattr.Attr |= tui.Italic
case "underline":
cattr.Attr |= tui.Underline
case "blink":
cattr.Attr |= tui.Blink
case "reverse":
cattr.Attr |= tui.Reverse
case "":
default:
if rrggbb.MatchString(component) {
cattr.Color = tui.HexToColor(component)
} else {
ansi32, err := strconv.Atoi(component)
if err != nil || ansi32 < -1 || ansi32 > 255 {
fail()
}
cattr.Color = tui.Color(ansi32)
}
}
ansi = tui.Color(ansi32)
}
switch pair[0] {
switch components[0] {
case "input":
theme.Input = cattr
case "fg":
theme.Fg = ansi
theme.Fg = cattr
case "bg":
theme.Bg = ansi
theme.Bg = cattr
case "preview-fg":
theme.PreviewFg = ansi
theme.PreviewFg = cattr
case "preview-bg":
theme.PreviewBg = ansi
theme.PreviewBg = cattr
case "fg+":
theme.Current = ansi
theme.Current = cattr
case "bg+":
theme.DarkBg = ansi
theme.DarkBg = cattr
case "gutter":
theme.Gutter = ansi
theme.Gutter = cattr
case "hl":
theme.Match = ansi
theme.Match = cattr
case "hl+":
theme.CurrentMatch = ansi
theme.CurrentMatch = cattr
case "border":
theme.Border = ansi
theme.Border = cattr
case "prompt":
theme.Prompt = ansi
theme.Prompt = cattr
case "spinner":
theme.Spinner = ansi
theme.Spinner = cattr
case "info":
theme.Info = ansi
theme.Info = cattr
case "pointer":
theme.Cursor = ansi
theme.Cursor = cattr
case "marker":
theme.Selected = ansi
theme.Selected = cattr
case "header":
theme.Header = ansi
theme.Header = cattr
default:
fail()
}
@ -1180,7 +1199,7 @@ func parseOptions(opts *Options, allArgs []string) {
case "--no-mouse":
opts.Mouse = false
case "+c", "--no-color":
opts.Theme = nil
opts.Theme = tui.NoColorTheme()
case "+2", "--no-256":
opts.Theme = tui.Default16
case "--black":
@ -1478,6 +1497,25 @@ func postProcessOptions(opts *Options) {
}
}
}
if opts.Bold {
theme := opts.Theme
boldify := func(c tui.ColorAttr) tui.ColorAttr {
dup := c
if !theme.Colored {
dup.Attr |= tui.Bold
} else if (c.Attr & tui.AttrRegular) == 0 {
dup.Attr |= tui.Bold
}
return dup
}
theme.Current = boldify(theme.Current)
theme.CurrentMatch = boldify(theme.CurrentMatch)
theme.Prompt = boldify(theme.Prompt)
theme.Input = boldify(theme.Input)
theme.Cursor = boldify(theme.Cursor)
theme.Spinner = boldify(theme.Spinner)
}
}
// ParseOptions parses command-line options

View File

@ -295,7 +295,7 @@ func TestColorSpec(t *testing.T) {
}
customized := parseTheme(theme, "fg:231,bg:232")
if customized.Fg != 231 || customized.Bg != 232 {
if customized.Fg.Color != 231 || customized.Bg.Color != 232 {
t.Errorf("color not customized")
}
if *tui.Dark256 == *customized {
@ -313,18 +313,6 @@ func TestColorSpec(t *testing.T) {
}
}
func TestParseNilTheme(t *testing.T) {
var theme *tui.ColorTheme
newTheme := parseTheme(theme, "prompt:12")
if newTheme != nil {
t.Errorf("color is disabled. keep it that way.")
}
newTheme = parseTheme(theme, "prompt:12,dark,prompt:13")
if newTheme.Prompt != 13 {
t.Errorf("color should now be enabled and customized")
}
}
func TestDefaultCtrlNP(t *testing.T) {
check := func(words []string, key int, expected actionType) {
opts := defaultOptions()

View File

@ -15,7 +15,6 @@ type Offset [2]int32
type colorOffset struct {
offset [2]int32
color tui.ColorPair
attr tui.Attr
}
type Result struct {
@ -87,14 +86,14 @@ func minRank() Result {
return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
}
func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, color tui.ColorPair, attr tui.Attr, current bool) []colorOffset {
func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, current bool) []colorOffset {
itemColors := result.item.Colors()
// No ANSI code, or --color=no
// No ANSI codes
if len(itemColors) == 0 {
var offsets []colorOffset
for _, off := range matchOffsets {
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, attr: attr})
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: colMatch})
}
return offsets
}
@ -111,17 +110,19 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
maxCol = ansi.offset[1]
}
}
cols := make([]int, maxCol)
cols := make([]int, maxCol)
for colorIndex, ansi := range itemColors {
for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
cols[i] = colorIndex + 1 // XXX
cols[i] = colorIndex + 1 // 1-based index of itemColors
}
}
for _, off := range matchOffsets {
for i := off[0]; i < off[1]; i++ {
cols[i] = -1
// Negative of 1-based index of itemColors
// - The extra -1 means highlighted
cols[i] = cols[i]*-1 - 1
}
}
@ -133,36 +134,41 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
// --++++++++-- --++++++++++---
curr := 0
start := 0
ansiToColorPair := func(ansi ansiOffset, base tui.ColorPair) tui.ColorPair {
fg := ansi.color.fg
bg := ansi.color.bg
if fg == -1 {
if current {
fg = theme.Current.Color
} else {
fg = theme.Fg.Color
}
}
if bg == -1 {
if current {
bg = theme.DarkBg.Color
} else {
bg = theme.Bg.Color
}
}
return tui.NewColorPair(fg, bg, ansi.color.attr).MergeAttr(base)
}
var colors []colorOffset
add := func(idx int) {
if curr != 0 && idx > start {
if curr == -1 {
colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, color: color, attr: attr})
} else {
ansi := itemColors[curr-1]
fg := ansi.color.fg
bg := ansi.color.bg
if theme != nil {
if fg == -1 {
if current {
fg = theme.Current
} else {
fg = theme.Fg
}
}
if bg == -1 {
if current {
bg = theme.DarkBg
} else {
bg = theme.Bg
}
}
if curr < 0 {
color := colMatch
if curr < -1 && theme.Colored {
origColor := ansiToColorPair(itemColors[-curr-2], colMatch)
color = origColor.MergeNonDefault(color)
}
colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, color: color})
} else {
ansi := itemColors[curr-1]
colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)},
color: tui.NewColorPair(fg, bg),
attr: ansi.color.attr.Merge(attr)})
color: ansiToColorPair(ansi, colBase)})
}
}
}

View File

@ -105,32 +105,55 @@ func TestColorOffset(t *testing.T) {
// ++++++++ ++++++++++
// --++++++++-- --++++++++++---
offsets := []Offset{Offset{5, 15}, Offset{25, 35}}
offsets := []Offset{{5, 15}, {25, 35}}
item := Result{
item: &Item{
colors: &[]ansiOffset{
ansiOffset{[2]int32{0, 20}, ansiState{1, 5, 0}},
ansiOffset{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
ansiOffset{[2]int32{30, 32}, ansiState{3, 7, 0}},
ansiOffset{[2]int32{33, 40}, ansiState{4, 8, tui.Bold}}}}}
// [{[0 5] 9 false} {[5 15] 99 false} {[15 20] 9 false} {[22 25] 10 true} {[25 35] 99 false} {[35 40] 11 true}]
{[2]int32{0, 20}, ansiState{1, 5, 0}},
{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
{[2]int32{30, 32}, ansiState{3, 7, 0}},
{[2]int32{33, 40}, ansiState{4, 8, tui.Bold}}}}}
pair := tui.NewColorPair(99, 199)
colors := item.colorOffsets(offsets, tui.Dark256, pair, tui.AttrRegular, true)
assert := func(idx int, b int32, e int32, c tui.ColorPair, bold bool) {
var attr tui.Attr
if bold {
attr = tui.Bold
}
colBase := tui.NewColorPair(89, 189, tui.AttrUndefined)
colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined)
colors := item.colorOffsets(offsets, tui.Dark256, colBase, colMatch, true)
assert := func(idx int, b int32, e int32, c tui.ColorPair) {
o := colors[idx]
if o.offset[0] != b || o.offset[1] != e || o.color != c || o.attr != attr {
t.Error(o)
if o.offset[0] != b || o.offset[1] != e || o.color != c {
t.Error(o, b, e, c)
}
}
assert(0, 0, 5, tui.NewColorPair(1, 5), false)
assert(1, 5, 15, pair, false)
assert(2, 15, 20, tui.NewColorPair(1, 5), false)
assert(3, 22, 25, tui.NewColorPair(2, 6), true)
assert(4, 25, 35, pair, false)
assert(5, 35, 40, tui.NewColorPair(4, 8), true)
// [{[0 5] {1 5 0}} {[5 15] {99 199 0}} {[15 20] {1 5 0}}
// {[22 25] {2 6 1}} {[25 27] {99 199 1}} {[27 30] {99 199 0}}
// {[30 32] {99 199 0}} {[32 33] {99 199 0}} {[33 35] {99 199 1}}
// {[35 40] {4 8 1}}]
assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
assert(1, 5, 15, colMatch)
assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
assert(4, 25, 27, colMatch.WithAttr(tui.Bold))
assert(5, 27, 30, colMatch)
assert(6, 30, 32, colMatch)
assert(7, 32, 33, colMatch) // TODO: Should we merge consecutive blocks?
assert(8, 33, 35, colMatch.WithAttr(tui.Bold))
assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
colRegular := tui.NewColorPair(-1, -1, tui.AttrUndefined)
colUnderline := tui.NewColorPair(-1, -1, tui.Underline)
colors = item.colorOffsets(offsets, tui.Dark256, colRegular, colUnderline, true)
// [{[0 5] {1 5 0}} {[5 15] {1 5 8}} {[15 20] {1 5 0}}
// {[22 25] {2 6 1}} {[25 27] {2 6 9}} {[27 30] {-1 -1 8}}
// {[30 32] {3 7 8}} {[32 33] {-1 -1 8}} {[33 35] {4 8 9}}
// {[35 40] {4 8 1}}]
assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
assert(1, 5, 15, tui.NewColorPair(1, 5, tui.Underline))
assert(2, 15, 20, tui.NewColorPair(1, 5, tui.AttrUndefined))
assert(3, 22, 25, tui.NewColorPair(2, 6, tui.Bold))
assert(4, 25, 27, tui.NewColorPair(2, 6, tui.Bold|tui.Underline))
assert(5, 27, 30, colUnderline)
assert(6, 30, 32, tui.NewColorPair(3, 7, tui.Underline))
assert(7, 32, 33, colUnderline)
assert(8, 33, 35, tui.NewColorPair(4, 8, tui.Bold|tui.Underline))
assert(9, 35, 40, tui.NewColorPair(4, 8, tui.Bold))
}

View File

@ -23,6 +23,7 @@ import (
var placeholder *regexp.Regexp
var numericPrefix *regexp.Regexp
var whiteSuffix *regexp.Regexp
var activeTempFiles []string
const ellipsis string = ".."
@ -31,6 +32,7 @@ const clearCode string = "\x1b[2J"
func init() {
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
whiteSuffix = regexp.MustCompile(`\s*$`)
activeTempFiles = []string{}
}
@ -515,9 +517,29 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
var state *ansiState
trimmed, colors, _ := extractColor(prompt, state, nil)
item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors}
// "Prompt> "
// ------- // Do not apply ANSI attributes to the trailing whitespaces
// // unless the part has a non-default ANSI state
loc := whiteSuffix.FindStringIndex(trimmed)
if loc != nil {
blankState := ansiOffset{[2]int32{int32(loc[0]), int32(loc[1])}, ansiState{-1, -1, tui.AttrClear}}
if item.colors != nil {
lastColor := (*item.colors)[len(*item.colors)-1]
fmt.Println(lastColor.offset[1], int32(loc[1]))
if lastColor.offset[1] < int32(loc[1]) {
blankState.offset[0] = lastColor.offset[1]
colors := append(*item.colors, blankState)
item.colors = &colors
}
} else {
colors := []ansiOffset{blankState}
item.colors = &colors
}
}
output := func() {
t.printHighlighted(
Result{item: item}, t.strong, tui.ColPrompt, tui.ColPrompt, false, false)
Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false)
}
_, promptLen := t.processTabs([]rune(trimmed), 0)
@ -839,8 +861,8 @@ func (t *Terminal) printPrompt() {
t.prompt()
before, after := t.updatePromptOffset()
t.window.CPrint(tui.ColNormal, t.strong, string(before))
t.window.CPrint(tui.ColNormal, t.strong, string(after))
t.window.CPrint(tui.ColInput, string(before))
t.window.CPrint(tui.ColInput, string(after))
}
func (t *Terminal) trimMessage(message string, maxWidth int) string {
@ -859,7 +881,7 @@ func (t *Terminal) printInfo() {
if t.reading {
duration := int64(spinnerDuration)
idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
t.window.CPrint(tui.ColSpinner, t.strong, t.spinner[idx])
t.window.CPrint(tui.ColSpinner, t.spinner[idx])
}
t.move(1, 2, false)
pos = 2
@ -870,9 +892,9 @@ func (t *Terminal) printInfo() {
}
t.move(0, pos, true)
if t.reading {
t.window.CPrint(tui.ColSpinner, t.strong, " < ")
t.window.CPrint(tui.ColSpinner, " < ")
} else {
t.window.CPrint(tui.ColPrompt, t.strong, " < ")
t.window.CPrint(tui.ColPrompt, " < ")
}
pos += len(" < ")
case infoHidden:
@ -903,7 +925,7 @@ func (t *Terminal) printInfo() {
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
}
output = t.trimMessage(output, t.window.Width()-pos)
t.window.CPrint(tui.ColInfo, 0, output)
t.window.CPrint(tui.ColInfo, output)
}
func (t *Terminal) printHeader() {
@ -928,7 +950,7 @@ func (t *Terminal) printHeader() {
t.move(line, 2, true)
t.printHighlighted(Result{item: item},
tui.AttrRegular, tui.ColHeader, tui.ColHeader, false, false)
tui.ColHeader, tui.ColHeader, false, false)
}
}
@ -958,7 +980,7 @@ func (t *Terminal) printList() {
func (t *Terminal) printItem(result Result, line int, i int, current bool) {
item := result.item
_, selected := t.selected[item.Index()]
label := t.pointerEmpty
label := ""
if t.jumping != jumpDisabled {
if i < len(t.jumpLabels) {
// Striped
@ -983,21 +1005,29 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool) {
t.move(line, 0, false)
if current {
t.window.CPrint(tui.ColCurrentCursor, t.strong, label)
if selected {
t.window.CPrint(tui.ColCurrentSelected, t.strong, t.marker)
if len(label) == 0 {
t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty)
} else {
t.window.CPrint(tui.ColCurrentSelected, t.strong, t.markerEmpty)
t.window.CPrint(tui.ColCurrentCursor, label)
}
newLine.width = t.printHighlighted(result, t.strong, tui.ColCurrent, tui.ColCurrentMatch, true, true)
} else {
t.window.CPrint(tui.ColCursor, t.strong, label)
if selected {
t.window.CPrint(tui.ColSelected, t.strong, t.marker)
t.window.CPrint(tui.ColCurrentSelected, t.marker)
} else {
t.window.CPrint(tui.ColCurrentSelectedEmpty, t.markerEmpty)
}
newLine.width = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true)
} else {
if len(label) == 0 {
t.window.CPrint(tui.ColCursorEmpty, t.pointerEmpty)
} else {
t.window.CPrint(tui.ColCursor, label)
}
if selected {
t.window.CPrint(tui.ColSelected, t.marker)
} else {
t.window.Print(t.markerEmpty)
}
newLine.width = t.printHighlighted(result, 0, tui.ColNormal, tui.ColMatch, false, true)
newLine.width = t.printHighlighted(result, tui.ColNormal, tui.ColMatch, false, true)
}
fillSpaces := prevLine.width - newLine.width
if fillSpaces > 0 {
@ -1051,7 +1081,7 @@ func (t *Terminal) overflow(runes []rune, max int) bool {
return t.displayWidthWithLimit(runes, 0, max) > max
}
func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.ColorPair, col2 tui.ColorPair, current bool, match bool) int {
func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool) int {
item := result.item
// Overflow
@ -1076,7 +1106,7 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
maxe = util.Max(maxe, int(offset[1]))
}
offsets := result.colorOffsets(charOffsets, t.theme, col2, attr, current)
offsets := result.colorOffsets(charOffsets, t.theme, colBase, colMatch, current)
maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
@ -1134,11 +1164,11 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
e := util.Constrain32(offset.offset[1], index, maxOffset)
substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
t.window.CPrint(col1, attr, substr)
t.window.CPrint(colBase, substr)
if b < e {
substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
t.window.CPrint(offset.color, offset.attr, substr)
t.window.CPrint(offset.color, substr)
}
index = e
@ -1148,7 +1178,7 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
}
if index < maxOffset {
substr, _ = t.processTabs(text[index:], prefixWidth)
t.window.CPrint(col1, attr, substr)
t.window.CPrint(colBase, substr)
}
return displayWidth
}
@ -1161,7 +1191,7 @@ func (t *Terminal) renderPreviewSpinner() {
if !t.previewer.scrollable {
if maxWidth > 0 {
t.pwindow.Move(0, maxWidth-1)
t.pwindow.CPrint(tui.ColSpinner, t.strong, spin)
t.pwindow.CPrint(tui.ColSpinner, spin)
}
} else {
offsetString := fmt.Sprintf("%d/%d", t.previewer.offset+1, numLines)
@ -1173,8 +1203,8 @@ func (t *Terminal) renderPreviewSpinner() {
pos := maxWidth - t.displayWidth(offsetRunes)
t.pwindow.Move(0, pos)
if maxWidth > 0 {
t.pwindow.CPrint(tui.ColSpinner, t.strong, spin)
t.pwindow.CPrint(tui.ColInfo, tui.Reverse, string(offsetRunes))
t.pwindow.CPrint(tui.ColSpinner, spin)
t.pwindow.CPrint(tui.ColInfo.WithAttr(tui.Reverse), string(offsetRunes))
}
}
}
@ -1185,7 +1215,7 @@ func (t *Terminal) renderPreviewText(unchanged bool) {
lineNo := -t.previewer.offset
height := t.pwindow.Height()
if unchanged {
t.pwindow.Move(0, 0)
t.pwindow.MoveAndClear(0, 0)
} else {
t.previewed.filled = false
t.pwindow.Erase()
@ -1205,7 +1235,7 @@ func (t *Terminal) renderPreviewText(unchanged bool) {
}
str, width := t.processTabs(trimmed, prefixWidth)
prefixWidth += width
if t.theme != nil && ansi != nil && ansi.colored() {
if t.theme.Colored && ansi != nil && ansi.colored() {
fillRet = t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
} else {
fillRet = t.pwindow.CFill(tui.ColPreview.Fg(), tui.ColPreview.Bg(), tui.AttrRegular, str)
@ -1258,7 +1288,7 @@ func (t *Terminal) printPreviewDelayed() {
message := t.trimMessage("Loading ..", t.pwindow.Width())
pos := t.pwindow.Width() - len(message)
t.pwindow.Move(0, pos)
t.pwindow.CPrint(tui.ColInfo, tui.Reverse, message)
t.pwindow.CPrint(tui.ColInfo.WithAttr(tui.Reverse), message)
}
func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {

View File

@ -15,14 +15,17 @@ func (a Attr) Merge(b Attr) Attr {
}
const (
AttrRegular Attr = Attr(0)
Bold = Attr(1)
Dim = Attr(1 << 1)
Italic = Attr(1 << 2)
Underline = Attr(1 << 3)
Blink = Attr(1 << 4)
Blink2 = Attr(1 << 5)
Reverse = Attr(1 << 6)
AttrUndefined = Attr(0)
AttrRegular = Attr(1 << 7)
AttrClear = Attr(1 << 8)
Bold = Attr(1)
Dim = Attr(1 << 1)
Italic = Attr(1 << 2)
Underline = Attr(1 << 3)
Blink = Attr(1 << 4)
Blink2 = Attr(1 << 5)
Reverse = Attr(1 << 6)
)
func (r *FullscreenRenderer) Init() {}

View File

@ -627,7 +627,7 @@ func (r *LightRenderer) MaxY() int {
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
w := &LightWindow{
renderer: r,
colored: r.theme != nil,
colored: r.theme.Colored,
preview: preview,
border: borderStyle,
top: top,
@ -637,14 +637,12 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
tabstop: r.tabstop,
fg: colDefault,
bg: colDefault}
if r.theme != nil {
if preview {
w.fg = r.theme.PreviewFg
w.bg = r.theme.PreviewBg
} else {
w.fg = r.theme.Fg
w.bg = r.theme.Bg
}
if preview {
w.fg = r.theme.PreviewFg.Color
w.bg = r.theme.PreviewBg.Color
} else {
w.fg = r.theme.Fg.Color
w.bg = r.theme.Bg.Color
}
w.drawBorder()
return w
@ -661,9 +659,9 @@ func (w *LightWindow) drawBorder() {
func (w *LightWindow) drawBorderHorizontal() {
w.Move(0, 0)
w.CPrint(ColBorder, AttrRegular, repeat(w.border.horizontal, w.width))
w.CPrint(ColBorder, repeat(w.border.horizontal, w.width))
w.Move(w.height-1, 0)
w.CPrint(ColBorder, AttrRegular, repeat(w.border.horizontal, w.width))
w.CPrint(ColBorder, repeat(w.border.horizontal, w.width))
}
func (w *LightWindow) drawBorderAround() {
@ -672,17 +670,15 @@ func (w *LightWindow) drawBorderAround() {
if w.preview {
color = ColPreviewBorder
}
w.CPrint(color, AttrRegular,
string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
for y := 1; y < w.height-1; y++ {
w.Move(y, 0)
w.CPrint(color, AttrRegular, string(w.border.vertical))
w.CPrint(color, AttrRegular, repeat(' ', w.width-2))
w.CPrint(color, AttrRegular, string(w.border.vertical))
w.CPrint(color, string(w.border.vertical))
w.CPrint(color, repeat(' ', w.width-2))
w.CPrint(color, string(w.border.vertical))
}
w.Move(w.height-1, 0)
w.CPrint(color, AttrRegular,
string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
}
func (w *LightWindow) csi(code string) {
@ -745,6 +741,9 @@ func (w *LightWindow) MoveAndClear(y int, x int) {
func attrCodes(attr Attr) []string {
codes := []string{}
if (attr & AttrClear) > 0 {
return codes
}
if (attr & Bold) > 0 {
codes = append(codes, "1")
}
@ -804,12 +803,8 @@ func cleanse(str string) string {
return strings.Replace(str, "\x1b", "", -1)
}
func (w *LightWindow) CPrint(pair ColorPair, attr Attr, text string) {
if !w.colored {
w.csiColor(colDefault, colDefault, attrFor(pair, attr))
} else {
w.csiColor(pair.Fg(), pair.Bg(), attr)
}
func (w *LightWindow) CPrint(pair ColorPair, text string) {
w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
w.stderrInternal(cleanse(text), false)
w.csi("m")
}

View File

@ -77,11 +77,13 @@ const (
Blink = Attr(tcell.AttrBlink)
Reverse = Attr(tcell.AttrReverse)
Underline = Attr(tcell.AttrUnderline)
Italic = Attr(tcell.AttrNone) // Not supported
Italic = Attr(tcell.AttrItalic)
)
const (
AttrRegular Attr = 0
AttrUndefined = Attr(0)
AttrRegular = Attr(1 << 7)
AttrClear = Attr(1 << 8)
)
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
@ -414,7 +416,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
normal = ColPreview
}
return &TcellWindow{
color: r.theme != nil,
color: r.theme.Colored,
preview: preview,
top: top,
left: left,
@ -460,27 +462,23 @@ func (w *TcellWindow) MoveAndClear(y int, x int) {
}
func (w *TcellWindow) Print(text string) {
w.printString(text, w.normal, 0)
w.printString(text, w.normal)
}
func (w *TcellWindow) printString(text string, pair ColorPair, a Attr) {
func (w *TcellWindow) printString(text string, pair ColorPair) {
t := text
lx := 0
a := pair.Attr()
var style tcell.Style
if w.color {
style = pair.style().
style := pair.style()
if a&AttrClear == 0 {
style = style.
Reverse(a&Attr(tcell.AttrReverse) != 0).
Underline(a&Attr(tcell.AttrUnderline) != 0)
} else {
style = w.normal.style().
Reverse(a&Attr(tcell.AttrReverse) != 0 || pair == ColCurrent || pair == ColCurrentMatch).
Underline(a&Attr(tcell.AttrUnderline) != 0 || pair == ColMatch || pair == ColCurrentMatch)
Underline(a&Attr(tcell.AttrUnderline) != 0).
Italic(a&Attr(tcell.AttrItalic) != 0).
Blink(a&Attr(tcell.AttrBlink) != 0).
Dim(a&Attr(tcell.AttrDim) != 0)
}
style = style.
Blink(a&Attr(tcell.AttrBlink) != 0).
Bold(a&Attr(tcell.AttrBold) != 0).
Dim(a&Attr(tcell.AttrDim) != 0)
for {
if len(t) == 0 {
@ -513,8 +511,8 @@ func (w *TcellWindow) printString(text string, pair ColorPair, a Attr) {
w.lastX += lx
}
func (w *TcellWindow) CPrint(pair ColorPair, attr Attr, text string) {
w.printString(text, pair, attr)
func (w *TcellWindow) CPrint(pair ColorPair, text string) {
w.printString(text, pair)
}
func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) FillReturn {
@ -531,7 +529,8 @@ func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) FillReturn
Bold(a&Attr(tcell.AttrBold) != 0).
Dim(a&Attr(tcell.AttrDim) != 0).
Reverse(a&Attr(tcell.AttrReverse) != 0).
Underline(a&Attr(tcell.AttrUnderline) != 0)
Underline(a&Attr(tcell.AttrUnderline) != 0).
Italic(a&Attr(tcell.AttrItalic) != 0)
for _, r := range text {
if r == '\n' {
@ -574,7 +573,7 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
if bg == colDefault {
bg = w.normal.Bg()
}
return w.fillString(str, NewColorPair(fg, bg), a)
return w.fillString(str, NewColorPair(fg, bg, a), a)
}
func (w *TcellWindow) drawBorder() {

View File

@ -123,6 +123,15 @@ func (c Color) is24() bool {
return c > 0 && (c&(1<<24)) > 0
}
type ColorAttr struct {
Color Color
Attr Attr
}
func NewColorAttr() ColorAttr {
return ColorAttr{Color: colUndefined, Attr: AttrUndefined}
}
const (
colUndefined Color = -2
colDefault Color = -1
@ -148,9 +157,9 @@ const (
)
type ColorPair struct {
fg Color
bg Color
id int
fg Color
bg Color
attr Attr
}
func HexToColor(rrggbb string) Color {
@ -160,8 +169,8 @@ func HexToColor(rrggbb string) Color {
return Color((1 << 24) + (r << 16) + (g << 8) + b)
}
func NewColorPair(fg Color, bg Color) ColorPair {
return ColorPair{fg, bg, -1}
func NewColorPair(fg Color, bg Color, attr Attr) ColorPair {
return ColorPair{fg, bg, attr}
}
func (p ColorPair) Fg() Color {
@ -172,23 +181,59 @@ func (p ColorPair) Bg() Color {
return p.bg
}
func (p ColorPair) Attr() Attr {
return p.attr
}
func (p ColorPair) merge(other ColorPair, except Color) ColorPair {
dup := p
dup.attr = dup.attr.Merge(other.attr)
if other.fg != except {
dup.fg = other.fg
}
if other.bg != except {
dup.bg = other.bg
}
return dup
}
func (p ColorPair) WithAttr(attr Attr) ColorPair {
dup := p
dup.attr = dup.attr.Merge(attr)
return dup
}
func (p ColorPair) MergeAttr(other ColorPair) ColorPair {
return p.WithAttr(other.attr)
}
func (p ColorPair) Merge(other ColorPair) ColorPair {
return p.merge(other, colUndefined)
}
func (p ColorPair) MergeNonDefault(other ColorPair) ColorPair {
return p.merge(other, colDefault)
}
type ColorTheme struct {
Fg Color
Bg Color
PreviewFg Color
PreviewBg Color
DarkBg Color
Gutter Color
Prompt Color
Match Color
Current Color
CurrentMatch Color
Spinner Color
Info Color
Cursor Color
Selected Color
Header Color
Border Color
Colored bool
Input ColorAttr
Fg ColorAttr
Bg ColorAttr
PreviewFg ColorAttr
PreviewBg ColorAttr
DarkBg ColorAttr
Gutter ColorAttr
Prompt ColorAttr
Match ColorAttr
Current ColorAttr
CurrentMatch ColorAttr
Spinner ColorAttr
Info ColorAttr
Cursor ColorAttr
Selected ColorAttr
Header ColorAttr
Border ColorAttr
}
type Event struct {
@ -307,7 +352,7 @@ type Window interface {
Move(y int, x int)
MoveAndClear(y int, x int)
Print(text string)
CPrint(color ColorPair, attr Attr, text string)
CPrint(color ColorPair, text string)
Fill(text string) FillReturn
CFill(fg Color, bg Color, attr Attr, text string) FillReturn
Erase()
@ -336,41 +381,69 @@ var (
Dark256 *ColorTheme
Light256 *ColorTheme
ColPrompt ColorPair
ColNormal ColorPair
ColMatch ColorPair
ColCursor ColorPair
ColSelected ColorPair
ColCurrent ColorPair
ColCurrentMatch ColorPair
ColCurrentCursor ColorPair
ColCurrentSelected ColorPair
ColSpinner ColorPair
ColInfo ColorPair
ColHeader ColorPair
ColBorder ColorPair
ColPreview ColorPair
ColPreviewBorder ColorPair
ColPrompt ColorPair
ColNormal ColorPair
ColInput ColorPair
ColMatch ColorPair
ColCursor ColorPair
ColCursorEmpty ColorPair
ColSelected ColorPair
ColCurrent ColorPair
ColCurrentMatch ColorPair
ColCurrentCursor ColorPair
ColCurrentCursorEmpty ColorPair
ColCurrentSelected ColorPair
ColCurrentSelectedEmpty ColorPair
ColSpinner ColorPair
ColInfo ColorPair
ColHeader ColorPair
ColBorder ColorPair
ColPreview ColorPair
ColPreviewBorder ColorPair
)
func EmptyTheme() *ColorTheme {
return &ColorTheme{
Fg: colUndefined,
Bg: colUndefined,
PreviewFg: colUndefined,
PreviewBg: colUndefined,
DarkBg: colUndefined,
Gutter: colUndefined,
Prompt: colUndefined,
Match: colUndefined,
Current: colUndefined,
CurrentMatch: colUndefined,
Spinner: colUndefined,
Info: colUndefined,
Cursor: colUndefined,
Selected: colUndefined,
Header: colUndefined,
Border: colUndefined}
Colored: true,
Input: ColorAttr{colUndefined, AttrUndefined},
Fg: ColorAttr{colUndefined, AttrUndefined},
Bg: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{colUndefined, AttrUndefined},
Gutter: ColorAttr{colUndefined, AttrUndefined},
Prompt: ColorAttr{colUndefined, AttrUndefined},
Match: ColorAttr{colUndefined, AttrUndefined},
Current: ColorAttr{colUndefined, AttrUndefined},
CurrentMatch: ColorAttr{colUndefined, AttrUndefined},
Spinner: ColorAttr{colUndefined, AttrUndefined},
Info: ColorAttr{colUndefined, AttrUndefined},
Cursor: ColorAttr{colUndefined, AttrUndefined},
Selected: ColorAttr{colUndefined, AttrUndefined},
Header: ColorAttr{colUndefined, AttrUndefined},
Border: ColorAttr{colUndefined, AttrUndefined}}
}
func NoColorTheme() *ColorTheme {
return &ColorTheme{
Colored: false,
Input: ColorAttr{colDefault, AttrRegular},
Fg: ColorAttr{colDefault, AttrRegular},
Bg: ColorAttr{colDefault, AttrRegular},
PreviewFg: ColorAttr{colDefault, AttrRegular},
PreviewBg: ColorAttr{colDefault, AttrRegular},
DarkBg: ColorAttr{colDefault, AttrRegular},
Gutter: ColorAttr{colDefault, AttrRegular},
Prompt: ColorAttr{colDefault, AttrRegular},
Match: ColorAttr{colDefault, Underline},
Current: ColorAttr{colDefault, Reverse},
CurrentMatch: ColorAttr{colDefault, Reverse | Underline},
Spinner: ColorAttr{colDefault, AttrRegular},
Info: ColorAttr{colDefault, AttrRegular},
Cursor: ColorAttr{colDefault, AttrRegular},
Selected: ColorAttr{colDefault, AttrRegular},
Header: ColorAttr{colDefault, AttrRegular},
Border: ColorAttr{colDefault, AttrRegular}}
}
func errorExit(message string) {
@ -380,74 +453,80 @@ func errorExit(message string) {
func init() {
Default16 = &ColorTheme{
Fg: colDefault,
Bg: colDefault,
PreviewFg: colUndefined,
PreviewBg: colUndefined,
DarkBg: colBlack,
Gutter: colUndefined,
Prompt: colBlue,
Match: colGreen,
Current: colYellow,
CurrentMatch: colGreen,
Spinner: colGreen,
Info: colWhite,
Cursor: colRed,
Selected: colMagenta,
Header: colCyan,
Border: colBlack}
Colored: true,
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{colBlack, AttrUndefined},
Gutter: ColorAttr{colUndefined, AttrUndefined},
Prompt: ColorAttr{colBlue, AttrUndefined},
Match: ColorAttr{colGreen, AttrUndefined},
Current: ColorAttr{colYellow, AttrUndefined},
CurrentMatch: ColorAttr{colGreen, AttrUndefined},
Spinner: ColorAttr{colGreen, AttrUndefined},
Info: ColorAttr{colWhite, AttrUndefined},
Cursor: ColorAttr{colRed, AttrUndefined},
Selected: ColorAttr{colMagenta, AttrUndefined},
Header: ColorAttr{colCyan, AttrUndefined},
Border: ColorAttr{colBlack, AttrUndefined}}
Dark256 = &ColorTheme{
Fg: colDefault,
Bg: colDefault,
PreviewFg: colUndefined,
PreviewBg: colUndefined,
DarkBg: 236,
Gutter: colUndefined,
Prompt: 110,
Match: 108,
Current: 254,
CurrentMatch: 151,
Spinner: 148,
Info: 144,
Cursor: 161,
Selected: 168,
Header: 109,
Border: 59}
Colored: true,
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{236, AttrUndefined},
Gutter: ColorAttr{colUndefined, AttrUndefined},
Prompt: ColorAttr{110, AttrUndefined},
Match: ColorAttr{108, AttrUndefined},
Current: ColorAttr{254, AttrUndefined},
CurrentMatch: ColorAttr{151, AttrUndefined},
Spinner: ColorAttr{148, AttrUndefined},
Info: ColorAttr{144, AttrUndefined},
Cursor: ColorAttr{161, AttrUndefined},
Selected: ColorAttr{168, AttrUndefined},
Header: ColorAttr{109, AttrUndefined},
Border: ColorAttr{59, AttrUndefined}}
Light256 = &ColorTheme{
Fg: colDefault,
Bg: colDefault,
PreviewFg: colUndefined,
PreviewBg: colUndefined,
DarkBg: 251,
Gutter: colUndefined,
Prompt: 25,
Match: 66,
Current: 237,
CurrentMatch: 23,
Spinner: 65,
Info: 101,
Cursor: 161,
Selected: 168,
Header: 31,
Border: 145}
Colored: true,
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{251, AttrUndefined},
Gutter: ColorAttr{colUndefined, AttrUndefined},
Prompt: ColorAttr{25, AttrUndefined},
Match: ColorAttr{66, AttrUndefined},
Current: ColorAttr{237, AttrUndefined},
CurrentMatch: ColorAttr{23, AttrUndefined},
Spinner: ColorAttr{65, AttrUndefined},
Info: ColorAttr{101, AttrUndefined},
Cursor: ColorAttr{161, AttrUndefined},
Selected: ColorAttr{168, AttrUndefined},
Header: ColorAttr{31, AttrUndefined},
Border: ColorAttr{145, AttrUndefined}}
}
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
if theme == nil {
initPalette(theme)
return
}
if forceBlack {
theme.Bg = colBlack
theme.Bg = ColorAttr{colBlack, AttrUndefined}
}
o := func(a Color, b Color) Color {
if b == colUndefined {
return a
o := func(a ColorAttr, b ColorAttr) ColorAttr {
c := a
if b.Color != colUndefined {
c.Color = b.Color
}
return b
if b.Attr != AttrUndefined {
c.Attr = b.Attr
}
return c
}
theme.Input = o(baseTheme.Input, theme.Input)
theme.Fg = o(baseTheme.Fg, theme.Fg)
theme.Bg = o(baseTheme.Bg, theme.Bg)
theme.PreviewFg = o(theme.Fg, o(baseTheme.PreviewFg, theme.PreviewFg))
@ -469,54 +548,29 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
}
func initPalette(theme *ColorTheme) {
idx := 0
pair := func(fg, bg Color) ColorPair {
idx++
return ColorPair{fg, bg, idx}
pair := func(fg, bg ColorAttr) ColorPair {
return ColorPair{fg.Color, bg.Color, fg.Attr}
}
if theme != nil {
ColPrompt = pair(theme.Prompt, theme.Bg)
ColNormal = pair(theme.Fg, theme.Bg)
ColMatch = pair(theme.Match, theme.Bg)
ColCursor = pair(theme.Cursor, theme.Gutter)
ColSelected = pair(theme.Selected, theme.Gutter)
ColCurrent = pair(theme.Current, theme.DarkBg)
ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg)
ColCurrentCursor = pair(theme.Cursor, theme.DarkBg)
ColCurrentSelected = pair(theme.Selected, theme.DarkBg)
ColSpinner = pair(theme.Spinner, theme.Bg)
ColInfo = pair(theme.Info, theme.Bg)
ColHeader = pair(theme.Header, theme.Bg)
ColBorder = pair(theme.Border, theme.Bg)
ColPreview = pair(theme.PreviewFg, theme.PreviewBg)
ColPreviewBorder = pair(theme.Border, theme.PreviewBg)
} else {
ColPrompt = pair(colDefault, colDefault)
ColNormal = pair(colDefault, colDefault)
ColMatch = pair(colDefault, colDefault)
ColCursor = pair(colDefault, colDefault)
ColSelected = pair(colDefault, colDefault)
ColCurrent = pair(colDefault, colDefault)
ColCurrentMatch = pair(colDefault, colDefault)
ColCurrentCursor = pair(colDefault, colDefault)
ColCurrentSelected = pair(colDefault, colDefault)
ColSpinner = pair(colDefault, colDefault)
ColInfo = pair(colDefault, colDefault)
ColHeader = pair(colDefault, colDefault)
ColBorder = pair(colDefault, colDefault)
ColPreview = pair(colDefault, colDefault)
ColPreviewBorder = pair(colDefault, colDefault)
}
}
blank := theme.Fg
blank.Attr = AttrRegular
func attrFor(color ColorPair, attr Attr) Attr {
switch color {
case ColCurrent:
return attr | Reverse
case ColMatch:
return attr | Underline
case ColCurrentMatch:
return attr | Underline | Reverse
}
return attr
ColPrompt = pair(theme.Prompt, theme.Bg)
ColNormal = pair(theme.Fg, theme.Bg)
ColInput = pair(theme.Input, theme.Bg)
ColMatch = pair(theme.Match, theme.Bg)
ColCursor = pair(theme.Cursor, theme.Gutter)
ColCursorEmpty = pair(blank, theme.Gutter)
ColSelected = pair(theme.Selected, theme.Gutter)
ColCurrent = pair(theme.Current, theme.DarkBg)
ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg)
ColCurrentCursor = pair(theme.Cursor, theme.DarkBg)
ColCurrentCursorEmpty = pair(blank, theme.DarkBg)
ColCurrentSelected = pair(theme.Selected, theme.DarkBg)
ColCurrentSelectedEmpty = pair(blank, theme.DarkBg)
ColSpinner = pair(theme.Spinner, theme.Bg)
ColInfo = pair(theme.Info, theme.Bg)
ColHeader = pair(theme.Header, theme.Bg)
ColBorder = pair(theme.Border, theme.Bg)
ColPreview = pair(theme.PreviewFg, theme.PreviewBg)
ColPreviewBorder = pair(theme.Border, theme.PreviewBg)
}