Skip to content

Instantly share code, notes, and snippets.

@gorkunov
Created December 6, 2012 15:57
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save gorkunov/4225560 to your computer and use it in GitHub Desktop.
Smart Selection for VIM
" About:
"
" How often do you forget which keys you should use to select/modify strings
" in the ' or " or in other pairs? I often use viw/ciw instead of vi'/vi" for
" the first time because it easier for my fingers (but after that I remember
" about vi'). This script allows you always use the same shortcut for all
" cases. When you want to select string in the ' use viv. Do you want to
" select all in the '()'? Use viv. All in the '[]'? Use viv.
"
" How it works:
"
" Script searches first unpair symbol from the left of the current cursor
" position and than runs target command with this symbol. You can use i or a
" modifiers for commands too.
"
" Available commands:
"
" vi* -> viv
" va* -> vav
" ci* -> civ
" ca* -> cav
" di* -> div
" da* -> dav
" ya* -> yiv
" ya* -> yav
" Where * is in <, >, ", ', `, (, ), [, ], {, } or t as tag
"
" NOTE: After v* commands you also can press v again and script extends selection
" to the next pairs.
"
" Author: @gorkunov (alex.g@cloudcastlegroup.com)
"
"
let s:pairs = { '<' : '>', '"': '"', "'": "'", '`': '`', '(': ')', '[': ']', '{': '}' }
function! s:SmartPairs(type, mod, ...)
let all = keys(s:pairs) + values(s:pairs)
if a:0 > 0
let str = getline(a:1)
let cur = len(str)
else
let str = getline('.')
let cur = col('.') - 1
let s:line = line('.')
let s:type = a:type
let s:mod = a:mod
let s:stops = []
endif
while cur >= 0
let cur = cur - 1
let ch = str[cur]
if index(all, ch) < 0
continue
endif
if len(s:stops) && get(s:pairs, ch, '') == s:stops[-1].symbol
call remove(s:stops, -1)
" tags workaround
if ch == '<'
" closed tag
if str[cur + 1] == '/'
call add(s:stops, { 'symbol': 'c', 'position': [s:line, cur + 1] })
else
if len(s:stops) && s:stops[-1].symbol == 'c'
call remove(s:stops, -1)
else
call add(s:stops, { 'symbol': 't', 'position': [s:line, cur + 1] })
endif
endif
endif
elseif str[cur - 1] != '\'
call add(s:stops, { 'symbol': ch, 'position': [s:line, cur + 1] })
endif
endwhile
call s:ApplyPairs()
endfunction
function! s:ApplyPairs()
let stop = get(s:stops, 0)
if type(stop) == type({}) && (has_key(s:pairs, stop.symbol) || stop.symbol == 't')
call remove(s:stops, 0)
execute "normal! " . stop.position[0] . "G" . stop.position[1] . "|"
execute "normal! " . s:type . s:mod . stop.symbol
elseif s:line > 1
let s:line = s:line - 1
call s:SmartPairs(s:type, s:mod, s:line)
endif
endfunction
function! s:NextPairs()
call s:ApplyPairs()
endfunction
nnoremap <silent> viv :call <SID>SmartPairs('v', 'i')<CR>
nnoremap <silent> vav :call <SID>SmartPairs('v', 'a')<CR>
nnoremap <silent> div :call <SID>SmartPairs('d', 'i')<CR>
nnoremap <silent> dav :call <SID>SmartPairs('d', 'a')<CR>
nnoremap <silent> civ :call <SID>SmartPairs('c', 'i')<CR>a
nnoremap <silent> cav :call <SID>SmartPairs('c', 'a')<CR>a
nnoremap <silent> yiv :call <SID>SmartPairs('y', 'i')<CR>
nnoremap <silent> yav :call <SID>SmartPairs('y', 'a')<CR>
vnoremap <silent> v <ESC>:call <SID>NextPairs()<CR>
@gorkunov
Copy link
Author

gorkunov commented Dec 6, 2012

e.g.

 _ - cursor position
 _...._ - selection

{ [ test_, 'blabla', cron ] } -> viv -> { [_test, 'blabla', cron_] }
{ [ test, 'bla_bla', cron ] } -> viv -> { [ test, '_blabla_', cron] }   
{ [ test, 'blabla', cron ]_}  -> viv -> {_[ test, 'blabla', cron]_}   

@krisleech
Copy link

I like this idea a lot, if I understand correctly it selects the nearest matching pair. I use di" or ci" mostly and rarely vi" but it looks like SmartSelect could easily be generalised to take another parameter which would be the first letter in the command, e.g SmartSelect('c', 'i'), of course the name of the function would need to change as well.

@gorkunov
Copy link
Author

gorkunov commented Dec 8, 2012

@krisleech thanks for suggestions :) I added deleting and changing to the script and also fixed some bugs.

@gorkunov
Copy link
Author

gorkunov commented Dec 9, 2012

Added extending selection for 'v' commands

@gorkunov
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment