Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

vim: update next/last motions

  • Loading branch information...
commit ac2e475a5954292019f0ac2ba290fdc0f284df63 1 parent a05f324
@majutsushi authored
Showing with 141 additions and 23 deletions.
  1. +141 −23 vim/vim/plugin/nextlast.vim
View
164 vim/vim/plugin/nextlast.vim
@@ -1,12 +1,18 @@
+" From https://github.com/sjl/dotfiles/blob/master/vim/vimrc#L1415-1583
" Next and Last {{{
-
+"
" 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 in the current line, last acts
-" on the previous object of the given type in the current line.
+" 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.
"
-" Currently only works for (, [, {, b, r, B, ', and ".
+" 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):
"
@@ -20,33 +26,145 @@
" foo = bar
" C
"
-" vil" -> select inside last double quotes print "hello ", name
-" C
+" vin" -> select inside next double quotes print "hello ", name
+" C
" print "hello ", name
" VVVVVV
-onoremap an :<c-u>call <SID>NextTextObject('a', 'f')<cr>
-xnoremap an :<c-u>call <SID>NextTextObject('a', 'f')<cr>
-onoremap in :<c-u>call <SID>NextTextObject('i', 'f')<cr>
-xnoremap in :<c-u>call <SID>NextTextObject('i', 'f')<cr>
+onoremap an :<c-u>call <SID>NextTextObject('a', '/')<cr>
+xnoremap an :<c-u>call <SID>NextTextObject('a', '/')<cr>
+onoremap in :<c-u>call <SID>NextTextObject('i', '/')<cr>
+xnoremap in :<c-u>call <SID>NextTextObject('i', '/')<cr>
+
+onoremap al :<c-u>call <SID>NextTextObject('a', '?')<cr>
+xnoremap al :<c-u>call <SID>NextTextObject('a', '?')<cr>
+onoremap il :<c-u>call <SID>NextTextObject('i', '?')<cr>
+xnoremap il :<c-u>call <SID>NextTextObject('i', '?')<cr>
-onoremap al :<c-u>call <SID>NextTextObject('a', 'F')<cr>
-xnoremap al :<c-u>call <SID>NextTextObject('a', 'F')<cr>
-onoremap il :<c-u>call <SID>NextTextObject('i', 'F')<cr>
-xnoremap il :<c-u>call <SID>NextTextObject('i', 'F')<cr>
function! s:NextTextObject(motion, dir)
- let c = nr2char(getchar())
+ 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 . "\<cr>"
+
+ 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\<esc>\<left>"
+ 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
+
+" }}}
+" Numbers {{{
+
+" Motion for numbers. Great for CSS. Lets you do things like this:
+"
+" margin-top: 200px; -> daN -> margin-top: px;
+" ^ ^
+" TODO: Handle floats.
+
+onoremap N :<c-u>call <SID>NumberTextObject(0)<cr>
+xnoremap N :<c-u>call <SID>NumberTextObject(0)<cr>
+onoremap aN :<c-u>call <SID>NumberTextObject(1)<cr>
+xnoremap aN :<c-u>call <SID>NumberTextObject(1)<cr>
+onoremap iN :<c-u>call <SID>NumberTextObject(1)<cr>
+xnoremap iN :<c-u>call <SID>NumberTextObject(1)<cr>
+
+function! s:NumberTextObject(whole)
+ normal! v
+
+ while getline('.')[col('.')] =~# '\v[0-9]'
+ normal! l
+ endwhile
- if c ==# "b"
- let c = "("
- elseif c ==# "B"
- let c = "{"
- elseif c ==# "r"
- let c = "["
- endif
+ if a:whole
+ normal! o
- exe "normal! " . a:dir . c . "v" . a:motion.c
+ while col('.') > 1 && getline('.')[col('.') - 2] =~# '\v[0-9]'
+ normal! h
+ endwhile
+ endif
endfunction
" }}}
Please sign in to comment.
Something went wrong with that request. Please try again.