├── .gitignore ├── .travis.yml ├── Gemfile ├── Guardfile ├── README.md ├── Rakefile ├── VimFlavor ├── autoload └── operator │ └── surround.vim ├── doc └── operator-surround.txt ├── plugin └── operator │ └── surround.vim └── t ├── append_spec.vim ├── corner_cases_spec.vim ├── default_spec.vim ├── delete_spec.vim ├── filetype_spec.vim ├── input_spec.vim ├── recognize_both_end_spec.vim ├── repetition_spec.vim ├── replace_spec.vim └── use_input_spec.vim /.gitignore: -------------------------------------------------------------------------------- 1 | *.lock 2 | .vim-flavor 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.3 4 | script: rake ci 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'vim-flavor', '~> 1.1' 4 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | def which cmd 4 | dir = ENV['PATH'].split(':').find {|p| File.executable? File.join(p, cmd)} 5 | File.join(dir, cmd) unless dir.nil? 6 | end 7 | 8 | def notify m 9 | msg = "'#{m.gsub(/'/, "''")}\\n#{Time.now.to_s}'" 10 | case 11 | when which('terminal-notifier') 12 | `terminal-notifier -message #{msg}` 13 | when which('notify-send') 14 | `notify-send #{msg}` 15 | when which('tmux') 16 | `tmux display-message #{msg}` if system('tmux list-clients 1>/dev/null 2>&1') 17 | end 18 | end 19 | 20 | guard :shell do 21 | watch /^(autoload|plugin|t)\/.+\.vim$/ do 22 | system "rake test" 23 | notify "test(s) failed" unless $?.success? 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Operator to Surround a Text Object 2 | ================================== 3 | [![Build Status](https://travis-ci.org/rhysd/vim-operator-surround.svg?branch=master)](https://travis-ci.org/rhysd/vim-operator-surround) 4 | 5 | This plugin provides Vim operator mappings to deal with surrounds like `()`, `""` and so on. 6 | In addition, both end of the text object are the same character, vim-operator-surround recognizes 7 | them as a surround (this behavior is customizable with some variables). 8 | 9 | It can 10 | - surround a text object with a specified block. 11 | - replace a surround in a text object with a specified block. 12 | - delete a surround in a text object. 13 | 14 | This plugin would be more useful with [vim-textobj-between](https://github.com/thinca/vim-textobj-between) or 15 | [vim-textobj-anyblock](https://github.com/rhysd/vim-textobj-anyblock). 16 | And you can customize and add surround definitions in global and filetype specific scope. 17 | 18 | 19 | ## Policy of This Plugin (or The Reason Why I Don't Use [vim-surround](https://github.com/tpope/vim-surround)) 20 | 21 | - **Simplicity** : All should be done with operator mappings. 22 | - **Extensibility** : The behavior should be highly customizable with `g:operator#surround#blocks` and text objects like [vim-textobj-multiblock](https://github.com/osyo-manga/vim-textobj-multiblock), [vim-textobj-between](https://github.com/thinca/vim-textobj-between) or [vim-textobj-anyblock](https://github.com/rhysd/vim-textobj-anyblock). 23 | - **Well-tested** 24 | 25 | 26 | ## Requirements 27 | 28 | This plugin uses [vim-operator-user](https://github.com/kana/vim-operator-user). 29 | Please install it in advance. 30 | 31 | 32 | ## Customize 33 | 34 | Set your favorite blocks to `g:operator#surround#blocks`. And control the behavior with some variables. 35 | Please read [doc](doc/operator-surround.txt) for more details. 36 | 37 | 38 | ## Example of `vimrc` 39 | 40 | ```vim 41 | " operator mappings 42 | map sa (operator-surround-append) 43 | map sd (operator-surround-delete) 44 | map sr (operator-surround-replace) 45 | 46 | 47 | " delete or replace most inner surround 48 | 49 | " if you use vim-textobj-multiblock 50 | nmap sdd (operator-surround-delete)(textobj-multiblock-a) 51 | nmap srr (operator-surround-replace)(textobj-multiblock-a) 52 | 53 | " if you use vim-textobj-anyblock 54 | nmap sdd (operator-surround-delete)(textobj-anyblock-a) 55 | nmap srr (operator-surround-replace)(textobj-anyblock-a) 56 | 57 | " if you use vim-textobj-between 58 | nmap sdb (operator-surround-delete)(textobj-between-a) 59 | nmap srb (operator-surround-replace)(textobj-between-a) 60 | ``` 61 | 62 | ## License 63 | 64 | vim-operator-surround is distributed under MIT license. 65 | 66 | Copyright (c) 2013-2017 rhysd 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining 69 | a copy of this software and associated documentation files (the 70 | "Software"), to deal in the Software without restriction, including 71 | without limitation the rights to use, copy, modify, merge, publish, 72 | distribute, sublicense, and/or sell copies of the Software, and to 73 | permit persons to whom the Software is furnished to do so, subject to 74 | the following conditions: 75 | The above copyright notice and this permission notice shall be 76 | included in all copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 81 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 82 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 83 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 84 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 85 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | task :ci => [:dump, :test] 2 | 3 | task :dump do 4 | sh 'vim --version' 5 | end 6 | 7 | task :test do 8 | sh 'bundle exec vim-flavor test' 9 | end 10 | -------------------------------------------------------------------------------- /VimFlavor: -------------------------------------------------------------------------------- 1 | flavor 'rhysd/vim-vspec-matchers' 2 | flavor 'kana/vim-operator-user' 3 | -------------------------------------------------------------------------------- /autoload/operator/surround.vim: -------------------------------------------------------------------------------- 1 | let s:save_cpo = &cpo 2 | set cpo&vim 3 | 4 | " customization {{{ 5 | function! s:getg(name, default) abort 6 | return get(g:, 'operator#surround#'.a:name, a:default) 7 | endfunction 8 | 9 | let g:operator#surround#blocks = s:getg('blocks', {}) 10 | 11 | let g:operator#surround#default_blocks = 12 | \ { 13 | \ '-' : [ 14 | \ { 'block' : ['(', ')'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['(', ')'] }, 15 | \ { 'block' : ['[', ']'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['[', ']'] }, 16 | \ { 'block' : ['{', '}'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['{', '}'] }, 17 | \ { 'block' : ['<', '>'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['<', '>'] }, 18 | \ { 'block' : ['"', '"'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['"'] }, 19 | \ { 'block' : ["'", "'"], 'motionwise' : ['char', 'line', 'block'], 'keys' : ["'"] }, 20 | \ { 'block' : ['`', '`'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['`'] }, 21 | \ { 'block' : ['( ', ' )'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' (', ' )'] }, 22 | \ { 'block' : ['{ ', ' }'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' {', ' }'] }, 23 | \ ], 24 | \ } 25 | lockvar! g:operator#surround#default_blocks 26 | 27 | let g:operator#surround#uses_input_if_no_block = s:getg('uses_input_if_no_block', 1) 28 | let g:operator#surround#recognizes_both_ends_as_surround = s:getg('recognizes_both_ends_as_surround', 1) 29 | let g:operator#surround#ignore_space = s:getg('ignore_space', 1) 30 | " }}} 31 | " input {{{ 32 | function! s:get_block_or_prefix_match_in_filetype(blocks, filetype, input, motion) abort 33 | if !has_key(a:blocks, a:filetype) 34 | return 0 35 | endif 36 | for b in a:blocks[a:filetype] 37 | if index(b.motionwise, a:motion) >= 0 38 | if index(b.keys, a:input) >= 0 39 | " completely matched 40 | return b.block 41 | elseif filter(copy(b.keys), 'v:val =~# "^\\V'.escape(a:input, '"\').'"') != [] 42 | " prefix matching 43 | return 1 44 | endif 45 | endif 46 | endfor 47 | return 0 48 | endfunction 49 | 50 | function! s:get_block_or_prefix_match_blocks(blocks, input, motion) abort 51 | let result = s:get_block_or_prefix_match_in_filetype(a:blocks, &filetype, a:input, a:motion) 52 | if type(result) == type([]) || result 53 | return result 54 | endif 55 | 56 | " '-' has the lowest priority 57 | return s:get_block_or_prefix_match_in_filetype(a:blocks, '-', a:input, a:motion) 58 | endfunction 59 | 60 | function! s:get_block_or_prefix_match(input, motion) abort 61 | if exists('b:operator#surround#blocks') 62 | let ret = s:get_block_or_prefix_match_blocks(b:operator#surround#blocks, a:input, a:motion) 63 | if type(ret) == type([]) || ret 64 | return ret 65 | endif 66 | endif 67 | 68 | let ret = s:get_block_or_prefix_match_blocks(g:operator#surround#blocks, a:input, a:motion) 69 | if type(ret) == type([]) || ret 70 | return ret 71 | endif 72 | 73 | if !s:getg('no_default_blocks', 0) 74 | return s:get_block_or_prefix_match_blocks(g:operator#surround#default_blocks, a:input, a:motion) 75 | endif 76 | 77 | return 0 78 | endfunction 79 | 80 | function! s:get_block_from_input(motion) abort 81 | echon 'block : ' 82 | let input = '' 83 | while 1 84 | let char = getchar() 85 | let char = type(char) == type(0) ? nr2char(char) : char 86 | 87 | " cancel when or is input 88 | if char ==# "\" || char ==# "\" 89 | echo 'canceled.' 90 | return 0 91 | endif 92 | 93 | let input .= char 94 | let result = s:get_block_or_prefix_match(input, a:motion) 95 | if type(result) == type([]) 96 | return result 97 | elseif ! result 98 | if g:operator#surround#uses_input_if_no_block 99 | return [input, input] 100 | else 101 | call s:echomsg(input . ' is not defined. Please check g:operator#surround#blocks.', 'ErrorMsg') 102 | return 0 103 | endif 104 | endif 105 | unlet result 106 | endwhile 107 | endfunction 108 | " }}} 109 | " helpers {{{ 110 | function! s:is_empty_region(begin, end) abort 111 | return a:begin[1] == a:end[1] && a:end[2] < a:begin[2] 112 | endfunction 113 | 114 | function! s:normal(cmd) abort 115 | execute 'keepjumps' 'silent' 'normal!' a:cmd 116 | endfunction 117 | 118 | function! s:echomsg(message, ...) abort 119 | if a:0 == 1 | execute 'echohl' a:1 | endif 120 | echomsg type(a:message) == type('') ? a:message : string(a:message) 121 | if a:0 == 1 | echohl None | endif 122 | endfunction 123 | 124 | function! s:get_paste_command(visual, region, motion_end_last_col) abort 125 | let [motion_end_line, motion_end_col] = a:region[1] 126 | let start_line = a:region[0][0] 127 | 128 | if a:visual ==# 'v' 129 | return ((a:motion_end_last_col == motion_end_col) 130 | \ || (line('$') == motion_end_line 131 | \ && len(getline('$')) <= motion_end_col)) 132 | \ ? 'p' : 'P' 133 | elseif a:visual ==# 'V' 134 | if start_line == 1 && motion_end_line == line('$') 135 | " NOTE: 136 | " p and P can't insert linewise object in this case 137 | " because 1 line remains definitely and the line remains 138 | " after pasting. 139 | return 'p`[k"_ddggVG"gy' 140 | endif 141 | return line('$') == motion_end_line ? 'p' : 'P' 142 | else 143 | return 'P' 144 | endif 145 | endfunction 146 | 147 | function! operator#surround#from_keymap() abort 148 | let s:state.from_keymap = 1 149 | endfunction 150 | 151 | " check whether the target region should be extended to each end of lines or not. 152 | function! s:is_extended_blockwise_visual() abort 153 | if getpos("'[")[0:2] != getpos("'<")[0:2] || getpos("']")[0:2] != getpos("'>")[0:2] 154 | return 0 155 | endif 156 | normal! gv 157 | let is_extended = winsaveview().curswant == 1/0 158 | execute "normal! \" 159 | return is_extended 160 | endfunction 161 | 162 | " }}} 163 | 164 | " TODO 165 | " - escape string when the surround is "" or '' 166 | " - add an option to escape for g:operator#surround#blocks 167 | 168 | let s:state = { 'from_keymap' : 0, 'block' : '' } 169 | 170 | " append {{{ 171 | 172 | " Check it should skip white spaces. 173 | " If srounded object consists of white spaces only, 174 | " skipping white spaces doesn't work. 175 | function! s:should_skip_spaces() abort 176 | let sel_save = &l:selection 177 | let [save_unnamed_reg, save_unnamed_regtype] = [getreg('"'), getregtype('"')] 178 | let [save_g_reg, save_g_regtype] = [getreg('g'), getregtype('g')] 179 | try 180 | let &l:selection = 'inclusive' 181 | 182 | " Update `> and `< 183 | call s:normal("`[v`]\"gy") 184 | 185 | return g:operator#surround#ignore_space && 186 | \ getreg('g') !~# '^[[:space:]\n]*$' 187 | finally 188 | call setreg('g', save_g_reg, save_g_regtype) 189 | call setreg('"', save_unnamed_reg, save_unnamed_regtype) 190 | let &l:selection = sel_save 191 | endtry 192 | endfunction 193 | function! s:surround_characters(block_begin, block_end) abort 194 | let should_skip_spaces = s:should_skip_spaces() 195 | " insert block to the region 196 | call s:normal('`>') 197 | if should_skip_spaces 198 | call search('\S', 'bcW') 199 | endif 200 | call s:normal(printf("a%s\", a:block_end)) 201 | call s:normal('`<') 202 | if should_skip_spaces 203 | call search('\S', 'cW') 204 | endif 205 | call s:normal(printf("i%s\", a:block_begin)) 206 | endfunction 207 | 208 | function! s:surround_lines(block_begin, block_end) abort 209 | " insert block to the head and tail of lines 210 | call s:normal( printf("%dgg$a%s\%dgg0i%s\", 211 | \ getpos("']")[1], 212 | \ a:block_end, 213 | \ getpos("'[")[1], 214 | \ a:block_begin) 215 | \ ) 216 | endfunction 217 | 218 | function! s:surround_blocks(block_begin, block_end) abort 219 | let [_, start_line, start_col, _] = getpos("'[") 220 | let [_, last_line, end_col, _] = getpos("']") 221 | let is_extended = s:is_extended_blockwise_visual() 222 | for line in range(start_line, last_line) 223 | if getline(line) =~# '^\s*$' 224 | continue 225 | endif 226 | " insert block to the one line in the block region 227 | call s:normal(printf("%dgg%d|a%s\%d|i%s\", 228 | \ line, 229 | \ is_extended ? col([line, '$']) : end_col, 230 | \ a:block_end, 231 | \ start_col, 232 | \ a:block_begin) 233 | \ ) 234 | endfor 235 | endfunction 236 | 237 | function! s:append_block(block_pair, motion) abort 238 | let pos_save = getpos('.') 239 | let autoindent_save = &autoindent 240 | let cindent_save = &cindent 241 | let smartindent_save = &smartindent 242 | let selection_save = &selection 243 | set noautoindent 244 | set nocindent 245 | set nosmartindent 246 | set selection=inclusive 247 | 248 | try 249 | if a:motion ==# 'char' 250 | call s:surround_characters(a:block_pair[0], a:block_pair[1]) 251 | elseif a:motion ==# 'line' 252 | call s:surround_lines(a:block_pair[0], a:block_pair[1]) 253 | elseif a:motion ==# 'block' 254 | call s:surround_blocks(a:block_pair[0], a:block_pair[1]) 255 | else 256 | " never reached here 257 | throw 'Invalid motion: ' . a:motion 258 | endif 259 | finally 260 | call setpos('.', pos_save) 261 | let &autoindent = autoindent_save 262 | let &cindent = cindent_save 263 | let &smartindent = smartindent_save 264 | let &selection = selection_save 265 | endtry 266 | endfunction 267 | 268 | function! operator#surround#append(motion) abort 269 | if s:is_empty_region(getpos("'["), getpos("']")) 270 | return 271 | endif 272 | 273 | let result = s:state.from_keymap ? s:get_block_from_input(a:motion) : s:state.block 274 | if type(result) == type(0) && ! result 275 | return 276 | endif 277 | let block = result 278 | 279 | call s:append_block(block, a:motion) 280 | 281 | let s:state.from_keymap = 0 282 | let s:state.block = block 283 | endfunction 284 | " }}} 285 | 286 | " delete {{{ 287 | function! s:get_surround_in_with_filetype(filetype, blocks, region) abort 288 | let space_skipper = g:operator#surround#ignore_space 289 | \ ? '\[[:space:]\n]\*' 290 | \ : '\n\*' 291 | 292 | for b in a:blocks[a:filetype] 293 | " if the block surrounds the object 294 | if match(a:region, printf('^\V%s%s\.\*%s%s\$', space_skipper, b.block[0], b.block[1], space_skipper)) >= 0 295 | return b.block 296 | endif 297 | endfor 298 | return [] 299 | endfunction 300 | 301 | function! s:get_surround_from_blocks_in(blocks, region) abort 302 | if has_key(a:blocks, &filetype) 303 | let result = s:get_surround_in_with_filetype(&filetype, a:blocks, a:region) 304 | if result != [] | return result | endif 305 | endif 306 | 307 | " '-' has the lowest priority 308 | if has_key(a:blocks, '-') 309 | return s:get_surround_in_with_filetype('-', a:blocks, a:region) 310 | else 311 | return [] 312 | endif 313 | endfunction 314 | 315 | function! s:get_surround_in(region) abort 316 | if exists('b:operator#surround#blocks') 317 | let ret = s:get_surround_from_blocks_in(b:operator#surround#blocks, a:region) 318 | if ret != [] | return ret | endif 319 | endif 320 | 321 | let ret = s:get_surround_from_blocks_in(g:operator#surround#blocks, a:region) 322 | if ret != [] | return ret | endif 323 | 324 | if !s:getg('no_default_blocks', 0) 325 | return s:get_surround_from_blocks_in(g:operator#surround#default_blocks, a:region) 326 | endif 327 | 328 | return [] 329 | endfunction 330 | 331 | function! s:get_same_str_surround(region) abort 332 | if g:operator#surround#ignore_space 333 | let region = matchstr(a:region, '^[[:space:]\n]*\zs.*\ze[[:space:]\n]*$') 334 | else 335 | let region = matchstr(a:region, '^\n*\zs.*\ze\n*$') 336 | endif 337 | 338 | let len = strlen(region) 339 | 340 | let [s, e] = [0, len - 1] 341 | 342 | while region[s] ==# region[e] && s < len && e >= 0 343 | let s += 1 344 | let e -= 1 345 | endwhile 346 | 347 | if s == 0 348 | throw 'vim-operator-surround: block is not found' 349 | endif 350 | 351 | let surround = region[ : s - 1] 352 | return [surround, surround] 353 | endfunction 354 | 355 | function! s:delete_surround(visual) abort 356 | let [save_reg_g, save_regtype_g] = [getreg('g'), getregtype('g')] 357 | let [save_reg_unnamed, save_regtype_unnamed] = [getreg('"'), getregtype('"')] 358 | try 359 | call setreg('g', '', 'v') 360 | call s:normal('`['.a:visual.'`]"gy') 361 | let region = getreg('g') 362 | 363 | let block = s:get_surround_in(region) 364 | if block == [] 365 | if ! g:operator#surround#recognizes_both_ends_as_surround 366 | throw 'vim-operator-surround: block is not found' 367 | endif 368 | 369 | let block = s:get_same_str_surround(region) 370 | endif 371 | 372 | let put_command = s:get_paste_command(a:visual, [getpos("'[")[1:2], getpos("']")[1:2]], len(getline("']"))) 373 | 374 | call s:normal('`['.a:visual.'`]"_d') 375 | 376 | " remove the former block and latter block 377 | let space_skipper = g:operator#surround#ignore_space 378 | \ ? '\[[:space:]\n]\*' 379 | \ : '\n\*' 380 | 381 | let after = substitute(region, '^\V'. space_skipper . '\zs' . escape(block[0], '\') , '', '') 382 | let after = substitute(after, '\V' . escape(block[1], '\') . '\ze' . space_skipper . '\$', '', '') 383 | 384 | call setreg('g', after, a:visual) 385 | call s:normal('"g'.put_command) 386 | catch /^vim-operator-surround: / 387 | call s:echomsg('no block matches to the region', 'ErrorMsg') 388 | finally 389 | call setreg('g', save_reg_g, save_regtype_g) 390 | call setreg('"', save_reg_unnamed, save_regtype_unnamed) 391 | endtry 392 | endfunction 393 | 394 | function! s:delete_surrounds_in_block() abort 395 | let [_, start_line, start_col, _] = getpos("'[") 396 | let [_, last_line, last_col, _] = getpos("']") 397 | let [save_reg_g, save_regtype_g] = [getreg('g'), getregtype('g')] 398 | let [save_reg_unnamed, save_regtype_unnamed] = [getreg('"'), getregtype('"')] 399 | let is_extended = s:is_extended_blockwise_visual() 400 | try 401 | for line in range(start_line, last_line) 402 | if getline(line) =~# '^\s*$' 403 | continue 404 | endif 405 | " yank to set '[ and '] 406 | call s:normal(line.'gg') 407 | let end_of_line_col = last_col > col('$')-1 || is_extended ? col('$')-1 : last_col 408 | call s:normal(printf('%d|v%d|"gy', start_col, end_of_line_col)) 409 | call s:delete_surround('v') 410 | endfor 411 | 412 | " leave whole region as a history of buffer changes 413 | call s:normal(printf("%dgg%d|\`]%s\"gy", start_line, start_col, is_extended ? '$' : '')) 414 | finally 415 | call setreg('g', save_reg_g, save_regtype_g) 416 | call setreg('"', save_reg_unnamed, save_regtype_unnamed) 417 | endtry 418 | endfunction 419 | 420 | function! operator#surround#delete(motion) abort 421 | if s:is_empty_region(getpos("'["), getpos("']")) 422 | return 423 | endif 424 | 425 | let pos = getpos('.') 426 | let selection = &selection 427 | set selection=inclusive 428 | try 429 | if a:motion ==# 'char' 430 | call s:delete_surround('v') 431 | elseif a:motion ==# 'line' 432 | call s:delete_surround('V') 433 | elseif a:motion ==# 'block' 434 | call s:delete_surrounds_in_block() 435 | else 436 | " never reached here 437 | throw 'Invalid motion: ' . a:motion 438 | endif 439 | finally 440 | call setpos('.', pos) 441 | let &selection = selection 442 | endtry 443 | endfunction 444 | " }}} 445 | 446 | " replace {{{ 447 | function! operator#surround#replace(motion) abort 448 | " get input at first because of undo history 449 | let result = s:state.from_keymap ? s:get_block_from_input(a:motion) : s:state.block 450 | if type(result) == type(0) && ! result 451 | return 452 | endif 453 | let block = result 454 | 455 | call operator#surround#delete(a:motion) 456 | call s:append_block(block, a:motion) 457 | 458 | let s:state.from_keymap = 0 459 | let s:state.block = block 460 | endfunction 461 | " }}} 462 | 463 | let &cpo = s:save_cpo 464 | unlet s:save_cpo 465 | -------------------------------------------------------------------------------- /doc/operator-surround.txt: -------------------------------------------------------------------------------- 1 | *operator-surround.txt* operator mappings to deal with surrounds 2 | 3 | Author : rhysd 4 | 5 | CONTENTS *operator-surround-contents* 6 | 7 | Introduction |operator-surround-introduction| 8 | Usage |operator-surround-usage| 9 | Policy |operator-surround-policy| 10 | Install |operator-surround-install| 11 | Dependency |operator-surround-dependency| 12 | Mappings |operator-surround-mappings| 13 | Variables |operator-surround-variables| 14 | Setup Example |operator-surround-setup-example| 15 | |vim-surround| Compatible Setting |operator-surround-vim-surround-compatible| 16 | Repository Page |operator-surround-repository-page| 17 | License |operator-surround-license| 18 | 19 | 20 | ============================================================================== 21 | INTRODUCTION *operator-surround-introduction* 22 | 23 | *operator-surround* provides Vim operator mappings to deal with surrounds like 24 | () , '', "" and so on. In addition, when both ends of the text-object are the 25 | same characters, |operator-surround| recognizes them as a surround (this 26 | behavior is customizable with some variables). 27 | Definitions of blocks are very customizable with |g:operator#surround#blocks|. 28 | 29 | 30 | ============================================================================== 31 | USAGE *operator-surround-usage* 32 | 33 | First, set |operator-surround-mappings| to your favorite mappings. These 34 | mappings append/delete/replace the |text-objects| you specified. 35 | 36 | To append surrounds, use |(operator-surround-append)|. For example, if 37 | you map it to |sa|, |saiw(| surrounds current inner word with (). |iw| is a 38 | text object for inner words. 39 | > 40 | hoge -> (hoge) 41 | < 42 | To delete surrounds, use |(operator-surround-delete)|. For example, if 43 | you map it to |sd|, |sdiW| deletes a surround which is included in a word. 44 | > 45 | (hoge) -> hoge 46 | < 47 | To replace surrounds, use |(operator-surround-replace)|. For example, 48 | if you map it to |sr|, |sriW'| replaces a surround which is included in a word 49 | with ''. 50 | > 51 | "hoge" -> 'hoge' 52 | < 53 | Default blocks are (), [], {}, <>, "", '', ``, ( ) and { }. 54 | 55 | 56 | ============================================================================== 57 | POLICY (or why I don't use |vim-surround|) *operator-surround-policy* 58 | 59 | - Simplicity 60 | 61 | All should be done with operator mappings. 62 | 63 | - Extensibility 64 | 65 | The behavior should be highly customizable with |g:operator#surround#blocks| 66 | and text objects like vim-textobj-multiblock, vim-textobj-between or 67 | vim-textobj-anyblock. 68 | 69 | - Well-tested 70 | 71 | c.f. 72 | https://github.com/osyo-manga/vim-textobj-multiblock 73 | https://github.com/thinca/vim-textobj-between 74 | https://github.com/rhysd/vim-textobj-anyblock 75 | 76 | 77 | ============================================================================== 78 | INSTALL *operator-surround-install* 79 | 80 | Using Vim plugin package manager is recommended. I use |neobundle|, and 81 | |vundle| seems the most famous. 82 | If you want to install manually, it is not recommended, copy files and 83 | directories in autoload, doc and plugin directories to your vim config 84 | directory. Vim config directory is usually $HOME/vimfiles on Windows or 85 | ~/.vim in other operating systems. 86 | 87 | 88 | ============================================================================== 89 | DEPENDENCY *operator-surround-dependency* 90 | 91 | |operator-user| (required) 92 | https://github.com/kana/vim-operator-user 93 | 94 | 95 | ============================================================================== 96 | MAPPINGS *operator-surround-mappings* 97 | 98 | These mappings are for |mapmode-x| and |mapmode-n|. 99 | 100 | (operator-surround-append) *(operator-surround-append)* 101 | 102 | Operator mapping to surround the text-object. After you specify text 103 | object, you must input some key sequence. If the sequence is defined in 104 | |g:operator#surround#blocks|, the block would be appended. Otherwise, the 105 | input would be appended to the both ends of the text-object. 106 | 107 | (operator-surround-delete) *(operator-surround-delete)* 108 | 109 | Operator mapping to delete the surround in the text-object. If the 110 | text-object is a block defined in |g:operator#surround#blocks| like 111 | '(hoge)' or if the text object's both ends are the same sequence like 112 | '!?hoge!?', the surrounds would be deleted. For example, '(' and ')' for 113 | '(hoge)', '!?' and '!?' for '!?hoge!?'. 114 | 115 | (operator-surround-replace) *(operator-surround-replace)* 116 | 117 | Operator mapping to replace surround with other surround in the 118 | text-object. This mapping's process is the same sequence as 'first, do 119 | (operator-surround-delete) and second, do 120 | (operator-surround-append)'. 121 | 122 | 123 | ============================================================================== 124 | VARIABLES *operator-surround-variables* 125 | 126 | g:operator#surround#blocks *g:operator#surround#blocks* 127 | *b:operator#surround#blocks* 128 | 129 | Block definitions. This is |Dictionary| of |List| which elements represent 130 | a block. This value has higher priority than default blocks so you can 131 | define your own blocks. 132 | 133 | For example, below is default definitions: 134 | > 135 | \ { 136 | \ '-' : [ 137 | \ { 'block' : ['(', ')'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['(', ')'] }, 138 | \ { 'block' : ['[', ']'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['[', ']'] }, 139 | \ { 'block' : ['{', '}'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['{', '}'] }, 140 | \ { 'block' : ['<', '>'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['<', '>'] }, 141 | \ { 'block' : ['"', '"'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['"'] }, 142 | \ { 'block' : ["'", "'"], 'motionwise' : ['char', 'line', 'block'], 'keys' : ["'"] }, 143 | \ { 'block' : ['`', '`'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['`'] }, 144 | \ { 'block' : ['( ', ' )'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' (', ' )'] }, 145 | \ { 'block' : ['{ ', ' }'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' {', ' }'] }, 146 | \ ], 147 | \ } 148 | < 149 | The keys of |g:operator#surround#blocks| ('-' in above example) mean 150 | filetypes. If you specify 'ruby', it means its blocks is available only 151 | in Ruby sources. '-' is a special key and it means its blocks are 152 | available in any filetypes. 153 | The values of |g:operator#surround#blocks| represent block definitions. 154 | They are |Dictionary| and must have following keys; block, motionwise and 155 | keys. 156 | 157 | - block 158 | 'block' means a surround which is consisted with the pair of the 159 | begin of it and the end of it. 160 | 161 | - motionwise 162 | Key motionwise represents that which kind of a text object, the block is 163 | available. 'char' means characterwise, 'line' means linewise and 164 | 'block' means blockwise. In above example, ['(', ')'] block is 165 | used in all kinds of objects. 166 | 167 | - keys 168 | 'keys' are input you specify in |(operator-surround-append)| 169 | or |(operator-surround-replace)|. If your input is equivalent to 170 | any of these values, the block is selected. Length of input can be 171 | more than 1. In above example, if you input '(', ['( ', ' )'] 172 | block would be selected. 173 | 174 | You can also define buffer local version |b:operator#surround#blocks|. 175 | It is used only in the buffer and has higher priority than 176 | |g:operator#surround#blocks|. 177 | 178 | g:operator#surround#default_blocks *g:operator#surround#default_blocks* 179 | 180 | The default blocks. This variable is readonly. If you will modify this 181 | variable, an exception will be thrown. The value is shown in 182 | |g:operator#surround#blocks|. 183 | 184 | The default block definitions are: 185 | > 186 | \ { 187 | \ '-' : [ 188 | \ { 'block' : ['(', ')'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['(', ')'] }, 189 | \ { 'block' : ['[', ']'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['[', ']'] }, 190 | \ { 'block' : ['{', '}'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['{', '}'] }, 191 | \ { 'block' : ['<', '>'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['<', '>'] }, 192 | \ { 'block' : ['"', '"'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['"'] }, 193 | \ { 'block' : ["'", "'"], 'motionwise' : ['char', 'line', 'block'], 'keys' : ["'"] }, 194 | \ { 'block' : ['`', '`'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['`'] }, 195 | \ { 'block' : ['( ', ' )'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' (', ' )'] }, 196 | \ { 'block' : ['{ ', ' }'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' {', ' }'] }, 197 | \ ], 198 | \ } 199 | < 200 | *g:operator#surround#uses_input_if_no_block* 201 | g:operator#surround#uses_input_if_no_block 202 | 203 | If the value is non-zero, use not only block definitions but also input 204 | sequence when appending and replacing surrounds. For example, if you 205 | input ! and ! is not any keys in block definitions, the text-object would 206 | be surrounded with !. 207 | The default value is 1. 208 | 209 | *g:operator#surround#recognizes_both_ends_as_surround* 210 | g:operator#surround#recognizes_both_ends_as_surround 211 | 212 | If the value is non-zero, if the both ends of the text-object are the same 213 | sequence, the sequence is recognized as a surround. For example, if 214 | '!?hoge!?' is a text-object, the both ends of it are the same sequence 215 | '!?'. Then it is recognized as a surround. 216 | The default value is 1. 217 | 218 | g:operator#surround#no_default_blocks *g:operator#surround#no_default_blocks* 219 | 220 | If the value is non-zero, default block definitions defined in 221 | |g:operator#surround#default_blocks| are not used. 222 | 223 | The default value is 0. 224 | 225 | g:operator#surround#ignore_space *g:operator#surround#ignore_space* 226 | 227 | If the value is non-zero, surroundings ignores white spaces when they are 228 | added with (operator-surround-append) or 229 | (operator-surround-replace). 230 | The default value is 1. 231 | 232 | 233 | ============================================================================== 234 | SETUP EXAMPLE *operator-surround-setup-example* 235 | 236 | Below is an example of setting for .vimrc. Note that these mappings overwrite 237 | |s| mapping. 238 | > 239 | " add ```...``` surround when filetype is markdown 240 | let g:operator#surround#blocks = { 241 | \ 'markdown' : [ 242 | \ { 'block' : ["```\n", "\n```"], 'motionwise' : ['line'], 'keys' : ['`'] }, 243 | \ ] } 244 | 245 | " operator mappings 246 | map sa (operator-surround-append) 247 | map sd (operator-surround-delete) 248 | map sr (operator-surround-replace) 249 | 250 | 251 | " delete or replace most inner surround using vim-textobj-multiblock or 252 | " vim-textobj-anyblock 253 | 254 | " if you use vim-textobj-multiblock 255 | nmap sdd (operator-surround-delete)(textobj-multiblock-a) 256 | nmap srr (operator-surround-replace)(textobj-multiblock-a) 257 | " if you use vim-textobj-anyblock 258 | nmap sdd (operator-surround-delete)(textobj-anyblock-a) 259 | nmap srr (operator-surround-replace)(textobj-anyblock-a) 260 | 261 | " delete or replace the object which is put between the same character 262 | " using vim-textobj-between 263 | 264 | " if you use vim-textobj-between 265 | nmap sdb (operator-surround-delete)(textobj-between-a) 266 | nmap srb (operator-surround-replace)(textobj-between-a) 267 | < 268 | 269 | ------------------------------------------------------------------------------ 270 | |vim-surround| COMPATIBLE SETTING *operator-surround-vim-surround-compatible* 271 | 272 | If you use |operator-surround| as an alternative to |vim-surround|. You may 273 | want to use |operator-surround| with the same mappings as |vim-surround|. 274 | But I don't recommend this setting because the feature of |operator-surround| 275 | is not the one of |vim-surround|. 276 | I think this setting can't take the advantage of |operator-surround|. And note 277 | that these mappings overwrite some Vim's default mappings (for example, y in 278 | visual mode). 279 | > 280 | map ys (operator-surround-append) 281 | map ds (operator-surround-delete) 282 | map cs (operator-surround-replace) 283 | nmap yss V(operator-surround-append) 284 | nmap dss V(operator-surround-delete) 285 | nmap css V(operator-surround-replace) 286 | < 287 | 288 | ============================================================================== 289 | REPOSITORY PAGE *operator-surround-repository-page* 290 | 291 | The latest version of |operator-surround| is available at 292 | https://github.com/rhysd/vim-operator-surround 293 | 294 | Contributions (pull requests) are welcome. None of them are too short. 295 | Especially, English check is very helpful because I'm poor at English :( 296 | 297 | 298 | ============================================================================== 299 | LICENSE *operator-surround-license* 300 | 301 | |operator-surround| is distributed under MIT license. 302 | 303 | Copyright (c) 2013 rhysd 304 | 305 | Permission is hereby granted, free of charge, to any person obtaining 306 | a copy of this software and associated documentation files (the 307 | "Software"), to deal in the Software without restriction, including 308 | without limitation the rights to use, copy, modify, merge, publish, 309 | distribute, sublicense, and/or sell copies of the Software, and to 310 | permit persons to whom the Software is furnished to do so, subject to 311 | the following conditions: 312 | The above copyright notice and this permission notice shall be 313 | included in all copies or substantial portions of the Software. 314 | 315 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 316 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 317 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 318 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 319 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 320 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 321 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 322 | 323 | 324 | ============================================================================== 325 | vim:tw=78:ts=8:ft=help:norl:et:fen:fdl=0: 326 | -------------------------------------------------------------------------------- /plugin/operator/surround.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_operator_surround') 2 | finish 3 | endif 4 | 5 | call operator#user#define('surround-append', 'operator#surround#append', 'call operator#surround#from_keymap()') 6 | call operator#user#define('surround-delete', 'operator#surround#delete') 7 | call operator#user#define('surround-replace', 'operator#surround#replace', 'call operator#surround#from_keymap()') 8 | 9 | nnoremap (operator-surround-repeat) . 10 | 11 | let g:loaded_operator_surround = 1 12 | -------------------------------------------------------------------------------- /t/append_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | command! -nargs=+ -count=1 Line call setline(, ) 10 | 11 | describe '(operator-surround-append)' 12 | before 13 | let g:operator#surround#uses_input_if_no_block = 0 14 | map s (operator-surround-append) 15 | new 16 | end 17 | 18 | after 19 | close! 20 | unmap s 21 | end 22 | 23 | " error handling {{{ 24 | it 'echos an error message when input is invalid if g:operator#surround#uses_input_if_no_block is not specified.' 25 | Line "hoge huga poyo" 26 | normal! gg0w 27 | redir => buffer 28 | silent normal siw& 29 | redir END 30 | Expect buffer =~# "& is not defined. Please check g:operator#surround#blocks." 31 | let buffer = '' 32 | redir => buffer 33 | silent normal viws& 34 | redir END 35 | Expect buffer =~# "& is not defined. Please check g:operator#surround#blocks." 36 | try 37 | Expect 'normal siw&' not to_move_cursor 38 | Expect 'normal viws&' not to_move_cursor 39 | catch 40 | endtry 41 | end 42 | " }}} 43 | 44 | " characterwise {{{ 45 | it 'appends blocks to a characterwise object with an operator mapping.' 46 | Line "hoge huga poyo" 47 | normal! gg0w 48 | normal siw(l 49 | Expect "hoge (huga) poyo" to_be_current_line 50 | normal siw[l 51 | Expect "hoge ([huga]) poyo" to_be_current_line 52 | normal siw{l 53 | Expect "hoge ([{huga}]) poyo" to_be_current_line 54 | normal siw}]) poyo" to_be_current_line 56 | normal siw"l 57 | Expect "hoge ([{<\"huga\">}]) poyo" to_be_current_line 58 | normal siw'l 59 | Expect "hoge ([{<\"'huga'\">}]) poyo" to_be_current_line 60 | normal siw`l 61 | Expect "hoge ([{<\"'`huga`'\">}]) poyo" to_be_current_line 62 | normal siw )ll 63 | Expect "hoge ([{<\"'`( huga )`'\">}]) poyo" to_be_current_line 64 | normal siw }ll 65 | Expect "hoge ([{<\"'`( { huga } )`'\">}]) poyo" to_be_current_line 66 | echon ' ' 67 | end 68 | 69 | it 'appends blocks to a characterwise object with visual mode mapping.' 70 | Line "hoge huga poyo" 71 | normal! gg0ww 72 | normal viws(l 73 | Expect "hoge huga (poyo)" to_be_current_line 74 | normal viws[l 75 | Expect "hoge huga ([poyo])" to_be_current_line 76 | normal viws{l 77 | Expect "hoge huga ([{poyo}])" to_be_current_line 78 | normal viws}])" to_be_current_line 80 | normal viws"l 81 | Expect "hoge huga ([{<\"poyo\">}])" to_be_current_line 82 | normal viws'l 83 | Expect "hoge huga ([{<\"'poyo'\">}])" to_be_current_line 84 | normal viws`l 85 | Expect "hoge huga ([{<\"'`poyo`'\">}])" to_be_current_line 86 | echon ' ' 87 | end 88 | 89 | it 'appends blocks to a characterwise object which is head of line.' 90 | Line "hoge huga poyo" 91 | normal! gg0 92 | normal siw(l 93 | Expect "(hoge) huga poyo" to_be_current_line 94 | normal siw[l 95 | Expect "([hoge]) huga poyo" to_be_current_line 96 | normal siw{l 97 | Expect "([{hoge}]) huga poyo" to_be_current_line 98 | normal siw}]) huga poyo" to_be_current_line 100 | normal siw"l 101 | Expect "([{<\"hoge\">}]) huga poyo" to_be_current_line 102 | normal siw'l 103 | Expect "([{<\"'hoge'\">}]) huga poyo" to_be_current_line 104 | normal siw`l 105 | Expect "([{<\"'`hoge`'\">}]) huga poyo" to_be_current_line 106 | echon ' ' 107 | end 108 | 109 | it 'does not ignore spaces both side of the block when g:operator#surround#ignore_space is 0.' 110 | let saved = g:operator#surround#ignore_space 111 | let g:operator#surround#ignore_space = 0 112 | 113 | Line "' foo bar baz '" 114 | normal! gg0ff 115 | normal si'( 116 | Expect "'( foo bar baz )'" to_be_current_line 117 | 118 | let g:operator#surround#ignore_space = saved 119 | end 120 | 121 | it 'ignore spaces both side of the block.' 122 | Line "' foo bar baz '" 123 | normal! gg0ff 124 | normal si'( 125 | Expect "' (foo bar baz) '" to_be_current_line 126 | end 127 | " }}} 128 | 129 | " linewise {{{ 130 | it 'appends blocks to linewise object with an operator mapping.' 131 | 1Line "hoge huga poyo" 132 | 2Line "hoge huga poyo" 133 | normal! gg0 134 | 135 | normal sip( 136 | Expect getline(1).getline(2) =~# '^(.\+)$' 137 | 138 | normal sip{ 139 | Expect getline(1).getline(2) =~# '^{(.\+)}$' 140 | 141 | normal sip[ 142 | Expect getline(1).getline(2) =~# '^\[{(.\+)}]$' 143 | 144 | normal sip< 145 | Expect getline(1).getline(2) =~# '^<\[{(.\+)}]>$' 146 | 147 | normal sip" to_be_current_line 148 | Expect getline(1).getline(2) =~# '^"<\[{(.\+)}]>"$' 149 | 150 | normal sip' 151 | Expect getline(1).getline(2) =~# '^''"<\[{(.\+)}]>"''$' 152 | 153 | normal sip` 154 | Expect getline(1).getline(2) =~# '^`''"<\[{(.\+)}]>"''`$' 155 | 156 | normal sip ) 157 | Expect getline(1).getline(2) =~# '^( `''"<\[{(.\+)}]>"''` )$' 158 | 159 | normal sip } 160 | Expect getline(1).getline(2) =~# '^{ ( `''"<\[{(.\+)}]>"''` ) }$' 161 | end 162 | 163 | it 'appends blocks to linewise object with an visual mode mapping.' 164 | 1Line "hoge huga poyo" 165 | 2Line "hoge huga poyo" 166 | normal! gg0 167 | 168 | normal vips( 169 | Expect getline(1).getline(2) =~# '^(.\+)$' 170 | 171 | normal vips{ 172 | Expect getline(1).getline(2) =~# '^{(.\+)}$' 173 | 174 | normal vips[ 175 | Expect getline(1).getline(2) =~# '^\[{(.\+)}]$' 176 | 177 | normal vips< 178 | Expect getline(1).getline(2) =~# '^<\[{(.\+)}]>$' 179 | 180 | normal vips" to_be_current_line 181 | Expect getline(1).getline(2) =~# '^"<\[{(.\+)}]>"$' 182 | 183 | normal vips' 184 | Expect getline(1).getline(2) =~# '^''"<\[{(.\+)}]>"''$' 185 | 186 | normal vips` 187 | Expect getline(1).getline(2) =~# '^`''"<\[{(.\+)}]>"''`$' 188 | 189 | normal vips ) 190 | Expect getline(1).getline(2) =~# '^( `''"<\[{(.\+)}]>"''` )$' 191 | 192 | normal vips } 193 | Expect getline(1).getline(2) =~# '^{ ( `''"<\[{(.\+)}]>"''` ) }$' 194 | end 195 | " }}} 196 | 197 | " blockwise {{{ 198 | it 'appends blocks to a blockwise object with an visual mode mapping.' 199 | 1Line "hoge huga poyo" 200 | 2Line "hoge huga poyo" 201 | execute 'normal' "gg0w\jt\s(" 202 | Expect getline(1) ==# "hoge (huga) poyo" 203 | Expect getline(2) ==# "hoge (huga) poyo" 204 | 205 | execute 'normal' "gg0w\jt\s{" 206 | Expect getline(1) ==# "hoge {(huga)} poyo" 207 | Expect getline(2) ==# "hoge {(huga)} poyo" 208 | 209 | execute 'normal' "gg0w\jt\s[" 210 | Expect getline(1) ==# "hoge [{(huga)}] poyo" 211 | Expect getline(2) ==# "hoge [{(huga)}] poyo" 212 | 213 | execute 'normal' "gg0w\jt\s\"" 214 | Expect getline(1) ==# "hoge \"[{(huga)}]\" poyo" 215 | Expect getline(2) ==# "hoge \"[{(huga)}]\" poyo" 216 | 217 | execute 'normal' "gg0w\jt\s'" 218 | Expect getline(1) ==# "hoge '\"[{(huga)}]\"' poyo" 219 | Expect getline(2) ==# "hoge '\"[{(huga)}]\"' poyo" 220 | 221 | execute 'normal' "gg0w\jt\s<" 222 | Expect getline(1) ==# "hoge <'\"[{(huga)}]\"'> poyo" 223 | Expect getline(2) ==# "hoge <'\"[{(huga)}]\"'> poyo" 224 | 225 | execute 'normal' "gg0w\jt\s\)" 226 | Expect getline(1) ==# "hoge ( <'\"[{(huga)}]\"'> ) poyo" 227 | Expect getline(2) ==# "hoge ( <'\"[{(huga)}]\"'> ) poyo" 228 | 229 | echon ' ' 230 | end 231 | " }}} 232 | 233 | end 234 | 235 | -------------------------------------------------------------------------------- /t/corner_cases_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | command! -nargs=+ -count=1 Line call setline(, ) 10 | 11 | describe 'auto indentation' 12 | 13 | before 14 | map sa (operator-surround-append) 15 | map sr (operator-surround-replace) 16 | new 17 | set autoindent 18 | set cindent 19 | set smartindent 20 | end 21 | 22 | after 23 | close! 24 | unmap sa 25 | unmap sr 26 | end 27 | 28 | it 'doesn''t make auto indentation when inserting surrounds' 29 | Line ' hoge' 30 | 31 | " at 'h' 32 | normal! gg0w 33 | 34 | normal saiw{ 35 | Expect getline('.') ==# ' {hoge}' 36 | end 37 | 38 | it 'doesn''t make auto indentation when replacing surrounds' 39 | Line ' ahogea' 40 | 41 | " at 'h' 42 | normal! gg0w 43 | 44 | normal sriw{ 45 | Expect getline('.') ==# ' {hoge}' 46 | end 47 | 48 | end 49 | 50 | describe 'backslash in surrounds' 51 | before 52 | let s:saved_blocks = g:operator#surround#blocks 53 | let g:operator#surround#blocks = {} 54 | let g:operator#surround#blocks['-'] = [ 55 | \ {'block' : ['\', '\'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['\']}, 56 | \ {'block' : ['(', ')'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['(', ')']}, 57 | \ ] 58 | new 59 | map sa (operator-surround-append) 60 | map sr (operator-surround-replace) 61 | end 62 | 63 | after 64 | close! 65 | let g:operator#surround#blocks = s:saved_blocks 66 | end 67 | 68 | it 'can be contained in surroundings (#18, #31)' 69 | Line 'hoge' 70 | normal saiw\ 71 | Expect getline('.') ==# '\hoge\' 72 | end 73 | 74 | it 'can be replaced properly as target' 75 | Line '\hoge\' 76 | normal! gg0v$ 77 | normal sr( 78 | Expect getline('.') ==# '(hoge)' 79 | end 80 | end 81 | 82 | describe 'blank lines inside block' 83 | 84 | before 85 | map sa (operator-surround-append) 86 | map sr (operator-surround-replace) 87 | map sd (operator-surround-delete) 88 | new 89 | end 90 | 91 | after 92 | close! 93 | unmap sa 94 | unmap sr 95 | unmap sd 96 | end 97 | 98 | it 'should be ignored when adding surrounds' 99 | 1Line "hoge" 100 | 2Line "" 101 | 3Line " " 102 | 4Line "hoge" 103 | execute 'normal' "gg0\G$sa(" 104 | 105 | Expect getline(1) ==# "(hoge)" 106 | Expect getline(2) ==# "" 107 | Expect getline(3) ==# " " 108 | Expect getline(4) ==# "(hoge)" 109 | end 110 | 111 | it 'should be ignored when replacing surrounds' 112 | 1Line "(hoge)" 113 | 2Line "" 114 | 3Line " " 115 | 4Line "(hoge)" 116 | execute 'normal' "gg0\G$sr'" 117 | 118 | Expect getline(1) ==# "'hoge'" 119 | Expect getline(2) ==# "" 120 | Expect getline(3) ==# " " 121 | Expect getline(4) ==# "'hoge'" 122 | end 123 | 124 | it 'should be ignored when deleting surrounds' 125 | 1Line "'hoge'" 126 | 2Line "" 127 | 3Line " " 128 | 4Line "'hoge'" 129 | execute 'normal' "gg0\G$sd'" 130 | 131 | Expect getline(1) ==# "hoge" 132 | Expect getline(2) ==# "" 133 | Expect getline(3) ==# " " 134 | Expect getline(4) ==# "hoge" 135 | end 136 | 137 | end 138 | -------------------------------------------------------------------------------- /t/default_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | try 10 | call operator#surround#append('char') 11 | catch 12 | endtry 13 | 14 | let g:operator#surround#blocks = { 15 | \ '-' : [ 16 | \ { 'block' : ['a', 'b'], 'motionwise' : [], 'keys' : ["\"] } 17 | \ ], 18 | \ 'test' : [ 19 | \ { 'block' : ['a', 'b'], 'motionwise' : [], 'keys' : ["\"] } 20 | \ ] 21 | \ } 22 | 23 | describe 'Default settings' 24 | it 'provide variables to customize' 25 | Expect 'g:operator#surround#blocks' to_exist 26 | Expect 'g:operator#surround#uses_input_if_no_block' to_exist_and_default_to 1 27 | Expect 'g:operator#surround#recognizes_both_ends_as_surround' to_exist_and_default_to 1 28 | Expect 'g:operator#surround#ignore_space' to_exist_and_default_to 1 29 | 30 | Expect g:operator#surround#default_blocks == 31 | \ { 32 | \ '-' : [ 33 | \ { 'block' : ['(', ')'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['(', ')'] }, 34 | \ { 'block' : ['[', ']'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['[', ']'] }, 35 | \ { 'block' : ['{', '}'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['{', '}'] }, 36 | \ { 'block' : ['<', '>'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['<', '>'] }, 37 | \ { 'block' : ['"', '"'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['"'] }, 38 | \ { 'block' : ["'", "'"], 'motionwise' : ['char', 'line', 'block'], 'keys' : ["'"] }, 39 | \ { 'block' : ['`', '`'], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['`'] }, 40 | \ { 'block' : ['( ', ' )'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' (', ' )'] }, 41 | \ { 'block' : ['{ ', ' }'], 'motionwise' : ['char', 'line', 'block'], 'keys' : [' {', ' }'] }, 42 | \ ], 43 | \ } 44 | end 45 | 46 | it 'provide functions to make operator' 47 | Expect '*operator#surround#append' to_exist 48 | Expect '*operator#surround#replace' to_exist 49 | Expect '*operator#surround#delete' to_exist 50 | end 51 | 52 | it 'provide default mappings' 53 | Expect maparg('(operator-surround-append)') not to_be_empty 54 | Expect maparg('(operator-surround-replace)') not to_be_empty 55 | Expect maparg('(operator-surround-delete)') not to_be_empty 56 | end 57 | 58 | it 'can''t change the default blocks' 59 | Expect 'let g:operator#surround#default_blocks.a = 0' to_throw_exception 60 | end 61 | end 62 | 63 | -------------------------------------------------------------------------------- /t/delete_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | command! -nargs=+ -count=1 Line call setline(, ) 10 | 11 | describe '(operator-surround-delete)' 12 | before 13 | let g:operator#surround#uses_input_if_no_block = 0 14 | map s (operator-surround-delete) 15 | new 16 | end 17 | 18 | after 19 | close! 20 | unmap s 21 | end 22 | 23 | " error handling {{{ 24 | it 'echos an error when no block is found in the object if g:operator#surround#uses_input_if_no_block is not specified' 25 | Line 'hoge huga poyo' 26 | redir => buffer 27 | silent normal gg0wsiw 28 | redir END 29 | Expect buffer =~# 'no block matches to the region' 30 | let buffer = '' 31 | redir => buffer 32 | silent normal gg0wviws 33 | redir END 34 | Expect buffer =~# 'no block matches to the region' 35 | try 36 | Expect 'normal gg0wsiw' not to_move_cursor 37 | Expect 'normal gg0wviws' not to_move_cursor 38 | catch 39 | endtry 40 | end 41 | 42 | " special case 43 | it 'deletes a block in the object at the end of line' 44 | Line 'hoge huga(poyo)' 45 | normal gg03wsa( 46 | Expect 'hoge hugapoyo' to_be_current_line 47 | end 48 | " }}} 49 | 50 | " characterwise {{{ 51 | it 'deletes blocks in a characterwise object with an operator mapping' 52 | Line "hoge \"'[<({huga})>]'\" piyo" 53 | normal! gg0ww 54 | normal siWw 55 | Expect getline('.') ==# "hoge '[<({huga})>]' piyo" 56 | normal siWw 57 | Expect getline('.') ==# "hoge [<({huga})>] piyo" 58 | normal siWw 59 | Expect getline('.') ==# "hoge <({huga})> piyo" 60 | normal siWw 61 | Expect getline('.') ==# "hoge ({huga}) piyo" 62 | normal siWw 63 | Expect getline('.') ==# "hoge {huga} piyo" 64 | normal siWw 65 | Expect getline('.') ==# "hoge huga piyo" 66 | end 67 | " }}} 68 | 69 | " linewise {{{ 70 | it 'deletes blocks in a linewise object with an operator mapping' 71 | 1Line '' 72 | 2Line "\"'[<({huga huga " 73 | 3Line "piyo})>]'\"" 74 | normal 2ggsip 75 | Expect getline(2).getline(3) ==# "'[<({huga huga piyo})>]'" 76 | normal 2ggsip 77 | Expect getline(2).getline(3) ==# "[<({huga huga piyo})>]" 78 | normal 2ggsip 79 | Expect getline(2).getline(3) ==# "<({huga huga piyo})>" 80 | normal 2ggsip 81 | Expect getline(2).getline(3) ==# "({huga huga piyo})" 82 | normal 2ggsip 83 | Expect getline(2).getline(3) ==# "{huga huga piyo}" 84 | normal 2ggsip 85 | Expect getline(2).getline(3) ==# "huga huga piyo" 86 | end 87 | " }}} 88 | 89 | " blockwise {{{ 90 | it 'deletes blocks in a blockwise object with an operator mapping' 91 | 1Line '(hoge)' 92 | 2Line '[huga]' 93 | 3Line '' 94 | 4Line '"hoge"' 95 | 5Line '{huga}' 96 | 6Line "'piyo'" 97 | execute "normal! gg\G$" 98 | normal s 99 | Expect getline(1) ==# 'hoge' 100 | Expect getline(2) ==# 'huga' 101 | Expect getline(3) ==# 'piyo' 102 | Expect getline(4) ==# 'hoge' 103 | Expect getline(5) ==# 'huga' 104 | Expect getline(6) ==# 'piyo' 105 | end 106 | 107 | it 'deletes blocks in a blockwise object when it includes the line which is shorter than the object' 108 | 1Line '(hogeee)' 109 | 2Line '[huga]' 110 | 3Line '' 111 | execute "normal! gg\G$" 112 | normal s 113 | Expect getline(1) ==# 'hogeee' 114 | Expect getline(2) ==# 'huga' 115 | Expect getline(3) ==# 'piyopoyo' 116 | end 117 | " }}} 118 | 119 | " for issues {{{ 120 | it 'does not occur an error when :set selection=exclusive (Issue #1)' 121 | let selection = &selection 122 | set selection=exclusive 123 | 124 | Line "hoge \"'[<({huga})>]'\" piyo" 125 | normal! gg0ww 126 | normal siWw 127 | Expect getline('.') ==# "hoge '[<({huga})>]' piyo" 128 | normal siWw 129 | Expect getline('.') ==# "hoge [<({huga})>] piyo" 130 | normal siWw 131 | Expect getline('.') ==# "hoge <({huga})> piyo" 132 | normal siWw 133 | Expect getline('.') ==# "hoge ({huga}) piyo" 134 | normal siWw 135 | Expect getline('.') ==# "hoge {huga} piyo" 136 | normal siWw 137 | Expect getline('.') ==# "hoge huga piyo" 138 | 139 | let &selection = selection 140 | end 141 | 142 | it 'does not pollute unnamed registers (Pull request #9)' 143 | call setreg('"', 'vim kawaii', 'v') 144 | Line "hoge \"'[<({huga})>]'\" piyo" 145 | normal! gg0ww 146 | normal siWw 147 | 148 | Expect getreg('"') ==# 'vim kawaii' 149 | Expect getregtype('"') ==# 'v' 150 | end 151 | 152 | " }}} 153 | 154 | end 155 | -------------------------------------------------------------------------------- /t/filetype_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | set rtp+=/Users/rhayasd/Github/vim-operator-surround 4 | 5 | call vspec#matchers#load() 6 | 7 | set rtp+=~/.vim/bundle/vim-operator-user 8 | runtime plugin/operator/surround.vim 9 | 10 | command! -nargs=+ -count=1 Line call setline(, ) 11 | 12 | describe 'filetype specific settings' 13 | 14 | before 15 | map sa (operator-surround-append) 16 | map sd (operator-surround-delete) 17 | new 18 | set ft=ruby 19 | let g:operator#surround#blocks = {} 20 | let g:operator#surround#blocks['ruby'] = [ 21 | \ { 'block' : ["do\n", "\nend"], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['do'] }, 22 | \ { 'block' : ["{|i| ", " }"], 'motionwise' : ['char', 'line', 'block'], 'keys' : ['{'] }, 23 | \ ] 24 | end 25 | 26 | after 27 | close! 28 | unmap sa 29 | unmap sd 30 | end 31 | 32 | it 'are reflected on appending and deleting surrounds' 33 | Line "hoge" 34 | normal gg0saiwdo 35 | Expect getline(1) ==# 'do' 36 | Expect getline(2) ==# 'hoge' 37 | Expect getline(3) ==# 'end' 38 | normal gg0vG$sd 39 | Expect getline(1) ==# 'hoge' 40 | end 41 | 42 | it 'has higher priority than ''-''' 43 | Line "hoge" 44 | normal gg0saiw{ 45 | Expect getline('.') ==# '{|i| hoge }' 46 | normal gg0v$sd 47 | Expect getline(1) ==# 'hoge' 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /t/input_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | describe 'input' 10 | before 11 | map s (operator-surround-append) 12 | new 13 | end 14 | 15 | after 16 | close! 17 | unmap s 18 | end 19 | 20 | it 'is canceled with ' 21 | call setline(1, 'aaa') 22 | let prev_line = getline('.') 23 | let prev_linum = line('$') 24 | execute 'normal' "siw\" 25 | Expect prev_line == 'aaa' 26 | Expect prev_linum == line('$') 27 | end 28 | 29 | it 'is canceled with ' 30 | call setline(1, 'aaa') 31 | let prev_line = getline('.') 32 | let prev_linum = line('$') 33 | execute 'normal' "siw\" 34 | Expect prev_line == 'aaa' 35 | Expect prev_linum == line('$') 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /t/recognize_both_end_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | command! -nargs=+ -count=1 Line call setline(, ) 10 | 11 | describe 'g:operator#surround#recognizes_both_end_as_surround' 12 | before 13 | let g:operator#surround#recognizes_both_end_as_surround = 1 14 | map sd (operator-surround-delete) 15 | map sr (operator-surround-replace) 16 | new 17 | end 18 | 19 | after 20 | close! 21 | unmap sd 22 | unmap sr 23 | end 24 | 25 | it 'deletes both end when they are the same character if they are not block' 26 | Line "-hoge-" 27 | normal sdiW 28 | Expect getline('.') ==# "hoge" 29 | end 30 | 31 | it 'replaces both end when they are the same character even if they are not block' 32 | Line "~hoge~" 33 | normal sriW, 34 | Expect getline('.') ==# ",hoge," 35 | end 36 | 37 | it 'deletes both end when they are the same multi characters if they are not block' 38 | Line "**hoge**" 39 | normal sdiW 40 | Expect getline('.') ==# "hoge" 41 | end 42 | 43 | it 'replaces both end when they are the same multi characters if they are not block' 44 | Line "**hoge**" 45 | normal sriW! 46 | Expect getline('.') ==# "!hoge!" 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /t/repetition_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | command! -nargs=+ -count=1 Line call setline(, ) 10 | 11 | describe '.' 12 | before 13 | map sa (operator-surround-append) 14 | map sd (operator-surround-delete) 15 | map sr (operator-surround-replace) 16 | new 17 | end 18 | 19 | after 20 | close! 21 | unmap sa 22 | unmap sd 23 | unmap sr 24 | end 25 | 26 | it 'repeats appending surrounds to the same object' 27 | SKIP "because it doesn't work properly only in vspec environment" 28 | Line "hoge" 29 | normal saiw(l 30 | normal .(l 31 | Expect getline('.') ==# "((hoge))" 32 | normal .{l 33 | Expect getline('.') ==# "(({hoge}))" 34 | normal .' 35 | Expect getline('.') ==# "(({'hoge'}))" 36 | end 37 | 38 | it 'repeats deleting surrounds to the same object' 39 | SKIP "because it doesn't work properly only in vspec environment" 40 | Line "((((hoge))))" 41 | normal fhsda( 42 | normal . 43 | Expect getline('.') ==# "((hoge))" 44 | normal . 45 | Expect getline('.') ==# "(hoge)" 46 | normal . 47 | Expect getline('.') ==# "hoge" 48 | end 49 | 50 | it 'repeats replacing surrounds to the same object' 51 | SKIP "because it doesn't work properly only in vspec environment" 52 | Line "(hoge)" 53 | normal sriW{ 54 | normal! ggfh 55 | normal .( 56 | Expect getline('.') ==# "(hoge)" 57 | normal! ggfh 58 | normal ." 59 | Expect getline('.') ==# '"hoge"' 60 | normal .{ 61 | Expect getline('.') ==# "{hoge}" 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /t/replace_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | command! -nargs=+ -count=1 Line call setline(, ) 10 | 11 | describe '(operator-surround-replace)' 12 | before 13 | map s (operator-surround-replace) 14 | new 15 | end 16 | 17 | after 18 | close! 19 | unmap s 20 | end 21 | 22 | " characterwise {{{ 23 | it 'replace a characterwise object in operator pending mode' 24 | 1Line '"hoge"' 25 | normal sa"{ 26 | Expect getline('.') ==# '{hoge}' 27 | normal sa{' 28 | Expect getline('.') ==# "'hoge'" 29 | normal sa'[ 30 | Expect getline('.') ==# '[hoge]' 31 | normal sa[ ) 32 | Expect getline('.') ==# '( hoge )' 33 | end 34 | 35 | it 'replace a characterwise object in visual mode' 36 | 1Line '"hoge"' 37 | normal va"s{ 38 | Expect getline('.') ==# '{hoge}' 39 | normal va{s' 40 | Expect getline('.') ==# "'hoge'" 41 | normal va's[ 42 | Expect getline('.') ==# '[hoge]' 43 | normal va[s ) 44 | Expect getline('.') ==# '( hoge )' 45 | end 46 | 47 | it 'skips whitespaces (fixes issue #13)' 48 | Line "hoge('aaa' , 'bbb')" 49 | normal! gg0fb 50 | normal sa'" 51 | Expect getline('.') ==# "hoge('aaa' , \"bbb\")" 52 | normal Fa 53 | normal sa'" 54 | Expect getline('.') ==# "hoge(\"aaa\" , \"bbb\")" 55 | end 56 | 57 | it 'does not skip whitespaces if g:operator#surround#ignore_space is 0' 58 | let saved = g:operator#surround#ignore_space 59 | let g:operator#surround#ignore_space = 0 60 | 61 | Line "( {abc} )" 62 | normal! gg0fb 63 | normal si(" 64 | Expect getline('.') ==# "(\"{abc}\")" 65 | 66 | let g:operator#surround#ignore_space = saved 67 | end 68 | " }}} 69 | 70 | " linewise {{{ 71 | it 'replace a linewise object in operator pending mode' 72 | 1Line '"hoge huga ' 73 | 2Line 'poyo"' 74 | 3Line '' 75 | normal gg0sip{ 76 | let line = getline(1).getline(2) 77 | Expect line ==# '{hoge huga poyo}' 78 | normal gg0sip' 79 | Expect getline(1).getline(2) ==# "'hoge huga poyo'" 80 | normal gg0sip[ 81 | Expect getline(1).getline(2) ==# '[hoge huga poyo]' 82 | normal gg0sip ) 83 | Expect getline(1).getline(2) ==# '( hoge huga poyo )' 84 | end 85 | 86 | it 'replace a linewise object in visual mode' 87 | 1Line '"hoge huga ' 88 | 2Line 'poyo"' 89 | 3Line '' 90 | normal gg0vips{ 91 | let line = getline(1).getline(2) 92 | Expect line ==# '{hoge huga poyo}' 93 | normal gg0vips' 94 | Expect getline(1).getline(2) ==# "'hoge huga poyo'" 95 | normal gg0vips[ 96 | Expect getline(1).getline(2) ==# '[hoge huga poyo]' 97 | normal gg0vips ) 98 | Expect getline(1).getline(2) ==# '( hoge huga poyo )' 99 | end 100 | 101 | it 'replace even the object is whole code which is a corner case' 102 | 1Line '" hoge huga ' 103 | 2Line 'poyo"' 104 | normal gg0sip{ 105 | Expect getline(1) ==# '{ hoge huga ' 106 | Expect getline(2) ==# 'poyo}' 107 | end 108 | 109 | " }}} 110 | 111 | " blockwise {{{ 112 | it 'replace a blockwise object in visual mode' 113 | 1Line "(hoge)" 114 | 2Line "(huga)" 115 | 3Line "(poyo)" 116 | 117 | execute 'normal' "gg0\G$s{" 118 | Expect getline(1) ==# "{hoge}" 119 | Expect getline(2) ==# "{huga}" 120 | Expect getline(3) ==# "{poyo}" 121 | end 122 | 123 | it 'replace a blockwise object even if the object is not rectangle' 124 | 1Line "(hogeee)" 125 | 2Line "(huga)" 126 | 3Line "(poyopiyo)" 127 | 128 | execute 'normal' "gg0\G$s{" 129 | Expect getline(1) ==# "{hogeee}" 130 | Expect getline(2) ==# "{huga}" 131 | Expect getline(3) ==# "{poyopiyo}" 132 | end 133 | 134 | it 'replace a blockwise object even if the blocks in the object is not the same' 135 | 1Line "(hogeee)" 136 | 2Line "" 137 | 3Line "'poyopiyo'" 138 | 139 | execute 'normal' "gg0\G$s{" 140 | Expect getline(1) ==# "{hogeee}" 141 | Expect getline(2) ==# "{huga}" 142 | Expect getline(3) ==# "{poyopiyo}" 143 | end 144 | " }}} 145 | 146 | " issue fixes {{{ 147 | it 'ensures to fix #23' 148 | Line "aaa '' bbb" 149 | 150 | execute 'normal' "gg0f'sa'{" 151 | Expect getline(1) ==# "aaa { }bbb" 152 | 153 | Line "aaa ' ' bbb" 154 | 155 | execute 'normal' "gg0f'sa'{" 156 | Expect getline(1) ==# "aaa { }bbb" 157 | end 158 | " }}} 159 | end 160 | -------------------------------------------------------------------------------- /t/use_input_spec.vim: -------------------------------------------------------------------------------- 1 | let s:root_dir = matchstr(system('git rev-parse --show-cdup'), '[^\n]\+') 2 | execute 'set' 'rtp +=./'.s:root_dir 3 | 4 | call vspec#matchers#load() 5 | 6 | set rtp+=~/.vim/bundle/vim-operator-user 7 | runtime plugin/operator/surround.vim 8 | 9 | command! -nargs=+ -count=1 Line call setline(, ) 10 | 11 | describe 'g:operator#surround#uses_input_if_no_block' 12 | 13 | before 14 | let g:operator#surround#uses_input_if_no_block = 1 15 | map sa (operator-surround-append) 16 | map sr (operator-surround-replace) 17 | new 18 | end 19 | 20 | after 21 | close! 22 | unmap sa 23 | unmap sr 24 | end 25 | 26 | it 'appends input if the input is not a block' 27 | Line "hoge" 28 | normal saiw, 29 | Expect getline('.') ==# ",hoge," 30 | end 31 | 32 | it 'replaces input if the input is not a block' 33 | Line "(hoge)" 34 | normal sra(, 35 | Expect getline('.') ==# ",hoge," 36 | end 37 | end 38 | --------------------------------------------------------------------------------