1

I develop a zsh plugin which is a port of vim-easymotion for zsh in vi emulation mode. Since the zsh line editor has multiline support, I added support for line motions (j and k). The plugin should also work for vi operator pending mode (for example in deletion mode), so I used the MARK and CURSOR variables of zsh to extend the user selection to complete lines (so a line motion will always delete or change whole lines and not only parts of them). This is the behavior a vim user would expect. However, the cursor position after a change/deletion operation is wrong. The cursor should be on the first word on the next line after a deleted block of lines. Instead, the cursor is at the end of the last line before the block of lines. I don't see a possibility to change the cursor after the operator pending mode since any cursor change will be part of the region for the selected operator.

So my question is: Does anyone know how the cursor position can be changed in a zsh custom widget after a vi operator was executed?

Update:

To make the question understandable, I tried to create a minimal example of the problem:

This small zsh script can be sourced:

function test-widget () {
    MARK=3
    CURSOR=6
}

zle -N test-widget

bindkey -M vicmd ' ' test-widget

If you are in zsh's vicmd/normal mode (bindkey -v to activate the vi emulation and <esc> to enter the command mode), you can now press <space> to set the cursor to the sixth character in the current edit buffer. If you enter a vi operator like d followed by <space>, everything from the third to the sixth character will be deleted (due to the setting of the MARK variable).

Deletion example: Consider your edit buffer is (cursor position marked with []):

[a]bc def ghi

and you press d<space>, you will get:

abc[f] ghi

However, I would like to change the position of the cursor after the deletion operation in the same widget call, e.g.:

abcf [g]hi

I don't know how this would be possible since all changes to CURSOR would affect the range of deleted text.

How can I achieve a deletion operation + a cursor positioning?

2
  • I have updated my question trying to make things a bit easier to understand.
    – IngoMeyer
    Commented Jul 28, 2020 at 6:07
  • Thanks. It's much clearer with that example. It sounds to me that you would have to wrap the widget bound to d (vi-delete) here. You could also try asking on the zsh-users mailing list. Commented Jul 28, 2020 at 6:22

1 Answer 1

0

It sounds to me you would have to redefine the widgets that take a motion, like the ones bound to d, c, y, gu, gU, g~...

Something like:

test-widget () {
  MARK=3
  CURSOR=6
  MOVE_AFTER_MOTION=2
}
zle -N test-widget
bindkey -M vicmd ' ' test-widget

for widget in vi-{delete,change,yank,{down,oper-swap,up}-case}; do
  $widget () {
    local ret
    zle ".$WIDGET"; ret=$?
    if (($+MOVE_AFTER_MOTION)); then
      (( CURSOR += MOVE_AFTER_MOTION ))
      unset MOVE_AFTER_MOTION
    fi
    return "$ret"
  }
  zle -N $widget
done

You may also have to think of what to do when $NUMERIC is not 1.

3
  • Works perfectly for my use case, thanks!
    – IngoMeyer
    Commented Jul 28, 2020 at 6:35
  • Does substitute / s take a motion?
    – IngoMeyer
    Commented Jul 28, 2020 at 7:28
  • No, sorry my bad. Fixed now. Commented Jul 28, 2020 at 7:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .