Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

cycle 插件的安装及配置

  • Loading branch information...
commit 731f2e37840a2a06545f730a6a084b05eb62e29e 1 parent 52bed0b
@lilydjwg authored
Showing with 1,190 additions and 0 deletions.
  1. +683 −0 autoload/cycle.vim
  2. +372 −0 doc/cycle.txt
  3. +31 −0 doc/tags
  4. +73 −0 plugin/cycle.vim
  5. +31 −0 vimrc
View
683 autoload/cycle.vim
@@ -0,0 +1,683 @@
+" Constants: {{{
+
+let s:OPTIONS = {
+ \ 'name': 'name',
+ \ 'match_case': 'match_case',
+ \ 'hard_case': 'hard_case',
+ \ 'restrict_cursor': 'restrict_cursor',
+ \ 'sub_tag': 'sub_tag',
+ \ 'sub_pair': 'sub_pair',
+ \ 'sub_pairs': 'sub_pairs',
+ \ 'end_with': 'end_with',
+ \ 'begin_with': 'begin_with',
+ \ 'regex': 'regex',
+ \ }
+
+" }}} Constants
+
+
+" Main Functions: {{{
+
+function! cycle#new(class_name, direction, count) "{{{
+ let matches = cycle#search(a:class_name, {'direction': a:direction, 'count': a:count})
+
+ if empty(matches)
+ return s:fallback(a:direction, a:count)
+ endif
+
+ if len(matches) > 1 && g:cycle_max_conflict > 1
+ let choice = s:conflict(matches)
+ if choice
+ let matches = [matches[choice - 1]]
+ else
+ echohl WarningMsg | echo "Aborted." | echohl None
+ return
+ endif
+ endif
+
+ if len(matches)
+ call s:substitute(
+ \ matches[0].pairs.before,
+ \ matches[0].pairs.after,
+ \ a:class_name,
+ \ matches[0].group.items,
+ \ extend(matches[0].group.options, {(s:OPTIONS.restrict_cursor): 1}),
+ \ )
+ silent! call repeat#set(
+ \ "\<Plug>Cycle" . (a:direction > 0 ? "Next" : "Prev"),
+ \ a:count
+ \ )
+ else
+ call s:fallback(a:direction, a:count)
+ endif
+endfunction "}}}
+
+
+function! cycle#search(class_name, ...) "{{{
+ let options = a:0 ? a:1 : {}
+ let groups = s:groups()
+ let direction = get(options, 'direction', 1)
+ let l:count = get(options, 'count', 1)
+ let matches = []
+ let cword = s:new_cword()
+ let cchar = s:new_cchar()
+
+ if a:class_name == 'w'
+ if len(cchar.text) > 1
+ let phases = ['.', 'w']
+ if cword != cchar
+ call add(phases, '')
+ endif
+ else
+ let phases = ['w', '']
+ endif
+ elseif a:class_name == 'v'
+ let phases = ['v']
+ else
+ let phases = []
+ endif
+
+ for phase in phases
+ let matches = s:phased_search(phase, groups, direction, l:count)
+ if len(matches)
+ break
+ endif
+ endfor
+
+ return matches
+endfunction "}}}
+
+
+function! s:phased_search(class_name, groups, direction, count) "{{{
+ let matches = []
+ let new_text = s:new_ctext('')
+ let new_index = -1
+
+ for group in a:groups
+ if len(matches) && g:cycle_max_conflict <= 1
+ break
+ endif
+
+ let [index, ctext] = s:group_search(group, a:class_name)
+ if index >= 0
+ let new_index = (index + a:direction * a:count) % len(group.items)
+ let new_text.text = s:text_transform(
+ \ ctext.text,
+ \ group.items[new_index],
+ \ group.options,
+ \ )
+ let new_text.line = ctext.line
+ let new_text.col = ctext.col
+ call add(matches, {
+ \ 'group': group,
+ \ 'pairs': {'before': deepcopy(ctext), 'after': deepcopy(new_text)},
+ \ })
+ endif
+ endfor
+
+ return matches
+endfunction "}}}
+
+
+function! s:substitute(before, after, class_name, items, options) "{{{
+ let callbacks = s:parse_callback_options(a:options)
+ let callback_params = {
+ \ 'before': a:before,
+ \ 'after': a:after,
+ \ 'class_name': a:class_name,
+ \ 'items': a:items,
+ \ 'options': a:options,
+ \ }
+
+ call setline(
+ \ a:before.line,
+ \ substitute(
+ \ getline(a:before.line),
+ \ '\%' . a:before.col . 'c' . s:escape_pattern(a:before.text),
+ \ s:escape_sub_expr(a:after.text),
+ \ ''
+ \ )
+ \ )
+
+ for Fn in callbacks.after_sub
+ call call(Fn, [callback_params])
+ endfor
+endfunction "}}}
+
+
+function! s:conflict(matches) "{{{
+ if len(a:matches) > g:cycle_max_conflict
+ redraw
+ echohl WarningMsg | echomsg "Cycle: Too many matches (" . len(a:matches) . " found)." | echohl None
+ return
+ endif
+
+ let index = 0
+ let candidates = []
+ let captions = []
+ for match in a:matches
+ let caption = nr2char(char2nr('A') + index)
+ call add(candidates, join([
+ \ ' ' . caption . ') ',
+ \ get(match.group.options, s:OPTIONS.name, '') . " => ",
+ \ match.pairs.after.text
+ \ ], ''))
+ call add(captions, '&' . caption)
+ let index += 1
+ endfor
+ return confirm("Cycle with:\n" . join(candidates, "\n"), join(captions, "\n"), 0)
+endfunction "}}}
+
+
+function! s:fallback(direction, count) "{{{
+ " TODO: doc
+ execute "normal " . a:count . "\<Plug>CycleFallback" . (a:direction > 0 ? 'Next' : 'Prev')
+endfunction "}}}
+
+" }}} Main Functions
+
+
+" Group Operations: {{{1
+" Structure of groups:
+" g:cycle_groups = [ | => groups, scoped by global or buffer
+" { | =>
+" 'items': ['foo', 'bar'], | =>
+" 'options': {'hard_case': 1}, | => a group
+" }, | =>
+" ], |
+
+function! s:groups(...) "{{{
+ let groups = []
+ for scope in ['b', 'g']
+ let name = scope . ':cycle_groups'
+ if exists(name)
+ let groups += {name}
+ endif
+ endfor
+ return groups
+endfunction "}}}
+
+
+function! s:group_search(group, class_name) "{{{
+ let options = a:group.options
+ let pos = s:getpos()
+ let index = -1
+ let ctext = s:new_ctext(a:class_name)
+
+ for item in a:group.items
+ if type(get(options, s:OPTIONS.regex)) == type('')
+ let pattern = item
+ let text_index = match(getline('.'), pattern)
+ if text_index >= 0
+ let index = index(a:group.items, item)
+ let ctext = {
+ \ 'text': matchstr(getline('.'), pattern),
+ \ 'line': line('.'),
+ \ 'col': text_index + 1,
+ \ }
+ break
+ endif
+ else
+ if a:class_name != ''
+ let pattern = join([
+ \ '\%' . ctext.col . 'c',
+ \ s:escape_pattern(item),
+ \ get(options, s:OPTIONS.match_case) ? '\C' : '\c',
+ \ ], '')
+ else
+ let pattern = join([
+ \ '\%>' . max([0, pos.col - strlen(item)]) . 'c',
+ \ '\%<' . (pos.col + 1) . 'c' . s:escape_pattern(item),
+ \ get(options, s:OPTIONS.match_case) ? '\C' : '\c',
+ \ ], '')
+ endif
+ let text_index = match(getline('.'), pattern)
+
+ if a:class_name == 'v' && item != s:new_cvisual().text
+ continue
+ endif
+
+ if a:class_name == 'w' && item != s:new_cword().text
+ continue
+ endif
+
+ if text_index >= 0
+ let index = index(a:group.items, item)
+ let ctext = {
+ \ 'text': strpart(getline('.'), text_index, len(item)),
+ \ 'line': line('.'),
+ \ 'col': text_index + 1,
+ \ }
+ break
+ endif
+
+ endif
+ endfor
+
+ return [index, ctext]
+endfunction "}}}
+
+
+function! s:add_group(scope, group_attrs) "{{{
+ let items = copy(a:group_attrs[0])
+ let options = {}
+
+ for param in a:group_attrs[1:]
+ if type(param) == type({})
+ call extend(options, param)
+ elseif type(param) == type([])
+ for option in param
+ if type(option) == type({})
+ call extend(options, option)
+ else
+ let options[option] = 1
+ endif
+ unlet option
+ endfor
+ else
+ for option in split(param)
+ let options[option] = 1
+ endfor
+ endif
+ unlet param
+ endfor
+
+ if has_key(options, s:OPTIONS.sub_pairs)
+ let separator = type(options.sub_pairs) == type(0) ? ':' : options.sub_pairs
+ let [begin_items, end_items] = [[], []]
+ for item in items
+ let [begin_item, end_item] = split(item, separator)
+ call add(begin_items, begin_item)
+ call add(end_items, end_item)
+ endfor
+ unlet options.sub_pairs
+ let options.sub_pair = 1
+ call s:add_group(a:scope, [begin_items, extend(deepcopy(options), {(s:OPTIONS.end_with): end_items})])
+ call s:add_group(a:scope, [end_items, extend(deepcopy(options), {(s:OPTIONS.begin_with): begin_items})])
+ return
+ endif
+
+ let group = {
+ \ 'items': items,
+ \ 'options': options,
+ \ }
+
+ let name = a:scope . ':cycle_groups'
+ if !exists(name)
+ let {name} = [group]
+ else
+ call add({name}, group)
+ endif
+endfunction "}}}
+
+
+function! cycle#add_group(group_or_items, ...) "{{{
+ call s:add_group_to('g', a:group_or_items, a:000)
+endfunction "}}}
+
+
+function! cycle#add_b_group(group_or_items, ...) "{{{
+ call s:add_group_to('b', a:group_or_items, a:000)
+endfunction "}}}
+
+
+function! cycle#add_groups(groups) "{{{
+ for group in a:groups
+ call cycle#add_group(group)
+ endfor
+endfunction "}}}
+
+
+function! cycle#add_b_groups(groups) "{{{
+ for group in a:groups
+ call cycle#add_b_group(group)
+ endfor
+endfunction "}}}
+
+
+function! s:add_group_to(scope, group_or_items, ...) "{{{
+ if type(a:group_or_items[0]) == type([])
+ call s:add_group(a:scope, a:group_or_items)
+ elseif a:0 > 0
+ call s:add_group(a:scope, [a:group_or_items] + a:1)
+ endif
+endfunction "}}}
+
+
+function! cycle#reset_b_groups(...) "{{{
+ if exists('b:cycle_groups')
+ unlet b:cycle_groups
+ endif
+
+ if a:0 && !empty(a:1)
+ call cycle#add_b_groups(a:1)
+ endif
+endfunction "}}}
+
+
+function! cycle#reset_b_groups_by_filetype() "{{{
+ let var_name = 'g:cycle_default_groups_for_' . &filetype
+ call cycle#reset_b_groups(exists(var_name) ? {var_name} : [])
+endfunction "}}}
+
+" }}} Group Operations
+
+
+" Text Classes: {{{
+
+function! s:new_ctext(text_class) "{{{
+ if a:text_class == 'w'
+ let ctext = s:new_cword()
+ if ctext.col == 0
+ let ctext = s:new_cchar()
+ endif
+ elseif a:text_class == '.'
+ let ctext = s:new_cchar()
+ elseif a:text_class == 'v'
+ let ctext = s:new_cvisual()
+ else
+ let ctext = {
+ \ "text": '',
+ \ 'line': 0,
+ \ "col": 0,
+ \ }
+ endif
+ return ctext
+endfunction "}}}
+
+
+function! s:new_cword() "{{{
+ let ckeyword = expand('<cword>')
+ let cchar = s:new_cchar()
+ let cword = {
+ \ "text": '',
+ \ 'line': 0,
+ \ "col": 0,
+ \ }
+
+ if match(ckeyword, s:escape_pattern(cchar.text)) >= 0
+ let cword.line = line('.')
+ let cword.col = match(
+ \ getline('.'),
+ \ '\%>' . max([0, cchar.col - strlen(ckeyword) - 1]) . 'c' . s:escape_pattern(ckeyword),
+ \ ) + 1
+ let cword.text = ckeyword
+ endif
+ return cword
+endfunction "}}}
+
+
+function! s:new_cvisual() "{{{
+ let save_mode = mode()
+
+ call s:save_reg('a')
+ normal! gv"ay
+ let cvisual = {
+ \ "text": @a,
+ \ "line": getpos('v')[1],
+ \ "col": getpos('v')[2],
+ \ }
+
+ if save_mode == 'v'
+ normal! gv
+ endif
+ call s:restore_reg('a')
+
+ return cvisual
+endfunction "}}}
+
+
+function! s:new_cchar() "{{{
+ call s:save_reg('a')
+ normal! "ayl
+ let cchar = {
+ \ "text": @a,
+ \ "line": getpos('.')[1],
+ \ "col": getpos('.')[2],
+ \ }
+ call s:restore_reg('a')
+ return cchar
+endfunction "}}}
+
+
+function! s:getpos() "{{{
+ let pos = getpos('.')
+ return {
+ \ "line": pos[1],
+ \ "col": pos[2],
+ \ }
+endfunction "}}}
+
+" }}} Text Classes
+
+
+" Optional Callbacks: {{{
+
+function! s:sub_tag_pair(params) "{{{
+ let before = a:params.before
+ let after = a:params.after
+ let options = a:params.options
+ let timeout = 600
+ let pattern_till_tag_end = '\_[^>]*>'
+ let ic_flag = get(options, s:OPTIONS.match_case) ? '\C' : '\c'
+ let pos = s:getpos()
+
+ if search(
+ \ '\v\</?\m\%' . before.line . 'l\%' . before.col . 'c'
+ \ . pattern_till_tag_end . '\C',
+ \ 'n',
+ \ )
+ let in_closing_tag = search('/\m\%' . before.line . 'l\%' . before.col . 'c\C', 'n')
+ let opposite = searchpairpos(
+ \ '<' . s:escape_pattern(before.text) . pattern_till_tag_end,
+ \ '',
+ \ '</' . s:escape_pattern(before.text) . '\s*>'
+ \ . (in_closing_tag ? '\zs' : '') . ic_flag,
+ \ 'nW' . (in_closing_tag ? 'b' : ''),
+ \ '',
+ \ '',
+ \ timeout,
+ \ )
+
+ if opposite != [0, 0]
+ let ctext = {
+ \ "text": before.text,
+ \ "line": opposite[0],
+ \ "col": opposite[1] + 1 + !in_closing_tag,
+ \ }
+ let len_diff = strlen(after.text) - strlen(ctext.text)
+
+ call s:substitute(
+ \ ctext,
+ \ after,
+ \ '-',
+ \ [],
+ \ s:cascade_options_for_callback(options),
+ \ )
+
+ if in_closing_tag && ctext.line == after.line
+ let new_col = before.col + len_diff
+ if a:params.class_name == 'v'
+ normal! "_y
+ call cursor(pos.line, new_col)
+ normal! vt>
+ else
+ if pos.col > new_col + strlen(after.text)
+ call cursor(pos.line, new_col)
+ execute 'normal! t>'
+ else
+ call cursor(pos.line, pos.col + len_diff)
+ endif
+ endif
+ endif
+
+ endif
+ endif
+endfunction "}}}
+
+
+function! s:sub_pair(params) "{{{
+ let before = a:params.before
+ let after = a:params.after
+ let options = a:params.options
+ let timeout = 600
+ let ic_flag = get(options, s:OPTIONS.match_case) ? '\C' : '\c'
+
+ if type(get(options, s:OPTIONS.end_with)) == type([])
+ let at_begin = 1
+ elseif type(get(options, s:OPTIONS.begin_with)) == type([])
+ let at_begin = 0
+ else
+ return
+ endif
+ let pair_at = at_begin ? 'end' : 'begin'
+
+ let pair_before = deepcopy(before)
+ let pair_before.text = get(
+ \ options[pair_at . '_with'],
+ \ index(a:params.items, before.text, 0, ic_flag == '\c'),
+ \ )
+ let pair_after = {}
+ let pair_after.text = get(
+ \ options[pair_at . '_with'],
+ \ index(a:params.items, after.text, 0, ic_flag == '\c'),
+ \ )
+
+ let opposite = searchpairpos(
+ \ s:escape_pattern(at_begin ? before.text : pair_before.text),
+ \ '',
+ \ s:escape_pattern(at_begin ? pair_before.text : pair_after.text)
+ \ . (at_begin ? '' : '\zs') . ic_flag,
+ \ 'nW' . (at_begin ? '' : 'b'),
+ \ '',
+ \ '',
+ \ timeout,
+ \ )
+ if opposite != [0, 0]
+ let pair_before.line = opposite[0]
+ let pair_before.col = opposite[1]
+ call extend(pair_after, pair_before, 'keep')
+ call s:substitute(
+ \ pair_before,
+ \ pair_after,
+ \ '-',
+ \ a:params.items,
+ \ s:cascade_options_for_callback(options),
+ \ )
+ endif
+endfunction "}}}
+
+
+function! s:restrict_cursor(params) "{{{
+ let before = a:params.before
+ let after = a:params.after
+ let pos = s:getpos()
+ let end_col = before.col + strlen(after.text) - 1
+ if a:params.class_name == 'v' || (after.text =~ '\W' && g:cycle_auto_visual)
+ call cursor(before.line, before.col)
+ normal! v
+ call cursor(after.line, end_col)
+ elseif after.line > before.line || end_col < pos.col
+ call cursor(after.line, end_col)
+ endif
+endfunction "}}}
+
+
+function! s:parse_callback_options(options) "{{{
+ let options = a:options
+ let callbacks = {
+ \ 'before_sub': [],
+ \ 'after_sub': [],
+ \ }
+
+ if get(options, s:OPTIONS.restrict_cursor)
+ call add(callbacks.after_sub, function('s:restrict_cursor'))
+ endif
+
+ if get(options, s:OPTIONS.sub_tag)
+ call add(callbacks.after_sub, function('s:sub_tag_pair'))
+ endif
+
+ if get(options, s:OPTIONS.sub_pair)
+ call add(callbacks.after_sub, function('s:sub_pair'))
+ endif
+
+ return callbacks
+endfunction "}}}
+
+
+function! s:cascade_options_for_callback(options, ...) "{{{
+ let extras = a:0 ? a:1 : {}
+ let filtered = filter(
+ \ deepcopy(a:options),
+ \ "index([s:OPTIONS.match_case, s:OPTIONS.hard_case], v:key) >= 0"
+ \ )
+ return extend(filtered, extras)
+endfunction "}}}
+
+" }}} Optional Callbacks
+
+
+" Utils: {{{
+
+function! s:escape_pattern(pattern) "{{{
+ return escape(a:pattern, '.*~\[^$')
+endfunction "}}}
+
+
+function! s:escape_sub_expr(pattern) "{{{
+ return escape(a:pattern, '~\&')
+endfunction "}}}
+
+
+function! s:text_transform(before, after, options) "{{{
+ let text = a:after
+
+ if type(get(a:options, s:OPTIONS.regex)) == type('')
+ let text = matchstr(
+ \ a:after,
+ \ get(a:options, s:OPTIONS.regex),
+ \ )
+ endif
+
+ if !get(a:options, s:OPTIONS.hard_case)
+ let text = s:imitate_case(text, a:before)
+ endif
+
+ return text
+endfunction "}}}
+
+
+function! s:imitate_case(text, reference) "{{{
+ if a:reference =~# '^\u*$'
+ return toupper(a:text)
+ elseif a:reference =~# '^\U*$'
+ return tolower(a:text)
+ else
+ let uppers = substitute(a:reference, '\U', '0', 'g')
+ let new_text = tolower(a:text)
+ while uppers !~ '^0\+$'
+ let index = match(uppers, '[^0]')
+ if len(new_text) < index
+ break
+ endif
+ let new_text = substitute(new_text, '\%' . (index + 1) . 'c[a-z]', toupper(new_text[index]), '')
+ let uppers = substitute(uppers, '\%' . (index + 1) . 'c.', '0', '')
+ endwhile
+ return new_text
+ endif
+endfunction "}}}
+
+
+function! s:save_reg(name) "{{{
+ let s:save_reg = [getreg(a:name), getregtype(a:name)]
+endfunction "}}}
+
+
+function! s:restore_reg(name) "{{{
+ if exists('s:save_reg')
+ call setreg(a:name, s:save_reg[0], s:save_reg[1])
+ endif
+endfunction "}}}
+
+" }}} Utils
View
372 doc/cycle.txt
@@ -0,0 +1,372 @@
+*cycle.txt* Cycle text to predefined candidates
+
+Version: 0.1.0
+Author: bootleq <bootleq@gmail.com>
+License: Public Domain
+Repository: https://github.com/bootleq/vim-cycle
+
+==============================================================================
+CONTENTS *cycle-contents*
+
+ Introduction .......................... |cycle-introduction|
+ Usage ................................. |cycle-usage|
+ Configuration ......................... |cycle-configuration|
+ General options ..................... |cycle-general-options|
+ Define groups ....................... |cycle-define-groups|
+ Group options ........................ |cycle-group-options|
+ Examples ............................ |cycle-examples|
+ Limitations ........................... |cycle-limitations|
+ Credits ............................... |cycle-credits|
+
+==============================================================================
+INTRODUCTION *cycle-introduction*
+
+*cycle* takes text near cursor, replace it with predefined alternates. Save
+your time from changing relative words like true/false, Yes/No, and so on.
+
+==============================================================================
+USAGE *cycle-usage*
+
+ *<Plug>CycleNext*
+ *<Plug>CyclePrev*
+Define key mappings for |<Plug>CycleNext| in |Normal| and |Visual| modes: >
+ nmap <silent> <Leader>a <Plug>CycleNext
+ vmap <silent> <Leader>a <Plug>CycleNext
+< to replace text-under-cursor or visual-selected-text with their alternates.
+(this plugin defines <Leader>a as default)
+
+You can add [count] to pick candidate at further index, instead of next ONE.
+You can map |<Plug>CyclePrev| (Prev, not Next) to cycle in reverse direction.
+
+For example, with items ["One", "Two", "Three"]
+and your custom mapping is <C-A> for CycleNext, <C-X> for CyclePrev,
+where the cursor is under "One", then
+
+ keys you type result in ~
+ ------------- --------- ~
+ <C-A> Two
+ 2<C-A> Three
+ <C-X> Three
+ 4<C-X> Three
+
+This plugin has a very small set of preset groups (says, yes/no is a group).
+It's recommended to define groups by yourself, see |cycle-define-groups|.
+
+There are some special features beside ordinary text replacing, which are
+controlled by group options, see |cycle-group-options|.
+
+You can use |.| to repeat last cycle if you have one of the following "repeat"
+plugins installed:
+- https://github.com/kana/vim-repeat
+- https://github.com/tpope/vim-repeat
+
+==============================================================================
+CONFIGURATION *cycle-configuration*
+
+------------------------------------------------------------------------------
+General options *cycle-general-options*
+
+ *g:loaded_cycle*
+g:loaded_cycle (default: none) ~
+
+ Set to 1 to prevent this plugin from being loaded.
+
+ *g:cycle_default_groups*
+g:cycle_default_groups (default: none) ~
+
+ This is a List for setting custom default groups. If exists, no groups will
+ be set by plugin itself. See |cycle-define-groups-practice|.
+
+ *g:cycle_default_groups_for_{filetype}*
+g:cycle_default_groups_for_{filetype} ~
+ (default: none) ~
+
+ Like |g:cycle_default_groups| but is for buffer-scoped |b:cycle_groups|, and
+ is loaded when 'filetype' changes.
+ The {filetype} part should be replaced with real type string, e.g.: >
+ let g:cycle_default_groups_for_ruby = []
+< is for filetype "ruby".
+
+ *g:cycle_no_mappings*
+g:cycle_no_mappings (default: 0) ~
+
+ Set to 1 to disable default key mappings.
+ List of defaults:
+
+ mode {lhs} {rhs} ~
+ ---- --------- --------------- ~
+ n <Leader>a <Plug>CycleNext
+ v <Leader>a <Plug>CycleNext
+
+ *g:cycle_max_conflict*
+g:cycle_max_conflict (default: 1) ~
+
+ When there are more than 1 alternates (in different groups) for current
+ text, you are prompted to select which is the wanted one.
+
+ When the number of conflicts is more than this setting, no prompt will be
+ shown, cycle aborts with a message instead.
+
+ The default value is 1, means no conflicts will be handled. This improve
+ some performance since no extra search is needed after first found.
+ And it's recommended to use buffer-scoped groups to prevent conflicts, see
+ |cycle-define-groups-practice|.
+
+ *g:cycle_auto_visual*
+g:cycle_auto_visual (default: 0) ~
+
+ When starting a cycle in normal mode, and the result text is not a keyword
+ (for example, it contains whitespace, see 'iskeyword'), you might want to
+ select the new text with visual mode. Set this option to 1 to enable it.
+
+ *g:cycle_phased_search*
+g:cycle_phased_search (default: 1) ~
+
+ When starting a cycle, it might not obviously know which text should be
+ cycled. In most cases it's exactly the |<cword>| under cursor, however, for
+ example, to cycle "Hello World", we must visual-select it because it's not a
+ <cword>.
+
+ When this option is on, the search can smartly perform with multiple phases.
+ For example, while searching of <cword> in "foo_with_bar" fails, some
+ secondary search can find the "with" and return intended "foo_without_bar"
+ as a result.
+
+------------------------------------------------------------------------------
+Define groups *cycle-define-groups*
+
+To start quickly, just read |cycle-define-groups-practice|.
+
+ *g:cycle_groups*
+ *b:cycle_groups*
+All candidates are defined as groups.
+The structure looks like:
+>
+ g:cycle_groups = [ | => groups, scoped by global/buffer
+ { | =>
+ 'items': ['foo', 'bar'], | =>
+ 'options': {'hard_case': 1}, | => a group
+ }, | =>
+ ], |
+<
+when there exists |b:cycle_groups|, it takes higher priority than the global
+one. |g:var| |b:var|
+
+You should not define cycle_groups directly. Use |cycle#add_group()| is the
+prefered way.
+
+ *cycle-default-groups*
+Without any configuration, the default preset groups is: >
+ call cycle#add_groups([
+ \ [['true', 'false']],
+ \ [['yes', 'no']],
+ \ [['on', 'off']],
+ \ [['0', '1']],
+ \ [['+', '-']],
+ \ [['>', '<']],
+ \ ])
+<
+ *cycle#add_group()*
+A group consists with "items" and optional "options".
+To add a group, use |cycle#add_group()| with any of below forms >
+ cycle#add_group(items)
+ cycle#add_group(items, options)
+ cycle#add_group(items, option, ..., option_can_be_dictionary_or_string)
+ cycle#add_group(group)
+< for example: >
+ call cycle#add_group(['Yes', 'No'])
+ call cycle#add_group(['Yes', 'No'], {'match_case': 1})
+ call cycle#add_group(['Yes', 'No'], 'match_case', {'hard_case': 1})
+ call cycle#add_group([['Yes', 'No'], 'match_case'])
+<
+ *cycle#add_b_group()*
+Use cycle#add_b_group() to add a group into b: scoped. |b:cycle_groups|
+
+ *cycle#add_groups()*
+ *cycle#add_b_groups()*
+To add multiple groups at the same time, there are shortcut version of adding
+functions. You have to wrap items and options as a List, for example: >
+ call cycle#add_groups([
+ \ [['true', 'false']],
+ \ [['Yes', 'No'], 'match_case'],
+ \ ])
+<
+ *cycle#reset_b_groups()*
+This clears |b:cycle_groups|.
+Also accepts a parameter same as |cycle#add_b_groups| to set new groups.
+
+ *cycle#reset_b_groups_by_filetype()*
+Reset |b:cycle_groups| with settings subject to current 'filetype', requires
+variable |g:cycle_default_groups_for_{filetype}| preset.
+
+ *cycle-define-groups-practice*
+The recommended way to define groups is setting |g:cycle_default_groups| and
+|g:cycle_default_groups_for_{filetype}| in your vimrc, this plugin will find
+and applied them behind the scene.
+See |cycle-config-examples|.
+
+------------------------------------------------------------------------------
+Group options *cycle-group-options*
+
+A group option is a Dictionary with key-value pair. While for convenience,
+in |cycle#add_group()| and similar functions, you can set it as a string (by
+its key), the option's default value will be used.
+
+By default no options are set.
+
+"name" String (default: none) ~
+
+ Name of the group.
+ Will be displayed in conflict prompt, see |g:cycle_max_conflict|.
+
+"match_case" Number (default: 1) ~
+
+ By default, searching of group items is case-insensitive. Thus "yes" and
+ "Yes" have identical effect.
+ Set this option other than 0 to enforce case-sensitive search.
+
+"hard_case" Number (default: 1) ~
+
+ By default, case in original text will be copied to resulting text. Thus
+ "friDAY" will become "satURDay".
+ Set this option other than 0 to keep resulting text with the same case as
+ they were defined.
+
+"sub_tag" Number (default: 1) ~
+
+ When editing a xml tag, opening and closing parts can be substituted
+ together: >
+ <EM>example</EM> => <STRONG>example</STRONG>
+< Set this option other then 0 to enable it.
+
+"sub_pair" Number (default: 1) ~
+
+ When editing special pairs (e.g.: {}, <>), opening and closing parts can be
+ substituted together: >
+ {example} => <example>
+< You have to set this option other than 0 and define "end_with" or
+ "start_with" option to recognize what the opposite part is.
+ Example: >
+ call cycle#add_group(
+ \ [ '{', '<' ], 'sub_pair', {'end_with': [ '}', '>' ]}
+ \ )
+< It's recommended to use "sub_pairs" other than this option.
+
+"end_with" List (default: none) ~
+"begin_with" List (default: none) ~
+
+ Used with "sub_pair" option, to define opposite pairs for original text.
+
+"sub_pairs" String (default: ':') ~
+
+ This is a shortcut to set "sub_pair", "end_with" and "begin_with" options at
+ the same time. Each group item will be split with this value into "begin"
+ and "end" parts.
+ Example: >
+ call cycle#add_group(
+ \ [ '{:}', '<:>' ], 'sub_pairs'
+ \ )
+< Internally, this group will be translated into two groups, just identical
+ with original 'sub_pair' version.
+
+"before_sub" (List) ~
+"after_sub" (List) ~
+"restrict_cursor" (Number) ~
+
+ Internal usage only, don't set them.
+
+------------------------------------------------------------------------------
+Configuration examples: *cycle-config-examples*
+
+General options:
+>
+ let g:cycle_no_mappings = 1
+ let g:cycle_max_conflict = 1
+ let g:cycle_phased_search = 1
+ nmap <silent> <LocalLeader>a <Plug>CycleNext
+ vmap <silent> <LocalLeader>a <Plug>CycleNext
+<
+
+Default global groups:
+>
+ let g:cycle_default_groups = [
+ \ [['true', 'false']],
+ \ [['yes', 'no']],
+ \ [['on', 'off']],
+ \ [['and', 'or']],
+ \ [['+', '-']],
+ \ [['>', '<']],
+ \ [['"', "'"]],
+ \ [['==', '!=']],
+ \ [['0', '1']],
+ \ [['是', '否']],
+ \ [["in", "out"]],
+ \ [["min", "max"]],
+ \ [["get", "post"]],
+ \ [["to", "from"]],
+ \ [["read", "write"]],
+ \ [["only", "except"]],
+ \ [['with', 'without']],
+ \ [["exclude", "include"]],
+ \ [["asc", "desc"]],
+ \ [['{:}', '[:]', '(:)'], 'sub_pairs'],
+ \ [['(:)', '「:」', '『:』'], 'sub_pairs'],
+ \ [['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
+ \ 'Friday', 'Saturday'], 'hard_case', {'name': 'Days'}],
+ \ ]
+
+ " ruby, rails
+ let g:cycle_default_groups += [
+ \ [["if", "unless"]],
+ \ [["blank", "present"]],
+ \ [["while", "until"]],
+ \ [["begin", "end"]],
+ \ [["foreign_key", "primary_key"]],
+ \ [["inspect", "to_yaml"]],
+ \ [["add_column", "remove_column"]],
+ \ ]
+
+ " CSS
+ let g:cycle_default_groups += [
+ \ [["none", "block"]],
+ \ [["show", "hide"]],
+ \ [["left", "right"]],
+ \ [["top", "bottom"]],
+ \ [["margin", "padding"]],
+ \ [["before", "after"]],
+ \ [["absolute", "relative"]],
+ \ [["first", "last"]],
+ \ ]
+
+ " HTML
+ let g:cycle_default_groups += [
+ \ [['h1', 'h2', 'h3'], 'sub_tag'],
+ \ [['ul', 'ol'], 'sub_tag'],
+ \ [['em', 'strong', 'i', 's', 'del', 'small'], 'sub_tag'],
+ \ ]
+<
+
+Filetype specified groups:
+>
+ " ruby only
+ let g:cycle_default_groups_for_ruby = [
+ \ [['accessible', 'protected']],
+ \ [['stylesheet_link_tag ', 'javascript_include_tag ']],
+ \ ]
+<
+
+==============================================================================
+LIMITATIONS *cycle-limitations*
+
+- Can't handle multi line text.
+
+==============================================================================
+CREDITS *cycle-credits*
+
+- Michael Brown ~
+ Author of SwapIt.vim: https://github.com/mjbrownie/swapit
+ SwapIt originally provides lots of awesome features include visual
+ multi-words, xml tag pairs, omni-completion cycling.
+
+==============================================================================
+vim:tw=78:fo=tcroq2mM:et:sts=2:sw=2:ft=help:norl:
View
31 doc/tags
@@ -131,6 +131,8 @@
<Leader>n mark.txt /*<Leader>n*
<Leader>r mark.txt /*<Leader>r*
<Leader>star mark.txt /*<Leader>star*
+<Plug>CycleNext cycle.txt /*<Plug>CycleNext*
+<Plug>CyclePrev cycle.txt /*<Plug>CyclePrev*
Align-copyright Align.txt /*Align-copyright*
AnsiEsc AnsiEsc.txt /*AnsiEsc*
AnsiEsc-contents AnsiEsc.txt /*AnsiEsc-contents*
@@ -412,6 +414,7 @@ alignmap-tt Align.txt /*alignmap-tt*
alignmap-t~ Align.txt /*alignmap-t~*
alignmaps Align.txt /*alignmaps*
alignusage Align.txt /*alignusage*
+b:cycle_groups cycle.txt /*b:cycle_groups*
b:match_col matchit.txt /*b:match_col*
b:match_debug matchit.txt /*b:match_debug*
b:match_ignorecase matchit.txt /*b:match_ignorecase*
@@ -457,6 +460,26 @@ cecutil-swp cecutil.txt /*cecutil-swp*
cecutil.txt cecutil.txt /*cecutil.txt*
cr abolish.txt /*cr*
cs surround.txt /*cs*
+cycle cycle.txt /*cycle*
+cycle#add_b_group() cycle.txt /*cycle#add_b_group()*
+cycle#add_b_groups() cycle.txt /*cycle#add_b_groups()*
+cycle#add_group() cycle.txt /*cycle#add_group()*
+cycle#add_groups() cycle.txt /*cycle#add_groups()*
+cycle#reset_b_groups() cycle.txt /*cycle#reset_b_groups()*
+cycle#reset_b_groups_by_filetype() cycle.txt /*cycle#reset_b_groups_by_filetype()*
+cycle-config-examples cycle.txt /*cycle-config-examples*
+cycle-configuration cycle.txt /*cycle-configuration*
+cycle-contents cycle.txt /*cycle-contents*
+cycle-credits cycle.txt /*cycle-credits*
+cycle-default-groups cycle.txt /*cycle-default-groups*
+cycle-define-groups cycle.txt /*cycle-define-groups*
+cycle-define-groups-practice cycle.txt /*cycle-define-groups-practice*
+cycle-general-options cycle.txt /*cycle-general-options*
+cycle-group-options cycle.txt /*cycle-group-options*
+cycle-introduction cycle.txt /*cycle-introduction*
+cycle-limitations cycle.txt /*cycle-limitations*
+cycle-usage cycle.txt /*cycle-usage*
+cycle.txt cycle.txt /*cycle.txt*
dav pi_netrw.txt /*dav*
davs pi_netrw.txt /*davs*
ds surround.txt /*ds*
@@ -523,6 +546,14 @@ g:bufExplorerSortBy bufexplorer.txt /*g:bufExplorerSortBy*
g:bufExplorerSplitBelow bufexplorer.txt /*g:bufExplorerSplitBelow*
g:bufExplorerSplitOutPathName bufexplorer.txt /*g:bufExplorerSplitOutPathName*
g:bufExplorerSplitRight bufexplorer.txt /*g:bufExplorerSplitRight*
+g:cycle_auto_visual cycle.txt /*g:cycle_auto_visual*
+g:cycle_default_groups cycle.txt /*g:cycle_default_groups*
+g:cycle_default_groups_for_{filetype} cycle.txt /*g:cycle_default_groups_for_{filetype}*
+g:cycle_groups cycle.txt /*g:cycle_groups*
+g:cycle_max_conflict cycle.txt /*g:cycle_max_conflict*
+g:cycle_no_mappings cycle.txt /*g:cycle_no_mappings*
+g:cycle_phased_search cycle.txt /*g:cycle_phased_search*
+g:loaded_cycle cycle.txt /*g:loaded_cycle*
g:manpageview_K_EXT manpageview.txt /*g:manpageview_K_EXT*
g:manpageview_iconv manpageview.txt /*g:manpageview_iconv*
g:manpageview_init_EXT manpageview.txt /*g:manpageview_init_EXT*
View
73 plugin/cycle.vim
@@ -0,0 +1,73 @@
+if exists('g:loaded_cycle')
+ finish
+endif
+let g:loaded_cycle = 1
+let s:save_cpo = &cpoptions
+set cpoptions&vim
+
+
+" Default Options: {{{
+
+function! s:set_default(name, value)
+ if !exists(a:name)
+ execute "let " . a:name . " = " . string(a:value)
+ endif
+endfunction
+
+call s:set_default('g:cycle_no_mappings', 0)
+call s:set_default('g:cycle_max_conflict', 1)
+call s:set_default('g:cycle_auto_visual', 0)
+call s:set_default('g:cycle_phased_search', 0)
+
+if !exists('g:cycle_default_groups')
+ call cycle#add_groups([
+ \ [['true', 'false']],
+ \ [['yes', 'no']],
+ \ [['on', 'off']],
+ \ [['0', '1']],
+ \ [['+', '-']],
+ \ [['>', '<']],
+ \ ])
+endif
+
+if exists('g:cycle_default_groups')
+ call cycle#add_groups(g:cycle_default_groups)
+endif
+
+" }}} Default Options
+
+
+" Interface: {{{
+
+nnoremap <silent> <Plug>CycleNext :<C-U>call Cycle('w', 1, v:count1)<CR>
+nnoremap <silent> <Plug>CyclePrev :<C-U>call Cycle('w', -1, v:count1)<CR>
+vnoremap <silent> <Plug>CycleNext :<C-U>call Cycle('v', 1, v:count1)<CR>
+vnoremap <silent> <Plug>CyclePrev :<C-U>call Cycle('v', -1, v:count1)<CR>
+
+if !g:cycle_no_mappings
+ silent! nmap <silent> <unique> <Leader>a <Plug>CycleNext
+ silent! vmap <silent> <unique> <Leader>a <Plug>CycleNext
+endif
+
+function! Cycle(class_name, direction, count)
+ call cycle#new(a:class_name, a:direction, a:count)
+endfunction
+
+augroup cycle
+ autocmd!
+ autocmd FileType * call cycle#reset_b_groups_by_filetype()
+augroup END
+
+" }}} Interface
+
+
+" Finish: {{{
+
+let &cpoptions = s:save_cpo
+unlet s:save_cpo
+
+" }}} Finish
+
+
+" modeline {{{
+" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker
View
31 vimrc
@@ -714,6 +714,37 @@ command MusicSelect runtime so/musicselect.vim
command -nargs=1 -range -complete=customlist,Lilydjwg_Align_complete LA <line1>,<line2>call Lilydjwg_Align("<args>")
command -range=% Paste <line1>,<line2>w !curl -F 'vimcn=<-' http://p.vim-cn.com
" 其它命令[[[1
+" cycle[[[2
+" https://github.com/lilydjwg/vim-cycle
+nmap <silent> <C-X> <Plug>CyclePrev
+vmap <silent> <C-X> <Plug>CyclePrev
+nmap <silent> <C-A> <Plug>CycleNext
+vmap <silent> <C-A> <Plug>CycleNext
+let g:cycle_no_mappings = 1
+let g:cycle_default_groups = [
+ \ [['true', 'false']],
+ \ [['yes', 'no']],
+ \ [['on', 'off']],
+ \ [['+', '-']],
+ \ [['>', '<']],
+ \ [['==', '!=']],
+ \ [['0', '1']],
+ \ [['是', '否']],
+ \ [["in", "out"]],
+ \ [["min", "max"]],
+ \ [["get", "post"]],
+ \ [["to", "from"]],
+ \ [["read", "write"]],
+ \ [['with', 'without']],
+ \ [["exclude", "include"]],
+ \ [["asc", "desc"]],
+ \ [["next", "prev"]],
+ \ [["encode", "decode"]],
+ \ [['{:}', '[:]', '(:)'], 'sub_pairs'],
+ \ [['(:)', '「:」', '『:』'], 'sub_pairs'],
+ \ [['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
+ \ 'Friday', 'Saturday'], 'hard_case', {'name': 'Days'}],
+ \ ]
" Erlang[[[2
let g:erlangHighlightBif = 1
let g:erlangFold = 1
Please sign in to comment.
Something went wrong with that request. Please try again.