252 |
ira |
1 |
" Vim script file vim600:fdm=marker:
|
|
|
2 |
" FileType: XML
|
|
|
3 |
" Author: Devin Weaver <vim (at) tritarget.com>
|
|
|
4 |
" Maintainer: Devin Weaver <vim (at) tritarget.com>
|
|
|
5 |
" Last Change: $Date: 2005/05/14 22:13:22 $
|
|
|
6 |
" Version: $Revision: 1.29 $
|
|
|
7 |
" Location: http://www.vim.org/scripts/script.php?script_id=301
|
|
|
8 |
" Licence: This program is free software; you can redistribute it
|
|
|
9 |
" and/or modify it under the terms of the GNU General Public
|
|
|
10 |
" License. See http://www.gnu.org/copyleft/gpl.txt
|
|
|
11 |
" Credits: Brad Phelan <bphelan (at) mathworks.co.uk> for completing
|
|
|
12 |
" tag matching and visual tag completion.
|
|
|
13 |
" Ma, Xiangjiang <Xiangjiang.Ma (at) broadvision.com> for
|
|
|
14 |
" pointing out VIM 6.0 map <buffer> feature.
|
|
|
15 |
" Luc Hermitte <hermitte (at) free.fr> for testing the self
|
|
|
16 |
" install documentation code and providing good bug fixes.
|
|
|
17 |
" Guo-Peng Wen for the self install documentation code.
|
|
|
18 |
|
|
|
19 |
" This script provides some convenience when editing XML (and some SGML)
|
|
|
20 |
" formated documents.
|
|
|
21 |
|
|
|
22 |
" Section: Documentation
|
|
|
23 |
" ----------------------
|
|
|
24 |
"
|
|
|
25 |
" Documentation should be available by ":help xml-plugin" command, once the
|
|
|
26 |
" script has been copied in you .vim/plugin directory.
|
|
|
27 |
"
|
|
|
28 |
" You still can read the documentation at the end of this file. Locate it by
|
|
|
29 |
" searching the "xml-plugin" string (and set ft=help to have
|
|
|
30 |
" appropriate syntaxic coloration).
|
|
|
31 |
|
|
|
32 |
" Note: If you used the 5.x version of this file (xmledit.vim) you'll need to
|
|
|
33 |
" comment out the section where you called it since it is no longer used in
|
|
|
34 |
" version 6.x.
|
|
|
35 |
|
|
|
36 |
" TODO: Revamp ParseTag to pull appart a tag a rebuild it properly.
|
|
|
37 |
" a tag like: < test nowrap testatt=foo >
|
|
|
38 |
" should be fixed to: <test nowrap="nowrap" testatt="foo"></test>
|
|
|
39 |
|
|
|
40 |
"==============================================================================
|
|
|
41 |
|
|
|
42 |
" Only do this when not done yet for this buffer
|
|
|
43 |
if exists("b:did_ftplugin")
|
|
|
44 |
finish
|
|
|
45 |
endif
|
|
|
46 |
let b:did_ftplugin = 1
|
|
|
47 |
|
|
|
48 |
" WrapTag -> Places an XML tag around a visual selection. {{{1
|
|
|
49 |
" Brad Phelan: Wrap the argument in an XML tag
|
|
|
50 |
" Added nice GUI support to the dialogs.
|
|
|
51 |
" Rewrote function to implement new algorythem that addresses several bugs.
|
|
|
52 |
if !exists("*s:WrapTag")
|
|
|
53 |
function s:WrapTag(text)
|
|
|
54 |
if (line(".") < line("'<"))
|
|
|
55 |
let insert_cmd = "o"
|
|
|
56 |
elseif (col(".") < col("'<"))
|
|
|
57 |
let insert_cmd = "a"
|
|
|
58 |
else
|
|
|
59 |
let insert_cmd = "i"
|
|
|
60 |
endif
|
|
|
61 |
if strlen(a:text) > 10
|
|
|
62 |
let input_text = strpart(a:text, 0, 10) . '...'
|
|
|
63 |
else
|
|
|
64 |
let input_text = a:text
|
|
|
65 |
endif
|
|
|
66 |
let wraptag = inputdialog('Tag to wrap "' . input_text . '" : ')
|
|
|
67 |
if strlen(wraptag)==0
|
|
|
68 |
if strlen(b:last_wrap_tag_used)==0
|
|
|
69 |
undo
|
|
|
70 |
return
|
|
|
71 |
endif
|
|
|
72 |
let wraptag = b:last_wrap_tag_used
|
|
|
73 |
let atts = b:last_wrap_atts_used
|
|
|
74 |
else
|
|
|
75 |
let atts = inputdialog('Attributes in <' . wraptag . '> : ')
|
|
|
76 |
endif
|
|
|
77 |
if (visualmode() ==# 'V')
|
|
|
78 |
let text = strpart(a:text,0,strlen(a:text)-1)
|
|
|
79 |
if (insert_cmd ==# "o")
|
|
|
80 |
let eol_cmd = ""
|
|
|
81 |
else
|
|
|
82 |
let eol_cmd = "\<Cr>"
|
|
|
83 |
endif
|
|
|
84 |
else
|
|
|
85 |
let text = a:text
|
|
|
86 |
let eol_cmd = ""
|
|
|
87 |
endif
|
|
|
88 |
if strlen(atts)==0
|
|
|
89 |
let text = "<".wraptag.">".text."</".wraptag.">"
|
|
|
90 |
let b:last_wrap_tag_used = wraptag
|
|
|
91 |
let b:last_wrap_atts_used = ""
|
|
|
92 |
else
|
|
|
93 |
let text = "<".wraptag." ".atts.">".text."</".wraptag.">"
|
|
|
94 |
let b:last_wrap_tag_used = wraptag
|
|
|
95 |
let b:last_wrap_atts_used = atts
|
|
|
96 |
endif
|
|
|
97 |
execute "normal! ".insert_cmd.text.eol_cmd
|
|
|
98 |
endfunction
|
|
|
99 |
endif
|
|
|
100 |
|
|
|
101 |
" NewFileXML -> Inserts <?xml?> at top of new file. {{{1
|
|
|
102 |
if !exists("*s:NewFileXML")
|
|
|
103 |
function s:NewFileXML( )
|
|
|
104 |
" Where is g:did_xhtmlcf_inits defined?
|
|
|
105 |
if &filetype == 'xml' || (!exists ("g:did_xhtmlcf_inits") && exists ("g:xml_use_xhtml") && (&filetype == 'html' || &filetype == 'xhtml'))
|
|
|
106 |
if append (0, '<?xml version="1.0"?>')
|
|
|
107 |
normal! G
|
|
|
108 |
endif
|
|
|
109 |
endif
|
|
|
110 |
endfunction
|
|
|
111 |
endif
|
|
|
112 |
|
|
|
113 |
|
|
|
114 |
" Callback -> Checks for tag callbacks and executes them. {{{1
|
|
|
115 |
if !exists("*s:Callback")
|
|
|
116 |
function s:Callback( xml_tag, isHtml )
|
|
|
117 |
let text = 0
|
|
|
118 |
if a:isHtml == 1 && exists ("*HtmlAttribCallback")
|
|
|
119 |
let text = HtmlAttribCallback (a:xml_tag)
|
|
|
120 |
elseif exists ("*XmlAttribCallback")
|
|
|
121 |
let text = XmlAttribCallback (a:xml_tag)
|
|
|
122 |
endif
|
|
|
123 |
if text != '0'
|
|
|
124 |
execute "normal! i " . text ."\<Esc>l"
|
|
|
125 |
endif
|
|
|
126 |
endfunction
|
|
|
127 |
endif
|
|
|
128 |
|
|
|
129 |
|
|
|
130 |
" IsParsableTag -> Check to see if the tag is a real tag. {{{1
|
|
|
131 |
if !exists("*s:IsParsableTag")
|
|
|
132 |
function s:IsParsableTag( tag )
|
|
|
133 |
" The "Should I parse?" flag.
|
|
|
134 |
let parse = 1
|
|
|
135 |
|
|
|
136 |
" make sure a:tag has a proper tag in it and is not a instruction or end tag.
|
|
|
137 |
if a:tag !~ '^<[[:alnum:]_:\-].*>$'
|
|
|
138 |
let parse = 0
|
|
|
139 |
endif
|
|
|
140 |
|
|
|
141 |
" make sure this tag isn't already closed.
|
|
|
142 |
if strpart (a:tag, strlen (a:tag) - 2, 1) == '/'
|
|
|
143 |
let parse = 0
|
|
|
144 |
endif
|
|
|
145 |
|
|
|
146 |
return parse
|
|
|
147 |
endfunction
|
|
|
148 |
endif
|
|
|
149 |
|
|
|
150 |
|
|
|
151 |
" ParseTag -> The major work hourse for tag completion. {{{1
|
|
|
152 |
if !exists("*s:ParseTag")
|
|
|
153 |
function s:ParseTag( )
|
|
|
154 |
" Save registers
|
|
|
155 |
let old_reg_save = @"
|
|
|
156 |
let old_save_x = @x
|
|
|
157 |
|
|
|
158 |
if (!exists("g:xml_no_auto_nesting") && strpart (getline ("."), col (".") - 2, 2) == '>>')
|
|
|
159 |
let multi_line = 1
|
|
|
160 |
execute "normal! \"xX"
|
|
|
161 |
else
|
|
|
162 |
let multi_line = 0
|
|
|
163 |
endif
|
|
|
164 |
|
|
|
165 |
let @" = ""
|
|
|
166 |
execute "normal! \"xy%%"
|
|
|
167 |
let ltag = @"
|
|
|
168 |
if (&filetype == 'html' || &filetype == 'xhtml') && (!exists ("g:xml_no_html"))
|
|
|
169 |
let html_mode = 1
|
|
|
170 |
let ltag = substitute (ltag, '[^[:graph:]]\+', ' ', 'g')
|
|
|
171 |
let ltag = substitute (ltag, '<\s*\([^[:alnum:]_:\-[:blank:]]\=\)\s*\([[:alnum:]_:\-]\+\)\>', '<\1\2', '')
|
|
|
172 |
else
|
|
|
173 |
let html_mode = 0
|
|
|
174 |
endif
|
|
|
175 |
|
|
|
176 |
if <SID>IsParsableTag (ltag)
|
|
|
177 |
" find the break between tag name and atributes (or closing of tag)
|
|
|
178 |
" Too bad I can't just grab the position index of a pattern in a string.
|
|
|
179 |
let index = 1
|
|
|
180 |
while index < strlen (ltag) && strpart (ltag, index, 1) =~ '[[:alnum:]_:\-]'
|
|
|
181 |
let index = index + 1
|
|
|
182 |
endwhile
|
|
|
183 |
|
|
|
184 |
let tag_name = strpart (ltag, 1, index - 1)
|
|
|
185 |
if strpart (ltag, index) =~ '[^/>[:blank:]]'
|
|
|
186 |
let has_attrib = 1
|
|
|
187 |
else
|
|
|
188 |
let has_attrib = 0
|
|
|
189 |
endif
|
|
|
190 |
|
|
|
191 |
" That's (index - 1) + 2, 2 for the '</' and 1 for the extra character the
|
|
|
192 |
" while includes (the '>' is ignored because <Esc> puts the curser on top
|
|
|
193 |
" of the '>'
|
|
|
194 |
let index = index + 2
|
|
|
195 |
|
|
|
196 |
" print out the end tag and place the cursor back were it left off
|
|
|
197 |
if html_mode && tag_name =~? '^\(img\|input\|param\|frame\|br\|hr\|meta\|link\|base\|area\)$'
|
|
|
198 |
if has_attrib == 0
|
|
|
199 |
call <SID>Callback (tag_name, html_mode)
|
|
|
200 |
endif
|
|
|
201 |
if exists ("g:xml_use_xhtml")
|
|
|
202 |
execute "normal! i /\<Esc>l"
|
|
|
203 |
endif
|
|
|
204 |
else
|
|
|
205 |
if multi_line
|
|
|
206 |
" Can't use \<Tab> because that indents 'tabstop' not 'shiftwidth'
|
|
|
207 |
" Also >> doesn't shift on an empty line hence the temporary char 'x'
|
|
|
208 |
let com_save = &comments
|
|
|
209 |
set comments-=n:>
|
|
|
210 |
execute "normal! a\<Cr>\<Cr>\<Esc>kAx\<Esc>>>$\"xx"
|
|
|
211 |
execute "set comments=" . com_save
|
|
|
212 |
|
|
|
213 |
" restore registers
|
|
|
214 |
let @" = old_reg_save
|
|
|
215 |
let @x = old_save_x
|
|
|
216 |
|
|
|
217 |
startinsert!
|
|
|
218 |
return ""
|
|
|
219 |
else
|
|
|
220 |
if has_attrib == 0
|
|
|
221 |
call <SID>Callback (tag_name, html_mode)
|
|
|
222 |
endif
|
|
|
223 |
execute "normal! a</" . tag_name . ">\<Esc>" . index . "h"
|
|
|
224 |
endif
|
|
|
225 |
endif
|
|
|
226 |
endif
|
|
|
227 |
|
|
|
228 |
" restore registers
|
|
|
229 |
let @" = old_reg_save
|
|
|
230 |
let @x = old_save_x
|
|
|
231 |
|
|
|
232 |
if col (".") < strlen (getline ("."))
|
|
|
233 |
execute "normal! l"
|
|
|
234 |
startinsert
|
|
|
235 |
else
|
|
|
236 |
startinsert!
|
|
|
237 |
endif
|
|
|
238 |
endfunction
|
|
|
239 |
endif
|
|
|
240 |
|
|
|
241 |
|
|
|
242 |
" ParseTag2 -> Experimental function to replace ParseTag {{{1
|
|
|
243 |
"if !exists("*s:ParseTag2")
|
|
|
244 |
"function s:ParseTag2( )
|
|
|
245 |
" My thought is to pull the tag out and reformat it to a normalized tag
|
|
|
246 |
" and put it back.
|
|
|
247 |
"endfunction
|
|
|
248 |
"endif
|
|
|
249 |
|
|
|
250 |
|
|
|
251 |
" BuildTagName -> Grabs the tag's name for tag matching. {{{1
|
|
|
252 |
if !exists("*s:BuildTagName")
|
|
|
253 |
function s:BuildTagName( )
|
|
|
254 |
"First check to see if we Are allready on the end of the tag. The / search
|
|
|
255 |
"forwards command will jump to the next tag otherwise
|
|
|
256 |
|
|
|
257 |
" Store contents of register x in a variable
|
|
|
258 |
let b:xreg = @x
|
|
|
259 |
|
|
|
260 |
exec "normal! v\"xy"
|
|
|
261 |
if @x=='>'
|
|
|
262 |
" Don't do anything
|
|
|
263 |
else
|
|
|
264 |
exec "normal! />/\<Cr>"
|
|
|
265 |
endif
|
|
|
266 |
|
|
|
267 |
" Now we head back to the < to reach the beginning.
|
|
|
268 |
exec "normal! ?<?\<Cr>"
|
|
|
269 |
|
|
|
270 |
" Capture the tag (a > will be catured by the /$/ match)
|
|
|
271 |
exec "normal! v/\\s\\|$/\<Cr>\"xy"
|
|
|
272 |
|
|
|
273 |
" We need to strip off any junk at the end.
|
|
|
274 |
let @x=strpart(@x, 0, match(@x, "[[:blank:]>\<C-J>]"))
|
|
|
275 |
|
|
|
276 |
"remove <, >
|
|
|
277 |
let @x=substitute(@x,'^<\|>$','','')
|
|
|
278 |
|
|
|
279 |
" remove spaces.
|
|
|
280 |
let @x=substitute(@x,'/\s*','/', '')
|
|
|
281 |
let @x=substitute(@x,'^\s*','', '')
|
|
|
282 |
|
|
|
283 |
" Swap @x and b:xreg
|
|
|
284 |
let temp = @x
|
|
|
285 |
let @x = b:xreg
|
|
|
286 |
let b:xreg = temp
|
|
|
287 |
endfunction
|
|
|
288 |
endif
|
|
|
289 |
|
|
|
290 |
" TagMatch1 -> First step in tag matching. {{{1
|
|
|
291 |
" Brad Phelan: First step in tag matching.
|
|
|
292 |
if !exists("*s:TagMatch1")
|
|
|
293 |
function s:TagMatch1()
|
|
|
294 |
" Save registers
|
|
|
295 |
let old_reg_save = @"
|
|
|
296 |
|
|
|
297 |
"Drop a marker here just in case we have a mismatched tag and
|
|
|
298 |
"wish to return (:mark looses column position)
|
|
|
299 |
normal! mz
|
|
|
300 |
|
|
|
301 |
call <SID>BuildTagName()
|
|
|
302 |
|
|
|
303 |
"Check to see if it is an end tag. If it is place a 1 in endtag
|
|
|
304 |
if match(b:xreg, '^/')==-1
|
|
|
305 |
let endtag = 0
|
|
|
306 |
else
|
|
|
307 |
let endtag = 1
|
|
|
308 |
endif
|
|
|
309 |
|
|
|
310 |
" Extract the tag from the whole tag block
|
|
|
311 |
" eg if the block =
|
|
|
312 |
" tag attrib1=blah attrib2=blah
|
|
|
313 |
" we will end up with
|
|
|
314 |
" tag
|
|
|
315 |
" with no trailing or leading spaces
|
|
|
316 |
let b:xreg=substitute(b:xreg,'^/','','g')
|
|
|
317 |
|
|
|
318 |
" Make sure the tag is valid.
|
|
|
319 |
" Malformed tags could be <?xml ?>, <![CDATA[]]>, etc.
|
|
|
320 |
if match(b:xreg,'^[[:alnum:]_:\-]') != -1
|
|
|
321 |
" Pass the tag to the matching
|
|
|
322 |
" routine
|
|
|
323 |
call <SID>TagMatch2(b:xreg, endtag)
|
|
|
324 |
endif
|
|
|
325 |
" Restore registers
|
|
|
326 |
let @" = old_reg_save
|
|
|
327 |
endfunction
|
|
|
328 |
endif
|
|
|
329 |
|
|
|
330 |
|
|
|
331 |
" TagMatch2 -> Second step in tag matching. {{{1
|
|
|
332 |
" Brad Phelan: Second step in tag matching.
|
|
|
333 |
if !exists("*s:TagMatch2")
|
|
|
334 |
function s:TagMatch2(tag,endtag)
|
|
|
335 |
let match_type=''
|
|
|
336 |
|
|
|
337 |
" Build the pattern for searching for XML tags based
|
|
|
338 |
" on the 'tag' type passed into the function.
|
|
|
339 |
" Note we search forwards for end tags and
|
|
|
340 |
" backwards for start tags
|
|
|
341 |
if a:endtag==0
|
|
|
342 |
"let nextMatch='normal /\(<\s*' . a:tag . '\(\s\+.\{-}\)*>\)\|\(<\/' . a:tag . '\s*>\)'
|
|
|
343 |
let match_type = '/'
|
|
|
344 |
else
|
|
|
345 |
"let nextMatch='normal ?\(<\s*' . a:tag . '\(\s\+.\{-}\)*>\)\|\(<\/' . a:tag . '\s*>\)'
|
|
|
346 |
let match_type = '?'
|
|
|
347 |
endif
|
|
|
348 |
|
|
|
349 |
if a:endtag==0
|
|
|
350 |
let stk = 1
|
|
|
351 |
else
|
|
|
352 |
let stk = 1
|
|
|
353 |
end
|
|
|
354 |
|
|
|
355 |
" wrapscan must be turned on. We'll recored the value and reset it afterward.
|
|
|
356 |
" We have it on because if we don't we'll get a nasty error if the search hits
|
|
|
357 |
" BOF or EOF.
|
|
|
358 |
let wrapval = &wrapscan
|
|
|
359 |
let &wrapscan = 1
|
|
|
360 |
|
|
|
361 |
"Get the current location of the cursor so we can
|
|
|
362 |
"detect if we wrap on ourselves
|
|
|
363 |
let lpos = line(".")
|
|
|
364 |
let cpos = col(".")
|
|
|
365 |
|
|
|
366 |
if a:endtag==0
|
|
|
367 |
" If we are trying to find a start tag
|
|
|
368 |
" then decrement when we find a start tag
|
|
|
369 |
let iter = 1
|
|
|
370 |
else
|
|
|
371 |
" If we are trying to find an end tag
|
|
|
372 |
" then increment when we find a start tag
|
|
|
373 |
let iter = -1
|
|
|
374 |
endif
|
|
|
375 |
|
|
|
376 |
"Loop until stk == 0.
|
|
|
377 |
while 1
|
|
|
378 |
" exec search.
|
|
|
379 |
" Make sure to avoid />$/ as well as /\s$/ and /$/.
|
|
|
380 |
exec "normal! " . match_type . '<\s*\/*\s*' . a:tag . '\([[:blank:]>]\|$\)' . "\<Cr>"
|
|
|
381 |
|
|
|
382 |
" Check to see if our match makes sence.
|
|
|
383 |
if a:endtag == 0
|
|
|
384 |
if line(".") < lpos
|
|
|
385 |
call <SID>MisMatchedTag (0, a:tag)
|
|
|
386 |
break
|
|
|
387 |
elseif line(".") == lpos && col(".") <= cpos
|
|
|
388 |
call <SID>MisMatchedTag (1, a:tag)
|
|
|
389 |
break
|
|
|
390 |
endif
|
|
|
391 |
else
|
|
|
392 |
if line(".") > lpos
|
|
|
393 |
call <SID>MisMatchedTag (2, '/'.a:tag)
|
|
|
394 |
break
|
|
|
395 |
elseif line(".") == lpos && col(".") >= cpos
|
|
|
396 |
call <SID>MisMatchedTag (3, '/'.a:tag)
|
|
|
397 |
break
|
|
|
398 |
endif
|
|
|
399 |
endif
|
|
|
400 |
|
|
|
401 |
call <SID>BuildTagName()
|
|
|
402 |
|
|
|
403 |
if match(b:xreg,'^/')==-1
|
|
|
404 |
" Found start tag
|
|
|
405 |
let stk = stk + iter
|
|
|
406 |
else
|
|
|
407 |
" Found end tag
|
|
|
408 |
let stk = stk - iter
|
|
|
409 |
endif
|
|
|
410 |
|
|
|
411 |
if stk == 0
|
|
|
412 |
break
|
|
|
413 |
endif
|
|
|
414 |
endwhile
|
|
|
415 |
|
|
|
416 |
let &wrapscan = wrapval
|
|
|
417 |
endfunction
|
|
|
418 |
endif
|
|
|
419 |
|
|
|
420 |
" MisMatchedTag -> What to do if a tag is mismatched. {{{1
|
|
|
421 |
if !exists("*s:MisMatchedTag")
|
|
|
422 |
function s:MisMatchedTag( id, tag )
|
|
|
423 |
"Jump back to our formor spot
|
|
|
424 |
normal! `z
|
|
|
425 |
normal zz
|
|
|
426 |
echohl WarningMsg
|
|
|
427 |
" For debugging
|
|
|
428 |
"echo "Mismatched tag " . a:id . ": <" . a:tag . ">"
|
|
|
429 |
" For release
|
|
|
430 |
echo "Mismatched tag <" . a:tag . ">"
|
|
|
431 |
echohl None
|
|
|
432 |
endfunction
|
|
|
433 |
endif
|
|
|
434 |
|
|
|
435 |
" DeleteTag -> Deletes surrounding tags from cursor. {{{1
|
|
|
436 |
" Modifies mark z
|
|
|
437 |
if !exists("*s:DeleteTag")
|
|
|
438 |
function s:DeleteTag( )
|
|
|
439 |
if strpart (getline ("."), col (".") - 1, 1) == "<"
|
|
|
440 |
normal! l
|
|
|
441 |
endif
|
|
|
442 |
if search ("<[^\/]", "bW") == 0
|
|
|
443 |
return
|
|
|
444 |
endif
|
|
|
445 |
normal! mz
|
|
|
446 |
normal \5
|
|
|
447 |
normal! d%`zd%
|
|
|
448 |
endfunction
|
|
|
449 |
endif
|
|
|
450 |
|
|
|
451 |
" VisualTag -> Selects Tag body in a visual selection. {{{1
|
|
|
452 |
" Modifies mark z
|
|
|
453 |
if !exists("*s:VisualTag")
|
|
|
454 |
function s:VisualTag( )
|
|
|
455 |
if strpart (getline ("."), col (".") - 1, 1) == "<"
|
|
|
456 |
normal! l
|
|
|
457 |
endif
|
|
|
458 |
if search ("<[^\/]", "bW") == 0
|
|
|
459 |
return
|
|
|
460 |
endif
|
|
|
461 |
normal! mz
|
|
|
462 |
normal \5
|
|
|
463 |
normal! %
|
|
|
464 |
exe "normal! " . visualmode()
|
|
|
465 |
normal! `z
|
|
|
466 |
endfunction
|
|
|
467 |
endif
|
|
|
468 |
|
|
|
469 |
" Section: Doc installation {{{1
|
|
|
470 |
" Function: s:XmlInstallDocumentation(full_name, revision) {{{2
|
|
|
471 |
" Install help documentation.
|
|
|
472 |
" Arguments:
|
|
|
473 |
" full_name: Full name of this vim plugin script, including path name.
|
|
|
474 |
" revision: Revision of the vim script. #version# mark in the document file
|
|
|
475 |
" will be replaced with this string with 'v' prefix.
|
|
|
476 |
" Return:
|
|
|
477 |
" 1 if new document installed, 0 otherwise.
|
|
|
478 |
" Note: Cleaned and generalized by guo-peng Wen
|
|
|
479 |
"'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
|
|
480 |
|
|
|
481 |
function! s:XmlInstallDocumentation(full_name, revision)
|
|
|
482 |
" Name of the document path based on the system we use:
|
|
|
483 |
if (has("unix"))
|
|
|
484 |
" On UNIX like system, using forward slash:
|
|
|
485 |
let l:slash_char = '/'
|
|
|
486 |
let l:mkdir_cmd = ':silent !mkdir -p '
|
|
|
487 |
else
|
|
|
488 |
" On M$ system, use backslash. Also mkdir syntax is different.
|
|
|
489 |
" This should only work on W2K and up.
|
|
|
490 |
let l:slash_char = '\'
|
|
|
491 |
let l:mkdir_cmd = ':silent !mkdir '
|
|
|
492 |
endif
|
|
|
493 |
|
|
|
494 |
let l:doc_path = l:slash_char . 'doc'
|
|
|
495 |
"let l:doc_home = l:slash_char . '.vim' . l:slash_char . 'doc'
|
|
|
496 |
|
|
|
497 |
" Figure out document path based on full name of this script:
|
|
|
498 |
let l:vim_plugin_path = fnamemodify(a:full_name, ':h')
|
|
|
499 |
"let l:vim_doc_path = fnamemodify(a:full_name, ':h:h') . l:doc_path
|
|
|
500 |
let l:vim_doc_path = matchstr(l:vim_plugin_path,
|
|
|
501 |
\ '.\{-}\ze\%(\%(ft\)\=plugin\|macros\)') . l:doc_path
|
|
|
502 |
if (!(filewritable(l:vim_doc_path) == 2))
|
|
|
503 |
echomsg "Doc path: " . l:vim_doc_path
|
|
|
504 |
execute l:mkdir_cmd . l:vim_doc_path
|
|
|
505 |
if (!(filewritable(l:vim_doc_path) == 2))
|
|
|
506 |
" Try a default configuration in user home:
|
|
|
507 |
"let l:vim_doc_path = expand("~") . l:doc_home
|
|
|
508 |
let l:vim_doc_path = matchstr(&rtp,
|
|
|
509 |
\ escape($HOME, '\') .'[/\\]\%(\.vim\|vimfiles\)')
|
|
|
510 |
if (!(filewritable(l:vim_doc_path) == 2))
|
|
|
511 |
execute l:mkdir_cmd . l:vim_doc_path
|
|
|
512 |
if (!(filewritable(l:vim_doc_path) == 2))
|
|
|
513 |
" Put a warning:
|
|
|
514 |
echomsg "Unable to open documentation directory"
|
|
|
515 |
echomsg " type :help add-local-help for more informations."
|
|
|
516 |
return 0
|
|
|
517 |
endif
|
|
|
518 |
endif
|
|
|
519 |
endif
|
|
|
520 |
endif
|
|
|
521 |
|
|
|
522 |
" Exit if we have problem to access the document directory:
|
|
|
523 |
if (!isdirectory(l:vim_plugin_path)
|
|
|
524 |
\ || !isdirectory(l:vim_doc_path)
|
|
|
525 |
\ || filewritable(l:vim_doc_path) != 2)
|
|
|
526 |
return 0
|
|
|
527 |
endif
|
|
|
528 |
|
|
|
529 |
" Full name of script and documentation file:
|
|
|
530 |
let l:script_name = 'xml.vim'
|
|
|
531 |
let l:doc_name = 'xml-plugin.txt'
|
|
|
532 |
let l:plugin_file = l:vim_plugin_path . l:slash_char . l:script_name
|
|
|
533 |
let l:doc_file = l:vim_doc_path . l:slash_char . l:doc_name
|
|
|
534 |
|
|
|
535 |
" Bail out if document file is still up to date:
|
|
|
536 |
if (filereadable(l:doc_file) &&
|
|
|
537 |
\ getftime(l:plugin_file) < getftime(l:doc_file))
|
|
|
538 |
return 0
|
|
|
539 |
endif
|
|
|
540 |
|
|
|
541 |
" Prepare window position restoring command:
|
|
|
542 |
if (strlen(@%))
|
|
|
543 |
let l:go_back = 'b ' . bufnr("%")
|
|
|
544 |
else
|
|
|
545 |
let l:go_back = 'enew!'
|
|
|
546 |
endif
|
|
|
547 |
|
|
|
548 |
" Create a new buffer & read in the plugin file (me):
|
|
|
549 |
setl nomodeline
|
|
|
550 |
exe 'enew!'
|
|
|
551 |
exe 'r ' . l:plugin_file
|
|
|
552 |
|
|
|
553 |
setl modeline
|
|
|
554 |
let l:buf = bufnr("%")
|
|
|
555 |
setl noswapfile modifiable
|
|
|
556 |
|
|
|
557 |
norm zR
|
|
|
558 |
norm gg
|
|
|
559 |
|
|
|
560 |
" Delete from first line to a line starts with
|
|
|
561 |
" === START_DOC
|
|
|
562 |
1,/^=\{3,}\s\+START_DOC\C/ d
|
|
|
563 |
|
|
|
564 |
" Delete from a line starts with
|
|
|
565 |
" === END_DOC
|
|
|
566 |
" to the end of the documents:
|
|
|
567 |
/^=\{3,}\s\+END_DOC\C/,$ d
|
|
|
568 |
|
|
|
569 |
" Remove fold marks:
|
|
|
570 |
% s/{\{3}[1-9]/ /
|
|
|
571 |
|
|
|
572 |
" Add modeline for help doc: the modeline string is mangled intentionally
|
|
|
573 |
" to avoid it be recognized by VIM:
|
|
|
574 |
call append(line('$'), '')
|
|
|
575 |
call append(line('$'), ' v' . 'im:tw=78:ts=8:ft=help:norl:')
|
|
|
576 |
|
|
|
577 |
" Replace revision:
|
|
|
578 |
exe "normal :1,5s/#version#/ v" . a:revision . "/\<CR>"
|
|
|
579 |
|
|
|
580 |
" Save the help document:
|
|
|
581 |
exe 'w! ' . l:doc_file
|
|
|
582 |
exe l:go_back
|
|
|
583 |
exe 'bw ' . l:buf
|
|
|
584 |
|
|
|
585 |
" Build help tags:
|
|
|
586 |
exe 'helptags ' . l:vim_doc_path
|
|
|
587 |
|
|
|
588 |
return 1
|
|
|
589 |
endfunction
|
|
|
590 |
" }}}2
|
|
|
591 |
|
|
|
592 |
let s:revision=
|
|
|
593 |
\ substitute("$Revision: 1.29 $",'\$\S*: \([.0-9]\+\) \$','\1','')
|
|
|
594 |
silent! let s:install_status =
|
|
|
595 |
\ s:XmlInstallDocumentation(expand('<sfile>:p'), s:revision)
|
|
|
596 |
if (s:install_status == 1)
|
|
|
597 |
echom expand("<sfile>:t:r") . '-plugin v' . s:revision .
|
|
|
598 |
\ ': Help-documentation installed.'
|
|
|
599 |
endif
|
|
|
600 |
|
|
|
601 |
|
|
|
602 |
" Mappings and Settings. {{{1
|
|
|
603 |
" This makes the '%' jump between the start and end of a single tag.
|
|
|
604 |
setlocal matchpairs+=<:>
|
|
|
605 |
|
|
|
606 |
" Have this as an escape incase you want a literal '>' not to run the
|
|
|
607 |
" ParseTag function.
|
|
|
608 |
if !exists("g:xml_tag_completion_map")
|
|
|
609 |
inoremap <buffer> <LocalLeader>. >
|
|
|
610 |
inoremap <buffer> <LocalLeader>> >
|
|
|
611 |
endif
|
|
|
612 |
|
|
|
613 |
" Jump between the beggining and end tags.
|
|
|
614 |
nnoremap <buffer> <LocalLeader>5 :call <SID>TagMatch1()<Cr>
|
|
|
615 |
nnoremap <buffer> <LocalLeader>% :call <SID>TagMatch1()<Cr>
|
|
|
616 |
vnoremap <buffer> <LocalLeader>5 <Esc>:call <SID>VisualTag()<Cr>
|
|
|
617 |
vnoremap <buffer> <LocalLeader>% <Esc>:call <SID>VisualTag()<Cr>
|
|
|
618 |
|
|
|
619 |
" Wrap selection in XML tag
|
|
|
620 |
vnoremap <buffer> <LocalLeader>x "xx:call <SID>WrapTag(@x)<Cr>
|
|
|
621 |
nnoremap <buffer> <LocalLeader>d :call <SID>DeleteTag()<Cr>
|
|
|
622 |
|
|
|
623 |
" Parse the tag after pressing the close '>'.
|
|
|
624 |
if !exists("g:xml_tag_completion_map")
|
|
|
625 |
inoremap <buffer> > ><Esc>:call <SID>ParseTag()<Cr>
|
|
|
626 |
else
|
|
|
627 |
execute "inoremap <buffer> " . g:xml_tag_completion_map . " ><Esc>:call <SID>ParseTag()<Cr>"
|
|
|
628 |
endif
|
|
|
629 |
|
|
|
630 |
augroup xml
|
|
|
631 |
au!
|
|
|
632 |
au BufNewFile * call <SID>NewFileXML()
|
|
|
633 |
augroup END
|
|
|
634 |
"}}}1
|
|
|
635 |
finish
|
|
|
636 |
|
|
|
637 |
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
|
638 |
" Section: Documentation content {{{1
|
|
|
639 |
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
|
640 |
=== START_DOC
|
|
|
641 |
*xml-plugin.txt* Help edit XML and SGML documents. #version#
|
|
|
642 |
|
|
|
643 |
XML Edit {{{2 ~
|
|
|
644 |
|
|
|
645 |
A filetype plugin to help edit XML and SGML documents.
|
|
|
646 |
|
|
|
647 |
This script provides some convenience when editing XML (and some SGML
|
|
|
648 |
including HTML) formated documents. It allows you to jump to the beginning
|
|
|
649 |
or end of the tag block your cursor is in. '%' will jump between '<' and '>'
|
|
|
650 |
within the tag your cursor is in. When in insert mode and you finish a tag
|
|
|
651 |
(pressing '>') the tag will be completed. If you press '>' twice it will
|
|
|
652 |
complete the tag and place the cursor in the middle of the tags on it's own
|
|
|
653 |
line (helps with nested tags).
|
|
|
654 |
|
|
|
655 |
Usage: Place this file into your ftplugin directory. To add html support
|
|
|
656 |
Sym-link or copy this file to html.vim in your ftplugin directory. To activte
|
|
|
657 |
the script place 'filetype plugin on' in your |.vimrc| file. See |ftplugins|
|
|
|
658 |
for more information on this topic.
|
|
|
659 |
|
|
|
660 |
If the file edited is of type "html" and "xml_use_html" is defined then the
|
|
|
661 |
following tags will not auto complete:
|
|
|
662 |
<img>, <input>, <param>, <frame>, <br>, <hr>, <meta>, <link>, <base>, <area>
|
|
|
663 |
|
|
|
664 |
If the file edited is of type 'html' and 'xml_use_xhtml' is defined the above
|
|
|
665 |
tags will autocomplete the xml closing staying xhtml compatable.
|
|
|
666 |
ex. <hr> becomes <hr /> (see |xml-plugin-settings|)
|
|
|
667 |
|
|
|
668 |
NOTE: If you used the VIM 5.x version of this file (xmledit.vim) you'll need
|
|
|
669 |
to comment out the section where you called it. It is no longer used in the
|
|
|
670 |
VIM 6.x version.
|
|
|
671 |
|
|
|
672 |
Known Bugs {{{2 ~
|
|
|
673 |
|
|
|
674 |
- This script will modify registers ". and "x; register "" will be restored.
|
|
|
675 |
- < & > marks inside of a CDATA section are interpreted as actual XML tags
|
|
|
676 |
even if unmatched.
|
|
|
677 |
- Although the script can handle leading spaces such as < tag></ tag> it is
|
|
|
678 |
illegal XML syntax and considered very bad form.
|
|
|
679 |
- Placing a literal `>' in an attribute value will auto complete dispite that
|
|
|
680 |
the start tag isn't finished. This is poor XML anyway you should use
|
|
|
681 |
> instead.
|
|
|
682 |
- The matching algorithm can handle illegal tag characters where as the tag
|
|
|
683 |
completion algorithm can not.
|
|
|
684 |
|
|
|
685 |
------------------------------------------------------------------------------
|
|
|
686 |
*xml-plugin-mappings*
|
|
|
687 |
Mappings {{{2 ~
|
|
|
688 |
|
|
|
689 |
<LocalLeader> is a setting in VIM that depicts a prefix for scripts and
|
|
|
690 |
plugins to use. By default this is the backslash key `\'. See |mapleader|
|
|
|
691 |
for details.
|
|
|
692 |
|
|
|
693 |
<LocalLeader>x
|
|
|
694 |
Visual - Place a custom XML tag to suround the selected text. You
|
|
|
695 |
need to have selected text in visual mode before you can use this
|
|
|
696 |
mapping. See |visual-mode| for details.
|
|
|
697 |
|
|
|
698 |
<LocalLeader>. or <LocalLeader>>
|
|
|
699 |
Insert - Place a literal '>' without parsing tag.
|
|
|
700 |
|
|
|
701 |
<LocalLeader>5 or <LocalLeader>%
|
|
|
702 |
Normal or Visual - Jump to the begining or end tag.
|
|
|
703 |
|
|
|
704 |
<LocalLeader>d
|
|
|
705 |
Normal - Deletes the surrounding tags from the cursor. >
|
|
|
706 |
<tag1>outter <tag2>inner text</tag2> text</tag1>
|
|
|
707 |
^
|
|
|
708 |
< Turns to: >
|
|
|
709 |
outter <tag2>inner text</tag2> text
|
|
|
710 |
^
|
|
|
711 |
<
|
|
|
712 |
|
|
|
713 |
------------------------------------------------------------------------------
|
|
|
714 |
*xml-plugin-settings*
|
|
|
715 |
Options {{{2 ~
|
|
|
716 |
|
|
|
717 |
(All options must be placed in your |.vimrc| prior to the |ftplugin|
|
|
|
718 |
command.)
|
|
|
719 |
|
|
|
720 |
xml_tag_completion_map
|
|
|
721 |
Use this setting to change the default mapping to auto complete a
|
|
|
722 |
tag. By default typing a literal `>' will cause the tag your editing
|
|
|
723 |
to auto complete; pressing twice will auto nest the tag. By using
|
|
|
724 |
this setting the `>' will be a literal `>' and you must use the new
|
|
|
725 |
mapping to perform auto completion and auto nesting. For example if
|
|
|
726 |
you wanted Control-L to perform auto completion inmstead of typing a
|
|
|
727 |
`>' place the following into your .vimrc: >
|
|
|
728 |
let xml_tag_completion_map = "<C-l>"
|
|
|
729 |
<
|
|
|
730 |
xml_no_auto_nesting
|
|
|
731 |
This turns off the auto nesting feature. After a completion is made
|
|
|
732 |
and another `>' is typed xml-edit automatically will break the tag
|
|
|
733 |
accross multiple lines and indent the curser to make creating nested
|
|
|
734 |
tqags easier. This feature turns it off. Enter the following in your
|
|
|
735 |
.vimrc: >
|
|
|
736 |
let xml_no_auto_nesting = 1
|
|
|
737 |
<
|
|
|
738 |
xml_use_xhtml
|
|
|
739 |
When editing HTML this will auto close the short tags to make valid
|
|
|
740 |
XML like <hr /> and <br />. Enter the following in your vimrc to
|
|
|
741 |
turn this option on: >
|
|
|
742 |
let xml_use_xhtml = 1
|
|
|
743 |
<
|
|
|
744 |
xml_no_html
|
|
|
745 |
This turns of the support for HTML specific tags. Place this in your
|
|
|
746 |
.vimrc: >
|
|
|
747 |
let xml_no_html = 1
|
|
|
748 |
<
|
|
|
749 |
------------------------------------------------------------------------------
|
|
|
750 |
*xml-plugin-callbacks*
|
|
|
751 |
Callback Functions {{{2 ~
|
|
|
752 |
|
|
|
753 |
A callback function is a function used to customize features on a per tag
|
|
|
754 |
basis. For example say you wish to have a default set of attributs when you
|
|
|
755 |
type an empty tag like this:
|
|
|
756 |
You type: <tag>
|
|
|
757 |
You get: <tag default="attributes"></tag>
|
|
|
758 |
|
|
|
759 |
This is for any script programmers who wish to add xml-plugin support to
|
|
|
760 |
there own filetype plugins.
|
|
|
761 |
|
|
|
762 |
Callback functions recive one attribute variable which is the tag name. The
|
|
|
763 |
all must return either a string or the number zero. If it returns a string
|
|
|
764 |
the plugin will place the string in the proper location. If it is a zero the
|
|
|
765 |
plugin will ignore and continue as if no callback existed.
|
|
|
766 |
|
|
|
767 |
The following are implemented callback functions:
|
|
|
768 |
|
|
|
769 |
HtmlAttribCallback
|
|
|
770 |
This is used to add default attributes to html tag. It is intended
|
|
|
771 |
for HTML files only.
|
|
|
772 |
|
|
|
773 |
XmlAttribCallback
|
|
|
774 |
This is a generic callback for xml tags intended to add attributes.
|
|
|
775 |
|
|
|
776 |
*xml-plugin-html*
|
|
|
777 |
Callback Example {{{2 ~
|
|
|
778 |
|
|
|
779 |
The following is an example of using XmlAttribCallback in your .vimrc
|
|
|
780 |
>
|
|
|
781 |
function XmlAttribCallback (xml_tag)
|
|
|
782 |
if a:xml_tag ==? "my-xml-tag"
|
|
|
783 |
return "attributes=\"my xml attributes\""
|
|
|
784 |
else
|
|
|
785 |
return 0
|
|
|
786 |
endif
|
|
|
787 |
endfunction
|
|
|
788 |
<
|
|
|
789 |
The following is a sample html.vim file type plugin you could use:
|
|
|
790 |
>
|
|
|
791 |
" Vim script file vim600:fdm=marker:
|
|
|
792 |
" FileType: HTML
|
|
|
793 |
" Maintainer: Devin Weaver <vim (at) tritarget.com>
|
|
|
794 |
" Location: http://www.vim.org/scripts/script.php?script_id=301
|
|
|
795 |
|
|
|
796 |
" This is a wrapper script to add extra html support to xml documents.
|
|
|
797 |
" Original script can be seen in xml-plugin documentation.
|
|
|
798 |
|
|
|
799 |
" Only do this when not done yet for this buffer
|
|
|
800 |
if exists("b:did_ftplugin")
|
|
|
801 |
finish
|
|
|
802 |
endif
|
|
|
803 |
" Don't set 'b:did_ftplugin = 1' because that is xml.vim's responsability.
|
|
|
804 |
|
|
|
805 |
let b:html_mode = 1
|
|
|
806 |
|
|
|
807 |
if !exists("*HtmlAttribCallback")
|
|
|
808 |
function HtmlAttribCallback( xml_tag )
|
|
|
809 |
if a:xml_tag ==? "table"
|
|
|
810 |
return "cellpadding=\"0\" cellspacing=\"0\" border=\"0\""
|
|
|
811 |
elseif a:xml_tag ==? "link"
|
|
|
812 |
return "href=\"/site.css\" rel=\"StyleSheet\" type=\"text/css\""
|
|
|
813 |
elseif a:xml_tag ==? "body"
|
|
|
814 |
return "bgcolor=\"white\""
|
|
|
815 |
elseif a:xml_tag ==? "frame"
|
|
|
816 |
return "name=\"NAME\" src=\"/\" scrolling=\"auto\" noresize"
|
|
|
817 |
elseif a:xml_tag ==? "frameset"
|
|
|
818 |
return "rows=\"0,*\" cols=\"*,0\" border=\"0\""
|
|
|
819 |
elseif a:xml_tag ==? "img"
|
|
|
820 |
return "src=\"\" width=\"0\" height=\"0\" border=\"0\" alt=\"\""
|
|
|
821 |
elseif a:xml_tag ==? "a"
|
|
|
822 |
if has("browse")
|
|
|
823 |
" Look up a file to fill the href. Used in local relative file
|
|
|
824 |
" links. typeing your own href before closing the tag with `>'
|
|
|
825 |
" will override this.
|
|
|
826 |
let cwd = getcwd()
|
|
|
827 |
let cwd = substitute (cwd, "\\", "/", "g")
|
|
|
828 |
let href = browse (0, "Link to href...", getcwd(), "")
|
|
|
829 |
let href = substitute (href, cwd . "/", "", "")
|
|
|
830 |
let href = substitute (href, " ", "%20", "g")
|
|
|
831 |
else
|
|
|
832 |
let href = ""
|
|
|
833 |
endif
|
|
|
834 |
return "href=\"" . href . "\""
|
|
|
835 |
else
|
|
|
836 |
return 0
|
|
|
837 |
endif
|
|
|
838 |
endfunction
|
|
|
839 |
endif
|
|
|
840 |
|
|
|
841 |
" On to loading xml.vim
|
|
|
842 |
runtime ftplugin/xml.vim
|
|
|
843 |
<
|
|
|
844 |
=== END_DOC
|
|
|
845 |
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
|
846 |
" v im:tw=78:ts=8:ft=help:norl:
|
|
|
847 |
" vim600: set foldmethod=marker tabstop=8 shiftwidth=2 softtabstop=2 smartindent smarttab :
|
|
|
848 |
"fileencoding=iso-8859-15
|