surround.vim (17329B)
1 " surround.vim - Surroundings 2 " Author: Tim Pope <http://tpo.pe/> 3 " Version: 2.1 4 " GetLatestVimScripts: 1697 1 :AutoInstall: surround.vim 5 6 if exists("g:loaded_surround") || &cp || v:version < 700 7 finish 8 endif 9 let g:loaded_surround = 1 10 11 " Input functions {{{1 12 13 function! s:getchar() 14 let c = getchar() 15 if c =~ '^\d\+$' 16 let c = nr2char(c) 17 endif 18 return c 19 endfunction 20 21 function! s:inputtarget() 22 let c = s:getchar() 23 while c =~ '^\d\+$' 24 let c .= s:getchar() 25 endwhile 26 if c == " " 27 let c .= s:getchar() 28 endif 29 if c =~ "\<Esc>\|\<C-C>\|\0" 30 return "" 31 else 32 return c 33 endif 34 endfunction 35 36 function! s:inputreplacement() 37 let c = s:getchar() 38 if c == " " 39 let c .= s:getchar() 40 endif 41 if c =~ "\<Esc>" || c =~ "\<C-C>" 42 return "" 43 else 44 return c 45 endif 46 endfunction 47 48 function! s:beep() 49 exe "norm! \<Esc>" 50 return "" 51 endfunction 52 53 function! s:redraw() 54 redraw 55 return "" 56 endfunction 57 58 " }}}1 59 60 " Wrapping functions {{{1 61 62 function! s:extractbefore(str) 63 if a:str =~ '\r' 64 return matchstr(a:str,'.*\ze\r') 65 else 66 return matchstr(a:str,'.*\ze\n') 67 endif 68 endfunction 69 70 function! s:extractafter(str) 71 if a:str =~ '\r' 72 return matchstr(a:str,'\r\zs.*') 73 else 74 return matchstr(a:str,'\n\zs.*') 75 endif 76 endfunction 77 78 function! s:fixindent(str,spc) 79 let str = substitute(a:str,'\t',repeat(' ',&sw),'g') 80 let spc = substitute(a:spc,'\t',repeat(' ',&sw),'g') 81 let str = substitute(str,'\(\n\|\%^\).\@=','\1'.spc,'g') 82 if ! &et 83 let str = substitute(str,'\s\{'.&ts.'\}',"\t",'g') 84 endif 85 return str 86 endfunction 87 88 function! s:process(string) 89 let i = 0 90 for i in range(7) 91 let repl_{i} = '' 92 let m = matchstr(a:string,nr2char(i).'.\{-\}\ze'.nr2char(i)) 93 if m != '' 94 let m = substitute(strpart(m,1),'\r.*','','') 95 let repl_{i} = input(match(m,'\w\+$') >= 0 ? m.': ' : m) 96 endif 97 endfor 98 let s = "" 99 let i = 0 100 while i < strlen(a:string) 101 let char = strpart(a:string,i,1) 102 if char2nr(char) < 8 103 let next = stridx(a:string,char,i+1) 104 if next == -1 105 let s .= char 106 else 107 let insertion = repl_{char2nr(char)} 108 let subs = strpart(a:string,i+1,next-i-1) 109 let subs = matchstr(subs,'\r.*') 110 while subs =~ '^\r.*\r' 111 let sub = matchstr(subs,"^\r\\zs[^\r]*\r[^\r]*") 112 let subs = strpart(subs,strlen(sub)+1) 113 let r = stridx(sub,"\r") 114 let insertion = substitute(insertion,strpart(sub,0,r),strpart(sub,r+1),'') 115 endwhile 116 let s .= insertion 117 let i = next 118 endif 119 else 120 let s .= char 121 endif 122 let i += 1 123 endwhile 124 return s 125 endfunction 126 127 function! s:wrap(string,char,type,removed,special) 128 let keeper = a:string 129 let newchar = a:char 130 let s:input = "" 131 let type = a:type 132 let linemode = type ==# 'V' ? 1 : 0 133 let before = "" 134 let after = "" 135 if type ==# "V" 136 let initspaces = matchstr(keeper,'\%^\s*') 137 else 138 let initspaces = matchstr(getline('.'),'\%^\s*') 139 endif 140 let pairs = "b()B{}r[]a<>" 141 let extraspace = "" 142 if newchar =~ '^ ' 143 let newchar = strpart(newchar,1) 144 let extraspace = ' ' 145 endif 146 let idx = stridx(pairs,newchar) 147 if newchar == ' ' 148 let before = '' 149 let after = '' 150 elseif exists("b:surround_".char2nr(newchar)) 151 let all = s:process(b:surround_{char2nr(newchar)}) 152 let before = s:extractbefore(all) 153 let after = s:extractafter(all) 154 elseif exists("g:surround_".char2nr(newchar)) 155 let all = s:process(g:surround_{char2nr(newchar)}) 156 let before = s:extractbefore(all) 157 let after = s:extractafter(all) 158 elseif newchar ==# "p" 159 let before = "\n" 160 let after = "\n\n" 161 elseif newchar ==# 's' 162 let before = ' ' 163 let after = '' 164 elseif newchar ==# ':' 165 let before = ':' 166 let after = '' 167 elseif newchar =~# "[tT\<C-T><]" 168 let dounmapp = 0 169 let dounmapb = 0 170 if !maparg(">","c") 171 let dounmapb = 1 172 " Hide from AsNeeded 173 exe "cn"."oremap > ><CR>" 174 endif 175 let default = "" 176 if newchar ==# "T" 177 if !exists("s:lastdel") 178 let s:lastdel = "" 179 endif 180 let default = matchstr(s:lastdel,'<\zs.\{-\}\ze>') 181 endif 182 let tag = input("<",default) 183 if dounmapb 184 silent! cunmap > 185 endif 186 let s:input = tag 187 if tag != "" 188 let keepAttributes = ( match(tag, ">$") == -1 ) 189 let tag = substitute(tag,'>*$','','') 190 let attributes = "" 191 if keepAttributes 192 let attributes = matchstr(a:removed, '<[^ \t\n]\+\zs\_.\{-\}\ze>') 193 endif 194 let s:input = tag . '>' 195 if tag =~ '/$' 196 let tag = substitute(tag, '/$', '', '') 197 let before = '<'.tag.attributes.' />' 198 let after = '' 199 else 200 let before = '<'.tag.attributes.'>' 201 let after = '</'.substitute(tag,' .*','','').'>' 202 endif 203 if newchar == "\<C-T>" 204 if type ==# "v" || type ==# "V" 205 let before .= "\n\t" 206 endif 207 if type ==# "v" 208 let after = "\n". after 209 endif 210 endif 211 endif 212 elseif newchar ==# 'l' || newchar == '\' 213 " LaTeX 214 let env = input('\begin{') 215 if env != "" 216 let s:input = env."\<CR>" 217 let env = '{' . env 218 let env .= s:closematch(env) 219 echo '\begin'.env 220 let before = '\begin'.env 221 let after = '\end'.matchstr(env,'[^}]*').'}' 222 endif 223 elseif newchar ==# 'f' || newchar ==# 'F' 224 let fnc = input('function: ') 225 if fnc != "" 226 let s:input = fnc."\<CR>" 227 let before = substitute(fnc,'($','','').'(' 228 let after = ')' 229 if newchar ==# 'F' 230 let before .= ' ' 231 let after = ' ' . after 232 endif 233 endif 234 elseif newchar ==# "\<C-F>" 235 let fnc = input('function: ') 236 let s:input = fnc."\<CR>" 237 let before = '('.fnc.' ' 238 let after = ')' 239 elseif idx >= 0 240 let spc = (idx % 3) == 1 ? " " : "" 241 let idx = idx / 3 * 3 242 let before = strpart(pairs,idx+1,1) . spc 243 let after = spc . strpart(pairs,idx+2,1) 244 elseif newchar == "\<C-[>" || newchar == "\<C-]>" 245 let before = "{\n\t" 246 let after = "\n}" 247 elseif newchar !~ '\a' 248 let before = newchar 249 let after = newchar 250 else 251 let before = '' 252 let after = '' 253 endif 254 let after = substitute(after ,'\n','\n'.initspaces,'g') 255 if type ==# 'V' || (a:special && type ==# "v") 256 let before = substitute(before,' \+$','','') 257 let after = substitute(after ,'^ \+','','') 258 if after !~ '^\n' 259 let after = initspaces.after 260 endif 261 if keeper !~ '\n$' && after !~ '^\n' 262 let keeper .= "\n" 263 elseif keeper =~ '\n$' && after =~ '^\n' 264 let after = strpart(after,1) 265 endif 266 if keeper !~ '^\n' && before !~ '\n\s*$' 267 let before .= "\n" 268 if a:special 269 let before .= "\t" 270 endif 271 elseif keeper =~ '^\n' && before =~ '\n\s*$' 272 let keeper = strcharpart(keeper,1) 273 endif 274 if type ==# 'V' && keeper =~ '\n\s*\n$' 275 let keeper = strcharpart(keeper,0,strchars(keeper) - 1) 276 endif 277 endif 278 if type ==# 'V' 279 let before = initspaces.before 280 endif 281 if before =~ '\n\s*\%$' 282 if type ==# 'v' 283 let keeper = initspaces.keeper 284 endif 285 let padding = matchstr(before,'\n\zs\s\+\%$') 286 let before = substitute(before,'\n\s\+\%$','\n','') 287 let keeper = s:fixindent(keeper,padding) 288 endif 289 if type ==# 'V' 290 let keeper = before.keeper.after 291 elseif type =~ "^\<C-V>" 292 " Really we should be iterating over the buffer 293 let repl = substitute(before,'[\\~]','\\&','g').'\1'.substitute(after,'[\\~]','\\&','g') 294 let repl = substitute(repl,'\n',' ','g') 295 let keeper = substitute(keeper."\n",'\(.\{-\}\)\(\n\)',repl.'\n','g') 296 let keeper = substitute(keeper,'\n\%$','','') 297 else 298 let keeper = before.extraspace.keeper.extraspace.after 299 endif 300 return keeper 301 endfunction 302 303 function! s:wrapreg(reg,char,removed,special) 304 let orig = getreg(a:reg) 305 let type = substitute(getregtype(a:reg),'\d\+$','','') 306 let new = s:wrap(orig,a:char,type,a:removed,a:special) 307 call setreg(a:reg,new,type) 308 endfunction 309 " }}}1 310 311 function! s:insert(...) " {{{1 312 " Optional argument causes the result to appear on 3 lines, not 1 313 let linemode = a:0 ? a:1 : 0 314 let char = s:inputreplacement() 315 while char == "\<CR>" || char == "\<C-S>" 316 " TODO: use total count for additional blank lines 317 let linemode += 1 318 let char = s:inputreplacement() 319 endwhile 320 if char == "" 321 return "" 322 endif 323 let cb_save = &clipboard 324 set clipboard-=unnamed clipboard-=unnamedplus 325 let reg_save = @@ 326 call setreg('"',"\r",'v') 327 call s:wrapreg('"',char,"",linemode) 328 " If line mode is used and the surrounding consists solely of a suffix, 329 " remove the initial newline. This fits a use case of mine but is a 330 " little inconsistent. Is there anyone that would prefer the simpler 331 " behavior of just inserting the newline? 332 if linemode && match(getreg('"'),'^\n\s*\zs.*') == 0 333 call setreg('"',matchstr(getreg('"'),'^\n\s*\zs.*'),getregtype('"')) 334 endif 335 " This can be used to append a placeholder to the end 336 if exists("g:surround_insert_tail") 337 call setreg('"',g:surround_insert_tail,"a".getregtype('"')) 338 endif 339 if &ve != 'all' && col('.') >= col('$') 340 if &ve == 'insert' 341 let extra_cols = virtcol('.') - virtcol('$') 342 if extra_cols > 0 343 let [regval,regtype] = [getreg('"',1,1),getregtype('"')] 344 call setreg('"',join(map(range(extra_cols),'" "'),''),'v') 345 norm! ""p 346 call setreg('"',regval,regtype) 347 endif 348 endif 349 norm! ""p 350 else 351 norm! ""P 352 endif 353 if linemode 354 call s:reindent() 355 endif 356 norm! `] 357 call search('\r','bW') 358 let @@ = reg_save 359 let &clipboard = cb_save 360 return "\<Del>" 361 endfunction " }}}1 362 363 function! s:reindent() " {{{1 364 if exists("b:surround_indent") ? b:surround_indent : (!exists("g:surround_indent") || g:surround_indent) 365 silent norm! '[='] 366 endif 367 endfunction " }}}1 368 369 function! s:dosurround(...) " {{{1 370 let scount = v:count1 371 let char = (a:0 ? a:1 : s:inputtarget()) 372 let spc = "" 373 if char =~ '^\d\+' 374 let scount = scount * matchstr(char,'^\d\+') 375 let char = substitute(char,'^\d\+','','') 376 endif 377 if char =~ '^ ' 378 let char = strpart(char,1) 379 let spc = 1 380 endif 381 if char == 'a' 382 let char = '>' 383 endif 384 if char == 'r' 385 let char = ']' 386 endif 387 let newchar = "" 388 if a:0 > 1 389 let newchar = a:2 390 if newchar == "\<Esc>" || newchar == "\<C-C>" || newchar == "" 391 return s:beep() 392 endif 393 endif 394 let cb_save = &clipboard 395 set clipboard-=unnamed clipboard-=unnamedplus 396 let append = "" 397 let original = getreg('"') 398 let otype = getregtype('"') 399 call setreg('"',"") 400 let strcount = (scount == 1 ? "" : scount) 401 if char == '/' 402 exe 'norm! '.strcount.'[/d'.strcount.']/' 403 elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>"''`]' 404 exe 'norm! T'.char 405 if getline('.')[col('.')-1] == char 406 exe 'norm! l' 407 endif 408 exe 'norm! dt'.char 409 else 410 exe 'norm! d'.strcount.'i'.char 411 endif 412 let keeper = getreg('"') 413 let okeeper = keeper " for reindent below 414 if keeper == "" 415 call setreg('"',original,otype) 416 let &clipboard = cb_save 417 return "" 418 endif 419 let oldline = getline('.') 420 let oldlnum = line('.') 421 if char ==# "p" 422 call setreg('"','','V') 423 elseif char ==# "s" || char ==# "w" || char ==# "W" 424 " Do nothing 425 call setreg('"','') 426 elseif char =~ "[\"'`]" 427 exe "norm! i \<Esc>d2i".char 428 call setreg('"',substitute(getreg('"'),' ','','')) 429 elseif char == '/' 430 norm! "_x 431 call setreg('"','/**/',"c") 432 let keeper = substitute(substitute(keeper,'^/\*\s\=','',''),'\s\=\*$','','') 433 elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>]' 434 exe 'norm! F'.char 435 exe 'norm! df'.char 436 else 437 " One character backwards 438 call search('\m.', 'bW') 439 exe "norm! da".char 440 endif 441 let removed = getreg('"') 442 let rem2 = substitute(removed,'\n.*','','') 443 let oldhead = strpart(oldline,0,strlen(oldline)-strlen(rem2)) 444 let oldtail = strpart(oldline, strlen(oldline)-strlen(rem2)) 445 let regtype = getregtype('"') 446 if char =~# '[\[({<T]' || spc 447 let keeper = substitute(keeper,'^\s\+','','') 448 let keeper = substitute(keeper,'\s\+$','','') 449 endif 450 if col("']") == col("$") && col('.') + 1 == col('$') 451 if oldhead =~# '^\s*$' && a:0 < 2 452 let keeper = substitute(keeper,'\%^\n'.oldhead.'\(\s*.\{-\}\)\n\s*\%$','\1','') 453 endif 454 let pcmd = "p" 455 else 456 let pcmd = "P" 457 endif 458 if line('.') + 1 < oldlnum && regtype ==# "V" 459 let pcmd = "p" 460 endif 461 call setreg('"',keeper,regtype) 462 if newchar != "" 463 let special = a:0 > 2 ? a:3 : 0 464 call s:wrapreg('"',newchar,removed,special) 465 endif 466 silent exe 'norm! ""'.pcmd.'`[' 467 if removed =~ '\n' || okeeper =~ '\n' || getreg('"') =~ '\n' 468 call s:reindent() 469 endif 470 if getline('.') =~ '^\s\+$' && keeper =~ '^\s*\n' 471 silent norm! cc 472 endif 473 call setreg('"',original,otype) 474 let s:lastdel = removed 475 let &clipboard = cb_save 476 if newchar == "" 477 silent! call repeat#set("\<Plug>Dsurround".char,scount) 478 else 479 silent! call repeat#set("\<Plug>C".(a:0 > 2 && a:3 ? "S" : "s")."urround".char.newchar.s:input,scount) 480 endif 481 endfunction " }}}1 482 483 function! s:changesurround(...) " {{{1 484 let a = s:inputtarget() 485 if a == "" 486 return s:beep() 487 endif 488 let b = s:inputreplacement() 489 if b == "" 490 return s:beep() 491 endif 492 call s:dosurround(a,b,a:0 && a:1) 493 endfunction " }}}1 494 495 function! s:opfunc(type, ...) abort " {{{1 496 if a:type ==# 'setup' 497 let &opfunc = matchstr(expand('<sfile>'), '<SNR>\w\+$') 498 return 'g@' 499 endif 500 let char = s:inputreplacement() 501 if char == "" 502 return s:beep() 503 endif 504 let reg = '"' 505 let sel_save = &selection 506 let &selection = "inclusive" 507 let cb_save = &clipboard 508 set clipboard-=unnamed clipboard-=unnamedplus 509 let reg_save = getreg(reg) 510 let reg_type = getregtype(reg) 511 let type = a:type 512 if a:type == "char" 513 silent exe 'norm! v`[o`]"'.reg.'y' 514 let type = 'v' 515 elseif a:type == "line" 516 silent exe 'norm! `[V`]"'.reg.'y' 517 let type = 'V' 518 elseif a:type ==# "v" || a:type ==# "V" || a:type ==# "\<C-V>" 519 let &selection = sel_save 520 let ve = &virtualedit 521 if !(a:0 && a:1) 522 set virtualedit= 523 endif 524 silent exe 'norm! gv"'.reg.'y' 525 let &virtualedit = ve 526 elseif a:type =~ '^\d\+$' 527 let type = 'v' 528 silent exe 'norm! ^v'.a:type.'$h"'.reg.'y' 529 if mode() ==# 'v' 530 norm! v 531 return s:beep() 532 endif 533 else 534 let &selection = sel_save 535 let &clipboard = cb_save 536 return s:beep() 537 endif 538 let keeper = getreg(reg) 539 if type ==# "v" && a:type !=# "v" 540 let append = matchstr(keeper,'\_s\@<!\s*$') 541 let keeper = substitute(keeper,'\_s\@<!\s*$','','') 542 endif 543 call setreg(reg,keeper,type) 544 call s:wrapreg(reg,char,"",a:0 && a:1) 545 if type ==# "v" && a:type !=# "v" && append != "" 546 call setreg(reg,append,"ac") 547 endif 548 silent exe 'norm! gv'.(reg == '"' ? '' : '"' . reg).'p`[' 549 if type ==# 'V' || (getreg(reg) =~ '\n' && type ==# 'v') 550 call s:reindent() 551 endif 552 call setreg(reg,reg_save,reg_type) 553 let &selection = sel_save 554 let &clipboard = cb_save 555 if a:type =~ '^\d\+$' 556 silent! call repeat#set("\<Plug>Y".(a:0 && a:1 ? "S" : "s")."surround".char.s:input,a:type) 557 else 558 silent! call repeat#set("\<Plug>SurroundRepeat".char.s:input) 559 endif 560 endfunction 561 562 function! s:opfunc2(...) abort 563 if !a:0 || a:1 ==# 'setup' 564 let &opfunc = matchstr(expand('<sfile>'), '<SNR>\w\+$') 565 return 'g@' 566 endif 567 call s:opfunc(a:1, 1) 568 endfunction " }}}1 569 570 function! s:closematch(str) " {{{1 571 " Close an open (, {, [, or < on the command line. 572 let tail = matchstr(a:str,'.[^\[\](){}<>]*$') 573 if tail =~ '^\[.\+' 574 return "]" 575 elseif tail =~ '^(.\+' 576 return ")" 577 elseif tail =~ '^{.\+' 578 return "}" 579 elseif tail =~ '^<.+' 580 return ">" 581 else 582 return "" 583 endif 584 endfunction " }}}1 585 586 nnoremap <silent> <Plug>SurroundRepeat . 587 nnoremap <silent> <Plug>Dsurround :<C-U>call <SID>dosurround(<SID>inputtarget())<CR> 588 nnoremap <silent> <Plug>Csurround :<C-U>call <SID>changesurround()<CR> 589 nnoremap <silent> <Plug>CSurround :<C-U>call <SID>changesurround(1)<CR> 590 nnoremap <expr> <Plug>Yssurround '^'.v:count1.<SID>opfunc('setup').'g_' 591 nnoremap <expr> <Plug>YSsurround <SID>opfunc2('setup').'_' 592 nnoremap <expr> <Plug>Ysurround <SID>opfunc('setup') 593 nnoremap <expr> <Plug>YSurround <SID>opfunc2('setup') 594 vnoremap <silent> <Plug>VSurround :<C-U>call <SID>opfunc(visualmode(),visualmode() ==# 'V' ? 1 : 0)<CR> 595 vnoremap <silent> <Plug>VgSurround :<C-U>call <SID>opfunc(visualmode(),visualmode() ==# 'V' ? 0 : 1)<CR> 596 inoremap <silent> <Plug>Isurround <C-R>=<SID>insert()<CR> 597 inoremap <silent> <Plug>ISurround <C-R>=<SID>insert(1)<CR> 598 599 if !exists("g:surround_no_mappings") || ! g:surround_no_mappings 600 nmap ds <Plug>Dsurround 601 nmap cs <Plug>Csurround 602 nmap cS <Plug>CSurround 603 nmap ys <Plug>Ysurround 604 nmap yS <Plug>YSurround 605 nmap yss <Plug>Yssurround 606 nmap ySs <Plug>YSsurround 607 nmap ySS <Plug>YSsurround 608 xmap S <Plug>VSurround 609 xmap gS <Plug>VgSurround 610 if !exists("g:surround_no_insert_mappings") || ! g:surround_no_insert_mappings 611 if !hasmapto("<Plug>Isurround","i") && "" == mapcheck("<C-S>","i") 612 imap <C-S> <Plug>Isurround 613 endif 614 imap <C-G>s <Plug>Isurround 615 imap <C-G>S <Plug>ISurround 616 endif 617 endif 618 619 " vim:set ft=vim sw=2 sts=2 et: