6

When editing multi-line commands, with escaped newlines, I cannot move up lines.

For example, suppose I enter echo \ one one line, then I press Enter, and then I want to edit the echo \ part of the command. Pressing Up doesn't move back to the first command line.

This works for long commands which wrap, but not with escaped newlines:

_physical_up_line()   { zle backward-char -n $COLUMNS }
_physical_down_line() { zle forward-char  -n $COLUMNS }
zle -N physical-up-line _physical_up_line
zle -N physical-down-line _physical_down_line
bindkey -M vicmd "R" physical-up-line
bindkey -M vicmd "N" physical-down-line
Leandros
  • 712
  • 2
  • 7
  • 16

5 Answers5

5

When you press Enter (accept-line command), the current line is parsed and scheduled for execution. If the line is syntactically incomplete (e.g. echo \ or for x in foo), it isn't executed, but it's already stored. You can see that zsh is in this state because it shows the PS2 prompt instead of the usual PS1.

As far as I know, there's no built-in way to edit such stored lines. It should be doable by storing the current line without executing it and recalling the previous history line for editing.

The easiest way to get at the previous line is to make sure that the current line is unfinished (e.g. type \ at the end), accept it (press Enter), then cancel it (press Ctrl+C). Then you can recall the whole stored command as a single history line as a single multi-line buffer by pressing Up.

3

Assuming you have the default emacs keybindings, try using Alt+Enter instead of plain Enter to schedule a line for execution.
For me when the lines are added this way I can easily move up and down those lines. e.g. When at a zsh terminal prompt:

% cat << EOF<ALT-ENTER>
first line<ALT-ENTER>
second line<ALT-ENTER>
thi
   ^ 
   pressing <UP> moves up a line like you would expect
2

One of the Zsh User Guides mentions that you could press Escape followed by Enter when you intend to create a line break, to avoid entering into a multiprompt(PS2) where you can no longer edit the previous line.

This also works only with the default emacs keybindings.

However, I find the answer from the_velour_fog should be marked as the accepted answer as it is even more elegant!

gerrnot
  • 21
0

From the zsh user guide on multiline editing:

Suppose you've already gone through a few continuation lines in the normal way with $PS2's? You can't scroll back then, even though the block hasn't yet been edited. There's a magic way of turning all those continuation lines into a single block: the editor command push-line-or-edit.

If you forgot to use Alt + Enter then this might be useful. By default it's not bound to any key, so there are two options:

  1. Call it using Alt + X (which executes commands like push-line-or-edit by name) and type in push-line-or-edit and hit Return.

  2. Bind it permenantly to a keybind, the zsh guide recommends Alt + Q (since the default mapping for ^q is push-line).

Note that when used in a single line, push-line-or-edit will act like push-line and will "save" the current command line, allow you to execute another command and then restore the previous command.

-2

ZSH: Move cursor to line 0 / position 0 of a prompt with ctrl-g (tested in vi mode/insert mode):

zsh-move-to-first-line() {
  local line_count=$(echo "$BUFFER" | wc -l)
  local current_line=1
  while [ "$current_line" -lt "$line_count" ]; do
    zle up-line-or-history
    current_line=$((current_line+1))
  done
  zle beginning-of-line
}

zle -N zsh-move-to-first-line bindkey '^G' zsh-move-to-first-line

Inverse: Go from position 0, line 0 to last line last position. Use ctrl-h

zsh-move-to-last-line() {
  local line_count=$(echo "$BUFFER" | wc -l)
  local current_line=1
  while [ "$current_line" -lt "$line_count" ]; do
    zle down-line-or-history
    current_line=$((current_line+1))
  done
  zle end-of-line
}

zle -N zsh-move-to-last-line bindkey '^H' zsh-move-to-last-line

Source: ChatGpt4