;;; my-replace-zen-to-ascii.el --- Convert fullwidth/superscript/subscript chars to ASCII -*- lexical-binding: t; -*- ;; This library converts selected fullwidth ASCII-like characters, ;; Unicode superscript/subscript digits, and common typographic symbols ;; into plain ASCII characters. ;; ;; Main commands: ;; ;; M-x replace-zen-to-ascii-region ;; M-x replace-zen-to-ascii-buffer ;; M-x replace-zen-to-ascii-region-copy ;; M-x replace-zen-to-ascii-buffer-copy ;; ;; The original command name `replace-zen-to-ascii-region' is kept ;; for compatibility with existing init.el settings. (require 'subr-x) (defgroup my-replace-zen-to-ascii nil "Convert selected fullwidth and Unicode compatibility characters to ASCII." :group 'editing :prefix "my-replace-zen-to-ascii-") (defcustom my-replace-zen-to-ascii-convert-scientific-symbols nil "If non-nil, also convert selected scientific symbols to ASCII. For example, μ/µ -> u, Å/Å -> A, × -> x, ° -> deg. This is disabled by default because these symbols may be meaningful in manuscripts." :type 'boolean :group 'my-replace-zen-to-ascii) (defconst my-replace-zen-to-ascii--basic-table '((" " . " ") ;; Fullwidth digits ("0" . "0") ("1" . "1") ("2" . "2") ("3" . "3") ("4" . "4") ("5" . "5") ("6" . "6") ("7" . "7") ("8" . "8") ("9" . "9") ;; Fullwidth uppercase letters ("A" . "A") ("B" . "B") ("C" . "C") ("D" . "D") ("E" . "E") ("F" . "F") ("G" . "G") ("H" . "H") ("I" . "I") ("J" . "J") ("K" . "K") ("L" . "L") ("M" . "M") ("N" . "N") ("O" . "O") ("P" . "P") ("Q" . "Q") ("R" . "R") ("S" . "S") ("T" . "T") ("U" . "U") ("V" . "V") ("W" . "W") ("X" . "X") ("Y" . "Y") ("Z" . "Z") ;; Fullwidth lowercase letters ("a" . "a") ("b" . "b") ("c" . "c") ("d" . "d") ("e" . "e") ("f" . "f") ("g" . "g") ("h" . "h") ("i" . "i") ("j" . "j") ("k" . "k") ("l" . "l") ("m" . "m") ("n" . "n") ("o" . "o") ("p" . "p") ("q" . "q") ("r" . "r") ("s" . "s") ("t" . "t") ("u" . "u") ("v" . "v") ("w" . "w") ("x" . "x") ("y" . "y") ("z" . "z") ;; Fullwidth ASCII punctuation ("!" . "!") (""" . "\"") ("#" . "#") ("$" . "$") ("%" . "%") ("&" . "&") ("'" . "'") ("(" . "(") (")" . ")") ("*" . "*") ("+" . "+") ("," . ",") ("-" . "-") ("." . ".") ("/" . "/") (":" . ":") (";" . ";") ("<" . "<") ("=" . "=") (">" . ">") ("?" . "?") ("@" . "@") ("[" . "[") ("\" . "\\") ("]" . "]") ("^" . "^") ("_" . "_") ("`" . "`") ("{" . "{") ("|" . "|") ("}" . "}") ("~" . "~") ;; Curly quotes and common dash/minus variants ("“" . "\"") ("”" . "\"") ("„" . "\"") ("‟" . "\"") ("‘" . "'") ("’" . "'") ("‚" . "'") ("‛" . "'") ("−" . "-") ("–" . "-") ("—" . "-") ("―" . "-") ;; Superscript digits ("⁰" . "0") ("¹" . "1") ("²" . "2") ("³" . "3") ("⁴" . "4") ("⁵" . "5") ("⁶" . "6") ("⁷" . "7") ("⁸" . "8") ("⁹" . "9") ;; Subscript digits ("₀" . "0") ("₁" . "1") ("₂" . "2") ("₃" . "3") ("₄" . "4") ("₅" . "5") ("₆" . "6") ("₇" . "7") ("₈" . "8") ("₉" . "9") ;; Superscript signs and parentheses ("⁺" . "+") ("⁻" . "-") ("⁼" . "=") ("⁽" . "(") ("⁾" . ")") ;; Subscript signs and parentheses ("₊" . "+") ("₋" . "-") ("₌" . "=") ("₍" . "(") ("₎" . ")")) "Basic conversion table.") (defconst my-replace-zen-to-ascii--scientific-table '(("μ" . "u") ("µ" . "u") ("Å" . "A") ("Å" . "A") ("×" . "x") ("°" . " deg")) "Optional scientific-symbol conversion table.") (defun my-replace-zen-to-ascii--table () "Return active conversion table." (if my-replace-zen-to-ascii-convert-scientific-symbols (append my-replace-zen-to-ascii--basic-table my-replace-zen-to-ascii--scientific-table) my-replace-zen-to-ascii--basic-table)) (defun my-replace-zen-to-ascii--regexp () "Return regexp for active conversion table." (regexp-opt (mapcar #'car (my-replace-zen-to-ascii--table)))) (defun my-replace-zen-to-ascii--replace-region (start end) "Convert selected characters in region START END." (let* ((table (my-replace-zen-to-ascii--table)) (regexp (regexp-opt (mapcar #'car table)))) (save-excursion (goto-char start) (while (re-search-forward regexp end t) (let* ((from (match-string 0)) (to (cdr (assoc from table)))) (when to (replace-match to t t))))))) ;;;###autoload (defun replace-zen-to-ascii-region (start end) "Convert fullwidth ASCII-like and super/subscript characters in region to ASCII." (interactive "r") (my-replace-zen-to-ascii--replace-region start end) (message "Converted selected characters to ASCII.")) ;;;###autoload (defun replace-zen-to-ascii-buffer () "Convert fullwidth ASCII-like and super/subscript characters in the whole buffer." (interactive) (replace-zen-to-ascii-region (point-min) (point-max))) ;;;###autoload (defun replace-zen-to-ascii-region-copy (start end) "Copy converted region to kill-ring without modifying current buffer." (interactive "r") (let ((text (buffer-substring-no-properties start end)) (buf (generate-new-buffer " *replace-zen-to-ascii-copy*"))) (unwind-protect (with-current-buffer buf (insert text) (replace-zen-to-ascii-region (point-min) (point-max)) (kill-new (buffer-string))) (kill-buffer buf)) (message "Converted text copied to kill-ring."))) ;;;###autoload (defun replace-zen-to-ascii-buffer-copy () "Copy converted whole buffer to kill-ring without modifying current buffer." (interactive) (replace-zen-to-ascii-region-copy (point-min) (point-max))) ;;;###autoload (defun replace-zen-to-ascii-toggle-scientific-symbols () "Toggle optional scientific-symbol conversion." (interactive) (setq my-replace-zen-to-ascii-convert-scientific-symbols (not my-replace-zen-to-ascii-convert-scientific-symbols)) (message "Scientific-symbol conversion: %s" (if my-replace-zen-to-ascii-convert-scientific-symbols "ON" "OFF"))) (provide 'my-replace-zen-to-ascii) ;;; my-replace-zen-to-ascii.el ends here