├── README.markdown ├── doc └── argumentative.txt └── plugin └── argumentative.vim /README.markdown: -------------------------------------------------------------------------------- 1 | # argumentative.vim 2 | 3 | Argumentative aids with manipulating and moving between function arguments. 4 | 5 | * Shifting arguments with `<,` and `>,` 6 | * Moving between argument boundaries with `[,` and `],` 7 | * New text objects `a,` and `i,` 8 | 9 | ## Installation 10 | 11 | If you don't have a preferred installation method, I recommend 12 | installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and 13 | then simply copy and paste: 14 | 15 | cd ~/.vim/bundle 16 | git clone git://github.com/PeterRincker/vim-argumentative.git 17 | 18 | Once help tags have been generated, you can view the manual with 19 | `:help argumentative`. 20 | 21 | 22 | ## Customization 23 | 24 | Argumentative mappings can be changed from the default by simply adding 25 | mappings in your `~/.vimrc` file to argumentative's `` mappings. 26 | 27 | nmap [; Argumentative_Prev 28 | nmap ]; Argumentative_Next 29 | xmap [; Argumentative_XPrev 30 | xmap ]; Argumentative_XNext 31 | nmap <; Argumentative_MoveLeft 32 | nmap >; Argumentative_MoveRight 33 | xmap i; Argumentative_InnerTextObject 34 | xmap a; Argumentative_OuterTextObject 35 | omap i; Argumentative_OpPendingInnerTextObject 36 | omap a; Argumentative_OpPendingOuterTextObject 37 | -------------------------------------------------------------------------------- /doc/argumentative.txt: -------------------------------------------------------------------------------- 1 | *argumentative.txt* Function argument motions and manipulation 2 | 3 | Author: Peter Rincker *argumentative-author* 4 | License: Same terms as Vim itself (see |license|) 5 | 6 | This plugin is only available if 'compatible' is not set. 7 | 8 | ============================================================================== 9 | *argumentative* 10 | Argumentative aids with manipulating and moving between function arguments. 11 | Shifting arguments with |<,| and |>,| and moving with |[,| and |],|. 12 | 13 | 1. Argument motions |argumentative-motions| 14 | 2. Shifting arguments |argumentative-shifting| 15 | 3. Argument text objects |argumentative-text-objects| 16 | 17 | ============================================================================== 18 | 1. Argument motions *argumentative-motions* 19 | 20 | *[,* 21 | [, go to [count] previous argument boundary 22 | 23 | *],* 24 | ], go to [count] next argument boundary 25 | 26 | ============================================================================== 27 | 2. Shifting arguments *argumentative-shifting* 28 | 29 | *<,* 30 | <, Shift current argument [count] arguments left 31 | 32 | *>,* 33 | >, Shift current argument [count] arguments right 34 | 35 | ============================================================================== 36 | 3. Argument text objects *argumentative-text-objects* 37 | 38 | *a,* 39 | a, "an argument" text object 40 | 41 | *i,* 42 | i, "inner argument" text object 43 | 44 | 45 | ============================================================================== 46 | SPECIAL THANKS *argumentative-special-thanks* 47 | 48 | Tim Pope who has created many amazing plugins, but especially repeat.vim which 49 | this plugin optionally uses. 50 | 51 | vim:tw=78:ts=8:ft=help:norl: 52 | -------------------------------------------------------------------------------- /plugin/argumentative.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_argumentative') || &cp || v:version < 700 2 | finish 3 | endif 4 | let g:loaded_argumentative = 1 5 | 6 | let s:pairs = {} 7 | let s:pairs['('] = ')' 8 | let s:pairs['{'] = '}' 9 | let s:pairs['['] = ']' 10 | let s:pairs[')'] = '(' 11 | let s:pairs['}'] = '{' 12 | let s:pairs[']'] = '[' 13 | 14 | " ArgMotion 15 | " Move to the next boundry. Takes nesting into account. 16 | " a:direction: int 17 | " 0 for backward, otherwise forwards 18 | function! s:ArgMotion(direction) 19 | let direction = a:direction ? '' : 'b' 20 | let s:stack = [] 21 | if s:is_implicit_function_call() 22 | let [l, start] = searchpos('\m\k[?!]\=\s\+\%("\|-\d\|\k\|[({([]]\)', 'nb', line('.')) 23 | if &ft =~ '\m\<\%(ruby\|eruby\)\>' 24 | let [l, end] = searchpos('\m\%(\S\s\+\\|%>\|$\)', 'n', line('.')) 25 | elseif &ft =~ '\' && match(getline(line('.')), '\m\S\s\+->$') > -1 26 | let [l, end] = searchpos('\m\S\zs\s\+->$', 'n', line('.')) 27 | else 28 | let end = col('$') - 1 29 | let c = getline(line('.'))[-1:] 30 | if s:is_open(c, 0) && col('.') == end 31 | let s:stack = [c] 32 | endif 33 | endif 34 | call searchpair('\%(\%' . (start+1) . 'c\|[({[]\)', ',', '\%(\%' . end . 'c\|[]})]\)', direction . 'W', "s:skip(" . a:direction . ", " . (start+1) . ", " . end . ")", line('.')) 35 | else 36 | call searchpair('[({[]', ',', '[]})]', direction . 'W', "s:skip(" . a:direction . ")") 37 | endif 38 | endfunction 39 | 40 | function! s:skip(direction, ...) 41 | if a:0 && ((col('.') == a:2) || (col('.') == a:1)) 42 | return 0 43 | endif 44 | let syn = synIDattr(synID(line("."), col("."), 0), "name") 45 | if syn =~? "string" || syn =~? "comment" || syn =~? "regex" || syn =~? "perlmatch" || syn =~? "perlsubstitution" 46 | return 1 47 | endif 48 | let c = s:getchar() 49 | let top = len(s:stack) > 0 ? s:stack[0] : '' 50 | if top == '' && !s:is_open(c, a:direction) && (!a:0 || c =~ ',') 51 | return 0 52 | elseif a:0 && c !~ '[](),{}[]' && a:2 == col('.') 53 | return 1 54 | elseif c =~ '[](){}[]' 55 | if top == s:get_pair(c) 56 | call remove(s:stack, 0) 57 | return 1 58 | else 59 | call insert(s:stack, c, 0) 60 | endif 61 | endif 62 | return len(s:stack) > 0 63 | endfunction 64 | 65 | function! s:is_implicit_function_call() 66 | return &ft =~ '\m\<\%(ruby\|eruby\|coffee\)\>' && getline(line('.')) =~ '\m\k[?!]\=\s\+\%("\|-\d\|\k\+\>[?!]\=\s*\%([(\.]\)\@!\|[({[]]\)' 67 | endfunction 68 | 69 | function! s:get_pair(c) 70 | return get(s:pairs, a:c, '') 71 | endfunction 72 | 73 | function! s:is_open(c, direction) 74 | return a:direction ? a:c =~ '[({[]' : a:c =~ '[])}]' 75 | endfunction 76 | 77 | function! s:MoveLeft() 78 | call s:Move(0) 79 | endfunction 80 | 81 | function! s:MoveRight() 82 | call s:Move(1) 83 | endfunction 84 | 85 | function! s:Move(direction) 86 | let selection = &selection 87 | let &selection = "inclusive" 88 | let pos = getpos('.') 89 | let outer = s:OuterTextObject() 90 | call setpos('.', pos) 91 | let a = s:InnerTextObject() 92 | if a:direction 93 | call setpos('.', outer[2]) 94 | call s:ArgMotion(a:direction) 95 | else 96 | call setpos('.', outer[1]) 97 | if s:getchar() != ',' 98 | call s:ArgMotion(a:direction) 99 | endif 100 | endif 101 | let b = s:InnerTextObject() 102 | call s:exchange(a, b) 103 | call s:ArgMotion(1) 104 | let &selection = selection 105 | endfunction 106 | 107 | function! s:Count(mapping, fn, ...) 108 | let operator = v:operator 109 | for i in range(v:count1) 110 | call call(a:fn, a:000) 111 | endfor 112 | if a:mapping != '' 113 | sil! call repeat#set("\Argumentative_" . a:mapping . (operator == 'c' ? "\." : ''), v:count1) 114 | endif 115 | endfunction 116 | 117 | function! s:exchange(a, b) 118 | let rv = getreg('a') 119 | let rt = getregtype('a') 120 | let vm = visualmode() 121 | let vs = getpos("'<") 122 | let ve = getpos("'>") 123 | let virtualedit = &virtualedit 124 | let &virtualedit = 'onemore' 125 | try 126 | if s:cmp(a:a[1], a:b[1]) < 0 127 | let x = a:a 128 | let y = a:b 129 | let adjust = 1 130 | else 131 | let x = a:b 132 | let y = a:a 133 | let adjust = 0 134 | endif 135 | call setpos("'[", x[1]) 136 | call setpos("']", x[2]) 137 | norm! `[v`]"ay 138 | let first = @a 139 | call setpos("'[", y[1]) 140 | call setpos("']", y[2]) 141 | norm! `[v`]"ad 142 | let second = @a 143 | let @a = first 144 | norm! "aP 145 | let adj_bottom = [getpos("'["), getpos("']")] 146 | call setpos("'[", x[1]) 147 | call setpos("']", x[2]) 148 | norm! `[v`]"_d 149 | let @a = second 150 | norm! "aP 151 | let adj_top = [getpos("'["), getpos("']")] 152 | if adjust 153 | let l_delta = 0 154 | let c_delta1 = 0 155 | let c_delta2 = 0 156 | if x[2][1] == adj_bottom[0][1] 157 | let c_delta1 = adj_top[1][2] - x[2][2] 158 | if x[1][1] == x[2][1] 159 | let c_delta2 = c_delta1 160 | endif 161 | endif 162 | if adj_top[1][1] != x[2][1] 163 | let l_delta = adj_top[1][1] - x[2][1] 164 | endif 165 | call setpos("'[", [adj_bottom[0][0], adj_bottom[0][1] + l_delta, adj_bottom[0][2] + c_delta1, adj_bottom[0][3]]) 166 | call setpos("']", [adj_bottom[1][0], adj_bottom[1][1] + l_delta, adj_bottom[1][2] + c_delta2, adj_bottom[1][3]]) 167 | endif 168 | finally 169 | let ms = getpos("'[") 170 | let me = getpos("']") 171 | if vm != '' 172 | call setpos("'[", vs) 173 | call setpos("']", ve) 174 | exe "norm! `[" . vm[0] . "`]\" 175 | endif 176 | call setpos("'[", ms) 177 | call setpos("']", me) 178 | norm! `] 179 | call setreg('a', rv, rt) 180 | let &virtualedit = virtualedit 181 | endtry 182 | endfunction 183 | 184 | function! s:cmp(a, b) 185 | for i in range(len(a:a)) 186 | if a:a[i] < a:b[i] 187 | return -1 188 | elseif a:a[i] > a:b[i] 189 | return 1 190 | endif 191 | endfor 192 | return 0 193 | endfunction 194 | 195 | function! s:OuterTextObject() 196 | if s:is_open(s:getchar(), 1) 197 | call s:ArgMotion(1) 198 | let ce = s:getchar() 199 | let end = getpos('.') 200 | call s:ArgMotion(0) 201 | let cs = s:getchar() 202 | let start = getpos('.') 203 | else 204 | call s:ArgMotion(0) 205 | let cs = s:getchar() 206 | let start = getpos('.') 207 | call s:ArgMotion(1) 208 | let ce = s:getchar() 209 | let end = getpos('.') 210 | endif 211 | if cs =~ '[{([]' || (cs == ',' && ce !~ '[])}]') 212 | call setpos('.', start) 213 | call search('\_.', 'W') 214 | let start = getpos('.') 215 | endif 216 | if ce =~ '[])}]' && !(s:is_implicit_function_call() && end[2] == col('$')-1) 217 | call setpos('.', end) 218 | call search('.', 'bW') 219 | let end = getpos('.') 220 | endif 221 | if cs =~ '[{([]' && ce =~ ',' 222 | call setpos('.', end) 223 | call search(',\%(\_s\{-}\n\ze\s*\|\s\+\ze\)\S', 'ceW') 224 | let end = getpos('.') 225 | endif 226 | return ['v', start, end] 227 | endfunction 228 | 229 | function! s:InnerTextObject() 230 | let outer = s:OuterTextObject() 231 | call setpos('.', outer[1]) 232 | call search('\S', 'W' . (s:getchar() != ',' ? 'c' : '')) 233 | let start = getpos('.') 234 | call setpos('.', outer[2]) 235 | call search('\S\&[^,]', 'bW' . (s:getchar() != ',' ? 'c' : '')) 236 | let end = getpos('.') 237 | return ['v', start, end] 238 | endfunction 239 | 240 | function! s:getchar(...) 241 | return getline(line('.'))[col('.') + (a:0 ? a:1 : 0) - 1] 242 | endfunction 243 | 244 | function! s:PlugMap(mode, lhs, rhs) 245 | let plugmap = 'Argumentative_' . a:rhs 246 | if !hasmapto(plugmap, a:mode) 247 | exe a:mode . 'map ' . a:lhs . ' ' . plugmap 248 | endif 249 | endfunction 250 | 251 | function! s:VisualTextObject(fn) 252 | let ms = getpos("'[") 253 | let me = getpos("']") 254 | let vs = getpos("'<") 255 | let ve = getpos("'>") 256 | 257 | call setpos(".", vs) 258 | if s:getchar() == ',' 259 | call search('.', 'W') 260 | endif 261 | 262 | try 263 | let obj = call(a:fn, []) 264 | 265 | if s:needs_to_expand(vs, ve, obj, a:fn) 266 | call setpos(".", ve) 267 | call s:ArgMotion(1) 268 | let c = s:getchar() 269 | while c !~ '[])}]' 270 | call s:ArgMotion(1) 271 | let c = s:getchar() 272 | endwhile 273 | normal! l 274 | let possible = call(a:fn, []) 275 | if s:contained(possible, obj) 276 | let obj = possible 277 | endif 278 | endif 279 | 280 | call setpos("'[", obj[1]) 281 | call setpos("']", obj[2]) 282 | exe 'norm! `[' . obj[0] . '`]' 283 | if &selection ==# 'exclusive' 284 | let virtualedit = &virtualedit 285 | let &virtualedit = "all" 286 | exe "norm! \gvl" 287 | let &virtualedit = virtualedit 288 | endif 289 | finally 290 | call setpos("'[", ms) 291 | call setpos("']", me) 292 | endtry 293 | endfunction 294 | 295 | function! s:needs_to_expand(vs, ve, obj, fn) 296 | " single character selections are not expanable 297 | if s:cmp(a:vs, a:ve) == 0 298 | return 0 299 | endif 300 | 301 | " object w/ same boundries as selction should expand 302 | if s:cmp(a:vs, a:obj[1]) == 0 && s:cmp(a:ve, a:obj[2]) == 0 303 | return 1 304 | endif 305 | 306 | " move cursor to opposite end of selection and repeat text object 307 | call setpos(".", a:ve) 308 | let obj = call(a:fn, []) 309 | call setpos(".", a:vs) 310 | 311 | " objects at different locations should expand 312 | if s:cmp(obj[1], a:obj[1]) != 0 || s:cmp(obj[2], a:obj[2]) != 0 313 | return 1 314 | endif 315 | 316 | return 0 317 | endfunction 318 | 319 | " region a contains region b 320 | function! s:contained(a, b) 321 | return s:cmp(a:a[1], a:b[1]) <= 0 && s:cmp(a:b[2], a:a[2]) <= 0 322 | endfunction 323 | 324 | noremap