Skip to content

Instantly share code, notes, and snippets.

@davisdude
Last active January 10, 2017 04:34
Show Gist options
  • Save davisdude/c8623b5aebbf8033b604168d4a17eab1 to your computer and use it in GitHub Desktop.
Save davisdude/c8623b5aebbf8033b604168d4a17eab1 to your computer and use it in GitHub Desktop.
A vim motion for "to-last"
" Add 'last' motion
" <count> is the number from last, i.e. 2gtl goes to the 2nd to last l with the `t` motion
" <mode> is the mode, i.e. 2vgtl visually selects from the cursor to the second to last l
" <count><mode>g<motion><char>
function s:strfind( str, find, start, stop, increment )
let l:i = a:start
let l:len = len( a:str )
let l:lenfind = len( a:find )
while ( a:increment > 0 && l:i < a:stop ) || ( a:increment < 0 && l:i > a:stop )
if strpart( a:str, l:i, l:lenfind ) == a:find
return l:i
endif
let l:i += a:increment
endwhile
return -1
endfunction
function HandleMotion( textMode, motion, count )
let l:key = nr2char( getchar() ) " Read in input
let l:line = getline( '.' ) " Get current line
let l:column = col( '.' )
let l:count = a:count
let l:before = strpart( l:line, 0, l:column )
let l:after = strpart( l:line, l:column )
if tolower( a:motion ) ==# a:motion " Motion is lowercase (forwards)
let l:lowercase = 1
let l:start = len( l:after )
let l:stop = 0
let l:increment = -1
let l:part = l:after
let l:beforeLen = len( l:before )
let l:offset = 0
else " Uppercase (backwards)
let l:lowercase = 0
let l:start = 0
let l:stop = len( l:before )
let l:increment = 1
let l:part = l:before
let l:beforeLen = 0
let l:offset = 2
endif
let l:match = l:start
while l:count > 0 && l:match > -1
let l:match = s:strfind( l:part, l:key, l:match, l:stop, l:increment )
let l:match += l:increment
let l:count -= 1
endwhile
let l:match -= l:increment
if l:match != -1
let l:match += l:offset
if a:motion ==# 'f'
let l:match -= l:increment
endif
if l:lowercase
if a:textMode ==# 'c' || a:textMode ==# 'd'
let l:match -= l:increment
endif
endif
let l:newPos = l:match + l:beforeLen
if a:textMode != ''
return a:textMode . l:newPos . '|'
else
" Do nothing. When not given a textMode but given a count, the
" strings are coerced. Would prefer to use <ESC> character, but it
" is interpreted by letter
return 'vv' . l:newPos . '|'
endif
endif
" o moves to start of selection, so the count won't affect the visual selection
return 'vov'
endfunction
for textMode in [ '', 'y', 'd', 'c', 'v' ]
for motion in [ 't', 'f', 'T', 'F' ]
execute( "nnoremap <expr> " . textMode . "g" . motion . " HandleMotion( '" . textMode . "', '" . motion . "', v:count1 )" )
endfor
endfor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment