citadel

My dotfiles, scripts and nix configs
git clone git://jb55.com/citadel
Log | Files | Refs | README | LICENSE

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: