1" Vim completion script
2" Language:	HTML and XHTML
3" Maintainer:	Mikolaj Machowski ( mikmach AT wp DOT pl )
4" Last Change:	2006 Oct 19
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-1) + [b:compl_context]
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	" Check if we should do CSS completion inside of <style> tag
131	" or JS completion inside of <script> tag or PHP completion in case of <?
132	" tag AND &ft==php
133	if exists("b:csscompl")
134		unlet! b:csscompl
135		let context = b:compl_context
136		unlet! b:compl_context
137		return csscomplete#CompleteCSS(0, context)
138	elseif exists("b:jscompl")
139		unlet! b:jscompl
140		return javascriptcomplete#CompleteJS(0, a:base)
141	elseif exists("b:phpcompl")
142		unlet! b:phpcompl
143		let context = b:compl_context
144		return phpcomplete#CompletePHP(0, a:base)
145	else
146		if len(b:compl_context) == 0 && !exists("b:entitiescompl")
147			return []
148		endif
149		let context = matchstr(b:compl_context, '.\zs.*')
150	endif
151	unlet! b:compl_context
152	" Entities completion {{{
153	if exists("b:entitiescompl")
154		unlet! b:entitiescompl
155
156		if !exists("b:html_doctype")
157			call htmlcomplete#CheckDoctype()
158		endif
159		if !exists("b:html_omni")
160			"runtime! autoload/xml/xhtml10s.vim
161			call htmlcomplete#LoadData()
162		endif
163
164	    let entities =  b:html_omni['vimxmlentities']
165
166		if len(a:base) == 1
167			for m in entities
168				if m =~ '^'.a:base
169					call add(res, m.';')
170				endif
171			endfor
172			return res
173		else
174			for m in entities
175				if m =~? '^'.a:base
176					call add(res, m.';')
177				elseif m =~? a:base
178					call add(res2, m.';')
179				endif
180			endfor
181
182			return res + res2
183		endif
184
185
186	endif
187	" }}}
188	if context =~ '>'
189		" Generally if context contains > it means we are outside of tag and
190		" should abandon action - with one exception: <style> span { bo
191		if context =~ 'style[^>]\{-}>[^<]\{-}$'
192			return csscomplete#CompleteCSS(0, context)
193		elseif context =~ 'script[^>]\{-}>[^<]\{-}$'
194			let b:jsrange = [line('.'), search('<\/script\>', 'nW')]
195			return javascriptcomplete#CompleteJS(0, context)
196		else
197			return []
198		endif
199	endif
200
201	" If context contains > it means we are already outside of tag and we
202	" should abandon action
203	" If context contains white space it is attribute.
204	" It can be also value of attribute.
205	" We have to get first word to offer proper completions
206	if context == ''
207		let tag = ''
208	else
209		let tag = split(context)[0]
210		" Detect if tag is uppercase to return in proper case,
211		" we need to make it lowercase for processing
212		if tag =~ '^[A-Z]*$'
213			let uppercase_tag = 1
214			let tag = tolower(tag)
215		else
216			let uppercase_tag = 0
217		endif
218	endif
219	" Get last word, it should be attr name
220	let attr = matchstr(context, '.*\s\zs.*')
221	" Possible situations where any prediction would be difficult:
222	" 1. Events attributes
223	if context =~ '\s'
224		" Sort out style, class, and on* cases
225		if context =~? "\\(on[a-z]*\\|id\\|style\\|class\\)\\s*=\\s*[\"']"
226			" Id, class completion {{{
227			if context =~? "\\(id\\|class\\)\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
228				if context =~? "class\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
229					let search_for = "class"
230				elseif context =~? "id\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
231					let search_for = "id"
232				endif
233				" Handle class name completion
234				" 1. Find lines of <link stylesheet>
235				" 1a. Check file for @import
236				" 2. Extract filename(s?) of stylesheet,
237				call cursor(1,1)
238				let head = getline(search('<head\>'), search('<\/head>'))
239				let headjoined = join(copy(head), ' ')
240				if headjoined =~ '<style'
241					" Remove possibly confusing CSS operators
242					let stylehead = substitute(headjoined, '+>\*[,', ' ', 'g')
243					if search_for == 'class'
244						let styleheadlines = split(stylehead)
245						let headclasslines = filter(copy(styleheadlines), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
246					else
247						let stylesheet = split(headjoined, '[{}]')
248						" Get all lines which fit id syntax
249						let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
250						" Filter out possible color definitions
251						call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
252						" Filter out complex border definitions
253						call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
254						let templines = join(classlines, ' ')
255						let headclasslines = split(templines)
256						call filter(headclasslines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
257					endif
258					let internal = 1
259				else
260					let internal = 0
261				endif
262				let styletable = []
263				let secimportfiles = []
264				let filestable = filter(copy(head), "v:val =~ '\\(@import\\|link.*stylesheet\\)'")
265				for line in filestable
266					if line =~ "@import"
267						let styletable += [matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")]
268					elseif line =~ "<link"
269						let styletable += [matchstr(line, "href\\s*=\\s*[\"']\\zs\\f\\+\\ze")]
270					endif
271				endfor
272				for file in styletable
273					if filereadable(file)
274						let stylesheet = readfile(file)
275						let secimport = filter(copy(stylesheet), "v:val =~ '@import'")
276						if len(secimport) > 0
277							for line in secimport
278								let secfile = matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")
279								let secfile = fnamemodify(file, ":p:h").'/'.secfile
280								let secimportfiles += [secfile]
281							endfor
282						endif
283					endif
284				endfor
285				let cssfiles = styletable + secimportfiles
286				let classes = []
287				for file in cssfiles
288					if filereadable(file)
289						let stylesheet = readfile(file)
290						let stylefile = join(stylesheet, ' ')
291						let stylefile = substitute(stylefile, '+>\*[,', ' ', 'g')
292						if search_for == 'class'
293							let stylesheet = split(stylefile)
294							let classlines = filter(copy(stylesheet), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
295						else
296							let stylesheet = split(stylefile, '[{}]')
297							" Get all lines which fit id syntax
298							let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
299							" Filter out possible color definitions
300							call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
301							" Filter out complex border definitions
302							call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
303							let templines = join(classlines, ' ')
304							let stylelines = split(templines)
305							let classlines = filter(stylelines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
306
307						endif
308					endif
309					" We gathered classes definitions from all external files
310					let classes += classlines
311				endfor
312				if internal == 1
313					let classes += headclasslines
314				endif
315
316				if search_for == 'class'
317					let elements = {}
318					for element in classes
319						if element =~ '^\.'
320							let class = matchstr(element, '^\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
321							let class = substitute(class, ':.*', '', '')
322							if has_key(elements, 'common')
323								let elements['common'] .= ' '.class
324							else
325								let elements['common'] = class
326							endif
327						else
328							let class = matchstr(element, '[a-zA-Z1-6]*\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
329							let tagname = tolower(matchstr(element, '[a-zA-Z1-6]*\ze.'))
330							if tagname != ''
331								if has_key(elements, tagname)
332									let elements[tagname] .= ' '.class
333								else
334									let elements[tagname] = class
335								endif
336							endif
337						endif
338					endfor
339
340					if has_key(elements, tag) && has_key(elements, 'common')
341						let values = split(elements[tag]." ".elements['common'])
342					elseif has_key(elements, tag) && !has_key(elements, 'common')
343						let values = split(elements[tag])
344					elseif !has_key(elements, tag) && has_key(elements, 'common')
345						let values = split(elements['common'])
346					else
347						return []
348					endif
349
350				elseif search_for == 'id'
351					" Find used IDs
352					" 1. Catch whole file
353					let filelines = getline(1, line('$'))
354					" 2. Find lines with possible id
355					let used_id_lines = filter(filelines, 'v:val =~ "id\\s*=\\s*[\"''][a-zA-Z0-9_-]\\+"')
356					" 3a. Join all filtered lines
357					let id_string = join(used_id_lines, ' ')
358					" 3b. And split them to be sure each id is in separate item
359					let id_list = split(id_string, 'id\s*=\s*')
360					" 4. Extract id values
361					let used_id = map(id_list, 'matchstr(v:val, "[\"'']\\zs[a-zA-Z0-9_-]\\+\\ze")')
362					let joined_used_id = ','.join(used_id, ',').','
363
364					let allvalues = map(classes, 'matchstr(v:val, ".*#\\zs[a-zA-Z0-9_-]\\+")')
365
366					let values = []
367
368					for element in classes
369						if joined_used_id !~ ','.element.','
370							let values += [element]
371						endif
372
373					endfor
374
375				endif
376
377				" We need special version of sbase
378				let classbase = matchstr(context, ".*[\"']")
379				let classquote = matchstr(classbase, '.$')
380
381				let entered_class = matchstr(attr, ".*=\\s*[\"']\\zs.*")
382
383				for m in sort(values)
384					if m =~? '^'.entered_class
385						call add(res, m . classquote)
386					elseif m =~? entered_class
387						call add(res2, m . classquote)
388					endif
389				endfor
390
391				return res + res2
392
393			elseif context =~? "style\\s*=\\s*[\"'][^\"']*$"
394				return csscomplete#CompleteCSS(0, context)
395
396			endif
397			" }}}
398			" Complete on-events {{{
399			if context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
400				" We have to:
401				" 1. Find external files
402				let b:js_extfiles = []
403				let l = line('.')
404				let c = col('.')
405				call cursor(1,1)
406				while search('<\@<=script\>', 'W') && line('.') <= l
407					if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
408						let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
409						if filereadable(sname)
410							let b:js_extfiles += readfile(sname)
411						endif
412					endif
413				endwhile
414				" 2. Find at least one <script> tag
415				call cursor(1,1)
416				let js_scripttags = []
417				while search('<script\>', 'W') && line('.') < l
418					if matchstr(getline('.'), '<script[^>]*src') == ''
419						let js_scripttag = getline(line('.'), search('</script>', 'W'))
420						let js_scripttags += js_scripttag
421					endif
422				endwhile
423				let b:js_extfiles += js_scripttags
424
425				" 3. Proper call for javascriptcomplete#CompleteJS
426				call cursor(l,c)
427				let js_context = matchstr(a:base, '\k\+$')
428				let js_shortcontext = substitute(a:base, js_context.'$', '', '')
429				let b:compl_context = context
430				let b:jsrange = [l, l]
431				unlet! l c
432				return javascriptcomplete#CompleteJS(0, js_context)
433
434			endif
435
436			" }}}
437			let stripbase = matchstr(context, ".*\\(on[a-zA-Z]*\\|style\\|class\\)\\s*=\\s*[\"']\\zs.*")
438			" Now we have context stripped from all chars up to style/class.
439			" It may fail with some strange style value combinations.
440			if stripbase !~ "[\"']"
441				return []
442			endif
443		endif
444		" Value of attribute completion {{{
445		" If attr contains =\s*[\"'] we catched value of attribute
446		if attr =~ "=\s*[\"']" || attr =~ "=\s*$"
447			" Let do attribute specific completion
448			let attrname = matchstr(attr, '.*\ze\s*=')
449			let entered_value = matchstr(attr, ".*=\\s*[\"']\\?\\zs.*")
450			let values = []
451			" Load data {{{
452			if !exists("b:html_doctype")
453				call htmlcomplete#CheckDoctype()
454			endif
455			if !exists("b:html_omni")
456				"runtime! autoload/xml/xhtml10s.vim
457				call htmlcomplete#LoadData()
458			endif
459			" }}}
460			if attrname == 'href'
461				" Now we are looking for local anchors defined by name or id
462				if entered_value =~ '^#'
463					let file = join(getline(1, line('$')), ' ')
464					" Split it be sure there will be one id/name element in
465					" item, it will be also first word [a-zA-Z0-9_-] in element
466					let oneelement = split(file, "\\(meta \\)\\@<!\\(name\\|id\\)\\s*=\\s*[\"']")
467					for i in oneelement
468						let values += ['#'.matchstr(i, "^[a-zA-Z][a-zA-Z0-9%_-]*")]
469					endfor
470				endif
471			else
472				if has_key(b:html_omni, tag) && has_key(b:html_omni[tag][1], attrname)
473					let values = b:html_omni[tag][1][attrname]
474				else
475					return []
476				endif
477			endif
478
479			if len(values) == 0
480				return []
481			endif
482
483			" We need special version of sbase
484			let attrbase = matchstr(context, ".*[\"']")
485			let attrquote = matchstr(attrbase, '.$')
486			if attrquote !~ "['\"]"
487				let attrquoteopen = '"'
488				let attrquote = '"'
489			else
490				let attrquoteopen = ''
491			endif
492
493			for m in values
494				" This if is needed to not offer all completions as-is
495				" alphabetically but sort them. Those beginning with entered
496				" part will be as first choices
497				if m =~ '^'.entered_value
498					call add(res, attrquoteopen . m . attrquote)
499				elseif m =~ entered_value
500					call add(res2, attrquoteopen . m . attrquote)
501				endif
502			endfor
503
504			return res + res2
505
506		endif
507		" }}}
508		" Attribute completion {{{
509		" Shorten context to not include last word
510		let sbase = matchstr(context, '.*\ze\s.*')
511
512		" Load data {{{
513		if !exists("b:html_doctype")
514			call htmlcomplete#CheckDoctype()
515		endif
516		if !exists("b:html_omni")
517			call htmlcomplete#LoadData()
518		endif
519		" }}}
520
521		if has_key(b:html_omni, tag)
522			let attrs = keys(b:html_omni[tag][1])
523		else
524			return []
525		endif
526
527		for m in sort(attrs)
528			if m =~ '^'.attr
529				call add(res, m)
530			elseif m =~ attr
531				call add(res2, m)
532			endif
533		endfor
534		let menu = res + res2
535		if has_key(b:html_omni, 'vimxmlattrinfo')
536			let final_menu = []
537			for i in range(len(menu))
538				let item = menu[i]
539				if has_key(b:html_omni['vimxmlattrinfo'], item)
540					let m_menu = b:html_omni['vimxmlattrinfo'][item][0]
541					let m_info = b:html_omni['vimxmlattrinfo'][item][1]
542				else
543					let m_menu = ''
544					let m_info = ''
545				endif
546				if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
547					let item = item
548					let m_menu = 'Bool'
549				else
550					let item .= '="'
551				endif
552				let final_menu += [{'word':item, 'menu':m_menu, 'info':m_info}]
553			endfor
554		else
555			let final_menu = []
556			for i in range(len(menu))
557				let item = menu[i]
558				if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
559					let item = item
560				else
561					let item .= '="'
562				endif
563				let final_menu += [item]
564			endfor
565			return final_menu
566
567		endif
568		return final_menu
569
570	endif
571	" }}}
572	" Close tag {{{
573	let b:unaryTagsStack = "base meta link hr br param img area input col"
574	if context =~ '^\/'
575		if context =~ '^\/.'
576			return []
577		else
578			let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
579			return [opentag.">"]
580		endif
581	endif
582	" }}}
583	" Load data {{{
584	if !exists("b:html_doctype")
585		call htmlcomplete#CheckDoctype()
586	endif
587	if !exists("b:html_omni")
588		"runtime! autoload/xml/xhtml10s.vim
589		call htmlcomplete#LoadData()
590	endif
591	" }}}
592	" Tag completion {{{
593	" Deal with tag completion.
594	let opentag = tolower(xmlcomplete#GetLastOpenTag("b:unaryTagsStack"))
595	" MM: TODO: GLOT works always the same but with some weird situation it
596	" behaves as intended in HTML but screws in PHP
597	if opentag == '' || &filetype == 'php' && !has_key(b:html_omni, opentag)
598		" Hack for sometimes failing GetLastOpenTag.
599		" As far as I tested fail isn't GLOT fault but problem
600		" of invalid document - not properly closed tags and other mish-mash.
601		" Also when document is empty. Return list of *all* tags.
602	    let tags = keys(b:html_omni)
603		call filter(tags, 'v:val !~ "^vimxml"')
604	else
605		if has_key(b:html_omni, opentag)
606			let tags = b:html_omni[opentag][0]
607		else
608			return []
609		endif
610	endif
611	" }}}
612
613	if exists("uppercase_tag") && uppercase_tag == 1
614		let context = tolower(context)
615	endif
616	" Handle XML keywords: DOCTYPE
617	if opentag == ''
618		let tags += [
619				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">',
620				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">',
621				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">',
622				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">',
623				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
624				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
625				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
626				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
627				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
628				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
629				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/1999/xhtml">'
630				\ ]
631	endif
632
633	for m in sort(tags)
634		if m =~ '^'.context
635			call add(res, m)
636		elseif m =~ context
637			call add(res2, m)
638		endif
639	endfor
640	let menu = res + res2
641	if has_key(b:html_omni, 'vimxmltaginfo')
642		let final_menu = []
643		for i in range(len(menu))
644			let item = menu[i]
645			if has_key(b:html_omni['vimxmltaginfo'], item)
646				let m_menu = b:html_omni['vimxmltaginfo'][item][0]
647				let m_info = b:html_omni['vimxmltaginfo'][item][1]
648			else
649				let m_menu = ''
650				let m_info = ''
651			endif
652			if &filetype == 'html' && exists("uppercase_tag") && uppercase_tag == 1 && item !~ 'DOCTYPE'
653				let item = toupper(item)
654			endif
655			if item =~ 'DOCTYPE'
656				let abbr = 'DOCTYPE '.matchstr(item, 'DTD \zsX\?HTML .\{-}\ze\/\/')
657			else
658				let abbr = item
659			endif
660			let final_menu += [{'abbr':abbr, 'word':item, 'menu':m_menu, 'info':m_info}]
661		endfor
662	else
663		let final_menu = menu
664	endif
665	return final_menu
666
667	" }}}
668  endif
669endfunction
670
671function! htmlcomplete#LoadData() " {{{
672	if !exists("b:html_omni_flavor")
673		if &filetype == 'html'
674			let b:html_omni_flavor = 'html401t'
675		else
676			let b:html_omni_flavor = 'xhtml10s'
677		endif
678	endif
679	" With that if we still have bloated memory but create new buffer
680	" variables only by linking to existing g:variable, not sourcing whole
681	" file.
682	if exists('g:xmldata_'.b:html_omni_flavor)
683		exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
684	else
685		exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
686		exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
687	endif
688endfunction
689" }}}
690function! htmlcomplete#CheckDoctype() " {{{
691	if exists('b:html_omni_flavor')
692		let old_flavor = b:html_omni_flavor
693	else
694		let old_flavor = ''
695	endif
696	let i = 1
697	while i < 10 && i < line("$")
698		let line = getline(i)
699		if line =~ '<!DOCTYPE.*\<DTD HTML 3\.2'
700			let b:html_omni_flavor = 'html32'
701			let b:html_doctype = 1
702			break
703		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Transitional'
704			let b:html_omni_flavor = 'html40t'
705			let b:html_doctype = 1
706			break
707		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Frameset'
708			let b:html_omni_flavor = 'html40f'
709			let b:html_doctype = 1
710			break
711		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0'
712			let b:html_omni_flavor = 'html40s'
713			let b:html_doctype = 1
714			break
715		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Transitional'
716			let b:html_omni_flavor = 'html401t'
717			let b:html_doctype = 1
718			break
719		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Frameset'
720			let b:html_omni_flavor = 'html401f'
721			let b:html_doctype = 1
722			break
723		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01'
724			let b:html_omni_flavor = 'html401s'
725			let b:html_doctype = 1
726			break
727		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Transitional'
728			let b:html_omni_flavor = 'xhtml10t'
729			let b:html_doctype = 1
730			break
731		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Frameset'
732			let b:html_omni_flavor = 'xhtml10f'
733			let b:html_doctype = 1
734			break
735		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Strict'
736			let b:html_omni_flavor = 'xhtml10s'
737			let b:html_doctype = 1
738			break
739		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.1'
740			let b:html_omni_flavor = 'xhtml11'
741			let b:html_doctype = 1
742			break
743		endif
744		let i += 1
745	endwhile
746	if !exists("b:html_doctype")
747		return
748	else
749		" Tie g:xmldata with b:html_omni this way we need to sourca data file only
750		" once, not every time per buffer.
751		if old_flavor == b:html_omni_flavor
752			return
753		else
754			if exists('g:xmldata_'.b:html_omni_flavor)
755				exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
756			else
757				exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
758				exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
759			endif
760			return
761		endif
762	endif
763endfunction
764" }}}
765" vim:set foldmethod=marker:
766