From 3449b9f8eda8a1c8c6fa3d1e9a7766524ecfe387 Mon Sep 17 00:00:00 2001 From: Luiz Gonzaga dos Santos Filho Date: Thu, 2 May 2013 16:16:12 +0200 Subject: [PATCH] Updating next-textobject to Steve Losh's new version --- vim/plugin/settings/next-textobject.vim | 149 ++++++++++++++++++++---- 1 file changed, 128 insertions(+), 21 deletions(-) diff --git a/vim/plugin/settings/next-textobject.vim b/vim/plugin/settings/next-textobject.vim index 1189a91..836d8f6 100644 --- a/vim/plugin/settings/next-textobject.vim +++ b/vim/plugin/settings/next-textobject.vim @@ -1,29 +1,136 @@ " Stolen from Steve Losh -" https://bitbucket.org/sjl/dotfiles/src/tip/vim/.vimrc +" https://github.com/sjl/dotfiles/blob/master/vim/vimrc#L1380 " -" Motion for "next/last object". For example, "din(" would go to the next "()" -" pair and delete its contents. -onoremap an :call NextTextObject('a', 'f') -xnoremap an :call NextTextObject('a', 'f') -onoremap in :call NextTextObject('i', 'f') -xnoremap in :call NextTextObject('i', 'f') +" 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', '?') -onoremap al :call NextTextObject('a', 'F') -xnoremap al :call NextTextObject('a', 'F') -onoremap il :call NextTextObject('i', 'F') -xnoremap il :call NextTextObject('i', 'F') function! s:NextTextObject(motion, dir) - let c = nr2char(getchar()) + let c = nr2char(getchar()) + let d = '' - if c ==# "b" - let c = "(" - elseif c ==# "B" - let c = "{" - elseif c ==# "d" - let c = "[" - endif + 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 - exe "normal! ".a:dir.c."v".a:motion.c + " 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 -