1a5792f58SBram Moolenaar" Vim completion script
2d12f5c17SBram Moolenaar" Language:	XML
3a5792f58SBram Moolenaar" Maintainer:	Mikolaj Machowski ( mikmach AT wp DOT pl )
414b6945eSBram Moolenaar" Last Change:	2013 Jun 29
53577c6faSBram Moolenaar" Version: 1.9
69964e468SBram Moolenaar"
79964e468SBram Moolenaar" Changelog:
83577c6faSBram Moolenaar" 1.9 - 2007 Aug 15
93577c6faSBram Moolenaar" 		- fix closing of namespaced tags (Johannes Weiss)
109964e468SBram Moolenaar" 1.8 - 2006 Jul 18
119964e468SBram Moolenaar"       - allow for closing of xml tags even when data file isn't available
12a5792f58SBram Moolenaar
13a5792f58SBram Moolenaar" This function will create Dictionary with users namespace strings and values
14a5792f58SBram Moolenaar" canonical (system) names of data files.  Names should be lowercase,
15a5792f58SBram Moolenaar" descriptive to avoid any future conflicts. For example 'xhtml10s' should be
16a5792f58SBram Moolenaar" name for data of XHTML 1.0 Strict and 'xhtml10t' for XHTML 1.0 Transitional
17910f66f9SBram Moolenaar" User interface will be provided by XMLns command defined in ftplugin/xml.vim
18a5792f58SBram Moolenaar" Currently supported canonicals are:
19a5792f58SBram Moolenaar" xhtml10s - XHTML 1.0 Strict
20a5792f58SBram Moolenaar" xsl      - XSL
2118144c84SBram Moolenaarfunction! xmlcomplete#CreateConnection(canonical, ...) " {{{
22a5792f58SBram Moolenaar
23a5792f58SBram Moolenaar	" When only one argument provided treat name as default namespace (without
24a5792f58SBram Moolenaar	" 'prefix:').
25a5792f58SBram Moolenaar	if exists("a:1")
26a5792f58SBram Moolenaar		let users = a:1
27a5792f58SBram Moolenaar	else
28a5792f58SBram Moolenaar		let users = 'DEFAULT'
29a5792f58SBram Moolenaar	endif
30a5792f58SBram Moolenaar
31a5792f58SBram Moolenaar	" Source data file. Due to suspected errors in autoload do it with
32a5792f58SBram Moolenaar	" :runtime.
33a5792f58SBram Moolenaar	" TODO: make it properly (using autoload, that is) later
34a5792f58SBram Moolenaar	exe "runtime autoload/xml/".a:canonical.".vim"
35a5792f58SBram Moolenaar
36a5792f58SBram Moolenaar	" Remove all traces of unexisting files to return [] when trying
37a5792f58SBram Moolenaar	" omnicomplete something
38a5792f58SBram Moolenaar	" TODO: give warning about non-existing canonicals - should it be?
39a5792f58SBram Moolenaar	if !exists("g:xmldata_".a:canonical)
40a5792f58SBram Moolenaar		unlet! g:xmldata_connection
41a5792f58SBram Moolenaar		return 0
42a5792f58SBram Moolenaar	endif
43a5792f58SBram Moolenaar
44a5792f58SBram Moolenaar	" We need to initialize Dictionary to add key-value pair
45a5792f58SBram Moolenaar	if !exists("g:xmldata_connection")
46a5792f58SBram Moolenaar		let g:xmldata_connection = {}
47a5792f58SBram Moolenaar	endif
48a5792f58SBram Moolenaar
49a5792f58SBram Moolenaar	let g:xmldata_connection[users] = a:canonical
50a5792f58SBram Moolenaar
51a5792f58SBram Moolenaarendfunction
5218144c84SBram Moolenaar" }}}
53a5792f58SBram Moolenaar
5418144c84SBram Moolenaarfunction! xmlcomplete#CreateEntConnection(...) " {{{
55a5792f58SBram Moolenaar	if a:0 > 0
56a5792f58SBram Moolenaar		let g:xmldata_entconnect = a:1
57a5792f58SBram Moolenaar	else
58a5792f58SBram Moolenaar		let g:xmldata_entconnect = 'DEFAULT'
59a5792f58SBram Moolenaar	endif
60a5792f58SBram Moolenaarendfunction
6118144c84SBram Moolenaar" }}}
62a5792f58SBram Moolenaar
63a5792f58SBram Moolenaarfunction! xmlcomplete#CompleteTags(findstart, base)
64a5792f58SBram Moolenaar  if a:findstart
65a5792f58SBram Moolenaar    " locate the start of the word
66d12f5c17SBram Moolenaar	let curline = line('.')
67a5792f58SBram Moolenaar    let line = getline('.')
68a5792f58SBram Moolenaar    let start = col('.') - 1
69a5792f58SBram Moolenaar	let compl_begin = col('.') - 2
70a5792f58SBram Moolenaar
71a5792f58SBram Moolenaar    while start >= 0 && line[start - 1] =~ '\(\k\|[:.-]\)'
72a5792f58SBram Moolenaar		let start -= 1
73a5792f58SBram Moolenaar    endwhile
74a5792f58SBram Moolenaar
75a5792f58SBram Moolenaar	if start >= 0 && line[start - 1] =~ '&'
76a5792f58SBram Moolenaar		let b:entitiescompl = 1
77a5792f58SBram Moolenaar		let b:compl_context = ''
78a5792f58SBram Moolenaar		return start
79a5792f58SBram Moolenaar	endif
80a5792f58SBram Moolenaar
81a5792f58SBram Moolenaar	let b:compl_context = getline('.')[0:(compl_begin)]
82d12f5c17SBram Moolenaar	if b:compl_context !~ '<[^>]*$'
83d12f5c17SBram Moolenaar		" Look like we may have broken tag. Check previous lines. Up to
84d12f5c17SBram Moolenaar		" 10?
85d12f5c17SBram Moolenaar		let i = 1
86d12f5c17SBram Moolenaar		while 1
87d12f5c17SBram Moolenaar			let context_line = getline(curline-i)
88d12f5c17SBram Moolenaar			if context_line =~ '<[^>]*$'
89d12f5c17SBram Moolenaar				" Yep, this is this line
909964e468SBram Moolenaar				let context_lines = getline(curline-i, curline-1) + [b:compl_context]
91d12f5c17SBram Moolenaar				let b:compl_context = join(context_lines, ' ')
92d12f5c17SBram Moolenaar				break
93c15ef30cSBram Moolenaar			elseif context_line =~ '>[^<]*$' || i == curline
94d12f5c17SBram Moolenaar				" Normal tag line, no need for completion at all
95c15ef30cSBram Moolenaar				" OR reached first line without tag at all
96d12f5c17SBram Moolenaar				let b:compl_context = ''
97d12f5c17SBram Moolenaar				break
98d12f5c17SBram Moolenaar			endif
99d12f5c17SBram Moolenaar			let i += 1
100d12f5c17SBram Moolenaar		endwhile
101d12f5c17SBram Moolenaar		" Make sure we don't have counter
102d12f5c17SBram Moolenaar		unlet! i
103d12f5c17SBram Moolenaar	endif
104d12f5c17SBram Moolenaar	let b:compl_context = matchstr(b:compl_context, '.*\zs<.*')
105a5792f58SBram Moolenaar
106a5792f58SBram Moolenaar	" Make sure we will have only current namespace
107a5792f58SBram Moolenaar	unlet! b:xml_namespace
108d12f5c17SBram Moolenaar	let b:xml_namespace = matchstr(b:compl_context, '^<\zs\k*\ze:')
109a5792f58SBram Moolenaar	if b:xml_namespace == ''
110a5792f58SBram Moolenaar		let b:xml_namespace = 'DEFAULT'
111a5792f58SBram Moolenaar	endif
112a5792f58SBram Moolenaar
113a5792f58SBram Moolenaar    return start
114a5792f58SBram Moolenaar
115a5792f58SBram Moolenaar  else
116a5792f58SBram Moolenaar	" Initialize base return lists
117a5792f58SBram Moolenaar    let res = []
118a5792f58SBram Moolenaar    let res2 = []
119a5792f58SBram Moolenaar	" a:base is very short - we need context
120d12f5c17SBram Moolenaar	if len(b:compl_context) == 0  && !exists("b:entitiescompl")
121d12f5c17SBram Moolenaar		return []
122d12f5c17SBram Moolenaar	endif
123d12f5c17SBram Moolenaar	let context = matchstr(b:compl_context, '^<\zs.*')
124a5792f58SBram Moolenaar	unlet! b:compl_context
1259964e468SBram Moolenaar	" There is no connection of namespace and data file.
1269964e468SBram Moolenaar	if !exists("g:xmldata_connection") || g:xmldata_connection == {}
1279964e468SBram Moolenaar		" There is still possibility we may do something - eg. close tag
1289964e468SBram Moolenaar		let b:unaryTagsStack = "base meta link hr br param img area input col"
1299964e468SBram Moolenaar		if context =~ '^\/'
1309964e468SBram Moolenaar			let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
1319964e468SBram Moolenaar			return [opentag.">"]
1329964e468SBram Moolenaar		else
1339964e468SBram Moolenaar			return []
1349964e468SBram Moolenaar		endif
1359964e468SBram Moolenaar	endif
136a5792f58SBram Moolenaar
137a5792f58SBram Moolenaar	" Make entities completion
138a5792f58SBram Moolenaar	if exists("b:entitiescompl")
139a5792f58SBram Moolenaar		unlet! b:entitiescompl
140a5792f58SBram Moolenaar
141a5792f58SBram Moolenaar		if !exists("g:xmldata_entconnect") || g:xmldata_entconnect == 'DEFAULT'
142a5792f58SBram Moolenaar			let values =  g:xmldata{'_'.g:xmldata_connection['DEFAULT']}['vimxmlentities']
143a5792f58SBram Moolenaar		else
144a5792f58SBram Moolenaar			let values =  g:xmldata{'_'.g:xmldata_entconnect}['vimxmlentities']
145a5792f58SBram Moolenaar		endif
146a5792f58SBram Moolenaar
147a5792f58SBram Moolenaar		" Get only lines with entity declarations but throw out
148a5792f58SBram Moolenaar		" parameter-entities - they may be completed in future
149a5792f58SBram Moolenaar		let entdecl = filter(getline(1, "$"), 'v:val =~ "<!ENTITY\\s\\+[^%]"')
150a5792f58SBram Moolenaar
151a5792f58SBram Moolenaar		if len(entdecl) > 0
152a5792f58SBram Moolenaar			let intent = map(copy(entdecl), 'matchstr(v:val, "<!ENTITY\\s\\+\\zs\\(\\k\\|[.-:]\\)\\+\\ze")')
153a5792f58SBram Moolenaar			let values = intent + values
154a5792f58SBram Moolenaar		endif
155a5792f58SBram Moolenaar
156d12f5c17SBram Moolenaar		if len(a:base) == 1
157a5792f58SBram Moolenaar			for m in values
158a5792f58SBram Moolenaar				if m =~ '^'.a:base
159a5792f58SBram Moolenaar					call add(res, m.';')
160a5792f58SBram Moolenaar				endif
161a5792f58SBram Moolenaar			endfor
162a5792f58SBram Moolenaar			return res
163d12f5c17SBram Moolenaar		else
164d12f5c17SBram Moolenaar			for m in values
165d12f5c17SBram Moolenaar				if m =~? '^'.a:base
166d12f5c17SBram Moolenaar					call add(res, m.';')
167d12f5c17SBram Moolenaar				elseif m =~? a:base
168d12f5c17SBram Moolenaar					call add(res2, m.';')
169d12f5c17SBram Moolenaar				endif
170d12f5c17SBram Moolenaar			endfor
171d12f5c17SBram Moolenaar
172d12f5c17SBram Moolenaar			return res + res2
173d12f5c17SBram Moolenaar		endif
174a5792f58SBram Moolenaar
175a5792f58SBram Moolenaar	endif
176a5792f58SBram Moolenaar	if context =~ '>'
177a5792f58SBram Moolenaar		" Generally if context contains > it means we are outside of tag and
178a5792f58SBram Moolenaar		" should abandon action
179a5792f58SBram Moolenaar		return []
180a5792f58SBram Moolenaar	endif
181a5792f58SBram Moolenaar
182a5792f58SBram Moolenaar    " find tags matching with "a:base"
183a5792f58SBram Moolenaar	" If a:base contains white space it is attribute.
184a5792f58SBram Moolenaar	" It could be also value of attribute...
185a5792f58SBram Moolenaar	" We have to get first word to offer
186a5792f58SBram Moolenaar	" proper completions
187a5792f58SBram Moolenaar	if context == ''
188a5792f58SBram Moolenaar		let tag = ''
189a5792f58SBram Moolenaar	else
190a5792f58SBram Moolenaar		let tag = split(context)[0]
191a5792f58SBram Moolenaar	endif
192a5792f58SBram Moolenaar	" Get rid of namespace
193a5792f58SBram Moolenaar	let tag = substitute(tag, '^'.b:xml_namespace.':', '', '')
194a5792f58SBram Moolenaar
195a5792f58SBram Moolenaar
196a5792f58SBram Moolenaar	" Get last word, it should be attr name
197a5792f58SBram Moolenaar	let attr = matchstr(context, '.*\s\zs.*')
198a5792f58SBram Moolenaar	" Possible situations where any prediction would be difficult:
199a5792f58SBram Moolenaar	" 1. Events attributes
200a5792f58SBram Moolenaar	if context =~ '\s'
201a5792f58SBram Moolenaar
202*6c391a74SBram Moolenaar		" If attr contains =\s*[\"'] we catch value of attribute
2038424a624SBram Moolenaar		if attr =~ "=\s*[\"']" || attr =~ "=\s*$"
204a5792f58SBram Moolenaar			" Let do attribute specific completion
205a5792f58SBram Moolenaar			let attrname = matchstr(attr, '.*\ze\s*=')
2068424a624SBram Moolenaar			let entered_value = matchstr(attr, ".*=\\s*[\"']\\?\\zs.*")
207a5792f58SBram Moolenaar
208a5792f58SBram Moolenaar			if tag =~ '^[?!]'
209a5792f58SBram Moolenaar				" Return nothing if we are inside of ! or ? tag
210a5792f58SBram Moolenaar				return []
211a5792f58SBram Moolenaar			else
2128424a624SBram Moolenaar				if has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}, tag) && has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1], attrname)
213a5792f58SBram Moolenaar					let values = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][attrname]
2148424a624SBram Moolenaar				else
2158424a624SBram Moolenaar					return []
2168424a624SBram Moolenaar				endif
217a5792f58SBram Moolenaar			endif
218a5792f58SBram Moolenaar
219a5792f58SBram Moolenaar			if len(values) == 0
220a5792f58SBram Moolenaar				return []
221a5792f58SBram Moolenaar			endif
222a5792f58SBram Moolenaar
223a5792f58SBram Moolenaar			" We need special version of sbase
224a5792f58SBram Moolenaar			let attrbase = matchstr(context, ".*[\"']")
225a5792f58SBram Moolenaar			let attrquote = matchstr(attrbase, '.$')
2268424a624SBram Moolenaar			if attrquote !~ "['\"]"
2278424a624SBram Moolenaar				let attrquoteopen = '"'
2288424a624SBram Moolenaar				let attrquote = '"'
2298424a624SBram Moolenaar			else
2308424a624SBram Moolenaar				let attrquoteopen = ''
2318424a624SBram Moolenaar			endif
232a5792f58SBram Moolenaar
233a5792f58SBram Moolenaar			for m in values
234a5792f58SBram Moolenaar				" This if is needed to not offer all completions as-is
235a5792f58SBram Moolenaar				" alphabetically but sort them. Those beginning with entered
236a5792f58SBram Moolenaar				" part will be as first choices
237a5792f58SBram Moolenaar				if m =~ '^'.entered_value
2388424a624SBram Moolenaar					call add(res, attrquoteopen . m . attrquote.' ')
239a5792f58SBram Moolenaar				elseif m =~ entered_value
2408424a624SBram Moolenaar					call add(res2, attrquoteopen . m . attrquote.' ')
241a5792f58SBram Moolenaar				endif
242a5792f58SBram Moolenaar			endfor
243a5792f58SBram Moolenaar
244a5792f58SBram Moolenaar			return res + res2
245a5792f58SBram Moolenaar
246a5792f58SBram Moolenaar		endif
247a5792f58SBram Moolenaar
248a5792f58SBram Moolenaar		if tag =~ '?xml'
249a5792f58SBram Moolenaar			" Two possible arguments for <?xml> plus variation
250a5792f58SBram Moolenaar			let attrs = ['encoding', 'version="1.0"', 'version']
251a5792f58SBram Moolenaar		elseif tag =~ '^!'
252a5792f58SBram Moolenaar			" Don't make completion at all
253910f66f9SBram Moolenaar			"
254a5792f58SBram Moolenaar			return []
255a5792f58SBram Moolenaar		else
256910f66f9SBram Moolenaar            if !has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}, tag)
257910f66f9SBram Moolenaar				" Abandon when data file isn't complete
258910f66f9SBram Moolenaar 				return []
259910f66f9SBram Moolenaar 			endif
260a5792f58SBram Moolenaar			let attrs = keys(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1])
261a5792f58SBram Moolenaar		endif
262a5792f58SBram Moolenaar
263a5792f58SBram Moolenaar		for m in sort(attrs)
264a5792f58SBram Moolenaar			if m =~ '^'.attr
265a5792f58SBram Moolenaar				call add(res, m)
266a5792f58SBram Moolenaar			elseif m =~ attr
2671d2ba7faSBram Moolenaar				call add(res2, m)
268a5792f58SBram Moolenaar			endif
269a5792f58SBram Moolenaar		endfor
2701d2ba7faSBram Moolenaar		let menu = res + res2
2711d2ba7faSBram Moolenaar		let final_menu = []
2721d2ba7faSBram Moolenaar		if has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}, 'vimxmlattrinfo')
2731d2ba7faSBram Moolenaar			for i in range(len(menu))
2741d2ba7faSBram Moolenaar				let item = menu[i]
2751d2ba7faSBram Moolenaar				if has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}['vimxmlattrinfo'], item)
2761d2ba7faSBram Moolenaar					let m_menu = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}['vimxmlattrinfo'][item][0]
2771d2ba7faSBram Moolenaar					let m_info = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}['vimxmlattrinfo'][item][1]
2781d2ba7faSBram Moolenaar				else
2791d2ba7faSBram Moolenaar					let m_menu = ''
2801d2ba7faSBram Moolenaar					let m_info = ''
2811d2ba7faSBram Moolenaar				endif
2821d2ba7faSBram Moolenaar				if tag !~ '^[?!]' && len(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][item]) > 0 && g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
2831d2ba7faSBram Moolenaar					let item = item
2841d2ba7faSBram Moolenaar				else
2851d2ba7faSBram Moolenaar					let item .= '="'
2861d2ba7faSBram Moolenaar				endif
2871d2ba7faSBram Moolenaar				let final_menu += [{'word':item, 'menu':m_menu, 'info':m_info}]
2881d2ba7faSBram Moolenaar			endfor
2891d2ba7faSBram Moolenaar		else
2901d2ba7faSBram Moolenaar			for i in range(len(menu))
2911d2ba7faSBram Moolenaar				let item = menu[i]
2921d2ba7faSBram Moolenaar				if tag !~ '^[?!]' && len(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][item]) > 0 && g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
2931d2ba7faSBram Moolenaar					let item = item
2941d2ba7faSBram Moolenaar				else
2951d2ba7faSBram Moolenaar					let item .= '="'
2961d2ba7faSBram Moolenaar				endif
2971d2ba7faSBram Moolenaar				let final_menu += [item]
2981d2ba7faSBram Moolenaar			endfor
2991d2ba7faSBram Moolenaar		endif
3001d2ba7faSBram Moolenaar		return final_menu
301a5792f58SBram Moolenaar
302a5792f58SBram Moolenaar	endif
303a5792f58SBram Moolenaar	" Close tag
304a5792f58SBram Moolenaar	let b:unaryTagsStack = "base meta link hr br param img area input col"
305a5792f58SBram Moolenaar	if context =~ '^\/'
306a5792f58SBram Moolenaar		let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
307a5792f58SBram Moolenaar		return [opentag.">"]
308a5792f58SBram Moolenaar	endif
309a5792f58SBram Moolenaar
310a5792f58SBram Moolenaar	" Complete elements of XML structure
311a5792f58SBram Moolenaar	" TODO: #REQUIRED, #IMPLIED, #FIXED, #PCDATA - but these should be detected like
312a5792f58SBram Moolenaar	" entities - in first run
313a5792f58SBram Moolenaar	" keywords: CDATA, ID, IDREF, IDREFS, ENTITY, ENTITIES, NMTOKEN, NMTOKENS
314a5792f58SBram Moolenaar	" are hardly recognizable but keep it in reserve
315a5792f58SBram Moolenaar	" also: EMPTY ANY SYSTEM PUBLIC DATA
316a5792f58SBram Moolenaar	if context =~ '^!'
317a5792f58SBram Moolenaar		let tags = ['!ELEMENT', '!DOCTYPE', '!ATTLIST', '!ENTITY', '!NOTATION', '![CDATA[', '![INCLUDE[', '![IGNORE[']
318a5792f58SBram Moolenaar
319a5792f58SBram Moolenaar		for m in tags
320a5792f58SBram Moolenaar			if m =~ '^'.context
321a5792f58SBram Moolenaar				let m = substitute(m, '^!\[\?', '', '')
322a5792f58SBram Moolenaar				call add(res, m)
323a5792f58SBram Moolenaar			elseif m =~ context
324a5792f58SBram Moolenaar				let m = substitute(m, '^!\[\?', '', '')
325a5792f58SBram Moolenaar				call add(res2, m)
326a5792f58SBram Moolenaar			endif
327a5792f58SBram Moolenaar		endfor
328a5792f58SBram Moolenaar
329a5792f58SBram Moolenaar		return res + res2
330a5792f58SBram Moolenaar
331a5792f58SBram Moolenaar	endif
332a5792f58SBram Moolenaar
333a5792f58SBram Moolenaar	" Complete text declaration
334a5792f58SBram Moolenaar	if context =~ '^?'
335a5792f58SBram Moolenaar		let tags = ['?xml']
336a5792f58SBram Moolenaar
337a5792f58SBram Moolenaar		for m in tags
338a5792f58SBram Moolenaar			if m =~ '^'.context
339a5792f58SBram Moolenaar				call add(res, substitute(m, '^?', '', ''))
340a5792f58SBram Moolenaar			elseif m =~ context
341a5792f58SBram Moolenaar				call add(res, substitute(m, '^?', '', ''))
342a5792f58SBram Moolenaar			endif
343a5792f58SBram Moolenaar		endfor
344a5792f58SBram Moolenaar
345a5792f58SBram Moolenaar		return res + res2
346a5792f58SBram Moolenaar
347a5792f58SBram Moolenaar	endif
348a5792f58SBram Moolenaar
349a5792f58SBram Moolenaar	" Deal with tag completion.
350a5792f58SBram Moolenaar	let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
351a5792f58SBram Moolenaar	let opentag = substitute(opentag, '^\k*:', '', '')
352d12f5c17SBram Moolenaar	if opentag == ''
3537e8fd636SBram Moolenaar		"return []
3547e8fd636SBram Moolenaar	    let tags = keys(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]})
3557e8fd636SBram Moolenaar		call filter(tags, 'v:val !~ "^vimxml"')
3567e8fd636SBram Moolenaar	else
35718144c84SBram Moolenaar		if !has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}, opentag)
358910f66f9SBram Moolenaar			" Abandon when data file isn't complete
359910f66f9SBram Moolenaar			return []
360910f66f9SBram Moolenaar		endif
3617e8fd636SBram Moolenaar		let tags = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}[opentag][0]
362d12f5c17SBram Moolenaar	endif
363a5792f58SBram Moolenaar
364a5792f58SBram Moolenaar	let context = substitute(context, '^\k*:', '', '')
365a5792f58SBram Moolenaar
366a5792f58SBram Moolenaar	for m in tags
367a5792f58SBram Moolenaar		if m =~ '^'.context
3681d2ba7faSBram Moolenaar			call add(res, m)
369a5792f58SBram Moolenaar		elseif m =~ context
3701d2ba7faSBram Moolenaar			call add(res2, m)
371a5792f58SBram Moolenaar		endif
372a5792f58SBram Moolenaar	endfor
3731d2ba7faSBram Moolenaar	let menu = res + res2
37418144c84SBram Moolenaar	if b:xml_namespace == 'DEFAULT'
37518144c84SBram Moolenaar		let xml_namespace = ''
37618144c84SBram Moolenaar	else
37718144c84SBram Moolenaar		let xml_namespace = b:xml_namespace.':'
37818144c84SBram Moolenaar	endif
3791d2ba7faSBram Moolenaar	if has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}, 'vimxmltaginfo')
3801d2ba7faSBram Moolenaar		let final_menu = []
3811d2ba7faSBram Moolenaar		for i in range(len(menu))
3821d2ba7faSBram Moolenaar			let item = menu[i]
3831d2ba7faSBram Moolenaar			if has_key(g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}['vimxmltaginfo'], item)
3841d2ba7faSBram Moolenaar				let m_menu = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}['vimxmltaginfo'][item][0]
3851d2ba7faSBram Moolenaar				let m_info = g:xmldata{'_'.g:xmldata_connection[b:xml_namespace]}['vimxmltaginfo'][item][1]
3861d2ba7faSBram Moolenaar			else
3871d2ba7faSBram Moolenaar				let m_menu = ''
3881d2ba7faSBram Moolenaar				let m_info = ''
3891d2ba7faSBram Moolenaar			endif
3901d2ba7faSBram Moolenaar			let final_menu += [{'word':xml_namespace.item, 'menu':m_menu, 'info':m_info}]
3911d2ba7faSBram Moolenaar		endfor
3921d2ba7faSBram Moolenaar	else
39318144c84SBram Moolenaar		let final_menu = map(menu, 'xml_namespace.v:val')
3941d2ba7faSBram Moolenaar	endif
39518144c84SBram Moolenaar
3961d2ba7faSBram Moolenaar	return final_menu
397a5792f58SBram Moolenaar
398a5792f58SBram Moolenaar  endif
399a5792f58SBram Moolenaarendfunction
400a5792f58SBram Moolenaar
40118144c84SBram Moolenaar" MM: This is severely reduced closetag.vim used with kind permission of Steven
402a5792f58SBram Moolenaar"     Mueller
403a5792f58SBram Moolenaar"     Changes: strip all comments; delete error messages; add checking for
404a5792f58SBram Moolenaar"     namespace
405a5792f58SBram Moolenaar" Author: Steven Mueller <[email protected]>
406a5792f58SBram Moolenaar" Last Modified: Tue May 24 13:29:48 PDT 2005
407a5792f58SBram Moolenaar" Version: 0.9.1
408a5792f58SBram Moolenaar
409a5792f58SBram Moolenaarfunction! xmlcomplete#GetLastOpenTag(unaryTagsStack)
410a5792f58SBram Moolenaar	let linenum=line('.')
411a5792f58SBram Moolenaar	let lineend=col('.') - 1 " start: cursor position
412a5792f58SBram Moolenaar	let first=1              " flag for first line searched
413a5792f58SBram Moolenaar	let b:TagStack=''        " main stack of tags
414a5792f58SBram Moolenaar	let startInComment=s:InComment()
415a5792f58SBram Moolenaar
416a5792f58SBram Moolenaar	if exists("b:xml_namespace")
417a5792f58SBram Moolenaar		if b:xml_namespace == 'DEFAULT'
4183577c6faSBram Moolenaar			let tagpat='</\=\(\k\|[.:-]\)\+\|/>'
419a5792f58SBram Moolenaar		else
420a5792f58SBram Moolenaar			let tagpat='</\='.b:xml_namespace.':\(\k\|[.-]\)\+\|/>'
421a5792f58SBram Moolenaar		endif
422a5792f58SBram Moolenaar	else
4233577c6faSBram Moolenaar		let tagpat='</\=\(\k\|[.:-]\)\+\|/>'
424a5792f58SBram Moolenaar	endif
425a5792f58SBram Moolenaar	while (linenum>0)
426a5792f58SBram Moolenaar		let line=getline(linenum)
427a5792f58SBram Moolenaar		if first
428a5792f58SBram Moolenaar			let line=strpart(line,0,lineend)
429a5792f58SBram Moolenaar		else
430a5792f58SBram Moolenaar			let lineend=strlen(line)
431a5792f58SBram Moolenaar		endif
432a5792f58SBram Moolenaar		let b:lineTagStack=''
433a5792f58SBram Moolenaar		let mpos=0
434a5792f58SBram Moolenaar		let b:TagCol=0
435a5792f58SBram Moolenaar		while (mpos > -1)
436a5792f58SBram Moolenaar			let mpos=matchend(line,tagpat)
437a5792f58SBram Moolenaar			if mpos > -1
438a5792f58SBram Moolenaar				let b:TagCol=b:TagCol+mpos
439a5792f58SBram Moolenaar				let tag=matchstr(line,tagpat)
440a5792f58SBram Moolenaar
441a5792f58SBram Moolenaar				if exists('b:closetag_disable_synID') || startInComment==s:InCommentAt(linenum, b:TagCol)
442a5792f58SBram Moolenaar					let b:TagLine=linenum
443a5792f58SBram Moolenaar					call s:Push(matchstr(tag,'[^<>]\+'),'b:lineTagStack')
444a5792f58SBram Moolenaar				endif
445a5792f58SBram Moolenaar				let lineend=lineend-mpos
446a5792f58SBram Moolenaar				let line=strpart(line,mpos,lineend)
447a5792f58SBram Moolenaar			endif
448a5792f58SBram Moolenaar		endwhile
449a5792f58SBram Moolenaar		while (!s:EmptystackP('b:lineTagStack'))
450a5792f58SBram Moolenaar			let tag=s:Pop('b:lineTagStack')
451a5792f58SBram Moolenaar			if match(tag, '^/') == 0		"found end tag
452a5792f58SBram Moolenaar				call s:Push(tag,'b:TagStack')
453a5792f58SBram Moolenaar			elseif s:EmptystackP('b:TagStack') && !s:Instack(tag, a:unaryTagsStack)	"found unclosed tag
454a5792f58SBram Moolenaar				return tag
455a5792f58SBram Moolenaar			else
456a5792f58SBram Moolenaar				let endtag=s:Peekstack('b:TagStack')
457a5792f58SBram Moolenaar				if endtag == '/'.tag || endtag == '/'
458a5792f58SBram Moolenaar					call s:Pop('b:TagStack')	"found a open/close tag pair
459a5792f58SBram Moolenaar				elseif !s:Instack(tag, a:unaryTagsStack) "we have a mismatch error
460a5792f58SBram Moolenaar					return ''
461a5792f58SBram Moolenaar				endif
462a5792f58SBram Moolenaar			endif
463a5792f58SBram Moolenaar		endwhile
464a5792f58SBram Moolenaar		let linenum=linenum-1 | let first=0
465a5792f58SBram Moolenaar	endwhile
466a5792f58SBram Moolenaarreturn ''
467a5792f58SBram Moolenaarendfunction
468a5792f58SBram Moolenaar
469a5792f58SBram Moolenaarfunction! s:InComment()
4708b6144bdSBram Moolenaar	return synIDattr(synID(line('.'), col('.'), 0), 'name') =~ 'Comment\|String'
471a5792f58SBram Moolenaarendfunction
472a5792f58SBram Moolenaar
473a5792f58SBram Moolenaarfunction! s:InCommentAt(line, col)
4748b6144bdSBram Moolenaar	return synIDattr(synID(a:line, a:col, 0), 'name') =~ 'Comment\|String'
475a5792f58SBram Moolenaarendfunction
476a5792f58SBram Moolenaar
477a5792f58SBram Moolenaarfunction! s:SetKeywords()
47814b6945eSBram Moolenaar	let s:IsKeywordBak=&l:iskeyword
47914b6945eSBram Moolenaar	let &l:iskeyword='33-255'
480a5792f58SBram Moolenaarendfunction
481a5792f58SBram Moolenaar
482a5792f58SBram Moolenaarfunction! s:RestoreKeywords()
48314b6945eSBram Moolenaar	let &l:iskeyword=s:IsKeywordBak
484a5792f58SBram Moolenaarendfunction
485a5792f58SBram Moolenaar
486a5792f58SBram Moolenaarfunction! s:Push(el, sname)
487a5792f58SBram Moolenaar	if !s:EmptystackP(a:sname)
488a5792f58SBram Moolenaar		exe 'let '.a:sname."=a:el.' '.".a:sname
489a5792f58SBram Moolenaar	else
490a5792f58SBram Moolenaar		exe 'let '.a:sname.'=a:el'
491a5792f58SBram Moolenaar	endif
492a5792f58SBram Moolenaarendfunction
493a5792f58SBram Moolenaar
494a5792f58SBram Moolenaarfunction! s:EmptystackP(sname)
495a5792f58SBram Moolenaar	exe 'let stack='.a:sname
496a5792f58SBram Moolenaar	if match(stack,'^ *$') == 0
497a5792f58SBram Moolenaar		return 1
498a5792f58SBram Moolenaar	else
499a5792f58SBram Moolenaar		return 0
500a5792f58SBram Moolenaar	endif
501a5792f58SBram Moolenaarendfunction
502a5792f58SBram Moolenaar
503a5792f58SBram Moolenaarfunction! s:Instack(el, sname)
504a5792f58SBram Moolenaar	exe 'let stack='.a:sname
505a5792f58SBram Moolenaar	call s:SetKeywords()
506a5792f58SBram Moolenaar	let m=match(stack, '\<'.a:el.'\>')
507a5792f58SBram Moolenaar	call s:RestoreKeywords()
508a5792f58SBram Moolenaar	if m < 0
509a5792f58SBram Moolenaar		return 0
510a5792f58SBram Moolenaar	else
511a5792f58SBram Moolenaar		return 1
512a5792f58SBram Moolenaar	endif
513a5792f58SBram Moolenaarendfunction
514a5792f58SBram Moolenaar
515a5792f58SBram Moolenaarfunction! s:Peekstack(sname)
516a5792f58SBram Moolenaar	call s:SetKeywords()
517a5792f58SBram Moolenaar	exe 'let stack='.a:sname
518a5792f58SBram Moolenaar	let top=matchstr(stack, '\<.\{-1,}\>')
519a5792f58SBram Moolenaar	call s:RestoreKeywords()
520a5792f58SBram Moolenaar	return top
521a5792f58SBram Moolenaarendfunction
522a5792f58SBram Moolenaar
523a5792f58SBram Moolenaarfunction! s:Pop(sname)
524a5792f58SBram Moolenaar	if s:EmptystackP(a:sname)
525a5792f58SBram Moolenaar		return ''
526a5792f58SBram Moolenaar	endif
527a5792f58SBram Moolenaar	exe 'let stack='.a:sname
528a5792f58SBram Moolenaar	call s:SetKeywords()
529a5792f58SBram Moolenaar	let loc=matchend(stack,'\<.\{-1,}\>')
530a5792f58SBram Moolenaar	exe 'let '.a:sname.'=strpart(stack, loc+1, strlen(stack))'
531a5792f58SBram Moolenaar	let top=strpart(stack, match(stack, '\<'), loc)
532a5792f58SBram Moolenaar	call s:RestoreKeywords()
533a5792f58SBram Moolenaar	return top
534a5792f58SBram Moolenaarendfunction
535a5792f58SBram Moolenaar
536a5792f58SBram Moolenaarfunction! s:Clearstack(sname)
537a5792f58SBram Moolenaar	exe 'let '.a:sname."=''"
538a5792f58SBram Moolenaarendfunction
53918144c84SBram Moolenaar" vim:set foldmethod=marker:
540