blob: 733fff521530436fc81ed4c0bc8ab0924a58b915 [file] [log] [blame]
Bram Moolenaarf75a9632005-09-13 21:20:47 +00001" Vim completion script
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00002" Language: HTML and XHTML
Bram Moolenaarf75a9632005-09-13 21:20:47 +00003" Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl )
Bram Moolenaar4a85b412006-04-23 22:40:29 +00004" Last Change: 2006 Apr 22
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
Bram Moolenaar28c258f2006-01-25 22:02:51 +000011 let curline = line('.')
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000012 let compl_begin = col('.') - 2
Bram Moolenaar4a85b412006-04-23 22:40:29 +000013 while start >= 0 && line[start - 1] =~ '\(\k\|[!:.-]\)'
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000014 let start -= 1
Bram Moolenaarf75a9632005-09-13 21:20:47 +000015 endwhile
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000016 " Handling of entities {{{
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000017 if start >= 0 && line[start - 1] =~ '&'
18 let b:entitiescompl = 1
19 let b:compl_context = ''
20 return start
21 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000022 " }}}
23 " Handling of <style> tag {{{
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000024 let stylestart = searchpair('<style\>', '', '<\/style\>', "bnW")
25 let styleend = searchpair('<style\>', '', '<\/style\>', "nW")
26 if stylestart != 0 && styleend != 0
Bram Moolenaar28c258f2006-01-25 22:02:51 +000027 if stylestart <= curline && styleend >= curline
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000028 let start = col('.') - 1
29 let b:csscompl = 1
30 while start >= 0 && line[start - 1] =~ '\(\k\|-\)'
31 let start -= 1
32 endwhile
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +000033 endif
34 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000035 " }}}
36 " Handling of <script> tag {{{
Bram Moolenaarb8a7b562006-02-01 21:47:16 +000037 let scriptstart = searchpair('<script\>', '', '<\/script\>', "bnW")
38 let scriptend = searchpair('<script\>', '', '<\/script\>', "nW")
39 if scriptstart != 0 && scriptend != 0
40 if scriptstart <= curline && scriptend >= curline
41 let start = col('.') - 1
42 let b:jscompl = 1
43 let b:jsrange = [scriptstart, scriptend]
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +000044 while start >= 0 && line[start - 1] =~ '\k'
Bram Moolenaarb8a7b562006-02-01 21:47:16 +000045 let start -= 1
46 endwhile
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000047 " We are inside of <script> tag. But we should also get contents
48 " of all linked external files and (secondary, less probably) other <script> tags
49 " This logic could possible be done in separate function - may be
50 " reused in events scripting (also with option could be reused for
51 " CSS
52 let b:js_extfiles = []
53 let l = line('.')
54 let c = col('.')
55 call cursor(1,1)
56 while search('<\@<=script\>', 'W') && line('.') <= l
57 if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
58 let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
59 if filereadable(sname)
60 let b:js_extfiles += readfile(sname)
61 endif
62 endif
63 endwhile
64 call cursor(1,1)
65 let js_scripttags = []
66 while search('<script\>', 'W') && line('.') < l
67 if matchstr(getline('.'), '<script[^>]*src') == ''
68 let js_scripttag = getline(line('.'), search('</script>', 'W'))
69 let js_scripttags += js_scripttag
70 endif
71 endwhile
72 let b:js_extfiles += js_scripttags
73 call cursor(l,c)
74 unlet! l c
Bram Moolenaarb8a7b562006-02-01 21:47:16 +000075 endif
76 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000077 " }}}
Bram Moolenaarb8a7b562006-02-01 21:47:16 +000078 if !exists("b:csscompl") && !exists("b:jscompl")
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +000079 let b:compl_context = getline('.')[0:(compl_begin)]
Bram Moolenaar28c258f2006-01-25 22:02:51 +000080 if b:compl_context !~ '<[^>]*$'
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000081 " Look like we may have broken tag. Check previous lines.
Bram Moolenaar28c258f2006-01-25 22:02:51 +000082 let i = 1
83 while 1
84 let context_line = getline(curline-i)
85 if context_line =~ '<[^>]*$'
86 " Yep, this is this line
87 let context_lines = getline(curline-i, curline)
88 let b:compl_context = join(context_lines, ' ')
89 break
Bram Moolenaare0fa5602006-03-19 22:08:37 +000090 elseif context_line =~ '>[^<]*$' || i == curline
91 " We are in normal tag line, no need for completion at all
92 " OR reached first line without tag at all
Bram Moolenaar28c258f2006-01-25 22:02:51 +000093 let b:compl_context = ''
94 break
95 endif
96 let i += 1
97 endwhile
98 " Make sure we don't have counter
99 unlet! i
100 endif
Bram Moolenaar09df3122006-01-23 22:23:09 +0000101 let b:compl_context = matchstr(b:compl_context, '.*\zs<.*')
Bram Moolenaare0fa5602006-03-19 22:08:37 +0000102
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000103 " Return proper start for on-events. Without that beginning of
104 " completion will be badly reported
105 if b:compl_context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
106 let start = col('.') - 1
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000107 while start >= 0 && line[start - 1] =~ '\k'
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000108 let start -= 1
109 endwhile
110 endif
Bram Moolenaare0fa5602006-03-19 22:08:37 +0000111 " If b:compl_context begins with <? we are inside of PHP code. It
112 " wasn't closed so PHP completion passed it to HTML
113 if &filetype =~? 'php' && b:compl_context =~ '^<?'
114 let b:phpcompl = 1
115 let start = col('.') - 1
116 while start >= 0 && line[start - 1] =~ '[a-zA-Z_0-9\x7f-\xff$]'
117 let start -= 1
118 endwhile
119 endif
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000120 else
121 let b:compl_context = getline('.')[0:compl_begin]
122 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000123 return start
124 else
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000125 " Initialize base return lists
126 let res = []
127 let res2 = []
128 " a:base is very short - we need context
129 let context = b:compl_context
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000130 let g:ab = a:base
131 let g:co = context
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000132 " Check if we should do CSS completion inside of <style> tag
Bram Moolenaare0fa5602006-03-19 22:08:37 +0000133 " or JS completion inside of <script> tag or PHP completion in case of <?
134 " tag AND &ft==php
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000135 if exists("b:csscompl")
136 unlet! b:csscompl
Bram Moolenaar09df3122006-01-23 22:23:09 +0000137 let context = b:compl_context
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000138 unlet! b:compl_context
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000139 return csscomplete#CompleteCSS(0, context)
Bram Moolenaarb8a7b562006-02-01 21:47:16 +0000140 elseif exists("b:jscompl")
141 unlet! b:jscompl
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000142 return javascriptcomplete#CompleteJS(0, a:base)
Bram Moolenaare0fa5602006-03-19 22:08:37 +0000143 elseif exists("b:phpcompl")
144 unlet! b:phpcompl
145 let context = b:compl_context
146 return phpcomplete#CompletePHP(0, a:base)
Bram Moolenaar09df3122006-01-23 22:23:09 +0000147 else
148 if len(b:compl_context) == 0 && !exists("b:entitiescompl")
149 return []
150 endif
151 let context = matchstr(b:compl_context, '.\zs.*')
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000152 endif
Bram Moolenaar09df3122006-01-23 22:23:09 +0000153 unlet! b:compl_context
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000154 " Entities completion {{{
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000155 if exists("b:entitiescompl")
156 unlet! b:entitiescompl
157
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000158 if !exists("b:html_omni")
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000159 "runtime! autoload/xml/xhtml10s.vim
160 call htmlcomplete#LoadData()
Bram Moolenaara5792f52005-11-23 21:25:05 +0000161 endif
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000162
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000163 let entities = b:html_omni['vimxmlentities']
Bram Moolenaara5792f52005-11-23 21:25:05 +0000164
Bram Moolenaar09df3122006-01-23 22:23:09 +0000165 if len(a:base) == 1
166 for m in entities
167 if m =~ '^'.a:base
168 call add(res, m.';')
169 endif
170 endfor
171 return res
172 else
173 for m in entities
174 if m =~? '^'.a:base
175 call add(res, m.';')
176 elseif m =~? a:base
177 call add(res2, m.';')
178 endif
179 endfor
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000180
Bram Moolenaar09df3122006-01-23 22:23:09 +0000181 return res + res2
182 endif
183
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000184
185 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000186 " }}}
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000187 if context =~ '>'
188 " Generally if context contains > it means we are outside of tag and
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000189 " should abandon action - with one exception: <style> span { bo
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000190 if context =~ 'style[^>]\{-}>[^<]\{-}$'
191 return csscomplete#CompleteCSS(0, context)
Bram Moolenaarb8a7b562006-02-01 21:47:16 +0000192 elseif context =~ 'script[^>]\{-}>[^<]\{-}$'
193 let b:jsrange = [line('.'), search('<\/script\>', 'nW')]
194 return javascriptcomplete#CompleteJS(0, context)
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000195 else
196 return []
197 endif
198 endif
199
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000200 " If context contains > it means we are already outside of tag and we
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000201 " should abandon action
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000202 " If context contains white space it is attribute.
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000203 " It can be also value of attribute.
204 " We have to get first word to offer proper completions
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000205 if context == ''
206 let tag = ''
207 else
208 let tag = split(context)[0]
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000209 " Detect if tag is uppercase to return in proper case,
210 " we need to make it lowercase for processing
211 if tag =~ '^[A-Z]*$'
Bram Moolenaar8424a622006-04-19 21:23:36 +0000212 let uppercase_tag = 1
213 let tag = tolower(tag)
214 else
215 let uppercase_tag = 0
216 endif
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000217 endif
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000218 " Get last word, it should be attr name
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000219 let attr = matchstr(context, '.*\s\zs.*')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000220 " Possible situations where any prediction would be difficult:
221 " 1. Events attributes
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000222 if context =~ '\s'
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000223 " Sort out style, class, and on* cases
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000224 if context =~? "\\(on[a-z]*\\|id\\|style\\|class\\)\\s*=\\s*[\"']"
225 " Id, class completion {{{
226 if context =~? "\\(id\\|class\\)\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
227 if context =~? "class\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
Bram Moolenaar1e015462005-09-25 22:16:38 +0000228 let search_for = "class"
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000229 elseif context =~? "id\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
Bram Moolenaar1e015462005-09-25 22:16:38 +0000230 let search_for = "id"
231 endif
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000232 " Handle class name completion
233 " 1. Find lines of <link stylesheet>
234 " 1a. Check file for @import
235 " 2. Extract filename(s?) of stylesheet,
236 call cursor(1,1)
237 let head = getline(search('<head\>'), search('<\/head>'))
238 let headjoined = join(copy(head), ' ')
239 if headjoined =~ '<style'
Bram Moolenaara5792f52005-11-23 21:25:05 +0000240 " Remove possibly confusing CSS operators
Bram Moolenaar1e015462005-09-25 22:16:38 +0000241 let stylehead = substitute(headjoined, '+>\*[,', ' ', 'g')
242 if search_for == 'class'
243 let styleheadlines = split(stylehead)
244 let headclasslines = filter(copy(styleheadlines), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
245 else
246 let stylesheet = split(headjoined, '[{}]')
247 " Get all lines which fit id syntax
248 let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
249 " Filter out possible color definitions
250 call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
251 " Filter out complex border definitions
252 call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
253 let templines = join(classlines, ' ')
254 let headclasslines = split(templines)
255 call filter(headclasslines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
256 endif
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000257 let internal = 1
258 else
259 let internal = 0
260 endif
261 let styletable = []
262 let secimportfiles = []
263 let filestable = filter(copy(head), "v:val =~ '\\(@import\\|link.*stylesheet\\)'")
264 for line in filestable
265 if line =~ "@import"
266 let styletable += [matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")]
267 elseif line =~ "<link"
268 let styletable += [matchstr(line, "href\\s*=\\s*[\"']\\zs\\f\\+\\ze")]
269 endif
270 endfor
Bram Moolenaar1e015462005-09-25 22:16:38 +0000271 for file in styletable
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000272 if filereadable(file)
273 let stylesheet = readfile(file)
274 let secimport = filter(copy(stylesheet), "v:val =~ '@import'")
275 if len(secimport) > 0
276 for line in secimport
Bram Moolenaar1e015462005-09-25 22:16:38 +0000277 let secfile = matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")
278 let secfile = fnamemodify(file, ":p:h").'/'.secfile
279 let secimportfiles += [secfile]
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000280 endfor
281 endif
282 endif
283 endfor
284 let cssfiles = styletable + secimportfiles
285 let classes = []
286 for file in cssfiles
287 if filereadable(file)
288 let stylesheet = readfile(file)
Bram Moolenaar1e015462005-09-25 22:16:38 +0000289 let stylefile = join(stylesheet, ' ')
290 let stylefile = substitute(stylefile, '+>\*[,', ' ', 'g')
291 if search_for == 'class'
292 let stylesheet = split(stylefile)
293 let classlines = filter(copy(stylesheet), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
294 else
295 let stylesheet = split(stylefile, '[{}]')
296 " Get all lines which fit id syntax
297 let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
298 " Filter out possible color definitions
299 call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
300 " Filter out complex border definitions
301 call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
302 let templines = join(classlines, ' ')
303 let stylelines = split(templines)
304 let classlines = filter(stylelines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
305
306 endif
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000307 endif
308 " We gathered classes definitions from all external files
309 let classes += classlines
310 endfor
311 if internal == 1
312 let classes += headclasslines
313 endif
Bram Moolenaar1e015462005-09-25 22:16:38 +0000314
315 if search_for == 'class'
316 let elements = {}
317 for element in classes
318 if element =~ '^\.'
319 let class = matchstr(element, '^\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
320 let class = substitute(class, ':.*', '', '')
321 if has_key(elements, 'common')
322 let elements['common'] .= ' '.class
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000323 else
Bram Moolenaar1e015462005-09-25 22:16:38 +0000324 let elements['common'] = class
325 endif
326 else
327 let class = matchstr(element, '[a-zA-Z1-6]*\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
328 let tagname = tolower(matchstr(element, '[a-zA-Z1-6]*\ze.'))
329 if tagname != ''
330 if has_key(elements, tagname)
331 let elements[tagname] .= ' '.class
332 else
333 let elements[tagname] = class
334 endif
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000335 endif
336 endif
Bram Moolenaar1e015462005-09-25 22:16:38 +0000337 endfor
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000338
Bram Moolenaar1e015462005-09-25 22:16:38 +0000339 if has_key(elements, tag) && has_key(elements, 'common')
340 let values = split(elements[tag]." ".elements['common'])
341 elseif has_key(elements, tag) && !has_key(elements, 'common')
342 let values = split(elements[tag])
343 elseif !has_key(elements, tag) && has_key(elements, 'common')
344 let values = split(elements['common'])
345 else
346 return []
347 endif
348
349 elseif search_for == 'id'
350 " Find used IDs
351 " 1. Catch whole file
352 let filelines = getline(1, line('$'))
353 " 2. Find lines with possible id
354 let used_id_lines = filter(filelines, 'v:val =~ "id\\s*=\\s*[\"''][a-zA-Z0-9_-]\\+"')
355 " 3a. Join all filtered lines
356 let id_string = join(used_id_lines, ' ')
357 " 3b. And split them to be sure each id is in separate item
358 let id_list = split(id_string, 'id\s*=\s*')
359 " 4. Extract id values
360 let used_id = map(id_list, 'matchstr(v:val, "[\"'']\\zs[a-zA-Z0-9_-]\\+\\ze")')
361 let joined_used_id = ','.join(used_id, ',').','
362
363 let allvalues = map(classes, 'matchstr(v:val, ".*#\\zs[a-zA-Z0-9_-]\\+")')
364
365 let values = []
366
367 for element in classes
368 if joined_used_id !~ ','.element.','
369 let values += [element]
370 endif
371
372 endfor
373
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000374 endif
375
376 " We need special version of sbase
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000377 let classbase = matchstr(context, ".*[\"']")
Bram Moolenaar1e015462005-09-25 22:16:38 +0000378 let classquote = matchstr(classbase, '.$')
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000379
380 let entered_class = matchstr(attr, ".*=\\s*[\"']\\zs.*")
381
382 for m in sort(values)
383 if m =~? '^'.entered_class
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000384 call add(res, m . classquote)
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000385 elseif m =~? entered_class
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000386 call add(res2, m . classquote)
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000387 endif
388 endfor
389
390 return res + res2
391
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000392 elseif context =~? "style\\s*=\\s*[\"'][^\"']*$"
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000393 return csscomplete#CompleteCSS(0, context)
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000394
395 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000396 " }}}
397 " Complete on-events {{{
398 if context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
399 " We have to:
400 " 1. Find external files
401 let b:js_extfiles = []
402 let l = line('.')
403 let c = col('.')
404 call cursor(1,1)
405 while search('<\@<=script\>', 'W') && line('.') <= l
406 if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
407 let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
408 if filereadable(sname)
409 let b:js_extfiles += readfile(sname)
410 endif
411 endif
412 endwhile
413 " 2. Find at least one <script> tag
414 call cursor(1,1)
415 let js_scripttags = []
416 while search('<script\>', 'W') && line('.') < l
417 if matchstr(getline('.'), '<script[^>]*src') == ''
418 let js_scripttag = getline(line('.'), search('</script>', 'W'))
419 let js_scripttags += js_scripttag
420 endif
421 endwhile
422 let b:js_extfiles += js_scripttags
423
424 " 3. Proper call for javascriptcomplete#CompleteJS
425 call cursor(l,c)
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000426 let js_context = matchstr(a:base, '\k\+$')
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000427 let js_shortcontext = substitute(a:base, js_context.'$', '', '')
428 let b:compl_context = context
429 let b:jsrange = [l, l]
430 unlet! l c
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000431 return javascriptcomplete#CompleteJS(0, js_context)
432
433 endif
434
435 " }}}
436 let stripbase = matchstr(context, ".*\\(on[a-zA-Z]*\\|style\\|class\\)\\s*=\\s*[\"']\\zs.*")
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000437 " Now we have context stripped from all chars up to style/class.
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000438 " It may fail with some strange style value combinations.
439 if stripbase !~ "[\"']"
440 return []
441 endif
442 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000443 " Value of attribute completion {{{
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000444 " If attr contains =\s*[\"'] we catched value of attribute
Bram Moolenaar8424a622006-04-19 21:23:36 +0000445 if attr =~ "=\s*[\"']" || attr =~ "=\s*$"
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000446 " Let do attribute specific completion
447 let attrname = matchstr(attr, '.*\ze\s*=')
Bram Moolenaar8424a622006-04-19 21:23:36 +0000448 let entered_value = matchstr(attr, ".*=\\s*[\"']\\?\\zs.*")
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000449 let values = []
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000450 if attrname == 'href'
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000451 " Now we are looking for local anchors defined by name or id
452 if entered_value =~ '^#'
453 let file = join(getline(1, line('$')), ' ')
454 " Split it be sure there will be one id/name element in
455 " item, it will be also first word [a-zA-Z0-9_-] in element
456 let oneelement = split(file, "\\(meta \\)\\@<!\\(name\\|id\\)\\s*=\\s*[\"']")
457 for i in oneelement
458 let values += ['#'.matchstr(i, "^[a-zA-Z][a-zA-Z0-9%_-]*")]
459 endfor
460 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000461 else
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000462 if has_key(b:html_omni, tag) && has_key(b:html_omni[tag][1], attrname)
463 let values = b:html_omni[tag][1][attrname]
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000464 else
465 return []
466 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000467 endif
468
469 if len(values) == 0
470 return []
471 endif
472
473 " We need special version of sbase
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000474 let attrbase = matchstr(context, ".*[\"']")
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000475 let attrquote = matchstr(attrbase, '.$')
Bram Moolenaar8424a622006-04-19 21:23:36 +0000476 if attrquote !~ "['\"]"
477 let attrquoteopen = '"'
478 let attrquote = '"'
479 else
480 let attrquoteopen = ''
481 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000482
483 for m in values
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000484 " This if is needed to not offer all completions as-is
485 " alphabetically but sort them. Those beginning with entered
486 " part will be as first choices
487 if m =~ '^'.entered_value
Bram Moolenaar8424a622006-04-19 21:23:36 +0000488 call add(res, attrquoteopen . m . attrquote.' ')
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000489 elseif m =~ entered_value
Bram Moolenaar8424a622006-04-19 21:23:36 +0000490 call add(res2, attrquoteopen . m . attrquote.' ')
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000491 endif
492 endfor
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000493
494 return res + res2
495
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000496 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000497 " }}}
498 " Attribute completion {{{
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000499 " Shorten context to not include last word
500 let sbase = matchstr(context, '.*\ze\s.*')
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000501
502 " Load data {{{
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000503 if !exists("b:html_omni_gen")
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000504 call htmlcomplete#LoadData()
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000505 endif
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000506 " }}}
Bram Moolenaar8424a622006-04-19 21:23:36 +0000507
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000508 if has_key(b:html_omni, tag)
509 let attrs = keys(b:html_omni[tag][1])
Bram Moolenaar8424a622006-04-19 21:23:36 +0000510 else
511 return []
512 endif
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000513
514 for m in sort(attrs)
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000515 if m =~ '^'.attr
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000516 call add(res, m)
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000517 elseif m =~ attr
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000518 call add(res2, m)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000519 endif
520 endfor
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000521 let menu = res + res2
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000522 if has_key(b:html_omni, 'vimxmlattrinfo')
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000523 let final_menu = []
524 for i in range(len(menu))
525 let item = menu[i]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000526 if has_key(b:html_omni['vimxmlattrinfo'], item)
527 let m_menu = b:html_omni['vimxmlattrinfo'][item][0]
528 let m_info = b:html_omni['vimxmlattrinfo'][item][1]
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000529 if m_menu !~ 'Bool'
530 let item .= '="'
531 endif
532 else
533 let m_menu = ''
534 let m_info = ''
535 let item .= '="'
536 endif
537 let final_menu += [{'word':item, 'menu':m_menu, 'info':m_info}]
538 endfor
539 else
540 let final_menu = map(menu, 'v:val."=\""')
541 endif
542 return final_menu
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000543
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000544 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000545 " }}}
546 " Close tag {{{
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000547 let b:unaryTagsStack = "base meta link hr br param img area input col"
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000548 if context =~ '^\/'
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000549 if context =~ '^\/.'
550 return []
551 else
552 let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
553 return [opentag.">"]
554 endif
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000555 endif
Bram Moolenaar8424a622006-04-19 21:23:36 +0000556 " }}}
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000557 " Load data {{{
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000558 if !exists("b:html_omni")
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000559 "runtime! autoload/xml/xhtml10s.vim
560 call htmlcomplete#LoadData()
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000561 endif
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000562 " }}}
563 " Tag completion {{{
Bram Moolenaar7e8fd632006-02-18 22:14:51 +0000564 " Deal with tag completion.
Bram Moolenaar8424a622006-04-19 21:23:36 +0000565 let opentag = tolower(xmlcomplete#GetLastOpenTag("b:unaryTagsStack"))
Bram Moolenaar362e1a32006-03-06 23:29:24 +0000566 " MM: TODO: GLOT works always the same but with some weird situation it
567 " behaves as intended in HTML but screws in PHP
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000568 if opentag == '' || &filetype == 'php' && !has_key(b:html_omni, opentag)
Bram Moolenaar7e8fd632006-02-18 22:14:51 +0000569 " Hack for sometimes failing GetLastOpenTag.
570 " As far as I tested fail isn't GLOT fault but problem
571 " of invalid document - not properly closed tags and other mish-mash.
572 " Also when document is empty. Return list of *all* tags.
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000573 let tags = keys(b:html_omni)
Bram Moolenaar7e8fd632006-02-18 22:14:51 +0000574 call filter(tags, 'v:val !~ "^vimxml"')
575 else
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000576 if has_key(b:html_omni, opentag)
577 let tags = b:html_omni[opentag][0]
Bram Moolenaar8424a622006-04-19 21:23:36 +0000578 else
579 return []
580 endif
Bram Moolenaar7e8fd632006-02-18 22:14:51 +0000581 endif
582 " }}}
Bram Moolenaar8424a622006-04-19 21:23:36 +0000583
584 if exists("uppercase_tag") && uppercase_tag == 1
585 let context = tolower(context)
586 endif
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000587 " Handle XML keywords: DOCTYPE and CDATA.
588 if opentag == '' || opentag ==? 'head'
589 let tags += [
590 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">',
591 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">',
592 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">',
593 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">',
594 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
595 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
596 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
597 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
598 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
599 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
600 \ '!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/1999/xhtml">',
601 \ '!CDATA'
602 \ ]
603 endif
Bram Moolenaar4c903f92005-09-14 21:32:32 +0000604
Bram Moolenaarcef9dcc2005-12-06 19:50:41 +0000605 for m in sort(tags)
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000606 if m =~ '^'.context
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000607 call add(res, m)
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +0000608 elseif m =~ context
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000609 call add(res2, m)
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000610 endif
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000611 endfor
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000612 let menu = res + res2
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000613 let g:me = menu
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000614 if has_key(b:html_omni, 'vimxmltaginfo')
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000615 let final_menu = []
616 for i in range(len(menu))
617 let item = menu[i]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000618 if has_key(b:html_omni['vimxmltaginfo'], item)
619 let m_menu = b:html_omni['vimxmltaginfo'][item][0]
620 let m_info = b:html_omni['vimxmltaginfo'][item][1]
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000621 else
622 let m_menu = ''
623 let m_info = ''
624 endif
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000625 if &filetype == 'html' && exists("uppercase_tag") && uppercase_tag == 1 && item !~ 'DOCTYPE'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000626 let item = toupper(item)
627 endif
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000628 if item =~ 'DOCTYPE'
629 let abbr = 'DOCTYPE '.matchstr(item, 'DTD \zsX\?HTML .\{-}\ze\/\/')
630 else
631 let abbr = item
632 endif
633 let final_menu += [{'abbr':abbr, 'word':item, 'menu':m_menu, 'info':m_info}]
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000634 endfor
635 else
636 let final_menu = menu
637 endif
638 return final_menu
Bram Moolenaar6b730e12005-09-16 21:47:57 +0000639
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000640 " }}}
Bram Moolenaarf75a9632005-09-13 21:20:47 +0000641 endif
642endfunction
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000643
644function! htmlcomplete#LoadData() " {{{
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000645 if !exists("b:html_omni_flavor")
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000646 if &filetype == 'html'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000647 let b:html_omni_flavor = 'html401t'
648 else
649 let b:html_omni_flavor = 'xhtml10s'
650 endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000651 endif
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000652 " With that if we still have bloated memory but create new buffer
653 " variables only by linking to existing g:variable, not sourcing whole
654 " file.
655 if exists('g:xmldata_'.b:html_omni_flavor)
656 exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
657 else
658 exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000659 endif
660 " This repetition is necessary because we don't know if
661 " b:html_omni_flavor file exists and was sourced
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000662 " Proper checking for files would require iterating through 'rtp'
663 " and could introduce OS dependent mess.
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000664 if !exists("g:xmldata_".b:html_omni_flavor)
Bram Moolenaar4a85b412006-04-23 22:40:29 +0000665 if &filetype == 'html'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000666 let b:html_omni_flavor = 'html401t'
667 else
668 let b:html_omni_flavor = 'xhtml10s'
669 endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000670 endif
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000671 if exists('g:xmldata_'.b:html_omni_flavor)
672 exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
673 else
674 exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
675 exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
676 endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000677endfunction
678" }}}
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000679" vim:set foldmethod=marker: