" Vim script file vim600:fdm=marker: " FileType: XML " Author: Rene de Zwart " Maintainer: Rene de Zwart " Last Change: $Date: 2006/05/12 21:56:27 $ " Version: $Revision: 1.32 $ " " Licence: This program is free software; you can redistribute it " and/or modify it under the terms of the GNU General Public " License. See http://www.gnu.org/copyleft/gpl.txt " Credits: Devin Weaver et all " for the original code. Guo-Peng Wen for the self " install documentation code. " Bart vam Deenen for makeElement function " Observation - If you want to do something to a match pair most of the time " you must do first the close tag. Because doing first the open " tag could change the close tag position. " Only do this when not done yet for this buffer if exists("b:did_ftplugin") finish endif let b:did_ftplugin = 1 setlocal commentstring= " XML: thanks to Johannes Zellner and Akbar Ibrahim " - case sensitive " - don't match empty tags " - match style comments (but not --, --) " - match inlined dtd's. This is not perfect, as it " gets confused for example by " "> if exists("loaded_matchit") let b:match_ignorecase=0 let b:match_words = \ '<:>,' . \ '<\@<=!\[CDATA\[:]]>,'. \ '<\@<=!--:-->,'. \ '<\@<=?\k\+:?>,'. \ '<\@<=\([^ \t>/]\+\)\%(\s\+[^>]*\%([^/]>\|$\)\|>\|$\):<\@<=/\1>,'. \ '<\@<=\%([^ \t>/]\+\)\%(\s\+[^/>]*\|$\):/>' endif " Script rgular expresion used. Documents those nasty criters {{{1 let s:NoSlashBeforeGt = '\(\/\)\@\' " Don't check for quotes around attributes!!! let s:Attrib = '\(\(\s\|\n\)\+\([^>= \t]\+=[^>=]\+\)\(\s\|\n\)*\)' let s:OptAttrib = s:Attrib . '*'. s:NoSlashBeforeGt let s:ReqAttrib = s:Attrib . '\+'. s:NoSlashBeforeGt let s:OpenTag = '<[^!/?][^>]*' . s:OptAttrib let s:OpenOrCloseTag = '<[^!?][^>]*'. s:OptAttrib let s:CloseTag = '<\/[^>]*'. s:OptAttrib let s:SpaceInfront = '^\s*<' let s:EndofName = '\($\|\s\|>\)' " Buffer variables {{{1 let b:emptyTags='^\(img\|input\|param\|frame\|br\|hr\|meta\|link\|base\|area\)$' let b:firstWasEndTag = 0 let b:html_mode =((&filetype =~ 'x\?html') && !exists("g:xml_no_html")) let b:haveAtt = 0 let b:lastTag = "" let b:lastAtt = "" let b:suffix = (exists('g:makeElementSuf') ? g:makeElementSuf : ';;') let b:xml_use_xhtml = 0 if exists('g:xml_use_xhtml') let b:xml_use_xhtml = g:xml_use_xhtml elseif &filetype == 'xhtml' let b:xml_use_xhtml = 1 en " NewFileXML -> Inserts at top of new file. {{{1 if !exists("*NewFileXML") function! NewFileXML( ) " Where is g:did_xhtmlcf_inits defined? if &filetype == 'xml' || \ (!exists ("g:did_xhtmlcf_inits") && \ b:xml_use_xhtml && \ (&filetype =~ 'x\?html')) if append (0, '') normal! G endif endif endfunction endif " Callback -> Checks for tag callbacks and executes them. {{{1 if !exists("*s:Callback") function! s:Callback( xml_tag, isHtml ) let text = 0 if a:isHtml == 1 && exists ("*HtmlAttribCallback") let text = HtmlAttribCallback (a:xml_tag) elseif exists ("*XmlAttribCallback") let text = XmlAttribCallback (a:xml_tag) endif if text != '0' execute "normal! i " . text ."\l" endif endfunction endif " SavePos() saves position in bufferwide variable {{{1 if !exists('*s:SavePos') fun! s:SavePos() retu 'normal '.line('.').'G0'. (col('.') > 1 ? (col('.')-1).'l' : '') endf en " findOpenTag() {{{1 if !exists('*s:findOpenTag') fun! s:findOpenTag(flag) call search(s:OpenTag,a:flag) endf en " findCloseTag() {{{1 if !exists('*s:findCloseTag') fun! s:findCloseTag(flag) call search(s:CloseTag,a:flag) endf en " GetTagName() Gets the tagname from start position {{{1 "Now lets go for the name part. The namepart are xmlnamechars which "is quite a big range. We assume that everything after '<' or '' ends de name part. if !exists('*s:GetTagName') fun! s:GetTagName(from) let l:end = match(getline('.'), s:EndofName,a:from) return strpart(getline('.'),a:from, l:end - a:from ) endf en " hasAtt() Looks for attribute in open tag {{{1 " expect cursor to be on < if !exists('*s:hasAtt') fun! s:hasAtt() "Check if this open tag has attributes let l:line = line('.') | let l:col = col('.') if search(b:tagName . s:ReqAttrib,'W') > 0 if l:line == line('.') && l:col == (col('.')-1) let b:haveAtt = 1 en en endf en " TagUnderCursor() Is there a tag under the cursor? {{{1 " Set bufer wide variable " - b:firstWasEndTag " - b:tagName " - b:endcol & b:endline only used by Match() " - b:gotoCloseTag (if the tag under the cursor is one) " - b:gotoOpenTag (if the tag under the cursor is one) " on exit " - returns 1 (true) or 0 (false) " - position is at '<' if !exists('*s:TagUnderCursor') fun! s:TagUnderCursor() let b:firstWasEndTag = 0 let l:haveTag = 0 let b:haveAtt = 0 "Lets find forward a < or a >. If we first find a > we might be in a tag. "If we find a < first or nothing we are definitly not in a tag if getline('.')[col('.') - 1] == '>' let b:endcol = col('.') let b:endline = line('.') if getline('.')[col('.')-2] == '/' "we don't work with empty tags retu l:haveTag en " begin: gwang customization for JSP development if getline('.')[col('.')-2] == '%' "we don't work with jsp %> tags retu l:haveTag en " end: gwang customization for JSP development " begin: gwang customization for PHP development if getline('.')[col('.')-2] == '?' "we don't work with php ?> tags retu l:haveTag en " end: gwang customization for PHP development elseif search('[<>]','W') >0 if getline('.')[col('.')-1] == '>' let b:endcol = col('.') let b:endline = line('.') if getline('.')[col('.')-2] == '-' "we don't work with comment tags retu l:haveTag en if getline('.')[col('.')-2] == '/' "we don't work with empty tags retu l:haveTag en el retu l:haveTag en el retu l:haveTag en if search('[<>]','bW' ) >=0 if getline('.')[col('.')-1] == '<' if getline('.')[col('.')] == '/' let b:firstWasEndTag = 1 let b:gotoCloseTag = s:SavePos() elseif getline('.')[col('.')] == '?' || getline('.')[col('.')] == '!' "we don't deal with processing instructions or dtd "related definitions retu l:haveTag el let b:gotoOpenTag = s:SavePos() en el retu l:haveTag en el retu l:haveTag en "we have established that we are between something like "']*>' let b:tagName = s:GetTagName(col('.') + b:firstWasEndTag) "echo 'Tag ' . b:tagName "begin: gwang customization, do not work with an empty tag name if b:tagName == '' retu l:haveTag en "end: gwang customization, do not work with an empty tag name let l:haveTag = 1 if b:firstWasEndTag == 0 call s:hasAtt() exe b:gotoOpenTag en retu l:haveTag endf en " InComment() Is there a Comment under the cursor? {{{1 " - returns 1 (true) or 0 (false) if !exists('*s:InComment') fun! s:InComment() let b:endcom=0 let b:begcom=0 "Lets find forward a < or a >. If we first find a > we might be in a comment. "If we find a < first or nothing we are definitly not in a Comment if getline('.')[col('.') - 1] == '>' if getline('.')[col('.')-2] == '-' && getline('.')[col('.')-3] == '-' let b:endcomcol=col('.') let b:endcomline=line('.') let b:endcom=1 retu 1 en elseif getline('.')[col('.')-1] == '<' && getline('.')[col('.')] == '!' \ && getline('.')[col('.')+1] == '-' && getline('.')[col('.')+2] == '-' let b:begcomcol= col('.') let b:begcomline=line('.') let b:begcom=1 retu 1 en "We are not standing on a begin/end comment "Is the first > an ending comment? if search('[<>]','W') >0 if getline('.')[col('.')-1] == '>' if getline('.')[col('.')-2] == '-' && getline('.')[col('.')-3] == '-' let b:endcomcol=col('.') let b:endcomline=line('.') let b:endcom=1 retu 1 en en en "Forward is not a ending comment "is backward a starting comment if search('[<>]','bW' ) >=0 if getline('.')[col('.')-1] == '<' && getline('.')[col('.')] == '!' \ && getline('.')[col('.')+1] == '-' && getline('.')[col('.')+2] == '-' let b:begcomcol=col('.') let b:begcomline=line('.') let b:begcom=1 retu 1 en en retu 0 endf en " DelComment() Is there a Comment under the cursor? {{{1 " - returns 1 (true) or 0 (false) if !exists('*s:DelComment') fun! s:DelComment() let l:restore = s:SavePos() if s:InComment() if b:begcom if search('-->','W' ) >=0 normal hh3x exe 'normal '.b:begcomline.'G0'.(b:begcomcol>1?(b:begcomcol-1).'l':'') normal 4x retu 1 en el if search('','W' ) >=0 exe "normal f>a".l:sentinel."\" exe 'normal '.b:begcomline.'G0'.(b:begcomcol>1?(b:begcomcol-1).'l':'') exe "normal \"xd/".l:sentinel."/e-".l:len."\" exe "normal ".l:len."x" retu 1 en el if search(' " " cursor comes here " normal h if s:TagUnderCursor() if b:firstWasEndTag == 0 exe "normal 2f>s\\Ox\>>$x" start! retu en en elseif s:TagUnderCursor() if b:firstWasEndTag == 0 exe "normal />\" if b:html_mode && b:tagName =~? b:emptyTags if b:haveAtt == 0 call s:Callback (b:tagName, b:html_mode) en if b:xml_use_xhtml exe "normal i/\l" en if l:endOfLine start! retu el normal l start retu en el if b:haveAtt == 0 call s:Callback (b:tagName, b:html_mode) en exe "normal! a\F<" start retu en en en exe l:restore if (col('.')+1) == col("$") startinsert! else normal l startinsert en endf en " BlockTag() Surround a visual block with a tag {{{1 " Be carefull where You place the block " the top is done with insert! " the bottem is done with append! if !exists('*s:BlockTag') fun! s:BlockTag(multi) let l:newname = inputdialog('Surround block with : ',b:lastTag) if strlen( l:newname) == 0 retu en let b:lastTag = l:newname let l:newatt = inputdialog('Attributes for '.l:newname.' : ',b:lastAtt) if strlen(l:newatt) let b:lastAtt = l:newatt en "Get at the end of the block if col('.') == col("'<") && line('.') == line("'<") normal gvov en if a:multi exe "normal! a\\\" let l:eline = line('.') normal gvov if col('.') == col("'>") && line('.') == line("'>") normal gvov en let l:sline = line(".") + 2 exe "normal! i\<".l:newname. \ (strlen(l:newatt) ? ' '.l:newatt : '' ) \ .">\\" let l:rep=&report let &report=999999 exe l:sline.','.l:eline.'>' let &report= l:rep exe 'normal '.l:sline.'G0mh'.l:eline."G$v'hgq" el exe "normal! a\gvov" if col('.') == col("'>") && line('.') == line("'>") normal gvov en exe "normal! i<".l:newname. \ (strlen(l:newatt) ? ' '.l:newatt : '' ) \ .">\" en endf en " BlockWith() Surround a visual block with a open and a close {{{1 " Be carefull where You place the block " the top is done with insert! " the bottem is done with append! if !exists('*s:BlockWith') fun! s:BlockWith(open,close) if col('.') == col("'<") && line('.') == line("'<") normal gvov en exe "normal! a\;x\0cfx".a:close."\\" normal gvov exe "normal! i\;x\0cfx".a:open."\\" endf en " vlistitem() Surround a visual block with a listitem para tag {{{1 " Be carefull where You place the block " the top is done with insert! " the bottem is done with append! if !exists('*s:vlistitem') fun! s:vlistitem() "Get at the end of the block if col('.') == col("'<") && line('.') == line("'<") normal gvov en exe "normal! a\\mh" normal gvov exe "normal! i\\\\'h/listitem>/e+1\" endf en " Change() Only renames the tag {{{1 if !exists('*s:Change') fun! s:Change() let l:restore = s:SavePos() if s:TagUnderCursor() let l:newname = inputdialog('Change tag '.b:tagName.' to : ',b:lastTag) if strlen( l:newname) == 0 retu en let b:lastTag = l:newname if s:Match(b:tagName) exe b:gotoCloseTag exe 'normal 2lcw' . l:newname . "\" exe b:gotoOpenTag exe 'normal lcw' . l:newname . "\" en en endf en " Join() Joins two the same tag adjacent sections {{{1 if !exists('*s:Join') fun! s:Join() let l:restore = s:SavePos() if s:TagUnderCursor() let l:pat = '<[^?!]\S\+\($\| \|\t\|>\)' let l:flags='W' if b:firstWasEndTag == 0 let l:flags='Wb' en if search(s:OpenOrCloseTag,l:flags) > 0 let l:secondChar = getline('.')[col('.')] if l:secondChar == '/' && b:firstWasEndTag ||l:secondChar != '/' && !b:firstWasEndTag exe l:restore retu en let l:end = 0 if l:secondChar == '/' let l:end = 1 en let l:name = s:GetTagName(col('.') + l:end) if l:name == b:tagName if b:firstWasEndTag let b:gotoOpenTag = s:SavePos() el let b:gotoCloseTag = s:SavePos() en let l:DeleteTag = "normal d/>/e\" exe b:gotoCloseTag exe l:DeleteTag exe b:gotoOpenTag exe l:DeleteTag en en en exe l:restore endf en " ChangeWholeTag() removes attributes and rename tag {{{1 if !exists('*s:ChangeWholeTag') fun! s:ChangeWholeTag() if s:TagUnderCursor() let l:newname = inputdialog('Change whole tag '.b:tagName.' to : ',b:lastTag) if strlen(l:newname) == 0 retu en let b:lastTag = l:newname let l:newatt = inputdialog('Attributes for '.l:newname.' : ',b:lastAtt) if strlen(l:newatt) let b:lastAtt = l:newatt en if s:Match(b:tagName) exe b:gotoCloseTag exe "normal 2lc/>\".l:newname."\" exe b:gotoOpenTag exe "normal lc/>/\".l:newname. \ (strlen(l:newatt) ? ' '.l:newatt : '' ) \."\" en en endf en " Delete() Removes a tag 'blah' --> 'blah' {{{1 if !exists('*s:Delete') fun! s:Delete() let l:restore = s:SavePos() if s:TagUnderCursor() if s:Match(b:tagName) let l:DeleteTag = "normal d/>/e\" exe b:gotoCloseTag exe l:DeleteTag exe b:gotoOpenTag exe l:DeleteTag en else exe l:restore en endf en " DeleteSection() Deletes everything between start of open tag and end of {{{1 " closing tag if !exists('*s:DeleteSection') fun! s:DeleteSection() let l:restore = s:SavePos() if s:TagUnderCursor() if s:Match(b:tagName) let l:sentinel = 'XmLSeNtInElXmL' let l:len = strlen(l:sentinel) let l:rep=&report let &report=999999 exe b:gotoCloseTag exe "normal />\a".l:sentinel."\" exe b:gotoOpenTag exe "normal \"xd/".l:sentinel."/e-".l:len."\" exe "normal ".l:len."x" let &report= l:rep en en endf en " " FoldTag() Fold the tag under the cursor {{{1 if !exists('*s:FoldTag') fun! s:FoldTag() let l:restore = s:SavePos() if s:TagUnderCursor() let l:sline = line('.') if s:Match(b:tagName) if b:firstWasEndTag exe '.,'.l:sline.'fold' el exe l:sline.',.fold' en en el exe l:restore en endf en " FoldTagAll() Fold all tags of under the cursor {{{1 " If no tag under the cursor it asks for a tag if !exists('*s:FoldTagAll') fun! s:FoldTagAll() let l:restore = s:SavePos() if s:TagUnderCursor() let l:tname = b:tagName el let l:tname = inputdialog('Which tag to fold : ',b:lastTag) if strlen(l:tname) == 0 exe l:restore retu en let b:lastTag = l:tname en normal G$ let l:flag='w' while search('<'.l:tname.s:OptAttrib,l:flag) > 0 let l:flag='W' let l:sline = line('.') let l:level = 1 while l:level && search(' 0 let l:level = l:level + (getline('.')[col('.')] == '/' ? -1 : 1) endwhile if l:level == 0 exe l:sline.',.fold' el let l:tmp = \ inputdialog("The tag ".l:tname."(".l:sline.") doesn't have a closetag") break en endwhile exe l:restore endf en " StartTag() provide the opening tag {{{1 if !exists('*s:StartTag') fun! s:StartTag() let l:restore = s:SavePos() let l:level = 1 if getline('.')[col('.')-1] == '<' if s:TagUnderCursor() if b:firstWasEndTag exe 'normal! i<'. b:tagName.">\F<" retu el let l:level = l:level + 1 en en exe l:restore en while l:level && search(s:OpenOrCloseTag ,'W') > 0 let l:level = l:level + (getline('.')[col('.')] == '/' ? -1 : 1) endwhile if l:level == 0 let l:Name = s:GetTagName(col('.') + 1) exe l:restore exe 'normal! i<'. l:Name.">\" en exe l:restore endf en " EndTag() search for open tag and produce endtaf {{{1 if !exists('*s:EndTag') fun! s:EndTag() let l:restore = s:SavePos() let l:level = -1 while l:level && search(s:OpenOrCloseTag,'bW') > 0 let l:level = l:level + (getline('.')[col('.')] == '/' ? -1 : 1) endwhile if l:level == 0 let l:Name = s:GetTagName(col('.')) exe l:restore exe 'normal a\e" el exe l:restore en endf en " BeforeTag() surrounds the current tag with a new one {{{1 if !exists('*s:BeforeTag') fun! s:BeforeTag() let l:restore = s:SavePos() if s:TagUnderCursor() let l:newname = \ inputdialog('Surround Before Tag '.b:tagName.' with : ',b:lastTag) if strlen(l:newname) == 0 retu en let b:lastTag = l:newname let l:newatt = inputdialog('Attributes for '.l:newname.' : ',b:lastAtt) if strlen(l:newatt) let b:lastAtt = l:newatt en if s:Match(b:tagName) exe b:gotoCloseTag exe "normal! />\a\\" let l:To = line('.') exe b:gotoOpenTag exe 'normal! i<' . l:newname . \ (strlen(l:newatt) ? ' '.l:newatt : '' ) \.">\\" let l:rep=&report let &report=999999 exe line('.').','.l:To.'>' let &report= l:rep en exe l:restore en endf en " CommentTag() surrounds the current tag with a new one {{{1 if !exists('*s:CommentTag') fun! s:CommentTag() let l:restore = s:SavePos() if s:TagUnderCursor() if s:Match(b:tagName) exe b:gotoCloseTag exe "normal! />\a\-->\" let l:To = line('.') exe b:gotoOpenTag exe "normal! i') vmenu