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