Reputation: 1686
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
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