From 483ed8287f10698a4261a3ae1e00173c5c4b8bf0 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Mon, 5 Aug 2024 18:55:01 -0400 Subject: [PATCH] Initial port to vim9 - There are currently no tests, but it works in my basic tests. - Add a plug mapping to play nicely with a remapped 'd'. - Change plug names for better composability --- import/surround.vim | 684 ++++++++++++++++++++++++++++++++++++++++++++ plugin/surround.vim | 660 +++--------------------------------------- 2 files changed, 724 insertions(+), 620 deletions(-) create mode 100644 import/surround.vim diff --git a/import/surround.vim b/import/surround.vim new file mode 100644 index 0000000..55bed82 --- /dev/null +++ b/import/surround.vim @@ -0,0 +1,684 @@ +vim9script + +export def InputTarget(): string + var c = getcharstr() + while c =~ '^\d+$' + c ..= getcharstr() + endwhile + + if c == ' ' + c ..= getcharstr() + endif + + return c =~ "\\|\\|\0" ? "" : c +enddef + +def InputReplacement(): string + var c = getcharstr() + + if c == " " + c ..= getcharstr() + endif + + return c =~ "\|\" ? "" : c +enddef + +def Beep() + execute "normal! \" +enddef + +def ExtractBefore(str: string): string + return str =~ '\r' ? matchstr(str, '.*\ze\r') : matchstr(str, '.*\ze\n') +enddef + +def ExtractAfter(str: string): string + return str =~ '\r' ? matchstr(str, '\r\zs.*') : matchstr(str, '\n\zs.*') +enddef + +def FixIndent(str: string, spc: string): string + var result = str->substitute('\t', repeat(' ', &sw), 'g') + var spaces = spc->substitute('\t', repeat(' ', &sw), 'g') + + result = result->substitute('\(\n\|\%^\).\@=', '\1' .. spaces, 'g') + + if !&expandtab + result = result->substitute('\s\{' .. &ts .. '\}', "\t", 'g') + endif + + return result +enddef + +def Process(str: string): string + var repl: dict = {} + + for i in range(7) + repl[i] = '' + + var m = matchstr(str, nr2char(i) .. '.\{-\}\ze' .. nr2char(i)) + + if m != '' + m = m->strpart(1)->substitute('\r.*', '', '') + repl[i] = input(m->match('\w\+$') >= 0 ? m .. ': ' : m) + endif + endfor + + var s = '' + var j = 0 + + while j < str->strlen() + var char = str->strpart(j, 1) + + if char2nr(char) < 8 + var next = str->stridx(char, j + 1) + + if next == -1 + s ..= char + continue + endif + + var insertion = repl[char2nr(char)] + var subs = str + ->strpart(j + 1, next - j - 1) + ->matchstr('\r.*') + + while subs =~ '^\r.*\r' + var sub = subs->matchstr("^\r\\zs[^\r]*\r[^\r]*") + subs = subs->strpart(sub->strlen() + 1) + var r = sub->stridx("\r") + insertion = insertion->substitute(sub->strpart(0, r), sub->strpart(r + 1), '') + endwhile + + s ..= insertion + + j = next + else + s ..= char + endif + + j += 1 + endwhile + + return s +enddef + +var sInput: string = "" +var sLastDel: string = "" + +def Wrap(str: string, char: string, wrapType: string, removed: string, linebreak: bool): string + var keeper = str + var newchar = char + var linemode = wrapType ==# 'V' + var before = "" + var after = "" + var initSpaces = linemode ? matchstr(keeper, '\%^\s*') : matchstr(getline('.'), '\%^\s*') + var pairs = "b()B{}r[]a<>" + var extraspace = "" + + if newchar =~ '^ ' + newchar = newchar->strpart(1) + extraspace = ' ' + endif + + var idx = pairs->stridx(newchar) + + var custom = printf('surround_%d', char2nr(newchar)) + + if newchar == ' ' + before = '' + after = '' + elseif exists("b:surround_" .. char2nr(newchar)) + var all = Process(b:->get('surround_' .. char2nr(newchar))) + before = ExtractBefore(all) + after = ExtractAfter(all) + elseif exists("g:surround_" .. char2nr(newchar)) + var all = Process(g:->get('surround_' .. char2nr(newchar))) + before = ExtractBefore(all) + after = ExtractAfter(all) + elseif newchar ==# "p" + before = "\n" + after = "\n\n" + elseif newchar ==# 's' + before = ' ' + after = '' + elseif newchar ==# ':' + before = ':' + after = '' + elseif newchar =~# "[tT\<]" + var dounmapp = false + var dounmapb = false + + if !maparg(">", "c") + dounmapb = true + + # Hide from AsNeeded + execute printf("cnoremap > >") + endif + + var default = "" + + if newchar ==# "T" + default = sLastDel->matchstr('<\zs.\{-\}\ze>') + endif + + var tag = input("<", default) + + if dounmapb + silent! cunmap > + endif + + sInput = tag + if tag != "" + var keepAttributes = tag->match(">$") == -1 + + tag = tag->substitute('>*$', '', '') + + var attributes = keepAttributes ? removed->matchstr('<[^ \t\n]\+\zs\_.\{-\}\ze>') : "" + + sInput = tag .. '>' + + if tag =~ '/$' + tag = tag->substitute('/$', '', '') + before = printf('<%s%s />', tag, attributes) + after = '' + else + before = printf('<%s%s>', tag, attributes) + after = printf('', tag->substitute(' .*', '', '')) + endif + + if newchar == "\" + if wrapType ==# "v" || wrapType ==# "V" + before ..= "\n\t" + endif + + if wrapType ==# "v" + after = "\n" .. after + endif + endif + endif + elseif newchar ==# 'l' || newchar == '\' + # LaTeX + var env = input('\begin{') + + if env != "" + sInput = env .. "\" + env = printf('{%s%s', env, CloseMatch(env)) + before = printf('\begin%s', env) + after = printf('\end%s}', env->matchstr('[^}]*')) + + echo before + endif + elseif newchar ==# 'f' || newchar ==# 'F' + var fnf = input('function: ') + if fnf != "" + sInput = fnf .. "\" + + before = fnf->substitute('($', '', '') .. '(' + after = ')' + + if newchar ==# 'F' + before ..= ' ' + after = ' ' .. after + endif + endif + elseif newchar ==# "\" + var fncf = input('function: ') + sInput = fncf .. "\" + before = printf('(%s ', fncf) + after = ')' + elseif idx >= 0 + var spc = (idx % 3) == 1 ? ' ' : '' + idx = (idx / 3) * 3 + + before = pairs->strpart(idx + 1, 1) .. spc + after = spc .. pairs->strpart(idx + 2, 1) + elseif newchar == "\" || newchar == "\" + before = "{\n\t" + after = "\n}" + elseif newchar !~ '\a' + before = newchar + after = newchar + else + before = '' + after = '' + endif + + after = after->substitute('\n', '\n' .. initSpaces, 'g') + + if wrapType ==# 'V' || (linebreak && wrapType ==# "v") + before = before->substitute(' \+$', '', '') + after = after->substitute('^ \+', '', '') + + if after !~ '^\n' + after = initSpaces .. after + endif + + if keeper !~ '\n$' && after !~ '^\n' + keeper ..= "\n" + elseif keeper =~ '\n$' && after =~ '^\n' + after = after->strpart(1) + endif + + if keeper !~ '^\n' && before !~ '\n\s*$' + before ..= "\n" + if linebreak + before ..= "\t" + endif + elseif keeper =~ '^\n' && before =~ '\n\s*$' + keeper = keeper->strcharpart(1) + endif + + if wrapType ==# 'V' && keeper =~ '\n\s*\n$' + keeper = keeper->strcharpart(0, keeper->strchars() - 1) + endif + endif + + if wrapType ==# 'V' + before = initSpaces .. before + endif + + if before =~ '\n\s*\%$' + if wrapType ==# 'v' + keeper = initSpaces .. keeper + endif + + var padding = before + ->matchstr('\n\zs\s\+\%$') + ->substitute('\n\s\+\%$', '\n', '') + + keeper = FixIndent(keeper, padding) + endif + + if wrapType ==# 'V' + keeper = before .. keeper .. after + elseif wrapType =~ "^\" + # Really we should be iterating over the buffer + var repl = printf( + '%s\\1%s', + before->substitute('[\\~]', '\\&', 'g'), + after->substitute('[\\~]', '\\&', 'g') + )->substitute('\n', ' ', 'g') + + keeper = printf("%s\n", keeper) + ->substitute('\(.\{-\}\)\(\n\)', repl .. '\n', 'g') + ->substitute('\n\%$', '', '') + else + keeper = before .. extraspace .. keeper .. extraspace .. after + endif + + return keeper +enddef + +def Wrapreg(reg: string, char: string, removed: string, linebreak: bool) + var orig = reg->getreg() + var wrapType = reg->getregtype()->substitute('\d\+$', '', '') + var new = Wrap(orig, char, wrapType, removed, linebreak) + + setreg(reg, new, wrapType) +enddef + +export def Insert(wantedLinemode: bool = false): string + var char = InputReplacement() + var linemode = wantedLinemode + + while char == "\" || char == "\" + linemode = true + char = InputReplacement() + endwhile + + if char == "" + return "" + endif + + var cb_save = &clipboard + set clipboard-=unnamed clipboard-=unnamedplus + + # how to fix this? + var reg_save = getreg("@", 1) + + setreg('"', "\032", 'v') + Wrapreg('"', char, "", linemode) + + # If line mode is used and the surrounding consists solely of a suffix, remove the + # initial newline. This fits a use case of mine but is a little inconsistent. Is there + # anyone that would prefer the simpler behavior of just inserting the newline? + if linemode && match(getreg('"'), '^\n\s*\zs.*') == 0 + setreg('"', matchstr(getreg('"'), '^\n\s*\zs.*'), getregtype('"')) + endif + + # This can be used to append a placeholder to the end + if exists("g:surround_insert_tail") + setreg('"', g:surround_insert_tail, "a" .. getregtype('"')) + endif + + if &virtualedit != 'all' && col('.') >= col('$') + if &virtualedit == 'insert' + var extra_cols = virtcol('.') - virtcol('$') + if extra_cols > 0 + var [regval, regtype] = [getreg('"', 1, 1), getregtype('"')] + setreg('"', extra_cols->range()->map('" "')->join(''), 'v') + normal! ""p + setreg('"', regval, regtype) + endif + endif + normal! ""p + else + normal! ""P + endif + + if linemode + Reindent() + endif + + normal! `] + + search("\032", 'bW') + + setreg("@", reg_save) + &clipboard = cb_save + return "\" +enddef + +def Reindent() + if !b:->get('surround_indent', g:->get('surround_indent', true)) + return + endif + + if !&equalprg->empty() || !&indentexpr->empty() || &cindent || &smartindent || &lisp + silent normal! '[='] + endif +enddef + +export def DoSurround(value: string = null_string, new_value: string = null_string, linebreak: bool = false) + var sol_save = &startofline + set startofline + + var scount = v:count1 + var char = value == null_string ? InputTarget() : value + var spc = false + + + if char =~ '^\d\+' + scount = scount * char->matchstr('^\d\+')->str2nr() + char = char->substitute('^\d\+', '', '') + endif + + if char =~ '^ ' + char = char->strpart(1) + spc = true + endif + + if char == 'a' + char = '>' + endif + + if char == 'r' + char = ']' + endif + + var newchar = "" + + if new_value != null_string + newchar = new_value + + if newchar == "\" || newchar == "\" || newchar == "" + if !sol_save + set nostartofline + endif + + Beep() + return + endif + endif + + var cb_save = &clipboard + set clipboard-=unnamed clipboard-=unnamedplus + var append = "" + var original = getreg('"') + var otype = getregtype('"') + + setreg('"', "") + + var strcount = (scount == 1 ? "" : string(scount)) + + if char == '/' + execute 'normal! ' .. strcount .. "[/\(surround-d)" .. strcount .. ']/' + elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>"''`]' + execute 'normal! T' .. char + if getline('.')[col('.') - 1] == char + execute 'normal! l' + endif + execute "normal! \(surround-d)t" .. char + else + execute "normal! \(surround-d)" .. strcount .. 'i' .. char + endif + + var keeper = getreg('"') + var okeeper = keeper # for reindent below + + if keeper == "" + setreg('"', original, otype) + &clipboard = cb_save + if !sol_save + set nostartofline + endif + return + endif + + var oldline = getline('.') + var oldlnum = line('.') + + if char ==# "p" + setreg('"', '', 'V') + elseif char ==# "s" || char ==# "w" || char ==# "W" + # Do nothing + setreg('"', '') + elseif char =~ "[\"'`]" + execute "normal! i \\(surround-d)2i" .. char + setreg('"', substitute(getreg('"'), ' ', '', '')) + elseif char == '/' + normal! "_x + setreg('"', '/**/', "c") + keeper = keeper + ->substitute('^/\*\s\=', '', '') + ->substitute('\s\=\*$', '', '') + elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>]' + execute 'normal! F' .. char + execute "normal! \(surround-d)f" .. char + else + # One character backwards + search('\m.', 'bW') + execute "normal! \(surround-d)a" .. char + endif + + var removed = getreg('"') + var rem2 = removed->substitute('\n.*', '', '') + var oldhead = oldline->strpart(0, oldline->strlen() - rem2->strlen()) + var oldtail = oldline->strpart(oldline->strlen() - rem2->strlen()) + var regtype = getregtype('"') + + if char =~# '[\[({substitute('^\s\+', '', '') + ->substitute('\s\+$', '', '') + endif + + var pcmd = 'p' + + if col("']") == col("$") && virtcol('.') + 1 == virtcol('$') + if oldhead =~# '^\s*$' && new_value == null_string + keeper = keeper->substitute('\%^\n' .. oldhead .. '\(\s*.\{-\}\)\n\s*\%$', '\1', '') + endif + else + pcmd = "P" + endif + + if line('.') + 1 < oldlnum && regtype ==# "V" + pcmd = "p" + endif + + setreg('"', keeper, regtype) + + if newchar != "" + Wrapreg('"', newchar, removed, linebreak) + endif + + silent execute 'normal! ""' .. pcmd .. '`[' + + if removed =~ '\n' || okeeper =~ '\n' || getreg('"') =~ '\n' + Reindent() + endif + + if getline('.') =~ '^\s\+$' && keeper =~ '^\s*\n' + silent normal! cc + endif + + setreg('"', original, otype) + + sLastDel = removed + &clipboard = cb_save + + if newchar == "" + silent! call repeat#set("\(surround-delete)" .. char, scount) + else + var map = linebreak + ? "\(surround-change-line)%s%s%s" + : "\(surround-change)%s%s%s" + + silent! call repeat#set(printf(map, char, newchar, sInput), scount) + endif + + if !sol_save + set nostartofline + endif +enddef + +export def ChangeSurround(linebreak: bool = false) + var a: string = InputTarget() + if a == "" + Beep() + return + endif + + var b: string = InputReplacement() + if b == "" + Beep() + return + endif + + DoSurround(a, b, linebreak) +enddef + +export def OpFunc(atype: string, linebreak: bool = false): string + if atype ==# 'setup' + &opfunc = OpFunc + return 'g@' + endif + + var char = InputReplacement() + if char == "" + Beep() + return "" + endif + + var reg = '"' + var sel_save = &selection + &selection = "inclusive" + var cb_save = &clipboard + set clipboard-=unnamed clipboard-=unnamedplus + var reg_save = reg->getreg() + var reg_type = reg->getregtype() + var type = atype + + if atype == "char" + silent execute 'normal! v`[o`]"' .. reg .. 'y' + type = 'v' + elseif atype == "line" + silent execute 'normal! `[V`]"' .. reg .. 'y' + type = 'V' + elseif atype ==# "v" || atype ==# "V" || atype ==# "\" + &selection = sel_save + var ve = &virtualedit + + if !linebreak + set virtualedit= + endif + + silent execute 'normal! gv"' .. reg .. 'y' + &virtualedit = ve + elseif atype =~ '^\d\+$' + type = 'v' + silent execute 'normal! ^v' .. atype .. '$h"' .. reg .. 'y' + if mode() ==# 'v' + normal! v + Beep() + return "" + endif + else + &selection = sel_save + &clipboard = cb_save + Beep() + return "" + endif + + var append = "" + var keeper = reg->getreg() + + if type ==# "v" && atype !=# "v" + append = keeper->matchstr('\_s\@substitute('\_s\@getreg() =~ '\n' && type ==# 'v') + Reindent() + endif + + setreg(reg, reg_save, reg_type) + + &selection = sel_save + &clipboard = cb_save + + if atype =~ '^\d\+$' + var map = linebreak + ? "\(surround-add-line)" + : "\(surround-add)" + + silent! call repeat#set(map, char, sInput, atype) + else + silent! call repeat#set("\(surround-.)" .. char .. sInput) + endif + + return "" +enddef + +export def OpFunc2(atype: string = null_string): string + if atype ==# 'setup' + &opfunc = OpFunc2 + return 'g@' + endif + return OpFunc(atype, true) +enddef + +def CloseMatch(str: string): string + # Close an open (, {, [, or < on the command line. + var tail = str->matchstr('.[^\[\](){}<>]*$') + + return tail =~ '^\[.\+' + ? "]" + : tail =~ '^(.\+' + ? ")" + : tail =~ '^{.\+' + ? "}" + : tail =~ '^<.+' + ? ">" + : "" +enddef + +defcompile diff --git a/plugin/surround.vim b/plugin/surround.vim index 8a4016e..c699f46 100644 --- a/plugin/surround.vim +++ b/plugin/surround.vim @@ -1,630 +1,50 @@ -" surround.vim - Surroundings -" Author: Tim Pope -" Version: 2.2 -" GetLatestVimScripts: 1697 1 :AutoInstall: surround.vim +vim9script + +# surround.vim - Surroundings +# Author: Tim Pope +# Version: 2.2 +# GetLatestVimScripts: 1697 1 :AutoInstall: surround.vim if exists("g:loaded_surround") || &cp || v:version < 700 finish endif -let g:loaded_surround = 1 - -" Input functions {{{1 - -function! s:getchar() - let c = getchar() - if c =~ '^\d\+$' - let c = nr2char(c) - endif - return c -endfunction - -function! s:inputtarget() - let c = s:getchar() - while c =~ '^\d\+$' - let c .= s:getchar() - endwhile - if c == " " - let c .= s:getchar() - endif - if c =~ "\\|\\|\0" - return "" - else - return c - endif -endfunction - -function! s:inputreplacement() - let c = s:getchar() - if c == " " - let c .= s:getchar() - endif - if c =~ "\" || c =~ "\" - return "" - else - return c - endif -endfunction - -function! s:beep() - exe "norm! \" - return "" -endfunction - -function! s:redraw() - redraw - return "" -endfunction - -" }}}1 - -" Wrapping functions {{{1 - -function! s:extractbefore(str) - if a:str =~ '\r' - return matchstr(a:str,'.*\ze\r') - else - return matchstr(a:str,'.*\ze\n') - endif -endfunction - -function! s:extractafter(str) - if a:str =~ '\r' - return matchstr(a:str,'\r\zs.*') - else - return matchstr(a:str,'\n\zs.*') - endif -endfunction - -function! s:fixindent(str,spc) - let str = substitute(a:str,'\t',repeat(' ',&sw),'g') - let spc = substitute(a:spc,'\t',repeat(' ',&sw),'g') - let str = substitute(str,'\(\n\|\%^\).\@=','\1'.spc,'g') - if ! &et - let str = substitute(str,'\s\{'.&ts.'\}',"\t",'g') - endif - return str -endfunction - -function! s:process(string) - let i = 0 - for i in range(7) - let repl_{i} = '' - let m = matchstr(a:string,nr2char(i).'.\{-\}\ze'.nr2char(i)) - if m != '' - let m = substitute(strpart(m,1),'\r.*','','') - let repl_{i} = input(match(m,'\w\+$') >= 0 ? m.': ' : m) - endif - endfor - let s = "" - let i = 0 - while i < strlen(a:string) - let char = strpart(a:string,i,1) - if char2nr(char) < 8 - let next = stridx(a:string,char,i+1) - if next == -1 - let s .= char - else - let insertion = repl_{char2nr(char)} - let subs = strpart(a:string,i+1,next-i-1) - let subs = matchstr(subs,'\r.*') - while subs =~ '^\r.*\r' - let sub = matchstr(subs,"^\r\\zs[^\r]*\r[^\r]*") - let subs = strpart(subs,strlen(sub)+1) - let r = stridx(sub,"\r") - let insertion = substitute(insertion,strpart(sub,0,r),strpart(sub,r+1),'') - endwhile - let s .= insertion - let i = next - endif - else - let s .= char - endif - let i += 1 - endwhile - return s -endfunction - -function! s:wrap(string,char,type,removed,special) - let keeper = a:string - let newchar = a:char - let s:input = "" - let type = a:type - let linemode = type ==# 'V' ? 1 : 0 - let before = "" - let after = "" - if type ==# "V" - let initspaces = matchstr(keeper,'\%^\s*') - else - let initspaces = matchstr(getline('.'),'\%^\s*') - endif - let pairs = "b()B{}r[]a<>" - let extraspace = "" - if newchar =~ '^ ' - let newchar = strpart(newchar,1) - let extraspace = ' ' - endif - let idx = stridx(pairs,newchar) - if newchar == ' ' - let before = '' - let after = '' - elseif exists("b:surround_".char2nr(newchar)) - let all = s:process(b:surround_{char2nr(newchar)}) - let before = s:extractbefore(all) - let after = s:extractafter(all) - elseif exists("g:surround_".char2nr(newchar)) - let all = s:process(g:surround_{char2nr(newchar)}) - let before = s:extractbefore(all) - let after = s:extractafter(all) - elseif newchar ==# "p" - let before = "\n" - let after = "\n\n" - elseif newchar ==# 's' - let before = ' ' - let after = '' - elseif newchar ==# ':' - let before = ':' - let after = '' - elseif newchar =~# "[tT\<]" - let dounmapp = 0 - let dounmapb = 0 - if !maparg(">","c") - let dounmapb = 1 - " Hide from AsNeeded - exe "cn"."oremap > >" - endif - let default = "" - if newchar ==# "T" - if !exists("s:lastdel") - let s:lastdel = "" - endif - let default = matchstr(s:lastdel,'<\zs.\{-\}\ze>') - endif - let tag = input("<",default) - if dounmapb - silent! cunmap > - endif - let s:input = tag - if tag != "" - let keepAttributes = ( match(tag, ">$") == -1 ) - let tag = substitute(tag,'>*$','','') - let attributes = "" - if keepAttributes - let attributes = matchstr(a:removed, '<[^ \t\n]\+\zs\_.\{-\}\ze>') - endif - let s:input = tag . '>' - if tag =~ '/$' - let tag = substitute(tag, '/$', '', '') - let before = '<'.tag.attributes.' />' - let after = '' - else - let before = '<'.tag.attributes.'>' - let after = '' - endif - if newchar == "\" - if type ==# "v" || type ==# "V" - let before .= "\n\t" - endif - if type ==# "v" - let after = "\n". after - endif - endif - endif - elseif newchar ==# 'l' || newchar == '\' - " LaTeX - let env = input('\begin{') - if env != "" - let s:input = env."\" - let env = '{' . env - let env .= s:closematch(env) - echo '\begin'.env - let before = '\begin'.env - let after = '\end'.matchstr(env,'[^}]*').'}' - endif - elseif newchar ==# 'f' || newchar ==# 'F' - let fnc = input('function: ') - if fnc != "" - let s:input = fnc."\" - let before = substitute(fnc,'($','','').'(' - let after = ')' - if newchar ==# 'F' - let before .= ' ' - let after = ' ' . after - endif - endif - elseif newchar ==# "\" - let fnc = input('function: ') - let s:input = fnc."\" - let before = '('.fnc.' ' - let after = ')' - elseif idx >= 0 - let spc = (idx % 3) == 1 ? " " : "" - let idx = idx / 3 * 3 - let before = strpart(pairs,idx+1,1) . spc - let after = spc . strpart(pairs,idx+2,1) - elseif newchar == "\" || newchar == "\" - let before = "{\n\t" - let after = "\n}" - elseif newchar !~ '\a' - let before = newchar - let after = newchar - else - let before = '' - let after = '' - endif - let after = substitute(after ,'\n','\n'.initspaces,'g') - if type ==# 'V' || (a:special && type ==# "v") - let before = substitute(before,' \+$','','') - let after = substitute(after ,'^ \+','','') - if after !~ '^\n' - let after = initspaces.after - endif - if keeper !~ '\n$' && after !~ '^\n' - let keeper .= "\n" - elseif keeper =~ '\n$' && after =~ '^\n' - let after = strpart(after,1) - endif - if keeper !~ '^\n' && before !~ '\n\s*$' - let before .= "\n" - if a:special - let before .= "\t" - endif - elseif keeper =~ '^\n' && before =~ '\n\s*$' - let keeper = strcharpart(keeper,1) - endif - if type ==# 'V' && keeper =~ '\n\s*\n$' - let keeper = strcharpart(keeper,0,strchars(keeper) - 1) - endif - endif - if type ==# 'V' - let before = initspaces.before - endif - if before =~ '\n\s*\%$' - if type ==# 'v' - let keeper = initspaces.keeper - endif - let padding = matchstr(before,'\n\zs\s\+\%$') - let before = substitute(before,'\n\s\+\%$','\n','') - let keeper = s:fixindent(keeper,padding) - endif - if type ==# 'V' - let keeper = before.keeper.after - elseif type =~ "^\" - " Really we should be iterating over the buffer - let repl = substitute(before,'[\\~]','\\&','g').'\1'.substitute(after,'[\\~]','\\&','g') - let repl = substitute(repl,'\n',' ','g') - let keeper = substitute(keeper."\n",'\(.\{-\}\)\(\n\)',repl.'\n','g') - let keeper = substitute(keeper,'\n\%$','','') - else - let keeper = before.extraspace.keeper.extraspace.after - endif - return keeper -endfunction - -function! s:wrapreg(reg,char,removed,special) - let orig = getreg(a:reg) - let type = substitute(getregtype(a:reg),'\d\+$','','') - let new = s:wrap(orig,a:char,type,a:removed,a:special) - call setreg(a:reg,new,type) -endfunction -" }}}1 - -function! s:insert(...) " {{{1 - " Optional argument causes the result to appear on 3 lines, not 1 - let linemode = a:0 ? a:1 : 0 - let char = s:inputreplacement() - while char == "\" || char == "\" - " TODO: use total count for additional blank lines - let linemode += 1 - let char = s:inputreplacement() - endwhile - if char == "" - return "" - endif - let cb_save = &clipboard - set clipboard-=unnamed clipboard-=unnamedplus - let reg_save = @@ - call setreg('"',"\032",'v') - call s:wrapreg('"',char,"",linemode) - " If line mode is used and the surrounding consists solely of a suffix, - " remove the initial newline. This fits a use case of mine but is a - " little inconsistent. Is there anyone that would prefer the simpler - " behavior of just inserting the newline? - if linemode && match(getreg('"'),'^\n\s*\zs.*') == 0 - call setreg('"',matchstr(getreg('"'),'^\n\s*\zs.*'),getregtype('"')) - endif - " This can be used to append a placeholder to the end - if exists("g:surround_insert_tail") - call setreg('"',g:surround_insert_tail,"a".getregtype('"')) - endif - if &ve != 'all' && col('.') >= col('$') - if &ve == 'insert' - let extra_cols = virtcol('.') - virtcol('$') - if extra_cols > 0 - let [regval,regtype] = [getreg('"',1,1),getregtype('"')] - call setreg('"',join(map(range(extra_cols),'" "'),''),'v') - norm! ""p - call setreg('"',regval,regtype) - endif - endif - norm! ""p - else - norm! ""P - endif - if linemode - call s:reindent() - endif - norm! `] - call search("\032",'bW') - let @@ = reg_save - let &clipboard = cb_save - return "\" -endfunction " }}}1 - -function! s:reindent() abort " {{{1 - if get(b:, 'surround_indent', get(g:, 'surround_indent', 1)) && (!empty(&equalprg) || !empty(&indentexpr) || &cindent || &smartindent || &lisp) - silent norm! '[='] - endif -endfunction " }}}1 - -function! s:dosurround(...) " {{{1 - let sol_save = &startofline - set startofline - let scount = v:count1 - let char = (a:0 ? a:1 : s:inputtarget()) - let spc = "" - if char =~ '^\d\+' - let scount = scount * matchstr(char,'^\d\+') - let char = substitute(char,'^\d\+','','') - endif - if char =~ '^ ' - let char = strpart(char,1) - let spc = 1 - endif - if char == 'a' - let char = '>' - endif - if char == 'r' - let char = ']' - endif - let newchar = "" - if a:0 > 1 - let newchar = a:2 - if newchar == "\" || newchar == "\" || newchar == "" - if !sol_save - set nostartofline - endif - return s:beep() - endif - endif - let cb_save = &clipboard - set clipboard-=unnamed clipboard-=unnamedplus - let append = "" - let original = getreg('"') - let otype = getregtype('"') - call setreg('"',"") - let strcount = (scount == 1 ? "" : scount) - if char == '/' - exe 'norm! '.strcount.'[/d'.strcount.']/' - elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>"''`]' - exe 'norm! T'.char - if getline('.')[col('.')-1] == char - exe 'norm! l' - endif - exe 'norm! dt'.char - else - exe 'norm! d'.strcount.'i'.char - endif - let keeper = getreg('"') - let okeeper = keeper " for reindent below - if keeper == "" - call setreg('"',original,otype) - let &clipboard = cb_save - if !sol_save - set nostartofline - endif - return "" - endif - let oldline = getline('.') - let oldlnum = line('.') - if char ==# "p" - call setreg('"','','V') - elseif char ==# "s" || char ==# "w" || char ==# "W" - " Do nothing - call setreg('"','') - elseif char =~ "[\"'`]" - exe "norm! i \d2i".char - call setreg('"',substitute(getreg('"'),' ','','')) - elseif char == '/' - norm! "_x - call setreg('"','/**/',"c") - let keeper = substitute(substitute(keeper,'^/\*\s\=','',''),'\s\=\*$','','') - elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>]' - exe 'norm! F'.char - exe 'norm! df'.char - else - " One character backwards - call search('\m.', 'bW') - exe "norm! da".char - endif - let removed = getreg('"') - let rem2 = substitute(removed,'\n.*','','') - let oldhead = strpart(oldline,0,strlen(oldline)-strlen(rem2)) - let oldtail = strpart(oldline, strlen(oldline)-strlen(rem2)) - let regtype = getregtype('"') - if char =~# '[\[({ 2 ? a:3 : 0 - call s:wrapreg('"',newchar,removed,special) - endif - silent exe 'norm! ""'.pcmd.'`[' - if removed =~ '\n' || okeeper =~ '\n' || getreg('"') =~ '\n' - call s:reindent() - endif - if getline('.') =~ '^\s\+$' && keeper =~ '^\s*\n' - silent norm! cc - endif - call setreg('"',original,otype) - let s:lastdel = removed - let &clipboard = cb_save - if newchar == "" - silent! call repeat#set("\Dsurround".char,scount) - else - silent! call repeat#set("\C".(a:0 > 2 && a:3 ? "S" : "s")."urround".char.newchar.s:input,scount) - endif - if !sol_save - set nostartofline - endif -endfunction " }}}1 - -function! s:changesurround(...) " {{{1 - let a = s:inputtarget() - if a == "" - return s:beep() - endif - let b = s:inputreplacement() - if b == "" - return s:beep() - endif - call s:dosurround(a,b,a:0 && a:1) -endfunction " }}}1 - -function! s:opfunc(type, ...) abort " {{{1 - if a:type ==# 'setup' - let &opfunc = matchstr(expand(''), '\w\+$') - return 'g@' - endif - let char = s:inputreplacement() - if char == "" - return s:beep() - endif - let reg = '"' - let sel_save = &selection - let &selection = "inclusive" - let cb_save = &clipboard - set clipboard-=unnamed clipboard-=unnamedplus - let reg_save = getreg(reg) - let reg_type = getregtype(reg) - let type = a:type - if a:type == "char" - silent exe 'norm! v`[o`]"'.reg.'y' - let type = 'v' - elseif a:type == "line" - silent exe 'norm! `[V`]"'.reg.'y' - let type = 'V' - elseif a:type ==# "v" || a:type ==# "V" || a:type ==# "\" - let &selection = sel_save - let ve = &virtualedit - if !(a:0 && a:1) - set virtualedit= - endif - silent exe 'norm! gv"'.reg.'y' - let &virtualedit = ve - elseif a:type =~ '^\d\+$' - let type = 'v' - silent exe 'norm! ^v'.a:type.'$h"'.reg.'y' - if mode() ==# 'v' - norm! v - return s:beep() - endif - else - let &selection = sel_save - let &clipboard = cb_save - return s:beep() - endif - let keeper = getreg(reg) - if type ==# "v" && a:type !=# "v" - let append = matchstr(keeper,'\_s\@Y".(a:0 && a:1 ? "S" : "s")."surround".char.s:input,a:type) - else - silent! call repeat#set("\SurroundRepeat".char.s:input) - endif -endfunction - -function! s:opfunc2(...) abort - if !a:0 || a:1 ==# 'setup' - let &opfunc = matchstr(expand(''), '\w\+$') - return 'g@' - endif - call s:opfunc(a:1, 1) -endfunction " }}}1 - -function! s:closematch(str) " {{{1 - " Close an open (, {, [, or < on the command line. - let tail = matchstr(a:str,'.[^\[\](){}<>]*$') - if tail =~ '^\[.\+' - return "]" - elseif tail =~ '^(.\+' - return ")" - elseif tail =~ '^{.\+' - return "}" - elseif tail =~ '^<.+' - return ">" - else - return "" - endif -endfunction " }}}1 - -nnoremap SurroundRepeat . -nnoremap Dsurround :call dosurround(inputtarget()) -nnoremap Csurround :call changesurround() -nnoremap CSurround :call changesurround(1) -nnoremap Yssurround '^'.v:count1.opfunc('setup').'g_' -nnoremap YSsurround opfunc2('setup').'_' -nnoremap Ysurround opfunc('setup') -nnoremap YSurround opfunc2('setup') -vnoremap VSurround :call opfunc(visualmode(),visualmode() ==# 'V' ? 1 : 0) -vnoremap VgSurround :call opfunc(visualmode(),visualmode() ==# 'V' ? 0 : 1) -inoremap Isurround =insert() -inoremap ISurround =insert(1) +g:loaded_surround = 1 + +import 'surround.vim' as s9 + +nnoremap (surround-repeat) . +nnoremap (surround-d) d +nnoremap (surround-delete) s9.DoSurround(s9.InputTarget()) +nnoremap (surround-change) s9.ChangeSurround() +nnoremap (surround-change-line) s9.ChangeSurround(true) +nnoremap (surround-line) '^' .. v:count1 .. s9.OpFunc('setup') .. 'g_' +nnoremap (surround-line-add) s9.OpFunc2('setup') .. '_' +nnoremap (surround-add) s9.OpFunc('setup') +nnoremap (surround-add-line) s9.OpFunc2('setup') +vnoremap (surround-visual) s9.OpFunc(visualmode(), visualmode() ==# 'V') +vnoremap (surround-visual-line) s9.OpFunc(visualmode(), visualmode() !=# 'V') +inoremap (surround-insert) =s9.Insert() +inoremap (surround-insert-line) =s9.Insert(true) + +if !exists("g:surround_no_mappings") || !g:surround_no_mappings + nmap ds (surround-delete) + nmap cs (surround-change) + nmap cS (surround-change-line) + nmap ys (surround-add) + nmap yS (surround-add-line) + nmap yss (surround-line) + nmap ySs (surround-line-add) + nmap ySS (surround-line-add) + xmap S (surround-visual) + xmap gS (surround-visual-line) -if !exists("g:surround_no_mappings") || ! g:surround_no_mappings - nmap ds Dsurround - nmap cs Csurround - nmap cS CSurround - nmap ys Ysurround - nmap yS YSurround - nmap yss Yssurround - nmap ySs YSsurround - nmap ySS YSsurround - xmap S VSurround - xmap gS VgSurround if !exists("g:surround_no_insert_mappings") || ! g:surround_no_insert_mappings - if !hasmapto("Isurround","i") && "" == mapcheck("","i") - imap Isurround + if !hasmapto("(surround-insert)", "i") && "" == mapcheck("", "i") + imap (surround-insert) endif - imap s Isurround - imap S ISurround + imap s (surround-insert) + imap S (surround-insert-line) endif endif -" vim:set ft=vim sw=2 sts=2 et: +# vim:set ft=vim sw=2 sts=2 et: