121

I am learning Vim but I thought this was a simple task but I cannot get it to work. I have browser SO but the solutions are not working for me.

I am trying to correctly indent an file (xml). The command I use is:

gg=G 

or ggVG= (made this one up myself probably does something different ;))

My .vimrc is:

syntax on 
filetype plugin indent on 
set nu 
Haagenti
  • 7,974
  • 5
  • 38
  • 52
  • What's the original, result, and expected output? – Ingo Karkat Jan 28 '14 at 14:33
  • What's the output of `:set ft? indentexpr?` Does it read `filetype=xml indentexpr=XmlIndentGet(v:lnum,1)`?! – Ingo Karkat Jan 28 '14 at 14:34
  • filetype=xml indentexpr=XmlIndentGet(v:lnum,1). Yup – Haagenti Jan 28 '14 at 14:36
  • 1
    See http://stackoverflow.com/questions/11655383/vim-formatting-using-gg-g-with-xml#11660992 –  Jan 28 '14 at 14:51
  • 1
    I like to add just a little to this map and make it `m'ggVG=''`, which simply saves the line you're on and moves back to it after reindenting the file. – Hovis Biddle Jul 17 '14 at 17:35
  • Note to self: `:% !xmllint "%" --format` (input: content of current file; output: written to current file; [from Vim Tips Wiki](https://vim.fandom.com/wiki/Format_your_xml_document_using_xmllint#xmllint_on_Windows)) – toraritte Feb 03 '23 at 18:54
  • No one mentioned the excellent [Format your XML document using `xmllint`](https://vim.fandom.com/wiki/Format_your_xml_document_using_xmllint) article that has a ton of examples, but also alternatives to `xmllint`. – toraritte Feb 03 '23 at 19:04

10 Answers10

240

I like Berei's answer. However, I think the following is a little more flexible in that you don't have to alter your vimrc file. Plus it is easier to format select portions of the XML file (something I happen to do a lot).

First, highlight the XML you want to format.

Then, in visual mode, type ! xmllint --format -

Your command-line at the bottom will look like this:

:'<,'>!xmllint --format -

Then hit enter.

Technical Explanation

The selected text is sent to the xmllint command, then --format'ed, and the results of xmllint are placed over your selected text in vim. The - at the end of the command is for receiving standard input - which, in this case, is the selected text that vim sends to xmllint.

mrfred
  • 613
  • 4
  • 15
Jesse Hogan
  • 3,075
  • 2
  • 16
  • 17
  • 6
    This of course only works if you have the external tool xmllint installed and added to your path. – Polymorphix Oct 29 '14 at 12:12
  • 83
    to simplify the command, `:%!xmllint --format -`, `%` means the whole scope of this xml file. – hakunami Jan 08 '15 at 08:37
  • 2
    Why does everybody expect there is an xmllint tool? In Ubuntu 15.04 there isn't and apt-get also can't find one. – erikbstack May 18 '15 at 12:01
  • 9
    @erikb85 Use `apt-cache search`, not `apt-get`, when searching for packages and package contents. `libxml2-utils` contains the missing tool. – ParanoiaPuppy May 22 '15 at 06:40
  • @hakunami i wish i could upvote you 1000 times for simplifying that process – niken Jul 16 '15 at 15:07
  • I added this to my .vimrc to map the leader key followed by 'X' to format an entire document: `nmap X ggVG !xmllint --format -` – adampasz Feb 09 '16 at 18:40
  • if you want to add syntax highligt, then use highlight like this : `:'<,'>!xmllint --format - | highlight --out-format=ansi --syntax=xml` if you do not have highlight, then install it like this : `sudo apt install -yq highlight` – serup Oct 27 '16 at 09:19
  • 1
    Why not use standard vim way of formatting? With smartindent and filetype it works without external dependency. – Krzysztof Krasoń Apr 24 '17 at 12:59
  • What I don't like about this approach is that it inserts – ka3ak Dec 16 '18 at 08:34
  • I found this link : https://gist.github.com/jlacar/00a07453ec4344cf0194 used this commande : :%!xmllint "%" --format – ktaria Aug 26 '23 at 16:12
78

Use an external program to indent your xml files. In this case I've choosen xmllint, so set the command to the equalprg option:

:set equalprg=xmllint\ --format\ -

Now you can execute

gg=G

to let xmllint format your xml files.

To get it every time you use , use an autocommand to set it.

autocommand from a comment below

au FileType xml setlocal equalprg=xmllint\ --format\ --recover\ -\ 2>/dev/null
Khaja Minhajuddin
  • 6,653
  • 7
  • 45
  • 47
Birei
  • 35,723
  • 2
  • 77
  • 82
  • 1
    Not sure why this works hence the beginner but I will figure this one out ;) Thank you it works nows ;) – Haagenti Jan 28 '14 at 15:11
  • 11
    You can use the following in your .vimrc to enable it when editing xml files - ```au FileType xml setlocal equalprg=xmllint\ --format\ --recover\ -\ 2>/dev/null```. Source - http://ku1ik.com/2011/09/08/formatting-xml-in-vim-with-indent-command.html – studgeek Jul 22 '14 at 22:30
  • 1
    Thank you! Very frustrated with large XML files and vim, in Sublime they reindents very quickly (5000-10000 lines and base64 data), but in Vim they reindents several minutes. This solution fix problem with large XML files. Again I happy with Vim. – Sonique May 17 '15 at 17:33
  • 2
    if you are on ubuntu, you'll need to install the `libxml2-utils` package to get xmllint – hoffmanc Jul 24 '18 at 20:26
38

A simple solution that I like which doesn't require any 3rd party tool is to insert a newline before each opening tag '<...>'. Then you can use standard vim auto-indentation. In short:

  1. %s/</\r</g
  2. gg=G to auto indent
KFL
  • 17,162
  • 17
  • 65
  • 89
13

Short answer: Turn on matchit.vim. You can add packadd matchit to your .vimrc, or use vim-sensible, which enables it by default.

Long answer: I have tried many strategies to auto-indent XML in Vim, including the xmllint approach discussed on the Vim wiki. The problem with xmllint (and the same approach with other external CLI tools such as xmlformat and tidy, too) is that they all insist on squeezing out blank newlines. If all you want is to indent your XML, without removing blank lines, comments and/or other intentional formatting, then xmllint is not the best choice.

It turns out that Vim's equals (=) command already supports XML, as long as the matchit.vim script is enabled. One super easy way to enable it is to adopt tpope's vim-sensible. Another is to add packadd matchit to your .vimrc. Then, XML formatting will "just work" out of the box.

ctrueden
  • 6,751
  • 3
  • 37
  • 69
  • 1
    To be more precise, it looks like `matchit.vim` is the actual solution. You can enable it in your `vimrc` by adding this line: `packadd matchit` – Christian Rondeau Feb 13 '17 at 22:05
  • 1
    Thanks Christian; I updated the answer to clarify that matchit.vim is what solves the problem. – ctrueden Mar 29 '17 at 16:01
  • Upvoted because this worked best for me. However, I would suggest adding Christian's suggestion (packadd matchit) to the short answer as it is very simple and does the job (at least for me) – mark sabido Apr 23 '18 at 09:57
  • @mark I updated the answer to include Christian's suggestion. – ctrueden Apr 25 '18 at 02:52
  • Hmm - so, what's the exact command to format an existing xml file without any line breaks using this approach? I've tried `gg=G` which did nothing. To be precise: i don't want to indent a single line correctly, i want to pretty-print the entire XML file (btw: the xmllint command did the trick, but i want to understand why the matchit.vim approach or vim-sensible didn't work). Any help would be greatly appreciated... – tohuwawohu Sep 01 '20 at 12:07
  • 1
    @tohuwawohu Use the `=` command. If you are at the top of the file, you can type `=G`. Alternately, you can go to the top of the file with `1G`, enter visual line select mode with `V`, then type `G` to go to the bottom of the file, selecting everything. And then press `=` to indent the whole file. Similarly, if you only want to indent part of a file, select only that part, and press `=`. – ctrueden Sep 02 '20 at 15:56
  • 1
    @tohuwawohu I just realized you said "without any line breaks". The `=` command won't insert (or remove) line breaks that aren't already present. It only affects leading indentation. So `matchit.vim` is not a solution for pretty-printing XML that's all squished onto one line. Use `xmllint` or similar for that. – ctrueden Sep 02 '20 at 17:34
