diff runtime/autoload/xmlformat.vim @ 18456:6d11fc4aa683

Update runtime files Commit: https://github.com/vim/vim/commit/96f45c0b6fc9e9d404e6805593ed1e0e6795e470 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Oct 26 19:53:45 2019 +0200 Update runtime files
author Bram Moolenaar <Bram@vim.org>
date Sat, 26 Oct 2019 20:00:04 +0200
parents bc1a8d21c811
children d4deb2e50667
line wrap: on
line diff
--- 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: 2019 Oct 24
+"     Version: 0.2
 "      Author: Christian Brabandt <cb@256bit.org>
 "  Repository: https://github.com/chrisbra/vim-xml-ftplugin
 "     License: VIM License
@@ -22,44 +22,73 @@ func! xmlformat#Format()
     " 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)
+      let line .= join(list[(current + 1):(nextmatch-1)], "\n")
+      call remove(list, current+1, nextmatch-1)
+    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)
+        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 '<', if there is one,
+          " but reformat according to &textwidth
+          let t=split(item, '.<\@=\zs')
+          " 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])
+            let s:indent = 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
@@ -88,6 +117,7 @@ func! s:StartTag(tag)
   let is_comment = s:IsComment(a:tag)
   return a:tag =~? '^\s*<[^/?]' && !is_comment
 endfunc
+" Check if tag is a Comment start {{{1
 func! s:IsComment(tag)
   return a:tag =~? '<!--'
 endfunc
@@ -108,6 +138,43 @@ endfunc
 func! s:EmptyTag(tag)
   return a:tag =~ '/>\s*$'
 endfunc
+" Format input line according to textwidth {{{1
+func! s:FormatContent(list)
+  let result=[]
+  let limit = 80
+  if &textwidth > 0
+    let limit = &textwidth
+  endif
+  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') 
+      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