pax_global_header 0000666 0000000 0000000 00000000064 13374506024 0014516 g ustar 00root root 0000000 0000000 52 comment=074ce2575543f790290b189860597a3dcac1f79d
vim-textobj-user-0.7.6/ 0000775 0000000 0000000 00000000000 13374506024 0014754 5 ustar 00root root 0000000 0000000 vim-textobj-user-0.7.6/.gitignore 0000664 0000000 0000000 00000000050 13374506024 0016737 0 ustar 00root root 0000000 0000000 .vim-flavor
Gemfile.lock
VimFlavor.lock
vim-textobj-user-0.7.6/.travis.yml 0000664 0000000 0000000 00000000056 13374506024 0017066 0 ustar 00root root 0000000 0000000 language: ruby
rvm:
- 2.0.0
script: rake ci
vim-textobj-user-0.7.6/Gemfile 0000664 0000000 0000000 00000000072 13374506024 0016246 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
gem 'vim-flavor', '~> 2.0'
vim-textobj-user-0.7.6/README.md 0000664 0000000 0000000 00000005020 13374506024 0016230 0 ustar 00root root 0000000 0000000 # vim-textobj-user - Create your own text objects
[](https://travis-ci.org/kana/vim-textobj-user)
vim-textobj-user is a Vim plugin to create your own text objects without pain.
It is hard to create text objects, because there are many pitfalls to deal
with. This plugin hides such details and provides a declarative way to define
text objects. You can use regular expressions to define simple text objects,
or use functions to define complex ones.
## Examples
### Simple text objects defined by a pattern
Define `ad`/`id` to select a date such as `2013-03-16`, and
define `at`/`it` to select a time such as `22:04:21`:
```vim
call textobj#user#plugin('datetime', {
\ 'date': {
\ 'pattern': '\<\d\d\d\d-\d\d-\d\d\>',
\ 'select': ['ad', 'id'],
\ },
\ 'time': {
\ 'pattern': '\<\d\d:\d\d:\d\d\>',
\ 'select': ['at', 'it'],
\ },
\ })
```
### Simple text objects surrounded by a pair of patterns
Define `aP` to select a PHP code with ``, and
define `iP` to select a PHP code without ``:
```vim
call textobj#user#plugin('php', {
\ 'code': {
\ 'pattern': ['', '?>'],
\ 'select-a': 'aP',
\ 'select-i': 'iP',
\ },
\ })
```
### Complex text objects defined by functions
Define `al` to select the current line, and
define `il` to select the current line without indentation:
```vim
call textobj#user#plugin('line', {
\ '-': {
\ 'select-a-function': 'CurrentLineA',
\ 'select-a': 'al',
\ 'select-i-function': 'CurrentLineI',
\ 'select-i': 'il',
\ },
\ })
function! CurrentLineA()
normal! 0
let head_pos = getpos('.')
normal! $
let tail_pos = getpos('.')
return ['v', head_pos, tail_pos]
endfunction
function! CurrentLineI()
normal! ^
let head_pos = getpos('.')
normal! g_
let tail_pos = getpos('.')
let non_blank_char_exists_p = getline('.')[head_pos[2] - 1] !~# '\s'
return
\ non_blank_char_exists_p
\ ? ['v', head_pos, tail_pos]
\ : 0
endfunction
```
## Further reading
You can define your own text objects like the above examples. See also
[the reference manual](https://github.com/kana/vim-textobj-user/blob/master/doc/textobj-user.txt)
for more details.
There are many text objects written with vim-textobj-user.
If you want to find useful ones, or to know how they are implemented,
see [a list of text objects implemented with
vim-textobj-user](https://github.com/kana/vim-textobj-user/wiki).
vim-textobj-user-0.7.6/Rakefile 0000664 0000000 0000000 00000000216 13374506024 0016420 0 ustar 00root root 0000000 0000000 #!/usr/bin/env rake
task :ci => [:dump, :test]
task :dump do
sh 'vim --version'
end
task :test do
sh 'bundle exec vim-flavor test'
end
vim-textobj-user-0.7.6/autoload/ 0000775 0000000 0000000 00000000000 13374506024 0016564 5 ustar 00root root 0000000 0000000 vim-textobj-user-0.7.6/autoload/textobj/ 0000775 0000000 0000000 00000000000 13374506024 0020243 5 ustar 00root root 0000000 0000000 vim-textobj-user-0.7.6/autoload/textobj/user.vim 0000664 0000000 0000000 00000056612 13374506024 0021750 0 ustar 00root root 0000000 0000000 " textobj-user - Create your own text objects
" Version: 0.7.6
" Copyright (C) 2007-2018 Kana Natsuno
" License: MIT license {{{
" Permission is hereby granted, free of charge, to any person obtaining
" a copy of this software and associated documentation files (the
" "Software"), to deal in the Software without restriction, including
" without limitation the rights to use, copy, modify, merge, publish,
" distribute, sublicense, and/or sell copies of the Software, and to
" permit persons to whom the Software is furnished to do so, subject to
" the following conditions:
"
" The above copyright notice and this permission notice shall be included
" in all copies or substantial portions of the Software.
"
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
" IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
" CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
" TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
" SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}
" Interfaces "{{{1
" simple "{{{2
function! textobj#user#move(pattern, flags, previous_mode)
let i = v:count1
call s:prepare_movement(a:previous_mode)
while 0 < i
let result = searchpos(a:pattern, a:flags.'W')
let i = i - 1
endwhile
return result
endfunction
" FIXME: growing the current selection like iw/aw, is/as, and others.
" FIXME: countable.
function! textobj#user#select(pattern, flags, previous_mode)
let ORIG_POS = s:gpos_to_spos(getpos('.'))
let pft = searchpos(a:pattern, 'ceW')
let pfh = searchpos(a:pattern, 'bcW')
call cursor(ORIG_POS)
let pbh = searchpos(a:pattern, 'bcW')
let pbt = searchpos(a:pattern, 'ceW')
let pos = s:choose_better_pos(a:flags, ORIG_POS, pfh, pft, pbh, pbt)
if pos isnot 0
if a:flags !~# 'N'
call s:range_select(pos[0], pos[1], s:choose_wise(a:flags))
endif
return pos
else
return s:cancel_selection(a:previous_mode, ORIG_POS)
endif
endfunction
function! s:choose_better_pos(flags, ORIG_POS, pfh, pft, pbh, pbt)
" search() family with 'c' flag may not be matched to a pattern which
" matches to multiple lines. To choose appropriate range, we have to check
" another range [X] whether it contains the cursor or not.
let vf = s:range_validp(a:pfh, a:pft)
let vb = s:range_validp(a:pbh, a:pbt)
let cf = vf && s:range_containsp(a:pfh, a:pft, a:ORIG_POS)
let cb = vb && s:range_containsp(a:pbh, a:pbt, a:ORIG_POS)
let lf = vf && s:range_in_linep(a:pfh, a:pft, a:ORIG_POS)
let lb = vb && s:range_in_linep(a:pbh, a:pbt, a:ORIG_POS)
if cb " [X]
return [a:pbh, a:pbt]
elseif cf
return [a:pfh, a:pft]
elseif lf && a:flags =~# '[nl]'
return [a:pfh, a:pft]
elseif lb && a:flags =~# '[nl]'
return [a:pbh, a:pbt]
elseif vf && (a:flags =~# '[fn]' || a:flags !~# '[bcl]')
return [a:pfh, a:pft]
elseif vb && a:flags =~# '[bn]'
return [a:pbh, a:pbt]
else
return 0
endif
endfunction
" pair "{{{2
" FIXME: NIY, but is this necessary?
" function! textobj#user#move_pair(pattern1, pattern2, flags)
" endfunction
" BUGS: With o_CTRL-V, this may not work properly.
function! textobj#user#select_pair(pattern1, pattern2, flags, previous_mode)
let ORIG_POS = s:gpos_to_spos(getpos('.'))
" adjust the cursor to the head of a:pattern2 if it's already in the range.
let pos2c_tail = searchpos(a:pattern2, 'ceW')
let pos2c_head = searchpos(a:pattern2, 'bcW')
if !s:range_validp(pos2c_head, pos2c_tail)
return s:cancel_selection(a:previous_mode, ORIG_POS)
endif
if s:range_containsp(pos2c_head, pos2c_tail, ORIG_POS)
let more_flags = 'c'
else
let more_flags = ''
call cursor(ORIG_POS)
endif
" get the positions of a:pattern1 and a:pattern2.
let pos2p_head = searchpairpos(a:pattern1, '', a:pattern2, 'W'.more_flags)
let pos2p_tail = searchpos(a:pattern2, 'ceW')
if !s:range_validp(pos2p_head, pos2p_tail)
return s:cancel_selection(a:previous_mode, ORIG_POS)
endif
call cursor(pos2p_head)
let pos1p_head = searchpairpos(a:pattern1, '', a:pattern2, 'bW')
let pos1p_tail = searchpos(a:pattern1, 'ceW')
if !s:range_validp(pos1p_head, pos1p_tail)
return s:cancel_selection(a:previous_mode, ORIG_POS)
endif
" select the range, then adjust if necessary.
if a:flags =~# 'i'
if s:range_no_text_without_edgesp(pos1p_tail, pos2p_head)
return s:cancel_selection(a:previous_mode, ORIG_POS)
endif
call s:range_select(pos1p_tail, pos2p_head, s:choose_wise(a:flags))
" adjust the range.
let whichwrap_orig = &whichwrap
let &whichwrap = '<,>'
execute "normal! \o\"
let &whichwrap = whichwrap_orig
else
call s:range_select(pos1p_head, pos2p_tail, s:choose_wise(a:flags))
endif
return
endfunction
function! textobj#user#define(pat0, pat1, pat2, guideline) "{{{2
let pat0 = s:rhs_escape(a:pat0)
let pat1 = s:rhs_escape(a:pat1)
let pat2 = s:rhs_escape(a:pat2)
for function_name in keys(a:guideline)
let _lhss = a:guideline[function_name]
if type(_lhss) == type('')
let lhss = [_lhss]
else
let lhss = _lhss
endif
for lhs in lhss
if function_name == 'move-to-next'
execute 'nnoremap' s:mapargs_single_move(lhs, pat0, '', 'n')
execute 'vnoremap' s:mapargs_single_move(lhs, pat0, '', 'v')
execute 'onoremap' s:mapargs_single_move(lhs, pat0, '', 'o')
elseif function_name == 'move-to-next-end'
execute 'nnoremap' s:mapargs_single_move(lhs, pat0, 'e', 'n')
execute 'vnoremap' s:mapargs_single_move(lhs, pat0, 'e', 'v')
execute 'onoremap' s:mapargs_single_move(lhs, pat0, 'e', 'o')
elseif function_name == 'move-to-prev'
execute 'nnoremap' s:mapargs_single_move(lhs, pat0, 'b', 'n')
execute 'vnoremap' s:mapargs_single_move(lhs, pat0, 'b', 'v')
execute 'onoremap' s:mapargs_single_move(lhs, pat0, 'b', 'o')
elseif function_name == 'move-to-prev-end'
execute 'nnoremap' s:mapargs_single_move(lhs, pat0, 'be', 'n')
execute 'vnoremap' s:mapargs_single_move(lhs, pat0, 'be', 'v')
execute 'onoremap' s:mapargs_single_move(lhs, pat0, 'be', 'o')
elseif function_name == 'select-next' || function_name == 'select'
execute 'vnoremap' s:mapargs_single_select(lhs, pat0, '', 'v')
execute 'onoremap' s:mapargs_single_select(lhs, pat0, '', 'o')
elseif function_name == 'select-prev'
execute 'vnoremap' s:mapargs_single_select(lhs, pat0, 'b', 'v')
execute 'onoremap' s:mapargs_single_select(lhs, pat0, 'b', 'o')
elseif function_name == 'select-pair-all'
execute 'vnoremap' s:mapargs_pair_select(lhs, pat1, pat2, 'a', 'v')
execute 'onoremap' s:mapargs_pair_select(lhs, pat1, pat2, 'a', 'o')
elseif function_name == 'select-pair-inner'
execute 'vnoremap' s:mapargs_pair_select(lhs, pat1, pat2, 'i', 'v')
execute 'onoremap' s:mapargs_pair_select(lhs, pat1, pat2, 'i', 'o')
else
throw 'Unknown function name: ' . string(function_name)
endif
endfor
endfor
endfunction
function! textobj#user#map(plugin_name, obj_specs, ...) "{{{2
if a:0 == 0
" It seems to be directly called by user - a:obj_specs are not normalized.
call s:normalize(a:obj_specs)
endif
let banged_p = a:0 == 0 || a:1
for [obj_name, specs] in items(a:obj_specs)
for [spec_name, spec_info] in items(specs)
let rhs = s:interface_mapping_name(a:plugin_name, obj_name, spec_name)
if s:is_non_ui_property_name(spec_name)
" ignore
elseif spec_name =~# '^move-[npNP]$'
for lhs in spec_info
call s:map(banged_p, lhs, rhs)
endfor
elseif spec_name =~# '^select\(\|-[ai]\)$'
for lhs in spec_info
call s:objmap(banged_p, lhs, rhs)
endfor
else
throw printf('Unknown property: %s given to %s',
\ string(spec_name),
\ string(obj_name))
endif
unlet spec_info " to avoid E706.
endfor
endfor
call s:define_failsafe_key_mappings(a:plugin_name, a:obj_specs)
endfunction
function! textobj#user#plugin(plugin_name, obj_specs) "{{{2
if a:plugin_name =~# '\L'
throw '{plugin} contains non-lowercase alphabet: ' . string(a:plugin_name)
endif
let plugin = a:plugin_name
let Plugin = substitute(a:plugin_name, '^\(\l\)', '\u\1', 0)
let g:__textobj_{plugin} = s:plugin.new(a:plugin_name, a:obj_specs)
execute
\ 'command! -bang -bar -nargs=0 Textobj'.Plugin.'DefaultKeyMappings'
\ 'call g:__textobj_'.plugin.'.define_default_key_mappings("" == "!")'
call g:__textobj_{plugin}.define_interface_key_mappings()
if (!has_key(a:obj_specs, '*no-default-key-mappings*'))
\ && (!exists('g:textobj_'.plugin.'_no_default_key_mappings'))
execute 'Textobj'.Plugin.'DefaultKeyMappings'
endif
return g:__textobj_{plugin}
endfunction
" Misc. "{{{1
" pos "{{{2
" Terms:
" gpos [bufnum, lnum, col, off] - a value returned by getpos()
" spos [lnum, col] - a value returned by searchpos()
" pos same as spos
function! s:gpos_to_spos(gpos)
return a:gpos[1:2]
endfunction
function! s:pos_headp(pos)
return a:pos[1] <= 1
endfunction
function! s:pos_lastp(pos)
return a:pos[1] == len(getline(a:pos[0]))
endfunction
function! s:pos_le(pos1, pos2) " less than or equal
return ((a:pos1[0] < a:pos2[0])
\ || (a:pos1[0] == a:pos2[0] && a:pos1[1] <= a:pos2[1]))
endfunction
" range "{{{2
function! s:range_containsp(range_head, range_tail, target_pos)
return (s:pos_le(a:range_head, a:target_pos)
\ && s:pos_le(a:target_pos, a:range_tail))
endfunction
function! s:range_in_linep(range_head, range_tail, target_pos)
return a:range_head[0] == a:target_pos[0]
\ || a:range_tail[0] == a:target_pos[0]
endfunction
function! s:range_no_text_without_edgesp(range_head, range_tail)
let [hl, hc] = a:range_head
let [tl, tc] = a:range_tail
return ((hl == tl && hc - tc == -1)
\ || (hl - tl == -1
\ && (s:pos_lastp(a:range_head) && s:pos_headp(a:range_tail))))
endfunction
function! s:range_validp(range_head, range_tail)
let NULL_POS = [0, 0]
return (a:range_head != NULL_POS) && (a:range_tail != NULL_POS)
endfunction
function! s:range_select(range_head, range_tail, fallback_wise)
execute 'normal!' s:wise(a:fallback_wise)
call cursor(a:range_head)
normal! o
call cursor(a:range_tail)
if &selection ==# 'exclusive'
normal! l
endif
endfunction
" Save the last cursort position "{{{2
noremap (save-cursor-pos) save_cursor_pos()
" let s:last_cursor_gpos = getpos('.')
function! s:save_cursor_pos()
let s:last_cursor_gpos = getpos('.')
return ''
endfunction
" for textobj#user#define() "{{{2
function! s:rhs_escape(pattern)
let r = a:pattern
let r = substitute(r, '<', '', 'g')
let r = substitute(r, '|', '', 'g')
return r
endfunction
function! s:mapargs_single_move(lhs, pattern, flags, previous_mode)
return printf(' %s :call textobj#user#move(%s, %s, %s)',
\ a:lhs,
\ string(a:pattern), string(a:flags), string(a:previous_mode))
endfunction
function! s:mapargs_single_select(lhs, pattern, flags, previous_mode)
return printf(' %s :call textobj#user#select(%s, %s, %s)',
\ a:lhs,
\ string(a:pattern), string(a:flags), string(a:previous_mode))
endfunction
function! s:mapargs_pair_select(lhs, pattern1, pattern2, flags, previous_mode)
return printf(
\ ' %s :call textobj#user#select_pair(%s,%s,%s,%s)',
\ a:lhs,
\ string(a:pattern1), string(a:pattern2),
\ string(a:flags), string(a:previous_mode)
\ )
endfunction
" for textobj#user#plugin() "{{{2
" basics "{{{3
let s:plugin = {}
function! s:plugin.new(plugin_name, obj_specs)
let _ = extend({'name': a:plugin_name, 'obj_specs': a:obj_specs},
\ s:plugin, 'keep')
call _.normalize()
return _
endfunction
function! s:plugin.normalize()
call s:normalize(self.obj_specs)
endfunction
function! s:normalize(obj_specs)
call s:normalize_property_names(a:obj_specs)
call s:normalize_property_values(a:obj_specs)
endfunction
function! s:normalize_property_names(obj_specs)
for spec in values(a:obj_specs)
for old_prop_name in keys(spec)
if old_prop_name =~ '^\*.*\*$'
let new_prop_name = substitute(old_prop_name, '^\*\(.*\)\*$', '\1', '')
let spec[new_prop_name] = spec[old_prop_name]
unlet spec[old_prop_name]
endif
endfor
endfor
endfunction
function! s:normalize_property_values(obj_specs)
for [obj_name, specs] in items(a:obj_specs)
for [spec_name, spec_info] in items(specs)
if s:is_ui_property_name(spec_name)
if type(spec_info) == type('')
let specs[spec_name] = [spec_info]
endif
endif
if spec_name =~# '-function$'
if spec_info =~# '^s:'
if has_key(specs, 'sfile')
let specs[spec_name] = substitute(spec_info,
\ '^s:',
\ s:snr_prefix(specs['sfile']),
\ '')
else
echoerr '"sfile" must be given to use a script-local function:'
\ string(spec_name) '/' string(spec_info)
endif
else
" Nothing to do.
endif
elseif spec_name ==# 'pattern'
if !has_key(specs, 'region-type')
let specs['region-type'] = 'v'
endif
if !has_key(specs, 'scan')
let specs['scan'] = 'forward'
endif
endif
unlet spec_info " to avoid E706.
endfor
endfor
endfunction
function! s:plugin.define_default_key_mappings(banged_p) "{{{3
call textobj#user#map(self.name, self.obj_specs, a:banged_p)
endfunction
function! s:plugin.define_interface_key_mappings() "{{{3
let RHS_FORMAT =
\ '(save-cursor-pos)'
\ . ':call g:__textobj_' . self.name . '.%s('
\ . '"%s",'
\ . '"%s",'
\ . '""'
\ . ')'
for [obj_name, specs] in items(self.obj_specs)
for spec_name in filter(keys(specs), 's:is_ui_property_name(v:val)')
" lhs
let lhs = self.interface_mapping_name(obj_name, spec_name)
" rhs
let _ = spec_name . '-function'
if has_key(specs, _)
let do = 'do_by_function'
elseif has_key(specs, 'pattern')
let do = 'do_by_pattern'
else
" skip to allow to define user's own {rhs} of the interface mapping.
continue
endif
let rhs = printf(RHS_FORMAT, do, spec_name, obj_name)
" map
if spec_name =~# '^move'
let MapFunction = function('s:noremap')
else " spec_name =~# '^select'
let MapFunction = function('s:objnoremap')
endif
call MapFunction(1, '