m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-13 05:43:48 -05:00

Add special 'strip' style attribute for stripping colors

Test cases:
  fd --color always | fzf --ansi --delimiter /
  fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim,nth:regular
  fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim:strip,nth:regular
  fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim:strip,nth:regular --raw
  fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim:strip,nth:regular,hidden:strikethrough --raw
  fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim:strip,nth:regular,hidden:strip:strikethrough --raw
  fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim:strip,nth:regular,hidden:strip:dim:strikethrough --raw
This commit is contained in:
Junegunn Choi
2025-10-01 23:47:09 +09:00
parent e6ad01fb90
commit 91beacf0f4
7 changed files with 40 additions and 23 deletions

View File

@@ -50,6 +50,14 @@ fzf --raw --color hidden:red:strikethrough:dim
fzf --raw --color hidden:red:strikethrough:dim:italic fzf --raw --color hidden:red:strikethrough:dim:italic
``` ```
For colored input, dimming alone may not be enough, and you may prefer to remove
colors entirely. For that case, a new special style attribute `strip` has been
added.
```sh
fd --color always | fzf --ansi --raw --color hidden:dim:strip:strikethrough
```
#### Conditional actions for raw mode #### Conditional actions for raw mode
You may want to perform different actions depending on whether the current item You may want to perform different actions depending on whether the current item

View File

@@ -325,7 +325,8 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
\fB#rrggbb \fR24-bit colors \fB#rrggbb \fR24-bit colors
.B ANSI ATTRIBUTES: (Only applies to foreground colors) .B ANSI ATTRIBUTES: (Only applies to foreground colors)
\fBregular \fRClears previously set attributes; should precede the other ones \fBregular \fRClear previously set attributes; should precede the other ones
\fBstrip \fRRemove colors
\fBbold\fR \fBbold\fR
\fBunderline\fR \fBunderline\fR
\fBreverse\fR \fBreverse\fR

View File

@@ -1366,6 +1366,8 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, *tui
cattr.Attr |= tui.Bold cattr.Attr |= tui.Bold
case "dim": case "dim":
cattr.Attr |= tui.Dim cattr.Attr |= tui.Dim
case "strip":
cattr.Attr |= tui.Strip
case "italic": case "italic":
cattr.Attr |= tui.Italic cattr.Attr |= tui.Italic
case "underline": case "underline":

View File

@@ -123,7 +123,7 @@ 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, nthOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, attrNth tui.Attr) []colorOffset { func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, attrNth tui.Attr, hidden bool) []colorOffset {
itemColors := result.item.Colors() itemColors := result.item.Colors()
// No ANSI codes // No ANSI codes
@@ -194,6 +194,10 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
if !theme.Colored { if !theme.Colored {
return tui.NewColorPair(-1, -1, ansi.color.attr).MergeAttr(base) return tui.NewColorPair(-1, -1, ansi.color.attr).MergeAttr(base)
} }
// fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim:strip,nth:regular
if base.ShouldStripColors() {
return base
}
fg := ansi.color.fg fg := ansi.color.fg
bg := ansi.color.bg bg := ansi.color.bg
if fg == -1 { if fg == -1 {
@@ -251,6 +255,9 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
if curr.nth { if curr.nth {
base = base.WithAttr(attrNth) base = base.WithAttr(attrNth)
} }
if hidden {
base = base.WithFg(theme.Hidden)
}
color := ansiToColorPair(ansi, base) color := ansiToColorPair(ansi, base)
colors = append(colors, colorOffset{ colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, offset: [2]int32{int32(start), int32(idx)},
@@ -258,9 +265,13 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
match: false, match: false,
url: ansi.color.url}) url: ansi.color.url})
} else { } else {
color := colBase.WithAttr(attrNth)
if hidden {
color = color.WithFg(theme.Hidden)
}
colors = append(colors, colorOffset{ colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, offset: [2]int32{int32(start), int32(idx)},
color: colBase.WithAttr(attrNth), color: color,
match: false, match: false,
url: nil}) url: nil})
} }

View File

