Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ctrl-r history direct output #477

Closed
Zaplanincan opened this issue Jan 25, 2016 · 18 comments
Closed

Ctrl-r history direct output #477

Zaplanincan opened this issue Jan 25, 2016 · 18 comments
Labels

Comments

@Zaplanincan
Copy link

Firs congratulations for make it to ArchLinux Community repo and second, I know I see that answer, but I did not make a mark (so mad at myself). How to make _ctrl-r_ commandline output without option to edit command and just directly start command.
Thank you and all the best, Boris

@junegunn
Copy link
Owner

Haha thanks but I have no idea what's going on with ArchLinux stuff.

Anyway the feature you asked is not implemented. It would be ideal if we could "choose" if we want to edit the line or not, like pressing CTRL-X instead of Enter will fire the command immediately. This should be possible in zsh using --expect ctrl-x option, but I don't think the same can be easily done on bash.

@naphthalene
Copy link

I would love this feature as well - if ctrl-x isn't bound to anything i also think that's an appropriate shortcut. I'll try my hand at adding this.

@junegunn
Copy link
Owner

junegunn commented Jul 4, 2016

For zsh, this is what we can do:

diff --git a/shell/key-bindings.zsh b/shell/key-bindings.zsh
index ea65c0c..f1fb720 100644
--- a/shell/key-bindings.zsh
+++ b/shell/key-bindings.zsh
@@ -47,12 +47,18 @@ bindkey '\ec' fzf-cd-widget
 fzf-history-widget() {
   local selected num
   setopt localoptions noglobsubst pipefail
-  selected=( $(fc -l 1 | eval "$(__fzfcmd) +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r $FZF_CTRL_R_OPTS -q ${(q)LBUFFER}") )
+  selected=( $(fc -l 1 | eval "$(__fzfcmd) +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r --expect=ctrl-x $FZF_CTRL_R_OPTS -q ${(q)LBUFFER}") )
   local ret=$?
   if [ -n "$selected" ]; then
+    local accept=0
+    if [[ $selected[1] = ctrl-x ]]; then
+      accept=1
+      shift selected
+    fi
     num=$selected[1]
     if [ -n "$num" ]; then
       zle vi-fetch-history -n $num
+      [[ $accept = 1 ]] && zle accept-line
     fi
   fi
   zle redisplay

but I couldn't find a solution for bash.

@bananatranada
Copy link

@junegunn is it possible to swap enter and ctrl-x? I seem to re-execute previous commands much more than editing them, and it would be nice to swap them around. Thanks

@ratorx
Copy link

ratorx commented Nov 10, 2018

@bananatranada If you want to set it up for yourself, just changing [[ $accept = 1 ]] to [[ $accept = 0]] before zle-accept-line in the above code would do the trick.

@raine
Copy link

raine commented Dec 4, 2018

Having used zaw history, I prefer Ctrl-E to select for editing, and Enter to run directly.

diff --git i/shell/key-bindings.zsh w/shell/key-bindings.zsh
index 6aacc7e..5b24df2 100644
--- i/shell/key-bindings.zsh
+++ w/shell/key-bindings.zsh
@@ -68,12 +68,18 @@ fzf-history-widget() {
   local selected num
   setopt localoptions noglobsubst noposixbuiltins pipefail 2> /dev/null
   selected=( $(fc -rl 1 |
-    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
+    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort --expect=ctrl-e $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
   local ret=$?
   if [ -n "$selected" ]; then
+    local accept=0
+    if [[ $selected[1] = ctrl-e ]]; then
+      accept=1
+      shift selected
+    fi
     num=$selected[1]
     if [ -n "$num" ]; then
       zle vi-fetch-history -n $num
+      [[ $accept = 0 ]] && zle accept-line
     fi
   fi
   zle reset-prompt

4z3 added a commit to 4z3/fzf that referenced this issue Feb 5, 2019
4z3 added a commit to 4z3/fzf that referenced this issue Feb 5, 2019
4z3 added a commit to 4z3/fzf that referenced this issue Feb 5, 2019
@junegunn
Copy link
Owner

See #1492.

fsouza added a commit to fsouza/dotfiles-old that referenced this issue Apr 15, 2020
@3v1n0
Copy link

3v1n0 commented May 20, 2020

Personally I prefer to always accept by default on "Enter" while I'd instead just select the item on prompt on tab (or maybe on side rows), so previous ideas applies with something like:

diff --git a/shell/key-bindings.zsh b/shell/key-bindings.zsh
index 74ce9b7..f18ac5b 100755
--- a/shell/key-bindings.zsh
+++ b/shell/key-bindings.zsh
@@ -101,12 +101,18 @@ fzf-history-widget() {
   local selected num
   setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
   selected=( $(fc -rl 1 | perl -ne 'print if !$seen{(/^\s*[0-9]+\s+(.*)/, $1)}++' |
-    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
+    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort --expect=tab $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
   local ret=$?
   if [ -n "$selected" ]; then
+    local select=0
+    if [[ $selected[1] == tab ]]; then
+      select=1
+      shift selected
+    fi
     num=$selected[1]
     if [ -n "$num" ]; then
       zle vi-fetch-history -n $num
+      [[ $select == 0 ]] && zle accept-line
     fi
   fi
   zle reset-prompt

vedang added a commit to vedang/fzf that referenced this issue Aug 15, 2020
If you want to edit the command before execution, press Ctrl-E instead
of Enter when selecting the entry.

Taken from: junegunn#477
vedang added a commit to vedang/fzf that referenced this issue Sep 7, 2020
If you want to edit the command before execution, press Ctrl-E instead
of Enter when selecting the entry.

Taken from: junegunn#477
schmee added a commit to schmee/dotfiles that referenced this issue Nov 21, 2021
@prateek
Copy link

prateek commented Jan 23, 2023

@junegunn fwiw - loads of people seem to want this functionality. How would you feel about landing the ZSH version to master?

@meinzer1899
Copy link

With the current fzf release 0.45.0, the workarounds presented here don't work because the logic in the selection has changed.

I tried a couple of changes but could not get it done, can anyone help out?

@LangLangBart
Copy link
Contributor

I tried a couple of changes but could not get it done, can anyone help out?

The diff below is based on the current version1, pressing ⌃ Control + X should allow the
user to execute a history entry immediately, while pressing the ⏎ Enter key should place
the command onto the buffer stack for editing. The accept-or-print-query2 action should also
work for returning the query when there is no selection, but not for immediate execution.

explanation
# Ensure that the matched regex is assigned to the 'MATCH' variable, not 'BASH_REMATCH'.
no_bash_rematch

# Set 'ctrl-x' to complete 'fzf'.
--expect=ctrl-x

# This line checks if only one line is returned. If so, it means that the user either used the 'ctrl-x' key or the 'accept-or-print-query' action on an empty list.
# Please note that the use of the '--print-query' flag is not covered in this diff.
[[ $(sed -n '$=' <<<$selected) -eq 1 ]]

# This line removes the first line and applies a regex to the rest. The result will be automatically assigned to 'MATCH'.
# See regex discussion: https://github.com/junegunn/fzf/issues/3591
[[ $(sed '1d' <<<$selected) =~ ^[[:space:]]*[[:digit:]]+ ]]

# This line accepts the line only if the first line was 'ctrl-x'.
[[ $(sed 'q' <<<$selected) == "ctrl-x" ]] && zle accept-line
--- a/shell/key-bindings.zsh
+++ b/shell/key-bindings.zsh
@@ -96,17 +96,17 @@ bindkey -M viins '\ec' fzf-cd-widget
 
 # CTRL-R - Paste the selected command from history into the command line
 fzf-history-widget() {
-  local selected num
-  setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
+  local selected
+  setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases no_bash_rematch 2> /dev/null
   selected="$(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
-    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m" $(__fzfcmd))"
+    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} --expect=ctrl-x  +m" $(__fzfcmd))"
   local ret=$?
-  if [ -n "$selected" ]; then
-    num=$(awk '{print $1}' <<< "$selected")
-    if [[ "$num" =~ '^[1-9][0-9]*\*?$' ]]; then
-      zle vi-fetch-history -n ${num%\*}
-    else # selected is a custom query, not from history
-      LBUFFER="$selected"
+  if [[ -n $selected ]]; then
+    if [[ $(sed -n '$=' <<<"$selected") -eq 1 ]]; then
+      [[ $selected != "ctrl-x" ]] && LBUFFER="$selected"
+    elif [[ $(sed '1d' <<<"$selected") =~ ^[[:space:]]*[[:digit:]]+ ]]; then
+      zle vi-fetch-history -n "$MATCH"
+      [[ $(sed 'q' <<<"$selected") == "ctrl-x" ]] && zle accept-line
     fi
   fi
   zle reset-prompt

Footnotes

  1. shell/key-bindings.zsh

  2. CHANGELOG.md 0.45.0

@51616
Copy link

51616 commented Apr 19, 2024

A bit off topic here and I don't know if this is even a valid question for fzf.
Is it possible to make this a default behavior for 'enter'?

@meinzer1899
Copy link

Thanks @LangLangBart for the diff and the explanation in #477 (comment) ❤️
It works with fzf 0.45.0 up to at least 0.53.0.

Like in #477 (comment), I like the immediate execution of the command to be the default, and to edit the prompt by a shortcut (e.g. ctrl-e).

You can adapt @LangLangBart's code snippet like this:

selected="$(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
-    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} --expect=ctrl-x  +m" $(__fzfcmd))"
+    FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} --expect=ctrl-e  +m" $(__fzfcmd))"
   local ret=$?

    if [[ $(sed -n '$=' <<<"$selected") -eq 1 ]]; then
-      [[ $selected != "ctrl-x" ]] && LBUFFER="$selected"
+      [[ $selected == "ctrl-e" ]] && LBUFFER="$selected"
    elif [[ $(sed '1d' <<<"$selected") =~ ^[[:space:]]*[[:digit:]]+ ]]; then
      zle vi-fetch-history -n "$MATCH"
-     [[ $(sed 'q' <<<"$selected") == "ctrl-x" ]] && zle accept-line
+     [[ $(sed 'q' <<<"$selected") != "ctrl-e" ]] && zle accept-line
     fi

@mbhall88
Copy link

May I request a noob summary? There's a lot of diffs flying around here and I'm not sure what I am supposed to do with them?

Essentially I want to be able to press Enter to execute a selected command in the history and Tab to copy that command to the command line. How do I achieve this?

@scintillavoy
Copy link

@mbhall88 You can do what junegunn suggested in the previous comment, replacing ctrl-x with enter (two occurrences), if you can stick to version <= 0.43.0. As of 0.44.0, the patch won't work and you can try what other people suggested. I haven't tried, but fixed the version of fzf to 0.43.0.

For zsh, this is what we can do:

diff --git a/shell/key-bindings.zsh b/shell/key-bindings.zsh
index ea65c0c..f1fb720 100644
--- a/shell/key-bindings.zsh
+++ b/shell/key-bindings.zsh
@@ -47,12 +47,18 @@ bindkey '\ec' fzf-cd-widget
 fzf-history-widget() {
   local selected num
   setopt localoptions noglobsubst pipefail
-  selected=( $(fc -l 1 | eval "$(__fzfcmd) +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r $FZF_CTRL_R_OPTS -q ${(q)LBUFFER}") )
+  selected=( $(fc -l 1 | eval "$(__fzfcmd) +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r --expect=ctrl-x $FZF_CTRL_R_OPTS -q ${(q)LBUFFER}") )
   local ret=$?
   if [ -n "$selected" ]; then
+    local accept=0
+    if [[ $selected[1] = ctrl-x ]]; then
+      accept=1
+      shift selected
+    fi
     num=$selected[1]
     if [ -n "$num" ]; then
       zle vi-fetch-history -n $num
+      [[ $accept = 1 ]] && zle accept-line
     fi
   fi
   zle redisplay

but I couldn't find a solution for bash.

@mbhall88
Copy link

I'm using the latest version (0.53.0).

What I was asking though is what am I supposed to do with the diff? For instance, I installed fzf from the precompiled binary, so I don't have that key bindings folder.

Am I supposed to add that function to my zsh profile and then apply the diff to it or something? Sorry for asking the silly question, just thought there might be some others like me who aren't quite sure what they're supposed to do.

@scintillavoy
Copy link

@mbhall88 I didn't expect you to install it from the precompiled binary. 😂 There was no "Binary releases" section in the readme before.

It seems "Setting up shell integration" section in the readme has been changed and if you follow the instruction, the script for key bindings and completion would be generated and loaded whenever you launch a new shell. However, when this issue was open, the scripts should be sourced from the /shell directory as denoted in the readme:

Note

--bash, --zsh, and --fish options are only available in fzf 0.48.0 or
later. If you have an older version of fzf, or want finer control, you can
source individual script files in the /shell directory. The
location of the files may vary depending on the package manager you use.
Please refer to the package documentation for more information.
(e.g. apt show fzf)

What the comments here are saying is to apply the diff to the scripts in the /shell directory and load them, instead of doing source <(fzf --zsh). To do so, you can use one of the package managers or download source code, and try:

# Change the working directory
# I supposed you downloaded the source code here. If you used a package manager, try `which fzf`.
cd fzf-0.53.0

# Save the patch to a file (`key-bindings.patch` for example).

# Apply the patch.
git apply key-bindings.patch

# Execute the install script.
./install

Meanwhile, I decided to stay with the version 0.43.0 because:

  • I have to apply the patch every time I update the fzf.
  • Future updates may break the patch.
  • I don't need any feature introduced after 0.43.0 (maybe I'm using just a few features of fzf).

... and I also hope this feature is supported officially.

@tgdfool2
Copy link

tgdfool2 commented Jul 5, 2024

Please find here the current patch I'm using for version 0.53.0, adjusted to use Enter to execute the selected command and CTRL-E to edit the command

--- fzf-0.53.0-key-bindings.zsh.ORIG	2024-07-05 07:31:11.836273594 +0200
+++ fzf-0.53.0-key-bindings.zsh	2024-07-05 07:46:33.741715361 +0200
@@ -106,26 +106,27 @@
 
 # CTRL-R - Paste the selected command from history into the command line
 fzf-history-widget() {
-  local selected num
-  setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
+  local selected
+  setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases no_bash_rematch 2> /dev/null
   # Ensure the associative history array, which maps event numbers to the full
   # history lines, is loaded, and that Perl is installed for multi-line output.
   if zmodload -F zsh/parameter p:history 2>/dev/null && (( ${#commands[perl]} )); then
     selected="$(printf '%1$s\t%2$s\000' "${(vk)history[@]}" |
       perl -0 -ne 'if (!$seen{(/^\s*[0-9]+\**\s+(.*)/, $1)}++) { s/\n/\n\t/gm; print; }' |
-      FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m --read0") \
+      FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} --expect=ctrl-e +m --read0") \
       FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
   else
     selected="$(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
-      FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m") \
+      FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} --expect=ctrl-e +m") \
       FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
   fi
   local ret=$?
-  if [ -n "$selected" ]; then
-    if num=$(awk '{print $1; exit}' <<< "$selected" | grep -o '^[1-9][0-9]*'); then
-      zle vi-fetch-history -n $num
-    else # selected is a custom query, not from history
-      LBUFFER="$selected"
+  if [[ -n $selected ]]; then
+    if [[ $(sed -n '$=' <<<"$selected") -eq 1 ]]; then
+      [[ $selected == "ctrl-e" ]] && LBUFFER="$selected"
+    elif [[ $(sed '1d' <<<"$selected") =~ ^[[:space:]]*[[:digit:]]+ ]]; then
+      zle vi-fetch-history -n "$MATCH"
+      [[ $(sed 'q' <<<"$selected") != "ctrl-e" ]] && zle accept-line
     fi
   fi
   zle reset-prompt

Slowly thinking to pin it to this version, not to have to adjust and reapply the patch every time a new version is released ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests