m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-18 00:03:39 -05:00

Add become(...) action that replaces current fzf process

Close #3159
This commit is contained in:
Junegunn Choi
2023-02-11 20:21:10 +09:00
parent f7447aece1
commit 6ea38b4438
5 changed files with 56 additions and 7 deletions

View File

@@ -10,6 +10,7 @@ import (
"github.com/junegunn/fzf/src/algo"
"github.com/junegunn/fzf/src/tui"
"github.com/junegunn/fzf/src/util"
"github.com/mattn/go-runewidth"
"github.com/mattn/go-shellwords"
@@ -921,7 +922,7 @@ const (
func init() {
executeRegexp = regexp.MustCompile(
`(?si)[:+](execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|border-label|preview-label)|change-preview-window|change-preview|(?:re|un)bind|pos|put)`)
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|border-label|preview-label)|change-preview-window|change-preview|(?:re|un)bind|pos|put)`)
splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
}
@@ -1171,6 +1172,10 @@ func parseActionList(masked string, original string, prevActions []*action, putA
actions = append(actions, &action{t: t, a: actionArg})
}
switch t {
case actBecome:
if util.IsWindows() {
exit("become action is not supported on Windows")
}
case actUnbind, actRebind:
parseKeyChordsImpl(actionArg, spec[0:offset]+" target required", exit)
case actChangePreviewWindow:
@@ -1223,6 +1228,8 @@ func isExecuteAction(str string) actionType {
prefix := actionNameRegexp.FindString(str)
switch prefix {
case "become":
return actBecome
case "reload":
return actReload
case "reload-sync":

View File

@@ -6,6 +6,7 @@ import (
"io/ioutil"
"math"
"os"
"os/exec"
"os/signal"
"regexp"
"sort"
@@ -387,6 +388,7 @@ const (
actDeselect
actUnbind
actRebind
actBecome
)
type placeholderFlags struct {
@@ -2237,7 +2239,7 @@ func (t *Terminal) redraw() {
func (t *Terminal) executeCommand(template string, forcePlus bool, background bool, captureFirstLine bool) string {
line := ""
valid, list := t.buildPlusList(template, forcePlus, false)
valid, list := t.buildPlusList(template, forcePlus)
// captureFirstLine is used for transform-{prompt,query} and we don't want to
// return an empty string in those cases
if !valid && !captureFirstLine {
@@ -2297,10 +2299,10 @@ func (t *Terminal) currentItem() *Item {
return nil
}
func (t *Terminal) buildPlusList(template string, forcePlus bool, forceEvaluation bool) (bool, []*Item) {
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, []*Item) {
current := t.currentItem()
slot, plus, query := hasPreviewFlags(template)
if !forceEvaluation && !(!slot || query || (forcePlus || plus) && len(t.selected) > 0) {
if !(!slot || query || (forcePlus || plus) && len(t.selected) > 0) {
return current != nil, []*Item{current, current}
}
@@ -2625,7 +2627,7 @@ func (t *Terminal) Loop() {
refreshPreview := func(command string) {
if len(command) > 0 && t.canPreview() {
_, list := t.buildPlusList(command, false, false)
_, list := t.buildPlusList(command, false)
t.cancelPreview()
t.previewBox.Set(reqPreviewEnqueue, previewRequest{command, t.pwindow, t.evaluateScrollOffset(), list})
}
@@ -2860,6 +2862,21 @@ func (t *Terminal) Loop() {
doAction = func(a *action) bool {
switch a.t {
case actIgnore:
case actBecome:
_, list := t.buildPlusList(a.a, false)
command := t.replacePlaceholder(a.a, false, string(t.input), list)
shell := os.Getenv("SHELL")
if len(shell) == 0 {
shell = "sh"
}
shellPath, err := exec.LookPath(shell)
if err == nil {
t.tui.Close()
if t.history != nil {
t.history.append(string(t.input))
}
syscall.Exec(shellPath, []string{shell, "-c", command}, os.Environ())
}
case actExecute, actExecuteSilent:
t.executeCommand(a.a, false, a.t == actExecuteSilent, false)
case actExecuteMulti:
@@ -2881,7 +2898,7 @@ func (t *Terminal) Loop() {
t.activePreviewOpts.Toggle()
updatePreviewWindow(false)
if t.canPreview() {
valid, list := t.buildPlusList(t.previewOpts.command, false, false)
valid, list := t.buildPlusList(t.previewOpts.command, false)
if valid {
t.cancelPreview()
t.previewBox.Set(reqPreviewEnqueue,
@@ -3360,7 +3377,7 @@ func (t *Terminal) Loop() {
case actReload, actReloadSync:
t.failed = nil
valid, list := t.buildPlusList(a.a, false, false)
valid, list := t.buildPlusList(a.a, false)
if !valid {
// We run the command even when there's no match
// 1. If the template doesn't have any slots