;;; eev-compose-hash.el --- `M-,' as a compose key (version with hash tables) -*- lexical-binding: nil; -*- ;; Copyright (C) 2021 Free Software Foundation, Inc. ;; ;; This file is part of GNU eev. ;; ;; GNU eev 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 3 of the License, or ;; (at your option) any later version. ;; ;; GNU eev is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;; ;; Author: Eduardo Ochs ;; Maintainer: Eduardo Ochs ;; Version: 20211216 ;; Keywords: e-scripts ;; ;; Latest version: ;; htmlized: ;; See also: ;; ;; ;; (find-eev-intro) ;; (find-here-links-intro) ;;; Commentary: ;; This is a new, experimental feature of eev that is not loaded by ;; default and that has been tested by very few people. I mentioned ;; it on help-gnu-emacs in this thread: ;; ;; https://lists.gnu.org/archive/html/help-gnu-emacs/2021-12/msg00173.html ;; https://lists.gnu.org/archive/html/help-gnu-emacs/2021-12/threads.html#00173 ;; ;; Its advantages over the official input methods are: ;; ;; 1. People can learn to use this very quickly and learn input ;; methods later - see the msg00173 above. ;; ;; 2. Each unicode character can be associated to a face and a ;; color, like the red stars and green "«»"s used by eev: ;; ;; (find-eev-intro "1. `eev-mode'") ;; (find-eev-intro "1. `eev-mode'" "colored glyphs") ;; http://angg.twu.net/eev-intros/find-eev-intro.html#1 ;; (find-eev-quick-intro "6.4. Red stars") ;; http://angg.twu.net/eev-intros/find-eev-quick-intro.html#6.4 ;; ;; 3. These faces can be turned on and off. I usually keep them on ;; almost all the time, and turn them off only for editing Agda ;; code. ;; ;; 4. New characters are easy to add. ;; ;; 5. Each unicode character can be associated to LaTeX code. I use ;; lualatex most of the time but when I have to prepare a file to ;; be uploaded to Arxiv I have to make it compilable with both ;; lualatex and pdflatex; this file includes hacks that generates ;; "definitions in lualatex" and "definitions in pdflatex" for ;; all the unicode characters that have LaTeX code associated to ;; them. See: ;; ;; (find-LATEX "edrx21chars.tex") ;; (find-LATEX "edrx21chars-d.tex") ;; (find-LATEX "2021composes-hash.tex") ;; http://angg.twu.net/LATEX/edrx21chars.tex.html ;; http://angg.twu.net/LATEX/edrx21chars-d.tex.html ;; http://angg.twu.net/LATEX/2021composes-hash.tex.html ;; ;; If you just want to test this, see: ;; ;; (find-LATEX "2021composes-hash.tex" "test") ;; http://angg.twu.net/LATEX/2021composes-hash.tex.html#test ;; ;; The disadvantages are obvious: this is non-standard, some parts of ;; the code are ugly and need to be rewritten, some names of functions ;; and variables will change, etc, etc. ;; «.ee-composes-do» (to "ee-composes-do") ;; «.to-strings» (to "to-strings") ;; «.compose» (to "compose") ;; «.faces» (to "faces") ;; «.unicode-characters» (to "unicode-characters") ;; «.bigstr-specs» (to "bigstr-specs") ;; Older docs: ;; ;; Greek letters: (find-einsert '((900 1000))) ;; Some mathematical characters: (find-einsert '((8592 9000))) ;; Some mathematical alphabets: ' (find-einsert '("Bold: " (119808 119859) "\n" "Italic: " (119860 119911) "\n" "Bold italic: " (119912 119963) "\n" "Script: " (119964 120015) "\n" "Bold script: " (120016 120067) "\n" "Fraktur: " (120068 120119) "\n" "Double-struck: " (120120 120171) "\n" "Bold Fraktur: " (120172 120223) "\n" "Sans-serif: " (120224 120275) "\n" "Sans-serif bold: " (120276 120327) "\n" "Sans-serif italic: " (120328 120379) "\n" "Sans-serif bold italic: " (120380 120431) "\n" "Monospace: " (120432 120483) "\n" "\n" (120484 120831))) ;; See: ;; ;; (find-equailfile "latin-ltx.el") ;; (find-equailfile "sgml-input.el") ;; (find-enode "Inserting Text" "C-x 8 left single") ;; (find-enode "International Chars" "C-x 8") ;; (find-enode "Input Methods") ;; ;; I needed something that was easier to understand and easier to ;; extend - in the sense of: adding new characters - than these ;; standard methods, and that would let me use one face for the ;; uppercase greek characters, another for lowercase greek, another ;; for the standard mathematical chars, and so on... so in the late ;; 90s I devised a very simple hack in which `M-,' followed by two ;; characters would "compose" these two characters according to a ;; conversion table, and I also had a very, very ugly hack that ;; allowed me to specify this conversion table, AND THE FACES, in a ;; somewhat human-friendly format, as lists of big strings and face ;; names... That implementation used alists for the conversion tables, ;; and was very hard to maintain. ;; ;; The implemention in this file is much simpler - because is uses ;; hash tables for the conversion tables, and uses a big regexp built ;; with `rx' to specify how to parse the human-readable conversion ;; tables given in big strings... and also, as a bonus, it lets me ;; specify how the special characters are to defined in LaTeX, both ;; with `\catcode' and with `\DeclareUnicodeCharacter'. ;; ;; I decided to include this in eev because it MAY be useful in my ;; next workshops on LaTeX (+ Emacs + eev): people can learn this, ;; including how to extend it, in a few minutes, and then learn the ;; standard input methods later - in more than a few minutes. ;; ;; This is very-new-ish code. Everything may change - ESPECIALLY the ;; terminology and the names of the variables and functions. I am ;; currently using these terms: ;; ;; a "keys" means "a pair of easy-to-type characters" (that have an ;; unicode character associated to them), ;; ;; a "pos" means this unicode character, ;; ;; "composes" means "all the data structures that show how each ;; "keys" is converted to a "pos", how each "pos" is converted to ;; latex code, which face is associated to each pos, etc, etc, ;; ;; a "big string", or a "bigstr", is a string that may contain, and ;; usually contains, newlines, ;; ;; a "bigstr spec" is a big string that specifies how the "pos"s are ;; associates to "keys", to latex code, and to faces. ;; ;; Usage: ;; (load "eev-compose-hash.el") ;; (ee-composes-do ee-composes-bigstr-accents) ;; (ee-composes-do ee-composes-bigstr-otheriso) ;; (ee-composes-do ee-composes-bigstr-math) ;; (define-key eev-mode-map (kbd "M-,") 'ee-compose-pair) ;;; _ ;;; ___ ___ _ __ ___ _ __ ___ ___ ___ ___ __| | ___ ;;; / __/ _ \| '_ ` _ \| '_ \ / _ \/ __|/ _ \/ __|_____ / _` |/ _ \ ;;; | (_| (_) | | | | | | |_) | (_) \__ \ __/\__ \_____| (_| | (_) | ;;; \___\___/|_| |_| |_| .__/ \___/|___/\___||___/ \__,_|\___/ ;;; |_| ;; ;; «ee-composes-do» (to ".ee-composes-do") ;; (defun ee-composes-initialize () (setq ee-composes-keys-to-pos (make-hash-table :test 'equal)) (setq ee-composes-pos-to-latex (make-hash-table :test 'equal)) (setq ee-composes-current-face nil)) (ee-composes-initialize) ;; The regexp that is used to parse bigstrspecs. ;; The "4" stresses that it has 4 groups. ;; See: (find-es "emacs" "while-string-match") ;; (find-es "emacs" "rx") ;; (setq ee-composes-regexp4 (rx-let ((nonblank (not (any " \t\n")))) (rx (or (and (group-n 1 nonblank) ; 1: pos " " (group-n 2 nonblank nonblank) ; 2: keys (optional " " (group-n 3 nonblank (zero-or-more nonblank))) ; 3: latex ) (and "face: " (group-n 4 (one-or-more nonblank))) ; 4: face (and ";; " (zero-or-more (not "\n"))) ; comment )))) (defun ee-composes-do (bigstrspec &optional action) "Parse BIGSTRSPEC and eval ACTION for each parseable part. The default ACTION is (ee-composes-do-default)." (setq action (or action '(ee-composes-do-default))) (let ((bigstrspecpos 0)) (while (string-match ee-composes-regexp4 bigstrspec bigstrspecpos) (let ((pos (match-string 1 bigstrspec)) (keys (match-string 2 bigstrspec)) (latex (match-string 3 bigstrspec)) (face (match-string 4 bigstrspec))) (eval action) (setq bigstrspecpos (match-end 0)))))) (defun ee-composes-do-default () "This is the default action for `ee-composes-do'." (if pos (puthash keys pos ee-composes-keys-to-pos)) (if pos (eepitch-set-glyph0 (aref pos 0) (aref pos 0) ee-composes-current-face)) (if latex (puthash pos latex ee-composes-pos-to-latex)) (if face (setq ee-composes-current-face (read face))) ) (defun ee-composes-do-remove-face () "Experimental." (if pos (puthash keys pos ee-composes-keys-to-pos)) (if pos (eepitch-set-glyph0 (aref pos 0) (aref pos 0) 'default)) (if latex (puthash pos latex ee-composes-pos-to-latex)) (if face (setq ee-composes-current-face (read face))) ) (defun ee-composes-do-test () "An alternative action for `ee-composes-do', for tests." (insert (format "\n;; %S %S %S / %S" pos keys latex face))) ;; Low-level tests: ;; (load "eev-compose-hash.el") ;; (ee-composes-initialize) ;; (ee-composes-do "á 'a é 'e é !e EE") ;; (ee-composes-do "á 'a é 'e é !e EE" '(ee-composes-do-test)) ;; (let ((pos "á") (keys "'a") (face nil) (latex "aacu")) (ee-composes-do-default)) ;; (let ((pos "á") (keys "'a") (face nil) (latex "aacu")) (ee-composes-do-test)) ;; (find-estring (ee-composes-to-string)) ;;; _ _ _ ;;; | |_ ___ ___| |_ _ __(_)_ __ __ _ ___ ;;; | __/ _ \ / __| __| '__| | '_ \ / _` / __| ;;; | || (_) | \__ \ |_| | | | | | | (_| \__ \ ;;; \__\___/ |___/\__|_| |_|_| |_|\__, |___/ ;;; |___/ ;; ;; «to-strings» (to ".to-strings") ;; Functions to convert the hash tables to ;; several human-readable formats. ;; ;; Tests: (find-ehashtable ee-composes-pos-to-latex) ;; (find-ehashtable ee-composes-keys-to-pos) ;; (find-estring (ee-composes-to-string)) ;; (find-estring (ee-composes-to-catcodes)) ;; (find-estring (ee-composes-to-declareunicodes)) ;; (defun ee-composes-to-string () (ee-hashtable-to-string (lambda (keys pos ht-keys-to-pos) (let ((latex (gethash pos ee-composes-pos-to-latex))) (format "%s %s %s\n" pos keys (or latex "")))) ee-composes-keys-to-pos)) (defun ee-composes-to-catcodes () (ee-hashtable-to-string (lambda (pos latex ht-pos-to-latex) (format "\\catcode`%s=13 \\def%s{%s}\n" pos pos latex)) ee-composes-pos-to-latex)) (defun ee-composes-to-declareunicodes () (ee-hashtable-to-string (lambda (pos latex ht-pos-to-latex) (let* ((nnnn (format "%04X" (aref pos 0))) (decl (format "\\DeclareUnicodeCharacter{%s}{%s}" nnnn latex)) (line (format "%-50s %% %s\n" decl pos))) line)) ee-composes-pos-to-latex)) ;;; ;;; ___ ___ _ __ ___ _ __ ___ ___ ___ ;;; / __/ _ \| '_ ` _ \| '_ \ / _ \/ __|/ _ \ ;;; | (_| (_) | | | | | | |_) | (_) \__ \ __/ ;;; \___\___/|_| |_| |_| .__/ \___/|___/\___| ;;; |_| ;; ;; «compose» (to ".compose") (defun ee-compose-pair (&optional arg) "Read two keys and insert the result of their \"composition\". Use `ee-composes-keys-to-pos' to determine the composite char. When ARG is 0 show the current composes in a temporary buffer instead of inserting." (interactive "P") (if (eq arg 0) (find-estring (concat " See: (find-eev \"eev-compose-hash.el\")\n\n" (ee-composes-to-string))) (let* ((keys (format "%c%c" (read-event "Compose key 1: " t) (read-event "Compose key 2: " t))) (pos (gethash keys ee-composes-keys-to-pos))) (if (not pos) (error "Pair %S not in `ee-composes-keys-to-pos'" keys)) ;; ;; Should we insert N copies when ARG is N? (insert pos)))) ;; Tests: (eek "RET M-, 8 8") ;; (eek "M-0 M-,") ;;; __ ;;; / _| __ _ ___ ___ ___ ;;; | |_ / _` |/ __/ _ \/ __| ;;; | _| (_| | (_| __/\__ \ ;;; |_| \__,_|\___\___||___/ ;;; ;; «faces» (to ".faces") ;; Adapted from: ;; (find-anggfile "eev-current/eev-glyphs.el" "yellow-on-red") ;; (find-angg "eev-current/eev-math-glyphs.el" "faces") ;; Define several faces for glyphs in a few lines of code. ;; This is too rigid, but whatever. ;; (find-ecolors) ;; (find-efaces) ;; (find-efaces "ee-composes") ;; (defun ee-composes-set-face (face fg bg) (make-face face) (set-face-foreground face fg) (set-face-background face bg)) (ee-composes-set-face 'ee-composes-face-Greek "orange" "gray20") (ee-composes-set-face 'ee-composes-face-greek "coral" "gray20") (ee-composes-set-face 'ee-composes-face-logical "SteelBlue1" "gray20") (ee-composes-set-face 'ee-composes-face-math "RoyalBlue2" "gray20") (ee-composes-set-face 'ee-composes-face-linear "PaleVioletRed" "gray20") (ee-composes-set-face 'ee-composes-face-script "PaleVioletRed" "gray20") (ee-composes-set-face 'ee-composes-face-graphic "red" "gray20") (ee-composes-set-face 'ee-composes-face-category "red" "gray20") (ee-composes-set-face 'ee-composes-face-font "gold" "DarkOrange4") (ee-composes-set-face 'ee-composes-face-yellow-on-red "yellow" "red") ;;; _ _ _ ;;; | |__ (_) __ _ ___| |_ _ __ ___ _ __ ___ ___ ___ ;;; | '_ \| |/ _` / __| __| '__| / __| '_ \ / _ \/ __/ __| ;;; | |_) | | (_| \__ \ |_| | \__ \ |_) | __/ (__\__ \ ;;; |_.__/|_|\__, |___/\__|_| |___/ .__/ \___|\___|___/ ;;; |___/ |_| ;; ;; «unicode-characters» (to ".unicode-characters") ;; «bigstr-specs» (to ".bigstr-specs") ;; These are the default bigstr specs. ;; I uses setq instead of defvar because this is a hack!... (setq ee-composes-bigstr-accents " face: nil À `A È `E Ì `I Ò `O Ù `U à `a è `e ì `i ò `o ù `u Á 'A É 'E Í 'I Ó 'O Ú 'U á 'a é 'e í 'i ó 'o ú 'u  ^A Ê ^E Î ^I Ô ^O Û ^U â ^a ê ^e î ^i ô ^o û ^u à ~A Õ ~O ã ~a õ ~o Ä \"A Ë \"E Ï \"I Ö \"O Ü \"U ä \"a ë \"e ï \"i ö \"o ü \"u Ç 'C Ç CC Ñ ~N ç 'c ç cc ñ ~n ") (setq ee-composes-bigstr-otheriso " face: nil ª _a º _o Æ AE æ ae ß ss ¼ 14 ½ 12 ¾ 34 ¿ ?? ¡ !! \\text{\\textexclamdown} § SS \\S ° 00 ^\\circ ± +- \\pm ÷ :- \\div · cd \\cdot × xx \\times ¬ nt \\neg face: eev-glyph-face-green « << » >> face: ee-composes-face-math ² 22 \\superscriptTwo ³ 33 \\superscriptThree ¹ -1 \\superscriptOne ¹ 11 \\superscriptOne ") (setq ee-composes-bigstr-math " face: ee-composes-face-Greek Γ GG \\Gamma Δ DD \\Delta Θ Th \\Theta Λ La \\Lambda Π Pi \\Pi Σ Si \\Sigma Φ Ph \\Phi Ψ Ps \\Psi Ω Om \\Omega face: ee-composes-face-greek α aa \\alpha β bb \\beta γ gg \\gamma δ dd \\delta ε ee \\epsilon ε ee \\varepsilon ζ ze \\zeta η et \\eta θ th \\theta ι io \\iota κ kk \\kappa λ ll \\lambda μ mu \\mu ν nu \\nu ξ xi \\xi π pi \\pi ρ ro \\rho σ si \\sigma τ ta \\tau φ ph \\varphi χ ch \\chi ψ ps \\psi ω om \\omega ϕ vp \\phi face: ee-composes-face-math • bu \\bullet … .. \\ldots ℓ el \\ell ⅋ && \\bindnasrepma ← <- \\ot ↑ up \\upto → -> \\to → to ↓ dn \\dnto ↔ <> \\bij ↕ ud \\updownarrow ↖ NW \\nwarrow ↗ NE \\nearrow ↘ SE \\searrow ↙ SW \\swarrow ↣ >t \\twoheadrightarrow ↣ ep \\epito ↤ mo \\mapsfrom ↦ mt \\mapsto ⇀ hu \\rightharpoonup ⇐ <= \\Leftarrow ⇒ => \\funto ⇔ LR \\Leftrightarrow ⇝ s> \\rightsquigarrow ∀ fa \\forall ∂ Pa \\partial ∃ ex \\exists ∅ em \\emptyset ∇ Na \\nabla ∈ in \\in ∎ qe \\blacksquare ∖ sm \\backslash ∘ oo \\circ √ sq \\sqrt ∞ 88 \\infty ∧ la \\land ∨ lo \\lor ∩ ca \\cap ∪ cu \\cup ∫ In \\int ∼ 1~ \\sim ∼ ~1 \\sim ≃ -~ \\simeq ≅ =~ \\cong ≈ ~~ \\approx ≠ != \\neq ≡ == \\equiv ≤ le \\le ≥ ge \\ge ⊂ su \\subset ⊃ Su \\supset ⊆ se \\subseteq ⊇ Se \\supseteq ⊓ qa \\sqcap ⊔ qu \\sqcup ⊕ o+ \\oplus ⊖ o- \\ominus ⊗ ox \\otimes ⊘ o/ \\oslash ⊙ o. \\odot ⊢ |- \\vdash ⊣ -| \\dashv ⊤ TT \\top ⊥ BO \\bot ⊨ |= \\vDash ⊸ -o ⋀ LA \\bigwedge ⋁ LO \\bigvee ⋂ CA \\bigcap ⋃ CU \\bigcup ⋄ po \\lozenge ⋅ Do \\Box ⋮ vd \\vdots ⋯ c. \\cdots 〈 <1 \\langle 〉 1> \\rangle ▁ __ \\_ □ Bo \\Box ◻ Bo \\Box ◻ nc ♭ fl \\flat ♮ na \\natural ♯ sh \\sharp ✀ li ⟦ [[ \\llbracket ⟧ ]] \\rrbracket ⠆ :: {:} face: ee-composes-face-yellow-on-red 𝐛 bf \\mathbf 𝐢 it \\textsl 𝐫 rm \\mathrm 𝐬 sf \\mathsf 𝐭 tx \\text face: ee-composes-face-script 𝓐 sA \\mathcal{A} 𝓑 sB \\mathcal{B} 𝓒 sC \\mathcal{C} 𝓓 sD \\mathcal{D} 𝓔 sE \\mathcal{E} 𝓕 sF \\mathcal{F} 𝓖 sG \\mathcal{G} 𝓗 sH \\mathcal{H} 𝓘 sI \\mathcal{I} 𝓙 sJ \\mathcal{J} 𝓚 sK \\mathcal{K} 𝓛 sL \\mathcal{L} 𝓜 sM \\mathcal{M} 𝓝 sN \\mathcal{N} 𝓞 sO \\mathcal{O} 𝓟 sP \\mathcal{P} 𝓠 sQ \\mathcal{Q} 𝓡 sR \\mathcal{R} 𝓢 sS \\mathcal{S} 𝓣 sT \\mathcal{T} 𝓤 sU \\mathcal{U} 𝓥 sV \\mathcal{V} 𝓦 sW \\mathcal{W} 𝓧 sX \\mathcal{X} 𝓨 sY \\mathcal{Y} 𝓩 sZ \\mathcal{Z} ;; Categories face: ee-composes-face-category 𝐀 cA \\mathbf{A} 𝐁 cB \\mathbf{B} 𝐂 cC \\mathbf{C} 𝐃 cD \\mathbf{D} 𝐄 cE \\mathbf{E} 𝐅 cF \\mathbf{F} 𝐆 cG \\mathbf{G} 𝐇 cH \\mathbf{H} 𝐈 cI \\mathbf{I} 𝐉 cJ \\mathbf{J} 𝐊 cK \\mathbf{K} 𝐋 cL \\mathbf{L} 𝐌 cM \\mathbf{M} 𝐍 cN \\mathbf{N} 𝐎 cO \\mathbf{O} 𝐏 cP \\mathbf{P} 𝐐 cQ \\mathbf{Q} 𝐑 cR \\mathbf{R} 𝐒 cS \\mathbf{S} 𝐓 cT \\mathbf{T} 𝐔 cU \\mathbf{U} 𝐕 cV \\mathbf{V} 𝐖 cW \\mathbf{W} 𝐗 cX \\mathbf{X} 𝐘 cY \\mathbf{Y} 𝐙 cZ \\mathbf{Z} face: ee-composes-face-math ₀ _0 {{\\ensuremath{{}_0}}} ₁ _1 {{\\ensuremath{{}_1}}} ₂ _2 {{\\ensuremath{{}_2}}} ₃ _3 {{\\ensuremath{{}_3}}} ₄ _4 {{\\ensuremath{{}_4}}} ₅ _5 {{\\ensuremath{{}_5}}} ₆ _6 {{\\ensuremath{{}_6}}} ₇ _7 {{\\ensuremath{{}_7}}} ₈ _8 {{\\ensuremath{{}_8}}} ₉ _9 {{\\ensuremath{{}_9}}} ₊ _+ {{}_+} ₋ _- {{}_-} ₌ _= {{}_=} ₍ _( {{}_(} ₎ _) {{}_)} ₐ _a {{}_a} ₑ _e {{}_e} ₒ _o {{}_o} ₓ _x {{}_x} ₕ _h {{}_h} ₖ _k {{}_k} ₗ _l {{}_l} ₘ _m {{}_m} ₙ _n {{}_n} ₚ _p {{}_p} ₛ _s {{}_s} ₜ _t {{}_t} ") ;; Test: ;; (load "eev-compose-hash.el") ;; (find-estring (ee-composes-to-string)) ;; (ee-composes-do ee-composes-bigstr-accents) ;; (ee-composes-do ee-composes-bigstr-otheriso) ;; (ee-composes-do ee-composes-bigstr-math) ;; (find-estring (ee-composes-to-string)) ;; (find-estring (ee-composes-to-catcodes)) ;; (find-estring (ee-composes-to-declareunicodes)) ;; (define-key eev-mode-map (kbd "M-,") 'ee-compose-pair) ;; ;; 2021aug31: ;; (ee-composes-do ee-composes-bigstr-math '(ee-composes-do-remove-face)) ;; (ee-composes-do ee-composes-bigstr-math) (provide 'eev-compose-hash) ;; Local Variables: ;; coding: utf-8-unix ;; no-byte-compile: t ;; End: