blob: a9791451f0d29adc5b10192d727e7f4753a5a7c8 [file] [log] [blame]
Bram Moolenaarf75a9632005-09-13 21:20:47 +00001" Vim completion script
2" Language: XHTML 1.0 Strict
3" Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl )
Bram Moolenaar6b730e12005-09-16 21:47:57 +00004" Last Change: 2005 Sep 15
Bram Moolenaarf75a9632005-09-13 21:20:47 +00005
6function! htmlcomplete#CompleteTags(findstart, base)
7 if a:findstart
8 " locate the start of the word
9 let line = getline('.')
10 let start = col('.') - 1
11 while start >= 0 && line[start - 1] !~ '<'
12 let start -= 1
13 endwhile
Bram Moolenaarf75a9632005-09-13 21:20:47 +000014 return start
15 else
16 " Set attribute groups
17 let g:coreattrs = ["id", "class", "style", "title"]
18 let g:i18n = ["lang", "xml:lang", "dir"]
19 let g:events = ["onclick", "ondblclick", "onmousedown", "onmouseup", "onmousemove",
20 \ "onmouseout", "onkeypress", "onkeydown", "onkeyup"]
21 let g:focus = ["accesskey", "tabindex", "onfocus", "onblur"]
22 let g:coregroup = g:coreattrs
23 let g:coregroup = extend(g:coregroup, g:i18n)
24 let g:coregroup = extend(g:coregroup, g:events)
25 " find tags matching with "a:base"
26 let res = []
27 " If a:base contains > it means we are already outside of tag and we
28 " should abandon action
29 if a:base =~ '>'
30 return []
31 endif
32 " If a:base contains white space it is attribute.
33 " It could be also value of attribute...
34 " Possible situations where any prediction would be difficult:
35 " 1. Events attributes
36 if a:base =~ '\s'
37 " Sort out style, class, and on* cases
38 " Perfect solution for style would be switching for CSS completion. Is
39 " it possible?
40 " Also retrieving class names from current file and linked
41 " stylesheets.
42 if a:base =~ "\\(on[a-z]*\\|style\\|class\\)\\s*=\\s*[\"']"
43 let stripbase = matchstr(a:base, ".*\\(on[a-z]*\\|style\\|class\\)\\s*=\\s*[\"']\\zs.*")
44 " Now we have a:base stripped from all chars up to style/class.
45 " It may fail with some strange style value combinations.
46 if stripbase !~ "[\"']"
47 return []
48 endif
49 endif
50 " We have to get first word to offer
51 " proper attributes.
52 let tag = split(a:base)[0]
53 " Get last word, it should be attr name
54 let attr = matchstr(a:base, '.*\s\zs.*')
55 " If attr contains =\s*[\"'] we catched value of attribute
56 if attr =~ "=\s*[\"']"
57 " Let do attribute specific completion
58 let attrname = matchstr(attr, '.*\ze\s*=')
59 let entered_value = matchstr(attr, ".*=\\s*[\"']\\zs.*")
60 let values = []
61 if attrname == 'media'
62 let values = ["screen", "tty", "tv", "projection", "handheld", "print", "braille", "aural", "all"]
63 elseif attrname == 'xml:space'
64 let values = ["preserve"]
65 elseif attrname == 'shape'
66 if a:base =~ '^a\>'
67 let values = ["rect"]
68 else
69 let values = ["rect", "circle", "poly", "default"]
70 endif
71 elseif attrname == 'valuetype'
72 let values = ["data", "ref", "object"]
73 elseif attrname == 'method'
74 let values = ["get", "post"]
Bram Moolenaar4c903f92005-09-14 21:32:32 +000075 elseif attrname == 'dir'
76 let values = ["ltr", "rtl"]
Bram Moolenaarf75a9632005-09-13 21:20:47 +000077 elseif attrname == 'frame'
78 let values = ["void", "above", "below", "hsides", "lhs", "rhs", "vsides", "box", "border"]
79 elseif attrname == 'rules'
80 let values = ["none", "groups", "rows", "all"]
81 elseif attrname == 'align'
82 let values = ["left", "center", "right", "justify", "char"]
83 elseif attrname == 'valign'
84 let values = ["top", "middle", "bottom", "baseline"]
85 elseif attrname == 'scope'
86 let values = ["row", "col", "rowgroup", "colgroup"]
87 elseif attrname == 'href'
88 " Now we are looking for local anchors defined by name or id
89 if entered_value =~ '^#'
90 let file = join(getline(1, line('$')), ' ')
91 " Split it be sure there will be one id/name element in
92 " item, it will be also first word [a-zA-Z0-9_-] in element
93 let oneelement = split(file, "\\(meta \\)\\@<!\\(name\\|id\\)\\s*=\\s*[\"']")
94 for i in oneelement
95 let values += ['#'.matchstr(i, "^[a-zA-Z][a-zA-Z0-9%_-]*")]
96 endfor
97 endif
98 elseif attrname == 'type'
99 if a:base =~ '^input'
100 let values = ["input-text", "password", "checkbox", "radio", "submit", "reset", "input-file", "hidden", "input-image", "input-button"]
101 elseif a:base =~ '^button'
102 let values = ["button", "submit", "reset"]
103 endif
104 else
105 return []
106 endif
107
108 if len(values) == 0
109 return []
110 endif
111
112 " We need special version of sbase
113 let attrbase = matchstr(a:base, ".*[\"']")
114
115 for m in values
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000116 if m =~ entered_value
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000117 call add(res, attrbase . m . '" ')
118 endif
119 endfor
120 endif
121 " Shorten a:base to not include last word
122 let sbase = matchstr(a:base, '.*\ze\s.*')
123 if tag =~ '^\(abbr\|acronym\|b\|bdo\|big\|caption\|cite\|code\|dd\|dfn\|div\|dl\|dt\|em\|fieldset\|h\d\|kbd\|li\|noscript\|ol\|p\|samp\|small\|span\|strong\|sub\|sup\|tt\|ul\|var\)$'
124 let attrs = g:coregroup
125 elseif tag == 'a'
126 let tagspec = ["charset", "type", "name", "href", "hreflang", "rel", "rev", "shape", "coords"]
127 let attrs = extend(tagspec, g:coregroup)
128 let attrs = extend(attrs, g:focus)
129 elseif tag == 'area'
130 let attrs = g:coregroup
131 elseif tag == 'base'
132 let attrs = ["href", "id"]
133 elseif tag == 'blockquote'
134 let attrs = g:coregroup
135 let attrs = extend(attrs, ["cite"])
136 elseif tag == 'body'
137 let attrs = g:coregroup
138 let attrs = extend(attrs, ["onload", "onunload"])
139 elseif tag == 'br'
140 let attrs = g:coreattrs
141 elseif tag == 'button'
142 let attrs = g:coreattrs
143 let attrs = extend(attrs, g:focus)
144 let attrs = extend(attrs, ["name", "value", "type"])
145 elseif tag == '^\(col\|colgroup\)$'
146 let attrs = g:coreattrs
147 let attrs = extend(attrs, ["span", "width", "align", "char", "charoff", "valign"])
148 elseif tag =~ '^\(del\|ins\)$'
149 let attrs = g:coreattrs
150 let attrs = extend(attrs, ["cite", "datetime"])
151 elseif tag == 'form'
152 let attrs = g:coreattrs
153 let attrs = extend(attrs, ["action", "method", "enctype", "onsubmit", "onreset", "accept", "accept-charset"])
154 elseif tag == 'head'
155 let attrs = g:i18n
156 let attrs = extend(attrs, ["id", "profile"])
157 elseif tag == 'html'
158 let attrs = g:i18n
159 let attrs = extend(attrs, ["id", "xmlns"])
160 elseif tag == 'img'
161 let attrs = g:coreattrs
162 let attrs = extend(attrs, ["src", "alt", "longdesc", "height", "width", "usemap", "ismap"])
163 elseif tag == 'input'
164 let attrs = g:coreattrs
165 let attrs = extend(attrs, g:focus)
166 let attrs = extend(attrs, ["type", "name", "value", "checked", "disabled", "readonly", "size", "maxlength", "src", "alt", "usemap", "onselect", "onchange", "accept"])
167 elseif tag == 'label'
168 let attrs = g:coreattrs
169 let attrs = extend(attrs, ["for", "accesskey", "onfocus", "onblur"])
170 elseif tag == 'legend'
171 let attrs = g:coreattrs
172 let attrs = extend(attrs, ["accesskey"])
173 elseif tag == 'link'
174 let attrs = g:coreattrs
175 let attrs = extend(attrs, ["charset", "href", "hreflang", "type", "rel", "rev", "media"])
176 elseif tag == 'map'
177 let attrs = g:i18n
178 let attrs = extend(attrs, g:events)
179 let attrs = extend(attrs, ["id", "class", "style", "title", "name"])
180 elseif tag == 'meta'
181 let attrs = g:i18n
182 let attrs = extend(attrs, ["id", "http-equiv", "content", "scheme", "name"])
183 elseif tag == 'title'
184 let attrs = g:i18n
185 let attrs = extend(attrs, ["id"])
186 elseif tag == 'object'
187 let attrs = g:coreattrs
188 let attrs = extend(attrs, ["declare", "classid", "codebase", "data", "type", "codetype", "archive", "standby", "height", "width", "usemap", "name", "tabindex"])
189 elseif tag == 'optgroup'
190 let attrs = g:coreattrs
191 let attrs = extend(attrs, ["disbled", "label"])
192 elseif tag == 'option'
193 let attrs = g:coreattrs
194 let attrs = extend(attrs, ["disbled", "selected", "value", "label"])
195 elseif tag == 'param'
196 let attrs = ["id", "name", "value", "valuetype", "type"]
197 elseif tag == 'pre'
198 let attrs = g:coreattrs
199 let attrs = extend(attrs, ["xml:space"])
200 elseif tag == 'q'
201 let attrs = g:coreattrs
202 let attrs = extend(attrs, ["cite"])
203 elseif tag == 'script'
204 let attrs = ["id", "charset", "type", "src", "defer", "xml:space"]
205 elseif tag == 'select'
206 let attrs = g:coreattrs
207 let attrs = extend(attrs, ["name", "size", "multiple", "disabled", "tabindex", "onfocus", "onblur", "onchange"])
208 elseif tag == 'style'
209 let attrs = g:coreattrs
210 let attrs = extend(attrs, ["id", "type", "media", "title", "xml:space"])
211 elseif tag == 'table'
212 let attrs = g:coreattrs
213 let attrs = extend(attrs, ["summary", "width", "border", "frame", "rules" "cellspacing", "cellpadding"])
214 elseif tag =~ '^\(thead\|tfoot\|tbody\|tr\)$'
215 let attrs = g:coreattrs
216 let attrs = extend(attrs, ["align", "char", "charoff", "valign"])
217 elseif tag == 'textarea'
218 let attrs = g:coreattrs
219 let attrs = extend(attrs, g:focus)
220 let attrs = extend(attrs, ["name", "rows", "cols", "disabled", "readonly", "onselect", "onchange"])
221 elseif tag =~ '^\(th\|td\)$'
222 let attrs = g:coreattrs
223 let attrs = extend(attrs, ["abbr", "headers", "scope", "rowspan", "colspan", "align", "char", "charoff", "valign"])
224 endif
225
226 for m in sort(attrs)
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000227 if m =~ attr
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000228 if m =~ '^\(ismap\|defer\|declare\|nohref\|checked\|disabled\|selected\|readonly\)$'
229 call add(res, sbase.' '.m)
230 else
231 call add(res, sbase.' '.m.'="')
232 endif
233 endif
234 endfor
235 return res
236 endif
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000237 " Close tag
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000238 let b:unaryTagsStack = "base meta link hr br param img area input col"
239 if a:base =~ '^\/'
240 let opentag = htmlcomplete#GetLastOpenTag("b:unaryTagsStack")
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000241 return ["/".opentag.">"]
242 endif
243 " Deal with tag completion.
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000244 let opentag = htmlcomplete#GetLastOpenTag("b:unaryTagsStack")
245 " Clusters
246 let special = "br span bdo map object img"
247 let phrase = "em strong dfn code q samp kbd var cite abbr acronym sub sup"
248 let inlineforms = "input select textarea label button"
249 let miscinline = "ins del script"
250 let inline = "a ".special." ".phrase." ".inlineforms." tt i b big small"
251 let misc = "noscript ".miscinline
252 let block = "p h1 h2 h3 h4 h5 h6 div ul ol dl pre hr blockquote address fieldset table"
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000253
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000254 if opentag == 'a'
255 let tags = split("tt i b big small ".special." ".phrase." ".inlineforms." ".miscinline)
256 elseif opentag =~ '^\(abbr\|acronym\|address\|b\|p\|h\d\|dt\|span\|bdo\|em\|strong\|dfn\|code\|samp\|kbd\|var\|cite\|q\|sub\|sup\|tt\|i\|big\|small\|label\|caption\)$'
257 let tags = split(inline." ".miscinline)
258 elseif opentag == 'pre'
259 let tags = split("a tt i b big small br span bdo map ".phrase." ".miscinline." ".inlineforms)
260 elseif opentag == 'html'
261 let tags = split("head body")
262 elseif opentag == 'legend'
263 let tags = split(inline." ".miscinline)
264 elseif opentag == 'head'
265 let tags = split("title base scipt style meta link object")
266 elseif opentag =~ '^\(noscript\|body\|blockquote\)$'
267 let tags = split("form ".block." ".misc)
268 elseif opentag =~ '^\(ul\|ol\)$'
269 let tags = ["li"]
270 elseif opentag == 'dl'
271 let tags = split("dt dd")
272 elseif opentag =~ '^\(ins\|del\|th\|td\|dd\|div\|li\)$'
273 let tags = split("form ".block." ".inline." ".misc)
274 elseif opentag == 'object'
275 let tags = split("param form ".block." ".inline." ".misc)
276 elseif opentag == 'fieldset'
277 let tags = split("legend form ".block." ".inline." ".misc)
278 elseif opentag == 'map'
279 let tags = split("area form ".block." ".misc)
280 elseif opentag == 'form'
281 let tags = split(block." ".misc)
282 elseif opentag == 'select'
283 let tags = split("optgroup option")
284 elseif opentag == 'optgroup'
285 let tags = ["option"]
286 elseif opentag == 'colgroup'
287 let tags = ["col"]
288 elseif opentag == '^\(textarea\|option\|script\|style\|title\)$'
289 let tags = []
290 elseif opentag == 'button'
291 let tags = split("p h1 h2 h3 h4 h5 h6 div ul ol dl table")
292 elseif opentag =~ '^\(thead\|tfoot\|tbody)$'
293 let tags = ["tr"]
294 elseif opentag == 'tr'
295 let tags = split("th td")
296 elseif opentag == 'table'
297 let tags = split("caption col colgroup thead tfoot tbody tr")
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000298 endif
299
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000300 for m in tags
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000301 if m =~ a:base
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000302 call add(res, m)
303 endif
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000304 endfor
305
306 return res
307
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000308 endif
309endfunction
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000310
311" MM: This is greatly reduced closetag.vim used with kind permission of Steven
312" Mueller
313" Changes: strip all comments; delete error messages
314" Author: Steven Mueller <diffusor@ugcs.caltech.edu>
315" Last Modified: Tue May 24 13:29:48 PDT 2005
316" Version: 0.9.1
317
318function! htmlcomplete#GetLastOpenTag(unaryTagsStack)
319 let linenum=line(".")
320 let lineend=col(".") - 1 " start: cursor position
321 let first=1 " flag for first line searched
322 let b:TagStack="" " main stack of tags
323 let startInComment=s:InComment()
324
325 let tagpat='</\=\(\k\|[-:]\)\+\|/>'
326 while (linenum>0)
327 let line=getline(linenum)
328 if first
329 let line=strpart(line,0,lineend)
330 else
331 let lineend=strlen(line)
332 endif
333 let b:lineTagStack=""
334 let mpos=0
335 let b:TagCol=0
336 while (mpos > -1)
337 let mpos=matchend(line,tagpat)
338 if mpos > -1
339 let b:TagCol=b:TagCol+mpos
340 let tag=matchstr(line,tagpat)
341
342 if exists("b:closetag_disable_synID") || startInComment==s:InCommentAt(linenum, b:TagCol)
343 let b:TagLine=linenum
344 call s:Push(matchstr(tag,'[^<>]\+'),"b:lineTagStack")
345 endif
346 let lineend=lineend-mpos
347 let line=strpart(line,mpos,lineend)
348 endif
349 endwhile
350 while (!s:EmptystackP("b:lineTagStack"))
351 let tag=s:Pop("b:lineTagStack")
352 if match(tag, "^/") == 0 "found end tag
353 call s:Push(tag,"b:TagStack")
354 elseif s:EmptystackP("b:TagStack") && !s:Instack(tag, a:unaryTagsStack) "found unclosed tag
355 return tag
356 else
357 let endtag=s:Peekstack("b:TagStack")
358 if endtag == "/".tag || endtag == "/"
359 call s:Pop("b:TagStack") "found a open/close tag pair
360 elseif !s:Instack(tag, a:unaryTagsStack) "we have a mismatch error
361 return ""
362 endif
363 endif
364 endwhile
365 let linenum=linenum-1 | let first=0
366endwhile
367return ""
368endfunction
369
370function! s:InComment()
371 return synIDattr(synID(line("."), col("."), 0), "name") =~ 'Comment'
372endfunction
373
374function! s:InCommentAt(line, col)
375 return synIDattr(synID(a:line, a:col, 0), "name") =~ 'Comment'
376endfunction
377
378
379function! s:SetKeywords()
380 let g:IsKeywordBak=&iskeyword
381 let &iskeyword="33-255"
382endfunction
383
384function! s:RestoreKeywords()
385 let &iskeyword=g:IsKeywordBak
386endfunction
387
388function! s:Push(el, sname)
389 if !s:EmptystackP(a:sname)
390 exe "let ".a:sname."=a:el.' '.".a:sname
391 else
392 exe "let ".a:sname."=a:el"
393 endif
394endfunction
395
396function! s:EmptystackP(sname)
397 exe "let stack=".a:sname
398 if match(stack,"^ *$") == 0
399 return 1
400 else
401 return 0
402 endif
403endfunction
404
405function! s:Instack(el, sname)
406 exe "let stack=".a:sname
407 call s:SetKeywords()
408 let m=match(stack, "\\<".a:el."\\>")
409 call s:RestoreKeywords()
410 if m < 0
411 return 0
412 else
413 return 1
414 endif
415endfunction
416
417function! s:Peekstack(sname)
418 call s:SetKeywords()
419 exe "let stack=".a:sname
420 let top=matchstr(stack, "\\<.\\{-1,}\\>")
421 call s:RestoreKeywords()
422 return top
423endfunction
424
425function! s:Pop(sname)
426 if s:EmptystackP(a:sname)
427 return ""
428 endif
429 exe "let stack=".a:sname
430 call s:SetKeywords()
431 let loc=matchend(stack,"\\<.\\{-1,}\\>")
432 exe "let ".a:sname."=strpart(stack, loc+1, strlen(stack))"
433 let top=strpart(stack, match(stack, "\\<"), loc)
434 call s:RestoreKeywords()
435 return top
436endfunction
437
438function! s:Clearstack(sname)
439 exe "let ".a:sname."=''"
440endfunction