Emacs: Convert Straight/Curly Quotes

By Xah Lee. Date: . Last updated: .

Here's a command to convert straight quote to curly quotes.

(defun xah-replace-straight-quotes (@begin @end)
  "Replace straight double quotes to curly ones, and others.
Works on current text block or selection.

Examples of changes:
 「\"…\"」 → 「“…”」
 「...」 → 「…」
 「I’m」 → 「I'm」
 「--」 → 「—」
 「~=」 → 「≈」

When called in lisp code, @begin and @end are region begin/end positions.

WARNING: this command does not guarantee 100% correct conversion of quotes, because it impossible. You should double check highlighted places after.

URL `http://ergoemacs.org/emacs/elisp_straight_curly_quotes.html'
Version 2018-03-02"
  ;; some examples for debug
  ;; do "‘em all -- done..."
  ;; I’am not
  ;; said "can’t have it, can’t, just can’t"
  ;; ‘I’ve can’t’
  (interactive
   (if (use-region-p)
       (list (region-beginning) (region-end))
     (let ($p1 $p2)
       (save-excursion
         (if (re-search-backward "\n[ \t]*\n" nil "move")
             (progn (re-search-forward "\n[ \t]*\n")
                    (setq $p1 (point)))
           (setq $p1 (point)))
         (if (re-search-forward "\n[ \t]*\n" nil "move")
             (progn (re-search-backward "\n[ \t]*\n")
                    (setq $p2 (point)))
           (setq $p2 (point))))
       (list $p1 $p2))))

  (let ( (case-fold-search nil))
    (save-excursion
      (save-restriction
        (narrow-to-region @begin @end )
        ;; Note: order is important since this is huristic.
        (xah-replace-pairs-region
         (point-min) (point-max)
         [
          ;; dash and ellipsis etc
          ["--" " — "]
          ["—" " — "]
          ["..." "…"]
          [" & " " & "]
          [" :)" " ☺"]
          [" :(" " ☹"]
          [" ;)" " 😉"]
          ["~=" "≈"]
          [" --> " " ⟶ "]
          [" , " ", "]
          ;; fix GNU style ASCII quotes
          ["``" "“"]
          ["''" "”"]
          ;; double straight quote → double curly quotes
          ["\n\"" "\n“"]
          [">\"" ">“"]
          ["(\"" "(“"]
          [" \"" " “"]
          ["\" " "” "]

          ["\", " "”, "]
          ["\",\n" "”,\n"]

          ["\". " "”. "]
          ["\".\n" "”.\n"]
          ["\"?" "”?"]
          ["\";" "”;"]
          ["\":" "”:"]
          ["\")" "”)"]
          ["\"]" "”]"]

          ;; ["\"[" "\”["]

          [".\"" ".”"]
          [",\"" ",”"]
          ["!\"" "!”"]
          ["?\"" "?”"]
          ["\"<" "”<"]
          ["\"\n" "”\n"]
          ] "REPORT" "HILIGHT")

        (xah-replace-pairs-region
         (point-min) (point-max)
         [
          ["  —  " " — "] ; rid of extra space in em-dash
          ] "REPORT" "HILIGHT")

        (xah-replace-pairs-region
         (point-min) (point-max)
         [
          [" —-> " " ⟶ "]
          [" <= " " ≤ "]
          [" >= " " ≥ "]
          ] "REPORT" "HILIGHT")

        ;; fix straight double quotes by regex
        (xah-replace-regexp-pairs-region
         (point-min) (point-max)
         [
          ["\\`\"" "“"]
          ] "FIXEDCASE" "LITERAL-P" "HILIGHT")

        ;; fix single quotes to curly
        (xah-replace-pairs-region
         (point-min) (point-max)
         [
          [">\'" ">‘"]
          [" \'" " ‘"]
          ["\' " "’ "]
          ["\'," "’,"]
          [".\'" ".’"]
          ["!\'" "!’"]
          ["?\'" "?’"]
          ["(\'" "(‘"]
          ["\')" "’)"]
          ["\']" "’]"]
          ] "REPORT" "HILIGHT")

        (xah-replace-regexp-pairs-region
         (point-min) (point-max)
         [
          ["\\bcan’t\\b" "can't"]
          ["\\bdon’t\\b" "don't"]
          ["\\bdoesn’t\\b" "doesn't"]
          ["\\bwon’t\\b" "won't"]
          ["\\bisn’t\\b" "isn't"]
          ["\\baren’t\\b" "aren't"]
          ["\\bain’t\\b" "ain't"]
          ["\\bdidn’t\\b" "didn't"]
          ["\\baren’t\\b" "aren't"]
          ["\\bwasn’t\\b" "wasn't"]
          ["\\bweren’t\\b" "weren't"]
          ["\\bcouldn’t\\b" "couldn't"]
          ["\\bshouldn’t\\b" "shouldn't"]

          ["\\b’ve\\b" "'ve"]
          ["\\b’re\\b" "'re"]
          ["\\b‘em\\b" "'em"]
          ["\\b’ll\\b" "'ll"]
          ["\\b’m\\b" "'m"]
          ["\\b’d\\b" "'d"]
          ["\\b’s\\b" "'s"]
          ["s’ " "s' "]
          ["s’\n" "s'\n"]

          ["\"$" "”"]
          ] "FIXEDCASE" "LITERAL-P" "HILIGHT")

        ;; fix back escaped quotes in code
        (xah-replace-pairs-region
         (point-min) (point-max)
         [
          ["\\”" "\\\""]
          ["\\”" "\\\""]
          ] "REPORT" "HILIGHT")

        ;; fix back. quotes in HTML code
        (xah-replace-regexp-pairs-region
         (point-min) (point-max)
         [
          ["” \\([-a-z]+\\)="       "\" \\1="] ; any 「” some-thing=」
          ["=”" "=\""]
          ["/” " "/\" "]
          ["\\([0-9]+\\)” "     "\\1\" "]
          ] "FIXEDCASE" nil "HILIGHT"
         )

        ))))

You need the elisp library Emacs: xah-replace-pairs.el Multi-Pair Find Replace.

Emacs Text Transform Under Cursor

  1. Toggle Letter Case
  2. Change to Title Case
  3. Upcase Sentences
  4. Cycle Replace Space Hyphen Underscore
  5. Remove Accent Marks
  6. Escape Quotes Command
  7. Spaces to New Lines
  8. Quote Lines
  9. Change Brackets/Quotes
  10. CSS Compressor
  11. Replace Greek Letter Names to Unicode
  12. Convert Straight/Curly Quotes
  13. Convert Full-Width/Half-Width Punctuations
  14. Lines to HTML Table
Patreon me $5 patreon

Or Buy Xah Emacs Tutorial

Or buy a nice keyboard: Best Keyboard for Emacs

If you have a question, put $5 at patreon and message me.