1" Vim completion script
2" Language:	HTML and XHTML
3" Maintainer:	Mikolaj Machowski ( mikmach AT wp DOT pl )
4" Last Change:	2011 Apr 28
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				  	let classlines = []
289					if filereadable(file)
290						let stylesheet = readfile(file)
291						let stylefile = join(stylesheet, ' ')
292						let stylefile = substitute(stylefile, '+>\*[,', ' ', 'g')
293						if search_for == 'class'
294							let stylesheet = split(stylefile)
295							let classlines = filter(copy(stylesheet), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
296						else
297							let stylesheet = split(stylefile, '[{}]')
298							" Get all lines which fit id syntax
299							let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
300							" Filter out possible color definitions
301							call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
302							" Filter out complex border definitions
303							call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
304							let templines = join(classlines, ' ')
305							let stylelines = split(templines)
306							let classlines = filter(stylelines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
307
308						endif
309					endif
310					" We gathered classes definitions from all external files
311					let classes += classlines
312				endfor
313				if internal == 1
314					let classes += headclasslines
315				endif
316
317				if search_for == 'class'
318					let elements = {}
319					for element in classes
320						if element =~ '^\.'
321							let class = matchstr(element, '^\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
322							let class = substitute(class, ':.*', '', '')
323							if has_key(elements, 'common')
324								let elements['common'] .= ' '.class
325							else
326								let elements['common'] = class
327							endif
328						else
329							let class = matchstr(element, '[a-zA-Z1-6]*\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
330							let tagname = tolower(matchstr(element, '[a-zA-Z1-6]*\ze.'))
331							if tagname != ''
332								if has_key(elements, tagname)
333									let elements[tagname] .= ' '.class
334								else
335									let elements[tagname] = class
336								endif
337							endif
338						endif
339					endfor
340
341					if has_key(elements, tag) && has_key(elements, 'common')
342						let values = split(elements[tag]." ".elements['common'])
343					elseif has_key(elements, tag) && !has_key(elements, 'common')
344						let values = split(elements[tag])
345					elseif !has_key(elements, tag) && has_key(elements, 'common')
346						let values = split(elements['common'])
347					else
348						return []
349					endif
350
351				elseif search_for == 'id'
352					" Find used IDs
353					" 1. Catch whole file
354					let filelines = getline(1, line('$'))
355					" 2. Find lines with possible id
356					let used_id_lines = filter(filelines, 'v:val =~ "id\\s*=\\s*[\"''][a-zA-Z0-9_-]\\+"')
357					" 3a. Join all filtered lines
358					let id_string = join(used_id_lines, ' ')
359					" 3b. And split them to be sure each id is in separate item
360					let id_list = split(id_string, 'id\s*=\s*')
361					" 4. Extract id values
362					let used_id = map(id_list, 'matchstr(v:val, "[\"'']\\zs[a-zA-Z0-9_-]\\+\\ze")')
363					let joined_used_id = ','.join(used_id, ',').','
364
365					let allvalues = map(classes, 'matchstr(v:val, ".*#\\zs[a-zA-Z0-9_-]\\+")')
366
367					let values = []
368
369					for element in classes
370						if joined_used_id !~ ','.element.','
371							let values += [element]
372						endif
373
374					endfor
375
376				endif
377
378				" We need special version of sbase
379				let classbase = matchstr(context, ".*[\"']")
380				let classquote = matchstr(classbase, '.$')
381
382				let entered_class = matchstr(attr, ".*=\\s*[\"']\\zs.*")
383
384				for m in sort(values)
385					if m =~? '^'.entered_class
386						call add(res, m . classquote)
387					elseif m =~? entered_class
388						call add(res2, m . classquote)
389					endif
390				endfor
391
392				return res + res2
393
394			elseif context =~? "style\\s*=\\s*[\"'][^\"']*$"
395				return csscomplete#CompleteCSS(0, context)
396
397			endif
398			" }}}
399			" Complete on-events {{{
400			if context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
401				" We have to:
402				" 1. Find external files
403				let b:js_extfiles = []
404				let l = line('.')
405				let c = col('.')
406				call cursor(1,1)
407				while search('<\@<=script\>', 'W') && line('.') <= l
408					if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
409						let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
410						if filereadable(sname)
411							let b:js_extfiles += readfile(sname)
412						endif
413					endif
414				endwhile
415				" 2. Find at least one <script> tag
416				call cursor(1,1)
417				let js_scripttags = []
418				while search('<script\>', 'W') && line('.') < l
419					if matchstr(getline('.'), '<script[^>]*src') == ''
420						let js_scripttag = getline(line('.'), search('</script>', 'W'))
421						let js_scripttags += js_scripttag
422					endif
423				endwhile
424				let b:js_extfiles += js_scripttags
425
426				" 3. Proper call for javascriptcomplete#CompleteJS
427				call cursor(l,c)
428				let js_context = matchstr(a:base, '\k\+$')
429				let js_shortcontext = substitute(a:base, js_context.'$', '', '')
430				let b:compl_context = context
431				let b:jsrange = [l, l]
432				unlet! l c
433				return javascriptcomplete#CompleteJS(0, js_context)
434
435			endif
436
437			" }}}
438			let stripbase = matchstr(context, ".*\\(on[a-zA-Z]*\\|style\\|class\\)\\s*=\\s*[\"']\\zs.*")
439			" Now we have context stripped from all chars up to style/class.
440			" It may fail with some strange style value combinations.
441			if stripbase !~ "[\"']"
442				return []
443			endif
444		endif
445		" Value of attribute completion {{{
446		" If attr contains =\s*[\"'] we catched value of attribute
447		if attr =~ "=\s*[\"']" || attr =~ "=\s*$"
448			" Let do attribute specific completion
449			let attrname = matchstr(attr, '.*\ze\s*=')
450			let entered_value = matchstr(attr, ".*=\\s*[\"']\\?\\zs.*")
451			let values = []
452			" Load data {{{
453			if !exists("b:html_doctype")
454				call htmlcomplete#CheckDoctype()
455			endif
456			if !exists("b:html_omni")
457				"runtime! autoload/xml/xhtml10s.vim
458				call htmlcomplete#LoadData()
459			endif
460			" }}}
461			if attrname == 'href'
462				" Now we are looking for local anchors defined by name or id
463				if entered_value =~ '^#'
464					let file = join(getline(1, line('$')), ' ')
465					" Split it be sure there will be one id/name element in
466					" item, it will be also first word [a-zA-Z0-9_-] in element
467					let oneelement = split(file, "\\(meta \\)\\@<!\\(name\\|id\\)\\s*=\\s*[\"']")
468					for i in oneelement
469						let values += ['#'.matchstr(i, "^[a-zA-Z][a-zA-Z0-9%_-]*")]
470					endfor
471				endif
472			else
473				if has_key(b:html_omni, tag) && has_key(b:html_omni[tag][1], attrname)
474					let values = b:html_omni[tag][1][attrname]
475				else
476					return []
477				endif
478			endif
479
480			if len(values) == 0
481				return []
482			endif
483
484			" We need special version of sbase
485			let attrbase = matchstr(context, ".*[\"']")
486			let attrquote = matchstr(attrbase, '.$')
487			if attrquote !~ "['\"]"
488				let attrquoteopen = '"'
489				let attrquote = '"'
490			else
491				let attrquoteopen = ''
492			endif
493
494			for m in values
495				" This if is needed to not offer all completions as-is
496				" alphabetically but sort them. Those beginning with entered
497				" part will be as first choices
498				if m =~ '^'.entered_value
499					call add(res, attrquoteopen . m . attrquote)
500				elseif m =~ entered_value
501					call add(res2, attrquoteopen . m . attrquote)
502				endif
503			endfor
504
505			return res + res2
506
507		endif
508		" }}}
509		" Attribute completion {{{
510		" Shorten context to not include last word
511		let sbase = matchstr(context, '.*\ze\s.*')
512
513		" Load data {{{
514		if !exists("b:html_doctype")
515			call htmlcomplete#CheckDoctype()
516		endif
517		if !exists("b:html_omni")
518			call htmlcomplete#LoadData()
519		endif
520		" }}}
521
522		if has_key(b:html_omni, tag)
523			let attrs = keys(b:html_omni[tag][1])
524		else
525			return []
526		endif
527
528		for m in sort(attrs)
529			if m =~ '^'.attr
530				call add(res, m)
531			elseif m =~ attr
532				call add(res2, m)
533			endif
534		endfor
535		let menu = res + res2
536		if has_key(b:html_omni, 'vimxmlattrinfo')
537			let final_menu = []
538			for i in range(len(menu))
539				let item = menu[i]
540				if has_key(b:html_omni['vimxmlattrinfo'], item)
541					let m_menu = b:html_omni['vimxmlattrinfo'][item][0]
542					let m_info = b:html_omni['vimxmlattrinfo'][item][1]
543				else
544					let m_menu = ''
545					let m_info = ''
546				endif
547				if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
548					let item = item
549					let m_menu = 'Bool'
550				else
551					let item .= '="'
552				endif
553				let final_menu += [{'word':item, 'menu':m_menu, 'info':m_info}]
554			endfor
555		else
556			let final_menu = []
557			for i in range(len(menu))
558				let item = menu[i]
559				if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
560					let item = item
561				else
562					let item .= '="'
563				endif
564				let final_menu += [item]
565			endfor
566			return final_menu
567
568		endif
569		return final_menu
570
571	endif
572	" }}}
573	" Close tag {{{
574	let b:unaryTagsStack = "base meta link hr br param img area input col"
575	if context =~ '^\/'
576		if context =~ '^\/.'
577			return []
578		else
579			let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
580			return [opentag.">"]
581		endif
582	endif
583	" }}}
584	" Load data {{{
585	if !exists("b:html_doctype")
586		call htmlcomplete#CheckDoctype()
587	endif
588	if !exists("b:html_omni")
589		"runtime! autoload/xml/xhtml10s.vim
590		call htmlcomplete#LoadData()
591	endif
592	" }}}
593	" Tag completion {{{
594	" Deal with tag completion.
595	let opentag = tolower(xmlcomplete#GetLastOpenTag("b:unaryTagsStack"))
596	" MM: TODO: GLOT works always the same but with some weird situation it
597	" behaves as intended in HTML but screws in PHP
598	if opentag == '' || &filetype == 'php' && !has_key(b:html_omni, opentag)
599		" Hack for sometimes failing GetLastOpenTag.
600		" As far as I tested fail isn't GLOT fault but problem
601		" of invalid document - not properly closed tags and other mish-mash.
602		" Also when document is empty. Return list of *all* tags.
603	    let tags = keys(b:html_omni)
604		call filter(tags, 'v:val !~ "^vimxml"')
605	else
606		if has_key(b:html_omni, opentag)
607			let tags = b:html_omni[opentag][0]
608		else
609			return []
610		endif
611	endif
612	" }}}
613
614	if exists("uppercase_tag") && uppercase_tag == 1
615		let context = tolower(context)
616	endif
617	" Handle XML keywords: DOCTYPE
618	if opentag == ''
619		let tags += [
620				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">',
621				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">',
622				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">',
623				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">',
624				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
625				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
626				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
627				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
628				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
629				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
630				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/1999/xhtml">'
631				\ ]
632	endif
633
634	for m in sort(tags)
635		if m =~ '^'.context
636			call add(res, m)
637		elseif m =~ context
638			call add(res2, m)
639		endif
640	endfor
641	let menu = res + res2
642	if has_key(b:html_omni, 'vimxmltaginfo')
643		let final_menu = []
644		for i in range(len(menu))
645			let item = menu[i]
646			if has_key(b:html_omni['vimxmltaginfo'], item)
647				let m_menu = b:html_omni['vimxmltaginfo'][item][0]
648				let m_info = b:html_omni['vimxmltaginfo'][item][1]
649			else
650				let m_menu = ''
651				let m_info = ''
652			endif
653			if &filetype == 'html' && exists("uppercase_tag") && uppercase_tag == 1 && item !~ 'DOCTYPE'
654				let item = toupper(item)
655			endif
656			if item =~ 'DOCTYPE'
657				let abbr = 'DOCTYPE '.matchstr(item, 'DTD \zsX\?HTML .\{-}\ze\/\/')
658			else
659				let abbr = item
660			endif
661			let final_menu += [{'abbr':abbr, 'word':item, 'menu':m_menu, 'info':m_info}]
662		endfor
663	else
664		let final_menu = menu
665	endif
666	return final_menu
667
668	" }}}
669  endif
670endfunction
671
672function! htmlcomplete#LoadData() " {{{
673	if !exists("b:html_omni_flavor")
674		if &filetype == 'html'
675			let b:html_omni_flavor = 'html401t'
676		else
677			let b:html_omni_flavor = 'xhtml10s'
678		endif
679	endif
680	" With that if we still have bloated memory but create new buffer
681	" variables only by linking to existing g:variable, not sourcing whole
682	" file.
683	if exists('g:xmldata_'.b:html_omni_flavor)
684		exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
685	else
686		exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
687		exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
688	endif
689endfunction
690" }}}
691function! htmlcomplete#CheckDoctype() " {{{
692	if exists('b:html_omni_flavor')
693		let old_flavor = b:html_omni_flavor
694	else
695		let old_flavor = ''
696	endif
697	let i = 1
698	while i < 10 && i < line("$")
699		let line = getline(i)
700		if line =~ '<!DOCTYPE.*\<DTD HTML 3\.2'
701			let b:html_omni_flavor = 'html32'
702			let b:html_doctype = 1
703			break
704		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Transitional'
705			let b:html_omni_flavor = 'html40t'
706			let b:html_doctype = 1
707			break
708		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Frameset'
709			let b:html_omni_flavor = 'html40f'
710			let b:html_doctype = 1
711			break
712		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0'
713			let b:html_omni_flavor = 'html40s'
714			let b:html_doctype = 1
715			break
716		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Transitional'
717			let b:html_omni_flavor = 'html401t'
718			let b:html_doctype = 1
719			break
720		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Frameset'
721			let b:html_omni_flavor = 'html401f'
722			let b:html_doctype = 1
723			break
724		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01'
725			let b:html_omni_flavor = 'html401s'
726			let b:html_doctype = 1
727			break
728		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Transitional'
729			let b:html_omni_flavor = 'xhtml10t'
730			let b:html_doctype = 1
731			break
732		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Frameset'
733			let b:html_omni_flavor = 'xhtml10f'
734			let b:html_doctype = 1
735			break
736		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Strict'
737			let b:html_omni_flavor = 'xhtml10s'
738			let b:html_doctype = 1
739			break
740		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.1'
741			let b:html_omni_flavor = 'xhtml11'
742			let b:html_doctype = 1
743			break
744		endif
745		let i += 1
746	endwhile
747	if !exists("b:html_doctype")
748		return
749	else
750		" Tie g:xmldata with b:html_omni this way we need to sourca data file only
751		" once, not every time per buffer.
752		if old_flavor == b:html_omni_flavor
753			return
754		else
755			if exists('g:xmldata_'.b:html_omni_flavor)
756				exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
757			else
758				exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
759				exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
760			endif
761			return
762		endif
763	endif
764endfunction
765" }}}
766" vim:set foldmethod=marker:
767