Mercurial > vim
comparison runtime/autoload/xmlcomplete.vim @ 557:862863033fdd v7.0158
updated for version 7.0158
author | vimboss |
---|---|
date | Wed, 23 Nov 2005 21:25:05 +0000 |
parents | |
children | 25a70b1cd2da |
comparison
equal
deleted
inserted
replaced
556:f9eaf0a9872d | 557:862863033fdd |
---|---|
1 " Vim completion script | |
2 " Language: XHTML 1.0 Strict | |
3 " Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl ) | |
4 " Last Change: 2005 Nov 22 | |
5 | |
6 " This function will create Dictionary with users namespace strings and values | |
7 " canonical (system) names of data files. Names should be lowercase, | |
8 " descriptive to avoid any future conflicts. For example 'xhtml10s' should be | |
9 " name for data of XHTML 1.0 Strict and 'xhtml10t' for XHTML 1.0 Transitional | |
10 " User interface will be provided by XMLns command defined ... | |
11 " Currently supported canonicals are: | |
12 " xhtml10s - XHTML 1.0 Strict | |
13 " xsl - XSL | |
14 function! xmlcomplete#CreateConnection(canonical, ...) | |
15 | |
16 " When only one argument provided treat name as default namespace (without | |
17 " 'prefix:'). | |
18 if exists("a:1") | |
19 let users = a:1 | |
20 else | |
21 let users = 'DEFAULT' | |
22 endif | |
23 | |
24 " Source data file. Due to suspected errors in autoload do it with | |
25 " :runtime. | |
26 " TODO: make it properly (using autoload, that is) later | |
27 exe "runtime autoload/xml/".a:canonical.".vim" | |
28 | |
29 " Remove all traces of unexisting files to return [] when trying | |
30 " omnicomplete something | |
31 " TODO: give warning about non-existing canonicals - should it be? | |
32 if !exists("g:xmldata_".a:canonical) | |
33 unlet! g:xmldata_connection | |
34 return 0 | |
35 endif | |
36 | |
37 " We need to initialize Dictionary to add key-value pair | |
38 if !exists("g:xmldata_connection") | |
39 let g:xmldata_connection = {} | |
40 endif | |
41 | |
42 let g:xmldata_connection[users] = a:canonical | |
43 | |
44 endfunction | |
45 | |
46 function! xmlcomplete#CreateEntConnection(...) | |
47 if a:0 > 0 | |
48 let g:xmldata_entconnect = a:1 | |
49 else | |
50 let g:xmldata_entconnect = 'DEFAULT' | |
51 endif | |
52 endfunction | |
53 | |
54 function! xmlcomplete#CompleteTags(findstart, base) | |
55 if a:findstart | |
56 " locate the start of the word | |
57 let line = getline('.') | |
58 let start = col('.') - 1 | |
59 let compl_begin = col('.') - 2 | |
60 | |
61 while start >= 0 && line[start - 1] =~ '\(\k\|[:.-]\)' | |
62 let start -= 1 | |
63 endwhile | |
64 | |
65 if start >= 0 && line[start - 1] =~ '&' | |
66 let b:entitiescompl = 1 | |
67 let b:compl_context = '' | |
68 return start | |
69 endif | |
70 | |
71 let b:compl_context = getline('.')[0:(compl_begin)] | |
72 let b:compl_context = matchstr(b:compl_context, '.*<\zs.*') | |
73 | |
74 " Make sure we will have only current namespace | |
75 unlet! b:xml_namespace | |
76 let b:xml_namespace = matchstr(b:compl_context, '^\k*\ze:') | |
77 if b:xml_namespace == '' | |
78 let b:xml_namespace = 'DEFAULT' | |
79 endif | |
80 | |
81 return start | |
82 | |
83 else | |
84 " There is no connction of namespace and data file. Abandon action | |
85 if !exists("g:xmldata_connection") || g:xmldata_connection == {} | |
86 return [] | |
87 endif | |
88 " Initialize base return lists | |
89 let res = [] | |
90 let res2 = [] | |
91 " a:base is very short - we need context | |
92 let context = b:compl_context | |
93 unlet! b:compl_context | |
94 | |
95 " Make entities completion | |
96 if exists("b:entitiescompl") | |
97 unlet! b:entitiescompl | |
98 | |
99 if !exists("g:xmldata_entconnect") || g:xmldata_entconnect == 'DEFAULT' | |
100 let values = g:xmldata{'_'.g:xmldata_connection['DEFAULT']}['vimxmlentities'] | |
101 else | |
102 let values = g:xmldata{'_'.g:xmldata_entconnect}['vimxmlentities'] | |
103 endif | |
104 | |
105 " Get only lines with entity declarations but throw out | |
106 " parameter-entities - they may be completed in future | |
107 let entdecl = filter(getline(1, "$"), 'v:val =~ "<!ENTITY\\s\\+[^%]"') | |
108 | |
109 if len(entdecl) > 0 | |
110 let intent = map(copy(entdecl), 'matchstr(v:val, "<!ENTITY\\s\\+\\zs\\(\\k\\|[.-:]\\)\\+\\ze")') | |
111 let values = intent + values | |
112 endif | |
113 | |
114 for m in values | |
115 if m =~ '^'.a:base | |
116 call add(res, m.';') | |
117 endif | |
118 endfor | |
119 | |
120 return res | |
121 | |
122 endif | |
123 if context =~ '>' | |
124 " Generally if context contains > it means we are outside of tag and | |
125 " should abandon action | |
126 return [] | |
127 endif | |
128 | |
129 " find tags matching with "a:base" | |
130 " If a:base contains white space it is attribute. | |
131 " It could be also value of attribute... | |
132 " We have to get first word to offer | |
133 " proper completions | |
134 if context == '' | |
135 let tag = '' | |
136 else | |
137 let tag = split(context)[0] | |
138 endif | |
139 " Get rid of namespace | |
140 let tag = substitute(tag, '^'.b:xml_namespace.':', '', '') | |
141 | |
142 | |
143 " Get last word, it should be attr name | |
144 let attr = matchstr(context, '.*\s\zs.*') | |
145 " Possible situations where any prediction would be difficult: | |
146 " 1. Events attributes | |
147 if context =~ '\s' | |
148 | |
149 " If attr contains =\s*[\"'] we catched value of attribute | |
150 if attr =~ "=\s*[\"']" | |
151 " Let do attribute specific completion | |
152 let attrname = matchstr(attr, '.*\ze\s*=') | |
153 let entered_value = matchstr(attr, ".*=\\s*[\"']\\zs.*") | |
154 | |
155 if tag =~ '^[?!]' | |
156 " Return nothing if we are inside of ! or ? tag | |
157 return [] | |
158 else | |
159 let values = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][attrname] | |
160 endif | |
161 | |
162 if len(values) == 0 | |
163 return [] | |
164 endif | |
165 | |
166 " We need special version of sbase | |
167 let attrbase = matchstr(context, ".*[\"']") | |
168 let attrquote = matchstr(attrbase, '.$') | |
169 | |
170 for m in values | |
171 " This if is needed to not offer all completions as-is | |
172 " alphabetically but sort them. Those beginning with entered | |
173 " part will be as first choices | |
174 if m =~ '^'.entered_value | |
175 call add(res, m . attrquote.' ') | |
176 elseif m =~ entered_value | |
177 call add(res2, m . attrquote.' ') | |
178 endif | |
179 endfor | |
180 | |
181 return res + res2 | |
182 | |
183 endif | |
184 | |
185 if tag =~ '?xml' | |
186 " Two possible arguments for <?xml> plus variation | |
187 let attrs = ['encoding', 'version="1.0"', 'version'] | |
188 elseif tag =~ '^!' | |
189 " Don't make completion at all | |
190 return [] | |
191 else | |
192 let attrs = keys(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1]) | |
193 endif | |
194 | |
195 for m in sort(attrs) | |
196 if m =~ '^'.attr | |
197 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$' | |
198 call add(res, m) | |
199 elseif m =~ '=' | |
200 call add(res, m) | |
201 else | |
202 call add(res, m.'="') | |
203 endif | |
204 elseif m =~ attr | |
205 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$' | |
206 call add(res, m) | |
207 elseif m =~ '=' | |
208 call add(res, m) | |
209 else | |
210 call add(res2, m.'="') | |
211 endif | |
212 endif | |
213 endfor | |
214 | |
215 return res + res2 | |
216 | |
217 endif | |
218 " Close tag | |
219 let b:unaryTagsStack = "base meta link hr br param img area input col" | |
220 if context =~ '^\/' | |
221 let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack") | |
222 return [opentag.">"] | |
223 endif | |
224 | |
225 " Complete elements of XML structure | |
226 " TODO: #REQUIRED, #IMPLIED, #FIXED, #PCDATA - but these should be detected like | |
227 " entities - in first run | |
228 " keywords: CDATA, ID, IDREF, IDREFS, ENTITY, ENTITIES, NMTOKEN, NMTOKENS | |
229 " are hardly recognizable but keep it in reserve | |
230 " also: EMPTY ANY SYSTEM PUBLIC DATA | |
231 if context =~ '^!' | |
232 let tags = ['!ELEMENT', '!DOCTYPE', '!ATTLIST', '!ENTITY', '!NOTATION', '![CDATA[', '![INCLUDE[', '![IGNORE['] | |
233 | |
234 for m in tags | |
235 if m =~ '^'.context | |
236 let m = substitute(m, '^!\[\?', '', '') | |
237 call add(res, m) | |
238 elseif m =~ context | |
239 let m = substitute(m, '^!\[\?', '', '') | |
240 call add(res2, m) | |
241 endif | |
242 endfor | |
243 | |
244 return res + res2 | |
245 | |
246 endif | |
247 | |
248 " Complete text declaration | |
249 let g:co = context | |
250 if context =~ '^?' | |
251 let tags = ['?xml'] | |
252 | |
253 for m in tags | |
254 if m =~ '^'.context | |
255 call add(res, substitute(m, '^?', '', '')) | |
256 elseif m =~ context | |
257 call add(res, substitute(m, '^?', '', '')) | |
258 endif | |
259 endfor | |
260 | |
261 return res + res2 | |
262 | |
263 endif | |
264 | |
265 " Deal with tag completion. | |
266 let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack") | |
267 let opentag = substitute(opentag, '^\k*:', '', '') | |
268 | |
269 let tags = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[opentag][0] | |
270 let context = substitute(context, '^\k*:', '', '') | |
271 | |
272 if b:xml_namespace == 'DEFAULT' | |
273 let b:xml_namespace = '' | |
274 else | |
275 let b:xml_namespace .= ':' | |
276 endif | |
277 | |
278 for m in tags | |
279 if m =~ '^'.context | |
280 call add(res, b:xml_namespace.m) | |
281 elseif m =~ context | |
282 call add(res2, b:xml_namespace.m) | |
283 endif | |
284 endfor | |
285 | |
286 return res + res2 | |
287 | |
288 endif | |
289 endfunction | |
290 | |
291 " MM: This is greatly reduced closetag.vim used with kind permission of Steven | |
292 " Mueller | |
293 " Changes: strip all comments; delete error messages; add checking for | |
294 " namespace | |
295 " Author: Steven Mueller <diffusor@ugcs.caltech.edu> | |
296 " Last Modified: Tue May 24 13:29:48 PDT 2005 | |
297 " Version: 0.9.1 | |
298 | |
299 function! xmlcomplete#GetLastOpenTag(unaryTagsStack) | |
300 let linenum=line('.') | |
301 let lineend=col('.') - 1 " start: cursor position | |
302 let first=1 " flag for first line searched | |
303 let b:TagStack='' " main stack of tags | |
304 let startInComment=s:InComment() | |
305 | |
306 if exists("b:xml_namespace") | |
307 if b:xml_namespace == 'DEFAULT' | |
308 let tagpat='</\=\(\k\|[.-]\)\+\|/>' | |
309 else | |
310 let tagpat='</\='.b:xml_namespace.':\(\k\|[.-]\)\+\|/>' | |
311 endif | |
312 else | |
313 let tagpat='</\=\(\k\|[.-:]\)\+\|/>' | |
314 endif | |
315 while (linenum>0) | |
316 let line=getline(linenum) | |
317 if first | |
318 let line=strpart(line,0,lineend) | |
319 else | |
320 let lineend=strlen(line) | |
321 endif | |
322 let b:lineTagStack='' | |
323 let mpos=0 | |
324 let b:TagCol=0 | |
325 while (mpos > -1) | |
326 let mpos=matchend(line,tagpat) | |
327 if mpos > -1 | |
328 let b:TagCol=b:TagCol+mpos | |
329 let tag=matchstr(line,tagpat) | |
330 | |
331 if exists('b:closetag_disable_synID') || startInComment==s:InCommentAt(linenum, b:TagCol) | |
332 let b:TagLine=linenum | |
333 call s:Push(matchstr(tag,'[^<>]\+'),'b:lineTagStack') | |
334 endif | |
335 let lineend=lineend-mpos | |
336 let line=strpart(line,mpos,lineend) | |
337 endif | |
338 endwhile | |
339 while (!s:EmptystackP('b:lineTagStack')) | |
340 let tag=s:Pop('b:lineTagStack') | |
341 if match(tag, '^/') == 0 "found end tag | |
342 call s:Push(tag,'b:TagStack') | |
343 elseif s:EmptystackP('b:TagStack') && !s:Instack(tag, a:unaryTagsStack) "found unclosed tag | |
344 return tag | |
345 else | |
346 let endtag=s:Peekstack('b:TagStack') | |
347 if endtag == '/'.tag || endtag == '/' | |
348 call s:Pop('b:TagStack') "found a open/close tag pair | |
349 elseif !s:Instack(tag, a:unaryTagsStack) "we have a mismatch error | |
350 return '' | |
351 endif | |
352 endif | |
353 endwhile | |
354 let linenum=linenum-1 | let first=0 | |
355 endwhile | |
356 return '' | |
357 endfunction | |
358 | |
359 function! s:InComment() | |
360 return synIDattr(synID(line('.'), col('.'), 0), 'name') =~ 'Comment' | |
361 endfunction | |
362 | |
363 function! s:InCommentAt(line, col) | |
364 return synIDattr(synID(a:line, a:col, 0), 'name') =~ 'Comment' | |
365 endfunction | |
366 | |
367 function! s:SetKeywords() | |
368 let g:IsKeywordBak=&iskeyword | |
369 let &iskeyword='33-255' | |
370 endfunction | |
371 | |
372 function! s:RestoreKeywords() | |
373 let &iskeyword=g:IsKeywordBak | |
374 endfunction | |
375 | |
376 function! s:Push(el, sname) | |
377 if !s:EmptystackP(a:sname) | |
378 exe 'let '.a:sname."=a:el.' '.".a:sname | |
379 else | |
380 exe 'let '.a:sname.'=a:el' | |
381 endif | |
382 endfunction | |
383 | |
384 function! s:EmptystackP(sname) | |
385 exe 'let stack='.a:sname | |
386 if match(stack,'^ *$') == 0 | |
387 return 1 | |
388 else | |
389 return 0 | |
390 endif | |
391 endfunction | |
392 | |
393 function! s:Instack(el, sname) | |
394 exe 'let stack='.a:sname | |
395 call s:SetKeywords() | |
396 let m=match(stack, '\<'.a:el.'\>') | |
397 call s:RestoreKeywords() | |
398 if m < 0 | |
399 return 0 | |
400 else | |
401 return 1 | |
402 endif | |
403 endfunction | |
404 | |
405 function! s:Peekstack(sname) | |
406 call s:SetKeywords() | |
407 exe 'let stack='.a:sname | |
408 let top=matchstr(stack, '\<.\{-1,}\>') | |
409 call s:RestoreKeywords() | |
410 return top | |
411 endfunction | |
412 | |
413 function! s:Pop(sname) | |
414 if s:EmptystackP(a:sname) | |
415 return '' | |
416 endif | |
417 exe 'let stack='.a:sname | |
418 call s:SetKeywords() | |
419 let loc=matchend(stack,'\<.\{-1,}\>') | |
420 exe 'let '.a:sname.'=strpart(stack, loc+1, strlen(stack))' | |
421 let top=strpart(stack, match(stack, '\<'), loc) | |
422 call s:RestoreKeywords() | |
423 return top | |
424 endfunction | |
425 | |
426 function! s:Clearstack(sname) | |
427 exe 'let '.a:sname."=''" | |
428 endfunction |