@@ -131,7 +131,7 @@ func TestColorOffset(t *testing.T) {
colBase := tui.NewColorPair(89, 189, tui.AttrUndefined) colBase := tui.NewColorPair(89, 189, tui.AttrUndefined)
colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined) colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined)
colors := item.colorOffsets(offsets, nil, tui.Dark256, colBase, colMatch, tui.AttrUndefined) colors := item.colorOffsets(offsets, nil, tui.Dark256, colBase, colMatch, tui.AttrUndefined, false)
assert := func(idx int, b int32, e int32, c tui.ColorPair) { assert := func(idx int, b int32, e int32, c tui.ColorPair) {
o := colors[idx] o := colors[idx]
if o.offset[0] != b || o.offset[1] != e || o.color != c { if o.offset[0] != b || o.offset[1] != e || o.color != c {
@@ -158,7 +158,7 @@ func TestColorOffset(t *testing.T) {
nthOffsets := []Offset{{37, 39}, {42, 45}} nthOffsets := []Offset{{37, 39}, {42, 45}}
for _, attr := range []tui.Attr{tui.AttrRegular, tui.StrikeThrough} { for _, attr := range []tui.Attr{tui.AttrRegular, tui.StrikeThrough} {
colors = item.colorOffsets(offsets, nthOffsets, tui.Dark256, colRegular, colUnderline, attr) colors = item.colorOffsets(offsets, nthOffsets, tui.Dark256, colRegular, colUnderline, attr, false)
// [{[0 5] {1 5 0}} {[5 15] {1 5 8}} {[15 20] {1 5 0}} // [{[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}} // {[22 25] {2 6 1}} {[25 27] {2 6 9}} {[27 30] {-1 -1 8}}

View File

@@ -1485,7 +1485,7 @@ func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool)
printFn := func(window tui.Window, limit int) { printFn := func(window tui.Window, limit int) {
if offsets == nil { if offsets == nil {
// tui.Col* are not initialized until renderer.Init() // tui.Col* are not initialized until renderer.Init()
offsets = result.colorOffsets(nil, nil, t.theme, *color, *color, t.nthAttr) offsets = result.colorOffsets(nil, nil, t.theme, *color, *color, t.nthAttr, false)
} }
for limit > 0 { for limit > 0 {
if length > limit { if length > limit {
@@ -1548,7 +1548,7 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
return 1 return 1
} }
t.printHighlighted( t.printHighlighted(
Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false, line, line, true, preTask, nil) Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false, false, line, line, true, preTask, nil)
}) })
t.wrap = wrap t.wrap = wrap
} }
@@ -3070,7 +3070,7 @@ func (t *Terminal) printFooter() {
colors: colors} colors: colors}
t.printHighlighted(Result{item: item}, t.printHighlighted(Result{item: item},
tui.ColFooter, tui.ColFooter, false, false, line, line, true, tui.ColFooter, tui.ColFooter, false, false, false, line, line, true,
func(markerClass) int { func(markerClass) int {
t.footerWindow.Print(indent) t.footerWindow.Print(indent)
return indentSize return indentSize
@@ -3142,7 +3142,7 @@ func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShap
colors: colors} colors: colors}
t.printHighlighted(Result{item: item}, t.printHighlighted(Result{item: item},
tui.ColHeader, tui.ColHeader, false, false, line, line, true, tui.ColHeader, tui.ColHeader, false, false, false, line, line, true,
func(markerClass) int { func(markerClass) int {
t.window.Print(indent) t.window.Print(indent)
return indentSize return indentSize
@@ -3378,13 +3378,7 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
} }
return indentSize return indentSize
} }
base := tui.ColCurrent finalLineNum = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true, !matched, line, maxLine, forceRedraw, preTask, postTask)
match := tui.ColCurrentMatch
if !matched {
base = base.WithFg(t.theme.Hidden)
match = match.WithFg(t.theme.Hidden)
}
finalLineNum = t.printHighlighted(result, base, match, true, true, line, maxLine, forceRedraw, preTask, postTask)
} else { } else {
preTask := func(marker markerClass) int { preTask := func(marker markerClass) int {
w := t.window.Width() - t.pointerLen w := t.window.Width() - t.pointerLen
@@ -3418,11 +3412,7 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
base = base.WithBg(altBg) base = base.WithBg(altBg)
match = match.WithBg(altBg) match = match.WithBg(altBg)
} }
if !matched { finalLineNum = t.printHighlighted(result, base, match, false, true, !matched, line, maxLine, forceRedraw, preTask, postTask)
base = base.WithFg(t.theme.Hidden)
match = match.WithFg(t.theme.Hidden)
}
finalLineNum = t.printHighlighted(result, base, match, false, true, line, maxLine, forceRedraw, preTask, postTask)
} }
for i := 0; i < t.gap && finalLineNum < maxLine; i++ { for i := 0; i < t.gap && finalLineNum < maxLine; i++ {
finalLineNum++ finalLineNum++
@@ -3469,7 +3459,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, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool, lineNum int, maxLineNum int, forceRedraw bool, preTask func(markerClass) int, postTask func(int, int, bool, bool, tui.ColorPair)) int { func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool, hidden bool, lineNum int, maxLineNum int, forceRedraw bool, preTask func(markerClass) int, postTask func(int, int, bool, bool, tui.ColorPair)) int {
var displayWidth int var displayWidth int
item := result.item item := result.item
matchOffsets := []Offset{} matchOffsets := []Offset{}
@@ -3521,7 +3511,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
sort.Sort(ByOrder(nthOffsets)) sort.Sort(ByOrder(nthOffsets))
} }
} }
allOffsets := result.colorOffsets(charOffsets, nthOffsets, t.theme, colBase, colMatch, t.nthAttr) allOffsets := result.colorOffsets(charOffsets, nthOffsets, t.theme, colBase, colMatch, t.nthAttr, hidden)
maxLines := 1 maxLines := 1
if t.canSpanMultiLines() { if t.canSpanMultiLines() {

View File

@@ -16,6 +16,7 @@ const (
AttrClear = Attr(1 << 9) AttrClear = Attr(1 << 9)
BoldForce = Attr(1 << 10) BoldForce = Attr(1 << 10)
FullBg = Attr(1 << 11) FullBg = Attr(1 << 11)
Strip = Attr(1 << 12)
) )
func (a Attr) Merge(b Attr) Attr { func (a Attr) Merge(b Attr) Attr {
@@ -384,6 +385,10 @@ func (p ColorPair) IsFullBgMarker() bool {
return p.attr&FullBg > 0 return p.attr&FullBg > 0
} }
func (p ColorPair) ShouldStripColors() bool {
return p.attr&Strip > 0
}
func (p ColorPair) HasBg() bool { func (p ColorPair) HasBg() bool {
return p.attr&Reverse == 0 && p.bg != colDefault || return p.attr&Reverse == 0 && p.bg != colDefault ||
p.attr&Reverse > 0 && p.fg != colDefault p.attr&Reverse > 0 && p.fg != colDefault