diff options
author | Bram Moolenaar <Bram@vim.org> | 2005-11-23 21:25:05 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2005-11-23 21:25:05 +0000 |
commit | a5792f58905da28f0ab37e1c4c3cfd8171b2e602 (patch) | |
tree | 258ddcfde89bbf98b90d42dd6c297e1b22a20d09 /runtime/autoload/xmlcomplete.vim | |
parent | af289d333a2985051948a53d510fa345df1ddeb3 (diff) | |
download | vim-git-a5792f58905da28f0ab37e1c4c3cfd8171b2e602.tar.gz |
updated for version 7.0158v7.0158
Diffstat (limited to 'runtime/autoload/xmlcomplete.vim')
-rw-r--r-- | runtime/autoload/xmlcomplete.vim | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/runtime/autoload/xmlcomplete.vim b/runtime/autoload/xmlcomplete.vim new file mode 100644 index 000000000..3cd4c7164 --- /dev/null +++ b/runtime/autoload/xmlcomplete.vim @@ -0,0 +1,428 @@ +" Vim completion script +" Language: XHTML 1.0 Strict +" Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) +" Last Change: 2005 Nov 22 + +" This function will create Dictionary with users namespace strings and values +" canonical (system) names of data files. Names should be lowercase, +" descriptive to avoid any future conflicts. For example 'xhtml10s' should be +" name for data of XHTML 1.0 Strict and 'xhtml10t' for XHTML 1.0 Transitional +" User interface will be provided by XMLns command defined ... +" Currently supported canonicals are: +" xhtml10s - XHTML 1.0 Strict +" xsl - XSL +function! xmlcomplete#CreateConnection(canonical, ...) + + " When only one argument provided treat name as default namespace (without + " 'prefix:'). + if exists("a:1") + let users = a:1 + else + let users = 'DEFAULT' + endif + + " Source data file. Due to suspected errors in autoload do it with + " :runtime. + " TODO: make it properly (using autoload, that is) later + exe "runtime autoload/xml/".a:canonical.".vim" + + " Remove all traces of unexisting files to return [] when trying + " omnicomplete something + " TODO: give warning about non-existing canonicals - should it be? + if !exists("g:xmldata_".a:canonical) + unlet! g:xmldata_connection + return 0 + endif + + " We need to initialize Dictionary to add key-value pair + if !exists("g:xmldata_connection") + let g:xmldata_connection = {} + endif + + let g:xmldata_connection[users] = a:canonical + +endfunction + +function! xmlcomplete#CreateEntConnection(...) + if a:0 > 0 + let g:xmldata_entconnect = a:1 + else + let g:xmldata_entconnect = 'DEFAULT' + endif +endfunction + +function! xmlcomplete#CompleteTags(findstart, base) + if a:findstart + " locate the start of the word + let line = getline('.') + let start = col('.') - 1 + let compl_begin = col('.') - 2 + + while start >= 0 && line[start - 1] =~ '\(\k\|[:.-]\)' + let start -= 1 + endwhile + + if start >= 0 && line[start - 1] =~ '&' + let b:entitiescompl = 1 + let b:compl_context = '' + return start + endif + + let b:compl_context = getline('.')[0:(compl_begin)] + let b:compl_context = matchstr(b:compl_context, '.*<\zs.*') + + " Make sure we will have only current namespace + unlet! b:xml_namespace + let b:xml_namespace = matchstr(b:compl_context, '^\k*\ze:') + if b:xml_namespace == '' + let b:xml_namespace = 'DEFAULT' + endif + + return start + + else + " There is no connction of namespace and data file. Abandon action + if !exists("g:xmldata_connection") || g:xmldata_connection == {} + return [] + endif + " Initialize base return lists + let res = [] + let res2 = [] + " a:base is very short - we need context + let context = b:compl_context + unlet! b:compl_context + + " Make entities completion + if exists("b:entitiescompl") + unlet! b:entitiescompl + + if !exists("g:xmldata_entconnect") || g:xmldata_entconnect == 'DEFAULT' + let values = g:xmldata{'_'.g:xmldata_connection['DEFAULT']}['vimxmlentities'] + else + let values = g:xmldata{'_'.g:xmldata_entconnect}['vimxmlentities'] + endif + + " Get only lines with entity declarations but throw out + " parameter-entities - they may be completed in future + let entdecl = filter(getline(1, "$"), 'v:val =~ "<!ENTITY\\s\\+[^%]"') + + if len(entdecl) > 0 + let intent = map(copy(entdecl), 'matchstr(v:val, "<!ENTITY\\s\\+\\zs\\(\\k\\|[.-:]\\)\\+\\ze")') + let values = intent + values + endif + + for m in values + if m =~ '^'.a:base + call add(res, m.';') + endif + endfor + + return res + + endif + if context =~ '>' + " Generally if context contains > it means we are outside of tag and + " should abandon action + return [] + endif + + " find tags matching with "a:base" + " If a:base contains white space it is attribute. + " It could be also value of attribute... + " We have to get first word to offer + " proper completions + if context == '' + let tag = '' + else + let tag = split(context)[0] + endif + " Get rid of namespace + let tag = substitute(tag, '^'.b:xml_namespace.':', '', '') + + + " Get last word, it should be attr name + let attr = matchstr(context, '.*\s\zs.*') + " Possible situations where any prediction would be difficult: + " 1. Events attributes + if context =~ '\s' + + " If attr contains =\s*[\"'] we catched value of attribute + if attr =~ "=\s*[\"']" + " Let do attribute specific completion + let attrname = matchstr(attr, '.*\ze\s*=') + let entered_value = matchstr(attr, ".*=\\s*[\"']\\zs.*") + + if tag =~ '^[?!]' + " Return nothing if we are inside of ! or ? tag + return [] + else + let values = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][attrname] + endif + + if len(values) == 0 + return [] + endif + + " We need special version of sbase + let attrbase = matchstr(context, ".*[\"']") + let attrquote = matchstr(attrbase, '.$') + + for m in values + " This if is needed to not offer all completions as-is + " alphabetically but sort them. Those beginning with entered + " part will be as first choices + if m =~ '^'.entered_value + call add(res, m . attrquote.' ') + elseif m =~ entered_value + call add(res2, m . attrquote.' ') + endif + endfor + + return res + res2 + + endif + + if tag =~ '?xml' + " Two possible arguments for <?xml> plus variation + let attrs = ['encoding', 'version="1.0"', 'version'] + elseif tag =~ '^!' + " Don't make completion at all + return [] + else + let attrs = keys(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1]) + endif + + for m in sort(attrs) + if m =~ '^'.attr + if tag !~ '^[?!]' && len(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][m]) > 0 && g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][m][0] =~ '^BOOL$' + call add(res, m) + elseif m =~ '=' + call add(res, m) + else + call add(res, m.'="') + endif + elseif m =~ attr + if tag !~ '^[?!]' && len(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][m]) > 0 && g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][m][0] =~ '^BOOL$' + call add(res, m) + elseif m =~ '=' + call add(res, m) + else + call add(res2, m.'="') + endif + endif + endfor + + return res + res2 + + endif + " Close tag + let b:unaryTagsStack = "base meta link hr br param img area input col" + if context =~ '^\/' + let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack") + return [opentag.">"] + endif + + " Complete elements of XML structure + " TODO: #REQUIRED, #IMPLIED, #FIXED, #PCDATA - but these should be detected like + " entities - in first run + " keywords: CDATA, ID, IDREF, IDREFS, ENTITY, ENTITIES, NMTOKEN, NMTOKENS + " are hardly recognizable but keep it in reserve + " also: EMPTY ANY SYSTEM PUBLIC DATA + if context =~ '^!' + let tags = ['!ELEMENT', '!DOCTYPE', '!ATTLIST', '!ENTITY', '!NOTATION', '![CDATA[', '![INCLUDE[', '![IGNORE['] + + for m in tags + if m =~ '^'.context + let m = substitute(m, '^!\[\?', '', '') + call add(res, m) + elseif m =~ context + let m = substitute(m, '^!\[\?', '', '') + call add(res2, m) + endif + endfor + + return res + res2 + + endif + + " Complete text declaration + let g:co = context + if context =~ '^?' + let tags = ['?xml'] + + for m in tags + if m =~ '^'.context + call add(res, substitute(m, '^?', '', '')) + elseif m =~ context + call add(res, substitute(m, '^?', '', '')) + endif + endfor + + return res + res2 + + endif + + " Deal with tag completion. + let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack") + let opentag = substitute(opentag, '^\k*:', '', '') + + let tags = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[opentag][0] + let context = substitute(context, '^\k*:', '', '') + + if b:xml_namespace == 'DEFAULT' + let b:xml_namespace = '' + else + let b:xml_namespace .= ':' + endif + + for m in tags + if m =~ '^'.context + call add(res, b:xml_namespace.m) + elseif m =~ context + call add(res2, b:xml_namespace.m) + endif + endfor + + return res + res2 + + endif +endfunction + +" MM: This is greatly reduced closetag.vim used with kind permission of Steven +" Mueller +" Changes: strip all comments; delete error messages; add checking for +" namespace +" Author: Steven Mueller <diffusor@ugcs.caltech.edu> +" Last Modified: Tue May 24 13:29:48 PDT 2005 +" Version: 0.9.1 + +function! xmlcomplete#GetLastOpenTag(unaryTagsStack) + let linenum=line('.') + let lineend=col('.') - 1 " start: cursor position + let first=1 " flag for first line searched + let b:TagStack='' " main stack of tags + let startInComment=s:InComment() + + if exists("b:xml_namespace") + if b:xml_namespace == 'DEFAULT' + let tagpat='</\=\(\k\|[.-]\)\+\|/>' + else + let tagpat='</\='.b:xml_namespace.':\(\k\|[.-]\)\+\|/>' + endif + else + let tagpat='</\=\(\k\|[.-:]\)\+\|/>' + endif + while (linenum>0) + let line=getline(linenum) + if first + let line=strpart(line,0,lineend) + else + let lineend=strlen(line) + endif + let b:lineTagStack='' + let mpos=0 + let b:TagCol=0 + while (mpos > -1) + let mpos=matchend(line,tagpat) + if mpos > -1 + let b:TagCol=b:TagCol+mpos + let tag=matchstr(line,tagpat) + + if exists('b:closetag_disable_synID') || startInComment==s:InCommentAt(linenum, b:TagCol) + let b:TagLine=linenum + call s:Push(matchstr(tag,'[^<>]\+'),'b:lineTagStack') + endif + let lineend=lineend-mpos + let line=strpart(line,mpos,lineend) + endif + endwhile + while (!s:EmptystackP('b:lineTagStack')) + let tag=s:Pop('b:lineTagStack') + if match(tag, '^/') == 0 "found end tag + call s:Push(tag,'b:TagStack') + elseif s:EmptystackP('b:TagStack') && !s:Instack(tag, a:unaryTagsStack) "found unclosed tag + return tag + else + let endtag=s:Peekstack('b:TagStack') + if endtag == '/'.tag || endtag == '/' + call s:Pop('b:TagStack') "found a open/close tag pair + elseif !s:Instack(tag, a:unaryTagsStack) "we have a mismatch error + return '' + endif + endif + endwhile + let linenum=linenum-1 | let first=0 + endwhile +return '' +endfunction + +function! s:InComment() + return synIDattr(synID(line('.'), col('.'), 0), 'name') =~ 'Comment' +endfunction + +function! s:InCommentAt(line, col) + return synIDattr(synID(a:line, a:col, 0), 'name') =~ 'Comment' +endfunction + +function! s:SetKeywords() + let g:IsKeywordBak=&iskeyword + let &iskeyword='33-255' +endfunction + +function! s:RestoreKeywords() + let &iskeyword=g:IsKeywordBak +endfunction + +function! s:Push(el, sname) + if !s:EmptystackP(a:sname) + exe 'let '.a:sname."=a:el.' '.".a:sname + else + exe 'let '.a:sname.'=a:el' + endif +endfunction + +function! s:EmptystackP(sname) + exe 'let stack='.a:sname + if match(stack,'^ *$') == 0 + return 1 + else + return 0 + endif +endfunction + +function! s:Instack(el, sname) + exe 'let stack='.a:sname + call s:SetKeywords() + let m=match(stack, '\<'.a:el.'\>') + call s:RestoreKeywords() + if m < 0 + return 0 + else + return 1 + endif +endfunction + +function! s:Peekstack(sname) + call s:SetKeywords() + exe 'let stack='.a:sname + let top=matchstr(stack, '\<.\{-1,}\>') + call s:RestoreKeywords() + return top +endfunction + +function! s:Pop(sname) + if s:EmptystackP(a:sname) + return '' + endif + exe 'let stack='.a:sname + call s:SetKeywords() + let loc=matchend(stack,'\<.\{-1,}\>') + exe 'let '.a:sname.'=strpart(stack, loc+1, strlen(stack))' + let top=strpart(stack, match(stack, '\<'), loc) + call s:RestoreKeywords() + return top +endfunction + +function! s:Clearstack(sname) + exe 'let '.a:sname."=''" +endfunction |