about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2024-06-06 23:32:52 -0500
committerCase Duckworth2024-06-06 23:32:52 -0500
commite578f72d4f1325a8f5fac4cf7272c8165d57a1f2 (patch)
treee0a71c27ab543ce19fb9aa050ffcd0cc213b48cc
parentInitial commit (diff)
downloaddots-e578f72d4f1325a8f5fac4cf7272c8165d57a1f2.tar.gz
dots-e578f72d4f1325a8f5fac4cf7272c8165d57a1f2.zip
Clean up emacs stuff
-rw-r--r--emacs173
-rw-r--r--emacs.d/early-init.el189
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.
51If 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.
139Calls `other-window', which see, unless
140- the current window is alone on its frame
141- `other-window-dwim' is called with \\[universal-argument]
142In 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.
150If the current window is alone in its frame, bury the buffer
151instead."
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.
168With 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'.
175Whether 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))))