diff options
-rw-r--r-- | basics.el | 315 | ||||
-rw-r--r-- | early-init.el | 15 | ||||
-rw-r--r-- | init.el | 451 | ||||
-rw-r--r-- | lisp/acdw.el | 39 |
4 files changed, 768 insertions, 52 deletions
diff --git a/basics.el b/basics.el index 06f5ece..4e9e0a9 100644 --- a/basics.el +++ b/basics.el | |||
@@ -8,41 +8,15 @@ | |||
8 | 8 | ||
9 | ;;; Code: | 9 | ;;; Code: |
10 | 10 | ||
11 | ;;; Directories | 11 | (push (locate-user-emacs-file "lisp/") load-path) |
12 | (require 'acdw) | ||
12 | 13 | ||
13 | (defmacro defdir (name directory &optional docstring makedir) | 14 | ;;; Directories |
14 | "Define a variable and a function NAME expanding to DIRECTORY. | ||
15 | DOCSTRING is applied to the variable; its default is DIRECTORY's | ||
16 | path. If MAKEDIR is non-nil, the directory and its parents will | ||
17 | be created." | ||
18 | (declare (indent 2) (doc-string 3)) | ||
19 | `(progn | ||
20 | (defvar ,name (expand-file-name ,directory) | ||
21 | ,(concat (or docstring (format "%s" directory)) "\n" | ||
22 | "Defined by `defdir'.")) | ||
23 | (defun ,name (file &optional mkdir) | ||
24 | ,(concat "Expand FILE relative to variable `" (symbol-name name) "'.\n" | ||
25 | "If MKDIR is non-nil, parent directories are created.\n" | ||
26 | "Defined by `defdir'.") | ||
27 | (let ((file-name (expand-file-name | ||
28 | (convert-standard-filename file) ,name))) | ||
29 | (when mkdir | ||
30 | (make-directory (file-name-directory file-name) :parents)) | ||
31 | file-name)) | ||
32 | ,(if makedir | ||
33 | `(make-directory ,directory :parents) | ||
34 | `(unless (file-exists-p ,directory) | ||
35 | (warn "Directory `%s' doesn't exist." ,directory))))) | ||
36 | 15 | ||
37 | (defdir etc/ (locate-user-emacs-file "etc/") | 16 | (defdir etc/ (locate-user-emacs-file "etc/") |
38 | "Where various Emacs files are placed." | 17 | "Where various Emacs files are placed." |
39 | :makedir) | 18 | :makedir) |
40 | 19 | ||
41 | (defdir lisp/ (locate-user-emacs-file "lisp/") | ||
42 | "My bespoke elisp files." | ||
43 | :makedir) | ||
44 | (push lisp/ load-path) | ||
45 | |||
46 | (defdir sync/ "~/Sync/" | 20 | (defdir sync/ "~/Sync/" |
47 | "My Syncthing directory." | 21 | "My Syncthing directory." |
48 | :makedir) | 22 | :makedir) |
@@ -50,7 +24,6 @@ be created." | |||
50 | (defdir private/ (sync/ "emacs/private/") | 24 | (defdir private/ (sync/ "emacs/private/") |
51 | "Private files and stuff." | 25 | "Private files and stuff." |
52 | :makedir) | 26 | :makedir) |
53 | (push private/ load-path) | ||
54 | 27 | ||
55 | (use-package no-littering | 28 | (use-package no-littering |
56 | :ensure t :demand t | 29 | :ensure t :demand t |
@@ -114,6 +87,15 @@ be created." | |||
114 | (file-name-shadow-mode) | 87 | (file-name-shadow-mode) |
115 | (minibuffer-electric-default-mode) | 88 | (minibuffer-electric-default-mode) |
116 | 89 | ||
90 | (define-minor-mode truncate-lines-local-mode | ||
91 | "Truncate lines locally in a buffer." | ||
92 | :lighter " ..." | ||
93 | :group 'display | ||
94 | (setq-local truncate-lines truncate-lines-local-mode)) | ||
95 | |||
96 | (add-hook 'minibuffer-setup-hook #'truncate-lines-local-mode) | ||
97 | |||
98 | |||
117 | (require 'savehist) | 99 | (require 'savehist) |
118 | (setq-default history-length 1024 | 100 | (setq-default history-length 1024 |
119 | history-delete-duplicates t | 101 | history-delete-duplicates t |
@@ -122,13 +104,16 @@ be created." | |||
122 | savehist-autosave-interval 30) | 104 | savehist-autosave-interval 30) |
123 | (savehist-mode) | 105 | (savehist-mode) |
124 | 106 | ||
125 | ;; Undo | ||
126 | (setq-default undo-limit (* 10 1024 1024)) | ||
127 | |||
128 | ;; Killing and yanking | 107 | ;; Killing and yanking |
129 | (setq-default kill-do-not-save-duplicates t | 108 | (setq-default kill-do-not-save-duplicates t |
130 | kill-read-only-ok t | 109 | kill-read-only-ok t |
131 | save-interprogram-paste-before-kill t | 110 | ;; XXX: This setting causes an error message the first time it's |
111 | ;; called: "Selection owner couldn't convert: TIMESTAMP". I have | ||
112 | ;; absolutely no idea why I get this error, but it's generated in | ||
113 | ;; `x_get_foreign_selection'. I also can't inhibit the message or | ||
114 | ;; do anything else with it, so for now, I'll just live with the | ||
115 | ;; message. | ||
116 | save-interprogram-paste-before-kill t | ||
132 | yank-pop-change-selection t) | 117 | yank-pop-change-selection t) |
133 | (delete-selection-mode) | 118 | (delete-selection-mode) |
134 | 119 | ||
@@ -147,14 +132,20 @@ be created." | |||
147 | initial-buffer-choice t | 132 | initial-buffer-choice t |
148 | initial-scratch-message nil) | 133 | initial-scratch-message nil) |
149 | 134 | ||
150 | ;; (menu-bar-mode -1) | 135 | (define-advice startup-echo-area-message (:override ()) |
136 | (if (get-buffer "*Warnings*") | ||
137 | ";_;" | ||
138 | "^_^")) | ||
139 | |||
140 | (menu-bar-mode -1) | ||
151 | (tool-bar-mode -1) | 141 | (tool-bar-mode -1) |
152 | (tooltip-mode -1) | 142 | (tooltip-mode -1) |
153 | 143 | ||
154 | ;; Text editing | 144 | ;; Text editing |
155 | (setq-default fill-column 80 | 145 | (setq-default fill-column 80 |
156 | sentence-end-double-space t | 146 | sentence-end-double-space t |
157 | tab-width 8) | 147 | tab-width 8 |
148 | tab-always-indent 'complete) | ||
158 | (global-so-long-mode) | 149 | (global-so-long-mode) |
159 | 150 | ||
160 | (setq-default show-paren-delay 0.01 | 151 | (setq-default show-paren-delay 0.01 |
@@ -202,7 +193,8 @@ be created." | |||
202 | auto-save-interval 1 | 193 | auto-save-interval 1 |
203 | auto-save-no-message t | 194 | auto-save-no-message t |
204 | auto-save-timeout 1 | 195 | auto-save-timeout 1 |
205 | auto-save-visited-interval 1) | 196 | auto-save-visited-interval 1 |
197 | remote-file-name-inhibit-auto-save-visited t) | ||
206 | (add-to-list 'auto-save-file-name-transforms | 198 | (add-to-list 'auto-save-file-name-transforms |
207 | `(".*" ,(etc/ "auto-save/" t) t)) | 199 | `(".*" ,(etc/ "auto-save/" t) t)) |
208 | (auto-save-visited-mode) | 200 | (auto-save-visited-mode) |
@@ -215,9 +207,9 @@ be created." | |||
215 | 207 | ||
216 | (require 'recentf) | 208 | (require 'recentf) |
217 | (setq-default ;; recentf-save-file (etc/ "recentf" t) | 209 | (setq-default ;; recentf-save-file (etc/ "recentf" t) |
218 | recentf-max-menu-items 500 | 210 | recentf-max-menu-items 500 |
219 | recentf-max-saved-items nil ; Save the whole list | 211 | recentf-max-saved-items nil ; Save the whole list |
220 | recentf-auto-cleanup 'mode) | 212 | recentf-auto-cleanup 'mode) |
221 | (add-to-list 'recentf-exclude etc/) | 213 | (add-to-list 'recentf-exclude etc/) |
222 | (add-to-list 'recentf-exclude "-autoloads.el\\'") | 214 | (add-to-list 'recentf-exclude "-autoloads.el\\'") |
223 | (add-hook 'buffer-list-update-hook #'recentf-track-opened-file) | 215 | (add-hook 'buffer-list-update-hook #'recentf-track-opened-file) |
@@ -225,8 +217,8 @@ be created." | |||
225 | 217 | ||
226 | (require 'saveplace) | 218 | (require 'saveplace) |
227 | (setq-default ;; save-place-file (etc/ "places.el") | 219 | (setq-default ;; save-place-file (etc/ "places.el") |
228 | save-place-forget-unreadable-files (eq system-type | 220 | save-place-forget-unreadable-files (eq system-type |
229 | 'gnu/linux)) | 221 | 'gnu/linux)) |
230 | (save-place-mode) | 222 | (save-place-mode) |
231 | 223 | ||
232 | (require 'uniquify) | 224 | (require 'uniquify) |
@@ -247,10 +239,12 @@ be created." | |||
247 | (startup-redirect-eln-cache native-compile-target-directory)) | 239 | (startup-redirect-eln-cache native-compile-target-directory)) |
248 | 240 | ||
249 | ;; Custom file | 241 | ;; Custom file |
250 | (setq-default custom-file (sync/ "emacs/custom.el")) | 242 | (setq-default custom-file (private/ "custom.el")) |
251 | (define-advice package--save-selected-packages (:around (orig &rest args) no-custom) | 243 | (define-advice package--save-selected-packages |
244 | (:around (orig &rest args) no-custom) | ||
252 | "Don't save `package-selected-packages' to `custom-file'." | 245 | "Don't save `package-selected-packages' to `custom-file'." |
253 | (let ((custom-file null-device)) | 246 | (let ((custom-file (expand-file-name "custom.el" |
247 | temporary-file-directory))) | ||
254 | (apply orig args))) | 248 | (apply orig args))) |
255 | 249 | ||
256 | ;; Goto Address | 250 | ;; Goto Address |
@@ -293,22 +287,56 @@ N spaces." | |||
293 | (global-set-key (kbd "C-x 0") #'delete-window|bury-buffer) | 287 | (global-set-key (kbd "C-x 0") #'delete-window|bury-buffer) |
294 | (global-set-key (kbd "M-SPC") #'+cycle-spacing) | 288 | (global-set-key (kbd "M-SPC") #'+cycle-spacing) |
295 | (global-set-key (kbd "C-x C-k") #'kill-this-buffer) | 289 | (global-set-key (kbd "C-x C-k") #'kill-this-buffer) |
290 | (global-set-key (kbd "C-/") #'undo-only) | ||
291 | (global-set-key (kbd "C-?") #'undo-redo) | ||
292 | |||
293 | (define-key emacs-lisp-mode-map (kbd "C-c C-c") | ||
294 | #'eval-defun) | ||
295 | (define-key emacs-lisp-mode-map (kbd "C-c C-k") | ||
296 | #'elisp-eval-region-or-buffer) | ||
297 | (define-key lisp-interaction-mode-map (kbd "C-c C-c") | ||
298 | #'eval-defun) | ||
299 | (define-key lisp-interaction-mode-map (kbd "C-c C-k") | ||
300 | #'elisp-eval-region-or-buffer) | ||
301 | (define-advice eval-region (:around (orig start end &rest args) pulse) | ||
302 | (apply orig start end args) | ||
303 | (pulse-momentary-highlight-region start end)) | ||
304 | |||
305 | (global-set-key (kbd "C-x C-b") #'ibuffer) | ||
296 | 306 | ||
297 | ;;; Hooks | 307 | ;;; Hooks |
298 | 308 | ||
299 | (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) | 309 | (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) |
310 | |||
300 | (add-hook 'find-file-not-found-functions | 311 | (add-hook 'find-file-not-found-functions |
301 | (defun create-missing-directories () | 312 | (defun create-missing-directories () |
302 | "Automatically create missing directories." | 313 | "Automatically create missing directories." |
303 | (let ((target-dir (file-name-directory buffer-file-name))) | 314 | (let ((target-dir (file-name-directory buffer-file-name))) |
304 | (unless (file-exists-p target-dir) | 315 | (unless (file-exists-p target-dir) |
305 | (make-directory target-dir :parents))))) | 316 | (make-directory target-dir :parents))))) |
317 | |||
306 | (add-hook 'find-file-hook | 318 | (add-hook 'find-file-hook |
307 | (defun vc-remote-off () | 319 | (defun vc-remote-off () |
308 | "Turn VC off when remote." | 320 | "Turn VC off when remote." |
309 | (when (file-remote-p (buffer-file-name)) | 321 | (when (file-remote-p (buffer-file-name)) |
310 | (setq-local vc-handled-backends nil)))) | 322 | (setq-local vc-handled-backends nil)))) |
311 | 323 | ||
324 | (add-hook 'after-init-hook | ||
325 | (defun after-init@emoji-font-setup () | ||
326 | "Set up emoji fonts after init." | ||
327 | (run-with-idle-timer | ||
328 | 1 nil (defun emoji-font-setup () | ||
329 | "Set up emoji fonts." | ||
330 | (let ((ffl (font-family-list))) | ||
331 | (dolist (font '("Noto Emoji" "Noto Color Emoji" | ||
332 | "Segoe UI Emoji" "Apple Color Emoji" | ||
333 | "FreeSans" "FreeMono" "FreeSerif" | ||
334 | "Unifont" "Symbola")) | ||
335 | (when (member font (font-family-list)) | ||
336 | (set-fontset-font t 'symbol | ||
337 | (font-spec :family font) | ||
338 | nil :add)))))))) | ||
339 | |||
312 | ;;; Advice | 340 | ;;; Advice |
313 | 341 | ||
314 | (define-advice switch-to-buffer (:after (&rest _) normal-mode) | 342 | (define-advice switch-to-buffer (:after (&rest _) normal-mode) |
@@ -373,4 +401,201 @@ See also `with-region-or-to-eol'." | |||
373 | (use-package _acdw | 401 | (use-package _acdw |
374 | :load-path private/) | 402 | :load-path private/) |
375 | 403 | ||
404 | (use-package custom-allowed | ||
405 | :load-path "/home/case/src/emacs/custom-allowed/" | ||
406 | :config | ||
407 | (add-to-list 'custom-allowed-variables 'safe-local-variable-values) | ||
408 | (add-to-list 'custom-allowed-variables 'ispell-buffer-session-localwords) | ||
409 | (add-to-list 'custom-allowed-variables 'warning-suppress-types) | ||
410 | (add-to-list 'custom-allowed-variables 'calendar-latitude) | ||
411 | (add-to-list 'custom-allowed-variables 'calendar-longitude) | ||
412 | (add-to-list 'custom-allowed-variables 'user-full-name) | ||
413 | (add-to-list 'custom-allowed-variables 'user-mail-address) | ||
414 | :hook | ||
415 | (after-init-hook . custom-allowed-load-custom-file)) | ||
416 | |||
417 | (use-package sophomore | ||
418 | :load-path "/home/case/src/emacs/sophomore/" | ||
419 | :config | ||
420 | (sophomore-enable-all) | ||
421 | (sophomore-disable 'view-hello-file | ||
422 | 'describe-gnu-project | ||
423 | 'suspend-frame) | ||
424 | (sophomore-mode)) | ||
425 | |||
426 | (use-package compat | ||
427 | ;; This shouldn't be necessary, but sadly I believe that it is. | ||
428 | :ensure t) | ||
429 | |||
430 | (use-package vertico | ||
431 | :ensure t :demand t | ||
432 | :config | ||
433 | (setq vertico-cycle t) | ||
434 | (vertico-mode)) | ||
435 | |||
436 | (use-package vertico-directory | ||
437 | :after vertico | ||
438 | :bind (:map vertico-map | ||
439 | ("RET" . vertico-directory-enter) | ||
440 | ("DEL" . vertico-directory-delete-char) | ||
441 | ("M-DEL" . vertico-directory-delete-word)) | ||
442 | :hook (rfn-shadow-update-overlay-hook . vertico-directory-tidy)) | ||
443 | |||
444 | (use-package vertico-mouse | ||
445 | :after vertico | ||
446 | :config (vertico-mouse-mode)) | ||
447 | |||
448 | ;; Example configuration for Consult | ||
449 | (use-package consult | ||
450 | :ensure t | ||
451 | ;; Replace bindings. Lazily loaded due by `use-package'. | ||
452 | :bind (;; C-c bindings (mode-specific-map) | ||
453 | ("C-c h" . consult-history) | ||
454 | ("C-c m" . consult-mode-command) | ||
455 | ("C-c k" . consult-kmacro) | ||
456 | ;; C-x bindings (ctl-x-map) | ||
457 | ("C-x M-:" . consult-complex-command) | ||
458 | ("C-x b" . consult-buffer) | ||
459 | ("C-x 4 b" . consult-buffer-other-window) | ||
460 | ("C-x 5 b" . consult-buffer-other-frame) | ||
461 | ("C-x r b" . consult-bookmark) | ||
462 | ("C-x p b" . consult-project-buffer) | ||
463 | ;; Custom M-# bindings for fast register access | ||
464 | ("M-#" . consult-register-load) | ||
465 | ("M-'" . consult-register-store) | ||
466 | ("C-M-#" . consult-register) | ||
467 | ;; Other custom bindings | ||
468 | ("M-y" . consult-yank-pop) | ||
469 | ;; M-g bindings (goto-map) | ||
470 | ("M-g e" . consult-compile-error) | ||
471 | ("M-g f" . consult-flymake) | ||
472 | ("M-g g" . consult-goto-line) | ||
473 | ("M-g M-g" . consult-goto-line) | ||
474 | ("M-g o" . consult-outline) | ||
475 | ("M-g m" . consult-mark) | ||
476 | ("M-g k" . consult-global-mark) | ||
477 | ("M-g i" . consult-imenu) | ||
478 | ("M-g I" . consult-imenu-multi) | ||
479 | ;; M-s bindings (search-map) | ||
480 | ("M-s d" . consult-find) | ||
481 | ("M-s D" . consult-locate) | ||
482 | ("M-s g" . consult-grep) | ||
483 | ("M-s G" . consult-git-grep) | ||
484 | ("M-s r" . consult-ripgrep) | ||
485 | ("M-s l" . consult-line) | ||
486 | ("M-s L" . consult-line-multi) | ||
487 | ("M-s k" . consult-keep-lines) | ||
488 | ("M-s u" . consult-focus-lines) | ||
489 | ;; Isearch integration | ||
490 | ("M-s e" . consult-isearch-history) | ||
491 | :map isearch-mode-map | ||
492 | ("M-e" . consult-isearch-history) | ||
493 | ("M-s e" . consult-isearch-history) | ||
494 | ("M-s l" . consult-line) | ||
495 | ("M-s L" . consult-line-multi) | ||
496 | ;; Minibuffer history | ||
497 | :map minibuffer-local-map | ||
498 | ("M-s" . consult-history) | ||
499 | ("M-r" . consult-history)) | ||
500 | |||
501 | ;; Enable automatic preview at point in the *Completions* buffer. This is | ||
502 | ;; relevant when you use the default completion UI. | ||
503 | :hook (completion-list-mode . consult-preview-at-point-mode) | ||
504 | |||
505 | ;; The :init configuration is always executed (Not lazy) | ||
506 | :init | ||
507 | |||
508 | ;; Optionally configure the register formatting. This improves the register | ||
509 | ;; preview for `consult-register', `consult-register-load', | ||
510 | ;; `consult-register-store' and the Emacs built-ins. | ||
511 | (setq register-preview-delay 0.5 | ||
512 | register-preview-function #'consult-register-format) | ||
513 | |||
514 | ;; Optionally tweak the register preview window. | ||
515 | ;; This adds thin lines, sorting and hides the mode line of the window. | ||
516 | (advice-add #'register-preview :override #'consult-register-window) | ||
517 | |||
518 | (define-advice completing-read-multiple (:filter-args (args) indicator) | ||
519 | (cons (format "[CRM%s] %s" | ||
520 | (replace-regexp-in-string | ||
521 | "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" | ||
522 | crm-separator) | ||
523 | (car args)) | ||
524 | (cdr args))) | ||
525 | |||
526 | ;; Use Consult to select xref locations with preview | ||
527 | (setq xref-show-xrefs-function #'consult-xref | ||
528 | xref-show-definitions-function #'consult-xref) | ||
529 | |||
530 | (setq completion-in-region-function #'consult-completion-in-region) | ||
531 | |||
532 | ;; Configure other variables and modes in the :config section, | ||
533 | ;; after lazily loading the package. | ||
534 | :config | ||
535 | |||
536 | ;; Optionally configure preview. The default value | ||
537 | ;; is 'any, such that any key triggers the preview. | ||
538 | ;; (setq consult-preview-key 'any) | ||
539 | ;; (setq consult-preview-key (kbd "M-.")) | ||
540 | ;; (setq consult-preview-key (list (kbd "<S-down>") (kbd "<S-up>"))) | ||
541 | ;; For some commands and buffer sources it is useful to configure the | ||
542 | ;; :preview-key on a per-command basis using the `consult-customize' macro. | ||
543 | (consult-customize | ||
544 | consult-theme :preview-key '(:debounce 0.2 any) | ||
545 | consult-ripgrep consult-git-grep consult-grep | ||
546 | consult-bookmark consult-recent-file consult-xref | ||
547 | consult--source-bookmark consult--source-file-register | ||
548 | consult--source-recent-file consult--source-project-recent-file | ||
549 | ;; :preview-key (kbd "M-.") | ||
550 | :preview-key '(:debounce 0.4 any)) | ||
551 | |||
552 | ;; Optionally configure the narrowing key. | ||
553 | ;; Both < and C-+ work reasonably well. | ||
554 | (setq consult-narrow-key "<") ;; (kbd "C-+") | ||
555 | |||
556 | ;; Optionally make narrowing help available in the minibuffer. | ||
557 | ;; You may want to use `embark-prefix-help-command' or which-key instead. | ||
558 | (define-key consult-narrow-map (vconcat consult-narrow-key "?") | ||
559 | #'consult-narrow-help)) | ||
560 | |||
561 | (use-package orderless | ||
562 | :ensure t :demand t | ||
563 | :init | ||
564 | (setopt completion-styles '(substring orderless basic) | ||
565 | completion-category-defaults nil | ||
566 | completion-category-overrides | ||
567 | '((file (styles basic partial-completion))))) | ||
568 | |||
569 | (use-package marginalia | ||
570 | :ensure t :demand t | ||
571 | :config | ||
572 | (marginalia-mode)) | ||
573 | |||
574 | (use-package embark | ||
575 | :ensure t | ||
576 | :bind | ||
577 | (("C-." . embark-act) | ||
578 | ("M-." . embark-dwim) | ||
579 | ("C-h B" . embark-bindings)) | ||
580 | :init | ||
581 | (setopt prefix-help-command #'embark-prefix-help-command) | ||
582 | :config | ||
583 | (add-to-list 'display-buffer-alist | ||
584 | '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" | ||
585 | nil | ||
586 | (window-parameters (mode-line-format . none))))) | ||
587 | |||
588 | (use-package embark-consult | ||
589 | :ensure t | ||
590 | :hook | ||
591 | (embark-collect-mode . consult-preview-at-point-mode)) | ||
592 | |||
593 | (use-package undo-fu-session | ||
594 | :ensure t | ||
595 | :config | ||
596 | (setq undo-fu-session-incompatible-files | ||
597 | '("/COMMIT_EDITMSG\\'" | ||
598 | "/git-rebase-todo\\'")) | ||
599 | (global-undo-fu-session-mode)) | ||
600 | |||
376 | ;;; basics.el ends here | 601 | ;;; basics.el ends here |
diff --git a/early-init.el b/early-init.el index 3dd74e0..d59fe64 100644 --- a/early-init.el +++ b/early-init.el | |||
@@ -5,10 +5,11 @@ | |||
5 | ;; Debugging shit | 5 | ;; Debugging shit |
6 | (setq debug-on-error t | 6 | (setq debug-on-error t |
7 | use-package-verbose t) | 7 | use-package-verbose t) |
8 | ;; (setq debug-on-message "Selection owner couldn’t convert: TIMESTAMP") | ||
8 | 9 | ||
9 | ;; Frames | 10 | ;; Frames |
10 | (setq default-frame-alist '((tool-bar-lines . 0) | 11 | (setq default-frame-alist '((tool-bar-lines . 0) |
11 | ;; (menu-bar-lines . 0) | 12 | (menu-bar-lines . 0) |
12 | (vertical-scroll-bars . nil) | 13 | (vertical-scroll-bars . nil) |
13 | (horizontal-scroll-bars . nil)) | 14 | (horizontal-scroll-bars . nil)) |
14 | frame-inhibit-implied-resize t | 15 | frame-inhibit-implied-resize t |
@@ -21,11 +22,13 @@ | |||
21 | ;; Packages | 22 | ;; Packages |
22 | (require 'package) | 23 | (require 'package) |
23 | (add-to-list 'package-archives | 24 | (add-to-list 'package-archives |
24 | '("melpa" . "https://melpa.org/packages/") | 25 | '("melpa" . "https://melpa.org/packages/") :append) |
25 | :append) | 26 | (add-to-list 'package-archives |
26 | (setq package-priorities '(("melpa" . 2) | 27 | '("melpa-stable" . "https://stable.melpa.org/packages/") :append) |
27 | ("nongnu" . 1) | 28 | (setq package-priorities '(("melpa" . 3) |
28 | ("gnu" . 0))) | 29 | ("nongnu" . 2) |
30 | ("gnu" . 1) | ||
31 | ("melpa-stable" . 0))) | ||
29 | (package-initialize) | 32 | (package-initialize) |
30 | (unless package-archive-contents | 33 | (unless package-archive-contents |
31 | (package-refresh-contents)) | 34 | (package-refresh-contents)) |
diff --git a/init.el b/init.el index 369641c..c3c2f49 100644 --- a/init.el +++ b/init.el | |||
@@ -1,7 +1,456 @@ | |||
1 | ;;; init.el --- An Emacs of one's own -*- lexical-binding: t -*- | 1 | ;;; init.el --- An Emacs of one's own -*- lexical-binding: t -*- |
2 | |||
3 | ;; Bankruptcy: 9.4 | 2 | ;; Bankruptcy: 9.4 |
4 | 3 | ||
5 | ;;; Code: | 4 | ;;; Code: |
6 | 5 | ||
7 | (load (locate-user-emacs-file "basics")) ; super basic stuff | 6 | (load (locate-user-emacs-file "basics")) ; super basic stuff |
7 | |||
8 | |||
9 | ;;; Built-ins | ||
10 | |||
11 | (use-package emacs | ||
12 | :custom-face | ||
13 | (default ((t :family "Comic Code" | ||
14 | :height 100))) | ||
15 | (variable-pitch ((t :family "Comic Code"))) | ||
16 | :config | ||
17 | (setopt tab-bar-show 1)) | ||
18 | |||
19 | (use-package text-mode | ||
20 | :config | ||
21 | (add-hook 'text-mode-hook #'abbrev-mode)) | ||
22 | |||
23 | (use-package prog-mode | ||
24 | :config | ||
25 | (add-hook 'prog-mode-hook #'auto-fill-mode) | ||
26 | (add-hook 'prog-mode-hook | ||
27 | (defun prog@indent-tabs-maybe () | ||
28 | (indent-tabs-mode | ||
29 | (if (derived-mode-p 'emacs-lisp-mode | ||
30 | 'python-mode | ||
31 | 'haskell-mode) | ||
32 | -1 1))))) | ||
33 | |||
34 | (use-package eshell | ||
35 | :preface | ||
36 | ;; TODO: Break this out into its own package (eshell-pop?). This may not work | ||
37 | ;; the way I want it to sometimes .. but then, I don't know how I want it to | ||
38 | ;; work sometimes either. More testing, clearly, is needed. | ||
39 | (defvar eshell-buffer-format "*eshell:%s*" | ||
40 | "Format for eshell buffer names.") | ||
41 | (defun eshell-rename-pwd () | ||
42 | (rename-buffer (format eshell-buffer-format default-directory) t)) | ||
43 | (defun eshell-last-dir () | ||
44 | (goto-char (point-max)) | ||
45 | (insert "cd -") | ||
46 | (eshell-send-input)) | ||
47 | (defun eshellp (buffer-or-name) | ||
48 | (with-current-buffer buffer-or-name | ||
49 | (derived-mode-p 'eshell-mode))) | ||
50 | (defun +eshell (&optional new) | ||
51 | (interactive "P") | ||
52 | (let ((dir default-directory) | ||
53 | (bname (format eshell-buffer-format default-directory)) | ||
54 | (display-comint-buffer-action 'pop-to-buffer)) | ||
55 | (if-let ((buf (and (not new) | ||
56 | (or (get-buffer bname) | ||
57 | (seq-find #'eshellp | ||
58 | (reverse (buffer-list))))))) | ||
59 | (pop-to-buffer buf) | ||
60 | (eshell new)) | ||
61 | (eshell-rename-pwd) | ||
62 | (unless (equal default-directory dir) | ||
63 | (goto-char (point-max)) | ||
64 | (insert (format "cd %s" dir)) | ||
65 | (eshell-send-input)))) | ||
66 | (defun +eshell-quit (&optional choose) | ||
67 | (interactive "P") | ||
68 | (if choose | ||
69 | (let* ((bufs (mapcar #'buffer-name | ||
70 | (seq-filter #'eshellp | ||
71 | (buffer-list)))) | ||
72 | (buf (get-buffer | ||
73 | (completing-read "Eshell: " | ||
74 | bufs nil t nil nil (car bufs))))) | ||
75 | (quit-window) | ||
76 | (pop-to-buffer buf)) | ||
77 | (quit-window))) | ||
78 | :init | ||
79 | (add-hook 'eshell-post-command-hook #'eshell-rename-pwd) | ||
80 | :commands eshell | ||
81 | :bind (("C-z" . +eshell) | ||
82 | :map eshell-mode-map | ||
83 | ("C-z" . +eshell-quit) | ||
84 | ("C-o" . eshell-last-dir)) | ||
85 | :config | ||
86 | (add-hook 'eshell-mode-hook | ||
87 | (defun eshell-setup () | ||
88 | (setq-local imenu-generic-expression | ||
89 | '(("Prompt" " $ \\(.*\\)" 1)))))) | ||
90 | |||
91 | (use-package auth-source | ||
92 | :config | ||
93 | (setopt auth-sources '(default "secrets:passwords")) | ||
94 | (add-hook 'auth-info-hook #'truncate-lines-local-mode)) | ||
95 | |||
96 | (use-package fringe | ||
97 | :config | ||
98 | (fringe-mode '(nil . 0))) | ||
99 | |||
100 | |||
101 | ;;; Locally-developed packages | ||
102 | |||
103 | (use-package dawn | ||
104 | :load-path "~/src/emacs/dawn/" | ||
105 | :after custom-allowed | ||
106 | :config | ||
107 | (add-hook 'custom-allowed-after-load-hook | ||
108 | (defun dawn-modus () | ||
109 | (dawn-schedule-themes 'modus-operandi | ||
110 | 'modus-vivendi)))) | ||
111 | |||
112 | (use-package electric-cursor | ||
113 | :load-path "~/src/emacs/electric-cursor/" | ||
114 | :config | ||
115 | (setopt electric-cursor-alist '((overwrite-mode . box) | ||
116 | (t . bar))) | ||
117 | (electric-cursor-mode)) | ||
118 | |||
119 | (use-package mode-line-bell | ||
120 | :load-path "~/src/emacs/mode-line-bell/" | ||
121 | :config | ||
122 | (setopt mode-line-bell-flash-time 0.25) | ||
123 | (mode-line-bell-mode)) | ||
124 | |||
125 | (use-package titlecase | ||
126 | :load-path "~/src/emacs/titlecase.el/" | ||
127 | :after scule | ||
128 | :bind (:map scule-map | ||
129 | ("M-t" . titlecase-dwim))) | ||
130 | |||
131 | (use-package scule | ||
132 | :load-path "~/src/emacs/scule/" | ||
133 | :config | ||
134 | (defvar-keymap scule-map | ||
135 | :doc "Keymap to twiddle scules." | ||
136 | :repeat t ; TODO: doesn't work | ||
137 | "M-u" #'scule-upcase | ||
138 | "M-l" #'scule-downcase | ||
139 | "M-c" #'scule-capitalize) | ||
140 | (keymap-global-set "M-c" scule-map) | ||
141 | ;; Use M-u for prefix keys | ||
142 | (keymap-global-set "M-u" #'universal-argument) | ||
143 | (keymap-set universal-argument-map "M-u" #'universal-argument-more)) | ||
144 | |||
145 | (use-package filldent | ||
146 | :load-path "~/src/emacs/filldent/" | ||
147 | :bind ("M-q" . filldent-dwim)) | ||
148 | |||
149 | (use-package frowny | ||
150 | :load-path "~/src/emacs/frowny/" | ||
151 | :config | ||
152 | (global-frowny-mode)) | ||
153 | |||
154 | (use-package jabber | ||
155 | :load-path "~/src/emacs/emacs-jabber/" | ||
156 | :preface | ||
157 | (defun jabber-ui-setup () | ||
158 | "Setup the `jabber' user interface." | ||
159 | (visual-fill-column-mode) | ||
160 | (electric-pair-local-mode -1) | ||
161 | (auto-fill-mode -1)) | ||
162 | :custom-face | ||
163 | (jabber-activity-face ((t :inherit jabber-chat-prompt-foreign | ||
164 | :foreground unspecified | ||
165 | :weight normal))) | ||
166 | (jabber-activity-personal-face ((t :inherit font-lock-warning-face | ||
167 | :foreground unspecified | ||
168 | :weight bold))) | ||
169 | (jabber-chat-prompt-local ((t :inherit font-lock-warning-face | ||
170 | :foreground unspecified))) | ||
171 | (jabber-chat-prompt-foreign ((t :inherit font-lock-constant-face | ||
172 | :foreground unspecified))) | ||
173 | (jabber-chat-prompt-system ((t :inherit font-lock-doc-face | ||
174 | :foreground unspecified))) | ||
175 | (jabber-rare-time-face ((t :inherit font-lock-comment-face | ||
176 | :foreground unspecified | ||
177 | :underline nil))) | ||
178 | :bind-keymap ("C-c j" . jabber-global-keymap) | ||
179 | :bind (("C-c C-SPC" . jabber-activity-switch-to)) | ||
180 | :config | ||
181 | (setopt jabber-account-list '(("acdw@hmm.st")) | ||
182 | jabber-auto-reconnect t | ||
183 | jabber-last-read-marker (make-string 40 ?-) | ||
184 | jabber-muc-presence-patterns | ||
185 | '(("\\( enters the room ([^)]+)\\| has left the chatroom\\)$") | ||
186 | ("." . jabber-muc-presence-dim)) | ||
187 | jabber-activity-make-strings #'jabber-activity-make-strings-shorten) | ||
188 | (add-hook 'jabber-chat-mode-hook #'jabber-ui-setup) | ||
189 | (keymap-global-set "C-x C-j" #'dired-jump) ; Extremely annoying fix | ||
190 | (require 'jabber-httpupload nil t) | ||
191 | (add-hook 'jabber-post-connect-hooks #'jabber-enable-carbons) | ||
192 | (remove-hook 'jabber-alert-muc-hooks 'jabber-muc-echo) | ||
193 | (remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo) | ||
194 | (add-hook 'jabber-alert-muc-hooks | ||
195 | (defun jabber@highlight-acdw (&optional _ _ buf _ _) | ||
196 | (when buf | ||
197 | (with-current-buffer buf | ||
198 | (let ((regexp (rx word-boundary | ||
199 | "acdw" ; maybe get from the config? | ||
200 | word-boundary))) | ||
201 | (hi-lock-unface-buffer regexp) | ||
202 | (highlight-regexp regexp 'font-lock-warning-face)))))) | ||
203 | (add-hook 'window-configuration-change-hook #'jabber-chat-update-focus)) | ||
204 | |||
205 | (use-package keepassxc-shim | ||
206 | :load-path "~/src/emacs/keepassxc-shim/" | ||
207 | :config | ||
208 | (keepassxc-shim-activate)) | ||
209 | |||
210 | |||
211 | ;;; External packages | ||
212 | |||
213 | (use-package minions | ||
214 | :ensure t | ||
215 | :config (minions-mode)) | ||
216 | |||
217 | (use-package visual-fill-column | ||
218 | :ensure t | ||
219 | :init | ||
220 | (setopt visual-fill-column-center-text t | ||
221 | visual-fill-column-extra-text-width '(1 . 1)) | ||
222 | :config | ||
223 | (add-hook 'visual-fill-column-mode-hook #'visual-line-mode) | ||
224 | (add-hook 'eww-mode-hook #'visual-fill-column-mode) | ||
225 | (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) | ||
226 | |||
227 | (use-package mlscroll | ||
228 | :ensure t :defer 1 | ||
229 | :after modus-themes | ||
230 | :preface | ||
231 | (define-advice load-theme (:after (&rest _) mlscroll) | ||
232 | (mlscroll-mode -1) | ||
233 | (when (seq-intersection '(modus-vivendi modus-operandi) | ||
234 | custom-enabled-themes) | ||
235 | (modus-themes-with-colors | ||
236 | (setq mlscroll-in-color fg-dim | ||
237 | mlscroll-out-color bg-inactive))) | ||
238 | (run-with-idle-timer 1 nil #'mlscroll-mode)) | ||
239 | :config | ||
240 | (load-theme@mlscroll)) | ||
241 | |||
242 | (use-package cape | ||
243 | :ensure t | ||
244 | :config | ||
245 | (add-hook 'completion-at-point-functions #'cape-file 90) | ||
246 | (add-hook 'completion-at-point-functions #'cape-dabbrev 91) | ||
247 | (advice-add 'emacs-completion-at-point | ||
248 | :around #'cape-wrap-nonexclusive)) | ||
249 | |||
250 | (use-package ws-butler | ||
251 | :ensure t | ||
252 | :config | ||
253 | (setopt ws-butler-trim-predicate | ||
254 | (lambda (begin end) | ||
255 | (not (eq 'font-lock-string-face | ||
256 | (get-text-property end 'face))))) | ||
257 | (ws-butler-global-mode)) | ||
258 | |||
259 | (use-package wgrep | ||
260 | :ensure t | ||
261 | :config | ||
262 | (setopt wgrep-enable-key (kbd "C-x C-q")) | ||
263 | :bind (:map grep-mode-map | ||
264 | ("C-x C-q" . wgrep-change-to-wgrep-mode))) | ||
265 | |||
266 | (use-package avy | ||
267 | :ensure t | ||
268 | :init | ||
269 | (setopt avy-background t | ||
270 | avy-keys (string-to-list "asdfghjklqwertyuiopzxcvbnm")) | ||
271 | :bind (("M-j" . avy-goto-char-timer) | ||
272 | :map isearch-mode-map | ||
273 | ("M-j" . avy-isearch))) | ||
274 | |||
275 | (use-package zzz-to-char | ||
276 | :ensure t | ||
277 | :bind (("M-z" . zzz-to-char))) | ||
278 | |||
279 | (use-package anzu | ||
280 | :ensure t | ||
281 | :bind (("M-%" . anzu-query-replace-regexp) | ||
282 | ("C-M-%" . anzu-query-replace))) | ||
283 | |||
284 | (use-package isearch-mb | ||
285 | :ensure t | ||
286 | :config | ||
287 | (setopt isearch-lazy-count t | ||
288 | isearch-regexp-lax-whitespace t | ||
289 | search-whitespace-regexp "\\W+" | ||
290 | search-default-mode t ; Search regexp by default | ||
291 | isearch-wrap-pause 'no) | ||
292 | (define-advice isearch-cancel (:before (&rest _) add-search-to-history) | ||
293 | "Add search string to history when canceling." | ||
294 | (unless (equal "" isearch-string) | ||
295 | (isearch-update-ring isearch-string isearch-regexp))) | ||
296 | (define-advice perform-replace (:around (orig &rest r) no-anykey-exit) | ||
297 | "Don't exit replace for any key that's not in `query-replace-map'." | ||
298 | (save-window-excursion | ||
299 | (cl-letf* ((lookup-key-orig (symbol-function 'lookup-key)) | ||
300 | ((symbol-function 'lookup-key) | ||
301 | (lambda (map key &optional accept-default) | ||
302 | (or (apply lookup-key-orig map key accept-default) | ||
303 | (when (eq map query-replace-map) 'help))))) | ||
304 | (apply orig r)))) | ||
305 | ;; Consult | ||
306 | (autoload 'consult-line "consult" nil t) | ||
307 | (autoload 'consult-isearch-history "consult" nil t) | ||
308 | (add-to-list 'isearch-mb--after-exit #'consult-line) | ||
309 | (add-to-list 'isearch-mb--with-buffer #'consult-isearch-history) | ||
310 | (keymap-set isearch-mb-minibuffer-map "M-s l" #'consult-line) | ||
311 | (keymap-set isearch-mb-minibuffer-map "M-r" #'consult-isearch-history) | ||
312 | ;; Anzu | ||
313 | (autoload 'anzu-isearch-query-replace "anzu" nil t) | ||
314 | (autoload 'anzu-isearch-query-replace-regexp "anzu" nil t) | ||
315 | (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace) | ||
316 | (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace-regexp) | ||
317 | (keymap-set isearch-mb-minibuffer-map | ||
318 | "M-%" #'anzu-isearch-query-replace-regexp) | ||
319 | (keymap-set isearch-mb-minibuffer-map | ||
320 | "C-M-%" #'anzu-isearch-query-replace) | ||
321 | (isearch-mb-mode)) | ||
322 | |||
323 | (use-package paredit | ||
324 | :ensure t | ||
325 | :hook ( emacs-lisp-mode-hook ielm-mode-hook | ||
326 | eval-expression-minibuffer-setup-hook | ||
327 | lisp-interaction-mode-hook | ||
328 | lisp-mode-hook scheme-mode-hook | ||
329 | fennel-mode-hook fennel-repl-mode-hook | ||
330 | geiser-mode-hook geiser-repl-mode-hook) | ||
331 | :config | ||
332 | (keymap-set paredit-mode-map "C-j" | ||
333 | (defun +paredit-newline | ||
334 | (interactive) | ||
335 | (call-interactively | ||
336 | (if (derived-mode-p 'lisp-interaction-mode) | ||
337 | #'eval-print-last-sexp #'paredit-newline)))) | ||
338 | (keymap-set paredit-mode-map "RET" nil) | ||
339 | (keymap-set paredit-mode-map "M-s" nil) | ||
340 | (add-to-list 'paredit-space-for-delimiter-predicates | ||
341 | (defun paredit@dont-space-@ (endp delimiter) | ||
342 | "Don't add a space after @ in `paredit-mode'." | ||
343 | (let ((point (point))) | ||
344 | (or endp | ||
345 | (seq-every-p | ||
346 | (lambda (prefix) | ||
347 | (and (> point (length prefix)) | ||
348 | (let ((start (- point (length prefix))) | ||
349 | (end point)) | ||
350 | (not (string= (buffer-substring start end) | ||
351 | prefix))))) | ||
352 | ;; Add strings to this list to inhibit adding a space | ||
353 | ;; after them. | ||
354 | '(",@")))))) | ||
355 | (with-eval-after-load 'eldoc | ||
356 | (eldoc-add-command #'paredit-backward-delete #'paredit-close-round))) | ||
357 | |||
358 | (use-package hungry-delete | ||
359 | :ensure t | ||
360 | :config | ||
361 | (setopt hungry-delete-chars-to-skip " \t" | ||
362 | hungry-delete-skip-regexp (format "[%s]" hungry-delete-chars-to-skip) | ||
363 | hungry-delete-join-reluctantly nil) | ||
364 | (add-to-list 'hungry-delete-except-modes 'eshell-mode) | ||
365 | (add-to-list 'hungry-delete-except-modes 'nim-mode) | ||
366 | (add-to-list 'hungry-delete-except-modes 'python-mode) | ||
367 | ;; Keys | ||
368 | (with-eval-after-load 'paredit | ||
369 | (define-key paredit-mode-map [remap paredit-backward-delete] | ||
370 | (defun paredit/hungry-delete-backward (arg) | ||
371 | (interactive "*p") | ||
372 | (if (looking-back hungry-delete-skip-regexp) | ||
373 | (hungry-delete-backward (or arg 1)) | ||
374 | (paredit-backward-delete arg)))) | ||
375 | (define-key paredit-mode-map [remap paredit-forward-delete] | ||
376 | (defun paredit/hungry-delete-forward (arg) | ||
377 | (interactive "*p") | ||
378 | (if (looking-at hungry-delete-skip-regexp) | ||
379 | (hungry-delete-forward (or arg 1)) | ||
380 | (paredit-forward-delete arg)))) | ||
381 | ;; Mode | ||
382 | (global-hungry-delete-mode))) | ||
383 | |||
384 | (use-package macrostep | ||
385 | :ensure t | ||
386 | :after elisp-mode | ||
387 | :bind ( :map emacs-lisp-mode-map | ||
388 | ("C-c e" . macrostep-expand) | ||
389 | :map lisp-interaction-mode-map | ||
390 | ("C-c e" . macrostep-expand))) | ||
391 | |||
392 | (use-package package-lint | ||
393 | :ensure t) | ||
394 | |||
395 | (use-package sly | ||
396 | :ensure t | ||
397 | :preface | ||
398 | (setopt inferior-lisp-program (choose-executable "sbcl")) | ||
399 | :when inferior-lisp-program | ||
400 | :bind (:map sly-mode-map | ||
401 | ("C-c C-z" . sly-mrepl)) | ||
402 | :config | ||
403 | (setopt sly-net-coding-system 'utf-8-unix) | ||
404 | (sly-symbol-completion-mode -1)) | ||
405 | |||
406 | (use-package eat | ||
407 | :ensure t | ||
408 | :hook (eshell-load-hook . eat-eshell-mode)) | ||
409 | |||
410 | (use-package pdf-tools | ||
411 | :ensure t | ||
412 | :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode) | ||
413 | :magic ("%PDF" . pdf-view-mode) | ||
414 | :config | ||
415 | (pdf-tools-install)) | ||
416 | |||
417 | (use-package keychain-environment | ||
418 | :ensure t | ||
419 | :when (executable-find "keychain") | ||
420 | :hook (after-init-hook . keychain-refresh-environment)) | ||
421 | |||
422 | (use-package web-mode | ||
423 | :ensure t | ||
424 | :mode ("\\.phtml\\'" | ||
425 | "\\.tpl\\.php\\'" | ||
426 | "\\.[agj]sp\\'" | ||
427 | "\\.as[cp]x\\'" | ||
428 | "\\.erb\\'" | ||
429 | "\\.mustache\\'" | ||
430 | "\\.djhtml\\'" | ||
431 | "\\.html?\\'")) | ||
432 | |||
433 | (use-package nginx-mode | ||
434 | :ensure t | ||
435 | :mode "/nginx/sites-\\(?:available\\|enabled\\)/") | ||
436 | |||
437 | (use-package markdown-mode | ||
438 | :ensure t | ||
439 | :mode "\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" | ||
440 | :config | ||
441 | (setopt markdown-command (choose-executable | ||
442 | '("pandoc" "--from=markdown" "--to=html5") | ||
443 | "markdown")) | ||
444 | (add-hook 'markdown-mode-hook #'visual-fill-column-mode)) | ||
445 | |||
446 | (use-package transpose-frame | ||
447 | :ensure t | ||
448 | :bind (("C-x 5 t" . transpose-frame) | ||
449 | ("C-x 5 h" . flop-frame) ; horizontal | ||
450 | ("C-x 5 v" . flip-frame) ; vertical | ||
451 | )) | ||
452 | |||
453 | (use-package magit | ||
454 | :pin melpa-stable | ||
455 | :ensure t | ||
456 | :bind ("C-x g" . magit)) | ||
diff --git a/lisp/acdw.el b/lisp/acdw.el new file mode 100644 index 0000000..f16a679 --- /dev/null +++ b/lisp/acdw.el | |||
@@ -0,0 +1,39 @@ | |||
1 | ;;; acdw.el --- My Emacs extras -*- lexical-binding: t; -*- | ||
2 | |||
3 | ;;; Code: | ||
4 | |||
5 | (defmacro defdir (name directory &optional docstring makedir) | ||
6 | "Define a variable and a function NAME expanding to DIRECTORY. | ||
7 | DOCSTRING is applied to the variable; its default is DIRECTORY's | ||
8 | path. If MAKEDIR is non-nil, the directory and its parents will | ||
9 | be created." | ||
10 | (declare (indent 2) (doc-string 3)) | ||
11 | `(progn | ||
12 | (defvar ,name (expand-file-name ,directory) | ||
13 | ,(concat (or docstring (format "%s" directory)) "\n" | ||
14 | "Defined by `defdir'.")) | ||
15 | (defun ,name (file &optional mkdir) | ||
16 | ,(concat "Expand FILE relative to variable `" (symbol-name name) "'.\n" | ||
17 | "If MKDIR is non-nil, parent directories are created.\n" | ||
18 | "Defined by `defdir'.") | ||
19 | (let ((file-name (expand-file-name | ||
20 | (convert-standard-filename file) ,name))) | ||
21 | (when mkdir | ||
22 | (make-directory (file-name-directory file-name) :parents)) | ||
23 | file-name)) | ||
24 | ,(if makedir | ||
25 | `(make-directory ,directory :parents) | ||
26 | `(unless (file-exists-p ,directory) | ||
27 | (warn "Directory `%s' doesn't exist." ,directory))))) | ||
28 | |||
29 | (defun choose-executable (&rest programs) | ||
30 | "Return the first of PROGRAMS that exists in the system's $PATH. | ||
31 | Each of PROGRAMS can be a single string, or a list. If it's a list then its car | ||
32 | will be tested with `executable-find', and the entire list returned. This | ||
33 | enables passing arguments to a calling function." | ||
34 | (seq-find (lambda (x) | ||
35 | (executable-find (car (ensure-list x)))) | ||
36 | programs)) | ||
37 | |||
38 | (provide 'acdw) | ||
39 | ;;; acdw.el ends here | ||