summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2023-02-11 20:21:10 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2023-02-11 20:26:31 +0900
commit6ea38b44384e7a09a3863465dc3cc7b93cd7e781 (patch)
tree38168bbc474e1defd9d177b9089c80aaba1cc330 /src
parentf7447aece12f9b95133b803d5c4f71815bee8ca6 (diff)
downloadfzf-6ea38b44384e7a09a3863465dc3cc7b93cd7e781.tar.gz
Add become(...) action that replaces current fzf process
Close #3159
Diffstat (limited to 'src')
-rw-r--r--src/options.go9
-rw-r--r--src/terminal.go29
2 files changed, 31 insertions, 7 deletions
diff --git a/src/options.go b/src/options.go
index e30ba2c5..9be29107 100644
--- a/src/options.go
+++ b/src/options.go
@@ -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":
diff --git a/src/terminal.go b/src/terminal.go
index 0dc711a3..13968a39 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -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