4ae1e1
4ae1e1

Reputation: 7634

zsh command line editor: go back to previous lines when entering a command

Note that I'm NOT talking about previous lines in the history. I'm talking about previous lines in the current multiline command.

When I type a multiline command in zsh, for instance (_ indicates the cursor):

$ for i in {1..5}; do
for> echo i_

At this point I might change my mind and want to let i loop through {1..10} instead, so I need to go back to the previous line. However, I can't figure out how to do this, as ⌫, or ←, or C-b, or whatever I can think of all stops at the beginning of the second line. So, is it possible at all to move back? Thanks in advance.

In fact, this is not limited to zsh. For instance, I've never figured this out in bash either.

I've already spent a fair amount of time Googling without any findings, so please excuse me and blame my Google-fu if this is obvious.


For your reference, I'm using Emacs keybinding, and

$ bindkey | grep delete
"^D" delete-char-or-list
"^H" backward-delete-char
"^[[3~" delete-char
"^?" backward-delete-char

Not sure whether this will help.

Upvotes: 6

Views: 2774

Answers (3)

branquito
branquito

Reputation: 4044

Actually this is possible.

While on that second line, pres Esc + X in order to execute a command, and type push-line-or-edit (note that you can use Tab key for completion) , then press return.

The prompt will change to a traditional one ditching your for> part from continuation line, and you will have a new buffer filled with all your previously typed lines, which of course now you can easily edit using well known C-a or C-b keys.

Upvotes: 3

ZyX
ZyX

Reputation: 53644

I do not think this is possible, but here are other workarounds:

  1. You can just write everything in one line. It is not easy to edit, but multiline strings are not easily editable either.
  2. You can bind some key (e.g. <C-Enter> if you can tell your terminal not to output <C-m> in this case) to

    function _-add-newline()
    {
        LBUFFER="$LBUFFER"$'\n'
    }
    zle -N add-newline _-add-newline
    bindkey "\Ca" add-newline
    

    . This way if you press <C-a> newline will appear in the buffer, but it will not trigger accept-line widget and previous line will still be editable. You can e.g. use left/right arrows to move to it.

  3. If you type for x in y ; do<CR>echo foo<CR><C-c><Up> you will see for x in y ; do\necho foo in your buffer and all text will be editable. Note: you need <CR> to preserve last line (line aborted by <C-c> is not saved) and <C-c> to abort input; this variant will not work if you have already typed done (first <CR> will run the cycle). In last case you may discard last line with <C-c> and retype it.
  4. Theoretically you may override accept-line widget with a code that parses your input and determines whether you have written command completely and if not then it will just append \n to the buffer (like in the above function) and do not run original accept-line, otherwise original accept-line is run. This variant is much harder to implement though, so I am saying “theoretically”.

Upvotes: 1

merlin2011
merlin2011

Reputation: 75585

Here is how you would visual mode in bash's vim mode.

Inside .bashrc put the following lines.

set -o vi

# I do not remember which one of these is used by `v`, so I set both
export VISUAL=/usr/bin/nano
export EDITOR=/usr/bin/nano

Reload bash, and then push Esc to go into normal mode. Type v, and nano should load. You can now enter your multiline command into nano. When you save and exit, the multiline command will be executed.


For zsh, the following lines in your rc may do it. I stole the last three lines from this answer and tested it on my zsh.

bindkey -v
autoload -U edit-command-line
zle -N edit-command-line
bindkey -M vicmd v edit-command-line

Upvotes: 5

Related Questions