gareth_rees (gareth_rees) wrote,

Smart quotes in Emacs 22

Support for U+2018 and friends seems pretty widespread these days, so I think it’s time for this:

;;; smart-quotes.el --- Smart Quotes mode for Emacs

;; Copyright (C) 2007 Gareth Rees

;; Author: Gareth Rees <>
;; Created: 2007-10-20
;; Version: 1.0
;; Keywords: abbrev

;; Smart Quotes mode is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or (at
;; your option) any later version.

;;; Commentary:

;; In Smart Quotes minor mode, the ' and \" keys insert left and
;; right quotation marks according to the context around point.

;;; Code:

(defcustom smart-quotes-left-context "\\s-\\|\\s(\\|^"
  "Regular expression matching the context in which a left
quotation mark will be inserted (a right quotation mark will
be inserted in all other contexts)."
  :type 'regexp)

(defun smart-quotes-insert-single ()
  "Insert U+2018 LEFT SINGLE QUOTATION MARK if point is preceded
by `smart-quotes-left-context'; U+2019 RIGHT SINGLE QUOTATION MARK
  (ucs-insert (if (looking-back smart-quotes-left-context) #x2018 #x2019)))

(defun smart-quotes-insert-double ()
  "Insert U+201C LEFT DOUBLE QUOTATION MARK if point is preceded
by `smart-quotes-left-context'; U+201D RIGHT DOUBLE QUOTATION MARK
  (ucs-insert (if (looking-back smart-quotes-left-context) #x201C #x201D)))

(define-minor-mode smart-quotes-mode
  "Toggle Smart Quotes mode in the current buffer.
With argument ARG, turn Smart Quotes mode on iff ARG is positive.
In Smart Quotes mode, the ' and \" keys insert left quotation
marks if point is preceded by text matching the option
`smart-quotes-left-context' and right quotation marks otherwise."
  :lighter (:eval (string ?  (decode-char 'ucs #x201C)
                          (decode-char 'ucs #x201D)))
  :keymap '(("'" . smart-quotes-insert-single)
            ("\"" . smart-quotes-insert-double)))

(provide 'smart-quotes)

ucs-insert and looking-back are new in Emacs 22. In later releases of Emacs 21 I think you can probably get away with the following:

(defun ucs-insert (c)
  "Insert the character whose Unicode code point is C."
  (insert (decode-char 'ucs c)))

(defun looking-back (regexp)
  "Return non-nil if text before point matches regular expression REGEXP."
  (and (save-excursion (re-search-backward regexp nil t))
       (= (match-end 0) (point))))

But if you’re editing text in a wide range of character sets, you probably want to upgrade to Emacs 22 anyway, as there are lots of improvements to international character support.


Comments for this post were locked by the author