1" Vim plugin for formatting XML 2" Last Change: Thu, 22 May 2018 21:26:55 +0100 3" Version: 0.1 4" Author: Christian Brabandt <[email protected]> 5" Script: http://www.vim.org/scripts/script.php?script_id= 6" License: VIM License 7" GetLatestVimScripts: ???? 18 :AutoInstall: xmlformat.vim 8" Documentation: see :h xmlformat.txt (TODO!) 9" --------------------------------------------------------------------- 10" Load Once: {{{1 11if exists("g:loaded_xmlformat") || &cp 12 finish 13endif 14let g:loaded_xmlformat = 1 15let s:keepcpo = &cpo 16set cpo&vim 17 18" Main function: Format the input {{{1 19func! xmlformat#Format() 20 " only allow reformatting through the gq command 21 " (e.g. Vim is in normal mode) 22 if mode() != 'n' 23 " do not fall back to internal formatting 24 return 0 25 endif 26 let sw = shiftwidth() 27 let prev = prevnonblank(v:lnum-1) 28 let s:indent = indent(prev)/sw 29 let result = [] 30 let lastitem = prev ? getline(prev) : '' 31 let is_xml_decl = 0 32 " split on `<`, but don't split on very first opening < 33 for item in split(join(getline(v:lnum, (v:lnum + v:count - 1))), '.\@<=[>]\zs') 34 if s:EndTag(item) 35 let s:indent = s:DecreaseIndent() 36 call add(result, s:Indent(item)) 37 elseif s:EmptyTag(lastitem) 38 call add(result, s:Indent(item)) 39 elseif s:StartTag(lastitem) && s:IsTag(item) 40 let s:indent += 1 41 call add(result, s:Indent(item)) 42 else 43 if !s:IsTag(item) 44 " Simply split on '<' 45 let t=split(item, '.<\@=\zs') 46 let s:indent+=1 47 call add(result, s:Indent(t[0])) 48 let s:indent = s:DecreaseIndent() 49 call add(result, s:Indent(t[1])) 50 else 51 call add(result, s:Indent(item)) 52 endif 53 endif 54 let lastitem = item 55 endfor 56 57 if !empty(result) 58 exe v:lnum. ",". (v:lnum + v:count - 1). 'd' 59 call append(v:lnum - 1, result) 60 " Might need to remove the last line, if it became empty because of the 61 " append() call 62 let last = v:lnum + len(result) 63 if getline(last) is '' 64 exe last. 'd' 65 endif 66 endif 67 68 " do not run internal formatter! 69 return 0 70endfunc 71" Check if given tag is XML Declaration header {{{1 72func! s:IsXMLDecl(tag) 73 return a:tag =~? '^\s*<?xml\s\?\%(version="[^"]*"\)\?\s\?\%(encoding="[^"]*"\)\? ?>\s*$' 74endfunc 75" Return tag indented by current level {{{1 76func! s:Indent(item) 77 return repeat(' ', shiftwidth()*s:indent). s:Trim(a:item) 78endfu 79" Return item trimmed from leading whitespace {{{1 80func! s:Trim(item) 81 if exists('*trim') 82 return trim(a:item) 83 else 84 return matchstr(a:item, '\S\+.*') 85 endif 86endfunc 87" Check if tag is a new opening tag <tag> {{{1 88func! s:StartTag(tag) 89 return a:tag =~? '^\s*<[^/?]' 90endfunc 91" Remove one level of indentation {{{1 92func! s:DecreaseIndent() 93 return (s:indent > 0 ? s:indent - 1 : 0) 94endfunc 95" Check if tag is a closing tag </tag> {{{1 96func! s:EndTag(tag) 97 return a:tag =~? '^\s*</' 98endfunc 99" Check that the tag is actually a tag and not {{{1 100" something like "foobar</foobar>" 101func! s:IsTag(tag) 102 return s:Trim(a:tag)[0] == '<' 103endfunc 104" Check if tag is empty <tag/> {{{1 105func! s:EmptyTag(tag) 106 return a:tag =~ '/>\s*$' 107endfunc 108" Restoration And Modelines: {{{1 109let &cpo= s:keepcpo 110unlet s:keepcpo 111" Modeline {{{1 112" vim: fdm=marker fdl=0 ts=2 et sw=0 sts=-1 113