1" Vim completion script
2" Language:	HTML and XHTML
3" Maintainer:	Mikolaj Machowski ( mikmach AT wp DOT pl )
4" Last Change:	2006 Apr 22
5
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	let curline = line('.')
12	let compl_begin = col('.') - 2
13    while start >= 0 && line[start - 1] =~ '\(\k\|[!:.-]\)'
14		let start -= 1
15    endwhile
16	" Handling of entities {{{
17	if start >= 0 && line[start - 1] =~ '&'
18		let b:entitiescompl = 1
19		let b:compl_context = ''
20		return start
21	endif
22	" }}}
23	" Handling of <style> tag {{{
24	let stylestart = searchpair('<style\>', '', '<\/style\>', "bnW")
25	let styleend   = searchpair('<style\>', '', '<\/style\>', "nW")
26	if stylestart != 0 && styleend != 0
27		if stylestart <= curline && styleend >= curline
28			let start = col('.') - 1
29			let b:csscompl = 1
30			while start >= 0 && line[start - 1] =~ '\(\k\|-\)'
31				let start -= 1
32			endwhile
33		endif
34	endif
35	" }}}
36	" Handling of <script> tag {{{
37	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]
44			while start >= 0 && line[start - 1] =~ '\k'
45				let start -= 1
46			endwhile
47			" 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
75		endif
76	endif
77	" }}}
78	if !exists("b:csscompl") && !exists("b:jscompl")
79		let b:compl_context = getline('.')[0:(compl_begin)]
80		if b:compl_context !~ '<[^>]*$'
81			" Look like we may have broken tag. Check previous lines.
82			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
90				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
93					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
101		let b:compl_context = matchstr(b:compl_context, '.*\zs<.*')
102
103		" 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
107			while start >= 0 && line[start - 1] =~ '\k'
108				let start -= 1
109			endwhile
110		endif
111		" 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
120	else
121		let b:compl_context = getline('.')[0:compl_begin]
122	endif
123    return start
124  else
125	" 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
130	let g:ab = a:base
131	let g:co = context
132	" Check if we should do CSS completion inside of <style> tag
133	" or JS completion inside of <script> tag or PHP completion in case of <?
134	" tag AND &ft==php
135	if exists("b:csscompl")
136		unlet! b:csscompl
137		let context = b:compl_context
138		unlet! b:compl_context
139		return csscomplete#CompleteCSS(0, context)
140	elseif exists("b:jscompl")
141		unlet! b:jscompl
142		return javascriptcomplete#CompleteJS(0, a:base)
143	elseif exists("b:phpcompl")
144		unlet! b:phpcompl
145		let context = b:compl_context
146		return phpcomplete#CompletePHP(0, a:base)
147	else
148		if len(b:compl_context) == 0 && !exists("b:entitiescompl")
149			return []
150		endif
151		let context = matchstr(b:compl_context, '.\zs.*')
152	endif
153	unlet! b:compl_context
154	" Entities completion {{{
155	if exists("b:entitiescompl")
156		unlet! b:entitiescompl
157
158		if !exists("b:html_omni")
159			"runtime! autoload/xml/xhtml10s.vim
160			call htmlcomplete#LoadData()
161		endif
162
163	    let entities =  b:html_omni['vimxmlentities']
164
165		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
180
181			return res + res2
182		endif
183
184
185	endif
186	" }}}
187	if context =~ '>'
188		" Generally if context contains > it means we are outside of tag and
189		" should abandon action - with one exception: <style> span { bo
190		if context =~ 'style[^>]\{-}>[^<]\{-}$'
191			return csscomplete#CompleteCSS(0, context)
192		elseif context =~ 'script[^>]\{-}>[^<]\{-}$'
193			let b:jsrange = [line('.'), search('<\/script\>', 'nW')]
194			return javascriptcomplete#CompleteJS(0, context)
195		else
196			return []
197		endif
198	endif
199
200	" If context contains > it means we are already outside of tag and we
201	" should abandon action
202	" If context contains white space it is attribute.
203	" It can be also value of attribute.
204	" We have to get first word to offer proper completions
205	if context == ''
206		let tag = ''
207	else
208		let tag = split(context)[0]
209		" Detect if tag is uppercase to return in proper case,
210		" we need to make it lowercase for processing
211		if tag =~ '^[A-Z]*$'
212			let uppercase_tag = 1
213			let tag = tolower(tag)
214		else
215			let uppercase_tag = 0
216		endif
217	endif
218	" Get last word, it should be attr name
219	let attr = matchstr(context, '.*\s\zs.*')
220	" Possible situations where any prediction would be difficult:
221	" 1. Events attributes
222	if context =~ '\s'
223		" Sort out style, class, and on* cases
224		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_ -]*$"
228					let search_for = "class"
229				elseif context =~? "id\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
230					let search_for = "id"
231				endif
232				" 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'
240					" Remove possibly confusing CSS operators
241					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
257					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
271				for file in styletable
272					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
277								let secfile = matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")
278								let secfile = fnamemodify(file, ":p:h").'/'.secfile
279								let secimportfiles += [secfile]
280							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)
289						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
307					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
314
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
323							else
324								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
335							endif
336						endif
337					endfor
338
339					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
374				endif
375
376				" We need special version of sbase
377				let classbase = matchstr(context, ".*[\"']")
378				let classquote = matchstr(classbase, '.$')
379
380				let entered_class = matchstr(attr, ".*=\\s*[\"']\\zs.*")
381
382				for m in sort(values)
383					if m =~? '^'.entered_class
384						call add(res, m . classquote)
385					elseif m =~? entered_class
386						call add(res2, m . classquote)
387					endif
388				endfor
389
390				return res + res2
391
392			elseif context =~? "style\\s*=\\s*[\"'][^\"']*$"
393				return csscomplete#CompleteCSS(0, context)
394
395			endif
396			" }}}
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)
426				let js_context = matchstr(a:base, '\k\+$')
427				let js_shortcontext = substitute(a:base, js_context.'$', '', '')
428				let b:compl_context = context
429				let b:jsrange = [l, l]
430				unlet! l c
431				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.*")
437			" Now we have context stripped from all chars up to style/class.
438			" It may fail with some strange style value combinations.
439			if stripbase !~ "[\"']"
440				return []
441			endif
442		endif
443		" Value of attribute completion {{{
444		" If attr contains =\s*[\"'] we catched value of attribute
445		if attr =~ "=\s*[\"']" || attr =~ "=\s*$"
446			" Let do attribute specific completion
447			let attrname = matchstr(attr, '.*\ze\s*=')
448			let entered_value = matchstr(attr, ".*=\\s*[\"']\\?\\zs.*")
449			let values = []
450			if attrname == 'href'
451				" 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
461			else
462				if has_key(b:html_omni, tag) && has_key(b:html_omni[tag][1], attrname)
463					let values = b:html_omni[tag][1][attrname]
464				else
465					return []
466				endif
467			endif
468
469			if len(values) == 0
470				return []
471			endif
472
473			" We need special version of sbase
474			let attrbase = matchstr(context, ".*[\"']")
475			let attrquote = matchstr(attrbase, '.$')
476			if attrquote !~ "['\"]"
477				let attrquoteopen = '"'
478				let attrquote = '"'
479			else
480				let attrquoteopen = ''
481			endif
482
483			for m in values
484				" 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
488					call add(res, attrquoteopen . m . attrquote.' ')
489				elseif m =~ entered_value
490					call add(res2, attrquoteopen . m . attrquote.' ')
491				endif
492			endfor
493
494			return res + res2
495
496		endif
497		" }}}
498		" Attribute completion {{{
499		" Shorten context to not include last word
500		let sbase = matchstr(context, '.*\ze\s.*')
501
502		" Load data {{{
503		if !exists("b:html_omni_gen")
504			call htmlcomplete#LoadData()
505		endif
506		" }}}
507
508		if has_key(b:html_omni, tag)
509			let attrs = keys(b:html_omni[tag][1])
510		else
511			return []
512		endif
513
514		for m in sort(attrs)
515			if m =~ '^'.attr
516				call add(res, m)
517			elseif m =~ attr
518				call add(res2, m)
519			endif
520		endfor
521		let menu = res + res2
522		if has_key(b:html_omni, 'vimxmlattrinfo')
523			let final_menu = []
524			for i in range(len(menu))
525				let item = menu[i]
526				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]
529					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
543
544	endif
545	" }}}
546	" Close tag {{{
547	let b:unaryTagsStack = "base meta link hr br param img area input col"
548	if context =~ '^\/'
549		if context =~ '^\/.'
550			return []
551		else
552			let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
553			return [opentag.">"]
554		endif
555	endif
556	" }}}
557	" Load data {{{
558	if !exists("b:html_omni")
559		"runtime! autoload/xml/xhtml10s.vim
560		call htmlcomplete#LoadData()
561	endif
562	" }}}
563	" Tag completion {{{
564	" Deal with tag completion.
565	let opentag = tolower(xmlcomplete#GetLastOpenTag("b:unaryTagsStack"))
566	" MM: TODO: GLOT works always the same but with some weird situation it
567	" behaves as intended in HTML but screws in PHP
568	if opentag == '' || &filetype == 'php' && !has_key(b:html_omni, opentag)
569		" 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.
573	    let tags = keys(b:html_omni)
574		call filter(tags, 'v:val !~ "^vimxml"')
575	else
576		if has_key(b:html_omni, opentag)
577			let tags = b:html_omni[opentag][0]
578		else
579			return []
580		endif
581	endif
582	" }}}
583
584	if exists("uppercase_tag") && uppercase_tag == 1
585		let context = tolower(context)
586	endif
587	" 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
604
605	for m in sort(tags)
606		if m =~ '^'.context
607			call add(res, m)
608		elseif m =~ context
609			call add(res2, m)
610		endif
611	endfor
612	let menu = res + res2
613	let g:me = menu
614	if has_key(b:html_omni, 'vimxmltaginfo')
615		let final_menu = []
616		for i in range(len(menu))
617			let item = menu[i]
618			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]
621			else
622				let m_menu = ''
623				let m_info = ''
624			endif
625			if &filetype == 'html' && exists("uppercase_tag") && uppercase_tag == 1 && item !~ 'DOCTYPE'
626				let item = toupper(item)
627			endif
628			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}]
634		endfor
635	else
636		let final_menu = menu
637	endif
638	return final_menu
639
640	" }}}
641  endif
642endfunction
643
644function! htmlcomplete#LoadData() " {{{
645	if !exists("b:html_omni_flavor")
646		if &filetype == 'html'
647			let b:html_omni_flavor = 'html401t'
648		else
649			let b:html_omni_flavor = 'xhtml10s'
650		endif
651	endif
652	" 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'
659	endif
660	" This repetition is necessary because we don't know if
661	" b:html_omni_flavor file exists and was sourced
662	" Proper checking for files would require iterating through 'rtp'
663	" and could introduce OS dependent mess.
664	if !exists("g:xmldata_".b:html_omni_flavor)
665		if &filetype == 'html'
666			let b:html_omni_flavor = 'html401t'
667		else
668			let b:html_omni_flavor = 'xhtml10s'
669		endif
670	endif
671	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
677endfunction
678" }}}
679" vim:set foldmethod=marker:
680