Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 457
4ae1e1 Avatar asked May 24 '14 01:05

4ae1e1


People also ask

How do I go to a previous line in command prompt?

You can go back to your previous location with the Go > Back command or Ctrl+Alt+-. You can also see the type definition if you press Ctrl (Cmd on macOS) when you are hovering over the type.

How do you end a multi line command in shell?

Using a Backslash. The backslash (\) is an escape character that instructs the shell not to interpret the next character. If the next character is a newline, the shell will read the statement as not having reached its end. This allows a statement to span multiple lines.

What is ZLE zsh?

The Zsh Line Editor (ZLE) is simply your command prompt. It's your interface to the shell interpreter, allowing you to write and edit your mind-blowing commands. It also allows you to use keystrokes to execute ZLE commands, more commonly called widgets.

How do I move the cursor to a previous line in Linux?

Move Cursor on The Command Line Ctrl+A or Home – moves the cursor to the start of a line. Ctrl+E or End – moves the cursor to the end of the line. Ctrl+B or Left Arrow – moves the cursor back one character at a time. Ctrl+F or Right Arrow – moves the cursor forward one character at a time.


3 Answers

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
like image 61
merlin2011 Avatar answered Nov 15 '22 08:11

merlin2011


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.

like image 41
branquito Avatar answered Nov 15 '22 07:11

branquito


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”.
like image 39
ZyX Avatar answered Nov 15 '22 07:11

ZyX