" Stolen from Steve Losh " https://github.com/sjl/dotfiles/blob/master/vim/vimrc#L1380 " " Motion for "next/last object". "Last" here means "previous", not "final". " Unfortunately the "p" motion was already taken for paragraphs. " " Next acts on the next object of the given type, last acts on the previous " object of the given type. These don't necessarily have to be in the current " line. " " Currently works for (, [, {, and their shortcuts b, r, B. " " Next kind of works for ' and " as long as there are no escaped versions of " them in the string (TODO: fix that). Last is currently broken for quotes " (TODO: fix that). " " Some examples (C marks cursor positions, V means visually selected): " " din' -> delete in next single quotes foo = bar('spam') " C " foo = bar('') " C " " canb -> change around next parens foo = bar('spam') " C " foo = bar " C " " vin" -> select inside next double quotes print "hello ", name " C " print "hello ", name " VVVVVV onoremap an :call NextTextObject('a', '/') xnoremap an :call NextTextObject('a', '/') onoremap in :call NextTextObject('i', '/') xnoremap in :call NextTextObject('i', '/') onoremap al :call NextTextObject('a', '?') xnoremap al :call NextTextObject('a', '?') onoremap il :call NextTextObject('i', '?') xnoremap il :call NextTextObject('i', '?') function! s:NextTextObject(motion, dir) let c = nr2char(getchar()) let d = '' if c ==# "b" || c ==# "(" || c ==# ")" let c = "(" elseif c ==# "B" || c ==# "{" || c ==# "}" let c = "{" elseif c ==# "r" || c ==# "[" || c ==# "]" let c = "[" elseif c ==# "'" let c = "'" elseif c ==# '"' let c = '"' else return endif " Find the next opening-whatever. execute "normal! " . a:dir . c . "\" if a:motion ==# 'a' " If we're doing an 'around' method, we just need to select around it " and we can bail out to Vim. execute "normal! va" . c else " Otherwise we're looking at an 'inside' motion. Unfortunately these " get tricky when you're dealing with an empty set of delimiters because " Vim does the wrong thing when you say vi(. let open = '' let close = '' if c ==# "(" let open = "(" let close = ")" elseif c ==# "{" let open = "{" let close = "}" elseif c ==# "[" let open = "\\[" let close = "\\]" elseif c ==# "'" let open = "'" let close = "'" elseif c ==# '"' let open = '"' let close = '"' endif " We'll start at the current delimiter. let start_pos = getpos('.') let start_l = start_pos[1] let start_c = start_pos[2] " Then we'll find it's matching end delimiter. if c ==# "'" || c ==# '"' " searchpairpos() doesn't work for quotes, because fuck me. let end_pos = searchpos(open) else let end_pos = searchpairpos(open, '', close) endif let end_l = end_pos[0] let end_c = end_pos[1] call setpos('.', start_pos) if start_l == end_l && start_c == (end_c - 1) " We're in an empty set of delimiters. We'll append an "x" " character and select that so most Vim commands will do something " sane. v is gonna be weird, and so is y. Oh well. execute "normal! ax\\" execute "normal! vi" . c elseif start_l == end_l && start_c == (end_c - 2) " We're on a set of delimiters that contain a single, non-newline " character. We can just select that and we're done. execute "normal! vi" . c else " Otherwise these delimiters contain something. But we're still not " sure Vim's gonna work, because if they contain nothing but " newlines Vim still does the wrong thing. So we'll manually select " the guts ourselves. let whichwrap = &whichwrap set whichwrap+=h,l execute "normal! va" . c . "hol" let &whichwrap = whichwrap endif endif endfunction