├── .github └── FUNDING.yml ├── .gitignore ├── README.markdown ├── doc └── surround.txt └── plugin └── surround.vim /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: tpope 2 | custom: ["https://www.paypal.me/vimpope"] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/tags 2 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # surround.vim 2 | 3 | Surround.vim is all about "surroundings": parentheses, brackets, quotes, 4 | XML tags, and more. The plugin provides mappings to easily delete, 5 | change and add such surroundings in pairs. 6 | 7 | It's easiest to explain with examples. Press `cs"'` inside 8 | 9 | "Hello world!" 10 | 11 | to change it to 12 | 13 | 'Hello world!' 14 | 15 | Now press `cs'` to change it to 16 | 17 | Hello world! 18 | 19 | To go full circle, press `cst"` to get 20 | 21 | "Hello world!" 22 | 23 | To remove the delimiters entirely, press `ds"`. 24 | 25 | Hello world! 26 | 27 | Now with the cursor on "Hello", press `ysiw]` (`iw` is a text object). 28 | 29 | [Hello] world! 30 | 31 | Let's make that braces and add some space (use `}` instead of `{` for no 32 | space): `cs]{` 33 | 34 | { Hello } world! 35 | 36 | Now wrap the entire line in parentheses with `yssb` or `yss)`. 37 | 38 | ({ Hello } world!) 39 | 40 | Revert to the original text: `ds{ds)` 41 | 42 | Hello world! 43 | 44 | Emphasize hello: `ysiw` 45 | 46 | Hello world! 47 | 48 | Finally, let's try out visual mode. Press a capital V (for linewise 49 | visual mode) followed by `S

