Created
April 16, 2018 16:04
-
-
Save icarofreire/43e0f25bcb7d4f19e7507b1890c64de6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | |
| " StupidEasyMotion - Vim motions on speed! | |
| " | |
| " Author: Kim Silkebækken <[email protected]> | |
| " Source repository: https://github.com/Lokaltog/vim-easymotion | |
| " Default configuration functions {{{ | |
| function! StupidEasyMotion_InitOptions(options) " {{{ | |
| for [key, value] in items(a:options) | |
| if ! exists('g:StupidEasyMotion_' . key) | |
| exec 'let g:StupidEasyMotion_' . key . ' = ' . string(value) | |
| endif | |
| endfor | |
| endfunction " }}} | |
| function! StupidEasyMotion_InitHL(group, colors) " {{{ | |
| let group_default = a:group . 'Default' | |
| " Prepare highlighting variables | |
| let guihl = printf('guibg=%s guifg=%s gui=%s', a:colors.gui[0], a:colors.gui[1], a:colors.gui[2]) | |
| if !exists('g:CSApprox_loaded') | |
| let ctermhl = &t_Co == 256 | |
| \ ? printf('ctermbg=%s ctermfg=%s cterm=%s', a:colors.cterm256[0], a:colors.cterm256[1], a:colors.cterm256[2]) | |
| \ : printf('ctermbg=%s ctermfg=%s cterm=%s', a:colors.cterm[0], a:colors.cterm[1], a:colors.cterm[2]) | |
| else | |
| let ctermhl = '' | |
| endif | |
| " Create default highlighting group | |
| execute printf('hi default %s %s %s', group_default, guihl, ctermhl) | |
| " Check if the hl group exists | |
| if hlexists(a:group) | |
| redir => hlstatus | exec 'silent hi ' . a:group | redir END | |
| " Return if the group isn't cleared | |
| if hlstatus !~ 'cleared' | |
| return | |
| endif | |
| endif | |
| " No colors are defined for this group, link to defaults | |
| execute printf('hi default link %s %s', a:group, group_default) | |
| endfunction " }}} | |
| function! StupidEasyMotion_InitMappings(motions) "{{{ | |
| for motion in keys(a:motions) | |
| call StupidEasyMotion_InitOptions({ 'mapping_' . motion : g:StupidEasyMotion_leader_key . motion }) | |
| endfor | |
| if g:StupidEasyMotion_do_mapping | |
| for [motion, fn] in items(a:motions) | |
| if empty(g:StupidEasyMotion_mapping_{motion}) | |
| continue | |
| endif | |
| silent exec 'nnoremap <silent> ' . g:StupidEasyMotion_mapping_{motion} . ' :call StupidEasyMotion_' . fn.name . '(0, ' . fn.dir . ')<CR>' | |
| silent exec 'onoremap <silent> ' . g:StupidEasyMotion_mapping_{motion} . ' :call StupidEasyMotion_' . fn.name . '(0, ' . fn.dir . ')<CR>' | |
| silent exec 'vnoremap <silent> ' . g:StupidEasyMotion_mapping_{motion} . ' :<C-U>call StupidEasyMotion_' . fn.name . '(1, ' . fn.dir . ')<CR>' | |
| endfor | |
| endif | |
| endfunction "}}} | |
| " }}} | |
| " Motion functions {{{ | |
| function! StupidEasyMotion_WB(visualmode, direction) " {{{ | |
| call StupidEasyMotion('\(\<.\|^$\)', a:direction, a:visualmode ? visualmode() : '', '') | |
| endfunction " }}} | |
| function! StupidEasyMotion_WBW(visualmode, direction) " {{{ | |
| call StupidEasyMotion('\(\(^\|\s\)\@<=\S\|^$\)', a:direction, a:visualmode ? visualmode() : '', '') | |
| endfunction " }}} | |
| function! StupidEasyMotion_F(visualmode, direction) " {{{ | |
| let char = s:GetSearchChar(a:visualmode) | |
| if empty(char) | |
| return | |
| endif | |
| let re = '\C' . escape(char, '.$^~') | |
| call StupidEasyMotion(re, a:direction, a:visualmode ? visualmode() : '', mode(1)) | |
| endfunction " }}} | |
| " }}} | |
| " Helper functions {{{ | |
| function! s:Message(message) " {{{ | |
| echo 'StupidEasyMotion: ' . a:message | |
| endfunction " }}} | |
| function! s:Prompt(message) " {{{ | |
| echohl Question | |
| echo a:message . ': ' | |
| echohl None | |
| endfunction " }}} | |
| function! s:VarReset(var, ...) " {{{ | |
| if ! exists('s:var_reset') | |
| let s:var_reset = {} | |
| endif | |
| let buf = bufname("") | |
| if a:0 == 0 && has_key(s:var_reset, a:var) | |
| " Reset var to original value | |
| call setbufvar(buf, a:var, s:var_reset[a:var]) | |
| elseif a:0 == 1 | |
| let new_value = a:0 == 1 ? a:1 : '' | |
| " Store original value | |
| let s:var_reset[a:var] = getbufvar(buf, a:var) | |
| " Set new var value | |
| call setbufvar(buf, a:var, new_value) | |
| endif | |
| endfunction " }}} | |
| function! s:SetLines(lines, key) " {{{ | |
| try | |
| " Try to join changes with previous undo block | |
| undojoin | |
| catch | |
| endtry | |
| for [line_num, line] in a:lines | |
| call setline(line_num, line[a:key]) | |
| endfor | |
| endfunction " }}} | |
| function! s:GetChar() " {{{ | |
| let char = getchar() | |
| if char == 27 | |
| " Escape key pressed | |
| redraw | |
| call s:Message('Cancelled') | |
| return '' | |
| endif | |
| return nr2char(char) | |
| endfunction " }}} | |
| function! s:GetSearchChar(visualmode) " {{{ | |
| call s:Prompt('Search for character') | |
| let char = s:GetChar() | |
| " Check that we have an input char | |
| if empty(char) | |
| " Restore selection | |
| if ! empty(a:visualmode) | |
| silent exec 'normal! gv' | |
| endif | |
| return '' | |
| endif | |
| return char | |
| endfunction " }}} | |
| " }}} | |
| " Grouping algorithms {{{ | |
| let s:grouping_algorithms = { | |
| \ 1: 'SCTree' | |
| \ , 2: 'Original' | |
| \ } | |
| " Single-key/closest target priority tree {{{ | |
| " This algorithm tries to assign one-key jumps to all the targets closest to the cursor. | |
| " It works recursively and will work correctly with as few keys as two. | |
| function! s:GroupingAlgorithmSCTree(targets, keys) | |
| " Prepare variables for working | |
| let targets_len = len(a:targets) | |
| let keys_len = len(a:keys) | |
| let groups = {} | |
| let keys = reverse(copy(a:keys)) | |
| " Semi-recursively count targets {{{ | |
| " We need to know exactly how many child nodes (targets) this branch will have | |
| " in order to pass the correct amount of targets to the recursive function. | |
| " Prepare sorted target count list {{{ | |
| " This is horrible, I know. But dicts aren't sorted in vim, so we need to | |
| " work around that. That is done by having one sorted list with key counts, | |
| " and a dict which connects the key with the keys_count list. | |
| let keys_count = [] | |
| let keys_count_keys = {} | |
| let i = 0 | |
| for key in keys | |
| call add(keys_count, 0) | |
| let keys_count_keys[key] = i | |
| let i += 1 | |
| endfor | |
| " }}} | |
| let targets_left = targets_len | |
| let level = 0 | |
| let i = 0 | |
| while targets_left > 0 | |
| " Calculate the amount of child nodes based on the current level | |
| let childs_len = (level == 0 ? 1 : (keys_len - 1) ) | |
| for key in keys | |
| " Add child node count to the keys_count array | |
| let keys_count[keys_count_keys[key]] += childs_len | |
| " Subtract the child node count | |
| let targets_left -= childs_len | |
| if targets_left <= 0 | |
| " Subtract the targets left if we added too many too | |
| " many child nodes to the key count | |
| let keys_count[keys_count_keys[key]] += targets_left | |
| break | |
| endif | |
| let i += 1 | |
| endfor | |
| let level += 1 | |
| endwhile | |
| " }}} | |
| " Create group tree {{{ | |
| let i = 0 | |
| let key = 0 | |
| call reverse(keys_count) | |
| for key_count in keys_count | |
| if key_count > 1 | |
| " We need to create a subgroup | |
| " Recurse one level deeper | |
| let groups[a:keys[key]] = s:GroupingAlgorithmSCTree(a:targets[i : i + key_count - 1], a:keys) | |
| elseif key_count == 1 | |
| " Assign single target key | |
| let groups[a:keys[key]] = a:targets[i] | |
| else | |
| " No target | |
| continue | |
| endif | |
| let key += 1 | |
| let i += key_count | |
| endfor | |
| " }}} | |
| " Finally! | |
| return groups | |
| endfunction | |
| " }}} | |
| " Original {{{ | |
| function! s:GroupingAlgorithmOriginal(targets, keys) | |
| " Split targets into groups (1 level) | |
| let targets_len = len(a:targets) | |
| let keys_len = len(a:keys) | |
| let groups = {} | |
| let i = 0 | |
| let root_group = 0 | |
| try | |
| while root_group < targets_len | |
| let groups[a:keys[root_group]] = {} | |
| for key in a:keys | |
| let groups[a:keys[root_group]][key] = a:targets[i] | |
| let i += 1 | |
| endfor | |
| let root_group += 1 | |
| endwhile | |
| catch | endtry | |
| " Flatten the group array | |
| if len(groups) == 1 | |
| let groups = groups[a:keys[0]] | |
| endif | |
| return groups | |
| endfunction | |
| " }}} | |
| " Coord/key dictionary creation {{{ | |
| function! s:CreateCoordKeyDict(groups, ...) | |
| " Dict structure: | |
| " 1,2 : a | |
| " 2,3 : b | |
| let sort_list = [] | |
| let coord_keys = {} | |
| let group_key = a:0 == 1 ? a:1 : '' | |
| for [key, item] in items(a:groups) | |
| let key = ( ! empty(group_key) ? group_key : key) | |
| if type(item) == 3 | |
| " Destination coords | |
| " The key needs to be zero-padded in order to | |
| " sort correctly | |
| let dict_key = printf('%05d,%05d', item[0], item[1]) | |
| let coord_keys[dict_key] = key | |
| " We need a sorting list to loop correctly in | |
| " PromptUser, dicts are unsorted | |
| call add(sort_list, dict_key) | |
| else | |
| " Item is a dict (has children) | |
| let coord_key_dict = s:CreateCoordKeyDict(item, key) | |
| " Make sure to extend both the sort list and the | |
| " coord key dict | |
| call extend(sort_list, coord_key_dict[0]) | |
| call extend(coord_keys, coord_key_dict[1]) | |
| endif | |
| unlet item | |
| endfor | |
| return [sort_list, coord_keys] | |
| endfunction | |
| " }}} | |
| " }}} | |
| "{{{ function! LinesInRange(lines_prev, lines_next) | |
| function! LinesInRange(lines_prev, lines_next) | |
| let all_lines = filter( range(line('w0'), line('w$')), 'foldclosed(v:val) == -1' ) | |
| let current = index(all_lines, line('.')) | |
| let lines_prev = a:lines_prev == -1 ? current : a:lines_prev | |
| let lines_next = a:lines_next == -1 ? len(all_lines) : a:lines_next | |
| let lines_prev_i = max( [0, current - lines_prev] ) | |
| let lines_next_i = min( [len(all_lines), current + lines_next] ) | |
| return all_lines[ lines_prev_i : lines_next_i ] | |
| endfunction | |
| "}}} | |
| "{{{ function! FindTargets(re, line_numbers) | |
| function! FindTargets(re, line_numbers) | |
| let targets = [] | |
| for l in a:line_numbers | |
| let n = 1 | |
| let match_start = match(getline(l), a:re, 0, 1) | |
| while match_start != -1 | |
| call add(targets, [l, match_start + 1]) | |
| let n += 1 | |
| let match_start = match(getline(l), a:re, 0, n) | |
| endwhile | |
| endfor | |
| return targets | |
| endfunction | |
| "}}} | |
| " Core functions {{{ | |
| function! s:PromptUser(groups) "{{{ | |
| " If only one possible match, jump directly to it {{{ | |
| let group_values = values(a:groups) | |
| if len(group_values) == 1 | |
| redraw | |
| return group_values[0] | |
| endif | |
| " }}} | |
| " Prepare marker lines {{{ | |
| let lines = {} | |
| let hl_coords = [] | |
| let coord_key_dict = s:CreateCoordKeyDict(a:groups) | |
| for dict_key in sort(coord_key_dict[0]) | |
| let target_key = coord_key_dict[1][dict_key] | |
| let [line_num, col_num] = split(dict_key, ',') | |
| let line_num = str2nr(line_num) | |
| let col_num = str2nr(col_num) | |
| " Add original line and marker line | |
| if ! has_key(lines, line_num) | |
| let current_line = getline(line_num) | |
| let lines[line_num] = { 'orig': current_line, 'marker': current_line } | |
| endif | |
| " Compensate for byte difference between marker | |
| " character and target character | |
| " | |
| " This has to be done in order to match the correct | |
| " column; \%c matches the byte column and not display | |
| " column. | |
| let target_char_len = strdisplaywidth(matchstr(lines[line_num]['marker'], '\%' . col_num . 'c.')) | |
| let target_key_len = strdisplaywidth(target_key) | |
| if strlen(lines[line_num]['marker']) > 0 | |
| " Substitute marker character if line length > 0 | |
| let lines[line_num]['marker'] = substitute(lines[line_num]['marker'], '\%' . col_num . 'c.', target_key . repeat(' ', target_char_len - target_key_len), '') | |
| else | |
| " Set the line to the marker character if the line is empty | |
| let lines[line_num]['marker'] = target_key | |
| endif | |
| " Add highlighting coordinates | |
| call add(hl_coords, '\%' . line_num . 'l\%' . col_num . 'c') | |
| endfor | |
| let lines_items = items(lines) | |
| " }}} | |
| " Highlight targets {{{ | |
| let target_hl_id = matchadd(g:StupidEasyMotion_hl_group_target, join(hl_coords, '\|'), 1) | |
| " }}} | |
| try | |
| " Set lines with markers | |
| call s:SetLines(lines_items, 'marker') | |
| redraw | |
| " Get target character {{{ | |
| call s:Prompt('Target key') | |
| let char = s:GetChar() | |
| " }}} | |
| finally | |
| " Restore original lines | |
| call s:SetLines(lines_items, 'orig') | |
| " Un-highlight targets {{{ | |
| if exists('target_hl_id') | |
| call matchdelete(target_hl_id) | |
| endif | |
| " }}} | |
| redraw | |
| endtry | |
| " Check if we have an input char {{{ | |
| if empty(char) | |
| throw 'Cancelled' | |
| endif | |
| " }}} | |
| " Check if the input char is valid {{{ | |
| if ! has_key(a:groups, char) | |
| throw 'Invalid target' | |
| endif | |
| " }}} | |
| let target = a:groups[char] | |
| if type(target) == 3 | |
| " Return target coordinates | |
| return target | |
| else | |
| " Prompt for new target character | |
| return s:PromptUser(target) | |
| endif | |
| endfunction "}}} | |
| function! StupidEasyMotion(regexp, direction, visualmode, mode) " {{{ | |
| let orig_pos = [line('.'), col('.')] | |
| let targets = [] | |
| try | |
| " Reset properties {{{ | |
| call s:VarReset('&scrolloff', 0) | |
| call s:VarReset('&modified', 0) | |
| call s:VarReset('&modifiable', 1) | |
| call s:VarReset('&readonly', 0) | |
| call s:VarReset('&spell', 0) | |
| call s:VarReset('&virtualedit', '') | |
| " }}} | |
| " Find motion targets {{{ | |
| let search_direction = (a:direction == 1 ? 'b' : '') | |
| let search_stopline = line(".") | |
| normal ^ | |
| " Handle the case where the pattern matches the first character | |
| " of the line. We have to handle this case specially because | |
| " allowing searchpos() to match the current character would | |
| " cause an infinite loop (since it would continue to match under | |
| " the cursor forever) | |
| " The 'c' flag lets searchpos() match under the cursor. | |
| "let pos = searchpos(a:regexp, search_direction.'c', search_stopline) | |
| "if pos == [line('.'), col('.')] | |
| " call add(targets, pos) | |
| "endif | |
| "while 1 | |
| "let pos = searchpos(a:regexp, search_direction, search_stopline) | |
| " Reached end of search range | |
| "if pos == [0, 0] | |
| " break | |
| "endif | |
| " Skip folded lines | |
| "if foldclosed(pos[0]) != -1 | |
| " continue | |
| "endif | |
| "call add(targets, pos) | |
| "endwhile | |
| " as posições dos caracteres vão para targets; | |
| " fazer com que pegue todos os respectivos caracteres X exibidos na tela; | |
| let lnums = LinesInRange(-1, -1) | |
| let targets = FindTargets(a:regexp, lnums) | |
| let targets_len = len(targets) | |
| if targets_len == 0 | |
| throw 'No matches' | |
| endif | |
| " }}} | |
| let GroupingFn = function('s:GroupingAlgorithm' . s:grouping_algorithms[g:StupidEasyMotion_grouping]) | |
| let groups = GroupingFn(targets, split(g:StupidEasyMotion_keys, '\zs')) | |
| " Prompt user for target group/character | |
| let coords = s:PromptUser(groups) | |
| " Update selection {{{ | |
| if ! empty(a:visualmode) | |
| keepjumps call cursor(orig_pos[0], orig_pos[1]) | |
| exec 'normal! ' . a:visualmode | |
| endif | |
| " }}} | |
| " Handle operator-pending mode {{{ | |
| if a:mode == 'no' | |
| " This mode requires that we eat one more | |
| " character to the right if we're using | |
| " a forward motion | |
| if a:direction != 1 | |
| let coords[1] += 1 | |
| endif | |
| endif | |
| " }}} | |
| " Update cursor position | |
| call cursor(orig_pos[0], orig_pos[1]) | |
| mark ' | |
| call cursor(coords[0], coords[1]) | |
| call s:Message('Jumping to [' . coords[0] . ', ' . coords[1] . ']') | |
| catch | |
| redraw | |
| " Show exception message | |
| call s:Message(v:exception) | |
| " Restore original cursor position/selection {{{ | |
| if ! empty(a:visualmode) | |
| silent exec 'normal! gv' | |
| else | |
| keepjumps call cursor(orig_pos[0], orig_pos[1]) | |
| endif | |
| " }}} | |
| finally | |
| " Restore properties {{{ | |
| call s:VarReset('&scrolloff') | |
| call s:VarReset('&modified') | |
| call s:VarReset('&modifiable') | |
| call s:VarReset('&readonly') | |
| call s:VarReset('&spell') | |
| call s:VarReset('&virtualedit') | |
| " }}} | |
| " Remove shading {{{ | |
| if g:StupidEasyMotion_do_shade && exists('shade_hl_id') | |
| call matchdelete(shade_hl_id) | |
| endif | |
| " }}} | |
| endtry | |
| endfunction " }}} | |
| " }}} | |
| " vim: fdm=marker:noet:ts=4:sw=4:sts=4 | |
| " StupidEasyMotion - Vim motions on speed! | |
| " | |
| " Author: Kim Silkebækken <[email protected]> | |
| " Source repository: https://github.com/joequery/Stupid-EasyMotion | |
| " Script initialization {{{ | |
| if exists('g:StupidEasyMotion_loaded') || &compatible || version < 702 | |
| finish | |
| endif | |
| let g:StupidEasyMotion_loaded = 1 | |
| " }}} | |
| " Default configuration {{{ | |
| " Default options {{{ | |
| call StupidEasyMotion_InitOptions({ | |
| \ 'leader_key' : '<Leader><Leader>' | |
| \ , 'keys' : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' | |
| \ , 'do_shade' : 1 | |
| \ , 'do_mapping' : 1 | |
| \ , 'grouping' : 1 | |
| \ | |
| \ , 'hl_group_target' : 'StupidEasyMotionTarget' | |
| \ , 'hl_group_shade' : 'StupidEasyMotionShade' | |
| \ }) | |
| " }}} | |
| " Default highlighting {{{ | |
| let s:target_hl_defaults = { | |
| \ 'gui' : ['NONE', 'darkred' , 'bold'] | |
| \ , 'cterm256': ['NONE', '196' , 'bold'] | |
| \ , 'cterm' : ['NONE', 'red' , 'bold'] | |
| \ } | |
| let s:shade_hl_defaults = { | |
| \ 'gui' : ['NONE', 'darkred' , 'NONE'] | |
| \ , 'cterm256': ['NONE', '242' , 'NONE'] | |
| \ , 'cterm' : ['NONE', 'grey' , 'NONE'] | |
| \ } | |
| call StupidEasyMotion_InitHL(g:StupidEasyMotion_hl_group_target, s:target_hl_defaults) | |
| call StupidEasyMotion_InitHL(g:StupidEasyMotion_hl_group_shade, s:shade_hl_defaults) | |
| " Reset highlighting after loading a new color scheme {{{ | |
| augroup StupidEasyMotionInitHL | |
| autocmd! | |
| autocmd ColorScheme * call StupidEasyMotion_InitHL(g:StupidEasyMotion_hl_group_target, s:target_hl_defaults) | |
| autocmd ColorScheme * call StupidEasyMotion_InitHL(g:StupidEasyMotion_hl_group_shade, s:shade_hl_defaults) | |
| augroup end | |
| " }}} | |
| " }}} | |
| " Default key mapping {{{ | |
| call StupidEasyMotion_InitMappings({ | |
| \ 'f' : { 'name': 'F' , 'dir': 0 } | |
| \ , 'w' : { 'name': 'WB' , 'dir': 0 } | |
| \ , 'W' : { 'name': 'WBW', 'dir': 0 } | |
| \ }) | |
| " }}} | |
| " }}} | |
| " vim: fdm=marker:noet:ts=4:sw=4:sts=4 | |
| "formas de executar; | |
| "executar com: call StupidEasyMotion_F('WBW', '1') | |
| "call StupidEasyMotion_F('', '') | |
| "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment