77

When editing HTML in emacs, is there a way to automatically pretty-format a blob of markup, changing something like this:

  <table>
  <tr>
<td>blah</td></tr></table>

...into this:

<table>
 <tr>
  <td>
   blah
  </td>
 </tr>
</table>
raldi
  • 21,344
  • 33
  • 76
  • 86

10 Answers10

121

You can do sgml-pretty-print and then indent-for-tab on the same region/buffer, provided you are in html-mode or nxml-mode.

sgml-pretty-print adds new lines to proper places and indent-for-tab adds nice indentation. Together they lead to properly formatted html/xml.

vava
  • 24,851
  • 11
  • 64
  • 79
31

By default, when you visit a .html file in Emacs (22 or 23), it will put you in html-mode. That is probably not what you want. You probably want nxml-mode, which is seriously fancy. nxml-mode seems to only come with Emacs 23, although you can download it for earlier versions of emacs from the nXML web site. There is also a Debian and Ubuntu package named nxml-mode. You can enter nxml-mode with:

M-x nxml-mode

You can view nxml mode documentation with:

C-h i g (nxml-mode) RET

All that being said, you will probably have to use something like Tidy to re-format your xhtml example. nxml-mode will get you from

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head></head>
<body>
<table>
  <tr>
<td>blah</td></tr></table>
</body>

to

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head></head>
  <body>
    <table>
      <tr>
    <td>blah</td></tr></table>
</body>
</html>

but I don't see a more general facility to do line breaks on certain xml tags as you want. Note that C-j will insert a new line with proper indentation, so you may be able to do a quick macro or hack up a defun that will do your tables.

Adobe
  • 12,967
  • 10
  • 85
  • 126
jfm3
  • 36,964
  • 10
  • 32
  • 35
  • 12
    After I enter nxml-mode, I use C-x h to select the whole region, then M-x 'indent-region'. You will get correctly indented HTML. Thanks to nxml-mode! – zhanxw Feb 23 '13 at 18:00
  • 2
    nxml-mode doesn't indent properly for html fragments, probably because they don't have a doctype. html-mode works fine once you get the line breaks in as suggested by @nevcx below – boatcoder Dec 14 '13 at 19:43
14

http://www.delorie.com/gnu/docs/emacs/emacs_277.html

After selecting the region you want to fix. (To select the whole buffer use C-x h)

C-M-q

Reindent all the lines within one parenthetical grouping(indent-sexp).

C-M-\

Reindent all lines in the region (indent-region).

JMFR
  • 799
  • 5
  • 18
Jay
  • 41,768
  • 14
  • 66
  • 83
10

i wrote a function myself to do this for xml, which works well in nxml-mode. should work pretty well for html as well:

(defun jta-reformat-xml ()
  "Reformats xml to make it readable (respects current selection)."
  (interactive)
  (save-excursion
    (let ((beg (point-min))
          (end (point-max)))
      (if (and mark-active transient-mark-mode)
          (progn
            (setq beg (min (point) (mark)))
            (setq end (max (point) (mark))))
        (widen))
      (setq end (copy-marker end t))
      (goto-char beg)
      (while (re-search-forward ">\\s-*<" end t)
        (replace-match ">\n<" t t))
      (goto-char beg)
      (indent-region beg end nil))))
jtahlborn
  • 52,909
  • 5
  • 76
  • 118
9

This question is quite old, but I wasn't really happy with the various answers. A simple way to re-indent an HTML file, given that you are running a relatively newer version of emacs (I am running 24.4.1) is to:

  • open the file in emacs
  • mark the entire file with C-x h (note: if you would like to see what is being marked, add (setq transient-mark-mode t) to your .emacs file)
  • execute M-x indent-region

What's nice about this method is that it does not require any plugins (Conway's suggestion), it does not require a replace regexp (nevcx's suggestion), nor does it require switching modes (jfm3's suggestion). Jay's suggestion is in the right direction — in general, executing C-M-q will indent according to a mode's rules — for example, C-M-q works, in my experience, in js-mode and in several other modes. But neither html-mode nor nxml-mode do not seem to implement C-M-q.

abhillman
  • 3,942
  • 1
  • 20
  • 21
  • 1
    Great answer. Two lines. Done all the job perfectly! – Tony Jan 06 '15 at 16:26
  • Follow up question, your solution won't indent the javascript in the html. Do you have any easy and fast solution for that? Thanks. – Tony Jan 06 '15 at 16:30
  • 1
    This works if the line breaks are already in the right places. sgml-pretty-print helps with that (but in my view, still not making the best choices). – pnj Jan 27 '15 at 13:51
9

In emacs 25, which I'm currently building from source, assuming you are in HTML mode, use
Ctrl-x
h

to select all, and then press Tab.

Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
8

You can do a replace regexp

 M-x replace-regexp

 \(</[^>]+>\)

 \1C-q-j

Indent the whole buffer

 C-x h
 M-x indent-region
nevcx
  • 81
  • 1
  • 1
6

Tidy can do what you want, but only for whole buffer it seems (and the result is XHTML)

M-x tidy-buffer
Chris Conway
  • 55,321
  • 43
  • 129
  • 155
4

You can pipe a region to xmllint (if you have it) using:

M-|
Shell command on region: xmllint --format -

The result will end up in a new buffer.

I do this with XML, and it works, though I believe xmllint needs certain other options to work with HTML or other not-perfect XML. nxml-mode will tell you if you have a well-formed document.

Geoff
  • 465
  • 3
  • 5
  • And with a prefix argument, `shell-command-on-region` replaces the original region with the shell command's output. i.e.: `C-u M-|` – phils Sep 15 '11 at 23:15
  • Thanks, phils, I knew that feature was there, but couldn't remember how to invoke it! – Geoff Sep 19 '11 at 21:06
2

The easiest way to do it is via command line.

  • Make sure you have tidy installed
  • type tidy -i -m <<file_name>>

Note that -m option replaces the newly tidied file with the old one. If you don't want that, you can type tidy -i -o <<tidied_file_name>> <<untidied_file_name>>

The -i is for indentation. Alternatively, you can create a .tidyrc file that has settings such as

indent: auto
indent-spaces: 2
wrap: 72
markup: yes
output-xml: no
input-xml: no
show-warnings: yes
numeric-entities: yes
quote-marks: yes
quote-nbsp: yes
quote-ampersand: no
break-before-br: no
uppercase-tags: no
uppercase-attributes: no

This way all you have to do is type tidy -o <<tidied_file_name>> <<untidied_file_name>>.

For more just type man tidy on the command line.

user1454331
  • 166
  • 1
  • 2