`. 50 | 51 |

52 | Hello world! 53 |

54 | 55 | This plugin is very powerful for HTML and XML editing, a niche which 56 | currently seems underfilled in Vim land. (As opposed to HTML/XML 57 | *inserting*, for which many plugins are available). Adding, changing, 58 | and removing pairs of tags simultaneously is a breeze. 59 | 60 | The `.` command will work with `ds`, `cs`, and `yss` if you install 61 | [repeat.vim](https://github.com/tpope/vim-repeat). 62 | 63 | ## Installation 64 | 65 | Install using your favorite package manager, or use Vim's built-in package 66 | support: 67 | 68 | mkdir -p ~/.vim/pack/tpope/start 69 | cd ~/.vim/pack/tpope/start 70 | git clone https://tpope.io/vim/surround.git 71 | vim -u NONE -c "helptags surround/doc" -c q 72 | 73 | ## FAQ 74 | 75 | > How do I surround without adding a space? 76 | 77 | Only the opening brackets—`[`, `{`, and `(`—add a space. Use a closing 78 | bracket, or the `b` (`(`) and `B` (`{`) aliases. 79 | 80 | ## Contributing 81 | 82 | See the contribution guidelines for 83 | [pathogen.vim](https://github.com/tpope/vim-pathogen#readme). 84 | 85 | ## Self-Promotion 86 | 87 | Like surround.vim? Star the repository on 88 | [GitHub](https://github.com/tpope/vim-surround) and vote for it on 89 | [vim.org](https://www.vim.org/scripts/script.php?script_id=1697). 90 | 91 | Love surround.vim? Follow [tpope](http://tpo.pe/) on 92 | [GitHub](https://github.com/tpope) and 93 | [Twitter](http://twitter.com/tpope). 94 | 95 | ## License 96 | 97 | Copyright (c) Tim Pope. Distributed under the same terms as Vim itself. 98 | See `:help license`. 99 | -------------------------------------------------------------------------------- /doc/surround.txt: -------------------------------------------------------------------------------- 1 | *surround.txt* Plugin for deleting, changing, and adding "surroundings" 2 | 3 | Author: Tim Pope 4 | License: Same terms as Vim itself (see |license|) 5 | 6 | This plugin is only available if 'compatible' is not set. 7 | 8 | INTRODUCTION *surround* 9 | 10 | This plugin is a tool for dealing with pairs of "surroundings." Examples 11 | of surroundings include parentheses, quotes, and HTML tags. They are 12 | closely related to what Vim refers to as |text-objects|. Provided 13 | are mappings to allow for removing, changing, and adding surroundings. 14 | 15 | Details follow on the exact semantics, but first, consider the following 16 | examples. An asterisk (*) is used to denote the cursor position. 17 | 18 | Old text Command New text ~ 19 | "Hello *world!" ds" Hello world! 20 | [123+4*56]/2 cs]) (123+456)/2 21 | "Look ma, I'm *HTML!" cs" Look ma, I'm HTML! 22 | if *x>3 { ysW( if ( x>3 ) { 23 | my $str = *whee!; vllllS' my $str = 'whee!'; 24 | 25 | While a few features of this plugin will work in older versions of Vim, 26 | Vim 7 is recommended for full functionality. 27 | 28 | MAPPINGS *surround-mappings* 29 | 30 | Delete surroundings is *ds* . The next character given determines the target 31 | to delete. The exact nature of the target is explained in |surround-targets| 32 | but essentially it is the last character of a |text-object|. This mapping 33 | deletes the difference between the "i"nner object and "a"n object. This is 34 | easiest to understand with some examples: 35 | 36 | Old text Command New text ~ 37 | "Hello *world!" ds" Hello world! 38 | (123+4*56)/2 ds) 123+456/2 39 |
Yo!*
dst Yo! 40 | 41 | Change surroundings is *cs* . It takes two arguments, a target like with 42 | |ds|, and a replacement. *cS* changes surroundings, placing the surrounded 43 | text on its own line(s) like |yS|. Details about the second argument can be 44 | found below in |surround-replacements|. Once again, examples are in order. 45 | 46 | Old text Command New text ~ 47 | "Hello *world!" cs"' 'Hello world!' 48 | "Hello *world!" cs" Hello world! 49 | (123+4*56)/2 cs)] [123+456]/2 50 | (123+4*56)/2 cs)[ [ 123+456 ]/2 51 |
Yo!*
cst

Yo!

52 | 53 | *ys* takes a valid Vim motion or text object as the first object, and wraps 54 | it using the second argument as with |cs|. (It's a stretch, but a good 55 | mnemonic for "ys" is "you surround".) 56 | 57 | Old text Command New text ~ 58 | Hello w*orld! ysiw) Hello (world)! 59 | 60 | As a special case, *yss* operates on the current line, ignoring leading 61 | whitespace. 62 | 63 | Old text Command New text ~ 64 | Hello w*orld! yssB {Hello world!} 65 | 66 | There is also *yS* and *ySS* which indent the surrounded text and place it 67 | on a line of its own. 68 | 69 | In visual mode, a simple "S" with an argument wraps the selection. This is 70 | referred to as the *vS* mapping, although ordinarily there will be 71 | additional keystrokes between the v and S. In linewise visual mode, the 72 | surroundings are placed on separate lines and indented. In blockwise visual 73 | mode, each line is surrounded. 74 | 75 | A "gS" in visual mode, known as *vgS* , behaves similarly. In linewise visual 76 | mode, the automatic indenting is suppressed. In blockwise visual mode, this 77 | enables surrounding past the end of the line with 'virtualedit' set (there 78 | seems to be no way in Vim Script to differentiate between a jagged end of line 79 | selection and a virtual block selected past the end of the line, so two maps 80 | were needed). 81 | 82 | *i_CTRL-G_s* *i_CTRL-G_S* 83 | Finally, there is an experimental insert mode mapping on s and . 84 | Beware that the latter won't work on terminals with flow control (if you 85 | accidentally freeze your terminal, use to unfreeze it). The mapping 86 | inserts the specified surroundings and puts the cursor between them. If, 87 | immediately after the mapping and before the replacement, a second or 88 | carriage return is pressed, the prefix, cursor, and suffix will be placed on 89 | three separate lines. S (not s) also exhibits this behavior. 90 | 91 | TARGETS *surround-targets* 92 | 93 | The |ds| and |cs| commands both take a target as their first argument. The 94 | possible targets are based closely on the |text-objects| provided by Vim. 95 | All targets are currently just one character. 96 | 97 | Eight punctuation marks, (, ), {, }, [, ], <, and >, represent themselves 98 | and their counterparts. If the opening mark is used, contained whitespace is 99 | also trimmed. The targets b, B, r, and a are aliases for ), }, ], and > 100 | (the first two mirror Vim; the second two are completely arbitrary and 101 | subject to change). 102 | 103 | Three quote marks, ', ", `, represent themselves, in pairs. They are only 104 | searched for on the current line. 105 | 106 | A t is a pair of HTML or XML tags. See |tag-blocks| for details. Remember 107 | that you can specify a numerical argument if you want to get to a tag other 108 | than the innermost one. 109 | 110 | The letters w, W, and s correspond to a |word|, a |WORD|, and a |sentence|, 111 | respectively. These are special in that they have nothing to delete, and 112 | used with |ds| they are a no-op. With |cs|, one could consider them a 113 | slight shortcut for ysi (cswb == ysiwb, more or less). 114 | 115 | A p represents a |paragraph|. This behaves similarly to w, W, and s above; 116 | however, newlines are sometimes added and/or removed. 117 | 118 | REPLACEMENTS *surround-replacements* 119 | 120 | A replacement argument is a single character, and is required by |cs|, |ys|, 121 | and |vS|. Undefined replacement characters (with the exception of alphabetic 122 | characters) default to placing themselves at the beginning and end of the 123 | destination, which can be useful for characters like / and |. 124 | 125 | If either ), }, ], or > is used, the text is wrapped in the appropriate pair 126 | of characters. Similar behavior can be found with (, {, and [ (but not <), 127 | which append an additional space to the inside. Like with the targets above, 128 | b, B, r, and a are aliases for ), }, ], and >. To fulfill the common need for 129 | code blocks in C-style languages, (which is really ) adds braces on 130 | lines separate from the content. 131 | 132 | If t or < is used, Vim prompts for an HTML/XML tag to insert. You may specify 133 | attributes here and they will be stripped from the closing tag. If replacing a 134 | tag, its attributes are kept in the new tag. End your input with > to discard 135 | the those attributes. If is used, the tags will appear on lines by 136 | themselves. 137 | 138 | If f, F, or is used, Vim prompts for a function name to insert. The target 139 | text will be wrapped in a function call. If f is used, the text is wrapped with 140 | () parentheses; F adds additional spaces inside the parentheses. inserts the 141 | function name inside the parentheses. 142 | 143 | Old text Command New text ~ 144 | "hello" ysWfprint print("hello") 145 | "hello" ysWFprint print( "hello" ) 146 | "hello" ysWprint (print "hello") 147 | 148 | If s is used, a leading but not trailing space is added. This is useful for 149 | removing parentheses from a function call with csbs. 150 | 151 | CUSTOMIZING *surround-customizing* 152 | 153 | The following adds a potential replacement on "-" (ASCII 45) in PHP files. 154 | (To determine the ASCII code to use, :echo char2nr("-")). The carriage 155 | return will be replaced by the original text. 156 | > 157 | autocmd FileType php let b:surround_45 = "" 158 | < 159 | This can be used in a PHP file as in the following example. 160 | 161 | Old text Command New text ~ 162 | print "Hello *world!" yss- 163 | 164 | Additionally, one can use a global variable for globally available 165 | replacements. 166 | > 167 | let g:surround_45 = "<% \r %>" 168 | let g:surround_61 = "<%= \r %>" 169 | < 170 | Advanced, experimental, and subject to change: One can also prompt for 171 | replacement text. The syntax for this is to surround the replacement in pairs 172 | of low numbered control characters. If this sounds confusing, that's because 173 | it is (but it makes the parsing easy). Consider the following example for a 174 | LaTeX environment on the "l" replacement. 175 | > 176 | let g:surround_108 = "\\begin{\1environment: \1}\r\\end{\1\1}" 177 | < 178 | When this replacement is used, the user is prompted with an "environment: " 179 | prompt for input. This input is inserted between each set of \1's. 180 | Additional inputs up to \7 can be used. 181 | 182 | Furthermore, one can specify a regular expression substitution to apply. 183 | > 184 | let g:surround_108 = "\\begin{\1environment: \1}\r\\end{\1\r}.*\r\1}" 185 | < 186 | This will remove anything after the first } in the input when the text is 187 | placed within the \end{} slot. The first \r marks where the pattern begins, 188 | and the second where the replacement text begins. 189 | 190 | Here's a second example for creating an HTML
. The substitution cleverly 191 | prompts for an id, but only adds id="" if it is non-blank. You may have to 192 | read this one a few times slowly before you understand it. 193 | > 194 | let g:surround_{char2nr("d")} = "\r
" 195 | < 196 | Inputting text replacements is a proof of concept at this point. The ugly, 197 | unintuitive interface and the brevity of the documentation reflect this. 198 | 199 | Finally, It is possible to always append a string to surroundings in insert 200 | mode (and only insert mode). This is useful with certain plugins and mappings 201 | that allow you to jump to such markings. 202 | > 203 | let g:surround_insert_tail = "<++>" 204 | < 205 | vim:tw=78:ts=8:ft=help:norl: 206 | -------------------------------------------------------------------------------- /plugin/surround.vim: -------------------------------------------------------------------------------- 1 | " surround.vim - Surroundings 2 | " Author: Tim Pope 3 | " Version: 2.2 4 | " GetLatestVimScripts: 1697 1 :AutoInstall: surround.vim 5 | 6 | if exists("g:loaded_surround") || &cp || v:version < 700 7 | finish 8 | endif 9 | let g:loaded_surround = 1 10 | 11 | " Input functions {{{1 12 | 13 | function! s:getchar() 14 | let c = getchar() 15 | if c =~ '^\d\+$' 16 | let c = nr2char(c) 17 | endif 18 | return c 19 | endfunction 20 | 21 | function! s:inputtarget() 22 | let c = s:getchar() 23 | while c =~ '^\d\+$' 24 | let c .= s:getchar() 25 | endwhile 26 | if c == " " 27 | let c .= s:getchar() 28 | endif 29 | if c =~ "\\|\\|\0" 30 | return "" 31 | else 32 | return c 33 | endif 34 | endfunction 35 | 36 | function! s:inputreplacement() 37 | let c = s:getchar() 38 | if c == " " 39 | let c .= s:getchar() 40 | endif 41 | if c =~ "\" || c =~ "\" 42 | return "" 43 | else 44 | return c 45 | endif 46 | endfunction 47 | 48 | function! s:beep() 49 | exe "norm! \" 50 | return "" 51 | endfunction 52 | 53 | function! s:redraw() 54 | redraw 55 | return "" 56 | endfunction 57 | 58 | " }}}1 59 | 60 | " Wrapping functions {{{1 61 | 62 | function! s:extractbefore(str) 63 | if a:str =~ '\r' 64 | return matchstr(a:str,'.*\ze\r') 65 | else 66 | return matchstr(a:str,'.*\ze\n') 67 | endif 68 | endfunction 69 | 70 | function! s:extractafter(str) 71 | if a:str =~ '\r' 72 | return matchstr(a:str,'\r\zs.*') 73 | else 74 | return matchstr(a:str,'\n\zs.*') 75 | endif 76 | endfunction 77 | 78 | function! s:fixindent(str,spc) 79 | let str = substitute(a:str,'\t',repeat(' ',&sw),'g') 80 | let spc = substitute(a:spc,'\t',repeat(' ',&sw),'g') 81 | let str = substitute(str,'\(\n\|\%^\).\@=','\1'.spc,'g') 82 | if ! &et 83 | let str = substitute(str,'\s\{'.&ts.'\}',"\t",'g') 84 | endif 85 | return str 86 | endfunction 87 | 88 | function! s:process(string) 89 | let i = 0 90 | for i in range(7) 91 | let repl_{i} = '' 92 | let m = matchstr(a:string,nr2char(i).'.\{-\}\ze'.nr2char(i)) 93 | if m != '' 94 | let m = substitute(strpart(m,1),'\r.*','','') 95 | let repl_{i} = input(match(m,'\w\+$') >= 0 ? m.': ' : m) 96 | endif 97 | endfor 98 | let s = "" 99 | let i = 0 100 | while i < strlen(a:string) 101 | let char = strpart(a:string,i,1) 102 | if char2nr(char) < 8 103 | let next = stridx(a:string,char,i+1) 104 | if next == -1 105 | let s .= char 106 | else 107 | let insertion = repl_{char2nr(char)} 108 | let subs = strpart(a:string,i+1,next-i-1) 109 | let subs = matchstr(subs,'\r.*') 110 | while subs =~ '^\r.*\r' 111 | let sub = matchstr(subs,"^\r\\zs[^\r]*\r[^\r]*") 112 | let subs = strpart(subs,strlen(sub)+1) 113 | let r = stridx(sub,"\r") 114 | let insertion = substitute(insertion,strpart(sub,0,r),strpart(sub,r+1),'') 115 | endwhile 116 | let s .= insertion 117 | let i = next 118 | endif 119 | else 120 | let s .= char 121 | endif 122 | let i += 1 123 | endwhile 124 | return s 125 | endfunction 126 | 127 | function! s:wrap(string,char,type,removed,special) 128 | let keeper = a:string 129 | let newchar = a:char 130 | let s:input = "" 131 | let type = a:type 132 | let linemode = type ==# 'V' ? 1 : 0 133 | let before = "" 134 | let after = "" 135 | if type ==# "V" 136 | let initspaces = matchstr(keeper,'\%^\s*') 137 | else 138 | let initspaces = matchstr(getline('.'),'\%^\s*') 139 | endif 140 | let pairs = "b()B{}r[]a<>" 141 | let extraspace = "" 142 | if newchar =~ '^ ' 143 | let newchar = strpart(newchar,1) 144 | let extraspace = ' ' 145 | endif 146 | let idx = stridx(pairs,newchar) 147 | if newchar == ' ' 148 | let before = '' 149 | let after = '' 150 | elseif exists("b:surround_".char2nr(newchar)) 151 | let all = s:process(b:surround_{char2nr(newchar)}) 152 | let before = s:extractbefore(all) 153 | let after = s:extractafter(all) 154 | elseif exists("g:surround_".char2nr(newchar)) 155 | let all = s:process(g:surround_{char2nr(newchar)}) 156 | let before = s:extractbefore(all) 157 | let after = s:extractafter(all) 158 | elseif newchar ==# "p" 159 | let before = "\n" 160 | let after = "\n\n" 161 | elseif newchar ==# 's' 162 | let before = ' ' 163 | let after = '' 164 | elseif newchar ==# ':' 165 | let before = ':' 166 | let after = '' 167 | elseif newchar =~# "[tT\<]" 168 | let dounmapp = 0 169 | let dounmapb = 0 170 | if !maparg(">","c") 171 | let dounmapb = 1 172 | " Hide from AsNeeded 173 | exe "cn"."oremap > >" 174 | endif 175 | let default = "" 176 | if newchar ==# "T" 177 | if !exists("s:lastdel") 178 | let s:lastdel = "" 179 | endif 180 | let default = matchstr(s:lastdel,'<\zs.\{-\}\ze>') 181 | endif 182 | let tag = input("<",default) 183 | if dounmapb 184 | silent! cunmap > 185 | endif 186 | let s:input = tag 187 | if tag != "" 188 | let keepAttributes = ( match(tag, ">$") == -1 ) 189 | let tag = substitute(tag,'>*$','','') 190 | let attributes = "" 191 | if keepAttributes 192 | let attributes = matchstr(a:removed, '<[^ \t\n]\+\zs\_.\{-\}\ze>') 193 | endif 194 | let s:input = tag . '>' 195 | if tag =~ '/$' 196 | let tag = substitute(tag, '/$', '', '') 197 | let before = '<'.tag.attributes.' />' 198 | let after = '' 199 | else 200 | let before = '<'.tag.attributes.'>' 201 | let after = '' 202 | endif 203 | if newchar == "\" 204 | if type ==# "v" || type ==# "V" 205 | let before .= "\n\t" 206 | endif 207 | if type ==# "v" 208 | let after = "\n". after 209 | endif 210 | endif 211 | endif 212 | elseif newchar ==# 'l' || newchar == '\' 213 | " LaTeX 214 | let env = input('\begin{') 215 | if env != "" 216 | let s:input = env."\" 217 | let env = '{' . env 218 | let env .= s:closematch(env) 219 | echo '\begin'.env 220 | let before = '\begin'.env 221 | let after = '\end'.matchstr(env,'[^}]*').'}' 222 | endif 223 | elseif newchar ==# 'f' || newchar ==# 'F' 224 | let fnc = input('function: ') 225 | if fnc != "" 226 | let s:input = fnc."\" 227 | let before = substitute(fnc,'($','','').'(' 228 | let after = ')' 229 | if newchar ==# 'F' 230 | let before .= ' ' 231 | let after = ' ' . after 232 | endif 233 | endif 234 | elseif newchar ==# "\" 235 | let fnc = input('function: ') 236 | let s:input = fnc."\" 237 | let before = '('.fnc.' ' 238 | let after = ')' 239 | elseif idx >= 0 240 | let spc = (idx % 3) == 1 ? " " : "" 241 | let idx = idx / 3 * 3 242 | let before = strpart(pairs,idx+1,1) . spc 243 | let after = spc . strpart(pairs,idx+2,1) 244 | elseif newchar == "\" || newchar == "\" 245 | let before = "{\n\t" 246 | let after = "\n}" 247 | elseif newchar !~ '\a' 248 | let before = newchar 249 | let after = newchar 250 | else 251 | let before = '' 252 | let after = '' 253 | endif 254 | let after = substitute(after ,'\n','\n'.initspaces,'g') 255 | if type ==# 'V' || (a:special && type ==# "v") 256 | let before = substitute(before,' \+$','','') 257 | let after = substitute(after ,'^ \+','','') 258 | if after !~ '^\n' 259 | let after = initspaces.after 260 | endif 261 | if keeper !~ '\n$' && after !~ '^\n' 262 | let keeper .= "\n" 263 | elseif keeper =~ '\n$' && after =~ '^\n' 264 | let after = strpart(after,1) 265 | endif 266 | if keeper !~ '^\n' && before !~ '\n\s*$' 267 | let before .= "\n" 268 | if a:special 269 | let before .= "\t" 270 | endif 271 | elseif keeper =~ '^\n' && before =~ '\n\s*$' 272 | let keeper = strcharpart(keeper,1) 273 | endif 274 | if type ==# 'V' && keeper =~ '\n\s*\n$' 275 | let keeper = strcharpart(keeper,0,strchars(keeper) - 1) 276 | endif 277 | endif 278 | if type ==# 'V' 279 | let before = initspaces.before 280 | endif 281 | if before =~ '\n\s*\%$' 282 | if type ==# 'v' 283 | let keeper = initspaces.keeper 284 | endif 285 | let padding = matchstr(before,'\n\zs\s\+\%$') 286 | let before = substitute(before,'\n\s\+\%$','\n','') 287 | let keeper = s:fixindent(keeper,padding) 288 | endif 289 | if type ==# 'V' 290 | let keeper = before.keeper.after 291 | elseif type =~ "^\" 292 | " Really we should be iterating over the buffer 293 | let repl = substitute(before,'[\\~]','\\&','g').'\1'.substitute(after,'[\\~]','\\&','g') 294 | let repl = substitute(repl,'\n',' ','g') 295 | let keeper = substitute(keeper."\n",'\(.\{-\}\)\(\n\)',repl.'\n','g') 296 | let keeper = substitute(keeper,'\n\%$','','') 297 | else 298 | let keeper = before.extraspace.keeper.extraspace.after 299 | endif 300 | return keeper 301 | endfunction 302 | 303 | function! s:wrapreg(reg,char,removed,special) 304 | let orig = getreg(a:reg) 305 | let type = substitute(getregtype(a:reg),'\d\+$','','') 306 | let new = s:wrap(orig,a:char,type,a:removed,a:special) 307 | call setreg(a:reg,new,type) 308 | endfunction 309 | " }}}1 310 | 311 | function! s:insert(...) " {{{1 312 | " Optional argument causes the result to appear on 3 lines, not 1 313 | let linemode = a:0 ? a:1 : 0 314 | let char = s:inputreplacement() 315 | while char == "\" || char == "\" 316 | " TODO: use total count for additional blank lines 317 | let linemode += 1 318 | let char = s:inputreplacement() 319 | endwhile 320 | if char == "" 321 | return "" 322 | endif 323 | let cb_save = &clipboard 324 | set clipboard-=unnamed clipboard-=unnamedplus 325 | let reg_save = @@ 326 | call setreg('"',"\032",'v') 327 | call s:wrapreg('"',char,"",linemode) 328 | " If line mode is used and the surrounding consists solely of a suffix, 329 | " remove the initial newline. This fits a use case of mine but is a 330 | " little inconsistent. Is there anyone that would prefer the simpler 331 | " behavior of just inserting the newline? 332 | if linemode && match(getreg('"'),'^\n\s*\zs.*') == 0 333 | call setreg('"',matchstr(getreg('"'),'^\n\s*\zs.*'),getregtype('"')) 334 | endif 335 | " This can be used to append a placeholder to the end 336 | if exists("g:surround_insert_tail") 337 | call setreg('"',g:surround_insert_tail,"a".getregtype('"')) 338 | endif 339 | if &ve != 'all' && col('.') >= col('$') 340 | if &ve == 'insert' 341 | let extra_cols = virtcol('.') - virtcol('$') 342 | if extra_cols > 0 343 | let [regval,regtype] = [getreg('"',1,1),getregtype('"')] 344 | call setreg('"',join(map(range(extra_cols),'" "'),''),'v') 345 | norm! ""p 346 | call setreg('"',regval,regtype) 347 | endif 348 | endif 349 | norm! ""p 350 | else 351 | norm! ""P 352 | endif 353 | if linemode 354 | call s:reindent() 355 | endif 356 | norm! `] 357 | call search("\032",'bW') 358 | let @@ = reg_save 359 | let &clipboard = cb_save 360 | return "\" 361 | endfunction " }}}1 362 | 363 | function! s:reindent() abort " {{{1 364 | if get(b:, 'surround_indent', get(g:, 'surround_indent', 1)) && (!empty(&equalprg) || !empty(&indentexpr) || &cindent || &smartindent || &lisp) 365 | silent norm! '[='] 366 | endif 367 | endfunction " }}}1 368 | 369 | function! s:dosurround(...) " {{{1 370 | let sol_save = &startofline 371 | set startofline 372 | let scount = v:count1 373 | let char = (a:0 ? a:1 : s:inputtarget()) 374 | let spc = "" 375 | if char =~ '^\d\+' 376 | let scount = scount * matchstr(char,'^\d\+') 377 | let char = substitute(char,'^\d\+','','') 378 | endif 379 | if char =~ '^ ' 380 | let char = strpart(char,1) 381 | let spc = 1 382 | endif 383 | if char == 'a' 384 | let char = '>' 385 | endif 386 | if char == 'r' 387 | let char = ']' 388 | endif 389 | let newchar = "" 390 | if a:0 > 1 391 | let newchar = a:2 392 | if newchar == "\" || newchar == "\" || newchar == "" 393 | if !sol_save 394 | set nostartofline 395 | endif 396 | return s:beep() 397 | endif 398 | endif 399 | let cb_save = &clipboard 400 | set clipboard-=unnamed clipboard-=unnamedplus 401 | let append = "" 402 | let original = getreg('"') 403 | let otype = getregtype('"') 404 | call setreg('"',"") 405 | let strcount = (scount == 1 ? "" : scount) 406 | if char == '/' 407 | exe 'norm! '.strcount.'[/d'.strcount.']/' 408 | elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>"''`]' 409 | exe 'norm! T'.char 410 | if getline('.')[col('.')-1] == char 411 | exe 'norm! l' 412 | endif 413 | exe 'norm! dt'.char 414 | else 415 | exe 'norm! d'.strcount.'i'.char 416 | endif 417 | let keeper = getreg('"') 418 | let okeeper = keeper " for reindent below 419 | if keeper == "" 420 | call setreg('"',original,otype) 421 | let &clipboard = cb_save 422 | if !sol_save 423 | set nostartofline 424 | endif 425 | return "" 426 | endif 427 | let oldline = getline('.') 428 | let oldlnum = line('.') 429 | if char ==# "p" 430 | call setreg('"','','V') 431 | elseif char ==# "s" || char ==# "w" || char ==# "W" 432 | " Do nothing 433 | call setreg('"','') 434 | elseif char =~ "[\"'`]" 435 | exe "norm! i \d2i".char 436 | call setreg('"',substitute(getreg('"'),' ','','')) 437 | elseif char == '/' 438 | norm! "_x 439 | call setreg('"','/**/',"c") 440 | let keeper = substitute(substitute(keeper,'^/\*\s\=','',''),'\s\=\*$','','') 441 | elseif char =~# '[[:punct:][:space:]]' && char !~# '[][(){}<>]' 442 | exe 'norm! F'.char 443 | exe 'norm! df'.char 444 | else 445 | " One character backwards 446 | call search('\m.', 'bW') 447 | exe "norm! da".char 448 | endif 449 | let removed = getreg('"') 450 | let rem2 = substitute(removed,'\n.*','','') 451 | let oldhead = strpart(oldline,0,strlen(oldline)-strlen(rem2)) 452 | let oldtail = strpart(oldline, strlen(oldline)-strlen(rem2)) 453 | let regtype = getregtype('"') 454 | if char =~# '[\[({ 2 ? a:3 : 0 472 | call s:wrapreg('"',newchar,removed,special) 473 | endif 474 | silent exe 'norm! ""'.pcmd.'`[' 475 | if removed =~ '\n' || okeeper =~ '\n' || getreg('"') =~ '\n' 476 | call s:reindent() 477 | endif 478 | if getline('.') =~ '^\s\+$' && keeper =~ '^\s*\n' 479 | silent norm! cc 480 | endif 481 | call setreg('"',original,otype) 482 | let s:lastdel = removed 483 | let &clipboard = cb_save 484 | if newchar == "" 485 | silent! call repeat#set("\Dsurround".char,scount) 486 | else 487 | silent! call repeat#set("\C".(a:0 > 2 && a:3 ? "S" : "s")."urround".char.newchar.s:input,scount) 488 | endif 489 | if !sol_save 490 | set nostartofline 491 | endif 492 | endfunction " }}}1 493 | 494 | function! s:changesurround(...) " {{{1 495 | let a = s:inputtarget() 496 | if a == "" 497 | return s:beep() 498 | endif 499 | let b = s:inputreplacement() 500 | if b == "" 501 | return s:beep() 502 | endif 503 | call s:dosurround(a,b,a:0 && a:1) 504 | endfunction " }}}1 505 | 506 | function! s:opfunc(type, ...) abort " {{{1 507 | if a:type ==# 'setup' 508 | let &opfunc = matchstr(expand(''), '\w\+$') 509 | return 'g@' 510 | endif 511 | let char = s:inputreplacement() 512 | if char == "" 513 | return s:beep() 514 | endif 515 | let reg = '"' 516 | let sel_save = &selection 517 | let &selection = "inclusive" 518 | let cb_save = &clipboard 519 | set clipboard-=unnamed clipboard-=unnamedplus 520 | let reg_save = getreg(reg) 521 | let reg_type = getregtype(reg) 522 | let type = a:type 523 | if a:type == "char" 524 | silent exe 'norm! v`[o`]"'.reg.'y' 525 | let type = 'v' 526 | elseif a:type == "line" 527 | silent exe 'norm! `[V`]"'.reg.'y' 528 | let type = 'V' 529 | elseif a:type ==# "v" || a:type ==# "V" || a:type ==# "\" 530 | let &selection = sel_save 531 | let ve = &virtualedit 532 | if !(a:0 && a:1) 533 | set virtualedit= 534 | endif 535 | silent exe 'norm! gv"'.reg.'y' 536 | let &virtualedit = ve 537 | elseif a:type =~ '^\d\+$' 538 | let type = 'v' 539 | silent exe 'norm! ^v'.a:type.'$h"'.reg.'y' 540 | if mode() ==# 'v' 541 | norm! v 542 | return s:beep() 543 | endif 544 | else 545 | let &selection = sel_save 546 | let &clipboard = cb_save 547 | return s:beep() 548 | endif 549 | let keeper = getreg(reg) 550 | if type ==# "v" && a:type !=# "v" 551 | let append = matchstr(keeper,'\_s\@Y".(a:0 && a:1 ? "S" : "s")."surround".char.s:input,a:type) 568 | else 569 | silent! call repeat#set("\SurroundRepeat".char.s:input) 570 | endif 571 | endfunction 572 | 573 | function! s:opfunc2(...) abort 574 | if !a:0 || a:1 ==# 'setup' 575 | let &opfunc = matchstr(expand(''), '\w\+$') 576 | return 'g@' 577 | endif 578 | call s:opfunc(a:1, 1) 579 | endfunction " }}}1 580 | 581 | function! s:closematch(str) " {{{1 582 | " Close an open (, {, [, or < on the command line. 583 | let tail = matchstr(a:str,'.[^\[\](){}<>]*$') 584 | if tail =~ '^\[.\+' 585 | return "]" 586 | elseif tail =~ '^(.\+' 587 | return ")" 588 | elseif tail =~ '^{.\+' 589 | return "}" 590 | elseif tail =~ '^<.+' 591 | return ">" 592 | else 593 | return "" 594 | endif 595 | endfunction " }}}1 596 | 597 | nnoremap SurroundRepeat . 598 | nnoremap Dsurround :call dosurround(inputtarget()) 599 | nnoremap Csurround :call changesurround() 600 | nnoremap CSurround :call changesurround(1) 601 | nnoremap Yssurround '^'.v:count1.opfunc('setup').'g_' 602 | nnoremap YSsurround opfunc2('setup').'_' 603 | nnoremap Ysurround opfunc('setup') 604 | nnoremap YSurround opfunc2('setup') 605 | vnoremap VSurround :call opfunc(visualmode(),visualmode() ==# 'V' ? 1 : 0) 606 | vnoremap VgSurround :call opfunc(visualmode(),visualmode() ==# 'V' ? 0 : 1) 607 | inoremap Isurround =insert() 608 | inoremap ISurround =insert(1) 609 | 610 | if !exists("g:surround_no_mappings") || ! g:surround_no_mappings 611 | nmap ds Dsurround 612 | nmap cs Csurround 613 | nmap cS CSurround 614 | nmap ys Ysurround 615 | nmap yS YSurround 616 | nmap yss Yssurround 617 | nmap ySs YSsurround 618 | nmap ySS YSsurround 619 | xmap S VSurround 620 | xmap gS VgSurround 621 | if !exists("g:surround_no_insert_mappings") || ! g:surround_no_insert_mappings 622 | if !hasmapto("Isurround","i") && "" == mapcheck("","i") 623 | imap Isurround 624 | endif 625 | imap s Isurround 626 | imap S ISurround 627 | endif 628 | endif 629 | 630 | " vim:set ft=vim sw=2 sts=2 et: 631 | --------------------------------------------------------------------------------