surround.vim 17 KB

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