You are viewing gareth_rees

Gareth Rees - Smart quotes in Emacs 22


Previous Entry Share Next Entry
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.


[User Picture]
Date:2007‒10‒16T23:44 (UTC)
I'm ashamed to say that I don't understand the value you have given to left-smart-quote-context.
I seem to be running 21.3. I'll upgrade the next time I do an OS upgrade.
(Replies frozen) (Thread)
[User Picture]
Date:2007‒10‒17T09:41 (UTC)

See the Emacs manual, “Backslash in Regular Expressions”.

\s- Match any character belonging to syntax class - (whitespace). Emacs Lisp manual, “Table of Syntax Classes”.
\| Alternation.
\` Matches the empty string, but only at the beginning of the string or buffer.
(Replies frozen) (Parent) (Thread)
[User Picture]
Date:2007‒10‒17T11:53 (UTC)
Oh, OK. Silly me. I've been spoiled by regexps in systems where you don't have to escape everything twice!
That's really simple.
(Replies frozen) (Parent) (Thread)
[User Picture]
Date:2007‒10‒17T12:30 (UTC)

Yes, Python’s r"..." strings are a nice feature. But Emacs has a symbolic regular expression module, which is pretty cool too:

(require 'sregex)
(sregexq (or (syntax ?-) bot))      ;-(  bot = "beginning of text", not "bottom"
     => "\\(?:\\s-\\|\\`\\)"
(Replies frozen) (Parent) (Thread)
[User Picture]
Date:2007‒10‒20T14:05 (UTC)
After trying this out for a while, I reckon left-smart-quote-context also needs to include "\\s(" (that is, “syntax class: open parenthesis character”).
(Replies frozen) (Thread)
[User Picture]
Date:2009‒10‒29T20:13 (UTC)

Emacs 23

Also works in Emacs 23. Thanks!
(Replies frozen) (Thread) Powered by