From 786956a3fe12197c81d4c730910b94a1d31dfd26 Mon Sep 17 00:00:00 2001 From: SL1900 Date: Fri, 11 Jul 2025 20:56:22 +0900 Subject: [PATCH] Added plugin for highlighting multiple words with different styles. Source: https://vim.fandom.com/wiki/Highlight_multiple_words --- init.lua | 3 + vim/highlight/highlight.csv | 28 ++++ vim/highlight/highlight.vim | 305 ++++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 vim/highlight/highlight.csv create mode 100644 vim/highlight/highlight.vim diff --git a/init.lua b/init.lua index eff885e..30e171d 100644 --- a/init.lua +++ b/init.lua @@ -138,3 +138,6 @@ vim.diagnostic.config({ border = "rounded", }, }) + +local highlight_script_path = debug.getinfo(1, "S").source:match("@?(.*[\\/])") +vim.cmd("source " .. highlight_script_path .. "vim/highlight/highlight.vim") diff --git a/vim/highlight/highlight.csv b/vim/highlight/highlight.csv new file mode 100644 index 0000000..1c650a3 --- /dev/null +++ b/vim/highlight/highlight.csv @@ -0,0 +1,28 @@ +hlnum,ctermfg,ctermbg,guifg,guibg +1,red,,red, +2,green,,green, +3,blue,,blue, +4,black,magenta,black,IndianRed +5,black,green,black,chartreuse +6,black,cyan,black,DeepSkyBlue +7,white,DarkRed,white,firebrick +8,white,DarkGreen,white,DarkGreen +9,white,DarkBlue,white,DarkSlateBlue +11,,,black,thistle +12,,,black,burlywood +13,,,black,PowderBlue +14,,,black,peru +15,,,black,DarkSeaGreen +16,,,black,SteelBlue +17,,,black,DarkOrange +18,,,black,DarkOliveGreen +19,,,black,SlateGray +21,,,red,grey20 +22,,,green,grey20 +23,,,CornflowerBlue,grey20 +24,,,red,grey40 +25,,,green,grey40 +26,,,blue,grey40 +27,,,red,grey70 +28,,,DarkGreen,grey70 +29,,,blue,grey70 diff --git a/vim/highlight/highlight.vim b/vim/highlight/highlight.vim new file mode 100644 index 0000000..1cabe6a --- /dev/null +++ b/vim/highlight/highlight.vim @@ -0,0 +1,305 @@ +" Plugin to highlight multiple words in different colors. +" Version 2008-11-19 from http://vim.wikia.com/wiki/VimTip1572 +" File highlights.csv (in same directory as script) defines the highlights. +" +" Type '\m' to toggle mapping of keypad on/off (assuming \ leader). +" Type '\f' to find the next match; '\F' to find backwards. +" Can also type '\n' or '\N' for search; then n or N will find next. +" On the numeric keypad, press: +" 1 to highlight visually selected text or current word +" using highlight group hl1 (defined below) +" 2 for highlight hl2, 3 for highlight hl3, etc +" (can press 1 to 9 on keypad for highlights hl1 to hl9) +" 0 to remove highlight from current word +" - to remove all highlights in current window +" + to restore highlights cleared with '-' in current window +" * to restore highlights (possibly from another window) +" Can press 1 or 2 on main keyboard before keypad 1..9 for more highlights. +" Commands: +" ':Highlight' list all highlights. +" ':Highlight [n [pattern]]' set highlight. +" ':Hsample' display all highlights in a scratch buffer. +" ':Hclear [hlnum|pattern|*]' clear highlights. +" ':Hsave x', ':Hrestore x' save/restore highlights (x any name). +" Saving current highlights requires '!' in 'viminfo' option. + +if v:version < 702 || exists('loaded_highlightmultiple') || &cp + finish +endif + +let loaded_highlightmultiple = 1 + +" On first call, read file highlights.csv in same directory as script. +" For example, line "5,white,blue,black,green" executes: +" highlight hl5 ctermfg=white ctermbg=blue guifg=black guibg=green +let s:data_file = expand(':p:r').'.csv' +let s:loaded_data = 0 +function! LoadHighlights() + if !s:loaded_data + if filereadable(s:data_file) + let names = ['hl', 'ctermfg=', 'ctermbg=', 'guifg=', 'guibg='] + for line in readfile(s:data_file) + let fields = split(line, ',', 1) + if len(fields) == 5 && fields[0] =~ '^\d\+$' + let cmd = range(5) + call map(cmd, 'names[v:val].fields[v:val]') + call filter(cmd, 'v:val!~''=$''') + execute 'silent highlight '.join(cmd) + endif + endfor + let s:loaded_data = 1 + endif + if !s:loaded_data + echo 'Error: Could not read highlight data from '.s:data_file + endif + endif +endfunction + +" Return last visually selected text or '\'. +" what = 1 (selection), or 2 (cword), or 0 (guess if 1 or 2 is wanted). +function! s:Pattern(what) + if a:what == 2 || (a:what == 0 && histget(':', -1) =~# '^H') + let result = expand("") + if !empty(result) + let result = '\<'.result.'\>' + endif + else + let old_reg = getreg('"') + let old_regtype = getregtype('"') + normal! gvy + let result = substitute(escape(@@, '\.*$^~['), '\_s\+', '\\_s\\+', 'g') + normal! gV + call setreg('"', old_reg, old_regtype) + endif + return result +endfunction + +" Remove any highlighting for hlnum then highlight pattern (if not empty). +" If pat is numeric, use current word or visual selection and +" increase hlnum by count*10 (if count [1..9] is given). +function! s:DoHighlight(hlnum, pat, decade) + call LoadHighlights() + let hltotal = a:hlnum + if 0 < a:decade && a:decade < 10 + let hltotal += a:decade * 10 + endif + if type(a:pat) == type(0) + let pattern = s:Pattern(a:pat) + else + let pattern = a:pat + endif + let id = hltotal + 100 + silent! call matchdelete(id) + if !empty(pattern) + try + call matchadd('hl'.hltotal, pattern, -1, id) + catch /E28:/ + echo 'Highlight hl'.hltotal.' is not defined' + endtry + endif +endfunction + +" Remove all matches for pattern. +function! s:UndoHighlight(pat) + if type(a:pat) == type(0) + let pattern = s:Pattern(a:pat) + else + let pattern = a:pat + endif + for m in getmatches() + if m.pattern ==# pattern + call matchdelete(m.id) + endif + endfor +endfunction + +" Return pattern to search for next match, and do search. +function! s:Search(backward) + let patterns = [] + for m in getmatches() + call add(patterns, m.pattern) + endfor + if empty(patterns) + let pat = '' + else + let pat = join(patterns, '\|') + call search(pat, a:backward ? 'b' : '') + endif + return pat +endfunction + +" Enable or disable mappings and any current matches. +function! s:MatchToggle() + if exists('g:match_maps') && g:match_maps + let g:match_maps = 0 + for i in range(0, 9) + execute 'unmap ' + endfor + nunmap + nunmap + nunmap + nunmap f + nunmap F + nunmap n + nunmap N + else + let g:match_maps = 1 + for i in range(1, 9) + execute 'vnoremap :call DoHighlight('.i.', 1, v:count)' + execute 'nnoremap :call DoHighlight('.i.', 2, v:count)' + endfor + vnoremap :call UndoHighlight(1) + nnoremap :call UndoHighlight(2) + nnoremap :call WindowMatches(0) + nnoremap :call WindowMatches(1) + nnoremap :call WindowMatches(2) + nnoremap f :call Search(0) + nnoremap F :call Search(1) + nnoremap n :let @/=Search(0) + nnoremap N :let @/=Search(1) + endif + call s:WindowMatches(g:match_maps) + echo 'Mappings for matching:' g:match_maps ? 'ON' : 'off' +endfunction +nnoremap m :call MatchToggle() + +" Remove and save current matches, or restore them. +function! s:WindowMatches(action) + call LoadHighlights() + if a:action == 1 + if exists('w:last_matches') + call setmatches(w:last_matches) + endif + elseif a:action == 2 + if exists('g:last_matches') + call setmatches(g:last_matches) + else + call s:Hrestore('') + endif + else + let m = getmatches() + if !empty(m) + let w:last_matches = m + let g:last_matches = m + call s:Hsave('') + call clearmatches() + endif + endif +endfunction + +" Return name of global variable to save value ('' if invalid). +function! s:NameForSave(name) + if a:name =~# '^\w*$' + return 'HI_SAVE_'.toupper(a:name) + endif + echo 'Error: Invalid name "'.a:name.'"' + return '' +endfunction + +" Return custom completion string (match patterns). +function! s:MatchPatterns(A, L, P) + return join(sort(map(getmatches(), 'v:val.pattern')), "\n") +endfunction + +" Return custom completion string (saved highlight names). +function! s:SavedNames(A, L, P) + let l = filter(keys(g:), 'v:val =~# ''^HI_SAVE_\w''') + return tolower(join(sort(map(l, 'strpart(v:val, 8)')), "\n")) +endfunction + +" Save current highlighting in a global variable. +function! s:Hsave(name) + let sname = s:NameForSave(a:name) + if !empty(sname) + let l = getmatches() + call map(l, 'join([v:val.group, v:val.pattern, v:val.priority, v:val.id], "\t")') + let g:{sname} = join(l, "\n") + endif +endfunction +command! -nargs=? -complete=custom,s:SavedNames Hsave call s:Hsave('') + +" Restore current highlighting from a global variable. +function! s:Hrestore(name) + call LoadHighlights() + let sname = s:NameForSave(a:name) + if !empty(sname) + if exists('g:{sname}') + let matches = [] + for l in split(g:{sname}, "\n") + let f = split(l, "\t", 1) + call add(matches, {'group':f[0], 'pattern':f[1], 'priority':f[2], 'id':f[3]}) + endfor + call setmatches(matches) + else + echo 'No such global variable: '.sname + endif + endif +endfunction +command! -nargs=? -complete=custom,s:SavedNames Hrestore call s:Hrestore('') + +" Clear a match, or clear all current matches. Example args: +" '14' = hl14, '*' = all, '' = visual selection or cword, +" 'pattern' = all matches for pattern +function! s:Hclear(pattern) range + if empty(a:pattern) + call s:UndoHighlight(0) + elseif a:pattern == '*' + call s:WindowMatches(0) + elseif a:pattern =~ '^[1-9][0-9]\?$' + call s:DoHighlight(str2nr(a:pattern), '', 0) + else + call s:UndoHighlight(a:pattern) + endif +endfunction +command! -nargs=* -complete=custom,s:MatchPatterns -range Hclear call s:Hclear('') + +" Create a scratch buffer with sample text, and apply all highlighting. +function! s:Hsample() + call LoadHighlights() + new + setlocal buftype=nofile bufhidden=hide noswapfile + let lines = [] + let items = [] + for hl in filter(range(1, 99), 'v:val % 10 > 0') + if hlexists('hl'.hl) + let sample = printf('Sample%2d', hl) + call s:DoHighlight(hl, sample, 0) + else + let sample = ' ' + endif + call add(items, sample) + if len(items) >= 3 + call insert(lines, substitute(join(items), '\s\+$', '', '')) + let items = [] + endif + endfor + call append(0, filter(lines, 'len(v:val) > 0')) + $d + %s/\d3$/&\r/e +endfunction +command! Hsample call s:Hsample() + +" Set a match, or display all current matches. Example args: +" '14' = set hl14 for visual selection or cword, +" '14 pattern' = set hl14 for pattern, '' = display all +function! s:Highlight(args) range + if empty(a:args) + echo 'Highlight groups and patterns:' + for m in getmatches() + echo m.group m.pattern + endfor + return + endif + let l = matchlist(a:args, '^\s*\([1-9][0-9]\?\)\%($\|\s\+\(.*\)\)') + if len(l) >= 3 + let hlnum = str2nr(l[1]) + let pattern = l[2] + if empty(pattern) + let pattern = s:Pattern(0) + endif + call s:DoHighlight(hlnum, pattern, 0) + return + endif + echo 'Error: First argument must be highlight number 1..99' +endfunction +command! -nargs=* -range Highlight call s:Highlight('')