;; Initialization of my Emacs sessions, part two │ -*- lexical-binding: t; -*- ;; ;; This file is loaded by part one of the initialization, q.v. at `http://reluk.ca/.config/emacs/init.el`. ;; ;; PUBLIC at `http://reluk.ca/.config/emacs/lisp/` because project Breccia Mode refers to it there. ;; http://reluk.ca/project/Breccia/Emacs/brec-mode.el (eval-when-compile (require 'cl-lib)); For macro `cl-assert`. (defun init/part-two () "Finish the initialization." ;; Package management ;; ────────────────── (require 'package); If only for sake of `package-generate-autoloads` in `~/.config/emacs/init.el`. ;;; (defvar package-archives); [FV] ;;; (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) ;;;;;; But it delays start up, at least since I deleted all manager-installed packages (2023-2). ;;;;;; Curiously the delay roughly matches that of the redirect at `https://melpa.org/packages/`. ;;; https://melpa.org/ ;;; https://github.com/melpa/melpa ;;; https://github.com/melpa/melpa/blob/master/CONTRIBUTING.org ;;; (package-initialize); Early that I might override packages’ initial settings ;;; ;;; with my own in the code below, rather than have mine overridden by theirs. ;;;;;; Disabled pending need. ;; To uninstall packages: https://www.gnu.org/software/emacs/manual/html_node/emacs/Package-Menu.html ;; ;; • Run command `list-packages` ;; • Press `/s` to filter by status, for which give `installed` ;; • Press `d` to mark a package for deletion ;; • Press `x` to execute all marks ;; • Run command `package-autoremove`, as prompted ;; ;; Custom variable `package-selected-packages` may still list the uninstalled packages. Not sure why. ;; The last time this occured (2023-2), I cleared the list using command `customize-variable`. ;; ───── (init/general) (when (string= window-system "x") (init/x)) (init/keys) (init/modes)) ;; ══════════════════════════════════════════════════════════════════════════════════════════════════════ ;; D e c l a r a t i o n s i n l e x i c o g r a p h i c o r d e r ;; ══════════════════════════════════════════════════════════════════════════════════════════════════════ (defun init/asterisk_quotes () (interactive) (if (use-region-p) (let* ((start (region-beginning)) (length (- (region-end) start))) (goto-char start) (insert-char ?*) (forward-char length) (insert-char ?*)) (insert "**") (backward-char))) (defconst init/autoload_docstring "[Description not available until function definition is loaded.]" "\ A placeholder description for ‘autoload’ functions.") ;;; After the pattern of the placeholder Emacs itself generates for the `arglist`. (defun init/breccia () "Configure `brec-mode` and other Breccian particulars." (load "brec-mode-autoloads" nil t); Q.v. at `http://reluk.ca/sys/computer/workstation/etc/emacs/`. (add-hook 'brec-mode-hook (lambda () (init/set-for-buffer 'line-spacing 0); Pending repair of the ‘text bounces around’ fault. ;;; http://reluk.ca/project/Breccia/Emacs/working_notes.brec.xht#bounces,whenever,newline ;;; http://reluk.ca/project/Breccia/Emacs/brec-mode.el § Bugs ;; Key bindings ;; ──────────── (local-set-key [f11] [insert ?\\ insert left down]) ;;; Overwrite a ‘\’ and move point beneath it. (Was `toggle-frame-fullscreen`.) (local-set-key [?\C-c ?`] (lambda (); Wrap a pattern-matcher template around the region if present, else around point. (interactive) (if (use-region-p) (let ((start (region-beginning))) (goto-char (region-end)) (insert "$`i") (goto-char start) (insert "`^^")) (insert "`^^${same}$`i") (backward-char 10))))))) (defun init/buffer-names-sorted (&optional to-include-specials) "Give a list of buffer names in lexicographic order. Exclude buffers with names starting or ending ‘*’ if TO-INCLUDE-SPECIALS is nil." (let (names) (dolist (buffer (buffer-list)) (let ((name (buffer-name buffer))) (unless (and (not to-include-specials) (or (string-prefix-p "*" name) (string-suffix-p "*" name))) (setq names (cons name names))))) (sort names 'init/string-lessp-i))) (defun init/conf-mode () "Configure `conf-mode`." (defvar conf-mode-map); [FV] (add-hook 'conf-mode-hook (lambda () ;; Defeat of unwanted key bindings that would override my `init/keys` ;; ─────────────────────────────── (local-set-key [?\C-c ?8] 'init/asterisk_quotes); TODO: a more reliable means of reverting the value? (local-set-key [?\C-c ?\"] 'init/double_quotes) (local-set-key [?\C-c ?'] 'init/single_quotes)))) (defun init/cperl-mode_hook (); Underived major mode, *-mode-map are defined only in here. (defvar cperl-mode-map); [FV] (define-key cperl-mode-map [?\C-c ?p] 'init/pl-println) (define-key cperl-mode-map [f11] 'init/pl-comment-down) ;; Undo stuff in `cperl-mode.el`. (define-key cperl-mode-map [?{] #'self-insert-command); Was `cperl-electric-lbrace`. (define-key cperl-mode-map [?}] #'self-insert-command); Was `cperl-electric-brace`. (define-key cperl-mode-map [tab] #'tab-to-tab-stop) ;;; Was `cperl-indent-command`, which still is bound to `C-i`. (font-lock-add-keywords nil '( ;; Subdue punctuation ("[,;:]" . font-lock-comment-face) ("['\"]" 0 font-lock-comment-face t) ;;;("[{}$@%*]" . 'init/subdued) ;;;;; But $@ (and presumeably %*) somehow defeat fontification of variable names ("[{}]" . 'init/subdued))) (setq indent-line-function #'init/indent-relative-plain)) (defun init/css-mode () "Configure CSS mode." (defvar css-fontify-colors); [FV] (defvar css-mode-map) (add-hook 'css-mode-hook (lambda () (let ((m css-mode-map)) (define-key m [tab] #'tab-to-tab-stop)) ;;; Was `indent-for-tab-command`, which still is bound to `C-i`. (setq indent-line-function #'init/indent-relative-plain css-fontify-colors nil); Fontification of colour literals. (font-lock-add-keywords nil '( ;; Defeat of unwanted facing ;; ───────────────────────── ("^\\([[:alpha:]_*.[].*?\\)\\(?:/\\*.*\\)?$"; Buggy, inconsistent selector facing. ;; ────────S───────── ───C── ;; S selector commencing with character [:alpha:]_ OR * OR . OR [ ;; C trailing comment 1 'default t) ; ("color: *\\(hsla?(.*)\\)" 1 'default t); Colour literal ;;; It seems undefeatable. https://emacs.stackexchange.com/questions/46629 ;; Namespace prefix ;; ──────────────── ("\\(\\*\\|[[:alpha:]_][[:alnum:]_]*\\)?\\(|\\)[[:alpha:]_*]" (1 'init/subdued t t) (2 font-lock-comment-face t)) ;; Punctuation ;; ─────────── ("[;:]" . font-lock-comment-face) ("['\"]" 0 font-lock-comment-face t) ("[{},]" . 'init/subdued))))) (autoload 'css-mode "css-mode" init/autoload_docstring t)) (defun init/do-nothing () nil) (defun init/double_quotes () (interactive) (if (use-region-p) (let* ((start (region-beginning)) (length (- (region-end) start))) (goto-char start) (insert-char ?\“) (forward-char length) (insert-char ?\”)) (insert "“”") (backward-char))) (defun init/ebuild-mode () "Define a major mode for Gentoo ebuilds." (define-derived-mode ebuild-mode shell-script-mode "ebuild" "Gentoo `ebuild` files." ;; Per `http://www.gentoo.org/doc/en/gentoo-howto.xml`: (setq indent-tabs-mode 1)) (add-to-list 'auto-mode-alist '("[^/]\\.ebuild$" . ebuild-mode)) (add-to-list 'auto-mode-alist '("^/home/mike/project/.+/ebuild$" . ebuild-mode)) (add-to-list 'auto-mode-alist '("[^/]\\.eclass$" . ebuild-mode))) (defun init/emacs-lisp-mode () "Configure Emacs Lisp mode." (add-hook 'emacs-lisp-mode-hook (lambda () (local-set-key [f11] [insert ?\; ?\; ?\; insert left left left down]) ;;; Overwrite a ‘;;;’, move point beneath it. (Was `toggle-frame-fullscreen`.) (local-set-key [?\C-c ?p]; Insert ‘(message " ——— =%S" ); TEST’, and move point before the ‘=’. [?\( ?m ?e ?s ?s ?a ?g ?e ? ?\" ? ?— ?— ?— ? ?= ?% ?S ?\" ? ?\) ?\; ? ?T ?E ?S ?T left left left left left left left left left left left left])))) (defun init/general () "General initialization." (delete-selection-mode t) (menu-bar-mode 0); Early, else the menu bar briefly flashes on screen. (show-paren-mode); Indicate opposing brackets. To move between the two, use C-M-left and right. ;;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Matching.html (setq auto-save-default nil column-number-mode t ;;;column-number-indicator-zero-based nil ;;;;;; Then Breccian perfect indentation would appear at stops 5, 9 and so forth, which is misleading. ;;;font-lock-maximum-decoration 2; TEST ;;;font-lock-maximum-decoration '((java-deprecated-mode . 1)(t . t)) ;;;font-lock-maximum-decoration '((jmt-mode . nil)(t . t)) ;;; css : It fontifies property names as keywords, making the text unreadable. ;;; Unfortunately setting a minimal decoration has no effect. ;;; java-deprecated : Minimal, as per `/usr/local/share/emacs/site-lisp/java-deprecated-mode.el`. ;;; default (t) : Maximal (t). Without this, Emacs Lisp mode (at least) fails to fontify ;;; its regular expressions. ;;;custom-unlispify-remove-prefixes t; TEST frame-title-format (format "%%b %%f") inhibit-startup-message t isearch-lax-whitespace nil kill-whole-line t make-backup-files nil query-replace-highlight t read-quoted-char-radix 16 search-highlight t ;;;track-eol t ;;;;;; Too surprising when I do not want it. ;;;vc-cvs-stay-local nil ;;;;;; But now disabling CVS along with all other version control modes: vc-handled-backends nil ;;; Disable all version control modes. The Git one is forcing Emacs to visit symlink ;;; targets ("Followed link to") instead of source. visual-line-fringe-indicators t); For `visual-line-mode`. (setq-default ;;;case-fold-search nil; Making searches case sensitive. fill-column 105; (default 70) indent-tabs-mode nil major-mode #'text-mode; By default, as opposed to `fundamental`. recenter-positions '(top middle bottom); As opposed to `middle top bottom`. scroll-conservatively 1; With 101, query lines for `query-replace` end up at screen bottom. ;;; I would add `scroll-margin 3`, but then `recenter-top-bottom` no longer positions at top. ;;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Auto-Scrolling.html show-trailing-whitespace t tab-width 4 truncate-lines t) ;;;(put 'narrow-to-region 'disabled nil); Enables the command `narrow-to-region`. ;;;;;; Emacs probably put it here; I cannot remember ever having a use for it, neat as it is. (setq require-final-newline t); (setq-default require-final-newline t) ;;; But don't know how to mask it or unset it locally in major modes where it is unwanted. ;;; So instead I could explicitly set it (locally). Cf. `mode-require-final-newline`. ;; Bell ;; ──── ;;;(setq bell-volume 0); Enforce silence ;;;;;; Does not work, but this does: (setq ring-bell-function #'init/do-nothing) ;; Buffer initializations according to source file ;; ─────────────────────────────────────────────── (add-hook 'find-file-hook (lambda () (let ((bfn (file-truename buffer-file-name))) ;; Rather than simply using `buffer-file-truename`, which abbreviates e.g. "/home/mike" to "~" ;; built files ;; ┈┈┈┈┈┈┈┈┈┈┈ (when (eq (string-match "/home/mike/var/deploy/" bfn) 0) (message-box "Warning: this might be a built file."); In case I edit the wrong copy. (auto-revert-mode 1)) ;; source repositories ;; ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ ;;; (when (eq (string-match; GWT ;;; "/home/mike/code/gwt/limb/[^/]+/repo/" bfn) 0) ;;; (message "Set fill column for GWT source code.") ;;; (setq fill-column 80) ;;; ) (when (eq (string-match; SMBProvider Android app. "/home/mike/code/smbprovider/repo/[^/]*/" bfn) 0) (message "Set tabbing for SMBProvider source code.") (setq indent-tabs-mode 1)) ;;; (when (eq ;;; (string-match; Textbook Valet.. ;;; "/home/mike/client/valet/code/limb/[^/]+/repo/" bfn) ;;; 0) ;;; (message "Defeat require-final-newline for Textbook Valet source code.") ;;; (setq require-final-newline nil)) ;;; ;; remote files served across an SSH file system ;;; ;; ┈┈┈┈┈┈┈┈┈┈┈┈ ;;; (when (eq (string-match "/home/mike/code/uhm/mnt/" bfn) 0) ;;; (message "Enabling auto-revert-mode for this remote file.") ;;; (auto-revert-mode 1)); To avoid spurious editing guards: "file changed on disk". ;; current projects ;; ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ ;; For sake of properly ordering their files (by title) on opening them together, ;; e.g. by way of `~/sys/bin/lay-editable-tools`. (when (eq (string-match "/home/mike/code/100-0/repo/poll/\\([0-9a-zA-Z]+\\)/" bfn) 0) (rename-buffer (concat (match-string-no-properties 1 bfn) "/…") t)) (when (eq (string-match "/home/mike/project/proto-waycast/\\(?:README\\|_/[a-z_]+\\.task$\\)" bfn) 0) (rename-buffer (concat "proto-waycast (" (file-name-nondirectory bfn) ")") t)) (when (eq (string-match "/home/mike/work/_/proto-wayic/\\(?:README\\|_/manual\\.task$\\)" bfn) ;;; Omitting any `wayic.task`, which would rename the buffer 'proto-wayic wayic'. 0) (rename-buffer (concat "proto-wayic (" (file-name-nondirectory bfn) ")") t)) (when (eq (string-match "/home/mike/work/_/proto-wayic/\\([a-z][a-z_]*\\)/\\(?:_/\\)?\\(?:README\\|[a-z_]+\\.task$\\)" ;; └──────────────┘ ;; project name bfn) 0) (rename-buffer (concat "proto-wayic." (match-string-no-properties 1 bfn) " (" (file-name-nondirectory bfn) ")") t)) (when (eq (string-match "/home/mike/work/\\([A-Za-z].*?\\)/\\(?:README\\.[^/]+\\|[^/]*\\.\\(?:brec\\|task\\)\\)\\'" ;; └─────────────┘ ;; project name bfn) 0) (rename-buffer (concat (match-string-no-properties 1 bfn) " (" (file-name-nondirectory bfn) ")") t))))) ;; Customizer, `customize` that is. Apparently the package manager depends on it. ;; ────────── (set 'custom-file "/home/mike/.config/emacs/lisp/customizer-store.el") (load custom-file nil t) ;; Line spacing ;; ──────────── ;;;(setq-default line-spacing 1) ;;;;;; Done rather in `~/.Xresources`; but it remains to stop the resulting mystery echo: (add-hook 'minibuffer-setup-hook (lambda () (init/set-for-buffer 'line-spacing 0))) ;;; Stopping the ‘[Saved text until: …]’ that attends any use of `kill-ring-save` in the echo area, ;;; whenever a positive `line-spacing` is in effect. https://emacs.stackexchange.com/q/71797/21090 ;; Tabbing and indentation ;; ─────────────────────── (setq tab-stop-list '(4 8 12 16 20 24 28 32 36 40 44 48 52 56 60)) (electric-indent-mode -1)); A minor mode enabled in 24.4. It messes up `return` key binding. ;; Yank ;; ──── ;;; (setq yank-handled-properties (assq-delete-all 'face yank-handled-properties)) ;;; (set 'yank-handled-properties (assq-delete-all 'font-lock-face yank-handled-properties)) ;;; (add-to-list 'yank-excluded-properties 'face) ;;; (add-to-list 'yank-excluded-properties 'font-lock-face) ;;;;;; Still I see misfaced text after yanking, at least in Java buffers. ;;; (set 'yank-handled-properties (cons (cons 'face 'yank-handle-font-lock-face-property) yank-handled-properties)) ;;;;;; Nor does that help. (fset 'init/html-a-href [?\C-w ?< ?a ? ?h ?r ?e ?f ?= ?' ?' ?> ?\C-v ?< ?/ ?a ?> ?\C-r ?< ?a right right right right right right right right right]) (defun init/html-mode () "Configure HTML mode." (add-to-list 'auto-mode-alist '("\\.html\\'" . html-mode)); As opposed to `mhtml-mode`, e.g. `XHTML+`. (add-to-list 'auto-mode-alist '("[^/]\\.xht$" . html-mode)) (add-to-list 'auto-mode-alist '("^/home/mike/work/_/proto-wayic/\\(?:.+/\\)?[^./]+$" . html-mode)) ;;; Wayic files named without an extension are HTML term files. (add-hook 'html-mode-hook (lambda () (local-set-key [?\C-c ?, ?a ?h] 'init/html-a-href)))) (defun init/indent-relative-plain () (indent-relative t)) (defun init/Java () "Configure Java Mode Tamed and other Java particulars." (load "jmt-mode-autoloads" nil t) ;;; Q.v. at `http://reluk.ca/sys/computer/workstation/etc/emacs/`. ;;;(set 'auto-mode-alist (cons (cons "\\.properties\\'" #'conf-javaprop-mode) auto-mode-alist)) ;;;;; Apparently redundant these days. (add-hook 'jmt-mode-hook (lambda () ;; Key bindings ;; ──────────── (local-set-key [f11] 'init/Java-comment-down); A keyboard macro; was `toggle-frame-fullscreen`. (local-set-key [tab] #'tab-to-tab-stop); Was `c-indent-line-or-region`. (local-set-key [?\M-\;] 'init/Javadoc-open-comment); A keyboard macro; was `comment-dwim`. ;;; https://www.gnu.org/software/emacs/manual/html_node/ccmode/Comment-Commands.html (local-set-key [?\C-c ?, ?a ?h] 'init/html-a-href) (local-set-key [?\C-c ?, ?l] 'init/Javadoc-linkplain) (local-set-key [?\C-c ?p] 'init/Java-println) (local-set-key [?\C-\M-,] 'init/sgml-entag); To `<` unshifted, that is; a keyboard macro. (local-set-key [?\C-\M-.] 'init/sgml-entag-end))); One for its opposite, too. ;; Disabling unwanted minor modes and “minor-mode-like” features ;; ────────────────────────────── ;; “By default, … electric mode and syntactic-indentation mode are enabled”. ;; And to support the former, Abbrev mode is “turned on by default”. ;; https://www.gnu.org/software/emacs/manual/html_node/ccmode/Minor-Modes.html ;; https://www.gnu.org/software/emacs/manual/html_node/ccmode/Electric-Keys.html (setq-default ;;;c-electric-flag nil ;;;;;; No need, already my setting elsewhere for `electric-indent-mode` has disabled it. c-syntactic-indentation nil); TODO: set using `init/set-for-buffer` within `jmt-mode-hook`. (add-hook 'jmt-mode-hook (lambda () (set 'abbrev-mode nil))) ;; Fontifiers ;; ────────── (font-lock-add-keywords #'jmt-mode (list (cons; Subdue any `assert` statements. (let (match-beg match-end) (lambda (limit) (setq match-beg (point)); Presumptively. (catch 'to-reface (while (< match-beg limit) (setq match-end (next-single-property-change match-beg 'face (current-buffer) limit)) (when (and (eq 'jmt-principal-keyword (get-text-property match-beg 'face)) (string= "assert" (buffer-substring-no-properties match-beg match-end))) (goto-char match-end) (when (search-forward ";" (min (line-end-position) limit) t); [BUG] ;;; May in rare cases match a ‘;’ embedded in string or comment. Correct by extending ;;; the search in a `while` loop till the face property `jmt-separator` is found. (setq match-end (point)) (set-match-data (list match-beg match-end (current-buffer))) (throw 'to-reface t))) (setq match-beg match-end)) nil))) '(0 'init/subdued t))) t)); To append. ;; A keyboard macro that overwrites a ‘//’ and moves point beneath it. (fset 'init/Java-comment-down [insert ?/ ?/ insert left left down]) (defun init/Java-common-mode (mode-map) "Initialization common to Java and JavaScript modes." (define-key mode-map [?\C-c ?* ?l] 'init/Javadoc-linkplain) (define-key mode-map [?\C-c ?* ?*] 'init/Javadoc-open-comment); A keyboard macro; was `comment-dwim`. (define-key mode-map [?\C-c ?/] [?/ ?* ?* ?/ left left]) (define-key mode-map [f11] 'init/Java-comment-down); A keyboard macro; was `toggle-frame-fullscreen`. (define-key mode-map [tab] #'tab-to-tab-stop) ;;; Was `indent-for-tab-command`, which still is bound to `C-i`. (setq indent-line-function #'init/indent-relative-plain)) (defun init/java-deprecated-mode () "Configure `java-deprecated-mode`." (defvar java-deprecated-mode-map); [FV] (add-hook 'java-deprecated-mode-hook (lambda () (let ((m java-deprecated-mode-map)) (define-key m [?\C-c ?p] 'init/Java-println) ;; Undo troublesome key assignments in cc-mode.el ;;; (define-key m "#" #'self-insert-command) ;;; (define-key m "{" #'self-insert-command) ;;; (define-key m "}" #'self-insert-command) ;;; (define-key m "/" #'self-insert-command) ;;; (define-key m "*" #'self-insert-command) ;;; (define-key m ";" #'self-insert-command) ;;; (define-key m "," #'self-insert-command) ;;; (define-key m ":" #'self-insert-command); Otherwise inserts a space. ;;; (define-key m "(" #'self-insert-command) ;;; (define-key m ")" #'self-insert-command) ;;; (init/sgml-common-mode m)) (init/Java-common-mode m)) (setq abbrev-mode nil))) ;;; Otherwise enabled for some reason. Not wanted. Also I want to avoid an ;;; infinite loop that I once saw involving `c-electric-continued-statement`, ;;; which occurred after typing "else" and other words in the abbreviation table. (autoload 'java-deprecated-mode "java-deprecated-mode" init/autoload_docstring t); ↑ Q.v. at `http://reluk.ca/sys/computer/workstation/etc/emacs/`. (set 'auto-mode-alist (cons (cons "\\.java\\'" 'java-deprecated-mode) auto-mode-alist))) ;; A keyboard macro that inserts an `@linkplain` Javadoc tag. Uses the selected text as the tag body. ;; Saves it to the kill ring, forms the tag around it and places point in readiness to enter (or yank) ;; the tag reference. (fset 'init/Javadoc-linkplain [?\C-w ?{ ?@ ?l ?i ?n ?k ?p ?l ?a ?i ?n ? ?\C-v ?} ?\C-r ?{ ?@ ?l ?i ?n ?k C-right ? ?#]) ;; A keyboard macro that inserts an empty API description before the present line, ;; and places point in readiness to enter its text. (fset 'init/Javadoc-open-comment [kp-prior ?/ ?* ?* return ? ? ?* ?/ up ? ?. left]) ;; A keyboard macro that inserts “System.err.println( \" ——— =\" + )” and moves point before the ‘=’. (fset 'init/Java-println [?S ?y ?s ?t ?e ?m ?. ?e ?r ?r ?. ?p ?r ?i ?n ?t ?l ?n ?\( ? ?\" ? ?— ?— ?— ? ?= ?\" ? ?+ ? ? ?\) ?\; ? ?/ ?/ ? ?T ?E ?S ?T left left left left left left left left left left left left left left left left]) (defun init/js-deprecated-mode () "Configure `js-deprecated-mode`." (defvar js-deprecated-mode-map); [FV] (add-hook 'js-deprecated-mode-hook (lambda () (let ((m js-deprecated-mode-map)) ;;; (define-key m [?\C-c ?p] [ ?j ?a ?v ?a ?. ?l ?a ?n ?g ?. ?S ?y ?s ?t ?e ?m ?. ?e ?r ?r ?. ?p ?r ?i ?n ?t ?l ?n ?\( ? ?' ? ?— ?— ?— ? ?= ?' ? ?+ ? ? ?\) ?\; ? ?/ ?/ ? ?T ?E ?S ?T left left left left left left left left left left left left left left left left]) ;;;;;; I guess I need this form when running under a JDK (e.g. as Nashorn). ;;;;;; I can sometimes distinguish that using `interpreter-mode-alist` (as below). ;;;;;; But this works only for a shebanged executable (headed by the likes of `#!/opt/jdk/bin/jjs`), ;;;;;; not for any library it loads, or any script executed by other means. So just: (define-key m [?\C-c ?p] [ ?c ?o ?n ?s ?o ?l ?e ?. ?l ?o ?g ?\( ? ?' ? ?— ?— ?— ? ?= ?' ? ?+ ? ? ?\) ?\; ? ?/ ?/ ? ?T ?E ?S ?T left left left left left left left left left left left left left left left left]) (init/Java-common-mode m)))) (autoload 'js-deprecated-mode "js-deprecated-mode" init/autoload_docstring t); ↑ Q.v. at `http://reluk.ca/sys/computer/workstation/etc/emacs/`. (set 'auto-mode-alist (cons (cons "\\.fab\\'" 'js-deprecated-mode) auto-mode-alist)) (set 'auto-mode-alist (cons (cons "\\.js\\'" 'js-deprecated-mode) auto-mode-alist)) (set 'auto-mode-alist (cons (cons "\\.jsm\\'" 'js-deprecated-mode) auto-mode-alist)) ;;; Waymaker file-associated build scripts (.fab), JavaScript (.js) ;;; and Votorola JavaScript modules (.jsm). (set 'interpreter-mode-alist (cons (cons "jjs" 'js-deprecated-mode) interpreter-mode-alist)) (set 'interpreter-mode-alist (cons (cons "jrunscript" 'js-deprecated-mode) interpreter-mode-alist))) ;;; JDK Nashorn JavaScript executable (jjs) and JDK JavaScript executable (jrunscript). (defun init/keys () "Initialize key bindings." (global-unset-key [f10]); Was `tmm-menubar`. (global-unset-key [menu]); Was `execute-extended-command`. (global-set-key [?\r] #'newline-and-indent); Was `newline`, q.v. herein. (global-set-key [kp-end] 'init/newline-after) (global-set-key [kp-home] 'init/newline-before) (global-set-key [kp-left] (lambda () (interactive) (indent-rigidly (region-beginning)(region-end) -1))) (global-set-key [kp-next] 'init/open-line-after) (global-set-key [kp-prior] #'init/open-line-before) (global-set-key [kp-right] (lambda () (interactive) (indent-rigidly (region-beginning)(region-end) 1))) (let ((im isearch-mode-map)) ;; `C`-qualified by `Ctrl` key ;; ───────────── (global-set-key [?\C-'] [?\u0301]); Accute accent ◌́ combining. (global-set-key [?\C-`] [?\u0300]); Grave accent ◌̀ combining. (global-unset-key [?\C-a]); Was `beginning-of-line`, but for that I use `Home`. ;;;(global-set-key [?\C-c] #'copy-region-as-kill-nomark) ;;;;;; Bad idea because it is a standard prefix; modes will clobber it. ;;;(global-unset-key [?\C-d]); Was `delete-char`, but for that I use `Del`. ;;;;;; Causes `Del` to fail; apparently it is remapped to `C-d` at a low level. (global-unset-key [?\C-o]) ;;; Was `open-line`, but for that I use my `init/open-line-before` and `init/open-line-after`. (global-set-key [?\C-v] #'yank); Was `scroll-up`. (define-key im [?\C-v] #'isearch-yank-kill); During incremental search, that is. (global-unset-key [?\C-z]); Was `iconify-or-deiconify-frame`. ;;; (global-set-key [C-end] #'end-of-buffer); Was `end-of-buffer-nomark`. ;;; (global-set-key [C-home] #'beginning-of-buffer); Was `beginning-of-buffer-nomark`. (global-set-key [C-next] #'init/switch-to-next-buffer) ; Was bound to `scroll-left`. (global-set-key [C-prior] #'init/switch-to-previous-buffer); Was bound to `scroll-right`. ;; `M`-qualified by `Alt` key ;; ───────────── (global-set-key [?\M-\r] #'newline); Cf. `newline-and-indent` herein. (global-set-key [?\M- ] [? ]); Was `just-one-space`. (define-key im [?\M- ] (lambda () (interactive) (isearch-yank-string " "))); [IS] (global-set-key [?\M-$] [?§]); Was `ispell-word`. (define-key im [?\M-$] (lambda () (interactive) (isearch-yank-string "§"))); [IS] (global-set-key [?\M-'] [?\’]); Was `abbrev-prefix-mark`. (define-key im [?\M-'] (lambda () (interactive) (isearch-yank-string "’"))); [IS] (global-set-key [?\M-*] [?•]) (define-key im [?\M-*] (lambda () (interactive) (isearch-yank-string "•"))); [IS] (global-set-key [?\M--] [?—]); Was `negative-argument`. (define-key im [?\M--] (lambda () (interactive) (isearch-yank-string "—"))); [IS] (global-set-key [?\M-.] [?…]); Was `xref-find-definitions`. (define-key im [?\M-.] (lambda () (interactive) (isearch-yank-string "…"))); [IS] (global-set-key [?\M-~] [?∼]); Was `not-modified`. (define-key im [?\M-~] (lambda () (interactive) (isearch-yank-string "∼"))); [IS] (global-set-key [?\M-b] [?∵]) (define-key im [?\M-b] (lambda () (interactive) (isearch-yank-string "∵"))); [IS] (global-set-key [?\M-k] 'init/kill-ring-save-line); A keyboard macro; was `kill-sentence`. (global-set-key [?\M-m] [?×]); Was `back-to-indentation`. (define-key im [?\M-m] (lambda () (interactive) (isearch-yank-string "×"))); [IS] (global-set-key [?\M-t] [?∴]); Was `transpose-words`. (define-key im [?\M-t] (lambda () (interactive) (isearch-yank-string "∴"))); [IS] (global-set-key [?\M-v] #'yank-pop); Was `scroll-down`. (define-key im [?\M-v] #'isearch-yank-pop); [IS] (global-set-key [M-down] (lambda () (interactive) (scroll-up-line))); Was `backward-line-nomark`. (global-set-key [M-up] (lambda () (interactive) (scroll-down-line))); Was `forward-line-nomark`. (global-set-key [M-left] (lambda () (interactive) (scroll-right 1))); Was `left-word`. (global-set-key [M-right] (lambda () (interactive) (scroll-left 1))); Was `right-word`. (global-set-key [M-kp-left] #'init/null-command); Was unbound and translating to `M-left`. [TOP] (global-set-key [M-kp-right] #'init/null-command); Was unbound and translating to `M-right`. [TOP] (global-set-key [M-S-kp-4] #'init/null-command); Was unbound and translating to `M-4`. [TOP] (global-set-key [M-S-kp-6] #'init/null-command); Was unbound and translating to `M-6`. [TOP] ;; `S-C`-qualified by `Shift-Ctrl` keys ;; ─────────────── (global-set-key [S-C-next] (lambda () (interactive) (init/switch-to-next-buffer t))) (global-set-key [S-C-prior] (lambda () (interactive) (init/switch-to-previous-buffer t)))) ;;; `S-C-next` and `S-C-prior` fail outside of X Windows. There `C-prior` (`Page Up`) ;;; and `C-next` (`Page Down`) are somehow equivalent to unqualified `prior` and `next`. ;; ═════════════════════ ;; Multi-stroke bindings ;; ═════════════════════ ;; `C-c` based ;; ─────────── (global-set-key [?\C-c ?8] 'init/asterisk_quotes) (global-set-key [?\C-c ?'] 'init/single_quotes) (global-set-key [?\C-c ?\"] 'init/double_quotes) (global-set-key [?\C-c ?h] "http://reluk.ca/project/") ;; `C-h` based ;; ─────────── (global-set-key [?\C-h ?c] #'describe-char); Was `describe-key-briefly`. ;; `C-x` based ;; ─────────── (global-set-key [?\C-x ?r ?v] #'yank-rectangle) (global-unset-key [?\C-x ?r ?y]); Was `yank-rectangle`. (global-set-key [?\C-x ?s] #'init/save-all-buffers) ;;; Was `save-some-buffers` which prompts, whereas this one is silent. (global-unset-key [?\C-x ?u]); Was `advertised-undo`, but for that I use `S-C--`. (global-unset-key [?\C-x ?\C-z])); Was `iconify-or-deiconify-frame`, ;;; but I never do that because it makes the window disappear as though it were killed. (fset 'init/kill-ring-save-line "\C-k\C-_") (defun init/markdown (); http://jblevins.org/projects/markdown-mode/ "Configure Markdown particulars." (defvar markdown-command); [FV] (setq markdown-command "markdown2") ;;; The default is `markdown`, but it seems a Gentoo package has installed `markdown2` instead. (autoload 'markdown-mode "markdown-mode" init/autoload_docstring t) (autoload 'gfm-mode "markdown-mode" init/autoload_docstring t); GitHub Flavoured Markdown (GFM). ;;; Likely it cannot do previews without additional work. See ‘GFM’ in the manual. (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode)) (add-to-list 'auto-mode-alist '("README\\.md\\'" . gfm-mode)) (add-hook 'markdown-mode-hook (lambda (); Overriding the present mode’s override of my `init/keys`: ;; Defeat of unwanted key bindings that would override my `init/keys` ;; ─────────────────────────────── (local-set-key [?\C-c ?8] 'init/asterisk_quotes) (local-set-key [?\C-c ?\"] 'init/double_quotes) (local-set-key [?\C-c ?'] 'init/single_quotes)))) (defun init/modes () "Define and/or customize the various major and minor modes." (init/breccia) (init/conf-mode) (init/css-mode) (init/ebuild-mode) (init/emacs-lisp-mode) (init/html-mode) (init/Java) ;;;(init/java-deprecated-mode); TEST (init/js-deprecated-mode) (when (string= (system-name) "primeval"); Which alone has Markdown modes installed. (init/markdown)) (init/mutt-mode) (init/perl-mode) (init/sh-mode) (init/sgml-mode) (init/task-sheet-mode) ;;;(init/test-mode); TEST (init/text-mode) (init/waybrec) (init/wayscript-mode)) ;;(add-to-list 'interpreter-mode-alist '("runscript" . shell-script-mode)); Gentoo init scripts. ;;(add-to-list 'auto-mode-alist '("[^/]\\.gradle$" . js-deprecated-mode)); Gradle build scripts. (defun init/mutt-mode () "Define Mutt-Mail mode." (define-derived-mode mutt-mail-mode text-mode "Mutt-Mail" "For composing mail in a buffer invoked by Mutt." (setq show-trailing-whitespace nil ;;; indent-line-function #'indent-relative) fill-column 70); Though usually you must set this in a mode hook. (defvar mutt-mail-font-lock-keywords (list '("^\\(?:From: \\|Cc: \\|Bcc: \\|Message-ID: \\|Reply-To: \\|In-Reply-To: \\).*$" . font-lock-comment-face) '("^\\(?:To: \\).*$" . font-lock-string-face) '("^\\(?:Subject: \\).*$" . font-lock-type-face) '("^\\(?: *> *>\\)+ *\\(?:[^ >\xA].*\\)?$"; Quote me (even). . font-lock-comment-face) '("^ *>.*$" ; Quote other (odd). . font-lock-string-face) '("^-- $" ; Signature delimiter. . font-lock-comment-face) '("^\\(?:Mike\\|Michael Allan\\)$" ; Signature. . font-lock-comment-face)) "For mutt-mail-mode.") ;;; (make-local-variable 'font-lock-defaults) ;;; (setq font-lock-defaults '(mutt-mail-font-lock-keywords t)) (init/set-for-buffer 'font-lock-defaults '(mutt-mail-font-lock-keywords t))) (add-to-list 'auto-mode-alist '("[mM]utt\\(?:-.*\\)?-[0-9]+-[0-9]+\\'" . mutt-mail-mode)) (add-hook 'mutt-mail-mode-hook (lambda () ;;; (make-local-variable 'require-final-newline) ;;; (setq require-final-newline nil) ;;;;;; No effect; rather than unsetting it like this, explicitly, I would have to set it explicitly ;;;;;; everywhere else [just don't like how Mutt's viewer shows the final newline] but it's not worth ;;;;;; it [and mail handlers append newlines etc. anyway, it seems] (re-search-forward "^$"); Move down to 1st blank line, past the headers. (forward-line)))); And one more, to beginning of message body. (fset 'init/newline-after [end M-return]) (fset 'init/newline-before [home M-return up]) (defun init/null-command (); Does nothing. (interactive)) (fset 'init/open-line-after [end return]) (fset 'init/open-line-before [end return ?\C-x ?\C-t up up end]) (defun init/perl-mode () "Configure Perl mode." (add-hook 'perl-mode-hook #'init/perl-mode_hook) (add-hook 'cperl-mode-hook #'init/cperl-mode_hook)) ;; Add the following if you want `cperl-mode` instead of `perl-mode`. ;; Note that opening an editor window via `lay-editable-tools` (which includes Perl buffers), ;; then switching back and forth among the buffers (Ctrl Page Up, Ctrl Page Down) results (2023-2) ;; puts copies of the following in the message buffer. ;; ;; Error during redisplay: (jit-lock-function 1) signaled (error "No match 4 in highlight (4 font-lock-variable-name-face)") ;; Error during redisplay: (jit-lock-function 2) signaled (error "No match 4 in highlight (4 font-lock-variable-name-face)") ;; ;; Meanwhile it turns out (2023-2) the JIT Lock debugger is now useless: ;; enabling `jit-lock-debug-mode` causes all modes to fail silently. ;; ;; However, `cperl-mode.el` has but one line that contains `4 font-lock-variable-name-face`. ;;;(add-to-list 'auto-mode-alist '("\\.\\([pP][Llm]\\|al\\)\\'" . cperl-mode)) ;;;(add-to-list 'interpreter-mode-alist '("perl" . cperl-mode)) ;;;(add-to-list 'interpreter-mode-alist '("perl5" . cperl-mode)) ;;;(add-to-list 'interpreter-mode-alist '("miniperl" . cperl-mode)) (defun init/perl-mode_hook (); Underived major mode, *-mode-map are defined only in here. (defvar perl-mode-map); [FV] (define-key perl-mode-map [?\C-c ?p] 'init/pl-println) (define-key perl-mode-map [f11] 'init/pl-comment-down) ;; Undo stuff in perl-mode.el (define-key perl-mode-map [?{] #'self-insert-command); Auto-indent too often wrong. (define-key perl-mode-map [?}] #'self-insert-command) (define-key perl-mode-map [?\;] #'self-insert-command) (define-key perl-mode-map [?:] #'self-insert-command) (define-key perl-mode-map [tab] #'tab-to-tab-stop); Can still use C-i for indent. (font-lock-add-keywords nil '( ;; Subdue punctuation ("[,;:]" . font-lock-comment-face) ("['\"]" 0 font-lock-comment-face t); t override existing fontification. ("[{}$@%*]" . 'init/subdued))); Unless already fontified. (setq indent-line-function #'init/indent-relative-plain)) (fset 'init/pl-comment-down [insert ?# insert left down]) (fset 'init/pl-println [?p ?r ?i ?n ?t ?\( ? ?\" ? ?— ?— ?— ? ?\\ ?= ?\" ? ?. ? ? ?. ? ?\" ?\\ ?n ?\" ? ?\) ?\; left left left left left left left left left left left left left left left]) (defun init/save-all-buffers () (interactive) (save-some-buffers t)) (defun init/sgml-entag () (interactive) (set-mark (point)) (left-word) (kill-region (point) (mark)) (insert-char ?<) (yank) (insert-char ?>)) (fset 'init/sgml-entag-end "\C-r<\C-m") (defun init/sgml-common-mode (mode-map) "SGML related init, common to various modes." ;;; [unsure if this is still true:] ;;; Many keys cannot be bound here, because bindings are clobbered in sgml-mode.el. ;;; Test first with describe-key-briefly. ;;; ;;; Changing? Change also in zz-swatch.txt. (define-key mode-map [?/] #'self-insert-command) ;;; Was `sgml-slash`, which blocks overwriting of the present selection when I type ‘/’. (define-key mode-map [?\C-\M-,] 'init/sgml-entag); To `<` unshifted, that is; a keyboard macro. (define-key mode-map [?\C-\M-.] 'init/sgml-entag-end)); One for its opposite, too. (defun init/sgml-mode () "Configure SGML mode." (defvar sgml-mode-map); [FV] (add-to-list 'auto-mode-alist '("[^/]\\.xml$" . sgml-mode)) ;;; Somehow puts the buffer into XML mode specifically, which is derived from SGML mode; ;;; this as opposed to the default nXML mode which is *not* so derived. ;;;(add-to-list 'auto-mode-alist '("[./]tld$" . sgml-mode)) ;;;(add-to-list 'auto-mode-alist '("[^/]\\.ent$" . sgml-mode)) ;;;(add-to-list 'auto-mode-alist '("[^/]\\.mod$" . sgml-mode)) ;;;(add-to-list 'auto-mode-alist '("[^/]\\.rdf$" . sgml-mode)) ;;;(add-to-list 'auto-mode-alist '("[^/]\\.xsd$" . sgml-mode)) (add-to-list 'auto-mode-alist '("[^/]\\.xslt$" . sgml-mode)) (add-hook 'sgml-mode-hook (lambda () ;;; (define-key sgml-mode-map [tab] #'tab-to-tab-stop); Can still use C-i for indent. ;;; (define-key sgml-mode-map "/" #'self-insert-command) ;;; ;;; Undo from sgml-mode.el; refuses to obey insert/overwrite mode. ;;; (make-local-variable 'require-final-newline) ;;; (setq require-final-newline t) (setq indent-line-function #'init/indent-relative-plain) ;; As this is un-derived major mode, *-mode-map are defined only in here (init/sgml-common-mode sgml-mode-map) (font-lock-add-keywords nil; 'nil' so it affects derived modes such as HTML, too. '( ;; See also my modified older version of sgml-mode.el, from which many of these rules ;; were originally taken: /usr/local/share/emacs/site-lisp/._ ;; Attribute assignment ;; ──────────────────── (" \\([_[:alpha:]][-._[:alnum:]]*:\\)?\\([_[:alpha:]][-._[:alnum:]]*\\) *\\(=\\) *\\(['\"]\\)\\(.*?\\)\\(\\4\\)" (1 font-lock-comment-face nil t) (2 font-lock-builtin-face) (3 font-lock-comment-face) (4 'init/subdued t) (5 font-lock-comment-face t) (6 'init/subdued t)) ;;; (" id *= *\\(['\"]\\)\\(.+?\\)\\1" 2 font-lock-string-face t); HTML allows any non-empty value ;;;;;; Defeated only because my attempt to override it fails in init/wayscript-mode, q.v. (" xmlns:\\([_[:alpha:]][-._[:alnum:]]*\\) *= *['\"]" 1 font-lock-type-face t) ;; DTD declaration ;; ──────────────- ("\\(" . font-lock-comment-face); In lieu of a multiline pattern. ;; Element end ;; ──────────- ("\\(" 1 font-lock-comment-face) ;; Processing instruction ;; ────────────────────── ("\\(<\\?\\(?:[_[:alpha:]][-._[:alnum:]]*:\\)?\\)\\([_[:alpha:]][-._[:alnum:]]*\\)" (1 font-lock-comment-face) (2 font-lock-constant-face)) ("\\(\\?\\)>" 1 font-lock-comment-face)))))); In lieu of a multiline pattern (defun init/sh-mode () "Configure the shell-script mode, `sh-mode`." (defvar sh-mode-map); [FV] (add-hook 'sh-mode-hook (lambda (); Underived major mode, *-mode-map are defined only in here. (define-key sh-mode-map [tab] #'tab-to-tab-stop)))); Can still use C-i for indent. (defun init/single_quotes () (interactive) (if (use-region-p) (let* ((start (region-beginning)) (length (- (region-end) start))) (goto-char start) (insert-char ?\‘) (forward-char length) (insert-char ?\’)) (insert "‘’") (backward-char))) (defun init/string-lessp-i (string1 string2) "Like 'string-lessp' but ignores case." (setq string1(downcase string1)) (setq string2(downcase string2)) (string-lessp string1 string2)) (defface init/subdued `((t . (:inherit shadow))) "\ A face for noisy symbolic clutter that needs subduing." :group 'faces) (defun init/switch-to-next-buffer (&optional to-include-specials) "Page to the next buffer in lexicographic order. Exclude buffers with names starting or ending ‘*’ if TO-INCLUDE-SPECIALS is nil." (interactive) (delete-other-windows); In case visiting multiple initial files, when Emacs shows a split frame ;;; with a buffer list in the second window: this deletes that second window. Not sure how else to ;;; do this, or where better to place this call — both `after-init-hook` and `-funcall` operate too ;;; early, prior to load. Besides, it is not bad to see the buffer list as long as it goes away ;;; without effort, and this does the trick. (let ((bb (init/buffer-names-sorted to-include-specials)) (name (buffer-name))) (while bb (if (equal name (car bb)) (progn ;; Switch to next list entry, or first if current is last. (switch-to-buffer (or (car(cdr bb)) (car(init/buffer-names-sorted to-include-specials)))) (setq bb nil)) (setq bb (cdr bb)))))) (defun init/switch-to-previous-buffer (&optional to-include-specials) "Page to the previous buffer in lexicographic order. Exclude buffers with names starting or ending ‘*’ if TO-INCLUDE-SPECIALS is nil." (interactive) (delete-other-windows); Concordant with init/switch-to-next-buffer. (let ((bb (reverse (init/buffer-names-sorted to-include-specials))) (name (buffer-name))) (while bb (if (equal name (car bb)) (progn ;; Switch to previous list entry, or last if current is first. (switch-to-buffer (or (cadr bb) (car (last (init/buffer-names-sorted to-include-specials))))) (setq bb nil))) (setq bb (cdr bb))))) (defun init/task-sheet-mode () "Configure Task Sheet mode." (defvar task-sheet-mode-map); [FV] (add-hook 'task-sheet-mode-hook (lambda () (let ((m task-sheet-mode-map)) ;;; (init/sgml-common-mode m)) (define-key m [f11] [insert ?/ insert left down]) ;;; Overwrite a ‘/’ and move point beneath it. (Was `toggle-frame-fullscreen`.) (define-key m [?\t] #'tab-to-tab-stop)) ;;; (make-local-variable 'require-final-newline) ;;; (setq require-final-newline t) (setq indent-line-function #'init/indent-relative-plain))) (autoload 'task-sheet-mode "task-sheet-mode" init/autoload_docstring t); ↑ Q.v. at `http://reluk.ca/sys/computer/workstation/etc/emacs/`. (add-to-list 'auto-mode-alist '("[./]task$" . task-sheet-mode))) (defun init/set-for-buffer (variable value) "Sets VARIABLE (a symbol) to VALUE. Signals an error if the setting is not buffer local." (set variable value) (cl-assert (local-variable-p variable))) ;;;(defun task-sheet-toggle-mode () ;;; "Toggles between major modes 'task-sheet-mode' and 'html-mode'." ;;; (interactive) ;;; (cond ((string= major-mode "html-mode") ;;; (task-sheet-mode)) ;;; ((string= major-mode "task-sheet-mode") ;;; (html-mode))) ;;; ) (defun task-sheet-toggle-mode () "Toggles between major modes 'task-sheet-mode' and the current mode." (interactive) (cond ((string= major-mode "task-sheet-mode") ;;; (if ;;; (fboundp init/task-sheet-toggle-mode-was) ;;; ;;; (init/task-sheet-toggle-mode-was) ;;; ;;; (funcall 'init/task-sheet-toggle-mode-was) ;;; (funcall (symbol-function #'init/task-sheet-toggle-mode-was)) ;;; ) ;;;;;; Above guard always fails complaining init/task-sheet-toggle-mode-was has void value as variable. ;;;;;; [Not sure, but why wasn’t that simply `(funcall #'init/task-sheet-toggle-mode-was)`? ;;; (cond ;;; ((fboundp init/task-sheet-toggle-mode-was) ;;; ;;;(init/task-sheet-toggle-mode-was) ;;; ;;;(funcall 'init/task-sheet-toggle-mode-was) ;;; (funcall (symbol-function #'init/task-sheet-toggle-mode-was)) ;;; ) ;;; ) ;;;;;; Ditto above. But the worst it does is complain mildly on toggling, so just leave it unguarded: (init/task-sheet-toggle-mode-was)) (t (fset 'init/task-sheet-toggle-mode-was major-mode) (task-sheet-mode)))) (defvar init/task-sheet-toggle-mode-was) (fset 'init/task-sheet-toggle-mode-was 'text-mode) ;;;(fset 'init/test "\223[^[:ascii:]]") ;;;(fset 'init/test [?\S-\C-\M-s ?\\ ?. ?h ?t ?m ?l ?\[ ?^ ?\" ?# ?< ?\]]) (defun init/test-mode () "Configure Test mode." (define-derived-mode test-mode text-mode "Test" "A major mode for testing purposes." (setq-local font-lock-extra-managed-props '(line-spacing)) (setq font-lock-defaults '(test-fontifiers) line-spacing 0; We can enlarge it only, not zero it. Therefore zero ;;; it up front, then restore the default on all but starred lines. truncate-lines t) (defconst test-line-spacing-default 4); Assume this for test purposes. (defconst test-fontifiers (list (cons; A fontifier to control line spacing. (lambda (limit) (let ((p (point)) c found starred) (while (and (not found) (< p limit)) (setq c (char-after p)) (cond ((= c ?\x2a); A starred line ‘*’. (Not `?*` only because ;; it kills the syntax highlighter at Stack Exchange.) (setq starred t)) ((= c ?\n); A terminal newline. (if starred; Then keep going, seeking one that is unstarred. (setq starred nil) (setq found t)))); Else (being unstarred) set a line spacing. (setq p (1+ p))) (when found; The newline to propertize is just before `p`. (set-match-data (list (1- p) (goto-char p) (current-buffer))) t))); Returning t to Font Lock if `found`, else nil. '(0 (list 'face 'default 'line-spacing test-line-spacing-default)))))) (set 'auto-mode-alist (cons (cons "\\.test\\'" 'test-mode) auto-mode-alist))) (defun init/text-mode () "Configure text mode." (let ((m text-mode-map)) (define-key m [?\t] #'tab-to-tab-stop)) (add-to-list 'auto-mode-alist '("/ChangeLog$" . text-mode))); Was Change Log, which is not Gentoo format. (defun init/waybrec () "Configure Waybrec mode and other Waybreccian particulars." (load "waybrec-mode-autoloads" nil t); Q.v. at `http://reluk.ca/sys/computer/workstation/etc/emacs/`. (set 'auto-mode-alist (cons (cons "/way/.*\\.brec\\'" 'waybrec-mode) auto-mode-alist))) (defun init/wayscript-mode () "Configure `Wayscript mode`." ;;;(add-hook 'wayscript-mode-hook ;;; (lambda () ;;; (font-lock-add-keywords nil; 'nil' so it affects any derived modes, too. ;;; ;; ID assignment ;;; ;; ────────────- ;;; '((" id *= *\\(['\"]\\)\\(.+?\\)\\1" 2 font-lock-warning-face t))))) ;;; ;;; Override `init/sgml-mode`, toning down the colour soup. ;;;;;; Fails. (Is it how I defined Wayscript Mode? Or fontified it?). ;;;;;; Instead defeating in `init/sgml-mode`. (autoload 'wayscript-mode "wayscript-mode" init/autoload_docstring t); ↑ Q.v. at `http://reluk.ca/sys/computer/workstation/etc/emacs/`. (add-to-list 'auto-mode-alist '("^/home/mike/project/proto-waycast/.+\\.xht$" . wayscript-mode))) (defun init/x () "Configure Emacs for the X Window System." (global-set-key [S-down-mouse-1] #'mouse-save-then-kill); Was `mouse-set-font`. (global-set-key [mouse-3] #'mouse-set-font); The popup button, was mouse-save-then-kill. ;;; What was the purpose of this button swap? What button is `S-down-mouse-1`? ;;; (global-set-key [C-mouse-4] #'init/switch-to-previous-buffer) ;;; (global-set-key [C-mouse-5] #'init/switch-to-next-buffer) ;;; (global-unset-key [S-mouse-4]); Was `mwheel-scroll`, but (below) I bind that to `M-mouse-4`. ;;; (global-unset-key [S-mouse-5]) ;;;;;; I no longer have side buttons. (global-unset-key (kbd "")); Was `mouse-drag-secondary`. ; https://www.reddit.com/r/emacs/comments/3c61zl/abolish_the_secondary_selection_quick_and_easy/csszyto (global-unset-key (kbd "")); Was `mouse-set-secondary`. ; https://www.reddit.com/r/emacs/comments/3c61zl/abolish_the_secondary_selection_quick_and_easy/csszyto (global-unset-key (kbd "")); Was `mouse-start-secondary`. (global-unset-key (kbd "")); Was `mouse-yank-secondary`. (global-unset-key (kbd "")); Was `mouse-secondary-save-then-kill`. ;;; (global-set-key [M-mouse-4] #'mwheel-scroll) ;;; (global-set-key [M-mouse-5] #'mwheel-scroll) ;;;;;; I no longer have side buttons. (setq mouse-wheel-scroll-amount '(5 ((meta) . 1) )); 5 lines normally, but 1 when M modified. (setq select-enable-clipboard t); So the latest kill-ring entry is visible to other programs. (add-to-list 'default-frame-alist '(unsplittable . t)) ;; Cursor ;; ────── (blink-cursor-mode 0) ;;; (mouse-avoidance-mode 'cat-and-mouse) ;;;;;; But raises window whenever it scurries (defun adjust-cursor-type (&optional frame) "Set cursor style according to overwrite mode." (modify-frame-parameters (or frame (selected-frame)) (list (cons 'cursor-type (if overwrite-mode 'box 'bar))))) ;;; (defadvice overwrite-mode (after adjusted-cursor activate) (adjust-cursor-type)) ;;; (defadvice binary-overwrite-mode (after adjusted-cursor activate) (adjust-cursor-type)) ;;; (add-hook 'after-make-frame-functions #'adjust-cursor-type) ;;; (defadvice other-window (after adjusted-cursor activate) (adjust-cursor-type)) ;;; (defadvice switch-to-buffer (after adjusted-cursor activate) (adjust-cursor-type)) ;;;;;; Incomplete. Overwrite mode is per buffer, but cursor type is per frame. ;;;;;; Need more hooks to tie them together. Easier to use brute force: (add-hook 'post-command-hook #'adjust-cursor-type); OPT, it slows all commands. ;;;;;; But Emacs Lisp manual says the "buffer-local variable cursor-type overrides ;;;;;; the value of the cursor-type frame parameter", so maybe try that? (setq-default cursor-in-non-selected-windows 'hollow)) ;;; Else bar becomes (hardly distinguishable) thin bar. (provide 'user-initialization) ;; NOTES ;; ───── ;; BUG This code is incorrect. ;; ;; FV · Suppressing sporadic compiler warnings ‘reference to free variable’ ;; or ‘assignment to free variable’. ;; ;; IS · Marks a key binding for use during an incremental search, e.g. one initiated by `C-c`. ;; ;; TOP Too often pressed accidently.