Add --accept-nth option to transform the output

This option can be used to replace a sed or awk in the post-processing step.

  ps -ef | fzf --multi --header-lines 1 | awk '{print $2}'
  ps -ef | fzf --multi --header-lines 1 --accept-nth 2

This may not be a very "Unix-y" thing to do, so I've always felt that fzf
shouldn't have such an option, but I've finally changed my mind because:

* fzf can be configured with a custom delimiter that is a fixed string
  or a regular expression.
* In such cases, you'd need to repeat the delimiter again in the
  post-processing step.
* Also, tools like awk or sed may interpret a regular expression
  differently, causing mismatches.

You can still use sed, cut, or awk if you prefer.

Close #3987
Close #1323
This commit is contained in:
Junegunn Choi
2025-02-09 11:53:35 +09:00
parent a1994ff0ab
commit 2b584586ed
9 changed files with 113 additions and 10 deletions

View File

@@ -305,6 +305,7 @@ type Terminal struct {
nthAttr tui.Attr
nth []Range
nthCurrent []Range
acceptNth []Range
tabstop int
margin [4]sizeSpec
padding [4]sizeSpec
@@ -914,6 +915,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
nthAttr: opts.Theme.Nth.Attr,
nth: opts.Nth,
nthCurrent: opts.Nth,
acceptNth: opts.AcceptNth,
tabstop: opts.Tabstop,
hasStartActions: false,
hasResultActions: false,
@@ -1561,16 +1563,24 @@ func (t *Terminal) output() bool {
for _, s := range t.printQueue {
t.printer(s)
}
transform := func(item *Item) string {
return item.AsString(t.ansi)
}
if len(t.acceptNth) > 0 {
transform = func(item *Item) string {
return JoinTokens(StripLastDelimiter(Transform(Tokenize(item.AsString(t.ansi), t.delimiter), t.acceptNth), t.delimiter))
}
}
found := len(t.selected) > 0
if !found {
current := t.currentItem()
if current != nil {
t.printer(current.AsString(t.ansi))
t.printer(transform(current))
found = true
}
} else {
for _, sel := range t.sortSelected() {
t.printer(sel.item.AsString(t.ansi))
t.printer(transform(sel.item))
}
}
return found
@@ -3847,7 +3857,7 @@ func replacePlaceholder(params replacePlaceholderParams) (string, []string) {
elems, prefixLength := awkTokenizer(params.query)
tokens := withPrefixLengths(elems, prefixLength)
trans := Transform(tokens, nth)
result := joinTokens(trans)
result := JoinTokens(trans)
if !flags.preserveSpace {
result = strings.TrimSpace(result)
}
@@ -3897,7 +3907,7 @@ func replacePlaceholder(params replacePlaceholderParams) (string, []string) {
replace = func(item *Item) string {
tokens := Tokenize(item.AsString(params.stripAnsi), params.delimiter)
trans := Transform(tokens, ranges)
str := joinTokens(trans)
str := JoinTokens(trans)
// trim the last delimiter
if params.delimiter.str != nil {