From 6d647e13ff1d953e6f8284046a86f0babe78150b Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 4 Dec 2020 20:34:41 +0900 Subject: [PATCH] Add change-prompt action Close #2270 --- CHANGELOG.md | 8 ++++++ man/man1/fzf-tmux.1 | 2 +- man/man1/fzf.1 | 62 ++++++++++++++++++++++++++------------------- src/options.go | 8 +++++- src/terminal.go | 4 +++ test/test_go.rb | 9 +++++++ 6 files changed, 65 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a305ecc..7609316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +0.24.4 +------ +- Added `change-prompt` action + ```sh + fzf --prompt 'foo> ' --bind $'a:change-prompt:\x1b[31mbar> ' + ``` +- Bug fixes and improvements + 0.24.3 ------ - Added `--padding` option diff --git a/man/man1/fzf-tmux.1 b/man/man1/fzf-tmux.1 index 2546542..7f0cda1 100644 --- a/man/man1/fzf-tmux.1 +++ b/man/man1/fzf-tmux.1 @@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .. -.TH fzf-tmux 1 "Nov 2020" "fzf 0.24.3" "fzf-tmux - open fzf in tmux split pane" +.TH fzf-tmux 1 "Dec 2020" "fzf 0.24.3" "fzf-tmux - open fzf in tmux split pane" .SH NAME fzf-tmux - open fzf in tmux split pane diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 0efa475..f779ad5 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .. -.TH fzf 1 "Nov 2020" "fzf 0.24.4" "fzf - a command-line fuzzy finder" +.TH fzf 1 "Dec 2020" "fzf 0.24.4" "fzf - a command-line fuzzy finder" .SH NAME fzf - a command-line fuzzy finder @@ -761,6 +761,7 @@ A key or an event can be bound to one or more of the following actions. \fBbackward-word\fR \fIalt-b shift-left\fR \fBbeginning-of-line\fR \fIctrl-a home\fR \fBcancel\fR (clear query string if not empty, abort fzf otherwise) + \fBchange-prompt(...)\fR (change prompt to the given string) \fBclear-screen\fR \fIctrl-l\fR \fBclear-selection\fR (clear multi-selection) \fBclear-query\fR (clear query string) @@ -816,7 +817,40 @@ A key or an event can be bound to one or more of the following actions. Multiple actions can be chained using \fB+\fR separator. e.g. - \fBfzf --bind 'ctrl-a:select-all+accept'\fR + \fBfzf --multi --bind 'ctrl-a:select-all+accept'\fR + \fBfzf --multi --bind 'ctrl-a:select-all' --bind 'ctrl-a:+accept'\fR + +.SS ACTION ARGUMENT + +An action denoted with \fB(...)\fR suffix takes an argument. + +e.g. + \fBfzf --bind 'ctrl-a:change-prompt(NewPrompt> )'\fR + \fBfzf --bind 'ctrl-v:preview(cat {})' --preview-window hidden\fR + +If the argument contains parentheses, fzf may fail to parse the expression. In +that case, you can use any of the following alternative notations to avoid +parse errors. + + \fBaction-name[...]\fR + \fBaction-name~...~\fR + \fBaction-name!...!\fR + \fBaction-name@...@\fR + \fBaction-name#...#\fR + \fBaction-name$...$\fR + \fBaction-name%...%\fR + \fBaction-name^...^\fR + \fBaction-name&...&\fR + \fBaction-name*...*\fR + \fBaction-name;...;\fR + \fBaction-name/.../\fR + \fBaction-name|...|\fR + \fBaction-name:...\fR +.RS +The last one is the special form that frees you from parse errors as it does +not expect the closing character. The catch is that it should be the last one +in the comma-separated list of key-action pairs. +.RE .SS COMMAND EXECUTION @@ -828,30 +862,6 @@ binding \fBenter\fR key to \fBless\fR command like follows. You can use the same placeholder expressions as in \fB--preview\fR. -If the command contains parentheses, fzf may fail to parse the expression. In -that case, you can use any of the following alternative notations to avoid -parse errors. - - \fBexecute[...]\fR - \fBexecute~...~\fR - \fBexecute!...!\fR - \fBexecute@...@\fR - \fBexecute#...#\fR - \fBexecute$...$\fR - \fBexecute%...%\fR - \fBexecute^...^\fR - \fBexecute&...&\fR - \fBexecute*...*\fR - \fBexecute;...;\fR - \fBexecute/.../\fR - \fBexecute|...|\fR - \fBexecute:...\fR -.RS -The last one is the special form that frees you from parse errors as it does -not expect the closing character. The catch is that it should be the last one -in the comma-separated list of key-action pairs. -.RE - fzf switches to the alternate screen when executing a command. However, if the command is expected to complete quickly, and you are not interested in its output, you might want to use \fBexecute-silent\fR instead, which silently diff --git a/src/options.go b/src/options.go index 0a6dcf9..b3fb78c 100644 --- a/src/options.go +++ b/src/options.go @@ -735,7 +735,7 @@ func init() { // Backreferences are not supported. // "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|') executeRegexp = regexp.MustCompile( - `(?si)[:+](execute(?:-multi|-silent)?|reload|preview):.+|[:+](execute(?:-multi|-silent)?|reload|preview)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`) + `(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`) } func parseKeymap(keymap map[int][]action, str string) { @@ -749,6 +749,8 @@ func parseKeymap(keymap map[int][]action, str string) { prefix = symbol + "reload" } else if strings.HasPrefix(src[1:], "preview") { prefix = symbol + "preview" + } else if strings.HasPrefix(src[1:], "change-prompt") { + prefix = symbol + "change-prompt" } else if src[len(prefix)] == '-' { c := src[len(prefix)+1] if c == 's' || c == 'S' { @@ -922,6 +924,8 @@ func parseKeymap(keymap map[int][]action, str string) { offset = len("reload") case actPreview: offset = len("preview") + case actChangePrompt: + offset = len("change-prompt") case actExecuteSilent: offset = len("execute-silent") case actExecuteMulti: @@ -961,6 +965,8 @@ func isExecuteAction(str string) actionType { return actReload case "preview": return actPreview + case "change-prompt": + return actChangePrompt case "execute": return actExecute case "execute-silent": diff --git a/src/terminal.go b/src/terminal.go index 14a90d5..0853548 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -214,6 +214,7 @@ const ( actBackwardDeleteCharEOF actBackwardWord actCancel + actChangePrompt actClearScreen actClearQuery actClearSelection @@ -2223,6 +2224,9 @@ func (t *Terminal) Loop() { } case actPrintQuery: req(reqPrintQuery) + case actChangePrompt: + t.prompt, t.promptLen = t.parsePrompt(a.a) + req(reqPrompt) case actPreview: togglePreview(true) refreshPreview(a.a) diff --git a/test/test_go.rb b/test/test_go.rb index 3ff118c..13b1ee4 100755 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -1823,6 +1823,15 @@ class TestGoFZF < TestBase tmux.until { |lines| lines.item_count == 100 } tmux.until { |lines| lines[1]&.include?('[200]') } end + + def test_change_prompt + tmux.send_keys "#{FZF} --bind 'a:change-prompt(a> ),b:change-prompt:b> ' --query foo", :Enter + tmux.until { |lines| assert_equal '> foo', lines[-1] } + tmux.send_keys 'a' + tmux.until { |lines| assert_equal 'a> foo', lines[-1] } + tmux.send_keys 'b' + tmux.until { |lines| assert_equal 'c> foo', lines[-1] } + end end module TestShell