diff options
-rw-r--r-- | emacs | 173 | ||||
-rw-r--r-- | emacs.d/early-init.el | 189 |
2 files changed, 279 insertions, 83 deletions
diff --git a/emacs b/emacs index e1df5dc..7b3d9ea 100644 --- a/emacs +++ b/emacs | |||
@@ -13,6 +13,8 @@ | |||
13 | 13 | ||
14 | ;; (load-theme 'modus-operandi :no-confirm) | 14 | ;; (load-theme 'modus-operandi :no-confirm) |
15 | 15 | ||
16 | ;;; Custom functions | ||
17 | |||
16 | (define-advice startup-echo-area-message (:override ()) | 18 | (define-advice startup-echo-area-message (:override ()) |
17 | (if (get-buffer "*Warnings*") | 19 | (if (get-buffer "*Warnings*") |
18 | ";_;" | 20 | ";_;" |
@@ -61,8 +63,11 @@ If already so, run FUNC immediately." | |||
61 | (set-face-attribute 'variable-pitch nil | 63 | (set-face-attribute 'variable-pitch nil |
62 | :family (first-found-font "Public Sans") | 64 | :family (first-found-font "Public Sans") |
63 | :height 1.0) | 65 | :height 1.0) |
66 | (set-face-attribute 'fixed-pitch nil | ||
67 | :family (first-found-font "Recursive Mono Linear Static")) | ||
64 | (set-face-attribute 'fixed-pitch-serif nil | 68 | (set-face-attribute 'fixed-pitch-serif nil |
65 | :inherit 'default) | 69 | :family (first-found-font "Go Mono" |
70 | "DejaVu Sans Mono")) | ||
66 | 71 | ||
67 | ;; Emojis | 72 | ;; Emojis |
68 | (cl-loop with ffl = (font-family-list) | 73 | (cl-loop with ffl = (font-family-list) |
@@ -100,62 +105,6 @@ If already so, run FUNC immediately." | |||
100 | if (member font ffl) | 105 | if (member font ffl) |
101 | do (set-fontset-font t charset font))) | 106 | do (set-fontset-font t charset font))) |
102 | 107 | ||
103 | (defun cancel-colors () | ||
104 | (mapatoms | ||
105 | (lambda (atom) | ||
106 | (when (facep atom) | ||
107 | (set-face-attribute atom nil | ||
108 | :foreground 'unspecified | ||
109 | :background 'unspecified | ||
110 | :weight 'unspecified | ||
111 | :slant 'unspecified | ||
112 | :underline 'unspecified | ||
113 | :overline 'unspecified | ||
114 | :strike-through 'unspecified | ||
115 | :box 'unspecified | ||
116 | :inverse-video nil | ||
117 | :stipple nil | ||
118 | :extend nil))))) | ||
119 | |||
120 | (defun set-faces (faces) | ||
121 | (dolist (face faces) | ||
122 | (apply #'set-face-attribute (car face) nil (cdr face)))) | ||
123 | |||
124 | (font-lock-mode -1) | ||
125 | (cancel-colors) | ||
126 | (run-after-frame-init | ||
127 | (defun uncancel-colors () | ||
128 | (set-faces `((font-lock-comment-face :italic t) | ||
129 | (font-lock-comment-delimiter-face :italic t) | ||
130 | (font-lock-doc-face :italic t) | ||
131 | (font-lock-keyword-face :bold t) | ||
132 | (bold :bold t) (italic :italic t) (underline :underline t) | ||
133 | (mode-line-active :box t :background "light goldenrod") | ||
134 | (mode-line-inactive :box "pale goldenrod" | ||
135 | :background "pale goldenrod") | ||
136 | (link :foreground "navy" :underline t) | ||
137 | (completions-annotations :inherit 'italic) | ||
138 | (highlight :background "wheat") | ||
139 | (match :background "wheat") | ||
140 | (isearch :background "sea green" :foreground "white") | ||
141 | (isearch-fail :background "tomato") | ||
142 | (lazy-highlight :background "dark sea green") | ||
143 | (region :background "papaya whip") | ||
144 | (query-replace :background "wheat") | ||
145 | (shadow :foreground "orchid") | ||
146 | (show-paren-match :background "goldenrod") | ||
147 | (success :foreground "sea green" :italic t) | ||
148 | (warning :foreground "brown" :italic t) | ||
149 | (error :foreground "red" :italic t) | ||
150 | (trailing-whitespace :background "tomato") | ||
151 | (vertical-border :foreground "gray") | ||
152 | (completions-highlight :background "wheat"))))) | ||
153 | |||
154 | (defun recancel-colors () | ||
155 | (interactive) | ||
156 | (cancel-colors) | ||
157 | (uncancel-colors)) | ||
158 | |||
159 | (defmacro inhibit-messages (&rest body) | 108 | (defmacro inhibit-messages (&rest body) |
160 | "Inhibit all messages in BODY." | 109 | "Inhibit all messages in BODY." |
161 | (declare (indent defun)) | 110 | (declare (indent defun)) |
@@ -216,8 +165,20 @@ With ARG, edit in other window." | |||
216 | (unless (package-installed-p pkg) | 165 | (unless (package-installed-p pkg) |
217 | (package-install pkg))) | 166 | (package-install pkg))) |
218 | 167 | ||
168 | (defun minibuffer-delete-directory () | ||
169 | (interactive) | ||
170 | (let ((here (point)) | ||
171 | (meta (completion-metadata | ||
172 | "" minibuffer-completion-table | ||
173 | minibuffer-completion-predicate))) | ||
174 | (if (eq (completion-metadata-get meta 'category) 'file) | ||
175 | (when (search-backward "/" (minibuffer-prompt-end) t) | ||
176 | (delete-region (point) here)) | ||
177 | (backward-kill-word 1)))) | ||
178 | |||
219 | ;;; Basic settings | 179 | ;;; Basic settings |
220 | 180 | ||
181 | (recancel-colors) | ||
221 | (tooltip-mode -1) | 182 | (tooltip-mode -1) |
222 | 183 | ||
223 | ;; Dialogs | 184 | ;; Dialogs |
@@ -238,6 +199,7 @@ With ARG, edit in other window." | |||
238 | 199 | ||
239 | ;; Whitespace | 200 | ;; Whitespace |
240 | (setopt whitespace-style '(face trailing tabs tab-mark)) | 201 | (setopt whitespace-style '(face trailing tabs tab-mark)) |
202 | (setopt whitespace-global-modes '(not rcirc-mode)) | ||
241 | (global-whitespace-mode) | 203 | (global-whitespace-mode) |
242 | (add-hook 'before-save-hook #'delete-trailing-whitespace-except-current-line) | 204 | (add-hook 'before-save-hook #'delete-trailing-whitespace-except-current-line) |
243 | (set-face-attribute 'whitespace-tab nil :background nil :foreground "#888") | 205 | (set-face-attribute 'whitespace-tab nil :background nil :foreground "#888") |
@@ -265,6 +227,7 @@ With ARG, edit in other window." | |||
265 | 227 | ||
266 | (keymap-set minibuffer-local-map "C-p" #'minibuffer-previous-completion) | 228 | (keymap-set minibuffer-local-map "C-p" #'minibuffer-previous-completion) |
267 | (keymap-set minibuffer-local-map "C-n" #'minibuffer-next-completion) | 229 | (keymap-set minibuffer-local-map "C-n" #'minibuffer-next-completion) |
230 | (keymap-set minibuffer-local-map "M-DEL" #'minibuffer-delete-directory) | ||
268 | 231 | ||
269 | (setopt enable-recursive-minibuffers t) | 232 | (setopt enable-recursive-minibuffers t) |
270 | (setopt minibuffer-default-prompt-format " [%s]") | 233 | (setopt minibuffer-default-prompt-format " [%s]") |
@@ -319,6 +282,10 @@ With ARG, edit in other window." | |||
319 | (package-ensure 'marginalia) | 282 | (package-ensure 'marginalia) |
320 | (marginalia-mode) | 283 | (marginalia-mode) |
321 | 284 | ||
285 | ;;; Frames / Windows | ||
286 | |||
287 | (winner-mode) | ||
288 | |||
322 | ;;; Files | 289 | ;;; Files |
323 | 290 | ||
324 | (setopt auto-revert-verbose nil) | 291 | (setopt auto-revert-verbose nil) |
@@ -334,13 +301,17 @@ With ARG, edit in other window." | |||
334 | (setopt auto-save-no-message t) | 301 | (setopt auto-save-no-message t) |
335 | (setopt auto-save-interval 2) | 302 | (setopt auto-save-interval 2) |
336 | (setopt auto-save-timeout 2) | 303 | (setopt auto-save-timeout 2) |
337 | (setopt auto-save-visited-interval 2) | 304 | (setopt auto-save-visited-interval 5) |
338 | (setopt remote-file-name-inhibit-auto-save t) | 305 | (setopt remote-file-name-inhibit-auto-save t) |
339 | (setopt remote-file-name-inhibit-auto-save-visited t) | 306 | (setopt remote-file-name-inhibit-auto-save-visited t) |
340 | (add-to-list 'auto-save-file-name-transforms | 307 | (add-to-list 'auto-save-file-name-transforms |
341 | `(".*" ,(locate-user-emacs-file "auto-save/") t)) | 308 | `(".*" ,(locate-user-emacs-file "auto-save/") t)) |
342 | (auto-save-visited-mode) | 309 | (auto-save-visited-mode) |
343 | 310 | ||
311 | (add-function :after after-focus-change-function | ||
312 | (defun focus-out-save () | ||
313 | (save-some-buffers t))) | ||
314 | |||
344 | (setopt backup-by-copying t) | 315 | (setopt backup-by-copying t) |
345 | (setopt version-control t) | 316 | (setopt version-control t) |
346 | (setopt kept-new-versions 3) | 317 | (setopt kept-new-versions 3) |
@@ -370,8 +341,15 @@ With ARG, edit in other window." | |||
370 | 341 | ||
371 | ;;; Buffers | 342 | ;;; Buffers |
372 | 343 | ||
344 | ;; Unique names | ||
373 | (setopt uniquify-buffer-name-style 'forward) | 345 | (setopt uniquify-buffer-name-style 'forward) |
374 | 346 | ||
347 | ;; Persistent undo | ||
348 | (package-ensure 'undo-fu-session) | ||
349 | (setopt undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'" | ||
350 | "/git-rebase-todo\\'")) | ||
351 | (undo-fu-session-global-mode) | ||
352 | |||
375 | ;; Encodings | 353 | ;; Encodings |
376 | (set-language-environment "UTF-8") | 354 | (set-language-environment "UTF-8") |
377 | (setopt buffer-file-coding-system 'utf-8-unix) | 355 | (setopt buffer-file-coding-system 'utf-8-unix) |
@@ -397,7 +375,6 @@ With ARG, edit in other window." | |||
397 | (setopt isearch-lazy-count t) | 375 | (setopt isearch-lazy-count t) |
398 | (setopt isearch-regexp-lax-whitespace t) | 376 | (setopt isearch-regexp-lax-whitespace t) |
399 | (setopt isearch-wrap-pause 'no) | 377 | (setopt isearch-wrap-pause 'no) |
400 | (setopt search-default-mode t) | ||
401 | (setopt search-whitespace-regexp ".*?") ; swiper-style | 378 | (setopt search-whitespace-regexp ".*?") ; swiper-style |
402 | (setopt search-ring-max 256) | 379 | (setopt search-ring-max 256) |
403 | (setopt regexp-search-ring-max 256) | 380 | (setopt regexp-search-ring-max 256) |
@@ -416,6 +393,11 @@ With ARG, edit in other window." | |||
416 | (keymap-set isearch-mb-minibuffer-map "M-s l" #'consult-line))) | 393 | (keymap-set isearch-mb-minibuffer-map "M-s l" #'consult-line))) |
417 | (isearch-mb-mode) | 394 | (isearch-mb-mode) |
418 | 395 | ||
396 | ;; Default to regexen | ||
397 | (setopt search-default-mode t) ; Isearch | ||
398 | (keymap-global-set "M-%" #'query-replace-regexp) | ||
399 | (keymap-global-set "C-M-%" #'query-replace) | ||
400 | |||
419 | ;;; Keybinds | 401 | ;;; Keybinds |
420 | 402 | ||
421 | (keymap-global-set "C-x C-c" #'save-buffers-kill-terminal) | 403 | (keymap-global-set "C-x C-c" #'save-buffers-kill-terminal) |
@@ -432,7 +414,7 @@ With ARG, edit in other window." | |||
432 | (keymap-global-set "C-c p" #'find-user-private-file) | 414 | (keymap-global-set "C-c p" #'find-user-private-file) |
433 | (keymap-global-set "C-c s" #'eshell) | 415 | (keymap-global-set "C-c s" #'eshell) |
434 | 416 | ||
435 | (keymap-global-set "C-c t" | 417 | (keymap-global-set "C-c d" |
436 | (defun insert-current-iso8601 () | 418 | (defun insert-current-iso8601 () |
437 | (interactive) | 419 | (interactive) |
438 | (insert (format-time-string "%FT%TZ" (current-time) t)))) | 420 | (insert (format-time-string "%FT%TZ" (current-time) t)))) |
@@ -441,7 +423,20 @@ With ARG, edit in other window." | |||
441 | (defun indent-buffer () | 423 | (defun indent-buffer () |
442 | (interactive) | 424 | (interactive) |
443 | (save-mark-and-excursion | 425 | (save-mark-and-excursion |
444 | (indent-region (point-min) (point-max))))) | 426 | (indent-region (point-min) (point-max)) |
427 | (if (apply #'derived-mode-p space-indent-modes) | ||
428 | (untabify (point-min) (point-max)) | ||
429 | (tabify (point-min) (point-max)))))) | ||
430 | |||
431 | (keymap-global-set "C-c t" | ||
432 | (define-keymap | ||
433 | :prefix 'toggle-map | ||
434 | "e" #'toggle-debug-on-error | ||
435 | "q" #'toggle-debug-on-quit | ||
436 | "c" #'column-number-mode | ||
437 | "l" #'line-number-mode | ||
438 | "L" #'display-line-numbers-mode | ||
439 | "t" #'truncate-lines-local-mode)) | ||
445 | 440 | ||
446 | ;; Un-keybinds | 441 | ;; Un-keybinds |
447 | (keymap-global-unset "C-<wheel-down>" t) | 442 | (keymap-global-unset "C-<wheel-down>" t) |
@@ -456,6 +451,7 @@ With ARG, edit in other window." | |||
456 | ;;; Writing | 451 | ;;; Writing |
457 | 452 | ||
458 | (add-hook 'text-mode-hook #'visual-line-mode) | 453 | (add-hook 'text-mode-hook #'visual-line-mode) |
454 | (add-hook 'text-mode-hook #'auto-fill-mode) | ||
459 | 455 | ||
460 | ;;; Hungry delete | 456 | ;;; Hungry delete |
461 | ;; I was using the hungry-delete package, but it turns out I can get *most* of | 457 | ;; I was using the hungry-delete package, but it turns out I can get *most* of |
@@ -576,19 +572,28 @@ With ARG, edit in other window." | |||
576 | (setopt compilation-always-kill t) | 572 | (setopt compilation-always-kill t) |
577 | (setopt compilation-ask-about-save nil) | 573 | (setopt compilation-ask-about-save nil) |
578 | 574 | ||
575 | ;;; Languages | ||
576 | |||
577 | (package-ensure 'gemtext-mode) | ||
578 | |||
579 | ;;; Miscellaneous settings | 579 | ;;; Miscellaneous settings |
580 | 580 | ||
581 | (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) | 581 | (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) |
582 | (add-hook 'prog-mode-hook #'auto-fill-mode) | 582 | (add-hook 'prog-mode-hook #'auto-fill-mode) |
583 | (add-hook 'prog-mode-hook #'display-fill-column-indicator-mode) | ||
584 | (add-hook 'prog-mode-hook #'electric-pair-local-mode) | 583 | (add-hook 'prog-mode-hook #'electric-pair-local-mode) |
584 | (global-display-fill-column-indicator-mode) | ||
585 | (delete-selection-mode) | 585 | (delete-selection-mode) |
586 | (global-so-long-mode) | 586 | (global-so-long-mode) |
587 | (global-goto-address-mode) | ||
588 | (context-menu-mode) | ||
589 | (setopt scroll-conservatively 101) | ||
587 | (setopt display-fill-column-indicator-character ?·) | 590 | (setopt display-fill-column-indicator-character ?·) |
588 | (setopt disabled-command-function nil) | 591 | (setopt disabled-command-function nil) |
589 | (setopt electric-pair-skip-whitespace 'chomp) | 592 | (setopt electric-pair-skip-whitespace 'chomp) |
590 | (setopt fill-column 80) | 593 | (setopt fill-column 80) |
591 | (setopt recenter-positions '(top middle bottom)) | 594 | (setopt recenter-positions '(top middle bottom)) |
595 | (setopt eval-expression-print-level nil) | ||
596 | (setopt eval-expression-print-length nil) | ||
592 | (setopt show-paren-delay 0.01) | 597 | (setopt show-paren-delay 0.01) |
593 | (setopt show-paren-style 'parenthesis) | 598 | (setopt show-paren-style 'parenthesis) |
594 | (setopt show-paren-when-point-in-periphery t) | 599 | (setopt show-paren-when-point-in-periphery t) |
@@ -612,31 +617,45 @@ With ARG, edit in other window." | |||
612 | (call-interactively #'rcirc)) | 617 | (call-interactively #'rcirc)) |
613 | 618 | ||
614 | (add-hook 'rcirc-mode-hook | 619 | (add-hook 'rcirc-mode-hook |
615 | (defun @rcirc-mode () | 620 | (defun @rcirc () |
616 | (whitespace-mode -1))) | 621 | (rcirc-track-minor-mode) |
617 | 622 | (rcirc-omit-mode) | |
618 | (add-hook 'rcirc-mode-hook #'rcirc-track-minor-mode) | 623 | (visual-line-mode) |
619 | (add-hook 'rcirc-mode-hook #'rcirc-omit-mode) | 624 | (setq default-directory (expand-file-name "~")))) |
620 | (add-hook 'rcirc-mode-hook #'visual-line-mode) | ||
621 | (add-hook 'rcirc-mode-hook | ||
622 | (lambda () (whitespace-mode -1))) | ||
623 | 625 | ||
624 | ;;; Eshell | 626 | ;;; Eshell |
625 | 627 | ||
628 | (setopt eshell-banner-message | ||
629 | (format "%s\n\n" (mapconcat #'identity | ||
630 | (process-lines "fortune" "-s") | ||
631 | "\n"))) | ||
626 | (setopt eshell-prompt-function | 632 | (setopt eshell-prompt-function |
627 | (defun @eshell-prompt () | 633 | (defun @eshell-prompt () |
628 | (concat "* " (abbreviate-file-name (eshell/pwd)) | 634 | (let ((rootp (zerop (user-uid)))) |
629 | (if (zerop (user-uid)) " # " " $ ")))) | 635 | (propertize |
636 | (concat "( " | ||
637 | (abbreviate-file-name (eshell/pwd)) | ||
638 | (if rootp ":root" "") | ||
639 | " ) ") | ||
640 | 'face 'bold)))) | ||
641 | (setopt eshell-prompt-regexp "^(.*) ") | ||
630 | 642 | ||
631 | ;;; Browsing | 643 | ;;; Browsing |
632 | 644 | ||
633 | ;; Dired (files) | 645 | ;; Dired (files) |
646 | (setopt dired-dwim-target t) | ||
647 | (setopt dired-listing-switches "-AlF") | ||
648 | (setopt dired-ls-F-marks-symlinks t) | ||
649 | (setopt dired-recursive-copies 'always) | ||
650 | (setopt dired-recursive-deletes 'always) | ||
651 | (setopt dired-auto-revert-buffer t) | ||
652 | (setopt dired-hide-details-hide-symlink-targets nil) | ||
634 | (with-eval-after-load 'dired | 653 | (with-eval-after-load 'dired |
635 | (setopt dired-dwim-target t) | 654 | (require 'dired-x) |
636 | (setopt dired-listing-switches "-alF") | 655 | (add-hook 'dired-mode-hook #'dired-hide-details-mode) |
637 | (setopt dired-ls-F-marks-symlinks t) | 656 | (add-hook 'dired-mode-hook #'hl-line-mode) |
638 | (keymap-set dired-mode-map "C-j" #'dired-up-directory) | 657 | (add-hook 'dired-mode-hook #'truncate-lines-local-mode) |
639 | (add-hook 'dired-mode-hook #'hl-line-mode)) | 658 | (keymap-set dired-mode-map "C-j" #'dired-up-directory)) |
640 | 659 | ||
641 | ;; Elpher (gemini/gopher) | 660 | ;; Elpher (gemini/gopher) |
642 | (package-ensure 'elpher) | 661 | (package-ensure 'elpher) |
diff --git a/emacs.d/early-init.el b/emacs.d/early-init.el index 7e7c431..2a4f2b5 100644 --- a/emacs.d/early-init.el +++ b/emacs.d/early-init.el | |||
@@ -5,17 +5,194 @@ | |||
5 | (setopt frame-resize-pixelwise t) | 5 | (setopt frame-resize-pixelwise t) |
6 | (setopt window-resize-pixelwise t) | 6 | (setopt window-resize-pixelwise t) |
7 | (setopt default-frame-alist | 7 | (setopt default-frame-alist |
8 | '((background-color . "alice blue") | 8 | '((menu-bar-lines . 0) |
9 | (font . "Recursive Mono Casual Static 10") | ||
10 | (menu-bar-lines . 0) | ||
11 | (tool-bar-lines . 0) | 9 | (tool-bar-lines . 0) |
12 | (vertical-scroll-bars) | 10 | (vertical-scroll-bars) |
13 | (horizontal-scroll-bars))) | 11 | (horizontal-scroll-bars))) |
14 | 12 | ||
13 | (defvar *fonts* | ||
14 | '((default | ||
15 | :family ("Recursive Mono Casual Static" "DejaVu Sans Mono") | ||
16 | :height 100) | ||
17 | (variable-pitch | ||
18 | :family ("Public Sans" "DejaVu Sans") | ||
19 | :height 1.0) | ||
20 | (fixed-pitch | ||
21 | :family ("Recursive Mono Linear Static" "DejaVu Sans Mono")) | ||
22 | (fixed-pitch-serif | ||
23 | :family ("Recursive Mono Linear Static" "DejaVu Sans Mono")))) | ||
24 | |||
15 | (require 'package) | 25 | (require 'package) |
16 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) | 26 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) |
17 | (package-initialize) | 27 | (package-initialize) |
18 | 28 | ||
19 | (setopt inhibit-startup-screen t) | 29 | ;;; Custom functions |
20 | (setopt initial-buffer-choice #'eshell) | 30 | |
21 | (setopt initial-scratch-message nil) | 31 | (defun pulse@eval (start end &rest _) |
32 | "ADVICE: makes `pulse-momentary-highlight-region' accept other arities." | ||
33 | (pulse-momentary-highlight-region start end)) | ||
34 | |||
35 | (defun create-missing-directories () | ||
36 | "Automatically create missing directories." | ||
37 | (let ((target-dir (file-name-directory buffer-file-name))) | ||
38 | (unless (file-exists-p target-dir) | ||
39 | (make-directory target-dir :parents)))) | ||
40 | |||
41 | (defun delete-trailing-whitespace-except-current-line () | ||
42 | "Delete all trailing whitespace except current line." | ||
43 | (save-excursion | ||
44 | (delete-trailing-whitespace (point-min) | ||
45 | (line-beginning-position)) | ||
46 | (delete-trailing-whitespace (line-end-position) | ||
47 | (point-max)))) | ||
48 | |||
49 | (defun run-after-frame-init (func) | ||
50 | "Run FUNC after the first frame is initialized. | ||
51 | If already so, run FUNC immediately." | ||
52 | (cond | ||
53 | ((daemonp) | ||
54 | (add-hook 'server-after-make-frame-hook func) | ||
55 | (advice-add func :after (lambda () | ||
56 | (remove-hook 'server-after-make-frame-hook | ||
57 | func) | ||
58 | (advice-remove func | ||
59 | 'after-frame-init-removing-advice)) | ||
60 | |||
61 | |||
62 | '((name . after-frame-init-removing-advice)))) | ||
63 | ((not after-init-time) | ||
64 | (add-hook 'after-init-hook func)) | ||
65 | (:else (funcall func)))) | ||
66 | |||
67 | (defun first-found-font (&rest cands) | ||
68 | "Return the first font of CANDS that is installed, or nil." | ||
69 | (cl-loop with ffl = (font-family-list) | ||
70 | for font in cands | ||
71 | if (member font ffl) | ||
72 | return font)) | ||
73 | |||
74 | (defun setup-faces () | ||
75 | "Setup Emacs faces." | ||
76 | ;; Default faces | ||
77 | (cl-loop for (face . spec) in *fonts* | ||
78 | do (set-face-attribute face nil | ||
79 | :family (apply #'first-found-font | ||
80 | (plist-get spec :family)) | ||
81 | :height (or (plist-get spec :height) | ||
82 | 'unspecified))) | ||
83 | ;; Specialized fonts | ||
84 | (cl-loop with ffl = (font-family-alist) | ||
85 | for (charset . font) | ||
86 | in '((latin . "Noto Sans") | ||
87 | (han . "Noto Sans CJK SC Regular") | ||
88 | (kana . "Noto Sans CJK JP Regular") | ||
89 | (hangul . "Noto Sans CJK KR Regular") | ||
90 | (cjk-misc . "Noto Sans CJK KR Regular") | ||
91 | (khmer . "Noto Sans Khmer") | ||
92 | (lao . "Noto Sans Lao") | ||
93 | (burmese . "Noto Sans Myanmar") | ||
94 | (thai . "Noto Sans Thai") | ||
95 | (ethiopic . "Noto Sans Ethiopic") | ||
96 | (hebrew . "Noto Sans Hebrew") | ||
97 | (arabic . "Noto Sans Arabic") | ||
98 | (gujarati . "Noto Sans Gujarati") | ||
99 | (devanagari . "Noto Sans Devanagari") | ||
100 | (kannada . "Noto Sans Kannada") | ||
101 | (malayalam . "Noto Sans Malayalam") | ||
102 | (oriya . "Noto Sans Oriya") | ||
103 | (sinhala . "Noto Sans Sinhala") | ||
104 | (tamil . "Noto Sans Tamil") | ||
105 | (telugu . "Noto Sans Telugu") | ||
106 | (tibetan . "Noto Sans Tibetan") | ||
107 | ;; emojis | ||
108 | (symbol . "Noto Emoji") | ||
109 | (symbol . "Noto Color Emoji") | ||
110 | (symbol . "Segoe UI Emoji") | ||
111 | (symbol . "Apple Color Emoji") | ||
112 | (symbol . "FreeSans") | ||
113 | (symbol . "FreeMono") | ||
114 | (symbol . "FreeSerif") | ||
115 | (symbol . "Unifont") | ||
116 | (symbol . "Symbola")) | ||
117 | if (member font ffl) | ||
118 | do (set-fontset-font t charset font))) | ||
119 | |||
120 | (defmacro inhibit-messages (&rest body) | ||
121 | "Inhibit all messages in BODY." | ||
122 | (declare (indent defun)) | ||
123 | `(cl-letf (((symbol-function 'message) #'ignore)) | ||
124 | ,@body)) | ||
125 | |||
126 | (defun kill-buffer-dwim (&optional buffer-or-name) | ||
127 | "Kill BUFFER-OR-NAME or the current buffer." | ||
128 | (interactive "P") | ||
129 | (cond | ||
130 | ((bufferp buffer-or-name) | ||
131 | (kill-buffer buffer-or-name)) | ||
132 | ((null buffer-or-name) | ||
133 | (kill-current-buffer)) | ||
134 | (:else | ||
135 | (kill-buffer (read-buffer "Kill: " nil :require-match))))) | ||
136 | |||
137 | (defun other-window-dwim (&optional arg) | ||
138 | "Switch to another window/buffer. | ||
139 | Calls `other-window', which see, unless | ||
140 | - the current window is alone on its frame | ||
141 | - `other-window-dwim' is called with \\[universal-argument] | ||
142 | In these cases, switch to the last-used buffer." | ||
143 | (interactive "P") | ||
144 | (if (or arg (one-window-p)) | ||
145 | (switch-to-buffer (other-buffer) nil t) | ||
146 | (other-window 1))) | ||
147 | |||
148 | (defun delete-window-dwim () | ||
149 | "Delete the current window or bury its buffer. | ||
150 | If the current window is alone in its frame, bury the buffer | ||
151 | instead." | ||
152 | (interactive) | ||
153 | (unless (ignore-errors (delete-window) t) | ||
154 | (bury-buffer))) | ||
155 | |||
156 | (defun cycle-spacing* (&optional n) | ||
157 | "Negate N argument on `cycle-spacing'." | ||
158 | (interactive "*p") | ||
159 | (cycle-spacing (- n))) | ||
160 | |||
161 | (defmacro find-user-file (name &optional file-name) | ||
162 | "Template macro to generate user file finding functions." | ||
163 | (declare (indent 1)) | ||
164 | (let ((file-name (or file-name (intern (format "user-%s-file" name)))) | ||
165 | (func-name (intern (format "find-user-%s-file" name)))) | ||
166 | `(defun ,func-name (&optional arg) | ||
167 | ,(format "Edit `%s' in the current window. | ||
168 | With ARG, edit in the other window." file-name) | ||
169 | (interactive "P") | ||
170 | (funcall (if arg #'find-file-other-window #'find-file) | ||
171 | ,file-name)))) | ||
172 | |||
173 | (defun indent-buffer+ () | ||
174 | "Indent the current buffer and (un)`tabify'. | ||
175 | Whether it tabifies or untabifies depends on `space-indent-modes'." | ||
176 | (interactive) | ||
177 | (save-mark-and-excursion | ||
178 | (indent-region (point-min) (point-max)) | ||
179 | (if (apply #'derived-mode-p space-indent-modes) | ||
180 | (untabify (point-min) (point-max)) | ||
181 | (tabify (point-min) (point-max))))) | ||
182 | |||
183 | (defun package-ensure (pkg) | ||
184 | "Install PKG if it's not already installed." | ||
185 | (unless (package-installed-p pkg) | ||
186 | (package-install pkg))) | ||
187 | |||
188 | (defun minibuffer-delete-directory () | ||
189 | "Delete the last directory in a file-completing minibuffer." | ||
190 | (interactive) | ||
191 | (let ((here (point)) | ||
192 | (meta (completion-metadata | ||
193 | "" minibuffer-completion-table | ||
194 | minibuffer-completion-predicate))) | ||
195 | (if (eq (completion-metadata-get meta 'category) 'file) | ||
196 | (when (search-backward "/" (minibuffer-prompt-end) t) | ||
197 | (delete-region (point) here)) | ||
198 | (backward-kill-word 1)))) | ||