├── .travis.yml ├── README.md ├── doc └── after-object.txt ├── autoload └── after_object.vim └── test └── after_object.vader /.travis.yml: -------------------------------------------------------------------------------- 1 | language: vim 2 | 3 | before_script: | 4 | git clone https://github.com/junegunn/vader.vim.git 5 | hg clone https://code.google.com/p/vim/ 6 | cd vim 7 | ./configure --with-features=huge 8 | make 9 | sudo make install 10 | cd - 11 | 12 | script: | 13 | vim -Nu <(cat << VIMRC 14 | set rtp+=vader.vim 15 | set rtp+=. 16 | VIMRC) -c 'Vader! test/*' > /dev/null 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-after-object ![travis-ci](https://travis-ci.org/junegunn/vim-after-object.svg?branch=master) 2 | ================ 3 | 4 | Defines text objects to target text *after* the designated characters. 5 | 6 | Installation 7 | ------------ 8 | 9 | Using [vim-plug](https://github.com/junegunn/vim-plug): 10 | 11 | ```vim 12 | Plug 'junegunn/vim-after-object' 13 | ``` 14 | 15 | Setting up 16 | ---------- 17 | 18 | vim-after-object does not define any mappings by default. You have to enable 19 | mappings you want. 20 | 21 | ```vim 22 | autocmd VimEnter * call after_object#enable('=', ':', '-', '#', ' ') 23 | ``` 24 | 25 | For each character in the argument list, a pair of mappings is defined; the 26 | default mapping with `a`-prefix (mnemonic for *after*) and the one with 27 | `aa`-prefix for backward search. The latter is only useful when the character 28 | appears more than once in the line. 29 | 30 | To use different prefixes, pass an optional list to `after_object#enable`: 31 | 32 | ```vim 33 | " ]= and [= instead of a= and aa= 34 | autocmd VimEnter * call after_object#enable([']', '['], '=', ':') 35 | ``` 36 | 37 | Usage 38 | ----- 39 | 40 | ```ruby 41 | # va= visual after = 42 | # ca= change after = 43 | # da= delete after = 44 | # ya= yank after = 45 | apple = 'juice' 46 | ``` 47 | 48 | When the line contains multiple occurrences of the character, you can move the 49 | visual selection forward by repeating `a=`, or backward with `aa=`. Both 50 | mappings can be preceded by a count. Refer to the test cases for the details. 51 | -------------------------------------------------------------------------------- /doc/after-object.txt: -------------------------------------------------------------------------------- 1 | after-object.txt after-object Last change: December 14 2014 2 | AFTER-OBJECT - TABLE OF CONTENTS *afterobject* *after-object* *after-object-toc* 3 | ============================================================================== 4 | 5 | vim-after-object 6 | Installation |after-object-1| 7 | Setting up |after-object-2| 8 | Usage |after-object-3| 9 | 10 | 11 | VIM-AFTER-OBJECT *vim-after-object* 12 | ============================================================================== 13 | 14 | Defines text objects to target text after the designated characters. 15 | 16 | 17 | *after-object-1* 18 | INSTALLATION *after-object-installation* 19 | ============================================================================== 20 | 21 | Using {vim-plug}{1}: 22 | > 23 | Plug 'junegunn/vim-after-object' 24 | < 25 | {1} https://github.com/junegunn/vim-plug 26 | 27 | 28 | *after-object-2* 29 | SETTING UP *after-object-setting-up* 30 | ============================================================================== 31 | 32 | vim-after-object does not define any mappings by default. You have to enable 33 | mappings you want. 34 | 35 | *after_object#enable* 36 | > 37 | autocmd VimEnter * call after_object#enable('=', ':', '-', '#', ' ') 38 | < 39 | For each character, a pair of mappings are defined; the default mapping with 40 | `a`-prefix (mnemonic for after) and the one with `aa`-prefix for backward 41 | search. The latter is only used when the character appears more than once in 42 | the line. 43 | 44 | To use different prefixes, pass an optional list to `after_object#enable`: 45 | > 46 | " ]= and [= instead of a= and aa= 47 | autocmd VimEnter * call after_object#enable([']', '['], '=', ':') 48 | < 49 | 50 | *after-object-3* 51 | USAGE *after-object-usage* 52 | ============================================================================== 53 | > 54 | # va= visual after = 55 | # ca= change after = 56 | # da= delete after = 57 | # ya= yank after = 58 | apple = 'juice' 59 | < 60 | When the line contains multiple occurrences of the character, you can move the 61 | visual selection forward by repeating `a=`, or backward with `aa=`. Both 62 | mappings can be preceded by a count. Refer to the test cases for the details. 63 | 64 | ============================================================================== 65 | vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap: 66 | -------------------------------------------------------------------------------- /autoload/after_object.vim: -------------------------------------------------------------------------------- 1 | " Copyright (c) 2015 Junegunn Choi 2 | " 3 | " MIT License 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining 6 | " a copy of this software and associated documentation files (the 7 | " "Software"), to deal in the Software without restriction, including 8 | " without limitation the rights to use, copy, modify, merge, publish, 9 | " distribute, sublicense, and/or sell copies of the Software, and to 10 | " permit persons to whom the Software is furnished to do so, subject to 11 | " the following conditions: 12 | " 13 | " The above copyright notice and this permission notice shall be 14 | " included in all copies or substantial portions of the Software. 15 | " 16 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | if exists('g:loaded_after_object') 25 | finish 26 | endif 27 | let g:loaded_after_object = 1 28 | 29 | let s:cpo_save = &cpo 30 | set cpo&vim 31 | 32 | function! s:after(str, cnt, vis, bw) 33 | let col = a:vis ? col("'<") : col('.') 34 | let line = getline('.') 35 | let parts = split(line, '\V'.a:str.'\zs', 1) 36 | 37 | try 38 | if len(parts) == 1 39 | throw 'exit' 40 | endif 41 | 42 | for i in reverse(range(1, a:cnt)) 43 | let len = 0 44 | let idx = 0 45 | for part in parts[0 : (a:bw ? -2 : -3)] 46 | let len += strchars(part) 47 | if len > col - 1 48 | break 49 | endif 50 | let idx += 1 51 | endfor 52 | 53 | if a:bw 54 | let idx = max([idx - 2, 0]) 55 | endif 56 | let col = strchars(join(parts[0 : idx], '')) + (i > 1 && a:cnt > 1) 57 | endfor 58 | 59 | let rest = substitute(line, '^.\{'.col.'}', '', '') 60 | if empty(rest) 61 | throw 'exit' 62 | else 63 | let idx = max([match(rest, '\S'), 0]) 64 | execute 'normal! 0'.(col + idx).'lv$h' 65 | endif 66 | catch 'exit' 67 | call s:textobj_cancel() 68 | if a:vis 69 | normal! gv 70 | endif 71 | return 72 | finally 73 | if histget(':', -1) =~ '[0-9_]*after(' 74 | call histdel(':', -1) 75 | endif 76 | echo 77 | endtry 78 | endfunction 79 | 80 | function! s:textobj_cancel() 81 | if v:operator == 'c' 82 | augroup afterobj_undo_empty_change 83 | autocmd InsertLeave execute 'normal! u' 84 | \| execute 'autocmd! afterobj_undo_empty_change' 85 | \| execute 'augroup! afterobj_undo_empty_change' 86 | augroup END 87 | endif 88 | endfunction 89 | 90 | noremap (AfterAfterObject) 91 | inoremap (AfterAfterObject) exists('#afterobj_undo_empty_change')?"\":'' 92 | 93 | function! s:esc(c) 94 | " TODO: anything else? 95 | return substitute(substitute(a:c, ' ', '', 'g'), '|', '', 'g') 96 | endfunction 97 | 98 | function! s:parse_args(args) 99 | let lists = filter(copy(a:args), 'type(v:val) == type([])') 100 | let chars = filter(copy(a:args), 'type(v:val) == type("")') 101 | return [get(lists, '-1', ['a', 'aa']), chars] 102 | endfunction 103 | 104 | function! after_object#enable(...) 105 | let [prefixes, chars] = s:parse_args(a:000) 106 | let prefixes = map(prefixes[0 : 1], '[v:val, v:key]') 107 | for c in chars 108 | for [p, b] in prefixes 109 | for [m, v] in items({ 'x': 1, 'o': 0 }) 110 | execute printf( 111 | \ '%smap %s%s :call after(%s, v:count1, %d, %d)(AfterAfterObject)', 112 | \ m, p, s:esc(c), string(s:esc(c)), v, b) 113 | endfor 114 | endfor 115 | endfor 116 | endfunction 117 | 118 | function! after_object#disable(...) 119 | let [prefixes, chars] = s:parse_args(a:000) 120 | for c in chars 121 | for p in prefixes[0 : 1] 122 | for m in ['x', 'o'] 123 | execute printf('%sunmap %s%s', m, p, s:esc(c)) 124 | endfor 125 | endfor 126 | endfor 127 | endfunction 128 | 129 | let &cpo = s:cpo_save 130 | unlet s:cpo_save 131 | 132 | -------------------------------------------------------------------------------- /test/after_object.vader: -------------------------------------------------------------------------------- 1 | Execute (Setup): 2 | call after_object#enable('=', ':') 3 | 4 | Given (=): 5 | apple=apple 6 | apple= apple 7 | apple= apple 8 | apple =apple 9 | apple = apple 10 | apple = apple 11 | 12 | Before: 13 | let @q = 'da=j' 14 | Do (da=): 15 | 100@q 16 | 17 | Expect (Deleted after =): 18 | apple= 19 | apple= 20 | apple= 21 | apple = 22 | apple = 23 | apple = 24 | 25 | Before: 26 | let @q = "va=cx\j" 27 | Do (va=cx): 28 | 100@q 29 | 30 | Expect (x after =): 31 | apple=x 32 | apple= x 33 | apple= x 34 | apple =x 35 | apple = x 36 | apple = x 37 | 38 | Given (:): 39 | apple:apple 40 | apple: apple 41 | apple: apple 42 | apple :apple 43 | apple : apple 44 | apple : apple 45 | 46 | Before: 47 | let @q = 'da:j' 48 | Do (da:): 49 | 100@q 50 | 51 | Expect (Deleted after :): 52 | apple: 53 | apple: 54 | apple: 55 | apple : 56 | apple : 57 | apple : 58 | 59 | Before: 60 | let @q = 'daa:j' 61 | Do (daa:): 62 | 100@q 63 | 64 | Expect (Deleted after :): 65 | apple: 66 | apple: 67 | apple: 68 | apple : 69 | apple : 70 | apple : 71 | 72 | Before: 73 | 74 | ------------------------ 75 | ~ Multiple occurrences ~ 76 | ------------------------ 77 | 78 | Given (=s): 79 | apple = apple = apple = apple 80 | 81 | Do (a=a=): 82 | gUa= 83 | gua= 84 | gUa= 85 | 86 | Expect: 87 | apple = APPLE = apple = APPLE 88 | 89 | Do (aa=aa=aa=): 90 | $v 91 | aa= 92 | aa= 93 | d 94 | 95 | Expect: 96 | apple = 97 | 98 | Do (With count (2)): 99 | 2da= 100 | 101 | Expect: 102 | apple = apple = 103 | 104 | Do (With count (3)): 105 | 3da= 106 | 107 | Expect: 108 | apple = apple = apple = 109 | 110 | ------------------------ 111 | ~ Repeatability ~ 112 | ------------------------ 113 | 114 | Given: 115 | apple = red 116 | banana = yellow 117 | right. 118 | 119 | Do (FIXME: Repeat ca=): 120 | ca=blue\ 121 | j. 122 | j. 123 | 124 | Expect: 125 | apple = blue 126 | banana = blue 127 | right. 128 | 129 | ------------------------ 130 | ~ Custom prefix ~ 131 | ------------------------ 132 | 133 | Given (=s): 134 | apple = apple = apple = apple 135 | 136 | Before (Define after-objects with custom prefixes): 137 | call after_object#enable([']', '['], '=', ':') 138 | Do (Using custom prefix): 139 | v]=]=]=[=U 140 | 141 | Expect: 142 | apple = apple = APPLE = APPLE 143 | 144 | Before (You can put prefix list anywhere in the argument list): 145 | call after_object#enable('=', ':', ['', '-']) 146 | Do (Using custom prefix): 147 | v\=\=\=-=U 148 | 149 | Expect: 150 | apple = apple = APPLE = APPLE 151 | 152 | Before (Remove mappings with after_object#disable call): 153 | call after_object#disable('=', ':', ['', '-']) 154 | Do (Mappings with custom prefixes are gone): 155 | v\=\=\=-=U 156 | 157 | Expect: 158 | apple = apple = apple = apple 159 | 160 | Before (Remove mappings with after_object#disable call w/o list argument): 161 | call after_object#disable('=', ':') 162 | Do (Mappings are gone): 163 | va=a=a=aa=U 164 | 165 | Expect: 166 | apple = apple = apple = apple 167 | 168 | Before: 169 | Execute (Re-enable mappings): 170 | call after_object#enable('=', ':') 171 | 172 | ------------------------ 173 | ~ Abort on no match ~ 174 | ------------------------ 175 | 176 | Given: 177 | apple = red 178 | banana = yellow 179 | right. 180 | 181 | Do (FIXME: ca= on no match should leave insert mode): 182 | ca=bad\ 183 | jj 184 | ca=ft 185 | 186 | Expect: 187 | apple = bad 188 | banana = yellow 189 | right. 190 | 191 | ------------------------ 192 | ~ Tabs ~ 193 | ------------------------ 194 | 195 | Given (Tab indentation): 196 | apple = red 197 | 198 | Do: 199 | da= 200 | 201 | Expect: 202 | apple = 203 | 204 | ------------------------ 205 | ~ Regressions ~ 206 | ------------------------ 207 | Given: 208 | abc = 1 209 | 210 | Do: 211 | ca=2\ 212 | 213 | Expect: 214 | abc = 2 215 | 216 | Given: 217 | abc = 218 | 219 | Do: 220 | da= 221 | 222 | Expect: 223 | abc = 224 | 225 | Given (Multi-byte characters): 226 | ___가나다: foobar 227 | ___가나다: ___라마바: foobar 228 | 229 | Do: 230 | ca:hello\ 231 | jva:a:chello\ 232 | 233 | Expect: 234 | ___가나다: hello 235 | ___가나다: ___라마바: hello 236 | 237 | --------------------------------------------------------------------------------