pax_global_header00006660000000000000000000000064133745060240014516gustar00rootroot0000000000000052 comment=074ce2575543f790290b189860597a3dcac1f79d vim-textobj-user-0.7.6/000077500000000000000000000000001337450602400147545ustar00rootroot00000000000000vim-textobj-user-0.7.6/.gitignore000066400000000000000000000000501337450602400167370ustar00rootroot00000000000000.vim-flavor Gemfile.lock VimFlavor.lock vim-textobj-user-0.7.6/.travis.yml000066400000000000000000000000561337450602400170660ustar00rootroot00000000000000language: ruby rvm: - 2.0.0 script: rake ci vim-textobj-user-0.7.6/Gemfile000066400000000000000000000000721337450602400162460ustar00rootroot00000000000000source 'https://rubygems.org' gem 'vim-flavor', '~> 2.0' vim-textobj-user-0.7.6/README.md000066400000000000000000000050201337450602400162300ustar00rootroot00000000000000# vim-textobj-user - Create your own text objects [![Build Status](https://travis-ci.org/kana/vim-textobj-user.png)](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/Rakefile000066400000000000000000000002161337450602400164200ustar00rootroot00000000000000#!/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/000077500000000000000000000000001337450602400165645ustar00rootroot00000000000000vim-textobj-user-0.7.6/autoload/textobj/000077500000000000000000000000001337450602400202435ustar00rootroot00000000000000vim-textobj-user-0.7.6/autoload/textobj/user.vim000066400000000000000000000566121337450602400217500ustar00rootroot00000000000000" 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, '