"============================================================================= " highlight.vim --- highlight mode for SpaceVim " Copyright (c) 2016-2023 Wang Shidong & Contributors " Author: Shidong Wang < wsdjeg@outlook.com > " URL: https://spacevim.org " License: GPLv3 "============================================================================= "" " @section Symbol highlighter, plugins-symbol-highlighter " @parentsection plugins " SpaceVim supports highlighting current symbol on demand and add a transient " state to easily navigate and rename these symbols. " " It is also possible to change the range of the navigation on the fly, the " available ranges are: " " 1. buffer: the whole buffer " 2. function: in current function " 3. visible area: in current visible area of the buffer " " The default key binding to Highlight the symbol under the cursor is `SPC s h`. " " Navigation between the highlighted symbols can be done with the commands: " > " Key Bindings | Descriptions " ------------ | ------------------------------------------- " * | highlight current symbol and jump forwards " # | highlight current symbol and jump backwards " SPC s e | start iedit mode on current symbol " SPC s h | highlight current symbol within default range " SPC s H | highlight last symbol within default range " < " In highlight symbol transient state, the following key bindings can be used: " > " Key Bindings | Descriptions " ------------- | ----------------------------------- " e | start iedit mode " n | go to next occurrence " N / p | go to previous occurrence " b | search occurrence in all buffers " / | search occurrence in whole project " | toggle highlight current occurrence " r | change range " R | go to home occurrence " Any other key | leave the navigation transient state " < " TODO: {{{ " e: iedit " d/D: next previous definition " f: search files " s: swoop " }}} " Loading SpaceVim api {{{ let s:VIMH = SpaceVim#api#import('vim#highlight') let s:STRING = SpaceVim#api#import('data#string') let s:CMP = SpaceVim#api#import('vim#compatible') let s:HI = SpaceVim#api#import('vim#highlight') "}}} " init local variable {{{ let s:function_expr = {} let s:hi_range_id = 0 let s:hi_range_index = 0 " }}} " transient_state API func: logo {{{ function! s:range_logo() abort let line = getline(3) let range = s:current_range let index = '[' . (s:index + 1) . '/' . len(s:cursor_stack) . ']' let logo = s:STRING.fill_middle(range . ' ' . index, 30) let begin = stridx(logo, s:current_range) call setline(3, logo . line[30:]) try call matchdelete(s:hi_range_id) call matchdelete(s:hi_range_index) catch endtry let s:hi_range_id = s:CMP.matchaddpos('HiRrange' . s:current_range, [[3, begin, len(s:current_range) + 2]]) let s:hi_range_index = s:CMP.matchaddpos('HiRrangeIndex', [[3, begin + len(s:current_range) + 2, len(index) + 2]]) endfunction " }}} " transient_state API func: init {{{ let s:hi_info = [{ \ 'name' : 'HiPurpleBold', \ 'guibg' : '#d3869b', \ 'guifg' : '#282828', \ 'ctermbg' : '', \ 'ctermfg' : 175, \ 'bold' : 1, \ },{ \ 'name' : 'HiRrangeDisplay', \ 'guibg' : '#458588', \ 'guifg' : '#282828', \ 'ctermbg' : '', \ 'ctermfg' : 175, \ 'bold' : 1, \ },{ \ 'name' : 'HiRrangeBuffer', \ 'guibg' : '#689d6a', \ 'guifg' : '#282828', \ 'ctermbg' : '', \ 'ctermfg' : 175, \ 'bold' : 1, \ },{ \ 'name' : 'HiRrangeFunction', \ 'guibg' : '#d38696', \ 'guifg' : '#282828', \ 'ctermbg' : '', \ 'ctermfg' : 175, \ 'bold' : 1, \ },{ \ 'name' : 'HiRrangeIndex', \ 'guibg' : '#3c3836', \ 'guifg' : '#a89984', \ 'ctermbg' : 237, \ 'ctermfg' : 246, \ 'bold' : 1, \ },{ \ 'name' : 'HiBlueBold', \ 'guibg' : '#83a598', \ 'guifg' : '#282828', \ 'ctermbg' : '', \ 'ctermfg' : 109, \ 'bold' : 1, \ },{ \ 'name' : 'HiInactive', \ 'guibg' : '#3c3836', \ 'guifg' : '#abb2bf', \ 'ctermbg' : '', \ 'ctermfg' : 145, \ 'bold' : 1, \ } \ ] function! s:hi() abort for info in s:hi_info call s:VIMH.hi(info) endfor endfunction function! s:is_ex_mode() abort return exists('v:argv') && index(v:argv, '-Ex') !=# -1 endfunction function! s:init() abort call s:hi() " https://github.com/neovim/neovim/issues/18050 " vim-patch:8.0.0542 make `line('w$')` return 0 in Ex mode " so the default range should be Buffer in Ex mode if s:is_ex_mode() let s:current_range = 'Buffer' let [s:cursor_stack, s:index] = SpaceVim#plugins#iedit#paser(1, line('$'), s:current_match, 0) else let s:current_range = 'Display' let [s:cursor_stack, s:index] = SpaceVim#plugins#iedit#paser(line('w0'), line('w$'), s:current_match, 0) endif call s:highlight() endfunction " }}} " use SPC s H to highlight all symbol on default range. " use SPC s h to highlight current symbol on default range. " public API func: start Highlight mode {{{ function! SpaceVim#plugins#highlight#start(...) abort " getcurpos is added in vim 7.4.313 let curpos = getpos('.') let save_reg_k = @k normal! viw"ky let s:current_match = @k let @k = save_reg_k call setpos('.', curpos) if s:current_match =~# '^\s*$' || empty(s:current_match) || s:current_match ==# "\n" echohl WarningMsg echo 'cursor is not on symbol' echohl None return endif let s:state = SpaceVim#api#import('transient_state') call s:state.set_title('Highlight Transient State') call s:state.defind_keys( \ { \ 'layout' : 'vertical split', \ 'logo' : s:_function('s:range_logo'), \ 'logo_width' : 30, \ 'init' : s:_function('s:init'), \ 'left' : [ \ { \ 'key' : 'n', \ 'desc' : 'Toggle highlight', \ 'func' : s:_function('s:next_item'), \ 'cmd' : '', \ 'exit' : 0, \ }, \ { \ 'key' : "\", \ 'desc' : 'Toggle highlight', \ 'func' : s:_function('s:toggle_item'), \ 'cmd' : '', \ 'exit' : 0, \ }, \ { \ 'key' : 'r', \ 'desc' : 'change range', \ 'func' : '', \ 'cmd' : 'call call(' . string(s:_function('s:change_range')) . ', [])', \ 'exit' : 0, \ }, \ { \ 'key' : 'e', \ 'desc' : 'iedit', \ 'cmd' : '', \ 'func' : '', \ 'exit_cmd' : 'call call(' . string(s:_function('s:iedit')) . ', [])', \ 'exit' : 1, \ }, \ ], \ 'right' : [ \ { \ 'key' : ['N', 'p'], \ 'desc' : 'Previous match', \ 'cmd' : 'call call(' . string(s:_function('s:previous_item')) . ', [])', \ 'func' : '', \ 'exit' : 0, \ }, \ { \ 'key' : 'b', \ 'desc' : 'search buffers', \ 'cmd' : '', \ 'func' : '', \ 'exit_cmd' : 'call call(' . string(s:_function('s:search_buffers')) . ', [])', \ 'exit' : 1, \ }, \ { \ 'key' : '/', \ 'desc' : 'Search project', \ 'cmd' : '', \ 'func' : '', \ 'exit_cmd' : 'call call(' . string(s:_function('s:search_project')) . ', [])', \ 'exit' : 1, \ }, \ { \ 'key' : 'R', \ 'desc' : 'Reset', \ 'cmd' : '', \ 'func' : s:_function('s:reset_range'), \ 'exit' : 0, \ }, \ ], \ } \ ) let save_tve = &t_ve setlocal t_ve= if has('gui_running') let cursor_hi = s:HI.group2dict('Cursor') call s:HI.hide_in_normal('Cursor') endif call s:state.open() let &t_ve = save_tve if has('gui_running') call s:HI.hi(cursor_hi) endif try call s:clear_highlight() catch endtry endfunction " }}} " public API func: register function range expression {{{ function! SpaceVim#plugins#highlight#reg_expr(ft, begin, end) abort call extend(s:function_expr, {a:ft : [a:begin, a:end]}) endfunction " }}} " key binding: R reset_range {{{ function! s:reset_range() abort let s:current_range = 'Display' let [s:cursor_stack, s:index] = SpaceVim#plugins#iedit#paser(line('w0'), line('w$'), s:current_match, 0) call s:clear_highlight() call s:highlight() endfunction "}}} " key binding: n next_item {{{ function! s:next_item() abort if s:index == len(s:cursor_stack) - 1 let s:index = 0 else let s:index += 1 endif call cursor(s:cursor_stack[s:index].lnum, s:cursor_stack[s:index].col + s:cursor_stack[s:index].len - 1) call s:update_highlight() endfunction " }}} " key binding: r change_range {{{ function! s:change_range() abort if s:current_range ==# 'Display' let s:current_range = 'Buffer' let [s:cursor_stack, s:index] = SpaceVim#plugins#iedit#paser(1, line('$'), s:current_match, 0) call s:clear_highlight() call s:highlight() elseif s:current_range ==# 'Buffer' let s:current_range = 'Function' let range = s:find_func_range() let [s:cursor_stack, s:index] = SpaceVim#plugins#iedit#paser(range[0], range[1], s:current_match, 0) call s:clear_highlight() call s:highlight() elseif s:current_range ==# 'Function' && s:is_ex_mode() let s:current_range = 'Buffer' let [s:cursor_stack, s:index] = SpaceVim#plugins#iedit#paser(1, line('$'), s:current_match, 0) call s:clear_highlight() call s:highlight() else let s:current_range = 'Display' let [s:cursor_stack, s:index] = SpaceVim#plugins#iedit#paser(line('w0'), line('w$'), s:current_match, 0) call s:clear_highlight() call s:highlight() endif let s:state._clear_cmdline = 0 echon ' Change current range to:' exe 'echohl HiRrange' . s:current_range echon s:current_range echohl None endfunction " }}} " key binding: e iedit {{{ function! s:iedit() abort call SpaceVim#plugins#iedit#start() endfunction " }}} " key binding: N/p previous_item {{{ function! s:previous_item() abort if s:index == 0 let s:index = len(s:cursor_stack) - 1 else let s:index -= 1 endif call cursor(s:cursor_stack[s:index].lnum, s:cursor_stack[s:index].col + s:cursor_stack[s:index].len - 1) call s:update_highlight() endfunction " }}} " key binding: b search_buffers {{{ function! s:search_buffers() abort call SpaceVim#plugins#flygrep#open({'input' : s:current_match, 'files':'@buffers'}) endfunction " }}} " key binding: / search_project {{{ function! s:search_project() abort call SpaceVim#plugins#flygrep#open({'input' : s:current_match}) endfunction " }}} " local func: highlight symbol {{{ function! s:highlight() abort for item in s:cursor_stack if item.active call s:CMP.matchaddpos('HiBlueBold', [[ \ item.lnum, \ item.col, \ item.len \ ]]) else call s:CMP.matchaddpos('HiInactive', [[ \ item.lnum, \ item.col, \ item.len \ ]]) endif endfor if !empty(get(s:cursor_stack, s:index, [])) && s:cursor_stack[s:index].active call s:CMP.matchaddpos('HiPurpleBold', [[ \ s:cursor_stack[s:index].lnum, \ s:cursor_stack[s:index].col, \ s:cursor_stack[s:index].len, \ ]]) endif endfunction " }}} " local func: clear highlight {{{ function! s:clear_highlight() abort call clearmatches() endfunction " }}} " key binding: Tab toggle_item {{{ function! s:toggle_item() abort let s:cursor_stack[s:index].active = s:cursor_stack[s:index].active ? 0 : 1 call s:update_highlight() endfunction " }}} " local func: function() wrapper {{{ if v:version > 703 || v:version == 703 && has('patch1170') function! s:_function(fstr) abort return function(a:fstr) endfunction else function! s:_SID() abort return matchstr(expand(''), '\zs\d\+\ze__SID$') endfunction let s:_s = '' . s:_SID() . '_' function! s:_function(fstr) abort return function(substitute(a:fstr, 's:', s:_s, 'g')) endfunction endif " }}} " local func: update highlight symbol {{{ function! s:update_highlight() abort call s:clear_highlight() call s:highlight() endfunction " }}} " local func: find function range {{{ function! s:find_func_range() abort let line = line('.') if !empty(&ft) && has_key(s:function_expr, &ft) let begin = s:function_expr[&ft][0] let end = s:function_expr[&ft][1] let pos1 = search(end, 'nb',line('w0')) let pos2 = search(begin, 'nb',line('w0')) let pos3 = search(end, 'n',line('w$')) let pos0 = line('.') if pos1 < pos2 && pos2 < pos0 && pos0 < pos3 return [pos2, pos3] endif endif return [line, line] endfunction " }}} " vim:set et sw=2 cc=80 foldenable: