" highlightvisual.vim highlight visually selected using :match " Maintainer: Preben "Peppe" Guldberg " Version: 2 " Date: 8th July, 2002 " You can use this script to highlight any number of visual selections. The " most obvious use is to highlight chunks of text that you want to select " among later - or just keep around for reference. " " A few mappings are handled to set, update, remove and clear the highlighting " set by the functions in this script: " " Mode Mapping Description " ---- ------- ----------- " vmap hh Highlight the selection " vmap hr Reset highlighting using current selection " nmap hd Delete highlighting for selections covering the cursor " nmap hc Clears all highlighted selections " nmap hu Update highlighting " " The default hightlighting group is Todo, but this can be overruled setting " either of a "w:highlightvisual_group" variable (window local, first choice) " or a global "highlightvisual_group" variable. " Internals note: " For each bufferwhere we have been called, a script local variable s:marks_NN " stores marks for what is to be highlighted with :match. "NN" is the number " of the current buffer. if &compatible finish endif if exists("loaded_hightlightvisual") finish endif "let loaded_hightlightvisual = 1 let s:opt_cpo = &cpo set cpo&vim " Set up the mappings vnoremap hh :silent call HighlightVisual("add") vnoremap hr :silent call HighlightVisual("reset") nnoremap hd :silent call HighlightVisual("delete") nnoremap hc :silent call HighlightVisual("clear") vnoremap hc :silent call HighlightVisual("clear")gv nnoremap hu :silent call HighlightVisual("update") vnoremap hu :silent call HighlightVisual("update")gv " HighlightVisual() is our frontend fun! HighlightVisual(action) let marks = s:CurrentMarks() let validargs = '"add", "reset", "delete", "clear" and "update"' if a:action ==? "add" let marks = s:JoinMarks(marks, s:GetMark()) elseif a:action ==? "reset" let marks = s:GetMark() elseif a:action ==? "delete" let marks = s:ClearMark(marks, line('.'), col('.')) elseif a:action ==? "clear" let marks = "" elseif a:action ==? "update" " Noop else echoerr 'HighlightVisual(action) only accepts ' . validargs return 0 endif call s:UpdateHighlighting(marks) return 1 endfun " HightLightGroup() determins which hightlight group to use - default is Todo fun! s:HightLightGroup() if exists("w:highlightvisual_group") return w:highlightvisual_group elseif exists("g:highlightvisual_group") return g:highlightvisual_group endif return "Todo" endfun " CurrentMarks() returns the current marks or an empty string for no marks fun! s:CurrentMarks() let var = 's:marks_' . bufnr("%") if !exists(var) return "" endif exec 'let pat = ' . var return pat endfun " UpdateHighlighting() sets the current marks and refreshes the highlighting fun! s:UpdateHighlighting(pat) exec 'let s:marks_' . bufnr("%") . ' = a:pat' if a:pat == '' match NONE else exec 'match ' . s:HightLightGroup() . ' /\v' . a:pat . '/' endif endfun " JoinMarks() joins an optional number of marks to a new pattern fun! s:JoinMarks(...) let pat = '' let i = 0 while i < a:0 let i = i + 1 exec 'let str = a:' . i if pat == '' let pat = str elseif str != '' let pat = pat . '|' . str endif endwhile return pat endfun " GetMark() returns a grouped pattern to match the latest visual selection. " Depending on the selection, there are four different results: " Linewise: line 9 to line 10 " '%(%>8l%<11l.)' " Characterwise: line 9, col 9 to line 10, col 7 " '%(%9l%>8v.|%10l%88v.|%>9l%<11l.|%11l%88l%<11l%6>v%<10v.)' fun! s:GetMark() let l1 = line("'<") | let v1 = virtcol("'<") let l2 = line("'>") | let v2 = virtcol("'>") if l1 > l2 | let tmp = l2 | let l2 = l1 | let l1 = tmp | endif let mode = visualmode() if mode ==# "V" let pat = '%>'.(l1-1).'l%<'.(l2+1).'l.' elseif mode ==# "v" if l1 == l2 let pat = '%'.l1.'l%>'.(v1-1).'v.%<'.(v2+1).'v.' else let pat = '%'.l1.'l%>'.(v1-1).'v.' if l1 < (l2 - 1) let pat = pat . '|%>'.l1.'l%<'.l2.'l.' endif let pat = pat . '|%'.l2.'l%<'.(v2+1).'v.' endif else " We only need to sort virtual columns here (and cannot with "v") if v1 > v2 | let tmp = v2 | let v2 = v1 | let v1 = tmp | endif let pat = '%>'.(l1-1).'l%<'.(l2+1).'l%>'.(v1-1).'v%<'.(v2+1).'v.' endif return '%(' . pat . ')' endfun " InMark() tests if a line and column is inside a marked selection. " The heuristics knows _a_lot_ about the patterns in GetMark(). fun! s:InMark(mark, line, col) if a:mark =~ '|' " visualmode() = 'v' let i = 0 let str = matchstr(a:mark, '.\{-}|', i) if ( (a:line == matchstr(str, '\d\+l\@=')) && \(a:col > matchstr(str, '\d\+v\@='))) return 1 endif let i = i + strlen(str) let str = matchstr(a:mark, '.\{-}|', i) if ( (a:line >= matchstr(a:mark, '>\@<=\d\+')) && \(a:line <= matchstr(a:mark, '<\@<=\d\+'))) return 1 endif let i = i + strlen(str) let str = strpart(a:mark, i) if ( (a:line == matchstr(str, '\d\+l\@=')) && \(a:col < matchstr(str, '\d\+v\@='))) return 1 endif return 0 elseif a:mark !~# 'v' " visualmode() = 'V' return ((a:line > matchstr(a:mark, '>\@<=\d\+')) && \ (a:line < matchstr(a:mark, '<\@<=\d\+'))) else return ((a:line > matchstr(a:mark, '>\@<=\d\+l\@=')) && \ (a:line < matchstr(a:mark, '<\@<=\d\+l\@=')) && \ (a:col > matchstr(a:mark, '>\@<=\d\+v\@=')) && \ (a:col < matchstr(a:mark, '<\@<=\d\+v\@='))) endif endfun " ClearMark() removes marks that overlaps a specific position. fun! s:ClearMark(pat, line, col) let newpat = '' let i = 0 let patlen = strlen(a:pat) while i < patlen let mark = matchstr(a:pat, '%(.\{-})|\=', i) let i = i + strlen(mark) if !s:InMark(mark, a:line, a:col) let newpat = s:JoinMarks(newpat, mark) endif endwhile return newpat endfun let &cpo = s:opt_cpo unlet s:opt_cpo