Search code examples
emacshtml-modesgml-mode

Close HTML tags as soon as one opens them


I'd like the corresponding, closing HTML tag to be automatically inserted whenever I open one.

So if I type

<div>

I should get

<div></div>

Without having to call to sgml-close-tag myself.

How to achieve this?


Solution

  • Rather than calling a hook function after every single key-stroke, it makes sense to only call it after a > was typed. This can be achieved by rebinding the > character in the keymap that sgml-mode uses.

    In addition, sgml-close-tag shouldn't get called if the tag is already closed. Therefore, the following code adds a simple regexp check for that:

    (defun my-sgml-insert-gt ()
      "Inserts a `>' character and calls 
    `my-sgml-close-tag-if-necessary', leaving point where it is."
      (interactive)
      (insert ">")
      (save-excursion (my-sgml-close-tag-if-necessary)))
    
    (defun my-sgml-close-tag-if-necessary ()
      "Calls sgml-close-tag if the tag immediately before point is
    an opening tag that is not followed by a matching closing tag."
      (when (looking-back "<\\s-*\\([^</> \t\r\n]+\\)[^</>]*>")
        (let ((tag (match-string 1)))
          (unless (and (not (sgml-unclosed-tag-p tag))
               (looking-at (concat "\\s-*<\\s-*/\\s-*" tag "\\s-*>")))
        (sgml-close-tag)))))
    
    (eval-after-load "sgml-mode"
      '(define-key sgml-mode-map ">" 'my-sgml-insert-gt))