diff options
-rw-r--r-- | early-init.el | 260 | ||||
-rw-r--r-- | init.el | 1030 | ||||
-rw-r--r-- | lisp/acdw.el | 83 |
3 files changed, 172 insertions, 1201 deletions
diff --git a/early-init.el b/early-init.el index 1bf78eb..352d4e6 100644 --- a/early-init.el +++ b/early-init.el | |||
@@ -1,10 +1,9 @@ | |||
1 | ;;; early-init.el -*- lexical-binding: t; coding: utf-8 -*- | 1 | ;;; early-init.el -*- lexical-binding: t; coding: utf-8-unix -*- |
2 | ;; Copyright (C) 2020-2021 Case Duckworth | ||
3 | ;; | 2 | ;; |
4 | ;; Author: Case Duckworth <acdw@acdw.net> | 3 | ;; Author: Case Duckworth <acdw@acdw.net> |
5 | ;; Created: Sometime during Covid-19, 2020 | 4 | ;; Created: Sometime during Covid-19, 2020 |
6 | ;; Keywords: configuration | 5 | ;; Keywords: configuration |
7 | ;; URL https://tildegit.org/acdw/emacs | 6 | ;; URL: https://tildegit.org/acdw/emacs |
8 | ;; | 7 | ;; |
9 | ;; This file is NOT part of GNU Emacs. | 8 | ;; This file is NOT part of GNU Emacs. |
10 | ;; | 9 | ;; |
@@ -23,174 +22,85 @@ | |||
23 | ;; | 22 | ;; |
24 | ;;; Code: | 23 | ;;; Code: |
25 | 24 | ||
26 | ;; Speed up init | 25 | ;;; Define personal-use constants |
27 | (setq gc-cons-threshold most-positive-fixnum | ||
28 | gc-cons-percentage 0.6 | ||
29 | comp-deferred-compilation nil) | ||
30 | |||
31 | (defconst gc-cons-basis (* 800 1024) | ||
32 | "The basis value to which to return after a max jump. | ||
33 | 800,000 (800 KB) is Emacs' default.") | ||
34 | |||
35 | (add-hook 'after-init-hook #'(lambda () | ||
36 | (setq gc-cons-threshold gc-cons-basis | ||
37 | gc-cons-percentage 0.1))) | ||
38 | |||
39 | (defun hook--gc-cons-maximize () | ||
40 | "Set `gc-cons-threshold' to the highest possible. | ||
41 | For memory-intensive features." | ||
42 | (setq gc-cons-threshold most-positive-fixnum)) | ||
43 | |||
44 | (defun hook--gc-cons-baseline () | ||
45 | "Return `gc-cons-threshold' to `gc-cons-basis'. | ||
46 | For after memory intensive operations." | ||
47 | (setq gc-cons-threshold gc-cons-basis)) | ||
48 | |||
49 | (add-hook 'minibuffer-setup-hook #'hook--gc-cons-maximize) | ||
50 | (add-hook 'minibuffer-exit-hook #'hook--gc-cons-baseline) | ||
51 | |||
52 | ;; From doom-emacs | ||
53 | (unless (daemonp) | ||
54 | (defvar doom--initial-file-name-handler-alist file-name-handler-alist) | ||
55 | (setq file-name-handler-alist nil) | ||
56 | (defun hook--reset-file-handler-alist () | ||
57 | (dolist (handler file-name-handler-alist) | ||
58 | (add-to-list 'doom--initial-file-name-handler-alist handler)) | ||
59 | (setq file-name-handler-alist doom--initial-file-name-handler-alist)) | ||
60 | (add-hook 'emacs-startup-hook #'hook--reset-file-handler-alist)) | ||
61 | |||
62 | ;; Where are we? | ||
63 | (defconst acdw/system (pcase system-type | 26 | (defconst acdw/system (pcase system-type |
64 | ('gnu/linux :home) | 27 | ('gnu/linux :home) |
65 | ((or 'msdos 'windows-nt) :work) | 28 | ((or 'msdos 'windows-nt) :work) |
66 | (_ :other))) | 29 | (_ :other)) |
67 | 30 | "Which system is currently being used.") | |
68 | ;; Frame initiation | ||
69 | 31 | ||
70 | ;; Initialize frames with as little UI as possible. | 32 | (defvar acdw/dir (expand-file-name |
71 | (setq-default | 33 | (convert-standard-filename "var/") |
72 | default-frame-alist ; The default look of frames | 34 | user-emacs-directory) |
73 | `((tool-bar-lines . 0) ; Remove tool bar | 35 | "A directory to hold extra configuration and emacs data.") |
74 | (menu-bar-lines . 0) ; Remove menu bar | ||
75 | (vertical-scroll-bars) ; Remove vertical scroll bars | ||
76 | (horizontal-scroll-bars) ; Remove horizontal scroll bars | ||
77 | (width . 84) ; A /little/ wider than `fill-column' | ||
78 | (height . 30) ; Text characters | ||
79 | (left-fringe . 8) ; Width of fringes | ||
80 | (right-fringe . 8) ; (8 is the default) | ||
81 | (font . ,(pcase acdw/system ; Default font | ||
82 | (:home "Terminus 12") | ||
83 | (:work "Consolas 11"))) | ||
84 | ) | ||
85 | 36 | ||
86 | x-underline-at-descent-line t ; underline at the descent line | 37 | ;;; Speed up init |
38 | ;; see doom-emacs, et al. | ||
87 | 39 | ||
88 | scroll-margin 0 ; how many lines to show at window edge | 40 | (defconst gc-cons-threshold-basis (* 800 1000) |
89 | scroll-conservatively 101 ; just enough to bring text into view | 41 | "The basis value for `gc-cons-threshold' to return to after a jump. |
90 | scroll-preserve-screen-position 1 ; always keep screen position | 42 | 800 KB is Emacs's default `gc-cons-threshold'.") |
91 | |||
92 | frame-title-format ; Titles for frames | ||
93 | '((:eval (if (buffer-file-name) ; (cf. `mode-line-format') | ||
94 | (abbreviate-file-name (buffer-file-name)) | ||
95 | "%b")) | ||
96 | " " | ||
97 | mode-line-client | ||
98 | mode-line-modified | ||
99 | " - GNU Emacs") | ||
100 | 43 | ||
101 | mode-line-format ; Mode line | 44 | (defconst gc-cons-percentage-basis 0.1 |
102 | `("%e" | 45 | "The basis value for `gc-cons-percentage' to return to after init. |
103 | mode-line-front-space | 46 | 0.1 is Emacs's default `gc-cons-percentage'.") |
104 | ;; mode-line-mule-info | ||
105 | mode-line-client | ||
106 | mode-line-modified | ||
107 | mode-line-remote | ||
108 | mode-line-frame-identification | ||
109 | mode-line-buffer-identification " " | ||
110 | mode-line-position | ||
111 | (vc-mode vc-mode) " " | ||
112 | minions-mode-line-modes | ||
113 | mode-line-misc-info | ||
114 | mode-line-end-spaces | ||
115 | )) | ||
116 | 47 | ||
117 | ;; Set the rest of the fonts after initiation | 48 | (defvar orig-file-name-handler-alist file-name-handler-alist |
118 | (defun hook--setup-fonts () | 49 | "The original value of `file-name-handler-alist' will be restored |
119 | (pcase acdw/system | 50 | after init.") |
120 | (:home (set-face-attribute 'default nil | ||
121 | :family "Terminus" | ||
122 | :height 120) | ||
123 | (set-face-attribute 'fixed-pitch nil | ||
124 | :family "Terminus" | ||
125 | :height 1.0) | ||
126 | (set-face-attribute 'variable-pitch nil | ||
127 | :family "DejaVu Sans" | ||
128 | :height 1.0)) | ||
129 | (:work (set-face-attribute 'default nil | ||
130 | :family "Consolas" | ||
131 | :height 110) | ||
132 | (set-face-attribute 'fixed-pitch nil | ||
133 | :family "Consolas" | ||
134 | :height 1.0) | ||
135 | (set-face-attribute 'variable-pitch nil | ||
136 | :family "Cambria" | ||
137 | :height 1.0)))) | ||
138 | 51 | ||
139 | (add-hook 'after-init-hook #'hook--setup-fonts) | 52 | (setq gc-cons-threshold most-positive-fixnum |
140 | 53 | gc-cons-percentage 0.6 | |
141 | ;; In case I do want the UI elements later, I also disable the modes | 54 | file-name-handler-alist nil) |
142 | ;; -- otherwise I'd have to run the mode twice to actually show the | 55 | |
143 | ;; thing. | 56 | (defun hook--post-init-reset () |
57 | "Reset `gc-cons-threshold', `gc-cons-percentage', and | ||
58 | `file-name-handler-alist' to their defaults after init." | ||
59 | (setq gc-cons-threshold gc-cons-threshold-basis | ||
60 | gc-cons-percentage gc-cons-percentage-basis) | ||
61 | (dolist (handler file-name-handler-alist) | ||
62 | (add-to-list 'orig-file-name-handler-alist handler)) | ||
63 | (setq file-name-handler-alist orig-file-name-handler-alist)) | ||
64 | |||
65 | (add-hook 'after-init-hook #'hook--post-init-reset) | ||
66 | |||
67 | ;; ;;; Frame settings | ||
68 | |||
69 | (setq default-frame-alist ; Remove most UI | ||
70 | `((tool-bar-lines . 0) ; No tool bar | ||
71 | (menu-bar-lines . 0) ; No menu bar | ||
72 | (vertical-scroll-bars) ; No scroll bars | ||
73 | (horizontal-scroll-bars) ; ... at all | ||
74 | (width . 84) ; A /little/ wider than | ||
75 | ; `fill-column' (set later) | ||
76 | (height . 30) | ||
77 | (left-fringe . 8) ; Width of fringes | ||
78 | (right-fringe . 8) ; (8 is default) | ||
79 | (font . ,(pcase acdw/system | ||
80 | (:home "Terminus 12") | ||
81 | (:work "Consolas 10")))) | ||
82 | frame-inhibit-implied-resize t ; Don't resize randomly | ||
83 | frame-resize-pixelwise t ; Resize by pixels, not chars | ||
84 | ) | ||
144 | 85 | ||
145 | (defun hook--disable-ui-modes () | 86 | (defun hook--disable-ui-modes () |
146 | (dolist (mode '(tool-bar-mode | 87 | "Disable frame UI using modes, for toggling later." |
147 | menu-bar-mode | 88 | (dolist (mode ;; each mode is of the form (MODE . FRAME-ALIST-VAR) |
148 | scroll-bar-mode | 89 | '((tool-bar-mode . tool-bar-lines) |
149 | horizontal-scroll-bar-mode)) | 90 | (menu-bar-mode . menu-bar-lines) |
150 | (funcall mode -1))) | 91 | (scroll-bar-mode . vertical-scroll-bars) |
92 | (horizontal-scroll-bar-mode . horizontal-scroll-bars) | ||
93 | )) | ||
94 | (let ((setting (alist-get (cdr mode) default-frame-alist))) | ||
95 | (when (or (not setting) | ||
96 | (= 0 setting)) | ||
97 | (funcall (car mode) -1))))) | ||
151 | 98 | ||
152 | ;; I run it on the `after-init-hook' so it doesn't slow down init so much. | ||
153 | (add-hook 'after-init-hook #'hook--disable-ui-modes) | 99 | (add-hook 'after-init-hook #'hook--disable-ui-modes) |
154 | 100 | ||
155 | ;; Customize the fringe | 101 | ;;; Bootstrap package manager (`straight.el') |
156 | (setq-default | ||
157 | indicate-empty-lines t ; show an indicator at the end of the buffer | ||
158 | indicate-buffer-boundaries 'right ; show buffer boundaries on the right | ||
159 | visual-line-fringe-indicators ; show continuation indicators on the left | ||
160 | '(left-curly-arrow nil)) | ||
161 | |||
162 | (defun hook--setup-fringe-bitmaps () | ||
163 | (define-fringe-bitmap 'left-curly-arrow | ||
164 | [#b11000000 | ||
165 | #b01100000 | ||
166 | #b00110000 | ||
167 | #b00011000]) | ||
168 | (define-fringe-bitmap 'right-curly-arrow | ||
169 | [#b00011000 | ||
170 | #b00110000 | ||
171 | #b01100000 | ||
172 | #b11000000]) | ||
173 | (define-fringe-bitmap 'left-arrow | ||
174 | [#b00000000 | ||
175 | #b01010100 | ||
176 | #b01010100 | ||
177 | #b00000000]) | ||
178 | (define-fringe-bitmap 'right-arrow | ||
179 | [#b00000000 | ||
180 | #b00101010 | ||
181 | #b00101010 | ||
182 | #b00000000]) | ||
183 | (remove-function after-focus-change-function #'hook--setup-fringe-bitmaps)) | ||
184 | (add-function :after after-focus-change-function #'hook--setup-fringe-bitmaps) | ||
185 | |||
186 | ;; Resize like it's 2021 | ||
187 | (setq-default frame-inhibit-implied-resize t | ||
188 | frame-resize-pixelwise t) | ||
189 | |||
190 | ;; Bootstrap package manager (`straight') | ||
191 | |||
192 | ;; First, I need to make sure it's in the `exec-path'. | ||
193 | 102 | ||
103 | ;; 1. Update `exec-path'. | ||
194 | (let ((win-app-dir "~/Applications")) | 104 | (let ((win-app-dir "~/Applications")) |
195 | (dolist (path (list | 105 | (dolist (path (list |
196 | ;; Windows | 106 | ;; Windows |
@@ -210,26 +120,19 @@ | |||
210 | )) | 120 | )) |
211 | (when (file-exists-p path) | 121 | (when (file-exists-p path) |
212 | (add-to-list 'exec-path path :append)))) | 122 | (add-to-list 'exec-path path :append)))) |
213 | 123 | ;; 1.5. Update $PATH to reflect changes. | |
214 | ;; Set $PATH back to `exec-path', for symmetry's sake. | ||
215 | (setenv "PATH" (mapconcat #'identity exec-path path-separator)) | 124 | (setenv "PATH" (mapconcat #'identity exec-path path-separator)) |
216 | 125 | ||
217 | ;; Set some variables | 126 | ;; 2. Set `package' and `straight' variables. |
218 | (defvar acdw/etc-dir | 127 | (setq package-enable-at-startup nil ; not sure if strictly |
219 | (expand-file-name "etc/" user-emacs-directory) | 128 | ; necessary |
220 | "Where to put other configurations.") | 129 | package-quickstart nil ; ditto |
221 | (defvar acdw/var-dir | 130 | straight-host-usernames '((github . "duckwork") |
222 | (expand-file-name "var/" user-emacs-directory) | 131 | (gitlab . "acdw")) |
223 | "Where to put variable stuff.") | 132 | straight-base-dir acdw/dir ; don't clutter ~/.emacs.d |
133 | ) | ||
224 | 134 | ||
225 | (setq-default package-enable-at-startup nil | 135 | ;; 3. Bootstrap `straight'. |
226 | package-quickstart nil | ||
227 | straight-use-package-by-default t | ||
228 | straight-host-usernames '((github . "duckwork") | ||
229 | (gitlab . "acdw")) | ||
230 | straight-base-dir acdw/var-dir) | ||
231 | |||
232 | ;; Run `straight''s bootstrap code | ||
233 | (defvar bootstrap-version) | 136 | (defvar bootstrap-version) |
234 | (let ((bootstrap-file | 137 | (let ((bootstrap-file |
235 | (expand-file-name | 138 | (expand-file-name |
@@ -239,20 +142,17 @@ | |||
239 | (unless (file-exists-p bootstrap-file) | 142 | (unless (file-exists-p bootstrap-file) |
240 | (with-current-buffer | 143 | (with-current-buffer |
241 | (url-retrieve-synchronously | 144 | (url-retrieve-synchronously |
242 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | 145 | (concat "https://raw.githubusercontent.com/" |
146 | "raxod502/straight.el/develop/install.el") | ||
243 | 'silent 'inhibit-cookies) | 147 | 'silent 'inhibit-cookies) |
244 | (goto-char (point-max)) | 148 | (goto-char (point-max)) |
245 | (eval-print-last-sexp))) | 149 | (eval-print-last-sexp))) |
246 | (load bootstrap-file nil 'nomessage)) | 150 | (load bootstrap-file nil 'nomessage)) |
247 | 151 | ||
248 | ;; `use-package' | 152 | ;;; Message startup time for profiling |
249 | |||
250 | (straight-use-package 'use-package) | ||
251 | (require 'use-package) | ||
252 | 153 | ||
253 | ;; Message startup time | ||
254 | (defun hook--message-startup-time () | 154 | (defun hook--message-startup-time () |
255 | "Message Emacs' startup time." | 155 | "Show Emacs's startup time in the message buffer. For profiling." |
256 | (message "Emacs ready in %s with %d garbage collections." | 156 | (message "Emacs ready in %s with %d garbage collections." |
257 | (format "%.2f seconds" | 157 | (format "%.2f seconds" |
258 | (float-time (time-subtract after-init-time | 158 | (float-time (time-subtract after-init-time |
diff --git a/init.el b/init.el index 94e2a0e..7f6da30 100644 --- a/init.el +++ b/init.el | |||
@@ -1,10 +1,10 @@ | |||
1 | ;;; init.el -*- lexical-binding: t; coding: utf-8 -*- | 1 | :;;; init.el -*- lexical-binding: t; coding: utf-8-unix -*- |
2 | ;; Copyright (C) 2020-2021 Case Duckworth | ||
3 | ;; | 2 | ;; |
4 | ;; Author: Case Duckworth <acdw@acdw.net> | 3 | ;; Author: Case Duckworth <acdw@acdw.net> |
5 | ;; Created: Sometime during Covid-19, 2020 | 4 | ;; Created: Sometime during Covid-19, 2020 |
6 | ;; Keywords: configuration | 5 | ;; Keywords: configuration |
7 | ;; URL https://tildegit.org/acdw/emacs | 6 | ;; URL: https://tildegit.org/acdw/emacs |
7 | ;; Bankruptcy: 5b | ||
8 | ;; | 8 | ;; |
9 | ;; This file is NOT part of GNU Emacs. | 9 | ;; This file is NOT part of GNU Emacs. |
10 | ;; | 10 | ;; |
@@ -16,1024 +16,12 @@ | |||
16 | ;; - Don't hurt yourself. | 16 | ;; - Don't hurt yourself. |
17 | ;; - Make good choices. | 17 | ;; - Make good choices. |
18 | ;; | 18 | ;; |
19 | ;;; Comentary: | ||
20 | ;; | ||
21 | ;;; Research: | ||
22 | ;; (map! :leader (:prefix "w" :desc "Toggle full screen buffer" "f" | ||
23 | ;; #'toggle-maximize-buffer)) | ||
24 | ;; | ||
25 | ;;; Code: | 19 | ;;; Code: |
26 | 20 | ||
27 | ;; User information | 21 | ;; Add `acdw.el' |
28 | (setq user-full-name "Case Duckworth" | 22 | (add-to-list 'load-path (expand-file-name "lisp/" |
29 | user-mail-address "acdw@acdw.net" | 23 | user-emacs-directory)) |
30 | calendar-location-name "Baton Rouge, LA" | 24 | (require 'acdw) |
31 | calendar-latitude 30.4 | ||
32 | calendar-longitude -91.1 | ||
33 | calendar-date-style 'iso | ||
34 | custom-file (expand-file-name "custom.el" acdw/etc-dir)) | ||
35 | |||
36 | ;; Load newer files first | ||
37 | (setq-default load-prefer-newer t) | ||
38 | |||
39 | ;; No littering | ||
40 | (use-package no-littering | ||
41 | :demand | ||
42 | :init (setq no-littering-etc-directory acdw/etc-dir | ||
43 | no-littering-var-directory acdw/var-dir)) | ||
44 | |||
45 | (defun when-unfocused (func &rest args) | ||
46 | "Run FUNC with ARGS iff all frames are out of focus." | ||
47 | (when (seq-every-p #'null (mapcar #'frame-focus-state (frame-list))) | ||
48 | (apply func args))) | ||
49 | |||
50 | (define-minor-mode acdw/reading-mode | ||
51 | "Make reading comfier." | ||
52 | :lighter " Read" | ||
53 | (if acdw/reading-mode | ||
54 | (progn ;; turn on | ||
55 | ;; (text-scale-increase +1) | ||
56 | (display-fill-column-indicator-mode -1) | ||
57 | (setq cursor-type 'hbar) | ||
58 | (dolist (func '(visual-fill-column-mode | ||
59 | iscroll-mode)) | ||
60 | (when (fboundp func) | ||
61 | (funcall func +1)))) | ||
62 | (progn ;; turn off | ||
63 | ;; (text-scale-increase 0) | ||
64 | (setq cursor-type 'bar) | ||
65 | (display-fill-column-indicator-mode +1) | ||
66 | (dolist (func '(visual-fill-column-mode | ||
67 | iscroll-mode)) | ||
68 | (when (fboundp func) | ||
69 | (funcall func -1)))))) | ||
70 | |||
71 | (defun hook--read-only-cursor () | ||
72 | (setq cursor-type (if buffer-read-only 'hbar 'bar))) | ||
73 | (add-hook 'read-only-mode-hook #'hook--read-only-cursor) | ||
74 | |||
75 | ;; Dialogs & alerts | ||
76 | (setq-default use-dialog-box nil) ; Don't use a dialog box | ||
77 | (fset 'yes-or-no-p #'y-or-n-p) | ||
78 | |||
79 | (defun flash-mode-line () | ||
80 | (ding) | ||
81 | (invert-face 'mode-line) | ||
82 | (run-with-timer 0.2 nil #'invert-face 'mode-line)) | ||
83 | |||
84 | (setq-default visible-bell nil ; Don't use a visible bell | ||
85 | ring-bell-function #'flash-mode-line) | ||
86 | |||
87 | (defun hook--gc-when-unfocused () | ||
88 | (when-unfocused #'garbage-collect)) | ||
89 | |||
90 | (add-function :after after-focus-change-function | ||
91 | #'hook--gc-when-unfocused) | ||
92 | |||
93 | ;; Minibuffer | ||
94 | (setq-default | ||
95 | minibuffer-prompt-properties '(read-only t | ||
96 | cursor-intangible t | ||
97 | face minibuffer-prompt) | ||
98 | enable-recursive-minibuffers t | ||
99 | file-name-shadow-properties '(invisible t)) | ||
100 | (file-name-shadow-mode +1) | ||
101 | (minibuffer-depth-indicate-mode +1) | ||
102 | |||
103 | (use-package savehist | ||
104 | :straight nil | ||
105 | :init | ||
106 | (setq-default | ||
107 | savehist-file (expand-file-name "history" acdw/var-dir) | ||
108 | savehist-additional-variables '(kill-ring search-ring regexp-search-ring) | ||
109 | history-length t | ||
110 | history-delete-duplicates t | ||
111 | savehist-autosave-interval 60) | ||
112 | :config (savehist-mode +1)) | ||
113 | |||
114 | ;; Backups | ||
115 | (setq-default backup-by-copying t | ||
116 | delete-old-versions -1 ; Don't delete old versions | ||
117 | version-control t ; Make numeric backups | ||
118 | vc-make-backup-files t ; Backup version-controlled files | ||
119 | ) | ||
120 | |||
121 | (let ((dir (expand-file-name "backup" acdw/var-dir))) | ||
122 | (make-directory dir 'parents) | ||
123 | (setq-default backup-directory-alist | ||
124 | `((".*" . ,dir)))) | ||
125 | |||
126 | ;; Lockfiles | ||
127 | (setq-default create-lockfiles nil) ; Are these necessary? | ||
128 | |||
129 | ;; Autosaves | ||
130 | (setq auto-save-default nil ; Don't use `auto-save' system | ||
131 | ) | ||
132 | (use-package super-save | ||
133 | :defer 5 ; This package can wait | ||
134 | :init | ||
135 | (setq-default | ||
136 | super-save-remote-files nil ; Don't save remote files | ||
137 | super-save-exclude '(".gpg") ; Wouldn't work anyway | ||
138 | super-save-auto-save-when-idle t) | ||
139 | :config | ||
140 | (super-save-mode +1)) | ||
141 | |||
142 | ;; Auto-revert | ||
143 | (global-auto-revert-mode +1) ; Automatically revert a file | ||
144 | ; to its on-disk contents | ||
145 | |||
146 | (use-package saveplace | ||
147 | :straight nil | ||
148 | :init | ||
149 | (setq-default | ||
150 | save-place-file (expand-file-name "places" acdw/var-dir) | ||
151 | save-place-forget-unreadable-files (eq acdw/system :home)) | ||
152 | :config (save-place-mode +1)) | ||
153 | |||
154 | (use-package recentf | ||
155 | :straight nil | ||
156 | :init | ||
157 | (setq recentf-save-file (expand-file-name "recentf" acdw/var-dir) | ||
158 | recentf-max-menu-items 100 | ||
159 | recentf-max-saved-items nil | ||
160 | recentf-auto-cleanup 'never) | ||
161 | (defun maybe-save-recentf () | ||
162 | "Save `recentf-file' every five minutes, but only when out of focus." | ||
163 | (defvar recentf--last-save (time-convert nil 'integer) | ||
164 | "When we last saved the `recentf-save-list'.") | ||
165 | |||
166 | (when (> (time-convert (time-since recentf--last-save) 'integer) | ||
167 | (* 60 5)) | ||
168 | (setq-default recentf--last-save (time-convert nil 'integer)) | ||
169 | (when-unfocused #'recentf-save-list))) | ||
170 | :config | ||
171 | (recentf-mode +1) | ||
172 | (add-to-list 'recentf-exclude acdw/var-dir) | ||
173 | (add-to-list 'recentf-exclude acdw/etc-dir) | ||
174 | (add-function :after after-focus-change-function | ||
175 | #'maybe-save-recentf)) | ||
176 | |||
177 | |||
178 | ;; Uniquify | ||
179 | (use-package uniquify | ||
180 | :straight nil | ||
181 | :init | ||
182 | (setq-default | ||
183 | uniquify-buffer-name-style 'forward ; bubble 'up' the directory tree | ||
184 | uniquify-separator "/" ; separate path elements | ||
185 | uniquify-after-kill-buffer-p t ; hook into buffer kills | ||
186 | uniquify-ignore-buffers-re "^\\*" ; don't worry about special buffers | ||
187 | )) | ||
188 | |||
189 | ;; Scratch | ||
190 | (setq-default | ||
191 | inhibit-startup-screen t ; Don't show the splash screen | ||
192 | initial-buffer-choice t ; Start on *scratch* | ||
193 | initial-scratch-message | ||
194 | (concat ";; Howdy, " | ||
195 | (nth 0 (split-string user-full-name)) "!" | ||
196 | " Welcome to GNU Emacs.\n\n")) | ||
197 | |||
198 | (defun immortal-scratch () | ||
199 | "Don't kill *scratch* when asked to by `kill-buffer'." | ||
200 | (if (not (eq (current-buffer) (get-buffer "*scratch*"))) | ||
201 | t | ||
202 | (bury-buffer) | ||
203 | nil)) | ||
204 | (add-hook 'kill-buffer-query-functions #'immortal-scratch) | ||
205 | |||
206 | ;; Easier buffer-killing | ||
207 | (defun kill-a-buffer (&optional prefix) | ||
208 | "Kill a buffer and its window, prompting only on unsaved changes. | ||
209 | |||
210 | `kill-a-buffer' uses the PREFIX argument to determine which buffer(s) to kill: | ||
211 | 0 => Kill THIS buffer & window | ||
212 | 4 (C-u) => Kill OTHER buffer & window | ||
213 | 16 (C-u C-u) => Run the default `kill-buffer'." | ||
214 | (interactive "P") | ||
215 | (pcase (or (car prefix) 0) | ||
216 | (0 (kill-current-buffer) | ||
217 | (unless (one-window-p) (delete-window))) | ||
218 | (4 (other-window 1) | ||
219 | (kill-current-buffer) | ||
220 | (unless (one-window-p) (delete-window))) | ||
221 | (16 (let ((current-prefix-arg nil)) | ||
222 | (kill-buffer))))) | ||
223 | |||
224 | (bind-key "C-x k" #'kill-a-buffer) | ||
225 | |||
226 | ;; UTF-8 with LF line endings | ||
227 | (set-charset-priority 'unicode) | ||
228 | (set-language-environment "UTF-8") | ||
229 | |||
230 | (prefer-coding-system 'utf-8-unix) | ||
231 | (set-default-coding-systems 'utf-8-unix) | ||
232 | (set-terminal-coding-system 'utf-8-unix) | ||
233 | (set-keyboard-coding-system 'utf-8-unix) | ||
234 | (set-selection-coding-system 'utf-8-unix) | ||
235 | |||
236 | (setq-default | ||
237 | locale-coding-system 'utf-8-unix | ||
238 | coding-system-for-read 'utf-8-unix | ||
239 | coding-system-for-write 'utf-8-unix | ||
240 | buffer-file-coding-system 'utf-8-unix | ||
241 | |||
242 | org-export-coding-system 'utf-8-unix | ||
243 | org-html-coding-system 'utf-8-unix ; doesn't take from above | ||
244 | |||
245 | default-process-coding-system '(utf-8-unix . utf-8-unix) | ||
246 | x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) | ||
247 | |||
248 | (defun ewiki/no-junk-please-were-unixish () | ||
249 | "Convert line endings to UNIX, dammit." | ||
250 | (let ((coding-str (symbol-name buffer-file-coding-system))) | ||
251 | (when (string-match "-\\(?:dos\\|mac\\)$" coding-str) | ||
252 | (set-buffer-file-coding-system 'unix)))) | ||
253 | |||
254 | (add-hook 'find-file-hook #'ewiki/no-junk-please-were-unixish) | ||
255 | (add-hook 'before-save-hook #'ewiki/no-junk-please-were-unixish) | ||
256 | |||
257 | ;; Cursor | ||
258 | (setq-default cursor-type 'bar | ||
259 | cursor-in-non-selected-windows nil) | ||
260 | (blink-cursor-mode 0) | ||
261 | |||
262 | ;; Filling text | ||
263 | (setq-default fill-column 80) | ||
264 | (global-display-fill-column-indicator-mode +1) | ||
265 | |||
266 | (bind-key "C-x f" #'find-file) ; I don't set `fill-column', ever | ||
267 | |||
268 | (setq-default comment-auto-fill-only-comments t) | ||
269 | ;; Enable `auto-fill-mode' everywhere | ||
270 | (add-hook 'text-mode-hook #'auto-fill-mode) | ||
271 | (add-hook 'prog-mode-hook #'auto-fill-mode) | ||
272 | ;; Also enable `visual-line-mode' everywhere | ||
273 | (global-visual-line-mode +1) | ||
274 | ;; "Fix" `visual-line-mode' in `org-mode' | ||
275 | (defun hook--visual-line-fix-org-keys () | ||
276 | (when (derived-mode-p 'org-mode) | ||
277 | (local-set-key (kbd "C-a") #'org-beginning-of-line) | ||
278 | (local-set-key (kbd "C-e") #'org-end-of-line) | ||
279 | (local-set-key (kbd "C-k") #'org-kill-line))) | ||
280 | (add-hook 'visual-line-mode-hook #'hook--visual-line-fix-org-keys) | ||
281 | |||
282 | (dolist (margin '(right-margin left-margin)) | ||
283 | (dolist (button '(mouse-1 mouse-2 mouse-3)) | ||
284 | (global-set-key (vector margin button) | ||
285 | (global-key-binding (vector button))))) | ||
286 | |||
287 | (mouse-wheel-mode +1) | ||
288 | |||
289 | (when (bound-and-true-p mouse-wheel-mode) | ||
290 | (dolist (margin '(right-margin left-margin)) | ||
291 | (dolist (event '(mouse-wheel-down-event | ||
292 | mouse-wheel-up-event | ||
293 | wheel-down | ||
294 | wheel-up | ||
295 | mouse-4 | ||
296 | mouse-5)) | ||
297 | (global-set-key (vector margin event) #'mwheel-scroll)))) | ||
298 | |||
299 | (use-package visual-fill-column | ||
300 | :init (setq-default visual-fill-column-center-text t) | ||
301 | (add-hook 'visual-fill-column-mode-hook #'visual-line-mode) | ||
302 | :config | ||
303 | (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) | ||
304 | |||
305 | (when (fboundp 'global-so-long-mode) | ||
306 | (global-so-long-mode +1)) | ||
307 | |||
308 | ;; Whitespace | ||
309 | (setq-default whitespace-style '(empty ; remove blank lines at buffer edges | ||
310 | indentation ; clean up indentation | ||
311 | ;; mixed tabs & spaces | ||
312 | space-before-tab | ||
313 | space-after-tab)) | ||
314 | (add-hook 'before-save-hook #'whitespace-cleanup) | ||
315 | |||
316 | (setq-default indent-tabs-mode t | ||
317 | tab-width 8) | ||
318 | |||
319 | (use-package smart-tabs-mode | ||
320 | :config | ||
321 | (smart-tabs-insinuate 'c 'c++ 'java 'javascript 'cperl 'python 'ruby 'nxml)) | ||
322 | |||
323 | ;; Window layouts | ||
324 | (setq-default | ||
325 | split-width-threshold 100 ; minimum width for window splits | ||
326 | split-height-threshold 50 ; minimum height for window splits | ||
327 | display-buffer-alist ; how to display buffers | ||
328 | '((".*" . (display-buffer-reuse-window display-buffer-same-window))) | ||
329 | display-buffer-reuse-frames t ; allow reuse of frames | ||
330 | even-window-sizes nil ; avoid resizing windows to even them | ||
331 | help-window-select t ; select *Help* window when opened | ||
332 | ) | ||
333 | |||
334 | (defun vsplit-other-window () | ||
335 | "Split the window vertically and switch to the new window." | ||
336 | (interactive) | ||
337 | (split-window-vertically) | ||
338 | (other-window 1 nil)) | ||
339 | |||
340 | (defun hsplit-other-window () | ||
341 | "Split the window horizontally and switch to the new window." | ||
342 | (interactive) | ||
343 | (split-window-horizontally) | ||
344 | (other-window 1 nil)) | ||
345 | |||
346 | (bind-key "C-x 2" #'vsplit-other-window) | ||
347 | (bind-key "C-x 3" #'hsplit-other-window) | ||
348 | |||
349 | ;; Theming | ||
350 | |||
351 | (use-package form-feed | ||
352 | :demand | ||
353 | :config (global-form-feed-mode +1)) | ||
354 | |||
355 | (use-package modus-themes | ||
356 | :straight (:host gitlab :repo "protesilaos/modus-themes") | ||
357 | :demand | ||
358 | :init | ||
359 | (setq-default modus-themes-slanted-constructs t | ||
360 | modus-themes-bold-constructs t | ||
361 | modus-themes-region 'bg-only | ||
362 | modus-themes-org-blocks 'grayscale | ||
363 | modus-themes-headings '((1 . section) | ||
364 | (t . no-color)) | ||
365 | modus-themes-scale-headings nil | ||
366 | modus-themes-mode-line nil) | ||
367 | :custom-face | ||
368 | (modus-theme-heading-1 | ||
369 | ((t (:inherit (modus-theme-heading-1 fixed-pitch bold))))) | ||
370 | (modus-theme-heading-2 | ||
371 | ((t (:inherit (modus-theme-heading-2 fixed-pitch bold))))) | ||
372 | (modus-theme-heading-3 | ||
373 | ((t (:inherit (modus-theme-heading-3 fixed-pitch bold))))) | ||
374 | (modus-theme-heading-4 | ||
375 | ((t (:inherit (modus-theme-heading-4 fixed-pitch bold))))) | ||
376 | (modus-theme-heading-5 | ||
377 | ((t (:inherit (modus-theme-heading-5 fixed-pitch bold))))) | ||
378 | (modus-theme-heading-6 | ||
379 | ((t (:inherit (modus-theme-heading-6 fixed-pitch bold))))) | ||
380 | (modus-theme-heading-7 | ||
381 | ((t (:inherit (modus-theme-heading-7 fixed-pitch bold))))) | ||
382 | (modus-theme-heading-8 | ||
383 | ((t (:inherit (modus-theme-heading-8 fixed-pitch bold)))))) | ||
384 | |||
385 | ;; Change themes based on time of day | ||
386 | |||
387 | (defun acdw/run-with-sun (sunrise-command sunset-command) | ||
388 | "Run commands at sunrise and sunset." | ||
389 | (let* ((times-regex (rx (* nonl) | ||
390 | (: (any ?s ?S) "unrise") " " | ||
391 | (group (repeat 1 2 digit) ":" | ||
392 | (repeat 1 2 digit) | ||
393 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
394 | (* nonl) | ||
395 | (: (any ?s ?S) "unset") " " | ||
396 | (group (repeat 1 2 digit) ":" | ||
397 | (repeat 1 2 digit) | ||
398 | (: (any ?a ?A ?p ?P) (any ?m ?M))) | ||
399 | (* nonl))) | ||
400 | (ss (sunrise-sunset)) | ||
401 | (_m (string-match times-regex ss)) | ||
402 | (sunrise-time (match-string 1 ss)) | ||
403 | (sunset-time (match-string 2 ss))) | ||
404 | (run-at-time sunrise-time (* 60 60 24) sunrise-command) | ||
405 | (run-at-time sunset-time (* 60 60 24) sunset-command) | ||
406 | (run-at-time "0:00" (* 60 60 24) sunset-command))) | ||
407 | |||
408 | (acdw/run-with-sun #'modus-themes-load-operandi | ||
409 | #'modus-themes-load-vivendi) | ||
410 | |||
411 | (use-package minions | ||
412 | :config (minions-mode +1)) | ||
413 | |||
414 | (use-package which-key | ||
415 | :config (which-key-mode +1)) | ||
416 | |||
417 | (delete-selection-mode +1) | ||
418 | |||
419 | (setq-default | ||
420 | save-interprogram-paste-before-kill t ; save existing text before replacing | ||
421 | yank-pop-change-selection t ; update X selection when rotating ring | ||
422 | x-select-enable-clipboard t ; Enable X clipboards | ||
423 | x-select-enable-primary t | ||
424 | mouse-drag-copy-region t ; Copy a region when mouse-selected | ||
425 | kill-do-not-save-duplicates t ; Don't append the same thing twice | ||
426 | ) | ||
427 | |||
428 | (use-package smartscan | ||
429 | :config (global-smartscan-mode +1)) | ||
430 | |||
431 | (when (fboundp 'global-goto-address-mode) | ||
432 | (global-goto-address-mode +1)) | ||
433 | |||
434 | (use-package flyspell | ||
435 | :init | ||
436 | (setenv "LANG" "en_US") | ||
437 | (setq-default ispell-program-name "hunspell" | ||
438 | ispell-dictionary "en_US" | ||
439 | ispell-personal-dictionary "~/.hunspell_personal") | ||
440 | :hook | ||
441 | (text-mode . flyspell-mode) | ||
442 | (prog-mode . flyspell-prog-mode) | ||
443 | :config | ||
444 | (ispell-set-spellchecker-params) | ||
445 | (unless (file-exists-p ispell-personal-dictionary) | ||
446 | (write-region "" nil ispell-personal-dictionary nil 0))) | ||
447 | |||
448 | (use-package flyspell-correct | ||
449 | :after flyspell | ||
450 | :bind (:map flyspell-mode-map | ||
451 | ("C-;" . flyspell-correct-wrapper))) | ||
452 | |||
453 | (setq-default show-paren-delay 0 | ||
454 | show-paren-style 'mixed | ||
455 | show-paren-when-point-inside-paren t | ||
456 | show-paren-when-point-in-periphery t) | ||
457 | (show-paren-mode +1) | ||
458 | |||
459 | (add-hook 'prog-mode-hook #'electric-pair-local-mode) | ||
460 | |||
461 | (setq-default prettify-symbols-unprettify-at-point 'right-edge) | ||
462 | (add-hook 'prog-mode-hook #'prettify-symbols-mode) | ||
463 | |||
464 | (add-hook 'after-save-hook | ||
465 | #'executable-make-buffer-file-executable-if-script-p) | ||
466 | |||
467 | (setq-default compilation-ask-about-save nil ; just save the buffer | ||
468 | compilation-always-kill t ; kill the processes without asking | ||
469 | compilation-scroll-output 'first-error) | ||
470 | |||
471 | (use-package reformatter | ||
472 | :demand) | ||
473 | |||
474 | (setq-default smie-indent-basic 8) | ||
475 | |||
476 | ;; Shell scripts | ||
477 | (setq-default sh-basic-offset 8 | ||
478 | ;; try to indent like shfmt | ||
479 | sh-indent-after-case 0 | ||
480 | sh-indent-for-case-alt '+ | ||
481 | sh-indent-for-case-label 0) | ||
482 | |||
483 | (use-package flymake-shellcheck | ||
484 | :when (executable-find "shellcheck") | ||
485 | :hook (sh-mode . flymake-shellcheck-load)) | ||
486 | |||
487 | (when (executable-find "shfmt") | ||
488 | (reformatter-define sh-format | ||
489 | :program "shfmt" | ||
490 | :lighter "Shfmt") | ||
491 | (add-hook 'sh-mode-hook #'sh-format-on-save-mode)) | ||
492 | |||
493 | (bind-key "M-/" #'hippie-expand) | ||
494 | |||
495 | ;; Tabs | ||
496 | (setq-default | ||
497 | tab-bar-show 1 ; show the tab bar when more than one | ||
498 | tab-bar-new-tab-choice "*scratch*" ; what to show on a new tab | ||
499 | tab-bar-tab-name-function ; how to name a new tab | ||
500 | #'tab-bar-tab-name-current-with-count | ||
501 | tab-bar-history-limit 25 ; how many tabs to save in history | ||
502 | ) | ||
503 | |||
504 | (tab-bar-history-mode +1) | ||
505 | |||
506 | ;; Smart hungry delete | ||
507 | (use-package smart-hungry-delete | ||
508 | :defer nil | ||
509 | :bind (("<backspace>" . smart-hungry-delete-backward-char) | ||
510 | ("C-d" . smart-hungry-delete-forward-char)) | ||
511 | :config (smart-hungry-delete-add-default-hooks)) | ||
512 | |||
513 | ;; Enable all commands | ||
514 | (setq-default disabled-command-function nil) | ||
515 | |||
516 | ;; Magit | ||
517 | (use-package magit | ||
518 | :bind ("C-x g" . magit-status) | ||
519 | :init (when (eq acdw/system :work) | ||
520 | (setenv "GIT_ASKPASS" "git-gui--askpass")) | ||
521 | (defun magit-display-buffer-same-window (buffer) | ||
522 | "Show `magit' in the same buffer, like god intended." | ||
523 | (display-buffer buffer '(display-buffer-same-window))) | ||
524 | (setq magit-display-buffer-function 'magit-display-buffer-same-window | ||
525 | magit-popup-display-buffer-action '((display-buffer-same-window)))) | ||
526 | (use-package forge | ||
527 | :after magit) | ||
528 | (use-package gitattributes-mode | ||
529 | :mode "\\.gitattributes\\'") | ||
530 | (use-package gitconfig-mode | ||
531 | :mode "\\.gitconfig\\'") | ||
532 | (use-package gitignore-mode | ||
533 | :mode "\\.gitignore\\'") | ||
534 | |||
535 | ;; crux | ||
536 | (use-package crux | ||
537 | :straight (:host github :repo "bbatsov/crux") | ||
538 | :bind | ||
539 | ("M-o" . crux-other-window-or-switch-buffer) | ||
540 | :config | ||
541 | (crux-with-region-or-line kill-ring-save) | ||
542 | (crux-with-region-or-line kill-region) | ||
543 | (crux-with-region-or-line comment-or-uncomment-region)) | ||
544 | |||
545 | ;; Completion and... stuff | ||
546 | (setq-default | ||
547 | completion-ignore-case t | ||
548 | read-buffer-completion-ignore-case t | ||
549 | read-file-name-completion-ignore-case t | ||
550 | minibuffer-eldef-shorten-default t) | ||
551 | |||
552 | (minibuffer-electric-default-mode +1) | ||
553 | |||
554 | (use-package icomplete-vertical | ||
555 | :demand | ||
556 | :init | ||
557 | (setq-default | ||
558 | icomplete-delay-completions-threshold 0 | ||
559 | icomplete-max-delay-chars 0 | ||
560 | icomplete-compute-delay 0 | ||
561 | icomplete-show-matches-on-no-input t | ||
562 | icomplete-hide-common-prefix nil | ||
563 | icomplete-with-completion-tables t | ||
564 | icomplete-in-buffer t) | ||
565 | :bind (:map icomplete-minibuffer-map | ||
566 | ("<down>" . icomplete-forward-completions) | ||
567 | ("C-n" . icomplete-forward-completions) | ||
568 | ("<up>" . icomplete-backward-completions) | ||
569 | ("C-p" . icomplete-backward-completions) | ||
570 | ("C-v" . icomplete-vertical-toggle)) | ||
571 | :config | ||
572 | (fido-mode -1) | ||
573 | (icomplete-mode +1) | ||
574 | (icomplete-vertical-mode +1)) | ||
575 | |||
576 | (use-package orderless | ||
577 | :after icomplete | ||
578 | :init (setq-default completion-styles '(orderless))) | ||
579 | |||
580 | (use-package marginalia | ||
581 | :after icomplete | ||
582 | :init (setq-default marginalia-annotators | ||
583 | '(marginalia-annotators-heavy | ||
584 | marginalia-annotators-light)) | ||
585 | :config (marginalia-mode +1)) | ||
586 | |||
587 | (use-package consult | ||
588 | :after icomplete | ||
589 | :bind (;; C-c bindings (mode-specific-map) | ||
590 | ("C-c h" . consult-history) | ||
591 | ("C-c m" . consult-mode-command) | ||
592 | ("C-c b" . consult-bookmark) | ||
593 | ("C-c k" . consult-kmacro) | ||
594 | ;; C-x bindings (ctl-x-map) | ||
595 | ("C-x M-:" . consult-complex-command) | ||
596 | ("C-x b" . consult-buffer) | ||
597 | ("C-x 4 b" . consult-buffer-other-window) | ||
598 | ("C-x 5 b" . consult-buffer-other-frame) | ||
599 | ;; Custom M-# bindings for fast register access | ||
600 | ("M-#" . consult-register-load) | ||
601 | ("M-'" . consult-register-store) | ||
602 | ("C-M-#" . consult-register) | ||
603 | ;; Other custom bindings | ||
604 | ("M-y" . consult-yank-pop) | ||
605 | ("<help> a" . consult-apropos) | ||
606 | ;; M-g bindings (goto-map) | ||
607 | ("M-g e" . consult-compile-error) | ||
608 | ("M-g g" . consult-goto-line) | ||
609 | ("M-g M-g" . consult-goto-line) | ||
610 | ("M-g o" . consult-outline) | ||
611 | ("M-g m" . consult-mark) | ||
612 | ("M-g k" . consult-global-mark) | ||
613 | ("M-g i" . consult-imenu) | ||
614 | ("M-g I" . consult-project-imenu) | ||
615 | ;; M-s bindings (search-map) | ||
616 | ("M-s f" . consult-find) | ||
617 | ("M-s L" . consult-locate) | ||
618 | ("M-s g" . consult-grep) | ||
619 | ("M-s G" . consult-git-grep) | ||
620 | ("M-s r" . consult-ripgrep) | ||
621 | ("M-s l" . consult-line) | ||
622 | ("M-s m" . consult-multi-occur) | ||
623 | ("M-s k" . consult-keep-lines) | ||
624 | ("M-s u" . consult-focus-lines) | ||
625 | ;; Isearch integration | ||
626 | ("M-s e" . consult-isearch) | ||
627 | :map isearch-mode-map | ||
628 | ("M-e" . consult-isearch) | ||
629 | ("M-s e" . consult-isearch) | ||
630 | ("M-s l" . consult-line)) | ||
631 | :init | ||
632 | (setq register-preview-delay 0 | ||
633 | register-preview-function #'consult-register-format) | ||
634 | (advice-add #'register-preview :override #'consult-register-window) | ||
635 | (setq xref-show-xrefs-function #'consult-xref | ||
636 | xref-show-definitions-function #'consult-xref) | ||
637 | :config | ||
638 | ;; (setq consult-preview-key 'any) | ||
639 | ;; (setq consult-preview-key (kbd "M-p")) | ||
640 | (setq consult-narrow-key "<")) | ||
641 | |||
642 | ;; Language: Emacs-Lisp | ||
643 | (setq-default eval-expression-print-length nil ; don't limit print length | ||
644 | eval-expression-print-level nil | ||
645 | ) | ||
646 | ;; indent like common lisp | ||
647 | (require 'cl-lib) | ||
648 | (setq-default lisp-indent-function #'common-lisp-indent-function) | ||
649 | (put 'cl-flet 'common-lisp-indent-function | ||
650 | (get 'flet 'common-lisp-indent-function)) | ||
651 | (put 'cl-labels 'common-lisp-indent-function | ||
652 | (get 'labels 'common-lisp-indent-function)) | ||
653 | (put 'if 'common-lisp-indent-function 2) | ||
654 | (put 'dotimes-protect 'common-lisp-indent-function | ||
655 | (get 'when 'common-lisp-indent-function)) | ||
656 | ;; eldoc mode | ||
657 | (use-package eldoc | ||
658 | :straight nil | ||
659 | :init (setq-default eldoc-echo-area-display-truncation-message nil | ||
660 | eldoc-idle-delay 0)) | ||
661 | |||
662 | ;; Dired | ||
663 | (use-package dired | ||
664 | :straight nil | ||
665 | :init | ||
666 | (setq-default dired-recursive-copies 'always | ||
667 | dired-recursive-deletes 'always | ||
668 | delete-by-moving-to-trash t | ||
669 | dired-listing-switches "-AFgho --group-directories-first" | ||
670 | dired-auto-revert-buffer t | ||
671 | dired-dwim-target t) | ||
672 | :bind ("C-x C-j" . dired-jump)) | ||
673 | (defun hook--dired-mode () | ||
674 | (hl-line-mode +1) | ||
675 | (dired-hide-details-mode +1)) | ||
676 | (add-hook 'dired-mode-hook #'hook--dired-mode) | ||
677 | |||
678 | (use-package dired-subtree | ||
679 | :bind (:map dired-mode-map | ||
680 | ("i" . dired-subtree-toggle))) | ||
681 | |||
682 | (use-package dired-collapse | ||
683 | :hook dired-mode) | ||
684 | |||
685 | (use-package 0x0 | ||
686 | :straight (:repo "https://git.sr.ht/~zge/nullpointer-emacs") | ||
687 | :init (setq 0x0-default-service 'ttm) | ||
688 | :commands (0x0-upload | ||
689 | 0x0-upload-file | ||
690 | 0x0-upload-string | ||
691 | 0x0-upload-kill-ring | ||
692 | 0x0-popup)) | ||
693 | |||
694 | (use-package elpher | ||
695 | :straight (:repo "git://thelambdalab.xyz/elpher.git") | ||
696 | :commands (elpher elpher-bookmarks) | ||
697 | :init (setq elpher-ipv4-always t | ||
698 | elpher-certificate-directory | ||
699 | (expand-file-name "elpher-certificates/" | ||
700 | acdw/var-dir)) | ||
701 | (add-hook 'elpher-mode-hook #'acdw/reading-mode) | ||
702 | :custom-face | ||
703 | (elpher-gemini-heading1 | ||
704 | ((t (:inherit (modus-theme-heading-1) | ||
705 | :height 1.0)))) | ||
706 | (elpher-gemini-heading2 | ||
707 | ((t (:inherit (modus-theme-heading-2) | ||
708 | :height 1.0)))) | ||
709 | (elpher-gemini-heading3 | ||
710 | ((t (:inherit (modus-theme-heading-3) | ||
711 | :height 1.0)))) | ||
712 | :bind (:map elpher-mode-map | ||
713 | ("n" . elpher-next-link) | ||
714 | ("p" . elpher-prev-link) | ||
715 | ("o" . elpher-follow-current-link) | ||
716 | ("G" . elpher-go-current))) | ||
717 | |||
718 | (use-package gemini-write | ||
719 | :straight (:repo | ||
720 | "https://alexschroeder.ch/cgit/gemini-write" | ||
721 | :fork (:repo "https://tildegit.org/acdw/gemini-write" | ||
722 | :branch "main")) | ||
723 | :after elpher) | ||
724 | |||
725 | (use-package gemini-mode | ||
726 | :straight (:repo "https://git.carcosa.net/jmcbray/gemini.el.git") | ||
727 | :mode "\\.\\(gemini\\|gmi\\)\\'" | ||
728 | :custom-face | ||
729 | (gemini-heading-face-1 | ||
730 | ((t (:inherit (elpher-gemini-heading1))))) | ||
731 | (gemini-heading-face2 | ||
732 | ((t (:inherit (elpher-gemini-heading2))))) | ||
733 | (gemini-heading-face3 | ||
734 | ((t (:inherit (elpher-gemini-heading3)))))) | ||
735 | |||
736 | (use-package nov | ||
737 | :init (setq nov-text-width fill-column) | ||
738 | (add-hook 'nov-mode-hook #'acdw/reading-mode) | ||
739 | :mode ("\\.epub\\'" . nov-mode)) | ||
740 | |||
741 | (use-package undo-fu | ||
742 | :bind (("C-/" . undo-fu-only-undo) | ||
743 | ("C-?" . undo-fu-only-redo))) | ||
744 | (use-package undo-fu-session | ||
745 | :init (setq undo-fu-session-directory (expand-file-name "undo/" | ||
746 | acdw/var-dir) | ||
747 | undo-fu-session-incompatible-files | ||
748 | '("/COMMIT_EDITMSG\\'" "/git-rebase-todo\\'")) | ||
749 | :config (global-undo-fu-session-mode)) | ||
750 | |||
751 | (setq-default set-mark-repeat-command-pop t) ; repeat mark pops w/o prefix | ||
752 | |||
753 | (use-package expand-region | ||
754 | :bind ("C-=" . er/expand-region)) | ||
755 | |||
756 | (use-package goggles | ||
757 | :hook ((text-mode prog-mode) . goggles-mode)) | ||
758 | |||
759 | (use-package isearch | ||
760 | :straight nil) | ||
761 | |||
762 | (use-package anzu | ||
763 | :init (setq search-default-mode t) | ||
764 | :bind (([remap query-replace] . anzu-query-replace-regexp) | ||
765 | ([remap query-replace-regexp] . anzu-query-replace) | ||
766 | :map isearch-mode-map | ||
767 | ([remap isearch-query-replace] . anzu-isearch-query-replace) | ||
768 | ([remap isearch-query-replace-regexp] . | ||
769 | anzu-isearch-query-replace-regexp))) | ||
770 | |||
771 | (use-package iscroll | ||
772 | :hook (text-mode . iscroll-mode)) | ||
773 | |||
774 | (use-package ibuffer | ||
775 | :straight nil | ||
776 | :bind ([remap list-buffers] . #'ibuffer) | ||
777 | ;; from http://martinowen.net/blog/2010/02/03/tips-for-emacs-ibuffer.html | ||
778 | :init | ||
779 | (setq ibuffer-saved-filter-groups ; this could still be improved | ||
780 | '(("home" | ||
781 | ("emacs-config" (or (filename . ".emacs.d") | ||
782 | (filename . "etc/emacs"))) | ||
783 | ("Org" (or (mode . org-mode) | ||
784 | (filename . "OrgMode"))) | ||
785 | ("Dired" (mode . dired-mode)) | ||
786 | ("Magit" (name . "magit")) | ||
787 | ("Help" (or (name . "\*Help\*") | ||
788 | (name . "\*Apropos\*") | ||
789 | (name . "\*info\*"))))) | ||
790 | ibuffer-expert t ; don't ask if i wanna kill unmodifieds | ||
791 | ibuffer-show-empty-filter-groups nil | ||
792 | ) | ||
793 | (defun hook--ibuffer-setup () | ||
794 | (ibuffer-auto-mode +1) | ||
795 | (ibuffer-switch-to-saved-filter-groups "home")) | ||
796 | (add-hook 'ibuffer-mode-hook #'hook--ibuffer-setup)) | ||
797 | |||
798 | (setq-default | ||
799 | browse-url-browser-function 'browse-url-firefox | ||
800 | browse-url-new-window-flag t | ||
801 | browse-url-firefox-new-window-is-tab t | ||
802 | shr-max-width fill-column | ||
803 | shr-width fill-column) | ||
804 | |||
805 | (when (eq acdw/system :work) | ||
806 | (add-to-list 'exec-path "C:/Program Files/Mozilla Firefox")) | ||
807 | |||
808 | (bind-key [remap just-one-space] #'cycle-spacing) | ||
809 | |||
810 | ;; Org mode | ||
811 | ;; org-return-dwim (unpacakged) | ||
812 | (defun unpackaged/org-element-descendant-of (type element) | ||
813 | "Return non-nil if ELEMENT is a descendant of TYPE. | ||
814 | TYPE should be an element type, like `item' or `paragraph'. | ||
815 | ELEMENT should be a list like that returned by `org-element-context'." | ||
816 | ;; MAYBE: Use `org-element-lineage'. | ||
817 | (when-let* ((parent (org-element-property :parent element))) | ||
818 | (or (eq type (car parent)) | ||
819 | (unpackaged/org-element-descendant-of type parent)))) | ||
820 | |||
821 | (defun unpackaged/org-return-dwim (&optional default) | ||
822 | "A helpful replacement for `org-return'. With prefix, call `org-return'. | ||
823 | |||
824 | On headings, move point to position after entry content. In | ||
825 | lists, insert a new item or end the list, with checkbox if | ||
826 | appropriate. In tables, insert a new row or end the table." | ||
827 | ;; Inspired by John Kitchin: | ||
828 | ;; http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ | ||
829 | (interactive "P") | ||
830 | (if default | ||
831 | (org-return) | ||
832 | (cond | ||
833 | ;; Act depending on context around point. | ||
834 | |||
835 | ;; NOTE: I prefer RET to not follow links, but by uncommenting | ||
836 | ;; this block, links will be followed. | ||
837 | ;; FURTHER NOTE: Ideally, I would follow links unless point | ||
838 | ;; /appeared/ to be at the end of the line (even if it's still | ||
839 | ;; inside the link) -- when it would do `org-return'. That | ||
840 | ;; would take some /doing/, however. | ||
841 | |||
842 | ;; ((eq 'link (car (org-element-context))) | ||
843 | ;; ;; Link: Open it. | ||
844 | ;; (org-open-at-point-global)) | ||
845 | |||
846 | ((org-at-heading-p) | ||
847 | ;; Heading: Move to position after entry content. | ||
848 | ;; NOTE: This is probably the most interesting feature of this function. | ||
849 | (let ((heading-start (org-entry-beginning-position))) | ||
850 | (goto-char (org-entry-end-position)) | ||
851 | (cond ((and (org-at-heading-p) | ||
852 | (= heading-start (org-entry-beginning-position))) | ||
853 | ;; Entry ends on its heading; add newline after | ||
854 | (end-of-line) | ||
855 | (insert "\n\n")) | ||
856 | (t | ||
857 | ;; Entry ends after its heading; back up | ||
858 | (forward-line -1) | ||
859 | (end-of-line) | ||
860 | (when (org-at-heading-p) | ||
861 | ;; At the same heading | ||
862 | (forward-line) | ||
863 | (insert "\n") | ||
864 | (forward-line -1)) | ||
865 | ;; FIXME: looking-back is supposed to be called with | ||
866 | ;; more arguments. | ||
867 | (while (not (looking-back (rx | ||
868 | (repeat 3 | ||
869 | (seq (optional blank) | ||
870 | "\n"))) | ||
871 | nil)) | ||
872 | (insert "\n")) | ||
873 | (forward-line -1))))) | ||
874 | |||
875 | ((org-at-item-checkbox-p) | ||
876 | ;; Checkbox: Insert new item with checkbox. | ||
877 | (org-insert-todo-heading nil)) | ||
878 | |||
879 | ((org-in-item-p) | ||
880 | ;; Plain list. Yes, this gets a little complicated... | ||
881 | (let ((context (org-element-context))) | ||
882 | (if (or (eq 'plain-list (car context)) ; First item in list | ||
883 | (and (eq 'item (car context)) | ||
884 | (not (eq (org-element-property :contents-begin context) | ||
885 | (org-element-property :contents-end context)))) | ||
886 | ;; Element in list item, e.g. a link | ||
887 | (unpackaged/org-element-descendant-of 'item context)) | ||
888 | ;; Non-empty item: Add new item. | ||
889 | (org-insert-item) | ||
890 | ;; Empty item: Close the list. | ||
891 | ;; TODO: Do this with org functions rather than operating | ||
892 | ;; on the text. Can't seem to find the right function. | ||
893 | (delete-region (line-beginning-position) (line-end-position)) | ||
894 | (insert "\n")))) | ||
895 | |||
896 | ((when (fboundp 'org-inlinetask-in-task-p) | ||
897 | (org-inlinetask-in-task-p)) | ||
898 | ;; Inline task: Don't insert a new heading. | ||
899 | (org-return)) | ||
900 | |||
901 | ((org-at-table-p) | ||
902 | (cond ((save-excursion | ||
903 | (beginning-of-line) | ||
904 | ;; See `org-table-next-field'. | ||
905 | (cl-loop with end = (line-end-position) | ||
906 | for cell = (org-element-table-cell-parser) | ||
907 | always (equal (org-element-property :contents-begin cell) | ||
908 | (org-element-property :contents-end cell)) | ||
909 | while (re-search-forward "|" end t))) | ||
910 | ;; Empty row: end the table. | ||
911 | (delete-region (line-beginning-position) (line-end-position)) | ||
912 | (org-return)) | ||
913 | (t | ||
914 | ;; Non-empty row: call `org-return'. | ||
915 | (org-return)))) | ||
916 | (t | ||
917 | ;; All other cases: call `org-return'. | ||
918 | (org-return))))) | ||
919 | |||
920 | ;; org-fix-blank-lines (unpackaged) | ||
921 | (defun unpackaged/org-fix-blank-lines (&optional prefix) | ||
922 | "Ensure that blank lines exist between headings and between headings and their contents. | ||
923 | With prefix, operate on whole buffer. Ensures that blank lines | ||
924 | exist after each headings's drawers." | ||
925 | (interactive "P") | ||
926 | (org-map-entries (lambda () | ||
927 | (org-with-wide-buffer | ||
928 | ;; `org-map-entries' narrows the buffer, which prevents us | ||
929 | ;; from seeing newlines before the current heading, so we | ||
930 | ;; do this part widened. | ||
931 | (while (not (looking-back "\n\n" nil)) | ||
932 | ;; Insert blank lines before heading. | ||
933 | (insert "\n"))) | ||
934 | (let ((end (org-entry-end-position))) | ||
935 | ;; Insert blank lines before entry content | ||
936 | (forward-line) | ||
937 | (while (and (org-at-planning-p) | ||
938 | (< (point) (point-max))) | ||
939 | ;; Skip planning lines | ||
940 | (forward-line)) | ||
941 | (while (re-search-forward org-drawer-regexp end t) | ||
942 | ;; Skip drawers. You might think that `org-at-drawer-p' | ||
943 | ;; would suffice, but for some reason it doesn't work | ||
944 | ;; correctly when operating on hidden text. This | ||
945 | ;; works, taken from `org-agenda-get-some-entry-text'. | ||
946 | (re-search-forward "^[ \t]*:END:.*\n?" end t) | ||
947 | (goto-char (match-end 0))) | ||
948 | (unless (or (= (point) (point-max)) | ||
949 | (org-at-heading-p) | ||
950 | (looking-at-p "\n")) | ||
951 | (insert "\n")))) | ||
952 | t (if prefix | ||
953 | nil | ||
954 | 'tree))) | ||
955 | |||
956 | (defun hook--org-mode-fix-blank-lines () | ||
957 | (when (eq major-mode 'org-mode) | ||
958 | (let ((current-prefix-arg 4)) ; Emulate C-u | ||
959 | (call-interactively 'unpackaged/org-fix-blank-lines)))) | ||
960 | (add-hook 'before-save-hook #'hook--org-mode-fix-blank-lines) | ||
961 | |||
962 | (use-package org | ||
963 | :straight (:repo "https://code.orgmode.org/bzg/org-mode.git") | ||
964 | :init | ||
965 | (setq-default | ||
966 | org-directory "~/org" ; where to search for org files | ||
967 | ;; typesetting | ||
968 | org-hide-emphasis-markers t | ||
969 | org-fontify-whole-heading-line t | ||
970 | org-fontify-done-headline t | ||
971 | org-fontify-quote-and-verse-blocks t | ||
972 | org-src-fontify-natively t | ||
973 | org-ellipsis " ≡" | ||
974 | org-pretty-entities t | ||
975 | org-tags-column (- 0 fill-column (- (length org-ellipsis))) | ||
976 | ;; Source blocks | ||
977 | org-src-tab-acts-natively t | ||
978 | org-src-window-setup 'current-window | ||
979 | org-confirm-babel-evaluate nil | ||
980 | ;; Behavior | ||
981 | org-adapt-indentation t ; indent text after a header | ||
982 | org-catch-invisible-edits 'smart | ||
983 | org-special-ctrl-a/e t | ||
984 | org-special-ctrl-k t | ||
985 | org-imenu-depth 8 ; catch all headings | ||
986 | ;; Exporting | ||
987 | org-export-headline-levels 8 ; export all headings | ||
988 | org-export-with-smart-quotes t | ||
989 | org-export-with-sub-superscripts t | ||
990 | ;; Modules | ||
991 | org-modules '(;; default (commented if unused) | ||
992 | ;; bbdb | ||
993 | ;; bibtex | ||
994 | ;; docview | ||
995 | eww | ||
996 | ;; gnus | ||
997 | info | ||
998 | ;; irc | ||
999 | ;; mhe | ||
1000 | ;; rmail | ||
1001 | ;; w3m | ||
1002 | ;; extra stuff for me | ||
1003 | ;; habit ; track your consistency with habits | ||
1004 | ;; inlinetask ; tasks independent of outline hierarchy | ||
1005 | mouse ; additional mouse support | ||
1006 | ;; protocol ; intercept calls from emacsclient | ||
1007 | ;; man | ||
1008 | tempo ; templates | ||
1009 | ) | ||
1010 | org-export-backends '(;; defaults | ||
1011 | ascii | ||
1012 | html | ||
1013 | latex | ||
1014 | odt | ||
1015 | ;; added by me | ||
1016 | man | ||
1017 | md | ||
1018 | ) | ||
1019 | ) | ||
1020 | :config | ||
1021 | (require 'org-tempo) | ||
1022 | (require 'ox-md) | ||
1023 | :bind (:map org-mode-map | ||
1024 | ("RET" . unpackaged/org-return-dwim))) | ||
1025 | |||
1026 | (use-package goto-addr | ||
1027 | :straight nil | ||
1028 | :config | ||
1029 | (goto-address-mode +1)) | ||
1030 | 25 | ||
1031 | (use-package web-mode | 26 | (autoload 'ehelp-command "ehelp") |
1032 | :mode (("\\.phtml\\'" . web-mode) | 27 | (define-key acdw/map (kbd "C-h") #'ehelp-command) |
1033 | ("\\.tpl\\.php\\'" . web-mode) | ||
1034 | ("\\.[agj]sp\\'" . web-mode) | ||
1035 | ("\\as[cp]x\\'" . web-mode) | ||
1036 | ("\\.erb\\'" . web-mode) | ||
1037 | ("\\.mustache\\'" . web-mode) | ||
1038 | ("\\.djhtml\\'" . web-mode) | ||
1039 | ("\\.html?\\'" . web-mode))) | ||
diff --git a/lisp/acdw.el b/lisp/acdw.el new file mode 100644 index 0000000..03e4a62 --- /dev/null +++ b/lisp/acdw.el | |||
@@ -0,0 +1,83 @@ | |||
1 | :;;; acdw.el -*- lexical-binding: t; coding: utf-8-unix -*- | ||
2 | ;; | ||
3 | ;; Author: Case Duckworth <acdw@acdw.net> | ||
4 | ;; Created: Sometime during Covid-19, 2020 | ||
5 | ;; Keywords: configuration | ||
6 | ;; URL: https://tildegit.org/acdw/emacs | ||
7 | ;; Bankruptcy: 5b | ||
8 | ;; | ||
9 | ;; This file is NOT part of GNU Emacs. | ||
10 | ;; | ||
11 | ;;; License: | ||
12 | ;; | ||
13 | ;; Everyone is permitted to do whatever with this software, without | ||
14 | ;; limitation. This software comes without any warranty whatsoever, | ||
15 | ;; but with two pieces of advice: | ||
16 | ;; - Don't hurt yourself. | ||
17 | ;; - Make good choices. | ||
18 | ;; | ||
19 | ;;; Commentary: | ||
20 | ;; `acdw.el' contains `acdw/map', its mode, and assorted ease-of-life | ||
21 | ;; functions for me, acdw. | ||
22 | ;; | ||
23 | ;;; Code: | ||
24 | |||
25 | ;;; Directories (think `no-littering') | ||
26 | |||
27 | (defvar acdw/dir (expand-file-name | ||
28 | (convert-standard-filename "var/") | ||
29 | user-emacs-directory) | ||
30 | "A directory to hold extra configuration and emacs data.") | ||
31 | |||
32 | (defun acdw/in-dir (file &optional make-directory) | ||
33 | "Expand FILE relative to `acdw/dir', optionally creating its | ||
34 | directory." | ||
35 | (let ((f (expand-file-name (convert-standard-filename file) | ||
36 | acdw/dir))) | ||
37 | (when make-directory | ||
38 | (make-directory (file-name-directory) 'parents)) | ||
39 | f)) | ||
40 | |||
41 | ;;; Settings | ||
42 | |||
43 | (defun acdw/set (assignments) | ||
44 | "Perform `customize-set-variable' on each of ASSIGNMENTS. | ||
45 | |||
46 | ASSIGNMENTS is a list where each element is of the form | ||
47 | (VARIABLE VALUE [COMMENT])." | ||
48 | (dolist (assn assignments) | ||
49 | (customize-set-variable (car assignment) | ||
50 | (cadr assignment) | ||
51 | (if (and (caddr assignment) | ||
52 | (stringp (caddr assignment))) | ||
53 | (caddr assignment) | ||
54 | "Customized by `acdw/set'.")))) | ||
55 | |||
56 | ;;; Keymap & Mode | ||
57 | |||
58 | (defvar acdw/map (make-sparse-keymap) | ||
59 | "A keymap for my custom bindings.") | ||
60 | |||
61 | (define-minor-mode acdw/mode | ||
62 | "A mode for `acdw/map'." | ||
63 | :init-value t | ||
64 | :lighter " acdw" | ||
65 | :keymap acdw/map) | ||
66 | (define-globalized-minor-mode acdw/global-mode acdw/mode acdw/mode) | ||
67 | |||
68 | ;; Disable `acdw/mode' in the minibuffer | ||
69 | (defun acdw/mode--disable () | ||
70 | "Disable `acdw/mode'." | ||
71 | (acdw/mode -1)) | ||
72 | (add-hook 'minibuffer-setup-hook #'acdw/mode--disable) | ||
73 | |||
74 | ;; Set up a leader key for `acdw/mode' | ||
75 | (defvar acdw/leader | ||
76 | (let ((map (make-sparse-keymap)) | ||
77 | (c-z (global-key-binding "\C-z"))) | ||
78 | (define-key acdw/map "\C-z" map) | ||
79 | (define-key map "\C-z" c-z) | ||
80 | map)) | ||
81 | |||
82 | (provide 'acdw) | ||
83 | ;;; acdw.el ends here | ||