aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/xmlformat.vim164
-rw-r--r--runtime/compiler/xmllint.vim18
-rw-r--r--runtime/indent/xml.vim15
-rw-r--r--runtime/syntax/xml.vim43
4 files changed, 171 insertions, 69 deletions
diff --git a/runtime/autoload/xmlformat.vim b/runtime/autoload/xmlformat.vim
index ea89401977..712e6f7351 100644
--- a/runtime/autoload/xmlformat.vim
+++ b/runtime/autoload/xmlformat.vim
@@ -1,6 +1,6 @@
" Vim plugin for formatting XML
-" Last Change: Thu, 07 Dec 2018
-" Version: 0.1
+" Last Change: 2020 Jan 06
+" Version: 0.3
" Author: Christian Brabandt <cb@256bit.org>
" Repository: https://github.com/chrisbra/vim-xml-ftplugin
" License: VIM License
@@ -15,51 +15,92 @@ let s:keepcpo = &cpo
set cpo&vim
" Main function: Format the input {{{1
-func! xmlformat#Format()
+func! xmlformat#Format() abort
" only allow reformatting through the gq command
" (e.g. Vim is in normal mode)
if mode() != 'n'
" do not fall back to internal formatting
return 0
endif
+ let count_orig = v:count
let sw = shiftwidth()
let prev = prevnonblank(v:lnum-1)
let s:indent = indent(prev)/sw
let result = []
let lastitem = prev ? getline(prev) : ''
let is_xml_decl = 0
- " split on `<`, but don't split on very first opening <
- for item in split(join(getline(v:lnum, (v:lnum + v:count - 1))), '.\@<=[>]\zs')
- if s:EndTag(item)
- let s:indent = s:DecreaseIndent()
- call add(result, s:Indent(item))
- elseif s:EmptyTag(lastitem)
- call add(result, s:Indent(item))
- elseif s:StartTag(lastitem) && s:IsTag(item)
- let s:indent += 1
- call add(result, s:Indent(item))
- else
- if !s:IsTag(item)
- " Simply split on '<'
- let t=split(item, '.<\@=\zs')
- let s:indent+=1
- call add(result, s:Indent(t[0]))
- let s:indent = s:DecreaseIndent()
- call add(result, s:Indent(t[1]))
- else
+ " go through every line, but don't join all content together and join it
+ " back. We might lose empty lines
+ let list = getline(v:lnum, (v:lnum + count_orig - 1))
+ let current = 0
+ for line in list
+ " Keep empty input lines?
+ if empty(line)
+ call add(result, '')
+ continue
+ elseif line !~# '<[/]\?[^>]*>'
+ let nextmatch = match(list, '<[/]\?[^>]*>', current)
+ if nextmatch > -1
+ let line .= ' '. join(list[(current + 1):(nextmatch-1)], " ")
+ call remove(list, current+1, nextmatch-1)
+ endif
+ endif
+ " split on `>`, but don't split on very first opening <
+ " this means, items can be like ['<tag>', 'tag content</tag>']
+ for item in split(line, '.\@<=[>]\zs')
+ if s:EndTag(item)
+ call s:DecreaseIndent()
+ call add(result, s:Indent(item))
+ elseif s:EmptyTag(lastitem)
call add(result, s:Indent(item))
+ elseif s:StartTag(lastitem) && s:IsTag(item)
+ let s:indent += 1
+ call add(result, s:Indent(item))
+ else
+ if !s:IsTag(item)
+ " Simply split on '<', if there is one,
+ " but reformat according to &textwidth
+ let t=split(item, '.<\@=\zs')
+
+ " if the content fits well within a single line, add it there
+ " so that the output looks like this:
+ "
+ " <foobar>1</foobar>
+ if s:TagContent(lastitem) is# s:TagContent(t[1]) && strlen(result[-1]) + strlen(item) <= s:Textwidth()
+ let result[-1] .= item
+ let lastitem = t[1]
+ continue
+ endif
+ " t should only contain 2 items, but just be safe here
+ if s:IsTag(lastitem)
+ let s:indent+=1
+ endif
+ let result+=s:FormatContent([t[0]])
+ if s:EndTag(t[1])
+ call s:DecreaseIndent()
+ endif
+ "for y in t[1:]
+ let result+=s:FormatContent(t[1:])
+ "endfor
+ else
+ call add(result, s:Indent(item))
+ endif
endif
- endif
- let lastitem = item
- endfor
+ let lastitem = item
+ endfor
+ let current += 1
+ endfor
- if !empty(result)
- exe v:lnum. ",". (v:lnum + v:count - 1). 'd'
+ if !empty(result)
+ let lastprevline = getline(v:lnum + count_orig)
+ let delete_lastline = v:lnum + count_orig - 1 == line('$')
+ exe v:lnum. ",". (v:lnum + count_orig - 1). 'd'
call append(v:lnum - 1, result)
" Might need to remove the last line, if it became empty because of the
" append() call
let last = v:lnum + len(result)
- if getline(last) is ''
+ " do not use empty(), it returns true for `empty(0)`
+ if getline(last) is '' && lastprevline is '' && delete_lastline
exe last. 'd'
endif
endif
@@ -68,15 +109,15 @@ func! xmlformat#Format()
return 0
endfunc
" Check if given tag is XML Declaration header {{{1
-func! s:IsXMLDecl(tag)
+func! s:IsXMLDecl(tag) abort
return a:tag =~? '^\s*<?xml\s\?\%(version="[^"]*"\)\?\s\?\%(encoding="[^"]*"\)\? ?>\s*$'
endfunc
" Return tag indented by current level {{{1
-func! s:Indent(item)
+func! s:Indent(item) abort
return repeat(' ', shiftwidth()*s:indent). s:Trim(a:item)
endfu
" Return item trimmed from leading whitespace {{{1
-func! s:Trim(item)
+func! s:Trim(item) abort
if exists('*trim')
return trim(a:item)
else
@@ -84,30 +125,77 @@ func! s:Trim(item)
endif
endfunc
" Check if tag is a new opening tag <tag> {{{1
-func! s:StartTag(tag)
+func! s:StartTag(tag) abort
let is_comment = s:IsComment(a:tag)
return a:tag =~? '^\s*<[^/?]' && !is_comment
endfunc
-func! s:IsComment(tag)
+" Check if tag is a Comment start {{{1
+func! s:IsComment(tag) abort
return a:tag =~? '<!--'
endfunc
" Remove one level of indentation {{{1
-func! s:DecreaseIndent()
- return (s:indent > 0 ? s:indent - 1 : 0)
+func! s:DecreaseIndent() abort
+ let s:indent = (s:indent > 0 ? s:indent - 1 : 0)
endfunc
" Check if tag is a closing tag </tag> {{{1
-func! s:EndTag(tag)
+func! s:EndTag(tag) abort
return a:tag =~? '^\s*</'
endfunc
" Check that the tag is actually a tag and not {{{1
" something like "foobar</foobar>"
-func! s:IsTag(tag)
+func! s:IsTag(tag) abort
return s:Trim(a:tag)[0] == '<'
endfunc
" Check if tag is empty <tag/> {{{1
-func! s:EmptyTag(tag)
+func! s:EmptyTag(tag) abort
return a:tag =~ '/>\s*$'
endfunc
+func! s:TagContent(tag) abort "{{{1
+ " Return content of a tag
+ return substitute(a:tag, '^\s*<[/]\?\([^>]*\)>\s*$', '\1', '')
+endfunc
+func! s:Textwidth() abort "{{{1
+ " return textwidth (or 80 if not set)
+ return &textwidth == 0 ? 80 : &textwidth
+endfunc
+" Format input line according to textwidth {{{1
+func! s:FormatContent(list) abort
+ let result=[]
+ let limit = s:Textwidth()
+ let column=0
+ let idx = -1
+ let add_indent = 0
+ let cnt = 0
+ for item in a:list
+ for word in split(item, '\s\+\S\+\zs')
+ if match(word, '^\s\+$') > -1
+ " skip empty words
+ continue
+ endif
+ let column += strdisplaywidth(word, column)
+ if match(word, "^\\s*\n\\+\\s*$") > -1
+ call add(result, '')
+ let idx += 1
+ let column = 0
+ let add_indent = 1
+ elseif column > limit || cnt == 0
+ let add = s:Indent(s:Trim(word))
+ call add(result, add)
+ let column = strdisplaywidth(add)
+ let idx += 1
+ else
+ if add_indent
+ let result[idx] = s:Indent(s:Trim(word))
+ else
+ let result[idx] .= ' '. s:Trim(word)
+ endif
+ let add_indent = 0
+ endif
+ let cnt += 1
+ endfor
+ endfor
+ return result
+endfunc
" Restoration And Modelines: {{{1
let &cpo= s:keepcpo
unlet s:keepcpo
diff --git a/runtime/compiler/xmllint.vim b/runtime/compiler/xmllint.vim
index 96cfa55383..79d38b4d14 100644
--- a/runtime/compiler/xmllint.vim
+++ b/runtime/compiler/xmllint.vim
@@ -1,7 +1,7 @@
" Vim compiler file
-" Compiler: xmllint
+" Compiler: Libxml2 Command-Line Tool
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2013 Jul 8
+" Last Change: 2020 Jul 30
if exists("current_compiler")
finish
@@ -13,14 +13,16 @@ if exists(":CompilerSet") != 2 " older Vim always used :setlocal
endif
let s:cpo_save = &cpo
-set cpo-=C
+set cpo&vim
CompilerSet makeprg=xmllint\ --valid\ --noout
-
-CompilerSet errorformat=%+E%f:%l:\ %.%#\ error\ :\ %m,
- \%+W%f:%l:\ %.%#\ warning\ :\ %m,
- \%-Z%p^,
- \%-G%.%#
+CompilerSet errorformat=%E%f:%l:\ %.%#\ error\ :\ %m,
+ \%W%f:%l:\ %.%#\ warning\ :\ %m,
+ \%-Z%p^,
+ \%C%.%#,
+ \%terror:\ %m,
+ \%tarning:\ %m,
+ \%-G%.%#
let &cpo = s:cpo_save
unlet s:cpo_save
diff --git a/runtime/indent/xml.vim b/runtime/indent/xml.vim
index 883af98563..413a3ddb53 100644
--- a/runtime/indent/xml.vim
+++ b/runtime/indent/xml.vim
@@ -1,9 +1,10 @@
-" Language: xml
-" Repository: https://github.com/chrisbra/vim-xml-ftplugin
-" Last Changed: July 27, 2019
-" Maintainer: Christian Brabandt <cb@256bit.org>
-" Previous Maintainer: Johannes Zellner <johannes@zellner.org>
+" Language: XML
+" Maintainer: Christian Brabandt <cb@256bit.org>
+" Repository: https://github.com/chrisbra/vim-xml-ftplugin
+" Previous Maintainer: Johannes Zellner <johannes@zellner.org>
+" Last Changed: 2019 Dec 02
" Last Change:
+" 20191202 - Handle docbk filetype
" 20190726 - Correctly handle non-tagged data
" 20190204 - correctly handle wrap tags
" https://github.com/chrisbra/vim-xml-ftplugin/issues/5
@@ -134,7 +135,7 @@ fun! XmlIndentGet(lnum, use_syntax_check)
if syn_name_end =~ 'Comment' && syn_name_start =~ 'Comment'
return <SID>XmlIndentComment(a:lnum)
- elseif empty(syn_name_start) && empty(syn_name_end)
+ elseif empty(syn_name_start) && empty(syn_name_end) && a:use_syntax_check
" non-xml tag content: use indent from 'autoindent'
return pind + shiftwidth()
endif
@@ -148,7 +149,7 @@ endfun
func! <SID>IsXMLContinuation(line)
" Checks, whether or not the line matches a start-of-tag
- return a:line !~ '^\s*<'
+ return a:line !~ '^\s*<' && &ft is# 'xml'
endfunc
func! <SID>HasNoTagEnd(line)
diff --git a/runtime/syntax/xml.vim b/runtime/syntax/xml.vim
index 7b503abf49..7c9791a7cc 100644
--- a/runtime/syntax/xml.vim
+++ b/runtime/syntax/xml.vim
@@ -1,11 +1,15 @@
" Vim syntax file
-" Language: XML
-" Maintainer: Johannes Zellner <johannes@zellner.org>
-" Author and previous maintainer:
-" Paul Siegmann <pauls@euronet.nl>
-" Last Change: 2013 Jun 07
+" Language: XML
+" Maintainer: Christian Brabandt <cb@256bit.org>
+" Repository: https://github.com/chrisbra/vim-xml-ftplugin
+" Previous Maintainer: Johannes Zellner <johannes@zellner.org>
+" Author: Paul Siegmann <pauls@euronet.nl>
+" Last Changed: Nov 03, 2019
" Filenames: *.xml
-" $Id: xml.vim,v 1.3 2006/04/11 21:32:00 vimboss Exp $
+" Last Change:
+" 20190923 - Fix xmlEndTag to match xmlTag (vim/vim#884)
+" 20190924 - Fix xmlAttribute property (amadeus/vim-xml@d8ce1c946)
+" 20191103 - Enable spell checking globally
" CONFIGURATION:
" syntax folding can be turned on by
@@ -49,6 +53,12 @@ set cpo&vim
syn case match
+" Allow spell checking in tag values,
+" there is no syntax region for that,
+" so enable spell checking in top-level elements
+" <tag>This text is spell checked</tag>
+syn spell toplevel
+
" mark illegal characters
syn match xmlError "[<&]"
@@ -81,7 +91,7 @@ syn match xmlEqual +=+ display
" ^^^^^^^^^^^^^
"
syn match xmlAttrib
- \ +[-'"<]\@1<!\<[a-zA-Z:_][-.0-9a-zA-Z:_]*\>\%(['">]\@!\|$\)+
+ \ +[-'"<]\@1<!\<[a-zA-Z:_][-.0-9a-zA-Z:_]*\>\%(['"]\@!\|$\)+
\ contained
\ contains=xmlAttribPunct,@xmlAttribHook
\ display
@@ -122,7 +132,7 @@ endif
" ^^^
"
syn match xmlTagName
- \ +<\@1<=[^ /!?<>"']\++
+ \ +\%(<\|</\)\@2<=[^ /!?<>"']\++
\ contained
\ contains=xmlNamespace,xmlAttribPunct,@xmlTagHook
\ display
@@ -157,11 +167,11 @@ if exists('g:xml_syntax_folding')
" </tag>
" ^^^^^^
"
- syn match xmlEndTag
- \ +</[^ /!?<>"']\+>+
+ syn region xmlEndTag
+ \ matchgroup=xmlTag start=+</[^ /!?<>"']\@=+
+ \ matchgroup=xmlTag end=+>+
\ contained
- \ contains=xmlNamespace,xmlAttribPunct,@xmlTagHook
-
+ \ contains=xmlTagName,xmlNamespace,xmlAttribPunct,@xmlTagHook
" tag elements with syntax-folding.
" NOTE: NO HIGHLIGHTING -- highlighting is done by contained elements
@@ -181,7 +191,7 @@ if exists('g:xml_syntax_folding')
\ start=+<\z([^ /!?<>"']\+\)+
\ skip=+<!--\_.\{-}-->+
\ end=+</\z1\_\s\{-}>+
- \ matchgroup=xmlEndTag end=+/>+
+ \ end=+/>+
\ fold
\ contains=xmlTag,xmlEndTag,xmlCdata,xmlRegion,xmlComment,xmlEntity,xmlProcessing,@xmlRegionHook,@Spell
\ keepend
@@ -198,9 +208,10 @@ else
\ matchgroup=xmlTag end=+>+
\ contains=xmlError,xmlTagName,xmlAttrib,xmlEqual,xmlString,@xmlStartTagHook
- syn match xmlEndTag
- \ +</[^ /!?<>"']\+>+
- \ contains=xmlNamespace,xmlAttribPunct,@xmlTagHook
+ syn region xmlEndTag
+ \ matchgroup=xmlTag start=+</[^ /!?<>"']\@=+
+ \ matchgroup=xmlTag end=+>+
+ \ contains=xmlTagName,xmlNamespace,xmlAttribPunct,@xmlTagHook
endif