diff options
-rw-r--r-- | init.el | 292 |
1 files changed, 286 insertions, 6 deletions
diff --git a/init.el b/init.el index 4648042..c654c07 100644 --- a/init.el +++ b/init.el | |||
@@ -9,12 +9,188 @@ | |||
9 | ;; | 9 | ;; |
10 | ;; For the tenth time! | 10 | ;; For the tenth time! |
11 | 11 | ||
12 | ;;; Packages | 12 | ;;; Code: |
13 | |||
14 | (load (locate-user-emacs-file "private")) | ||
15 | |||
16 | |||
17 | ;;; Definitions: | ||
18 | |||
19 | (defun other-window-or-switch-buffer (&optional arg) | ||
20 | "Switch to the other window. | ||
21 | If a window is the only buffer on a frame, switch buffer. When | ||
22 | run with \\[universal-argument], unconditionally switch buffer." | ||
23 | (interactive "P") | ||
24 | (if (or arg (one-window-p)) | ||
25 | (switch-to-buffer (other-buffer) nil t) | ||
26 | (other-window 1))) | ||
27 | |||
28 | (defun cycle-spacing@ (&optional n) | ||
29 | ;; `cycle-spacing' is wildly different in 29.1 over 28. | ||
30 | "Negate N argument on `cycle-spacing'. | ||
31 | That is, with a positive N, deletes newlines as well, leaving -N | ||
32 | spaces. If N is negative, it will not delete newlines and leave | ||
33 | N spaces." | ||
34 | (interactive "*p") | ||
35 | (cycle-spacing (- n))) | ||
36 | |||
37 | (defun first-frame@set-fonts () | ||
38 | (remove-hook 'server-after-make-frame-hook | ||
39 | #'first-frame@set-fonts) | ||
40 | (face-spec-set 'default | ||
41 | `((t :family "Recursive Mono Casual Static" | ||
42 | :height 110))) | ||
43 | ;; Emojis | ||
44 | (cl-loop with ffl = (font-family-list) | ||
45 | for font in '("Noto Emoji" "Noto Color Emoji" | ||
46 | "Segoe UI Emoji" "Apple Color Emoji" | ||
47 | "FreeSans" "FreeMono" "FreeSerif" | ||
48 | "Unifont" "Symbola") | ||
49 | if (member font ffl) | ||
50 | do (set-fontset-font t 'symbol font)) | ||
51 | ;; International fonts | ||
52 | (cl-loop with ffl = (font-family-list) | ||
53 | for (charset . font) | ||
54 | in '((latin . "Noto Sans") | ||
55 | (han . "Noto Sans CJK SC Regular") | ||
56 | (kana . "Noto Sans CJK JP Regular") | ||
57 | (hangul . "Noto Sans CJK KR Regular") | ||
58 | (cjk-misc . "Noto Sans CJK KR Regular") | ||
59 | (khmer . "Noto Sans Khmer") | ||
60 | (lao . "Noto Sans Lao") | ||
61 | (burmese . "Noto Sans Myanmar") | ||
62 | (thai . "Noto Sans Thai") | ||
63 | (ethiopic . "Noto Sans Ethiopic") | ||
64 | (hebrew . "Noto Sans Hebrew") | ||
65 | (arabic . "Noto Sans Arabic") | ||
66 | (gujarati . "Noto Sans Gujarati") | ||
67 | (devanagari . "Noto Sans Devanagari") | ||
68 | (kannada . "Noto Sans Kannada") | ||
69 | (malayalam . "Noto Sans Malayalam") | ||
70 | (oriya . "Noto Sans Oriya") | ||
71 | (sinhala . "Noto Sans Sinhala") | ||
72 | (tamil . "Noto Sans Tamil") | ||
73 | (telugu . "Noto Sans Telugu") | ||
74 | (tibetan . "Noto Sans Tibetan")) | ||
75 | if (member font ffl) | ||
76 | do (set-fontset-font t charset font)) | ||
77 | ;; XXX: tab-bar does a weird thing, so i set it up here.... | ||
78 | (setopt tab-bar-show t) | ||
79 | (tab-bar-mode)) | ||
80 | |||
81 | (defun renz/sort-by-alpha-length (elems) | ||
82 | "Sort ELEMS first alphabetically, then by length." | ||
83 | (sort elems (lambda (c1 c2) | ||
84 | (or (string-version-lessp c1 c2) | ||
85 | (< (length c1) (length c2)))))) | ||
86 | |||
87 | (defun renz/sort-by-history (elems) | ||
88 | "Sort ELEMS by minibuffer history. | ||
89 | Use `mct-sort-sort-by-alpha-length' if no history is available." | ||
90 | (if-let ((hist (and (not (eq minibuffer-history-variable t)) | ||
91 | (symbol-value minibuffer-history-variable)))) | ||
92 | (minibuffer--sort-by-position hist elems) | ||
93 | (renz/sort-by-alpha-length elems))) | ||
94 | |||
95 | (defun renz/completion-category () | ||
96 | "Return completion category." | ||
97 | (when-let ((window (active-minibuffer-window))) | ||
98 | (with-current-buffer (window-buffer window) | ||
99 | (completion-metadata-get | ||
100 | (completion-metadata (buffer-substring-no-properties | ||
101 | (minibuffer-prompt-end) | ||
102 | (max (minibuffer-prompt-end) (point))) | ||
103 | minibuffer-completion-table | ||
104 | minibuffer-completion-predicate) | ||
105 | 'category)))) | ||
106 | |||
107 | (defun renz/sort-multi-category (elems) | ||
108 | "Sort ELEMS per completion category." | ||
109 | (pcase (renz/completion-category) | ||
110 | ('nil elems) ; no sorting | ||
111 | ('kill-ring elems) | ||
112 | ('project-file (renz/sort-by-alpha-length elems)) | ||
113 | (_ (renz/sort-by-history elems)))) | ||
114 | |||
115 | (defvar no-tabs-modes '(emacs-lisp-mode | ||
116 | lisp-mode | ||
117 | scheme-mode | ||
118 | python-mode | ||
119 | haskell-mode) | ||
120 | "Modes /not/ to indent with tabs.") | ||
121 | |||
122 | (defun indent-tabs-mode-maybe () | ||
123 | (if (apply #'derived-mode-p no-tabs-modes) | ||
124 | (indent-tabs-mode -1) | ||
125 | (indent-tabs-mode 1))) | ||
126 | |||
127 | (define-minor-mode truncate-lines-mode | ||
128 | "Buffer-local mode to toggle `truncate-lines'." | ||
129 | :lighter "" | ||
130 | (setq-local truncate-lines truncate-lines-mode)) | ||
131 | |||
132 | ;;; Region or buffer stuff | ||
133 | |||
134 | (defun call-with-region-or-buffer (fn &rest _r) | ||
135 | "Call function FN with current region or buffer. | ||
136 | Good to use for :around advice." | ||
137 | (if (region-active-p) | ||
138 | (funcall fn (region-beginning) (region-end)) | ||
139 | (funcall fn (point-min) (point-max)))) | ||
140 | |||
141 | (defun delete-trailing-whitespace-except-current-line () | ||
142 | (save-excursion | ||
143 | (delete-trailing-whitespace (point-min) | ||
144 | (line-beginning-position)) | ||
145 | (delete-trailing-whitespace (line-end-position) | ||
146 | (point-max)))) | ||
147 | |||
148 | (defun create-missing-directories () | ||
149 | "Automatically create missing directories." | ||
150 | (let ((target-dir (file-name-directory buffer-file-name))) | ||
151 | (unless (file-exists-p target-dir) | ||
152 | (make-directory target-dir :parents)))) | ||
153 | |||
154 | |||
155 | (defun vc-remote-off () | ||
156 | "Turn VC off when remote." | ||
157 | (when (file-remote-p (buffer-file-name)) | ||
158 | (setq-local vc-handled-backends nil))) | ||
159 | |||
160 | (defun +titlecase-sentence-style-dwim (&optional arg) | ||
161 | "Titlecase a sentence. | ||
162 | With prefix ARG, toggle the value of | ||
163 | `titlecase-downcase-sentences' before sentence-casing." | ||
164 | (interactive "P") | ||
165 | (let ((titlecase-downcase-sentences (if arg (not titlecase-downcase-sentences) | ||
166 | titlecase-downcase-sentences))) | ||
167 | (titlecase-dwim 'sentence))) | ||
168 | |||
169 | (defun +titlecase-org-headings () | ||
170 | (interactive) | ||
171 | (require 'org) | ||
172 | (save-excursion | ||
173 | (goto-char (point-min)) | ||
174 | ;; See also `org-map-tree'. I'm not using that function because I want to | ||
175 | ;; skip the first headline. A better solution would be to patch | ||
176 | ;; `titlecase-line' to ignore org-mode metadata (TODO cookies, tags, etc). | ||
177 | (let ((level (funcall outline-level)) | ||
178 | (org-special-ctrl-a/e t)) | ||
179 | (while (and (progn (outline-next-heading) | ||
180 | (> (funcall outline-level) level)) | ||
181 | (not (eobp))) | ||
182 | (titlecase-region (progn (org-beginning-of-line) (point)) | ||
183 | (progn (org-end-of-line) (point))))))) | ||
184 | |||
185 | |||
186 | ;;; Packages: | ||
13 | 187 | ||
14 | (require 'package) | 188 | (require 'package) |
15 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) | 189 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) |
16 | (package-initialize) | 190 | (package-initialize) |
17 | 191 | ||
192 | ;; Install packages here. Acutal configuration is done in the Configuration | ||
193 | ;; section. | ||
18 | (dolist (pkg `(consult | 194 | (dolist (pkg `(consult |
19 | marginalia | 195 | marginalia |
20 | visual-fill-column | 196 | visual-fill-column |
@@ -31,9 +207,95 @@ | |||
31 | (package-refresh-contents) | 207 | (package-refresh-contents) |
32 | (package-install pkg)))) | 208 | (package-install pkg)))) |
33 | 209 | ||
34 | (load (locate-user-emacs-file "definitions")) | 210 | (dolist (local-pkg `(scule |
35 | (load (locate-user-emacs-file "packages")) | 211 | frowny |
36 | (load (locate-user-emacs-file "private")) | 212 | hippie-completing-read |
213 | mode-line-bell | ||
214 | titlecase | ||
215 | jabber)) | ||
216 | (add-to-list 'load-path | ||
217 | (expand-file-name | ||
218 | (format "~/src/%s.el" | ||
219 | (symbol-name local-pkg))))) | ||
220 | |||
221 | ;;; Jabber | ||
222 | |||
223 | (use-package jabber | ||
224 | :load-path "~/src/jabber.el" | ||
225 | :defer t | ||
226 | :bind-keymap (("C-c j" . jabber-global-keymap)) | ||
227 | :preface nil | ||
228 | (setq-default jabber-chat-buffer-format "*%n*" | ||
229 | jabber-browse-buffer-format "*%n*" | ||
230 | jabber-groupchat-buffer-format "*%n*" | ||
231 | jabber-muc-private-buffer-format "*%n*") | ||
232 | :custom-face | ||
233 | (jabber-activity-face ((t :inherit jabber-chat-prompt-foreign | ||
234 | :foreground unspecified | ||
235 | :weight normal))) | ||
236 | (jabber-activity-personal-face ((t :inherit jabber-chat-prompt-local | ||
237 | :foreground unspecified | ||
238 | :weight bold))) | ||
239 | (jabber-chat-prompt-local ((t :inherit minibuffer-prompt | ||
240 | :foreground unspecified | ||
241 | :weight normal | ||
242 | :slant italic))) | ||
243 | (jabber-chat-prompt-foreign ((t :inherit warning | ||
244 | :foreground unspecified | ||
245 | :weight normal))) | ||
246 | (jabber-chat-prompt-system ((t :inherit font-lock-doc-face | ||
247 | :foreground unspecified))) | ||
248 | (jabber-rare-time-face ((t :inherit font-lock-comment-face | ||
249 | :foreground unspecified | ||
250 | :underline nil))) | ||
251 | :config | ||
252 | (require 'jabber-httpupload nil t) | ||
253 | (setopt jabber-auto-reconnect t | ||
254 | jabber-last-read-marker "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" | ||
255 | jabber-muc-decorate-presence-patterns | ||
256 | '(("\\( enters the room ([^)]+)\\| has left the chatroom\\)$" . nil) | ||
257 | ("Mode #.*" . jabber-muc-presence-dim) | ||
258 | ("." . jabber-muc-presence-dim)) | ||
259 | jabber-activity-make-strings #'jabber-activity-make-strings-shorten | ||
260 | jabber-rare-time-format | ||
261 | (format " - - - - - %%H:%d %%F" | ||
262 | (let ((min (string-to-number (format-time-string "%M")))) | ||
263 | (* 5 (floor min 5)))) | ||
264 | jabber-muc-header-line-format '(" " jabber-muc-topic)) | ||
265 | |||
266 | (setopt jabber-groupchat-prompt-format "%n. " | ||
267 | jabber-chat-local-prompt-format "%n. " | ||
268 | jabber-chat-foreign-prompt-format "%n. " | ||
269 | jabber-muc-private-foreign-prompt-format "%g/%n. ") | ||
270 | |||
271 | (keymap-global-set "C-c C-SPC" #'jabber-activity-switch-to) | ||
272 | (map-keymap (lambda (key command) | ||
273 | (define-key jabber-global-keymap (vector (+ key #x60)) command)) | ||
274 | jabber-global-keymap) | ||
275 | (keymap-global-set "C-x C-j" #'dired-jump) | ||
276 | |||
277 | (add-hook 'jabber-post-connect-hooks #'jabber-enable-carbons) | ||
278 | (remove-hook 'jabber-alert-muc-hooks 'jabber-muc-echo) | ||
279 | (remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo) | ||
280 | (add-hook 'jabber-chat-mode-hook 'visual-line-mode) | ||
281 | (add-hook 'jabber-chat-mode-hook (defun jabber-no-position () | ||
282 | (setq-local mode-line-position nil))) | ||
283 | |||
284 | (add-hook 'jabber-alert-muc-hooks | ||
285 | (defun jabber@highlight-acdw (&optional _ _ buf _ _) | ||
286 | (when buf | ||
287 | (with-current-buffer buf | ||
288 | (let ((regexp (rx word-boundary | ||
289 | "acdw" ; maybe get from the config? | ||
290 | word-boundary))) | ||
291 | (hi-lock-unface-buffer regexp) | ||
292 | (highlight-regexp regexp 'jabber-chat-prompt-local)))))) | ||
293 | |||
294 | (when (fboundp 'jabber-chat-update-focus) | ||
295 | (add-hook 'window-configuration-change-hook #'jabber-chat-update-focus))) | ||
296 | |||
297 | |||
298 | ;;; Configuration: | ||
37 | 299 | ||
38 | (setopt custom-file (locate-user-emacs-file "custom.el")) | 300 | (setopt custom-file (locate-user-emacs-file "custom.el")) |
39 | (load custom-file :noerror) | 301 | (load custom-file :noerror) |
@@ -355,11 +617,12 @@ | |||
355 | (defun tab-bar-end-space () | 617 | (defun tab-bar-end-space () |
356 | `((end menu-item " " ignore))) | 618 | `((end menu-item " " ignore))) |
357 | 619 | ||
358 | (setopt tab-bar-show t) | 620 | |
359 | (add-to-list 'tab-bar-format 'tab-bar-format-align-right :append) | 621 | (add-to-list 'tab-bar-format 'tab-bar-format-align-right :append) |
360 | (add-to-list 'tab-bar-format 'tab-bar-format-global :append) | 622 | (add-to-list 'tab-bar-format 'tab-bar-format-global :append) |
361 | (add-to-list 'tab-bar-format 'tab-bar-end-space :append) | 623 | (add-to-list 'tab-bar-format 'tab-bar-end-space :append) |
362 | (tab-bar-mode) | 624 | ;;(setopt tab-bar-show t) |
625 | ;;(tab-bar-mode) ; done after setting fonts | ||
363 | 626 | ||
364 | ;;; Org mode | 627 | ;;; Org mode |
365 | 628 | ||
@@ -472,3 +735,20 @@ ORG-EXPORT-ARGS are passed to `org-export-to-file'." | |||
472 | 735 | ||
473 | (keymap-global-set "C-x C-b" #'ibuffer) | 736 | (keymap-global-set "C-x C-b" #'ibuffer) |
474 | (add-hook 'ibuffer-hook #'hl-line-mode) | 737 | (add-hook 'ibuffer-hook #'hl-line-mode) |
738 | |||
739 | (autoload 'scule-map "scule" nil nil 'keymap) | ||
740 | (keymap-global-set "M-c" 'scule-map) | ||
741 | (with-eval-after-load 'scule | ||
742 | (keymap-set scule-map "M-t" #'titlecase-dwim)) | ||
743 | |||
744 | ;; Use M-u for prefix keys | ||
745 | (keymap-global-set "M-u" #'universal-argument) | ||
746 | (keymap-set universal-argument-map "M-u" #'universal-argument-more) | ||
747 | |||
748 | (add-hook 'jabber-chat-mode-hook #'frowny-mode) | ||
749 | |||
750 | (keymap-global-set "M-/" #'hippie-completing-read) | ||
751 | |||
752 | (setopt mode-line-bell-flash-time 0.25) | ||
753 | (autoload 'mode-line-bell-mode "mode-line-bell" nil t) | ||
754 | (mode-line-bell-mode) | ||