mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-12-23 11:29:01 +00:00
parent
03c4f04246
commit
11841f688b
@ -15,6 +15,13 @@ CHANGELOG
|
|||||||
sleep 0.01
|
sleep 0.01
|
||||||
done'
|
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
|
- To indicate if `--multi` mode is enabled, fzf will print the number of
|
||||||
selected items even when no item is selected
|
selected items even when no item is selected
|
||||||
```sh
|
```sh
|
||||||
|
@ -267,11 +267,9 @@ Enable processing of ANSI color codes
|
|||||||
.BI "--tabstop=" SPACES
|
.BI "--tabstop=" SPACES
|
||||||
Number of spaces for a tab character (default: 8)
|
Number of spaces for a tab character (default: 8)
|
||||||
.TP
|
.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 configuration. The name of the base color scheme is followed by custom
|
||||||
color mappings. Ansi color code of -1 denotes terminal default
|
color mappings.
|
||||||
foreground/background color. You can also specify 24-bit color in \fB#rrggbb\fR
|
|
||||||
format.
|
|
||||||
|
|
||||||
.RS
|
.RS
|
||||||
.B BASE SCHEME:
|
.B BASE SCHEME:
|
||||||
@ -282,7 +280,7 @@ format.
|
|||||||
\fB16 \fRColor scheme for 16-color terminal
|
\fB16 \fRColor scheme for 16-color terminal
|
||||||
\fBbw \fRNo colors (equivalent to \fB--no-color\fR)
|
\fBbw \fRNo colors (equivalent to \fB--no-color\fR)
|
||||||
|
|
||||||
.B COLOR:
|
.B COLOR NAMES:
|
||||||
\fBfg \fRText
|
\fBfg \fRText
|
||||||
\fBbg \fRBackground
|
\fBbg \fRBackground
|
||||||
\fBpreview-fg \fRPreview window text
|
\fBpreview-fg \fRPreview window text
|
||||||
@ -300,6 +298,20 @@ format.
|
|||||||
\fBspinner \fRStreaming input indicator
|
\fBspinner \fRStreaming input indicator
|
||||||
\fBheader \fRHeader
|
\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:
|
.B EXAMPLES:
|
||||||
|
|
||||||
\fB# Seoul256 theme with 8-bit colors
|
\fB# Seoul256 theme with 8-bit colors
|
||||||
|
@ -66,7 +66,7 @@ func Run(opts *Options, revision string) {
|
|||||||
|
|
||||||
var lineAnsiState, prevLineAnsiState *ansiState
|
var lineAnsiState, prevLineAnsiState *ansiState
|
||||||
if opts.Ansi {
|
if opts.Ansi {
|
||||||
if opts.Theme != nil {
|
if opts.Theme.Colored {
|
||||||
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
ansiProcessor = func(data []byte) (util.Chars, *[]ansiOffset) {
|
||||||
prevLineAnsiState = lineAnsiState
|
prevLineAnsiState = lineAnsiState
|
||||||
trimmed, offsets, newState := extractColor(string(data), lineAnsiState, nil)
|
trimmed, offsets, newState := extractColor(string(data), lineAnsiState, nil)
|
||||||
@ -102,7 +102,7 @@ func Run(opts *Options, revision string) {
|
|||||||
} else {
|
} else {
|
||||||
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
chunkList = NewChunkList(func(item *Item, data []byte) bool {
|
||||||
tokens := Tokenize(string(data), opts.Delimiter)
|
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
|
var ansiState *ansiState
|
||||||
if prevLineAnsiState != nil {
|
if prevLineAnsiState != nil {
|
||||||
ansiStateDup := *prevLineAnsiState
|
ansiStateDup := *prevLineAnsiState
|
||||||
|
104
src/options.go
104
src/options.go
@ -590,11 +590,8 @@ func parseTiebreak(str string) []criterion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
|
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
|
||||||
if theme != nil {
|
dupe := *theme
|
||||||
dupe := *theme
|
return &dupe
|
||||||
return &dupe
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
||||||
@ -619,54 +616,76 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pair := strings.Split(str, ":")
|
components := strings.Split(str, ":")
|
||||||
if len(pair) != 2 {
|
if len(components) < 2 {
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
var ansi tui.Color
|
cattr := tui.NewColorAttr()
|
||||||
if rrggbb.MatchString(pair[1]) {
|
for _, component := range components[1:] {
|
||||||
ansi = tui.HexToColor(pair[1])
|
switch component {
|
||||||
} else {
|
case "regular":
|
||||||
ansi32, err := strconv.Atoi(pair[1])
|
cattr.Attr = tui.AttrRegular
|
||||||
if err != nil || ansi32 < -1 || ansi32 > 255 {
|
case "bold", "strong":
|
||||||
fail()
|
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":
|
case "fg":
|
||||||
theme.Fg = ansi
|
theme.Fg = cattr
|
||||||
case "bg":
|
case "bg":
|
||||||
theme.Bg = ansi
|
theme.Bg = cattr
|
||||||
case "preview-fg":
|
case "preview-fg":
|
||||||
theme.PreviewFg = ansi
|
theme.PreviewFg = cattr
|
||||||
case "preview-bg":
|
case "preview-bg":
|
||||||
theme.PreviewBg = ansi
|
theme.PreviewBg = cattr
|
||||||
case "fg+":
|
case "fg+":
|
||||||
theme.Current = ansi
|
theme.Current = cattr
|
||||||
case "bg+":
|
case "bg+":
|
||||||
theme.DarkBg = ansi
|
theme.DarkBg = cattr
|
||||||
case "gutter":
|
case "gutter":
|
||||||
theme.Gutter = ansi
|
theme.Gutter = cattr
|
||||||
case "hl":
|
case "hl":
|
||||||
theme.Match = ansi
|
theme.Match = cattr
|
||||||
case "hl+":
|
case "hl+":
|
||||||
theme.CurrentMatch = ansi
|
theme.CurrentMatch = cattr
|
||||||
case "border":
|
case "border":
|
||||||
theme.Border = ansi
|
theme.Border = cattr
|
||||||
case "prompt":
|
case "prompt":
|
||||||
theme.Prompt = ansi
|
theme.Prompt = cattr
|
||||||
case "spinner":
|
case "spinner":
|
||||||
theme.Spinner = ansi
|
theme.Spinner = cattr
|
||||||
case "info":
|
case "info":
|
||||||
theme.Info = ansi
|
theme.Info = cattr
|
||||||
case "pointer":
|
case "pointer":
|
||||||
theme.Cursor = ansi
|
theme.Cursor = cattr
|
||||||
case "marker":
|
case "marker":
|
||||||
theme.Selected = ansi
|
theme.Selected = cattr
|
||||||
case "header":
|
case "header":
|
||||||
theme.Header = ansi
|
theme.Header = cattr
|
||||||
default:
|
default:
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
@ -1180,7 +1199,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--no-mouse":
|
case "--no-mouse":
|
||||||
opts.Mouse = false
|
opts.Mouse = false
|
||||||
case "+c", "--no-color":
|
case "+c", "--no-color":
|
||||||
opts.Theme = nil
|
opts.Theme = tui.NoColorTheme()
|
||||||
case "+2", "--no-256":
|
case "+2", "--no-256":
|
||||||
opts.Theme = tui.Default16
|
opts.Theme = tui.Default16
|
||||||
case "--black":
|
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
|
// ParseOptions parses command-line options
|
||||||
|
@ -295,7 +295,7 @@ func TestColorSpec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
customized := parseTheme(theme, "fg:231,bg:232")
|
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")
|
t.Errorf("color not customized")
|
||||||
}
|
}
|
||||||
if *tui.Dark256 == *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) {
|
func TestDefaultCtrlNP(t *testing.T) {
|
||||||
check := func(words []string, key int, expected actionType) {
|
check := func(words []string, key int, expected actionType) {
|
||||||
opts := defaultOptions()
|
opts := defaultOptions()
|
||||||
|
@ -15,7 +15,6 @@ type Offset [2]int32
|
|||||||
type colorOffset struct {
|
type colorOffset struct {
|
||||||
offset [2]int32
|
offset [2]int32
|
||||||
color tui.ColorPair
|
color tui.ColorPair
|
||||||
attr tui.Attr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
@ -87,14 +86,14 @@ func minRank() Result {
|
|||||||
return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
|
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()
|
itemColors := result.item.Colors()
|
||||||
|
|
||||||
// No ANSI code, or --color=no
|
// No ANSI codes
|
||||||
if len(itemColors) == 0 {
|
if len(itemColors) == 0 {
|
||||||
var offsets []colorOffset
|
var offsets []colorOffset
|
||||||
for _, off := range matchOffsets {
|
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
|
return offsets
|
||||||
}
|
}
|
||||||
@ -111,17 +110,19 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
|
|||||||
maxCol = ansi.offset[1]
|
maxCol = ansi.offset[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cols := make([]int, maxCol)
|
|
||||||
|
|
||||||
|
cols := make([]int, maxCol)
|
||||||
for colorIndex, ansi := range itemColors {
|
for colorIndex, ansi := range itemColors {
|
||||||
for i := ansi.offset[0]; i < ansi.offset[1]; i++ {
|
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 _, off := range matchOffsets {
|
||||||
for i := off[0]; i < off[1]; i++ {
|
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
|
curr := 0
|
||||||
start := 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
|
var colors []colorOffset
|
||||||
add := func(idx int) {
|
add := func(idx int) {
|
||||||
if curr != 0 && idx > start {
|
if curr != 0 && idx > start {
|
||||||
if curr == -1 {
|
if curr < 0 {
|
||||||
colors = append(colors, colorOffset{
|
color := colMatch
|
||||||
offset: [2]int32{int32(start), int32(idx)}, color: color, attr: attr})
|
if curr < -1 && theme.Colored {
|
||||||
} else {
|
origColor := ansiToColorPair(itemColors[-curr-2], colMatch)
|
||||||
ansi := itemColors[curr-1]
|
color = origColor.MergeNonDefault(color)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
colors = append(colors, colorOffset{
|
||||||
|
offset: [2]int32{int32(start), int32(idx)}, color: color})
|
||||||
|
} else {
|
||||||
|
ansi := itemColors[curr-1]
|
||||||
colors = append(colors, colorOffset{
|
colors = append(colors, colorOffset{
|
||||||
offset: [2]int32{int32(start), int32(idx)},
|
offset: [2]int32{int32(start), int32(idx)},
|
||||||
color: tui.NewColorPair(fg, bg),
|
color: ansiToColorPair(ansi, colBase)})
|
||||||
attr: ansi.color.attr.Merge(attr)})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 := Result{
|
||||||
item: &Item{
|
item: &Item{
|
||||||
colors: &[]ansiOffset{
|
colors: &[]ansiOffset{
|
||||||
ansiOffset{[2]int32{0, 20}, ansiState{1, 5, 0}},
|
{[2]int32{0, 20}, ansiState{1, 5, 0}},
|
||||||
ansiOffset{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
|
{[2]int32{22, 27}, ansiState{2, 6, tui.Bold}},
|
||||||
ansiOffset{[2]int32{30, 32}, ansiState{3, 7, 0}},
|
{[2]int32{30, 32}, ansiState{3, 7, 0}},
|
||||||
ansiOffset{[2]int32{33, 40}, ansiState{4, 8, tui.Bold}}}}}
|
{[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}]
|
|
||||||
|
|
||||||
pair := tui.NewColorPair(99, 199)
|
colBase := tui.NewColorPair(89, 189, tui.AttrUndefined)
|
||||||
colors := item.colorOffsets(offsets, tui.Dark256, pair, tui.AttrRegular, true)
|
colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined)
|
||||||
assert := func(idx int, b int32, e int32, c tui.ColorPair, bold bool) {
|
colors := item.colorOffsets(offsets, tui.Dark256, colBase, colMatch, true)
|
||||||
var attr tui.Attr
|
assert := func(idx int, b int32, e int32, c tui.ColorPair) {
|
||||||
if bold {
|
|
||||||
attr = tui.Bold
|
|
||||||
}
|
|
||||||
o := colors[idx]
|
o := colors[idx]
|
||||||
if o.offset[0] != b || o.offset[1] != e || o.color != c || o.attr != attr {
|
if o.offset[0] != b || o.offset[1] != e || o.color != c {
|
||||||
t.Error(o)
|
t.Error(o, b, e, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(0, 0, 5, tui.NewColorPair(1, 5), false)
|
// [{[0 5] {1 5 0}} {[5 15] {99 199 0}} {[15 20] {1 5 0}}
|
||||||
assert(1, 5, 15, pair, false)
|
// {[22 25] {2 6 1}} {[25 27] {99 199 1}} {[27 30] {99 199 0}}
|
||||||
assert(2, 15, 20, tui.NewColorPair(1, 5), false)
|
// {[30 32] {99 199 0}} {[32 33] {99 199 0}} {[33 35] {99 199 1}}
|
||||||
assert(3, 22, 25, tui.NewColorPair(2, 6), true)
|
// {[35 40] {4 8 1}}]
|
||||||
assert(4, 25, 35, pair, false)
|
assert(0, 0, 5, tui.NewColorPair(1, 5, tui.AttrUndefined))
|
||||||
assert(5, 35, 40, tui.NewColorPair(4, 8), true)
|
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))
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
var placeholder *regexp.Regexp
|
var placeholder *regexp.Regexp
|
||||||
var numericPrefix *regexp.Regexp
|
var numericPrefix *regexp.Regexp
|
||||||
|
var whiteSuffix *regexp.Regexp
|
||||||
var activeTempFiles []string
|
var activeTempFiles []string
|
||||||
|
|
||||||
const ellipsis string = ".."
|
const ellipsis string = ".."
|
||||||
@ -31,6 +32,7 @@ const clearCode string = "\x1b[2J"
|
|||||||
func init() {
|
func init() {
|
||||||
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
|
placeholder = regexp.MustCompile(`\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\+?f?nf?})`)
|
||||||
numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
|
numericPrefix = regexp.MustCompile(`^[[:punct:]]*([0-9]+)`)
|
||||||
|
whiteSuffix = regexp.MustCompile(`\s*$`)
|
||||||
activeTempFiles = []string{}
|
activeTempFiles = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,9 +517,29 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
|
|||||||
var state *ansiState
|
var state *ansiState
|
||||||
trimmed, colors, _ := extractColor(prompt, state, nil)
|
trimmed, colors, _ := extractColor(prompt, state, nil)
|
||||||
item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors}
|
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() {
|
output := func() {
|
||||||
t.printHighlighted(
|
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)
|
_, promptLen := t.processTabs([]rune(trimmed), 0)
|
||||||
|
|
||||||
@ -839,8 +861,8 @@ func (t *Terminal) printPrompt() {
|
|||||||
t.prompt()
|
t.prompt()
|
||||||
|
|
||||||
before, after := t.updatePromptOffset()
|
before, after := t.updatePromptOffset()
|
||||||
t.window.CPrint(tui.ColNormal, t.strong, string(before))
|
t.window.CPrint(tui.ColInput, string(before))
|
||||||
t.window.CPrint(tui.ColNormal, t.strong, string(after))
|
t.window.CPrint(tui.ColInput, string(after))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) trimMessage(message string, maxWidth int) string {
|
func (t *Terminal) trimMessage(message string, maxWidth int) string {
|
||||||
@ -859,7 +881,7 @@ func (t *Terminal) printInfo() {
|
|||||||
if t.reading {
|
if t.reading {
|
||||||
duration := int64(spinnerDuration)
|
duration := int64(spinnerDuration)
|
||||||
idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
|
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)
|
t.move(1, 2, false)
|
||||||
pos = 2
|
pos = 2
|
||||||
@ -870,9 +892,9 @@ func (t *Terminal) printInfo() {
|
|||||||
}
|
}
|
||||||
t.move(0, pos, true)
|
t.move(0, pos, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
t.window.CPrint(tui.ColSpinner, t.strong, " < ")
|
t.window.CPrint(tui.ColSpinner, " < ")
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(tui.ColPrompt, t.strong, " < ")
|
t.window.CPrint(tui.ColPrompt, " < ")
|
||||||
}
|
}
|
||||||
pos += len(" < ")
|
pos += len(" < ")
|
||||||
case infoHidden:
|
case infoHidden:
|
||||||
@ -903,7 +925,7 @@ func (t *Terminal) printInfo() {
|
|||||||
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
output = fmt.Sprintf("[Command failed: %s]", *t.failed)
|
||||||
}
|
}
|
||||||
output = t.trimMessage(output, t.window.Width()-pos)
|
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() {
|
func (t *Terminal) printHeader() {
|
||||||
@ -928,7 +950,7 @@ func (t *Terminal) printHeader() {
|
|||||||
|
|
||||||
t.move(line, 2, true)
|
t.move(line, 2, true)
|
||||||
t.printHighlighted(Result{item: item},
|
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) {
|
func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
||||||
item := result.item
|
item := result.item
|
||||||
_, selected := t.selected[item.Index()]
|
_, selected := t.selected[item.Index()]
|
||||||
label := t.pointerEmpty
|
label := ""
|
||||||
if t.jumping != jumpDisabled {
|
if t.jumping != jumpDisabled {
|
||||||
if i < len(t.jumpLabels) {
|
if i < len(t.jumpLabels) {
|
||||||
// Striped
|
// Striped
|
||||||
@ -983,21 +1005,29 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
|||||||
|
|
||||||
t.move(line, 0, false)
|
t.move(line, 0, false)
|
||||||
if current {
|
if current {
|
||||||
t.window.CPrint(tui.ColCurrentCursor, t.strong, label)
|
if len(label) == 0 {
|
||||||
if selected {
|
t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty)
|
||||||
t.window.CPrint(tui.ColCurrentSelected, t.strong, t.marker)
|
|
||||||
} else {
|
} 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 {
|
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 {
|
} else {
|
||||||
t.window.Print(t.markerEmpty)
|
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
|
fillSpaces := prevLine.width - newLine.width
|
||||||
if fillSpaces > 0 {
|
if fillSpaces > 0 {
|
||||||
@ -1051,7 +1081,7 @@ func (t *Terminal) overflow(runes []rune, max int) bool {
|
|||||||
return t.displayWidthWithLimit(runes, 0, max) > max
|
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
|
item := result.item
|
||||||
|
|
||||||
// Overflow
|
// Overflow
|
||||||
@ -1076,7 +1106,7 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
|
|||||||
maxe = util.Max(maxe, int(offset[1]))
|
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)
|
maxWidth := t.window.Width() - (t.pointerLen + t.markerLen + 1)
|
||||||
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
|
maxe = util.Constrain(maxe+util.Min(maxWidth/2-2, t.hscrollOff), 0, len(text))
|
||||||
displayWidth := t.displayWidthWithLimit(text, 0, maxWidth)
|
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)
|
e := util.Constrain32(offset.offset[1], index, maxOffset)
|
||||||
|
|
||||||
substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
|
substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
|
||||||
t.window.CPrint(col1, attr, substr)
|
t.window.CPrint(colBase, substr)
|
||||||
|
|
||||||
if b < e {
|
if b < e {
|
||||||
substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
|
substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
|
||||||
t.window.CPrint(offset.color, offset.attr, substr)
|
t.window.CPrint(offset.color, substr)
|
||||||
}
|
}
|
||||||
|
|
||||||
index = e
|
index = e
|
||||||
@ -1148,7 +1178,7 @@ func (t *Terminal) printHighlighted(result Result, attr tui.Attr, col1 tui.Color
|
|||||||
}
|
}
|
||||||
if index < maxOffset {
|
if index < maxOffset {
|
||||||
substr, _ = t.processTabs(text[index:], prefixWidth)
|
substr, _ = t.processTabs(text[index:], prefixWidth)
|
||||||
t.window.CPrint(col1, attr, substr)
|
t.window.CPrint(colBase, substr)
|
||||||
}
|
}
|
||||||
return displayWidth
|
return displayWidth
|
||||||
}
|
}
|
||||||
@ -1161,7 +1191,7 @@ func (t *Terminal) renderPreviewSpinner() {
|
|||||||
if !t.previewer.scrollable {
|
if !t.previewer.scrollable {
|
||||||
if maxWidth > 0 {
|
if maxWidth > 0 {
|
||||||
t.pwindow.Move(0, maxWidth-1)
|
t.pwindow.Move(0, maxWidth-1)
|
||||||
t.pwindow.CPrint(tui.ColSpinner, t.strong, spin)
|
t.pwindow.CPrint(tui.ColSpinner, spin)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
offsetString := fmt.Sprintf("%d/%d", t.previewer.offset+1, numLines)
|
offsetString := fmt.Sprintf("%d/%d", t.previewer.offset+1, numLines)
|
||||||
@ -1173,8 +1203,8 @@ func (t *Terminal) renderPreviewSpinner() {
|
|||||||
pos := maxWidth - t.displayWidth(offsetRunes)
|
pos := maxWidth - t.displayWidth(offsetRunes)
|
||||||
t.pwindow.Move(0, pos)
|
t.pwindow.Move(0, pos)
|
||||||
if maxWidth > 0 {
|
if maxWidth > 0 {
|
||||||
t.pwindow.CPrint(tui.ColSpinner, t.strong, spin)
|
t.pwindow.CPrint(tui.ColSpinner, spin)
|
||||||
t.pwindow.CPrint(tui.ColInfo, tui.Reverse, string(offsetRunes))
|
t.pwindow.CPrint(tui.ColInfo.WithAttr(tui.Reverse), string(offsetRunes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1185,7 +1215,7 @@ func (t *Terminal) renderPreviewText(unchanged bool) {
|
|||||||
lineNo := -t.previewer.offset
|
lineNo := -t.previewer.offset
|
||||||
height := t.pwindow.Height()
|
height := t.pwindow.Height()
|
||||||
if unchanged {
|
if unchanged {
|
||||||
t.pwindow.Move(0, 0)
|
t.pwindow.MoveAndClear(0, 0)
|
||||||
} else {
|
} else {
|
||||||
t.previewed.filled = false
|
t.previewed.filled = false
|
||||||
t.pwindow.Erase()
|
t.pwindow.Erase()
|
||||||
@ -1205,7 +1235,7 @@ func (t *Terminal) renderPreviewText(unchanged bool) {
|
|||||||
}
|
}
|
||||||
str, width := t.processTabs(trimmed, prefixWidth)
|
str, width := t.processTabs(trimmed, prefixWidth)
|
||||||
prefixWidth += width
|
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)
|
fillRet = t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
|
||||||
} else {
|
} else {
|
||||||
fillRet = t.pwindow.CFill(tui.ColPreview.Fg(), tui.ColPreview.Bg(), tui.AttrRegular, str)
|
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())
|
message := t.trimMessage("Loading ..", t.pwindow.Width())
|
||||||
pos := t.pwindow.Width() - len(message)
|
pos := t.pwindow.Width() - len(message)
|
||||||
t.pwindow.Move(0, pos)
|
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) {
|
func (t *Terminal) processTabs(runes []rune, prefixWidth int) (string, int) {
|
||||||
|
@ -15,14 +15,17 @@ func (a Attr) Merge(b Attr) Attr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AttrRegular Attr = Attr(0)
|
AttrUndefined = Attr(0)
|
||||||
Bold = Attr(1)
|
AttrRegular = Attr(1 << 7)
|
||||||
Dim = Attr(1 << 1)
|
AttrClear = Attr(1 << 8)
|
||||||
Italic = Attr(1 << 2)
|
|
||||||
Underline = Attr(1 << 3)
|
Bold = Attr(1)
|
||||||
Blink = Attr(1 << 4)
|
Dim = Attr(1 << 1)
|
||||||
Blink2 = Attr(1 << 5)
|
Italic = Attr(1 << 2)
|
||||||
Reverse = Attr(1 << 6)
|
Underline = Attr(1 << 3)
|
||||||
|
Blink = Attr(1 << 4)
|
||||||
|
Blink2 = Attr(1 << 5)
|
||||||
|
Reverse = Attr(1 << 6)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *FullscreenRenderer) Init() {}
|
func (r *FullscreenRenderer) Init() {}
|
||||||
|
@ -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 {
|
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
|
||||||
w := &LightWindow{
|
w := &LightWindow{
|
||||||
renderer: r,
|
renderer: r,
|
||||||
colored: r.theme != nil,
|
colored: r.theme.Colored,
|
||||||
preview: preview,
|
preview: preview,
|
||||||
border: borderStyle,
|
border: borderStyle,
|
||||||
top: top,
|
top: top,
|
||||||
@ -637,14 +637,12 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
|
|||||||
tabstop: r.tabstop,
|
tabstop: r.tabstop,
|
||||||
fg: colDefault,
|
fg: colDefault,
|
||||||
bg: colDefault}
|
bg: colDefault}
|
||||||
if r.theme != nil {
|
if preview {
|
||||||
if preview {
|
w.fg = r.theme.PreviewFg.Color
|
||||||
w.fg = r.theme.PreviewFg
|
w.bg = r.theme.PreviewBg.Color
|
||||||
w.bg = r.theme.PreviewBg
|
} else {
|
||||||
} else {
|
w.fg = r.theme.Fg.Color
|
||||||
w.fg = r.theme.Fg
|
w.bg = r.theme.Bg.Color
|
||||||
w.bg = r.theme.Bg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.drawBorder()
|
w.drawBorder()
|
||||||
return w
|
return w
|
||||||
@ -661,9 +659,9 @@ func (w *LightWindow) drawBorder() {
|
|||||||
|
|
||||||
func (w *LightWindow) drawBorderHorizontal() {
|
func (w *LightWindow) drawBorderHorizontal() {
|
||||||
w.Move(0, 0)
|
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.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() {
|
func (w *LightWindow) drawBorderAround() {
|
||||||
@ -672,17 +670,15 @@ func (w *LightWindow) drawBorderAround() {
|
|||||||
if w.preview {
|
if w.preview {
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
w.CPrint(color, AttrRegular,
|
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
|
||||||
string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
|
|
||||||
for y := 1; y < w.height-1; y++ {
|
for y := 1; y < w.height-1; y++ {
|
||||||
w.Move(y, 0)
|
w.Move(y, 0)
|
||||||
w.CPrint(color, AttrRegular, string(w.border.vertical))
|
w.CPrint(color, string(w.border.vertical))
|
||||||
w.CPrint(color, AttrRegular, repeat(' ', w.width-2))
|
w.CPrint(color, repeat(' ', w.width-2))
|
||||||
w.CPrint(color, AttrRegular, string(w.border.vertical))
|
w.CPrint(color, string(w.border.vertical))
|
||||||
}
|
}
|
||||||
w.Move(w.height-1, 0)
|
w.Move(w.height-1, 0)
|
||||||
w.CPrint(color, AttrRegular,
|
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
|
||||||
string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LightWindow) csi(code string) {
|
func (w *LightWindow) csi(code string) {
|
||||||
@ -745,6 +741,9 @@ func (w *LightWindow) MoveAndClear(y int, x int) {
|
|||||||
|
|
||||||
func attrCodes(attr Attr) []string {
|
func attrCodes(attr Attr) []string {
|
||||||
codes := []string{}
|
codes := []string{}
|
||||||
|
if (attr & AttrClear) > 0 {
|
||||||
|
return codes
|
||||||
|
}
|
||||||
if (attr & Bold) > 0 {
|
if (attr & Bold) > 0 {
|
||||||
codes = append(codes, "1")
|
codes = append(codes, "1")
|
||||||
}
|
}
|
||||||
@ -804,12 +803,8 @@ func cleanse(str string) string {
|
|||||||
return strings.Replace(str, "\x1b", "", -1)
|
return strings.Replace(str, "\x1b", "", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LightWindow) CPrint(pair ColorPair, attr Attr, text string) {
|
func (w *LightWindow) CPrint(pair ColorPair, text string) {
|
||||||
if !w.colored {
|
w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
|
||||||
w.csiColor(colDefault, colDefault, attrFor(pair, attr))
|
|
||||||
} else {
|
|
||||||
w.csiColor(pair.Fg(), pair.Bg(), attr)
|
|
||||||
}
|
|
||||||
w.stderrInternal(cleanse(text), false)
|
w.stderrInternal(cleanse(text), false)
|
||||||
w.csi("m")
|
w.csi("m")
|
||||||
}
|
}
|
||||||
|
@ -77,11 +77,13 @@ const (
|
|||||||
Blink = Attr(tcell.AttrBlink)
|
Blink = Attr(tcell.AttrBlink)
|
||||||
Reverse = Attr(tcell.AttrReverse)
|
Reverse = Attr(tcell.AttrReverse)
|
||||||
Underline = Attr(tcell.AttrUnderline)
|
Underline = Attr(tcell.AttrUnderline)
|
||||||
Italic = Attr(tcell.AttrNone) // Not supported
|
Italic = Attr(tcell.AttrItalic)
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AttrRegular Attr = 0
|
AttrUndefined = Attr(0)
|
||||||
|
AttrRegular = Attr(1 << 7)
|
||||||
|
AttrClear = Attr(1 << 8)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
||||||
@ -414,7 +416,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
|||||||
normal = ColPreview
|
normal = ColPreview
|
||||||
}
|
}
|
||||||
return &TcellWindow{
|
return &TcellWindow{
|
||||||
color: r.theme != nil,
|
color: r.theme.Colored,
|
||||||
preview: preview,
|
preview: preview,
|
||||||
top: top,
|
top: top,
|
||||||
left: left,
|
left: left,
|
||||||
@ -460,27 +462,23 @@ func (w *TcellWindow) MoveAndClear(y int, x int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) Print(text string) {
|
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
|
t := text
|
||||||
lx := 0
|
lx := 0
|
||||||
|
a := pair.Attr()
|
||||||
|
|
||||||
var style tcell.Style
|
style := pair.style()
|
||||||
if w.color {
|
if a&AttrClear == 0 {
|
||||||
style = pair.style().
|
style = style.
|
||||||
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
||||||
Underline(a&Attr(tcell.AttrUnderline) != 0)
|
Underline(a&Attr(tcell.AttrUnderline) != 0).
|
||||||
} else {
|
Italic(a&Attr(tcell.AttrItalic) != 0).
|
||||||
style = w.normal.style().
|
Blink(a&Attr(tcell.AttrBlink) != 0).
|
||||||
Reverse(a&Attr(tcell.AttrReverse) != 0 || pair == ColCurrent || pair == ColCurrentMatch).
|
Dim(a&Attr(tcell.AttrDim) != 0)
|
||||||
Underline(a&Attr(tcell.AttrUnderline) != 0 || pair == ColMatch || pair == ColCurrentMatch)
|
|
||||||
}
|
}
|
||||||
style = style.
|
|
||||||
Blink(a&Attr(tcell.AttrBlink) != 0).
|
|
||||||
Bold(a&Attr(tcell.AttrBold) != 0).
|
|
||||||
Dim(a&Attr(tcell.AttrDim) != 0)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if len(t) == 0 {
|
if len(t) == 0 {
|
||||||
@ -513,8 +511,8 @@ func (w *TcellWindow) printString(text string, pair ColorPair, a Attr) {
|
|||||||
w.lastX += lx
|
w.lastX += lx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) CPrint(pair ColorPair, attr Attr, text string) {
|
func (w *TcellWindow) CPrint(pair ColorPair, text string) {
|
||||||
w.printString(text, pair, attr)
|
w.printString(text, pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) fillString(text string, pair ColorPair, a Attr) FillReturn {
|
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).
|
Bold(a&Attr(tcell.AttrBold) != 0).
|
||||||
Dim(a&Attr(tcell.AttrDim) != 0).
|
Dim(a&Attr(tcell.AttrDim) != 0).
|
||||||
Reverse(a&Attr(tcell.AttrReverse) != 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 {
|
for _, r := range text {
|
||||||
if r == '\n' {
|
if r == '\n' {
|
||||||
@ -574,7 +573,7 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
|
|||||||
if bg == colDefault {
|
if bg == colDefault {
|
||||||
bg = w.normal.Bg()
|
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() {
|
func (w *TcellWindow) drawBorder() {
|
||||||
|
372
src/tui/tui.go
372
src/tui/tui.go
@ -123,6 +123,15 @@ func (c Color) is24() bool {
|
|||||||
return c > 0 && (c&(1<<24)) > 0
|
return c > 0 && (c&(1<<24)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ColorAttr struct {
|
||||||
|
Color Color
|
||||||
|
Attr Attr
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColorAttr() ColorAttr {
|
||||||
|
return ColorAttr{Color: colUndefined, Attr: AttrUndefined}
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
colUndefined Color = -2
|
colUndefined Color = -2
|
||||||
colDefault Color = -1
|
colDefault Color = -1
|
||||||
@ -148,9 +157,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ColorPair struct {
|
type ColorPair struct {
|
||||||
fg Color
|
fg Color
|
||||||
bg Color
|
bg Color
|
||||||
id int
|
attr Attr
|
||||||
}
|
}
|
||||||
|
|
||||||
func HexToColor(rrggbb string) Color {
|
func HexToColor(rrggbb string) Color {
|
||||||
@ -160,8 +169,8 @@ func HexToColor(rrggbb string) Color {
|
|||||||
return Color((1 << 24) + (r << 16) + (g << 8) + b)
|
return Color((1 << 24) + (r << 16) + (g << 8) + b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewColorPair(fg Color, bg Color) ColorPair {
|
func NewColorPair(fg Color, bg Color, attr Attr) ColorPair {
|
||||||
return ColorPair{fg, bg, -1}
|
return ColorPair{fg, bg, attr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ColorPair) Fg() Color {
|
func (p ColorPair) Fg() Color {
|
||||||
@ -172,23 +181,59 @@ func (p ColorPair) Bg() Color {
|
|||||||
return p.bg
|
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 {
|
type ColorTheme struct {
|
||||||
Fg Color
|
Colored bool
|
||||||
Bg Color
|
Input ColorAttr
|
||||||
PreviewFg Color
|
Fg ColorAttr
|
||||||
PreviewBg Color
|
Bg ColorAttr
|
||||||
DarkBg Color
|
PreviewFg ColorAttr
|
||||||
Gutter Color
|
PreviewBg ColorAttr
|
||||||
Prompt Color
|
DarkBg ColorAttr
|
||||||
Match Color
|
Gutter ColorAttr
|
||||||
Current Color
|
Prompt ColorAttr
|
||||||
CurrentMatch Color
|
Match ColorAttr
|
||||||
Spinner Color
|
Current ColorAttr
|
||||||
Info Color
|
CurrentMatch ColorAttr
|
||||||
Cursor Color
|
Spinner ColorAttr
|
||||||
Selected Color
|
Info ColorAttr
|
||||||
Header Color
|
Cursor ColorAttr
|
||||||
Border Color
|
Selected ColorAttr
|
||||||
|
Header ColorAttr
|
||||||
|
Border ColorAttr
|
||||||
}
|
}
|
||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
@ -307,7 +352,7 @@ type Window interface {
|
|||||||
Move(y int, x int)
|
Move(y int, x int)
|
||||||
MoveAndClear(y int, x int)
|
MoveAndClear(y int, x int)
|
||||||
Print(text string)
|
Print(text string)
|
||||||
CPrint(color ColorPair, attr Attr, text string)
|
CPrint(color ColorPair, text string)
|
||||||
Fill(text string) FillReturn
|
Fill(text string) FillReturn
|
||||||
CFill(fg Color, bg Color, attr Attr, text string) FillReturn
|
CFill(fg Color, bg Color, attr Attr, text string) FillReturn
|
||||||
Erase()
|
Erase()
|
||||||
@ -336,41 +381,69 @@ var (
|
|||||||
Dark256 *ColorTheme
|
Dark256 *ColorTheme
|
||||||
Light256 *ColorTheme
|
Light256 *ColorTheme
|
||||||
|
|
||||||
ColPrompt ColorPair
|
ColPrompt ColorPair
|
||||||
ColNormal ColorPair
|
ColNormal ColorPair
|
||||||
ColMatch ColorPair
|
ColInput ColorPair
|
||||||
ColCursor ColorPair
|
ColMatch ColorPair
|
||||||
ColSelected ColorPair
|
ColCursor ColorPair
|
||||||
ColCurrent ColorPair
|
ColCursorEmpty ColorPair
|
||||||
ColCurrentMatch ColorPair
|
ColSelected ColorPair
|
||||||
ColCurrentCursor ColorPair
|
ColCurrent ColorPair
|
||||||
ColCurrentSelected ColorPair
|
ColCurrentMatch ColorPair
|
||||||
ColSpinner ColorPair
|
ColCurrentCursor ColorPair
|
||||||
ColInfo ColorPair
|
ColCurrentCursorEmpty ColorPair
|
||||||
ColHeader ColorPair
|
ColCurrentSelected ColorPair
|
||||||
ColBorder ColorPair
|
ColCurrentSelectedEmpty ColorPair
|
||||||
ColPreview ColorPair
|
ColSpinner ColorPair
|
||||||
ColPreviewBorder ColorPair
|
ColInfo ColorPair
|
||||||
|
ColHeader ColorPair
|
||||||
|
ColBorder ColorPair
|
||||||
|
ColPreview ColorPair
|
||||||
|
ColPreviewBorder ColorPair
|
||||||
)
|
)
|
||||||
|
|
||||||
func EmptyTheme() *ColorTheme {
|
func EmptyTheme() *ColorTheme {
|
||||||
return &ColorTheme{
|
return &ColorTheme{
|
||||||
Fg: colUndefined,
|
Colored: true,
|
||||||
Bg: colUndefined,
|
Input: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
DarkBg: colUndefined,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: colUndefined,
|
DarkBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Match: colUndefined,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: colUndefined,
|
Prompt: ColorAttr{colUndefined, AttrUndefined},
|
||||||
CurrentMatch: colUndefined,
|
Match: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Spinner: colUndefined,
|
Current: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Info: colUndefined,
|
CurrentMatch: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Cursor: colUndefined,
|
Spinner: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Selected: colUndefined,
|
Info: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Header: colUndefined,
|
Cursor: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Border: colUndefined}
|
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) {
|
func errorExit(message string) {
|
||||||
@ -380,74 +453,80 @@ func errorExit(message string) {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Default16 = &ColorTheme{
|
Default16 = &ColorTheme{
|
||||||
Fg: colDefault,
|
Colored: true,
|
||||||
Bg: colDefault,
|
Input: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colDefault, AttrUndefined},
|
||||||
DarkBg: colBlack,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: colBlue,
|
DarkBg: ColorAttr{colBlack, AttrUndefined},
|
||||||
Match: colGreen,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: colYellow,
|
Prompt: ColorAttr{colBlue, AttrUndefined},
|
||||||
CurrentMatch: colGreen,
|
Match: ColorAttr{colGreen, AttrUndefined},
|
||||||
Spinner: colGreen,
|
Current: ColorAttr{colYellow, AttrUndefined},
|
||||||
Info: colWhite,
|
CurrentMatch: ColorAttr{colGreen, AttrUndefined},
|
||||||
Cursor: colRed,
|
Spinner: ColorAttr{colGreen, AttrUndefined},
|
||||||
Selected: colMagenta,
|
Info: ColorAttr{colWhite, AttrUndefined},
|
||||||
Header: colCyan,
|
Cursor: ColorAttr{colRed, AttrUndefined},
|
||||||
Border: colBlack}
|
Selected: ColorAttr{colMagenta, AttrUndefined},
|
||||||
|
Header: ColorAttr{colCyan, AttrUndefined},
|
||||||
|
Border: ColorAttr{colBlack, AttrUndefined}}
|
||||||
Dark256 = &ColorTheme{
|
Dark256 = &ColorTheme{
|
||||||
Fg: colDefault,
|
Colored: true,
|
||||||
Bg: colDefault,
|
Input: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colDefault, AttrUndefined},
|
||||||
DarkBg: 236,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: 110,
|
DarkBg: ColorAttr{236, AttrUndefined},
|
||||||
Match: 108,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: 254,
|
Prompt: ColorAttr{110, AttrUndefined},
|
||||||
CurrentMatch: 151,
|
Match: ColorAttr{108, AttrUndefined},
|
||||||
Spinner: 148,
|
Current: ColorAttr{254, AttrUndefined},
|
||||||
Info: 144,
|
CurrentMatch: ColorAttr{151, AttrUndefined},
|
||||||
Cursor: 161,
|
Spinner: ColorAttr{148, AttrUndefined},
|
||||||
Selected: 168,
|
Info: ColorAttr{144, AttrUndefined},
|
||||||
Header: 109,
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Border: 59}
|
Selected: ColorAttr{168, AttrUndefined},
|
||||||
|
Header: ColorAttr{109, AttrUndefined},
|
||||||
|
Border: ColorAttr{59, AttrUndefined}}
|
||||||
Light256 = &ColorTheme{
|
Light256 = &ColorTheme{
|
||||||
Fg: colDefault,
|
Colored: true,
|
||||||
Bg: colDefault,
|
Input: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewFg: colUndefined,
|
Fg: ColorAttr{colDefault, AttrUndefined},
|
||||||
PreviewBg: colUndefined,
|
Bg: ColorAttr{colDefault, AttrUndefined},
|
||||||
DarkBg: 251,
|
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Gutter: colUndefined,
|
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Prompt: 25,
|
DarkBg: ColorAttr{251, AttrUndefined},
|
||||||
Match: 66,
|
Gutter: ColorAttr{colUndefined, AttrUndefined},
|
||||||
Current: 237,
|
Prompt: ColorAttr{25, AttrUndefined},
|
||||||
CurrentMatch: 23,
|
Match: ColorAttr{66, AttrUndefined},
|
||||||
Spinner: 65,
|
Current: ColorAttr{237, AttrUndefined},
|
||||||
Info: 101,
|
CurrentMatch: ColorAttr{23, AttrUndefined},
|
||||||
Cursor: 161,
|
Spinner: ColorAttr{65, AttrUndefined},
|
||||||
Selected: 168,
|
Info: ColorAttr{101, AttrUndefined},
|
||||||
Header: 31,
|
Cursor: ColorAttr{161, AttrUndefined},
|
||||||
Border: 145}
|
Selected: ColorAttr{168, AttrUndefined},
|
||||||
|
Header: ColorAttr{31, AttrUndefined},
|
||||||
|
Border: ColorAttr{145, AttrUndefined}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
||||||
if theme == nil {
|
|
||||||
initPalette(theme)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if forceBlack {
|
if forceBlack {
|
||||||
theme.Bg = colBlack
|
theme.Bg = ColorAttr{colBlack, AttrUndefined}
|
||||||
}
|
}
|
||||||
|
|
||||||
o := func(a Color, b Color) Color {
|
o := func(a ColorAttr, b ColorAttr) ColorAttr {
|
||||||
if b == colUndefined {
|
c := a
|
||||||
return 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.Fg = o(baseTheme.Fg, theme.Fg)
|
||||||
theme.Bg = o(baseTheme.Bg, theme.Bg)
|
theme.Bg = o(baseTheme.Bg, theme.Bg)
|
||||||
theme.PreviewFg = o(theme.Fg, o(baseTheme.PreviewFg, theme.PreviewFg))
|
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) {
|
func initPalette(theme *ColorTheme) {
|
||||||
idx := 0
|
pair := func(fg, bg ColorAttr) ColorPair {
|
||||||
pair := func(fg, bg Color) ColorPair {
|
return ColorPair{fg.Color, bg.Color, fg.Attr}
|
||||||
idx++
|
|
||||||
return ColorPair{fg, bg, idx}
|
|
||||||
}
|
}
|
||||||
if theme != nil {
|
blank := theme.Fg
|
||||||
ColPrompt = pair(theme.Prompt, theme.Bg)
|
blank.Attr = AttrRegular
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func attrFor(color ColorPair, attr Attr) Attr {
|
ColPrompt = pair(theme.Prompt, theme.Bg)
|
||||||
switch color {
|
ColNormal = pair(theme.Fg, theme.Bg)
|
||||||
case ColCurrent:
|
ColInput = pair(theme.Input, theme.Bg)
|
||||||
return attr | Reverse
|
ColMatch = pair(theme.Match, theme.Bg)
|
||||||
case ColMatch:
|
ColCursor = pair(theme.Cursor, theme.Gutter)
|
||||||
return attr | Underline
|
ColCursorEmpty = pair(blank, theme.Gutter)
|
||||||
case ColCurrentMatch:
|
ColSelected = pair(theme.Selected, theme.Gutter)
|
||||||
return attr | Underline | Reverse
|
ColCurrent = pair(theme.Current, theme.DarkBg)
|
||||||
}
|
ColCurrentMatch = pair(theme.CurrentMatch, theme.DarkBg)
|
||||||
return attr
|
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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user