mirror of
https://github.com/junegunn/fzf.git
synced 2026-08-02 06:00:32 +08:00
Add underline style variants and underline color support
Support double, curly, dotted, and dashed underline styles via --color (e.g. underline-curly) and ANSI passthrough (SGR 4:N, 58, 59) with --ansi. Close #4633 Close #4678 Thanks to @shtse8 for the test cases.
This commit is contained in:
+37
-7
@@ -1323,7 +1323,18 @@ func attrCodes(attr Attr) []string {
|
||||
codes = append(codes, "3")
|
||||
}
|
||||
if (attr & Underline) > 0 {
|
||||
codes = append(codes, "4")
|
||||
switch attr.UnderlineStyle() {
|
||||
case UlStyleDouble:
|
||||
codes = append(codes, "4:2")
|
||||
case UlStyleCurly:
|
||||
codes = append(codes, "4:3")
|
||||
case UlStyleDotted:
|
||||
codes = append(codes, "4:4")
|
||||
case UlStyleDashed:
|
||||
codes = append(codes, "4:5")
|
||||
default:
|
||||
codes = append(codes, "4")
|
||||
}
|
||||
}
|
||||
if (attr & Blink) > 0 {
|
||||
codes = append(codes, "5")
|
||||
@@ -1361,8 +1372,27 @@ func colorCodes(fg Color, bg Color) []string {
|
||||
return codes
|
||||
}
|
||||
|
||||
func (w *LightWindow) csiColor(fg Color, bg Color, attr Attr) (bool, string) {
|
||||
func ulColorCode(c Color) string {
|
||||
if c == colDefault {
|
||||
return ""
|
||||
}
|
||||
if c.is24() {
|
||||
r := (c >> 16) & 0xff
|
||||
g := (c >> 8) & 0xff
|
||||
b := (c) & 0xff
|
||||
return fmt.Sprintf("58;2;%d;%d;%d", r, g, b)
|
||||
}
|
||||
if c >= 0 && c < 256 {
|
||||
return fmt.Sprintf("58;5;%d", c)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (w *LightWindow) csiColor(fg Color, bg Color, ul Color, attr Attr) (bool, string) {
|
||||
codes := append(attrCodes(attr), colorCodes(fg, bg)...)
|
||||
if ulCode := ulColorCode(ul); ulCode != "" {
|
||||
codes = append(codes, ulCode)
|
||||
}
|
||||
code := w.csi(";" + strings.Join(codes, ";") + "m")
|
||||
return len(codes) > 0, code
|
||||
}
|
||||
@@ -1376,13 +1406,13 @@ func cleanse(str string) string {
|
||||
}
|
||||
|
||||
func (w *LightWindow) CPrint(pair ColorPair, text string) {
|
||||
_, code := w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
|
||||
_, code := w.csiColor(pair.Fg(), pair.Bg(), pair.Ul(), pair.Attr())
|
||||
w.stderrInternal(cleanse(text), false, code)
|
||||
w.csi("0m")
|
||||
}
|
||||
|
||||
func (w *LightWindow) cprint2(fg Color, bg Color, attr Attr, text string) {
|
||||
hasColors, code := w.csiColor(fg, bg, attr)
|
||||
hasColors, code := w.csiColor(fg, bg, colDefault, attr)
|
||||
if hasColors {
|
||||
defer w.csi("0m")
|
||||
}
|
||||
@@ -1472,7 +1502,7 @@ func (w *LightWindow) fill(str string, resetCode string) FillReturn {
|
||||
|
||||
func (w *LightWindow) setBg() string {
|
||||
if w.bg != colDefault {
|
||||
_, code := w.csiColor(colDefault, w.bg, AttrRegular)
|
||||
_, code := w.csiColor(colDefault, w.bg, colDefault, AttrRegular)
|
||||
return code
|
||||
}
|
||||
// Should clear dim attribute after ␍ in the preview window
|
||||
@@ -1494,7 +1524,7 @@ func (w *LightWindow) Fill(text string) FillReturn {
|
||||
return w.fill(text, code)
|
||||
}
|
||||
|
||||
func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillReturn {
|
||||
func (w *LightWindow) CFill(fg Color, bg Color, ul Color, attr Attr, text string) FillReturn {
|
||||
w.Move(w.posy, w.posx)
|
||||
if fg == colDefault {
|
||||
fg = w.fg
|
||||
@@ -1502,7 +1532,7 @@ func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillRetu
|
||||
if bg == colDefault {
|
||||
bg = w.bg
|
||||
}
|
||||
if hasColors, resetCode := w.csiColor(fg, bg, attr); hasColors {
|
||||
if hasColors, resetCode := w.csiColor(fg, bg, ul, attr); hasColors {
|
||||
defer w.csi("0m")
|
||||
return w.fill(text, resetCode)
|
||||
}
|
||||
|
||||
+33
-4
@@ -825,6 +825,21 @@ func (w *TcellWindow) withUrl(style tcell.Style) tcell.Style {
|
||||
return style
|
||||
}
|
||||
|
||||
func underlineStyleFromAttr(a Attr) tcell.UnderlineStyle {
|
||||
switch a.UnderlineStyle() {
|
||||
case UlStyleDouble:
|
||||
return tcell.UnderlineStyleDouble
|
||||
case UlStyleCurly:
|
||||
return tcell.UnderlineStyleCurly
|
||||
case UlStyleDotted:
|
||||
return tcell.UnderlineStyleDotted
|
||||
case UlStyleDashed:
|
||||
return tcell.UnderlineStyleDashed
|
||||
default:
|
||||
return tcell.UnderlineStyleSolid
|
||||
}
|
||||
}
|
||||
|
||||
func (w *TcellWindow) printString(text string, pair ColorPair) {
|
||||
lx := 0
|
||||
a := pair.Attr()
|
||||
@@ -833,11 +848,18 @@ func (w *TcellWindow) printString(text string, pair ColorPair) {
|
||||
if a&AttrClear == 0 {
|
||||
style = style.
|
||||
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
||||
Underline(a&Attr(tcell.AttrUnderline) != 0).
|
||||
StrikeThrough(a&Attr(tcell.AttrStrikeThrough) != 0).
|
||||
Italic(a&Attr(tcell.AttrItalic) != 0).
|
||||
Blink(a&Attr(tcell.AttrBlink) != 0).
|
||||
Dim(a&Attr(tcell.AttrDim) != 0)
|
||||
if a&Attr(tcell.AttrUnderline) != 0 {
|
||||
style = style.Underline(underlineStyleFromAttr(a))
|
||||
if pair.Ul() != colDefault {
|
||||
style = style.Underline(asTcellColor(pair.Ul()))
|
||||
}
|
||||
} else {
|
||||
style = style.Underline(false)
|
||||
}
|
||||
}
|
||||
style = w.withUrl(style)
|
||||
|
||||
@@ -887,9 +909,16 @@ func (w *TcellWindow) fillString(text string, pair ColorPair) FillReturn {
|
||||
Bold(a&Attr(tcell.AttrBold) != 0 || a&BoldForce != 0).
|
||||
Dim(a&Attr(tcell.AttrDim) != 0).
|
||||
Reverse(a&Attr(tcell.AttrReverse) != 0).
|
||||
Underline(a&Attr(tcell.AttrUnderline) != 0).
|
||||
StrikeThrough(a&Attr(tcell.AttrStrikeThrough) != 0).
|
||||
Italic(a&Attr(tcell.AttrItalic) != 0)
|
||||
if a&Attr(tcell.AttrUnderline) != 0 {
|
||||
style = style.Underline(underlineStyleFromAttr(a))
|
||||
if pair.Ul() != colDefault {
|
||||
style = style.Underline(asTcellColor(pair.Ul()))
|
||||
}
|
||||
} else {
|
||||
style = style.Underline(false)
|
||||
}
|
||||
style = w.withUrl(style)
|
||||
|
||||
gr := uniseg.NewGraphemes(text)
|
||||
@@ -967,14 +996,14 @@ func (w *TcellWindow) LinkEnd() {
|
||||
w.params = nil
|
||||
}
|
||||
|
||||
func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
|
||||
func (w *TcellWindow) CFill(fg Color, bg Color, ul Color, a Attr, str string) FillReturn {
|
||||
if fg == colDefault {
|
||||
fg = w.normal.Fg()
|
||||
}
|
||||
if bg == colDefault {
|
||||
bg = w.normal.Bg()
|
||||
}
|
||||
return w.fillString(str, NewColorPair(fg, bg, a))
|
||||
return w.fillString(str, NewColorPair(fg, bg, a).WithUl(ul))
|
||||
}
|
||||
|
||||
func (w *TcellWindow) DrawBorder() {
|
||||
|
||||
+40
-7
@@ -17,15 +17,34 @@ const (
|
||||
BoldForce = Attr(1 << 10)
|
||||
FullBg = Attr(1 << 11)
|
||||
Strip = Attr(1 << 12)
|
||||
|
||||
// Underline style stored in bits 13-15 (3 bits, values 0-4)
|
||||
// Only meaningful when the Underline attribute bit is also set.
|
||||
// 0 = solid (default)
|
||||
UnderlineStyleShift = 13
|
||||
UnderlineStyleMask = Attr(0b111 << UnderlineStyleShift)
|
||||
UlStyleDouble = Attr(0b001 << UnderlineStyleShift)
|
||||
UlStyleCurly = Attr(0b010 << UnderlineStyleShift)
|
||||
UlStyleDotted = Attr(0b011 << UnderlineStyleShift)
|
||||
UlStyleDashed = Attr(0b100 << UnderlineStyleShift)
|
||||
)
|
||||
|
||||
func (a Attr) UnderlineStyle() Attr {
|
||||
return a & UnderlineStyleMask
|
||||
}
|
||||
|
||||
func (a Attr) Merge(b Attr) Attr {
|
||||
if b&AttrRegular > 0 {
|
||||
// Only keep bold attribute set by the system
|
||||
return (b &^ AttrRegular) | (a & BoldForce)
|
||||
}
|
||||
|
||||
return (a &^ AttrRegular) | b
|
||||
merged := (a &^ AttrRegular) | b
|
||||
// When b sets Underline, use b's underline style instead of OR'ing
|
||||
if b&Underline > 0 {
|
||||
merged = (merged &^ UnderlineStyleMask) | (b & UnderlineStyleMask)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// Types of user action
|
||||
@@ -352,6 +371,7 @@ const (
|
||||
type ColorPair struct {
|
||||
fg Color
|
||||
bg Color
|
||||
ul Color
|
||||
attr Attr
|
||||
}
|
||||
|
||||
@@ -363,11 +383,11 @@ func HexToColor(rrggbb string) Color {
|
||||
}
|
||||
|
||||
func NewColorPair(fg Color, bg Color, attr Attr) ColorPair {
|
||||
return ColorPair{fg, bg, attr}
|
||||
return ColorPair{fg, bg, colDefault, attr}
|
||||
}
|
||||
|
||||
func NoColorPair() ColorPair {
|
||||
return ColorPair{-1, -1, 0}
|
||||
return ColorPair{-1, -1, -1, 0}
|
||||
}
|
||||
|
||||
func (p ColorPair) Fg() Color {
|
||||
@@ -378,6 +398,16 @@ func (p ColorPair) Bg() Color {
|
||||
return p.bg
|
||||
}
|
||||
|
||||
func (p ColorPair) Ul() Color {
|
||||
return p.ul
|
||||
}
|
||||
|
||||
func (p ColorPair) WithUl(ul Color) ColorPair {
|
||||
dup := p
|
||||
dup.ul = ul
|
||||
return dup
|
||||
}
|
||||
|
||||
func (p ColorPair) Attr() Attr {
|
||||
return p.attr
|
||||
}
|
||||
@@ -404,6 +434,9 @@ func (p ColorPair) merge(other ColorPair, except Color) ColorPair {
|
||||
if other.bg != except {
|
||||
dup.bg = other.bg
|
||||
}
|
||||
if other.ul != except {
|
||||
dup.ul = other.ul
|
||||
}
|
||||
return dup
|
||||
}
|
||||
|
||||
@@ -415,13 +448,13 @@ func (p ColorPair) WithAttr(attr Attr) ColorPair {
|
||||
|
||||
func (p ColorPair) WithFg(fg ColorAttr) ColorPair {
|
||||
dup := p
|
||||
fgPair := ColorPair{fg.Color, colUndefined, fg.Attr}
|
||||
fgPair := ColorPair{fg.Color, colUndefined, colUndefined, fg.Attr}
|
||||
return dup.Merge(fgPair)
|
||||
}
|
||||
|
||||
func (p ColorPair) WithBg(bg ColorAttr) ColorPair {
|
||||
dup := p
|
||||
bgPair := ColorPair{colUndefined, bg.Color, bg.Attr}
|
||||
bgPair := ColorPair{colUndefined, bg.Color, colUndefined, bg.Attr}
|
||||
return dup.Merge(bgPair)
|
||||
}
|
||||
|
||||
@@ -783,7 +816,7 @@ type Window interface {
|
||||
Print(text string)
|
||||
CPrint(color ColorPair, text string)
|
||||
Fill(text string) FillReturn
|
||||
CFill(fg Color, bg Color, attr Attr, text string) FillReturn
|
||||
CFill(fg Color, bg Color, ul Color, attr Attr, text string) FillReturn
|
||||
LinkBegin(uri string, params string)
|
||||
LinkEnd()
|
||||
Erase()
|
||||
@@ -1271,7 +1304,7 @@ func initPalette(theme *ColorTheme) {
|
||||
if fg.Color == colDefault && (fg.Attr&Reverse) > 0 {
|
||||
bg.Color = colDefault
|
||||
}
|
||||
return ColorPair{fg.Color, bg.Color, fg.Attr}
|
||||
return ColorPair{fg.Color, bg.Color, colDefault, fg.Attr}
|
||||
}
|
||||
blank := theme.ListFg
|
||||
blank.Attr = AttrRegular
|
||||
|
||||
Reference in New Issue
Block a user