From b0e7edb0e51ce414d312dd2a86d2815a6b709b1b Mon Sep 17 00:00:00 2001 From: "Mark E. Shoulson" Date: Wed, 18 Dec 2019 16:08:09 -0500 Subject: Make an emacs-mode for editing these files. I'm sure many of you will hate it, but I didn't make it for you. --- xcompose-mode.el | 216 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 190 insertions(+), 26 deletions(-) diff --git a/xcompose-mode.el b/xcompose-mode.el index 769b68e..4140bcb 100644 --- a/xcompose-mode.el +++ b/xcompose-mode.el @@ -1,46 +1,98 @@ -;; Blah blah add headers here. -;; Playing with the notion of a major mode for .XCompose files. -;; Maybe eventually smart enough to keep columns lined up. -;; For now, settling for pretty colors. +;;; xcompose.el --- major mode for editing .XCompose files +;; Copyright (C) 2019 Mark Shoulson + +;; Author: Mark Shoulson +;; Maintainer: Mark Shoulson +;; URL: + +;; This file is NOT part of GNU Emacs + +;; Permission is hereby granted, free of charge, to any person +;; obtaining a copy of this software and associated documentation +;; files (the "Software"), to deal in the Software without +;; restriction, including without limitation the rights to use, copy, +;; modify, merge, publish, distribute, sublicense, and/or sell copies +;; of the Software, and to permit persons to whom the Software is +;; furnished to do so, subject to the following conditions: + +;; The above copyright notice and this permission notice shall be +;; included in all copies or substantial portions of the Software. + +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +;; SOFTWARE. + +;;; Commentary: + +;; This file provides the major mode xcompose-mode, for use in editing +;; .XCompose files, which are used in X-windows systems to define the +;; behavior certain sequential keystroke combinations, usually involving +;; the "Multi-Key". It was also built with an eye towards use with "base" +;; files, which are slightly simplified compose files I use for +;; convenience. + +;; Mainly fontifying; eventually should do better formatting, finding the +;; right tab stops etc. + +;;; Code: (defface xcompose-angle-face - '((t (:inherit font-lock-keyword-face))) - "Face for the <>s" + ;; Black seems to stand out best, what can I say? + '((t (:inherit bold))) + "Face for the angle brackets (<>) around key-names." :group 'xcompose-mode) (defface xcompose-keys-face '((t (:inherit font-lock-constant-face))) - "Face for the key names" + "Face for the key names." :group 'xcompose-mode) (defface xcompose-string-face + ;; I feel like these should really stand out, not just plain-jane + ;; font-lock-string-face. Even to the point of increasing the size and + ;; drawing boxes. '((t (:inherit font-lock-string-face :height 1.2 :box "black"))) - "Face for the strings. Not straight font-lock-string-face in case I want -to make it big or something." + "Face for the quoted strings containing the character(s) to be produced." :group 'xcompose-mode) + (defface xcompose-quotemark-face '((t (:inherit font-lock-string-face :foreground "dark orchid"))) - "Face for quotes around strings" + "Face for quote-marks around character strings." :group 'xcompose-mode) +;; Yes, kind of a lot of faces and a lot of fine-tuning of the line's +;; appearance. Maybe a bit too much. (defface xcompose-num-face '((t (:inherit font-lock-preprocessor-face :weight bold))) - "Face for the hex numbers" + "Face for the hex numbers identifying the code-point." :group 'xcompose-mode) (defface xcompose-U-face '((t (:inherit font-lock-preprocessor-face))) - "Face for the U before the hex numbers" + "Face for the U before the hex numbers." :group 'xcompose-mode) (defface xcompose-colon-face '((t (:inherit bold))) - "Face for the \":\"." + "Face for the \":\" separating the keystrokes from the character string." :group 'xcompose-mode) +;; There are LOTS of comments (commented-out lines) in some of these files; +;; I'd like them to fade into the background a bit, and I use a white +;; background. +(defface xcompose-comment-face + `((t (:inherit font-lock-comment-face + :foreground "light coral"))) + "Face for comments in xcompose files." + :group 'xcompose-mode) (defvar xcompose-mode-syntax-table (let ((st (make-syntax-table text-mode-syntax-table))) @@ -49,12 +101,17 @@ to make it big or something." (modify-syntax-entry ?# "< " st) (modify-syntax-entry ?_ "_ " st) (modify-syntax-entry ?\n "> " st) + (modify-syntax-entry ?{ "| " st) + (modify-syntax-entry ?} "| " st) st) "Syntax table for xcompose-mode") (defvar xcompose-mode-map (let ((map (make-sparse-keymap))) - ;; ADD BINDINGS + (define-key map (kbd "C-c u") 'xcompose-fill-in-char-string) + (define-key map (kbd "C-c C-u") 'xcompose-fill-in-char-code) + ;; (define-key map (kbd "C-c C-i") 'xcompose-insert-char-name) + (define-key map (kbd "C-c ;") 'xcompose-insert-char-name) map) "Keymap for xcompose-mode") @@ -68,23 +125,130 @@ to make it big or something." ("\\(U\\)\\([0-9A-Fa-f]*\\)" . ((1 'xcompose-U-face) (2 'xcompose-num-face))) (":" . 'xcompose-colon-face) - (" #.*" . 'glyphless-char) - )) + ;; I want to be able to open my "base" files too, and automatically + ;; de-emphasize (comment-out) things that won't be expanded (because of + ;; too long a string.) + ("^.*{[^}]\\{8,\\}}.*$" 0 'xcompose-comment-face prepend) + ) + "Keywords for xcompose-mode") + +(defvar xcompose-key-re "<[a-zA-Z0-9_]+" + "Regexp matching the beginning of a keystroke.") + +;; I wonder if this will be useful or really annoying. +(define-abbrev-table 'xcompose-mode-abbrev-table + '(("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) ; ? + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ("" nil :system t) + ) + "Abbrev table" + :regexp "\\(<[a-zA-Z0-9_]+\\)" + :case-fixed t) + +;; See https://emacs.stackexchange.com/questions/51216/how-to-expand-abbrevs-without-hitting-another-extra-key +(defun xcompose-expand-abbrev () + "Run `expand-abbrev' when text before point matches `xcompose-key-re'" + (when (looking-back xcompose-key-re (line-beginning-position)) + (expand-abbrev))) + +;; Not really all that useful, since I can fill in the comment automatically +(defun xcompose-capitalize-comment nil + "Set any trailing comment on the current line to all-caps." + (interactive) + (save-excursion + (let* ((eol (progn (end-of-line) (point))) + (bol (progn (beginning-of-line) (point)))) + (if (search-forward comment-start eol t) + (upcase-region (point) eol))))) + +(defun xcompose-find-quoted-char (&optional pos) + "Find the character in quotes in the current line (or that given by pos)." + (save-excursion + (let* ((pos (or pos (point))) + (chr nil) + (eol (progn (end-of-line) (point))) + (bol (progn (beginning-of-line) (point)))) + (if (search-forward ":" eol t) + (progn + (if (search-forward "\"" eol t) + (setq chr (char-after))))) + chr))) + +;; If you can type the char but don't know its code (using C-x 8 RET is +;; great for this, if you know the character name) +(defun xcompose-fill-in-char-code (&optional pos) + "Look up character in string on line given and fill in the UXXXX code at point." + (interactive) + (let* ((pos (or pos (point))) + (chr (xcompose-find-quoted-char pos))) + (goto-char pos) + (insert (format "U%.04X" chr)))) + +;; Conversely, if you know the character code but for some reason can't +;; type it (C-x 8 RET not working for you?) you can do it the other way. +(defun xcompose-fill-in-char-string (&optional pos) + "Look up character given by UXXXX code on line given and insert into string before it, separated by a space." + ;; Probably needs some reformatting afterwards + (interactive) + (let* ((pos (or pos (point))) + (eol (progn (end-of-line) (point))) + (bol (progn (beginning-of-line) (point)))) + ;; search not necessarily as precise + (if (search-forward-regexp "\\