Revert "Add {*} placeholder flag"

This reverts commit 27258f7207.
This commit is contained in:
Junegunn Choi
2025-06-19 12:39:31 +09:00
parent 27258f7207
commit 16d338da84
4 changed files with 41 additions and 70 deletions

View File

@@ -3,11 +3,6 @@ CHANGELOG
0.63.0
------
- Added `{*}` placeholder flag that evaluates to all matched items.
```bash
seq 10000 | fzf --preview "awk '{sum += \$1} END {print sum}' {*f}"
```
- Use this with caution, as it can make fzf sluggish for large lists.
- Added background variants of transform actions with `bg-` prefix that run asynchronously in the background
```sh
GETTER='curl -s http://metaphorpsum.com/sentences/1'

View File

@@ -789,16 +789,13 @@ fzf also exports \fB$FZF_PREVIEW_TOP\fR and \fB$FZF_PREVIEW_LEFT\fR so that
the preview command can determine the position of the preview window.
A placeholder expression starting with \fB+\fR flag will be replaced to the
space-separated list of the selected items (or the current item if no selection
space-separated list of the selected lines (or the current line if no selection
was made) individually quoted.
e.g.
\fBfzf \-\-multi \-\-preview='head \-10 {+}'
git log \-\-oneline | fzf \-\-multi \-\-preview 'git show {+1}'\fR
Similarly, a placeholder expression starting with \fB*\fR flag will be replaced
to the space-separated list of all matched items individually quoted.
Each expression expands to a quoted string, so that it's safe to pass it as an
argument to an external command. So you should not manually add quotes around
the curly braces. But if you don't want this behavior, you can put
@@ -810,13 +807,14 @@ from the replacement string. To preserve the whitespace, use the \fBs\fR flag.
A placeholder expression with \fBf\fR flag is replaced to the path of
a temporary file that holds the evaluated list. This is useful when you
pass a large number of items and the length of the evaluated string may
multi-select a large number of items and the length of the evaluated string may
exceed \fBARG_MAX\fR.
e.g.
\fB# See the sum of all the matched numbers
\fB# Press CTRL\-A to select 100K items and see the sum of all the numbers.
# This won't work properly without 'f' flag due to ARG_MAX limit.
seq 100000 | fzf \-\-preview "awk '{sum+=\\$1} END {print sum}' {*f}"\fR
seq 100000 | fzf \-\-multi \-\-bind ctrl\-a:select\-all \\
\-\-preview "awk '{sum+=\\$1} END {print sum}' {+f}"\fR
Also,

View File

@@ -66,7 +66,7 @@ const maxFocusEvents = 10000
const blockDuration = 1 * time.Second
func init() {
placeholder = regexp.MustCompile(`\\?(?:{[+*sfr]*[0-9,-.]*}|{q(?::s?[0-9,-.]+)?}|{fzf:(?:query|action|prompt)}|{\+?f?nf?})`)
placeholder = regexp.MustCompile(`\\?(?:{[+sfr]*[0-9,-.]*}|{q(?::s?[0-9,-.]+)?}|{fzf:(?:query|action|prompt)}|{\+?f?nf?})`)
whiteSuffix = regexp.MustCompile(`\s*$`)
offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`)
offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`)
@@ -692,7 +692,6 @@ func processExecution(action actionType) bool {
type placeholderFlags struct {
plus bool
asterisk bool
preserveSpace bool
number bool
forceUpdate bool
@@ -714,7 +713,7 @@ type searchRequest struct {
type previewRequest struct {
template string
scrollOffset int
list [3][]*Item
list []*Item
env []string
query string
}
@@ -4100,8 +4099,6 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) {
trimmed := ""
for _, char := range match[1:] {
switch char {
case '*':
flags.asterisk = true
case '+':
flags.plus = true
case 's':
@@ -4125,16 +4122,19 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) {
return false, matchWithoutFlags, flags
}
func hasPreviewFlags(template string) (slot bool, plus bool, asterisk bool, forceUpdate bool) {
func hasPreviewFlags(template string) (slot bool, plus bool, forceUpdate bool) {
for _, match := range placeholder.FindAllString(template, -1) {
escaped, _, flags := parsePlaceholder(match)
if escaped {
continue
}
if flags.plus {
plus = true
}
if flags.forceUpdate {
forceUpdate = true
}
slot = true
plus = plus || flags.plus
asterisk = asterisk || flags.asterisk
forceUpdate = forceUpdate || flags.forceUpdate
}
return
}
@@ -4146,17 +4146,17 @@ type replacePlaceholderParams struct {
printsep string
forcePlus bool
query string
allItems [3][]*Item // current, select, and all matched items
allItems []*Item
lastAction actionType
prompt string
executor *util.Executor
}
func (t *Terminal) replacePlaceholderInInitialCommand(template string) (string, []string) {
return t.replacePlaceholder(template, false, string(t.input), [3][]*Item{nil, nil, nil})
return t.replacePlaceholder(template, false, string(t.input), []*Item{nil, nil})
}
func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list [3][]*Item) (string, []string) {
func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) (string, []string) {
return replacePlaceholder(replacePlaceholderParams{
template: template,
stripAnsi: t.ansi,
@@ -4177,7 +4177,7 @@ func (t *Terminal) evaluateScrollOffset() int {
}
// We only need the current item to calculate the scroll offset
replaced, tempFiles := t.replacePlaceholder(t.activePreviewOpts.scroll, false, "", [3][]*Item{{t.currentItem()}, nil, nil})
replaced, tempFiles := t.replacePlaceholder(t.activePreviewOpts.scroll, false, "", []*Item{t.currentItem(), nil})
removeFiles(tempFiles)
offsetExpr := offsetTrimCharsRegex.ReplaceAllString(replaced, "")
@@ -4209,9 +4209,14 @@ func (t *Terminal) evaluateScrollOffset() int {
func replacePlaceholder(params replacePlaceholderParams) (string, []string) {
tempFiles := []string{}
current := params.allItems[0]
selected := params.allItems[1]
matched := params.allItems[2]
current := params.allItems[:1]
selected := params.allItems[1:]
if current[0] == nil {
current = []*Item{}
}
if selected[0] == nil {
selected = []*Item{}
}
// replace placeholders one by one
replaced := placeholder.ReplaceAllStringFunc(params.template, func(match string) string {
@@ -4307,9 +4312,7 @@ func replacePlaceholder(params replacePlaceholderParams) (string, []string) {
// apply 'replace' function over proper set of items and return result
items := current
if flags.asterisk {
items = matched
} else if flags.plus || params.forcePlus {
if flags.plus || params.forcePlus {
items = selected
}
replacements := make([]string, len(items))
@@ -4543,15 +4546,11 @@ func (t *Terminal) currentItem() *Item {
return nil
}
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, [3][]*Item) {
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
current := t.currentItem()
slot, plus, asterisk, forceUpdate := hasPreviewFlags(template)
if !(!slot || forceUpdate || asterisk || (forcePlus || plus) && len(t.selected) > 0) {
if current == nil {
// Invalid
return false, [3][]*Item{nil, nil, nil}
}
return true, [3][]*Item{{current}, {current}, nil}
slot, plus, forceUpdate := hasPreviewFlags(template)
if !(!slot || forceUpdate || (forcePlus || plus) && len(t.selected) > 0) {
return current != nil, []*Item{current, current}
}
// We would still want to update preview window even if there is no match if
@@ -4562,26 +4561,17 @@ func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, [3][]*I
current = &minItem
}
var all []*Item
if asterisk {
cnt := t.merger.Length()
all = make([]*Item, cnt)
for i := 0; i < cnt; i++ {
item := t.merger.Get(i).item
all[i] = item
}
}
var sels []*Item
if len(t.selected) == 0 {
sels = []*Item{current}
} else if len(t.selected) > 0 {
sels = make([]*Item, len(t.selected))
sels = []*Item{current, current}
} else {
sels = make([]*Item, len(t.selected)+1)
sels[0] = current
for i, sel := range t.sortSelected() {
sels[i] = sel.item
sels[i+1] = sel.item
}
}
return true, [3][]*Item{{current}, sels, all}
return true, sels
}
func (t *Terminal) selectItem(item *Item) bool {
@@ -4841,8 +4831,7 @@ func (t *Terminal) Loop() error {
stop := false
t.previewBox.WaitFor(reqPreviewReady)
for {
requested := false
var items [3][]*Item
var items []*Item
var commandTemplate string
var env []string
var query string
@@ -4860,7 +4849,6 @@ func (t *Terminal) Loop() error {
items = request.list
env = request.env
query = request.query
requested = true
}
}
events.Clear()
@@ -4868,7 +4856,7 @@ func (t *Terminal) Loop() error {
if stop {
break
}
if !requested {
if items == nil {
continue
}
version++
@@ -6408,7 +6396,7 @@ func (t *Terminal) Loop() error {
// We run the command even when there's no match
// 1. If the template doesn't have any slots
// 2. If the template has {q}
slot, _, _, forceUpdate := hasPreviewFlags(a.a)
slot, _, forceUpdate := hasPreviewFlags(a.a)
valid = !slot || forceUpdate
}
if valid {
@@ -6597,7 +6585,7 @@ func (t *Terminal) Loop() error {
}
if queryChanged && t.canPreview() && len(t.previewOpts.command) > 0 {
_, _, _, forceUpdate := hasPreviewFlags(t.previewOpts.command)
_, _, forceUpdate := hasPreviewFlags(t.previewOpts.command)
if forceUpdate {
t.version++
}

View File

@@ -189,16 +189,6 @@ class TestPreview < TestInteractive
tmux.until { |lines| assert_includes lines[1], ' {//1 10/1 10 /123//0 9} ' }
end
def test_preview_asterisk
tmux.send_keys %(seq 5 | #{FZF} --multi --preview 'echo {} / {+} / {*}'), :Enter
tmux.until { |lines| assert_equal 5, lines.match_count }
tmux.until { |lines| assert_includes lines[1], ' 1 / 1 / 1 2 3 4 5 ' }
tmux.send_keys :BTab
tmux.until { |lines| assert_includes lines[1], ' 2 / 1 / 1 2 3 4 5 ' }
tmux.send_keys :BTab
tmux.until { |lines| assert_includes lines[1], ' 3 / 1 2 / 1 2 3 4 5 ' }
end
def test_preview_file
tmux.send_keys %[(echo foo bar; echo bar foo) | #{FZF} --multi --preview 'cat {+f} {+f2} {+nf} {+fn}' --print0], :Enter
tmux.until { |lines| assert_includes lines[1], ' foo barbar00 ' }