diff --git a/ftplugin/xml.vim b/ftplugin/xml.vim
index 59bb03d..707c23d 100644
--- a/ftplugin/xml.vim
+++ b/ftplugin/xml.vim
@@ -556,6 +556,59 @@ if !exists("*s:MoveCursor")
endfunction
endif
+" XML formatter
+if !exists("*s:FormatXML")
+ function! s:FormatXML() range
+ " Save the file type
+ let l:origft = &ft
+
+ " Clean the file type
+ set ft=
+
+ " Add fake initial tag (so we can process multiple top-level elements)
+ exe ":let l:beforeFirstLine=" . a:firstline . "-1"
+ if l:beforeFirstLine < 0
+ let l:beforeFirstLine=0
+ endif
+ exe a:lastline . "put =''"
+ exe l:beforeFirstLine . "put =''"
+ exe ":let l:newLastLine=" . a:lastline . "+2"
+ if l:newLastLine > line('$')
+ let l:newLastLine=line('$')
+ endif
+
+ " Remove XML header
+ exe ":" . a:firstline . "," . a:lastline . "s/<\?xml\\_.*\?>\\_s*//e"
+
+ " Recalculate last line of the edited code
+ let l:newLastLine=search('')
+
+ " Execute external formatter
+ exe ":silent " . a:firstline . "," . l:newLastLine . "!xmllint --noblanks --format --recover -"
+
+ " Recalculate first and last lines of the edited code
+ let l:newFirstLine=search('')
+ let l:newLastLine=search('')
+
+ " Get inner range
+ let l:innerFirstLine=l:newFirstLine+1
+ let l:innerLastLine=l:newLastLine-1
+
+ " Remove extra unnecessary indentation
+ exe ":silent " . l:innerFirstLine . "," . l:innerLastLine "s/^ //e"
+
+ " Remove fake tag
+ exe l:newLastLine . "d"
+ exe l:newFirstLine . "d"
+
+ " Put the cursor at the first line of the edited code
+ exe ":" . l:newFirstLine
+
+ " Restore the file type
+ exe "set ft=" . l:origft
+ endfunction
+endif
+
" Mappings and Settings. {{{1
" This makes the '%' jump between the start and end of a single tag.
setlocal matchpairs+=<:>
@@ -599,6 +652,10 @@ nnoremap w :call ClearJumpMarks()
" The syntax files clear out any predefined syntax definitions. Recreate
" this when ever a xml_jump_string is created. (in ParseTag)
+" Format XML
+nnoremap x :call FormatXML()
+vnoremap x :call FormatXML()
+
augroup xml
au!
au BufNewFile * call NewFileXML()