Skip to content

Commit

Permalink
vim: update next/last motions
Browse files Browse the repository at this point in the history
  • Loading branch information
majutsushi committed Mar 22, 2013
1 parent a05f324 commit ac2e475
Showing 1 changed file with 141 additions and 23 deletions.
164 changes: 141 additions & 23 deletions vim/vim/plugin/nextlast.vim
@@ -1,12 +1,18 @@
" From https://github.com/sjl/dotfiles/blob/master/vim/vimrc#L1415-1583
" Next and Last {{{ " Next and Last {{{

"
" Motion for "next/last object". "Last" here means "previous", not "final". " Motion for "next/last object". "Last" here means "previous", not "final".
" Unfortunately the "p" motion was already taken for paragraphs. " 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 " Next acts on the next object of the given type, last acts on the previous
" on the previous object of the given type in the current line. " 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): " Some examples (C marks cursor positions, V means visually selected):
" "
Expand All @@ -20,33 +26,145 @@
" foo = bar " foo = bar
" C " C
" "
" vil" -> select inside last double quotes print "hello ", name " vin" -> select inside next double quotes print "hello ", name
" C " C
" print "hello ", name " print "hello ", name
" VVVVVV " VVVVVV


onoremap an :<c-u>call <SID>NextTextObject('a', 'f')<cr> onoremap an :<c-u>call <SID>NextTextObject('a', '/')<cr>
xnoremap an :<c-u>call <SID>NextTextObject('a', 'f')<cr> xnoremap an :<c-u>call <SID>NextTextObject('a', '/')<cr>
onoremap in :<c-u>call <SID>NextTextObject('i', 'f')<cr> onoremap in :<c-u>call <SID>NextTextObject('i', '/')<cr>
xnoremap in :<c-u>call <SID>NextTextObject('i', 'f')<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) 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" if a:whole
let c = "(" normal! o
elseif c ==# "B"
let c = "{"
elseif c ==# "r"
let c = "["
endif


exe "normal! " . a:dir . c . "v" . a:motion.c while col('.') > 1 && getline('.')[col('.') - 2] =~# '\v[0-9]'
normal! h
endwhile
endif
endfunction endfunction


" }}} " }}}

0 comments on commit ac2e475

Please sign in to comment.