2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2016-11-18 23:11:12 +00:00
|
|
|
" File: highlight.vim
|
|
|
|
" Maintainer: Joshua Potter <jrpotter2112@gmail.com>
|
2017-05-17 08:10:17 +00:00
|
|
|
" Comment: For the sake of distinguishing between vim *:reg*isters and
|
|
|
|
" highlight registers used in the given script, we use register
|
|
|
|
" to describe the former and h_register to describe the latter.
|
2016-11-18 23:11:12 +00:00
|
|
|
"
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2016-11-18 23:11:12 +00:00
|
|
|
|
|
|
|
" SCRIPT VARIABLES:
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2016-11-18 23:11:12 +00:00
|
|
|
|
2017-05-17 06:55:52 +00:00
|
|
|
" s:active_register :: String {{{2
|
2017-05-17 07:35:27 +00:00
|
|
|
" ------------------------------------------------------------------------------
|
2017-05-17 08:10:17 +00:00
|
|
|
" The h_register currently active. This defaults to the unnamed register.
|
2016-11-18 23:11:12 +00:00
|
|
|
|
2017-05-24 04:35:51 +00:00
|
|
|
let s:active_register = "1"
|
2016-11-18 23:11:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
" s:registry :: { String : { String : Match } } {{{2
|
2017-05-17 07:35:27 +00:00
|
|
|
" ------------------------------------------------------------------------------
|
2017-05-17 08:10:17 +00:00
|
|
|
" The keys of the outer dictionary are any active h_registers (that is, before a
|
|
|
|
" call to ClearRegister is called). By default, this will be set to be
|
2017-05-17 07:35:27 +00:00
|
|
|
" populated with at least g:highlight_registry once the plugin is loaded.
|
|
|
|
"
|
|
|
|
" The corresponding values of the outer dictionary is a key value pairing of a
|
|
|
|
" matched identifier and the Match object corresponding to it. We must keep
|
|
|
|
" track of match objects as they must be deleted manually by matchdelete.
|
2016-11-18 23:11:12 +00:00
|
|
|
|
|
|
|
let s:registry = {}
|
|
|
|
|
|
|
|
|
2017-05-24 04:28:14 +00:00
|
|
|
" FUNCTION: ExpandRegister(reg) {{{1
|
|
|
|
" ==============================================================================
|
|
|
|
" Convenience method to determine which register is being currently used.
|
|
|
|
" The unnamed register defaults to the last used register to avoid having to
|
|
|
|
" constantly prefix registration. This can be changed by setting the value of
|
2017-05-25 01:04:59 +00:00
|
|
|
" g:highlight_persist_unnamed_register to 1.
|
2017-05-24 04:28:14 +00:00
|
|
|
|
2017-05-24 04:35:51 +00:00
|
|
|
function! highlight#expand_reg(reg)
|
2017-05-25 01:04:59 +00:00
|
|
|
if !g:highlight_persist_unnamed_register && a:reg ==# '"'
|
2017-05-24 04:28:14 +00:00
|
|
|
return s:active_register
|
|
|
|
endif
|
|
|
|
return a:reg
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
|
|
|
" FUNCTION: ExpandFlag(flag) {{{1
|
|
|
|
" ==============================================================================
|
|
|
|
" Convenience method used to make the mappings in plugin/highlight.vim a bit
|
|
|
|
" easier to read through. The passed flag can be:
|
|
|
|
"
|
|
|
|
" c: Indicates the current word, with word boundary.
|
|
|
|
" g: Indicates the current word, without word boundary.
|
|
|
|
" v: Indicates the current visual selection.
|
|
|
|
"
|
|
|
|
" Throws an error otherwise.
|
|
|
|
|
|
|
|
function! highlight#expand_flag(flag) abort
|
|
|
|
if a:flag ==# 'c'
|
|
|
|
return '\<' . expand('<cword>') . '\>'
|
|
|
|
elseif a:flag ==# 'g'
|
|
|
|
return expand('<cword>')
|
|
|
|
elseif a:flag ==# 'v'
|
|
|
|
return highlight#get_visual_selection()
|
|
|
|
endif
|
|
|
|
throw 'Could not expand passed flag: ' . a:flag
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2017-05-24 04:21:13 +00:00
|
|
|
" FUNCTION: CountPattern(pattern) {{{1
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
|
|
|
" Convenience method used to display the number of times the passed pattern has
|
|
|
|
" occurred in the current buffer.
|
|
|
|
|
2017-05-24 04:21:13 +00:00
|
|
|
function! highlight#count_pattern(pattern)
|
2017-05-17 07:35:27 +00:00
|
|
|
if len(@/) > 0
|
|
|
|
let pos = getpos('.')
|
2017-05-24 04:21:13 +00:00
|
|
|
exe ' %s/' . a:pattern . '//gne'
|
2017-05-17 07:35:27 +00:00
|
|
|
call setpos('.', pos)
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
|
|
|
" FUNCTION: GetVisualSelection {{{1
|
|
|
|
" ==============================================================================
|
2017-05-17 08:10:17 +00:00
|
|
|
" Borrowed from http://stackoverflow.com/a/6271254/794380.
|
2017-05-17 07:35:27 +00:00
|
|
|
|
|
|
|
function! highlight#get_visual_selection()
|
|
|
|
let [lnum1, col1] = getpos("'<")[1:2]
|
|
|
|
let [lnum2, col2] = getpos("'>")[1:2]
|
|
|
|
let lines = getline(lnum1, lnum2)
|
|
|
|
let lines[-1] = lines[-1][:col2 - (&selection == 'inclusive' ? 1 : 2)]
|
|
|
|
let lines[0] = lines[0][col1 - 1:]
|
|
|
|
return substitute(escape(join(lines, "\n"), '\\/.*$%~[]'), '\n', '\\n', 'g')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2017-05-17 06:55:52 +00:00
|
|
|
" FUNCTION: Statusline() {{{1
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2017-05-17 06:55:52 +00:00
|
|
|
" Allow for integrating the currently highlighted section into the statusline.
|
2017-05-17 17:10:31 +00:00
|
|
|
" Mirrors the look of a given prompted highlight group (e.g. :hi Search)
|
2017-05-17 09:39:02 +00:00
|
|
|
|
2017-05-17 17:10:31 +00:00
|
|
|
function! highlight#statusline()
|
2017-05-17 18:49:45 +00:00
|
|
|
return repeat(s:active_register, 3)
|
2017-05-17 09:39:02 +00:00
|
|
|
endfunction
|
2017-05-17 06:55:52 +00:00
|
|
|
|
|
|
|
|
2016-11-18 23:11:12 +00:00
|
|
|
" FUNCTION: GetGroupName(reg) {{{1
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2017-05-17 08:10:17 +00:00
|
|
|
" Group names are not allowed to have special characters; they must be
|
|
|
|
" alphanumeric or underscores.
|
2016-11-18 23:11:12 +00:00
|
|
|
|
2016-11-18 23:38:01 +00:00
|
|
|
function! highlight#get_group_name(reg)
|
|
|
|
return 'highlight_registry_' . char2nr(a:reg)
|
2016-11-18 23:11:12 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2017-05-17 09:32:27 +00:00
|
|
|
" FUNCTION: GetGroupSpecification(reg) {{{1
|
|
|
|
" ==============================================================================
|
|
|
|
" Gets the specification created in the g:highlight_registry for a given
|
|
|
|
" h_register. If it does not exist then we pick a 'random' option from the
|
|
|
|
" registry.
|
|
|
|
|
|
|
|
function! highlight#get_group_specification(reg)
|
|
|
|
if has_key(g:highlight_registry, a:reg)
|
|
|
|
return g:highlight_registry[a:reg]
|
|
|
|
endif
|
|
|
|
" Since vim does not have built in random functionality, we instead look at
|
|
|
|
" the given line we are currently at and choose this value mod the size of the
|
|
|
|
" registry.
|
|
|
|
"
|
|
|
|
" TODO(jrpotter): Neovim provides Lua builtin. Perhaps use that instead?
|
|
|
|
let l:target = line('.') % len(g:highlight_registry)
|
|
|
|
let l:index = 0
|
|
|
|
for l:key in keys(g:highlight_registry)
|
|
|
|
if l:index == l:target
|
|
|
|
return g:highlight_registry[l:key]
|
|
|
|
else
|
|
|
|
let l:index = l:index + 1
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2016-11-18 23:11:12 +00:00
|
|
|
" FUNCTION: InitRegister() {{{1
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2017-05-17 08:10:17 +00:00
|
|
|
" Sets up the highlight group. This must be called before any attempts to add
|
|
|
|
" matches to a given h_register is performed.
|
2016-11-18 23:11:12 +00:00
|
|
|
|
2016-11-18 23:38:01 +00:00
|
|
|
function! highlight#init_register(reg, color)
|
|
|
|
call highlight#clear_register(a:reg)
|
2017-05-17 08:10:17 +00:00
|
|
|
let s:registry[a:reg] = {}
|
2017-05-17 09:32:27 +00:00
|
|
|
|
|
|
|
" Build custom highlight group with any attributes supported by cterm. If the
|
|
|
|
" specification has a 'group' key, use that group as a base template instead
|
|
|
|
" of the default 'Search' group.
|
|
|
|
let l:specs = highlight#get_group_specification(a:reg)
|
|
|
|
let l:group = get(l:specs, 'group', 'Search')
|
|
|
|
|
|
|
|
" Supported attributes for 'cterm' and 'gui', as indicated by *synIDattr*.
|
|
|
|
let l:attrs = [ 'fg', 'bg', 'bold', 'italic', 'underline',
|
|
|
|
\ 'reverse', 'inverse', 'standout', 'underline', 'undercurl']
|
|
|
|
|
|
|
|
let l:highlight=[]
|
|
|
|
for l:mode in ['cterm', 'gui']
|
|
|
|
let l:group_fg = synIDattr(synIDtrans(hlID(l:group)), 'fg', l:mode)
|
|
|
|
let l:group_bg = synIDattr(synIDtrans(hlID(l:group)), 'bg', l:mode)
|
|
|
|
let l:group_attrs = {}
|
|
|
|
for l:key in l:attrs[2:]
|
|
|
|
if synIDattr(synIDtrans(hlID(l:group)), l:key, l:mode)
|
|
|
|
let l:attrs[l:key] = '1'
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
" First build up text formats.
|
|
|
|
let l:text_format = []
|
|
|
|
for l:key in l:attrs[2:]
|
|
|
|
if has_key(l:specs, l:key)
|
|
|
|
if l:specs[l:key] ==# '1'
|
|
|
|
call add(l:text_format, l:key)
|
|
|
|
endif
|
|
|
|
" If not present, then can default to highlight group.
|
|
|
|
elseif get(l:group_attrs, l:key, '0') ==# '1'
|
|
|
|
call add(l:text_format, l:key)
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
" Now append the attributes for the given mode.
|
|
|
|
if !empty(get(l:specs, 'fg', l:group_fg))
|
|
|
|
call add(l:highlight, l:mode . 'fg=' . get(l:specs, 'fg', l:group_fg))
|
|
|
|
endif
|
|
|
|
if !empty(get(l:specs, 'bg', l:group_bg))
|
|
|
|
call add(l:highlight, l:mode . 'bg=' . get(l:specs, 'bg', l:group_bg))
|
|
|
|
endif
|
|
|
|
if !empty(l:text_format)
|
|
|
|
call add(l:highlight, l:mode . '=' . join(l:text_format, ','))
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
|
|
|
|
exe 'hi' highlight#get_group_name(a:reg) join(l:highlight)
|
2016-12-16 16:21:40 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2016-11-18 23:11:12 +00:00
|
|
|
" FUNCTION: ActivateRegister() {{{1
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2017-05-17 08:10:17 +00:00
|
|
|
" Places the contents of a highlight register into the search register and links
|
|
|
|
" the Search highlight group to the highlight group name. Activation of an
|
|
|
|
" h_register that has not yet been initialized is allowed - in this case, the
|
|
|
|
" search register is simply cleared.
|
2016-11-18 23:11:12 +00:00
|
|
|
|
2016-11-18 23:38:01 +00:00
|
|
|
function! highlight#activate_register(reg)
|
2017-05-17 06:55:52 +00:00
|
|
|
let s:active_register = a:reg
|
|
|
|
if has_key(s:registry, a:reg)
|
2016-11-18 23:11:12 +00:00
|
|
|
let search = ''
|
2016-11-18 23:38:01 +00:00
|
|
|
for key in keys(s:registry[a:reg])
|
2016-11-18 23:11:12 +00:00
|
|
|
let search = search . key . '\|'
|
|
|
|
endfor
|
|
|
|
let @/ = search[:-3]
|
2017-05-17 19:00:09 +00:00
|
|
|
exe 'hi! link Search' highlight#get_group_name(a:reg)
|
2016-11-18 23:11:12 +00:00
|
|
|
set hlsearch
|
|
|
|
else
|
|
|
|
let @/ = ''
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2017-05-24 04:21:13 +00:00
|
|
|
" FUNCTION: AppendToSearch(reg, pattern) {{{1
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2017-05-17 08:10:17 +00:00
|
|
|
" Extends the current matches of h_register reg with the pattern found once flag
|
|
|
|
" is expanded. If the h_register specified has not yet been initialized, simply
|
|
|
|
" create a new h_register and continue.
|
2016-11-18 23:11:12 +00:00
|
|
|
|
2017-05-24 04:21:13 +00:00
|
|
|
function! highlight#append_to_search(reg, pattern)
|
|
|
|
if len(a:pattern) > 0
|
2017-05-17 08:10:17 +00:00
|
|
|
if !has_key(s:registry, a:reg)
|
|
|
|
" TODO(jrpotter): Choose color better.
|
|
|
|
call highlight#init_register(a:reg, 'Yellow')
|
|
|
|
endif
|
|
|
|
" Don't want to add multiple match objects into registry
|
2017-05-24 04:21:13 +00:00
|
|
|
if !has_key(s:registry[a:reg], a:pattern)
|
|
|
|
let s:registry[a:reg][a:pattern] =
|
|
|
|
\ matchadd(highlight#get_group_name(a:reg), a:pattern)
|
2017-05-17 08:10:17 +00:00
|
|
|
endif
|
|
|
|
" Updates the search register
|
|
|
|
call highlight#activate_register(a:reg)
|
2016-11-18 23:38:01 +00:00
|
|
|
endif
|
2017-05-17 08:10:17 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2017-05-24 04:21:13 +00:00
|
|
|
" FUNCTION: RemoveFromSearch(reg, pattern) {{{1
|
2017-05-17 08:10:17 +00:00
|
|
|
" ==============================================================================
|
|
|
|
" Removes the given pattern found once flag is expanded from the passed
|
|
|
|
" h_register reg. If the h_register will be emptied as a result of this call,
|
|
|
|
" instead delegating to clearing out the register instead.
|
|
|
|
|
2017-05-24 04:21:13 +00:00
|
|
|
function! highlight#remove_from_search(reg, pattern)
|
|
|
|
if has_key(s:registry, a:reg) && has_key(s:registry[a:reg], a:pattern)
|
2017-05-17 09:32:27 +00:00
|
|
|
if len(s:registry[a:reg]) == 1
|
2017-05-17 08:10:17 +00:00
|
|
|
call highlight#clear_register(a:reg)
|
|
|
|
else
|
2017-05-24 04:21:13 +00:00
|
|
|
silent! call matchdelete(s:registry[a:reg][a:pattern])
|
|
|
|
unlet s:registry[a:reg][a:pattern]
|
2017-05-17 08:10:17 +00:00
|
|
|
endif
|
2016-11-18 23:11:12 +00:00
|
|
|
endif
|
2017-05-17 08:10:17 +00:00
|
|
|
" Updates the search register
|
2016-11-18 23:38:01 +00:00
|
|
|
call highlight#activate_register(a:reg)
|
2016-11-18 23:11:12 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
2017-05-17 08:10:17 +00:00
|
|
|
" FUNCTION: ClearRegister(reg) {{{1
|
2017-05-17 07:35:27 +00:00
|
|
|
" ==============================================================================
|
2017-05-17 08:10:17 +00:00
|
|
|
" Used to clear out the h_register reg and potentially unlink the Search
|
|
|
|
" highlight group.
|
2016-11-18 23:11:12 +00:00
|
|
|
|
2017-05-17 08:10:17 +00:00
|
|
|
function! highlight#clear_register(reg)
|
|
|
|
exe 'hi clear ' . highlight#get_group_name(a:reg)
|
2016-11-18 23:38:01 +00:00
|
|
|
if has_key(s:registry, a:reg)
|
2017-05-17 08:10:17 +00:00
|
|
|
for key in keys(s:registry[a:reg])
|
|
|
|
silent! call matchdelete(s:registry[a:reg][key])
|
|
|
|
unlet s:registry[a:reg][key]
|
|
|
|
endfor
|
|
|
|
unlet s:registry[a:reg]
|
2016-11-18 23:11:12 +00:00
|
|
|
endif
|
2017-05-17 08:10:17 +00:00
|
|
|
if a:reg ==# s:active_register
|
|
|
|
hi! link Search NONE
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
|
|
|
|
" FUNCTION: Reset() {{{1
|
|
|
|
" ==============================================================================
|
|
|
|
" Used to reset the state of all h_register's.
|
|
|
|
|
|
|
|
function! highlight#reset()
|
|
|
|
for key in keys(s:registry)
|
|
|
|
call highlight#clear_register(key)
|
|
|
|
endfor
|
|
|
|
for [key, value] in items(g:highlight_registry)
|
|
|
|
call highlight#init_register(key, value)
|
|
|
|
endfor
|
2016-11-18 23:11:12 +00:00
|
|
|
endfunction
|
|
|
|
|