I looked at a number of different modes for authoring HTML, including HtmlMode, PsgmlMode, HtmlHelperMode, HtmlModeDeluxe, but I decided to use the wonderful NxmlMode instead. This has the advantage of providing validation as you type and excellent auto-completion facilities. There is a catch, though, and that is that you have to write XHTML (HTML expressed as a dialect of XML) rather than SGML or HTML). This is not really a big deal, but I thought I’d record my experience here.
To make it even more simple I wrote a new mode nxhtml-mode (which is now part of nXhtml). nxhtml-mode is derived from nxml-mode (NxmlMode). It adds a lot of things useful for XHTML. Among other things it adds some way of handling multiple modes, folding, an improved interface to Tidy etc. However, it requires Emacs22, while NxmlMode works in Emacs21 as well.
There are a few things you have to do differently if you are authoring web pages as XHTML instead of HTML:
<?xml version="1.0" encoding="utf-8"?> <!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" xml:lang="en"> <head>
All of this amounts to a very small change in my writing habits in exchange for a very large benefit of having an editor that prompts me as I type incorrect markup. In contrast to modes that require you to install DTDs, nxml-mode works for editing XHTML “out of the box”. To try it out, just install it and type M-x nxml-mode <RET> while editing an HTML file.
If you have html code and want to use xhtml instead, use tidy to fix it - see http://www.hollenback.net/index.php?EmacsTidy
This is also the way Tidy handles this.
You can use either comment style to hide the CDATA tags, but the style above works also for inlined CSS in IE 6.
MuMaMo works well out of the box with nXml mode and multiple major modes. It was released just yesterday (2007-04-04).
I’ve got MMM working along with nxml mode and Turadg’s PHP mode. As I see it, the problem is that nxml re-fontifies regions that have another submode (eg CSS, PHP). So when you want to fontify a PHP region, for example, you have to have MMM reparse the buffer (eg with a C-c % C-5) to get font locking for the submode. This is no more obtrusive than using an HtmlHelperMode paradigm; but it allows you to edit the submode region inline. See MmmAndNxmlScreenshot for an example where nxml is clobbering the fontification of everything but the submode region in which the point is under MMM. – DeanScarff
I made the following changes to get mmm-mode working with nxml (because nxml creates local copies of these parameters):
diff -c /Users/ptw/.emacs.d/site-lisp/mmm-vars.el.\~1\~ /Users/ptw/.emacs.d/site-lisp/mmm-vars.el *** /Users/ptw/.emacs.d/site-lisp/mmm-vars.el.~1~ Wed Jun 16 10:14:18 2004 --- /Users/ptw/.emacs.d/site-lisp/mmm-vars.el Thu Nov 23 09:46:31 2006 *************** *** 293,298 **** --- 293,309 ---- ;; Abbrev mode abbrev-mode local-abbrev-table + ;; nxml-mode makes these local + adaptive-fill-mode + forward-sexp-function + indent-line-function + fill-paragraph-function + comment-indent-function + comment-start + comment-start-skip + comment-end + comment-end-skip + comment-line-break-function ;; And finally the syntax table and local map. ((syntax-table . set-syntax-table)) ((current-local-map . use-local-map) buffer) Diff finished. Thu Nov 23 09:51:15 2006
The built-in folding capability of nxml-mode will not work with xhtml, since it requires sections with a beginning and end tag, whereas xhtml is ususally structured by means of <h1> tags, and so forth. You can easily add support for this using OutlineMode, or you might want to use HideShow to fold elements. I use both together; in my nxml-mode-hook, I have:
(when (string-match "\\.\\(x?html\\|php?\\)$" (file-name-sans-versions (buffer-file-name))) (my-xhtml-extras))
And here is the code:
(defun my-xhtml-extras () (make-local-variable 'outline-regexp) (setq outline-regexp "\\s *<\\([h][1-6]\\|html\\|body\\|head\\)\\b") (make-local-variable 'outline-level) (setq outline-level 'my-xhtml-outline-level) (outline-minor-mode 1) (hs-minor-mode 1))
(defun my-xhtml-outline-level () (save-excursion (re-search-forward html-outline-level)) (let ((tag (buffer-substring (match-beginning 1) (match-end 1)))) (if (eq (length tag) 2) (- (aref tag 1) ?0) 0)))
(add-to-list 'hs-special-modes-alist '(nxml-mode "<!--\\|<[^/>]>\\|<[^/][^>]*[^/]>" "" "<!--" ;; won't work on its own; uses syntax table (lambda (arg) (my-nxml-forward-element)) nil))
(defun my-nxml-forward-element () (let ((nxml-sexp-element-flag)) (setq nxml-sexp-element-flag (not (looking-at "<!--"))) (unless (looking-at outline-regexp) (condition-case nil (nxml-forward-balanced-item 1) (error nil)))))
See also FoldDwim for a convenient interface to all this, and to nxml-mode’s folding, too.
When the webpage is structured using HTML5 ‘section’ and ‘article’ elements, the native xnml support for outlines may be used for folding. It is however necessary to tell the outline feature to use the ‘h[1-6]’ headers as header elements and the ‘section’, ‘article’, ‘aside’ and ‘nav’ elements as sectioning elements (and ignore the ‘div’ element).
(defun nxml-html-outline() (interactive) "Sets up the outline mode for XHTML5 for this buffer" (make-local-variable 'nxml-section-element-name-regexp) (setq nxml-section-element-name-regexp "head\\|body\\|blockquote\\|details\\|fieldset\\|figure\\|td\\|section\\|article\\|nav\\|aside") (make-local-variable 'nxml-heading-element-name-regexp) (setq nxml-heading-element-name-regexp "title\\|h[1-9]") ) (defun my-xnml-hook() (when (string-match ".x?html$") (nxml-html-outline) ) ) (add-hook 'nxml-mode-hook 'my-nxml-hook)
One limitation is that it doesn’t recognise multiple ‘h[1-6]’ in a ‘header’ element and doesn’t work with implicit sections.