mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-16 18:25:08 +00:00
Add support for more ANSI color attributes (#674)
Dim, underline, blink, reverse
This commit is contained in:
parent
1acd2adce2
commit
1fc5659842
24
src/ansi.go
24
src/ansi.go
@ -6,6 +6,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/curses"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ansiOffset struct {
|
type ansiOffset struct {
|
||||||
@ -16,18 +18,18 @@ type ansiOffset struct {
|
|||||||
type ansiState struct {
|
type ansiState struct {
|
||||||
fg int
|
fg int
|
||||||
bg int
|
bg int
|
||||||
bold bool
|
attr curses.Attr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ansiState) colored() bool {
|
func (s *ansiState) colored() bool {
|
||||||
return s.fg != -1 || s.bg != -1 || s.bold
|
return s.fg != -1 || s.bg != -1 || s.attr > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ansiState) equals(t *ansiState) bool {
|
func (s *ansiState) equals(t *ansiState) bool {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return !s.colored()
|
return !s.colored()
|
||||||
}
|
}
|
||||||
return s.fg == t.fg && s.bg == t.bg && s.bold == t.bold
|
return s.fg == t.fg && s.bg == t.bg && s.attr == t.attr
|
||||||
}
|
}
|
||||||
|
|
||||||
var ansiRegex *regexp.Regexp
|
var ansiRegex *regexp.Regexp
|
||||||
@ -94,9 +96,9 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
// State
|
// State
|
||||||
var state *ansiState
|
var state *ansiState
|
||||||
if prevState == nil {
|
if prevState == nil {
|
||||||
state = &ansiState{-1, -1, false}
|
state = &ansiState{-1, -1, 0}
|
||||||
} else {
|
} else {
|
||||||
state = &ansiState{prevState.fg, prevState.bg, prevState.bold}
|
state = &ansiState{prevState.fg, prevState.bg, prevState.attr}
|
||||||
}
|
}
|
||||||
if ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
|
if ansiCode[1] != '[' || ansiCode[len(ansiCode)-1] != 'm' {
|
||||||
return state
|
return state
|
||||||
@ -108,7 +110,7 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
init := func() {
|
init := func() {
|
||||||
state.fg = -1
|
state.fg = -1
|
||||||
state.bg = -1
|
state.bg = -1
|
||||||
state.bold = false
|
state.attr = 0
|
||||||
state256 = 0
|
state256 = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +134,15 @@ func interpretCode(ansiCode string, prevState *ansiState) *ansiState {
|
|||||||
case 49:
|
case 49:
|
||||||
state.bg = -1
|
state.bg = -1
|
||||||
case 1:
|
case 1:
|
||||||
state.bold = true
|
state.attr = curses.Bold
|
||||||
|
case 2:
|
||||||
|
state.attr = curses.Dim
|
||||||
|
case 4:
|
||||||
|
state.attr = curses.Underline
|
||||||
|
case 5:
|
||||||
|
state.attr = curses.Blink
|
||||||
|
case 7:
|
||||||
|
state.attr = curses.Reverse
|
||||||
case 0:
|
case 0:
|
||||||
init()
|
init()
|
||||||
default:
|
default:
|
||||||
|
@ -23,6 +23,16 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Bold = C.A_BOLD
|
||||||
|
Dim = C.A_DIM
|
||||||
|
Blink = C.A_BLINK
|
||||||
|
Reverse = C.A_REVERSE
|
||||||
|
Underline = C.A_UNDERLINE
|
||||||
|
)
|
||||||
|
|
||||||
|
type Attr C.int
|
||||||
|
|
||||||
// Types of user action
|
// Types of user action
|
||||||
const (
|
const (
|
||||||
Rune = iota
|
Rune = iota
|
||||||
@ -158,7 +168,7 @@ type MouseEvent struct {
|
|||||||
var (
|
var (
|
||||||
_buf []byte
|
_buf []byte
|
||||||
_in *os.File
|
_in *os.File
|
||||||
_color func(int, bool) C.int
|
_color func(int, Attr) C.int
|
||||||
_colorMap map[int]int
|
_colorMap map[int]int
|
||||||
_prevDownTime time.Time
|
_prevDownTime time.Time
|
||||||
_clickY []int
|
_clickY []int
|
||||||
@ -183,7 +193,7 @@ type Window struct {
|
|||||||
func NewWindow(top int, left int, width int, height int, border bool) *Window {
|
func NewWindow(top int, left int, width int, height int, border bool) *Window {
|
||||||
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
|
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
|
||||||
if border {
|
if border {
|
||||||
attr := _color(ColBorder, false)
|
attr := _color(ColBorder, 0)
|
||||||
C.wattron(win, attr)
|
C.wattron(win, attr)
|
||||||
C.box(win, 0, 0)
|
C.box(win, 0, 0)
|
||||||
C.wattroff(win, attr)
|
C.wattroff(win, attr)
|
||||||
@ -266,22 +276,19 @@ func init() {
|
|||||||
Border: 145}
|
Border: 145}
|
||||||
}
|
}
|
||||||
|
|
||||||
func attrColored(pair int, bold bool) C.int {
|
func attrColored(pair int, a Attr) C.int {
|
||||||
var attr C.int
|
var attr C.int
|
||||||
if pair > ColNormal {
|
if pair > ColNormal {
|
||||||
attr = C.COLOR_PAIR(C.int(pair))
|
attr = C.COLOR_PAIR(C.int(pair))
|
||||||
}
|
}
|
||||||
if bold {
|
return attr | C.int(a)
|
||||||
attr = attr | C.A_BOLD
|
|
||||||
}
|
|
||||||
return attr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func attrMono(pair int, bold bool) C.int {
|
func attrMono(pair int, a Attr) C.int {
|
||||||
var attr C.int
|
var attr C.int
|
||||||
switch pair {
|
switch pair {
|
||||||
case ColCurrent:
|
case ColCurrent:
|
||||||
if bold {
|
if a&C.A_BOLD == C.A_BOLD {
|
||||||
attr = C.A_REVERSE
|
attr = C.A_REVERSE
|
||||||
}
|
}
|
||||||
case ColMatch:
|
case ColMatch:
|
||||||
@ -289,7 +296,7 @@ func attrMono(pair int, bold bool) C.int {
|
|||||||
case ColCurrentMatch:
|
case ColCurrentMatch:
|
||||||
attr = C.A_UNDERLINE | C.A_REVERSE
|
attr = C.A_UNDERLINE | C.A_REVERSE
|
||||||
}
|
}
|
||||||
if bold {
|
if a&C.A_BOLD == C.A_BOLD {
|
||||||
attr = attr | C.A_BOLD
|
attr = attr | C.A_BOLD
|
||||||
}
|
}
|
||||||
return attr
|
return attr
|
||||||
@ -648,8 +655,8 @@ func (w *Window) Print(text string) {
|
|||||||
}, text)))
|
}, text)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) CPrint(pair int, bold bool, text string) {
|
func (w *Window) CPrint(pair int, a Attr, text string) {
|
||||||
attr := _color(pair, bold)
|
attr := _color(pair, a)
|
||||||
C.wattron(w.win, attr)
|
C.wattron(w.win, attr)
|
||||||
w.Print(text)
|
w.Print(text)
|
||||||
C.wattroff(w.win, attr)
|
C.wattroff(w.win, attr)
|
||||||
@ -675,8 +682,8 @@ func (w *Window) Fill(str string) bool {
|
|||||||
return C.waddstr(w.win, C.CString(str)) == C.OK
|
return C.waddstr(w.win, C.CString(str)) == C.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) CFill(str string, fg int, bg int, bold bool) bool {
|
func (w *Window) CFill(str string, fg int, bg int, a Attr) bool {
|
||||||
attr := _color(PairFor(fg, bg), bold)
|
attr := _color(PairFor(fg, bg), a)
|
||||||
C.wattron(w.win, attr)
|
C.wattron(w.win, attr)
|
||||||
ret := w.Fill(str)
|
ret := w.Fill(str)
|
||||||
C.wattroff(w.win, attr)
|
C.wattroff(w.win, attr)
|
||||||
|
@ -14,7 +14,7 @@ type Offset [2]int32
|
|||||||
type colorOffset struct {
|
type colorOffset struct {
|
||||||
offset [2]int32
|
offset [2]int32
|
||||||
color int
|
color int
|
||||||
bold bool
|
attr curses.Attr
|
||||||
index int32
|
index int32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +91,14 @@ func minRank() rank {
|
|||||||
return rank{index: 0, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
|
return rank{index: 0, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (result *Result) colorOffsets(matchOffsets []Offset, color int, bold bool, current bool) []colorOffset {
|
func (result *Result) colorOffsets(matchOffsets []Offset, color int, attr curses.Attr, current bool) []colorOffset {
|
||||||
itemColors := result.item.Colors()
|
itemColors := result.item.Colors()
|
||||||
|
|
||||||
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, bold: bold})
|
offsets = append(offsets, colorOffset{offset: [2]int32{off[0], off[1]}, color: color, attr: attr})
|
||||||
}
|
}
|
||||||
return offsets
|
return offsets
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ func (result *Result) colorOffsets(matchOffsets []Offset, color int, bold bool,
|
|||||||
if curr != 0 && idx > start {
|
if curr != 0 && idx > start {
|
||||||
if curr == -1 {
|
if curr == -1 {
|
||||||
colors = append(colors, colorOffset{
|
colors = append(colors, colorOffset{
|
||||||
offset: [2]int32{int32(start), int32(idx)}, color: color, bold: bold})
|
offset: [2]int32{int32(start), int32(idx)}, color: color, attr: attr})
|
||||||
} else {
|
} else {
|
||||||
ansi := itemColors[curr-1]
|
ansi := itemColors[curr-1]
|
||||||
fg := ansi.color.fg
|
fg := ansi.color.fg
|
||||||
@ -164,7 +164,7 @@ func (result *Result) colorOffsets(matchOffsets []Offset, color int, bold bool,
|
|||||||
colors = append(colors, colorOffset{
|
colors = append(colors, colorOffset{
|
||||||
offset: [2]int32{int32(start), int32(idx)},
|
offset: [2]int32{int32(start), int32(idx)},
|
||||||
color: curses.PairFor(fg, bg),
|
color: curses.PairFor(fg, bg),
|
||||||
bold: ansi.color.bold || bold})
|
attr: ansi.color.attr | attr})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,24 +526,24 @@ func (t *Terminal) placeCursor() {
|
|||||||
|
|
||||||
func (t *Terminal) printPrompt() {
|
func (t *Terminal) printPrompt() {
|
||||||
t.move(0, 0, true)
|
t.move(0, 0, true)
|
||||||
t.window.CPrint(C.ColPrompt, true, t.prompt)
|
t.window.CPrint(C.ColPrompt, C.Bold, t.prompt)
|
||||||
t.window.CPrint(C.ColNormal, true, string(t.input))
|
t.window.CPrint(C.ColNormal, C.Bold, string(t.input))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printInfo() {
|
func (t *Terminal) printInfo() {
|
||||||
if t.inlineInfo {
|
if t.inlineInfo {
|
||||||
t.move(0, displayWidth([]rune(t.prompt))+displayWidth(t.input)+1, true)
|
t.move(0, displayWidth([]rune(t.prompt))+displayWidth(t.input)+1, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
t.window.CPrint(C.ColSpinner, true, " < ")
|
t.window.CPrint(C.ColSpinner, C.Bold, " < ")
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(C.ColPrompt, true, " < ")
|
t.window.CPrint(C.ColPrompt, C.Bold, " < ")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.move(1, 0, true)
|
t.move(1, 0, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
duration := int64(spinnerDuration)
|
duration := int64(spinnerDuration)
|
||||||
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
|
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
|
||||||
t.window.CPrint(C.ColSpinner, true, _spinner[idx])
|
t.window.CPrint(C.ColSpinner, C.Bold, _spinner[idx])
|
||||||
}
|
}
|
||||||
t.move(1, 2, false)
|
t.move(1, 2, false)
|
||||||
}
|
}
|
||||||
@ -562,7 +562,7 @@ func (t *Terminal) printInfo() {
|
|||||||
if t.progress > 0 && t.progress < 100 {
|
if t.progress > 0 && t.progress < 100 {
|
||||||
output += fmt.Sprintf(" (%d%%)", t.progress)
|
output += fmt.Sprintf(" (%d%%)", t.progress)
|
||||||
}
|
}
|
||||||
t.window.CPrint(C.ColInfo, false, output)
|
t.window.CPrint(C.ColInfo, 0, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printHeader() {
|
func (t *Terminal) printHeader() {
|
||||||
@ -586,7 +586,7 @@ func (t *Terminal) printHeader() {
|
|||||||
colors: colors}
|
colors: colors}
|
||||||
|
|
||||||
t.move(line, 2, true)
|
t.move(line, 2, true)
|
||||||
t.printHighlighted(&Result{item: item}, false, C.ColHeader, 0, false)
|
t.printHighlighted(&Result{item: item}, 0, C.ColHeader, 0, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -620,21 +620,21 @@ func (t *Terminal) printItem(result *Result, i int, current bool) {
|
|||||||
} else if current {
|
} else if current {
|
||||||
label = ">"
|
label = ">"
|
||||||
}
|
}
|
||||||
t.window.CPrint(C.ColCursor, true, label)
|
t.window.CPrint(C.ColCursor, C.Bold, label)
|
||||||
if current {
|
if current {
|
||||||
if selected {
|
if selected {
|
||||||
t.window.CPrint(C.ColSelected, true, ">")
|
t.window.CPrint(C.ColSelected, C.Bold, ">")
|
||||||
} else {
|
} else {
|
||||||
t.window.CPrint(C.ColCurrent, true, " ")
|
t.window.CPrint(C.ColCurrent, C.Bold, " ")
|
||||||
}
|
}
|
||||||
t.printHighlighted(result, true, C.ColCurrent, C.ColCurrentMatch, true)
|
t.printHighlighted(result, C.Bold, C.ColCurrent, C.ColCurrentMatch, true)
|
||||||
} else {
|
} else {
|
||||||
if selected {
|
if selected {
|
||||||
t.window.CPrint(C.ColSelected, true, ">")
|
t.window.CPrint(C.ColSelected, C.Bold, ">")
|
||||||
} else {
|
} else {
|
||||||
t.window.Print(" ")
|
t.window.Print(" ")
|
||||||
}
|
}
|
||||||
t.printHighlighted(result, false, 0, C.ColMatch, false)
|
t.printHighlighted(result, 0, 0, C.ColMatch, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,7 +690,7 @@ func overflow(runes []rune, max int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 int, current bool) {
|
func (t *Terminal) printHighlighted(result *Result, attr C.Attr, col1 int, col2 int, current bool) {
|
||||||
item := result.item
|
item := result.item
|
||||||
|
|
||||||
// Overflow
|
// Overflow
|
||||||
@ -715,7 +715,7 @@ func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 in
|
|||||||
maxe = util.Max(maxe, int(offset[1]))
|
maxe = util.Max(maxe, int(offset[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
offsets := result.colorOffsets(charOffsets, col2, bold, current)
|
offsets := result.colorOffsets(charOffsets, col2, attr, current)
|
||||||
maxWidth := t.window.Width - 3
|
maxWidth := t.window.Width - 3
|
||||||
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))
|
||||||
if overflow(text, maxWidth) {
|
if overflow(text, maxWidth) {
|
||||||
@ -764,11 +764,11 @@ func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 in
|
|||||||
e := util.Constrain32(offset.offset[1], index, maxOffset)
|
e := util.Constrain32(offset.offset[1], index, maxOffset)
|
||||||
|
|
||||||
substr, prefixWidth = processTabs(text[index:b], prefixWidth)
|
substr, prefixWidth = processTabs(text[index:b], prefixWidth)
|
||||||
t.window.CPrint(col1, bold, substr)
|
t.window.CPrint(col1, attr, substr)
|
||||||
|
|
||||||
if b < e {
|
if b < e {
|
||||||
substr, prefixWidth = processTabs(text[b:e], prefixWidth)
|
substr, prefixWidth = processTabs(text[b:e], prefixWidth)
|
||||||
t.window.CPrint(offset.color, offset.bold, substr)
|
t.window.CPrint(offset.color, offset.attr, substr)
|
||||||
}
|
}
|
||||||
|
|
||||||
index = e
|
index = e
|
||||||
@ -778,7 +778,7 @@ func (t *Terminal) printHighlighted(result *Result, bold bool, col1 int, col2 in
|
|||||||
}
|
}
|
||||||
if index < maxOffset {
|
if index < maxOffset {
|
||||||
substr, _ = processTabs(text[index:], prefixWidth)
|
substr, _ = processTabs(text[index:], prefixWidth)
|
||||||
t.window.CPrint(col1, bold, substr)
|
t.window.CPrint(col1, attr, substr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -812,7 +812,7 @@ func (t *Terminal) printPreview() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ansi != nil && ansi.colored() {
|
if ansi != nil && ansi.colored() {
|
||||||
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.bold)
|
return t.pwindow.CFill(str, ansi.fg, ansi.bg, ansi.attr)
|
||||||
}
|
}
|
||||||
return t.pwindow.Fill(str)
|
return t.pwindow.Fill(str)
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user