blob: 815dc45fb3c4b9c41c552f602f530a53549024fc [file] [log] [blame]
;;; ascii-braille.el --- Replace Unicode Braille with Ascii Braille overlays
;; Copyright (C) 2020 Christian Egli
;; Author: Christian Egli <christian.egli@sbs.ch>
;; Version: 0.1
;; URL: https://github.com/liblouis/ascii-braille-overlay/
;; Keywords: faces, matching, braille
;; Package-Requires: ((emacs "24.3") (ht "2.2"))
;; This program 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.
;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Replace unicode braille with ascii braille overlays while. It was
;; originally inspired by the package `symbol-overlays'.
;; Usage
;; Toggle the `ascii-braille-mode' minor mode to see the unicode
;; braille in your buffer as ascii braille. Disabling the minor will
;; remove all overlays.
;; TODO
;; - I suppose it would be nice if you could edit the ascii braille
;; and have it reflected in the unicode braille.
;; - Add a modify hook that updates the ascii braille if the unicode
;; braille changes.
;;; Code:
(require 'ht)
(defconst ascii-braille-unicode-to-ascii-map
(ht
(?⠀ ? )
(?⠁ ?a)
(?⠃ ?b)
(?⠉ ?c)
(?⠙ ?d)
(?⠑ ?e)
(?⠋ ?f)
(?⠛ ?g)
(?⠓ ?h)
(?⠊ ?i)
(?⠚ ?j)
(?⠅ ?k)
(?⠇ ?l)
(?⠍ ?m)
(?⠝ ?n)
(?⠕ ?o)
(?⠏ ?p)
(?⠟ ?q)
(?⠗ ?r)
(?⠎ ?s)
(?⠞ ?t)
(?⠥ ?u)
(?⠧ ?v)
(?⠺ ?w)
(?⠭ ?x)
(?⠽ ?y)
(?⠵ ?z)
(?⠬ ?0)
(?⠡ ?1)
(?⠣ ?2)
(?⠩ ?3)
(?⠹ ?4)
(?⠱ ?5)
(?⠫ ?6)
(?⠻ ?7)
(?⠳ ?8)
(?⠪ ?9)
(?⠯ ?&)
(?⠿ ?%)
(?⠷ ?{)
(?⠮ ?~)
(?⠾ ?})
(?⠂ ?,)
(?⠆ ?\;)
(?⠒ ?:)
(?⠲ ?/)
(?⠢ ??)
(?⠖ ?+)
(?⠶ ?=)
(?⠦ ?()
(?⠔ ?*)
(?⠴ ?))
(?⠄ ?.)
(?⠤ ?-)
(?⠌ ?|)
(?⠜ ?`)
(?⠼ ?#)
(?⠈ ?\")
(?⠐ ?!)
(?⠘ ?>)
(?⠨ ?$)
(?⠸ ?_)
(?⠰ ?<)
(?⠠ ?')))
(defconst ascii-braille-dots-to-unicode-map
'((?1 #x1)
(?2 #x2)
(?3 #x4)
(?4 #x8)
(?5 #x10)
(?6 #x20)
(?7 #x40)
(?8 #x80)))
(defun ascii-braille-dots-to-unicode (dots)
"Convert a string of DOTS such as \"1234\" to a unicode character."
;; see https://en.wikipedia.org/wiki/Braille_Patterns
(let* ((offsets (seq-map (lambda (tuple)
(seq-let (dot value) tuple
(if (seq-contains dots dot) value 0)))
ascii-braille-dots-to-unicode-map))
(offset-sum (seq-reduce #'+ offsets 0))
(unicode-braille-base #x2800))
(+ unicode-braille-base offset-sum)))
(defgroup ascii-braille nil
"Convert unicode braille to ascii braille using overlays."
:group 'convenience)
(defface ascii-braille-overlay-default-face
'((t (:inherit highlight)))
"Ascii braille overlay default face"
:group 'ascii-braille)
(defun ascii-braille-unicode-to-ascii (char)
"Convert a unicode braille char to ascii braille. If the char
is not a unicode braille char just return the char itself."
(ht-get ascii-braille-unicode-to-ascii-map char char))
(defun ascii-braille-to-ascii (s)
"Convert a unicode braille string to a ascii braille string."
(seq-concatenate 'string (seq-map #'ascii-braille-unicode-to-ascii s)))
(defun ascii-braille-overlay-put-one ()
"Put overlay on current occurrence of unicode braille after a match."
(let ((ov (make-overlay (match-beginning 0) (match-end 0)))
(ascii-braille (ascii-braille-to-ascii (match-string 0))))
; (overlay-put ov 'face 'ascii-braille-overlay-default-face)
;(overlay-put ov 'invisible t)
(overlay-put ov 'evaporate t)
(overlay-put ov 'braille t)
(add-face-text-property 0 (length ascii-braille) 'ascii-braille-overlay-default-face nil ascii-braille)
(overlay-put ov 'before-string ascii-braille)))
(defun ascii-braille-overlay-put-all ()
"Put overlays on all occurrences of unicode braille in the buffer."
(let* ((case-fold-search nil))
(save-excursion
(save-restriction
(goto-char (point-min))
(let ((re "[⠀-⠿]+"))
(while (re-search-forward re nil t)
(ascii-braille-overlay-put-one)))))))
(define-minor-mode ascii-braille-mode
"Toggle Ascii Braille mode.
Hide all Unicode Braille and put an overlay with Ascii Braille over top of it."
;; The initial value.
nil
;; The indicator for the mode line.
" AsciiBraille"
:group 'ascii-braille
(if ascii-braille-mode
(ascii-braille-overlay-put-all)
(remove-overlays nil nil 'braille t)))