11

This is easy to achieve once ~/.vimrc is setup properly.

Set the following options in ~/.vimrc:

filetype indent on
set smartindent

This allows you to move the cursor to the top of the file and indent to the end: gg=G

You can also select the desired text with visual mode and hit = to indent it.

jarederaj
  • 1,350
  • 13
  • 18
9

Jesse Hogan's answer is good, but I'd rather more simple answer:

While you are in vim and your XML file is opened, in normal mode write this:

:%!xmllint --format %

then press Enter. All of your XML file will be formatted.

remember you should have installed xmllint before.

Cheetah Coder
  • 149
  • 2
  • 5
5

In VIM, I auto-indent the whole file (using hard tabs) using the following command:

:%!XMLLINT_INDENT="^I" xmllint --format -

The ^I is a single character you generate via: CTRL+v,CTRL+i

Cloud
  • 18,753
  • 15
  • 79
  • 153
1

I had the same problem. It turned out, that the line

set viewdir=~\.vim\views\

in my .vimrc caused the problem. Just make sure, you don't have it.

Boris Brodski
  • 8,425
  • 4
  • 40
  • 55
0

for those one which is using coc.nvim plugin, you can install coc-xml by :CocInstall coc-xml , then mapping format key in your config file: nmap <silent> fm <Plug>(coc-format). From now on, you can format not only xml file but other file very easy

lehanh
  • 569
  • 7
  • 22
0

Cannot get coc-xml working due to some java issue. Turns out using python's xml module could be a quick solution given many modern systems already come with python. Add below to your .vimrc

com! FormatXML :%!python3 -c "import xml.dom.minidom, sys; print(xml.dom.minidom.parse(sys.stdin).toprettyxml())"

Now you can do :FormatXML from command mode. Then set syntax highlight with

:set ft=xml
# OR
:set syntax=xml

Bonus: since I was doing this with SAML you can quickly decode base64 inside vim/neovim by selecting the base64 text(visual mode) and do !base64 -d in command mode. it will show as below in command:

:'<,'>!base64 -d

LeOn - Han Li
  • 9,388
  • 1
  • 65
  • 59