Interesting. Vertical analogues of word motion commands. I'm not aware of any native way to do this so my first thought is to just start with regex searches. Turns out doing such searches in one step is not trivial but still viable.
First a couple notes:
- To make it quite a bit easier I'm using (vertical)
WORD
motions (W
,B
,E
) rather than word
(w
,b
,e
). The latter is [a-zA-Z0-9_]+
OR a sequence of other non-blank characters while a WORD
is just \S+
. In fact, your example is actually demonstrating WORD
motion because otherwise the second e
would end up on the L
not the @
. [Note: the question originally referred to w
,b
,e
but has since been edited.]
- The patterns will use 'very magic' flag to reduce length and improve readability.
- To start, my pattern will be based on a fixed column number (4). This will be made variable later.
Starting with E
...
\v%4v\S.*(\n.{3}\s|\n.{,3}\n|%$)
\v
- very magic
%4v\S
- in column 4 there must be a non-whitespace character (our WORD
char)
.*
- slurp up anything remaining on the line
(..|..|..)
- parens indicating any of the following three patterns are allowed on the next line...
\n.{3}\s
- after the current line's newline the next line starts with three characters followed by whitespace (in the cursor column)
\n.{,3}\n
- after the newline there are three or fewer characters total on the next line
%$
- there is no next line as indicated by this end of file token (allowing vertical E
motion to end on the last line of the buffer)
This doesn't do us much good as a standalone pattern. The best thing to do is put it in a function. The key lines will look like:
let curcol = getcurpos()[2]
let prevcol = curcol - 1
call searchpos('\v%' . curcol . 'v\S.*\n(.{' . prevcol . '}\s|.{,' . prevcol . '}\n)')
This just looks at whatever column the cursor is currently on and inserts that and one less into the previously discussed pattern.
Combining the above pattern and similar patterns for the other motions and parameterizing by motion types W
, B
and E
I have this:
func! VertWordMotion(motion)
let curcol = getcurpos()[2]
let prevcol = curcol - 1
if a:motion == 'E'
call searchpos('\v%' . curcol . 'v\S.*(\n.{' . prevcol . '}\s|\n.{,' . prevcol . '}\n|%$)', 'W')
elseif a:motion == 'B'
call searchpos('\v(%' . curcol . 'v\s.*\n|^.{,' . prevcol . '}\n|%^).{' . prevcol . '}\zs\S', 'bW')
elseif a:motion == 'W'
call searchpos('\v(%' . curcol . 'v\s.*|^.{,' . prevcol . '})\n.{' . prevcol . '}\zs\S', 'W')
else
echohl ErrorMsg | echo 'Not a valid motion: ' . a:motion | echohl None
endif
endfunc
Finally, create mappings like these:
nnoremap <leader>E :call VertWordMotion('E')<CR>
nnoremap <leader>B :call VertWordMotion('B')<CR>
nnoremap <leader>W :call VertWordMotion('W')<CR>