" Vim plugin for showing matching html tags. " Maintainer: Greg Sexton " Credits: Bram Moolenar and the 'matchparen' plugin from which this draws heavily. if exists("b:did_ftplugin") finish endif augroup matchhtmlparen autocmd! CursorMoved,CursorMovedI,WinEnter call s:Highlight_Matching_Pair() augroup END fu! s:Highlight_Matching_Pair() " Remove any previous match. if exists('w:tag_hl_on') && w:tag_hl_on 2match none let w:tag_hl_on = 0 endif " Avoid that we remove the popup menu. " Return when there are no colors (looks like the cursor jumps). if pumvisible() || (&t_Co < 8 && !has("gui_running")) return endif "get html tag under cursor let tagname = s:GetCurrentCursorTag() if tagname == ""|return|endif if tagname[0] == '/' let position = s:SearchForMatchingTag(tagname[1:], 0) else let position = s:SearchForMatchingTag(tagname, 1) endif call s:HighlightTagAtPosition(position) endfu fu! s:GetCurrentCursorTag() "returns the tag under the cursor, includes the '/' if on a closing tag. let c_col = col('.') let matched = matchstr(getline('.'), '\(<[^<>]*\%'.c_col.'c.\{-}>\)\|\(\%'.c_col.'c<.\{-}>\)') if matched =~ '/>$' return "" elseif matched == "" " The tag itself may be spread over multiple lines. let matched = matchstr(getline('.'), '\(<[^<>]*\%'.c_col.'c.\{-}$\)\|\(\%'.c_col.'c<.\{-}$\)') if matched == "" return "" endif endif " XML Tag definition is " (Letter | '_' | ':') (Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender)* " Instead of dealing with CombiningChar and Extender, and because Vim's " [:alpha:] only includes 8-bit characters, let's include all non-ASCII " characters. let tagname = matchstr(matched, '<\zs/\?\%([[:alpha:]_:]\|[^\x00-\x7F]\)\%([-._:[:alnum:]]\|[^\x00-\x7F]\)*') return tagname endfu fu! s:SearchForMatchingTag(tagname, forwards) "returns the position of a matching tag or [0 0] let starttag = '\V<'.escape(a:tagname, '\').'\%(\_s\%(\.\{-}\|\_.\{-}\%<'.line('.').'l\)/\@' let midtag = '' let endtag = '\V' let flags = 'nW'.(a:forwards?'':'b') " When not in a string or comment ignore matches inside them. let skip ='synIDattr(synID(line("."), col("."), 0), "name") ' . \ '=~? "\\%(html\\|xml\\)String\\|\\%(html\\|xml\\)CommentPart"' if skip | let skip = 0 | endif " Limit the search to lines visible in the window. let stopline = a:forwards ? line('w$') : line('w0') let timeout = 300 " The searchpairpos() timeout parameter was added in 7.2 if v:version >= 702 return searchpairpos(starttag, midtag, endtag, flags, skip, stopline, timeout) else return searchpairpos(starttag, midtag, endtag, flags, skip, stopline) endif endfu fu! s:HighlightTagAtPosition(position) if a:position == [0, 0] return endif let [m_lnum, m_col] = a:position exe '2match MatchParen /\(\%' . m_lnum . 'l\%' . m_col . 'c<\zs.\{-}\ze[\n >]\)\|' \ .'\(\%' . line('.') . 'l\%' . col('.') . 'c<\zs.\{-}\ze[\n >]\)\|' \ .'\(\%' . line('.') . 'l<\zs[^<> ]*\%' . col('.') . 'c.\{-}\ze[\n >]\)\|' \ .'\(\%' . line('.') . 'l<\zs[^<>]\{-}\ze\s[^<>]*\%' . col('.') . 'c.\{-}[\n>]\)/' let w:tag_hl_on = 1 endfu " vim: set ts=8 sts=4 sw=4 expandtab :