In vim (neovim), XML code is indenting strangely when attributes are put on their own lines, like so:
<test
first="text"
second="more text"
third="last">
<foo
a="a"
b="b">
<bar />
</foo>
</test>
When I expect, and desire, this:
<test
first="text"
second="more text"
third="last">
<foo
a="a"
b="b">
<bar />
</foo>
</test>
This feels like none other than a bug to me. I could not find a plugin to address this. Perhaps there is something that I could add to my init.vim to correct this behavior?
EDIT: The patch below almost fixes this issue, but does not correctly handle the following case:
<test
first="text"
second="more text"
third="last">
<foobar>
</foobar>
<foobar>
</foobar>
</test>
EDIT 2:
Removing the + shiftwidth()
on line 90 solves all but this case:
<test
first="text">
<foobar>
</foobar>
</test>
<foobar>
should indent as shown above, because the tag spanning the first two lines is an opening tag, indicating that the next lines should be indented.
EDIT 3: For some reason, <foo
is indenting twice with the patch. Expected:
<test>
<foo
a="a">
</foo>
</test>
I know regex enough to play with it, but vim script makes this tricky.
EDIT 4: Expected output should be:
<test>
<foo
a="a" />
<foobar
a="a" >
<test>
</test>
</foobar>
</test>
/>
should not indent the next line.
I'm not knowledgeable enough about XML to determine whether it's expected behavior nor can do proper tests, so I don't know if my solution is the best.
But if you suspect it to be a bug, then the best action would be to report1 it on the official GitHub repository of XML runtime files: chrisbra/vim-xml-runtime.
I managed to get your expected results by applying this patch2:
@@ -81,11 +81,16 @@
endfun
" [-- return the sum of indents of a:lnum --]
-fun! <SID>XmlIndentSum(line, style, add)
+fun! <SID>XmlIndentSum(line, style, add, ...)
if <SID>IsXMLContinuation(a:line) && a:style == 0 && !<SID>IsXMLEmptyClosingTag(a:line)
" no complete tag, add one additional indent level
" but only for the current line
return a:add + shiftwidth()
+ elseif a:0 && a:line =~ '^\s*<[^/]' && a:1 =~ '^\s*<[^/]' && a:1 !~ '>\s*$'
+ \ && getline(prevnonblank(a:2-1)) !~ '/\s*>\s*$'
+ return a:add + shiftwidth()
+ elseif a:0 && a:line =~ '^\s*</' && a:1 =~ '^\s*<' && a:1 !~ '>\s*$'
+ return a:add
elseif <SID>HasNoTagEnd(a:line)
" no complete tag, return initial indent
return a:add
@@ -156,7 +161,7 @@
" Get indent from previous tag line
let ind = <SID>XmlIndentSum(pline, -1, pind)
" Determine indent from current line
- let ind = <SID>XmlIndentSum(curline, 0, ind)
+ let ind = <SID>XmlIndentSum(curline, 0, ind, pline, a:lnum)
return ind
endfun
Just copy the indent file to ~/.vim/indent/xml.vim
3 and apply the change.
1 You may link this answer in the issue
2 Which is of quality of just a temporary hack
3 I believe it's $XDG_CONFIG_HOME/nvim/indent/xml.vim
in case of Neovim