Pandu
Pandu

Reputation: 1686

What's causing this weird movement behavior in visual mode?

I'm trying to write a plugin that involves moving up or down a variable number of lines. I want to create something that can be used as a custom motion, for use in normal, visual, and operator-pending modes.

Here's a little snippet of the idea: (the "3" here is for the example. The goal is to move a variable number of lines, but that's not relevant to what my question is.)

function! MoveDownThreeLines(mode)
    if a:mode == 'v'
        normal! gv
    endif

    if a:mode == 'o'
        normal! V3j
    else
        normal! 3j
    endif
endfunction

nnoremap w :call MoveDownThreeLines('n')<CR>
vnoremap w :call MoveDownThreeLines('v')<CR>
onoremap w :call MoveDownThreeLines('o')<CR>

The goal for this snippet is to create a custom motion that moves down three lines. I want the behavior of w, then, to be identical to that of 3j. In normal mode, it moves down three lines like expected, and in operator-pending mode, the operator acts on four lines (the current and the three below) like expected. (Though I had to force the motion to be linewise using V, which is strange, because j is already a linewise motion. Does anyone know why that is?)

The problem is in visual mode. If I enter visual mode, then press w, the cursor moves down three lines, like expected. If I press w again, though, it only moves down two lines.

Similarly, if I enter visual mode and extend the selection downward in some other way, pressing w only moves down two lines. A strange other problem is if I enter visual mode and extend the selection upward, w changes my selection to be from the bottom of the old selection to two lines down. (The goal would be for w to move the top of the selection down three lines, just like if I used 3j.)

How do I make w move down three lines in all cases?

PS: Ingo Karkat, if you happen to read this, thank you for your quite well-commented camelcasemotion script -- this takes some ideas from that.

Upvotes: 0

Views: 199

Answers (1)

Ingo Karkat
Ingo Karkat

Reputation: 172570

You should prefix all (not just the visual mode one, as Peter Rincker commented) mappings with <C-u>, since you probably want to supply a [count] with your mappings (instead of the hard-coded 3).

In normal mode, a count (of e.g. 3) gets translated into :.,+2 when : is pressed. Likewise, visual mode : prepends :'<,'>. The :call command executes the function once for each line, which typically is not what you want. (You can change that by appending the range keyword to the :function definition, but avoiding that the count gets translated into a range lets you use it even more flexibly; you can use it inside your function with v:count.)

Upvotes: 3

Related Questions