diff options
| author | Junegunn Choi <junegunn.c@gmail.com> | 2025-06-19 22:35:23 +0900 |
|---|---|---|
| committer | Junegunn Choi <junegunn.c@gmail.com> | 2025-06-19 22:35:23 +0900 |
| commit | dcec6354f5e57be1f8420850c3cfd6a56206c67a (patch) | |
| tree | 098e183bbad04c6d4babbfb16bc9557e7d8fecb6 /src/terminal.go | |
| parent | 16d338da84dda718935c46cbbc54c8c3be9e21a2 (diff) | |
| download | fzf-dcec6354f5e57be1f8420850c3cfd6a56206c67a.tar.gz | |
Add {*} placeholder flag
Diffstat (limited to 'src/terminal.go')
| -rw-r--r-- | src/terminal.go | 87 |
1 files changed, 51 insertions, 36 deletions
diff --git a/src/terminal.go b/src/terminal.go index 85a51112..231375ba 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -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,6 +692,7 @@ func processExecution(action actionType) bool { type placeholderFlags struct { plus bool + asterisk bool preserveSpace bool number bool forceUpdate bool @@ -713,7 +714,7 @@ type searchRequest struct { type previewRequest struct { template string scrollOffset int - list []*Item + list [3][]*Item // current, select, and all matched items env []string query string } @@ -4099,6 +4100,8 @@ 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': @@ -4122,19 +4125,16 @@ func parsePlaceholder(match string) (bool, string, placeholderFlags) { return false, matchWithoutFlags, flags } -func hasPreviewFlags(template string) (slot bool, plus bool, forceUpdate bool) { +func hasPreviewFlags(template string) (slot bool, plus bool, asterisk 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 []*Item + allItems [3][]*Item // current, select, and all matched items lastAction actionType prompt string executor *util.Executor } func (t *Terminal) replacePlaceholderInInitialCommand(template string) (string, []string) { - return t.replacePlaceholder(template, false, string(t.input), []*Item{nil, nil}) + return t.replacePlaceholder(template, false, string(t.input), [3][]*Item{nil, nil, nil}) } -func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) (string, []string) { +func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list [3][]*Item) (string, []string) { return replacePlaceholder(replacePlaceholderParams{ template: template, stripAnsi: t.ansi, @@ -4177,7 +4177,11 @@ func (t *Terminal) evaluateScrollOffset() int { } // We only need the current item to calculate the scroll offset - replaced, tempFiles := t.replacePlaceholder(t.activePreviewOpts.scroll, false, "", []*Item{t.currentItem(), nil}) + current := []*Item{t.currentItem()} + if current[0] == nil { + current = nil + } + replaced, tempFiles := t.replacePlaceholder(t.activePreviewOpts.scroll, false, "", [3][]*Item{current, nil, nil}) removeFiles(tempFiles) offsetExpr := offsetTrimCharsRegex.ReplaceAllString(replaced, "") @@ -4209,14 +4213,9 @@ func (t *Terminal) evaluateScrollOffset() int { func replacePlaceholder(params replacePlaceholderParams) (string, []string) { tempFiles := []string{} - current := params.allItems[:1] - selected := params.allItems[1:] - if current[0] == nil { - current = []*Item{} - } - if selected[0] == nil { - selected = []*Item{} - } + current := params.allItems[0] + selected := params.allItems[1] + matched := params.allItems[2] // replace placeholders one by one replaced := placeholder.ReplaceAllStringFunc(params.template, func(match string) string { @@ -4312,7 +4311,9 @@ func replacePlaceholder(params replacePlaceholderParams) (string, []string) { // apply 'replace' function over proper set of items and return result items := current - if flags.plus || params.forcePlus { + if flags.asterisk { + items = matched + } else if flags.plus || params.forcePlus { items = selected } replacements := make([]string, len(items)) @@ -4546,11 +4547,15 @@ func (t *Terminal) currentItem() *Item { return nil } -func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) { +func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, [3][]*Item) { current := t.currentItem() - slot, plus, forceUpdate := hasPreviewFlags(template) - if !(!slot || forceUpdate || (forcePlus || plus) && len(t.selected) > 0) { - return current != nil, []*Item{current, current} + 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} } // We would still want to update preview window even if there is no match if @@ -4561,17 +4566,25 @@ func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item current = &minItem } + var all []*Item + if asterisk { + cnt := t.merger.Length() + all = make([]*Item, cnt) + for i := 0; i < cnt; i++ { + all[i] = t.merger.Get(i).item + } + } + var sels []*Item if len(t.selected) == 0 { - sels = []*Item{current, current} - } else { - sels = make([]*Item, len(t.selected)+1) - sels[0] = current + sels = []*Item{current} + } else if len(t.selected) > 0 { + sels = make([]*Item, len(t.selected)) for i, sel := range t.sortSelected() { - sels[i+1] = sel.item + sels[i] = sel.item } } - return true, sels + return true, [3][]*Item{{current}, sels, all} } func (t *Terminal) selectItem(item *Item) bool { @@ -4831,7 +4844,8 @@ func (t *Terminal) Loop() error { stop := false t.previewBox.WaitFor(reqPreviewReady) for { - var items []*Item + requested := false + var items [3][]*Item var commandTemplate string var env []string var query string @@ -4849,6 +4863,7 @@ func (t *Terminal) Loop() error { items = request.list env = request.env query = request.query + requested = true } } events.Clear() @@ -4856,7 +4871,7 @@ func (t *Terminal) Loop() error { if stop { break } - if items == nil { + if !requested { continue } version++ @@ -6396,7 +6411,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 { @@ -6585,7 +6600,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++ } |
