diff options
Diffstat (limited to 'emacs.d/early-init.el')
-rw-r--r-- | emacs.d/early-init.el | 282 |
1 files changed, 243 insertions, 39 deletions
diff --git a/emacs.d/early-init.el b/emacs.d/early-init.el index 7374bd1..b2de2f2 100644 --- a/emacs.d/early-init.el +++ b/emacs.d/early-init.el | |||
@@ -10,18 +10,21 @@ | |||
10 | (vertical-scroll-bars) | 10 | (vertical-scroll-bars) |
11 | (horizontal-scroll-bars))) | 11 | (horizontal-scroll-bars))) |
12 | 12 | ||
13 | (when (getenv "IN_EXWM") | ||
14 | (add-to-list 'default-frame-alist '(fullscreen . fullboth))) | ||
15 | |||
13 | (defvar *fonts* | 16 | (defvar *fonts* |
14 | '((default | 17 | (let ((fixed "Recursive Mono Casual Static") |
15 | :family ;;("Recursive Mono Casual Static" "DejaVu Sans Mono") | 18 | (variable "Recursive Sans Casual Static")) |
16 | ("Public Sans" "DejaVu Sans") | 19 | `((default |
17 | :height 100) | 20 | :family ,variable |
18 | (variable-pitch | 21 | :height 100) |
19 | :family ("Public Sans" "DejaVu Sans") | 22 | (variable-pitch |
20 | :height 1.0) | 23 | :family ,variable) |
21 | (fixed-pitch | 24 | (fixed-pitch |
22 | :family ("Recursive Mono Casual Static" "DejaVu Sans Mono")) | 25 | :family ,fixed) |
23 | (fixed-pitch-serif | 26 | (fixed-pitch-serif |
24 | :family ("Recursive Mono Linear Static" "DejaVu Sans Mono")))) | 27 | :family "Recursive Mono Linear Static")))) |
25 | 28 | ||
26 | (require 'package) | 29 | (require 'package) |
27 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) | 30 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")) |
@@ -47,24 +50,6 @@ | |||
47 | (delete-trailing-whitespace (line-end-position) | 50 | (delete-trailing-whitespace (line-end-position) |
48 | (point-max)))) | 51 | (point-max)))) |
49 | 52 | ||
50 | (defun run-after-frame-init (func) | ||
51 | "Run FUNC after the first frame is initialized. | ||
52 | If already so, run FUNC immediately." | ||
53 | (cond | ||
54 | ((daemonp) | ||
55 | (add-hook 'server-after-make-frame-hook func) | ||
56 | (advice-add func :after (lambda () | ||
57 | (remove-hook 'server-after-make-frame-hook | ||
58 | func) | ||
59 | (advice-remove func | ||
60 | 'after-frame-init-removing-advice)) | ||
61 | |||
62 | |||
63 | '((name . after-frame-init-removing-advice)))) | ||
64 | ((not after-init-time) | ||
65 | (add-hook 'after-init-hook func)) | ||
66 | (:else (funcall func)))) | ||
67 | |||
68 | (defun first-found-font (&rest cands) | 53 | (defun first-found-font (&rest cands) |
69 | "Return the first font of CANDS that is installed, or nil." | 54 | "Return the first font of CANDS that is installed, or nil." |
70 | (cl-loop with ffl = (font-family-list) | 55 | (cl-loop with ffl = (font-family-list) |
@@ -77,8 +62,7 @@ If already so, run FUNC immediately." | |||
77 | ;; Default faces | 62 | ;; Default faces |
78 | (cl-loop for (face . spec) in *fonts* | 63 | (cl-loop for (face . spec) in *fonts* |
79 | do (set-face-attribute face nil | 64 | do (set-face-attribute face nil |
80 | :family (apply #'first-found-font | 65 | :family (plist-get spec :family) |
81 | (plist-get spec :family)) | ||
82 | :height (or (plist-get spec :height) | 66 | :height (or (plist-get spec :height) |
83 | 'unspecified))) | 67 | 'unspecified))) |
84 | ;; Specialized fonts | 68 | ;; Specialized fonts |
@@ -171,20 +155,40 @@ With ARG, edit in the other window." file-name) | |||
171 | (funcall (if arg #'find-file-other-window #'find-file) | 155 | (funcall (if arg #'find-file-other-window #'find-file) |
172 | ,file-name)))) | 156 | ,file-name)))) |
173 | 157 | ||
174 | (defun indent-buffer+ () | 158 | (defun fixup-whitespace () |
175 | "Indent the current buffer and (un)`tabify'. | 159 | "Indent the current buffer and (un)`tabify'. |
176 | Whether it tabifies or untabifies depends on `space-indent-modes'." | 160 | Whether it tabifies or untabifies depends on `space-indent-modes'." |
177 | (interactive) | 161 | (interactive) |
178 | (save-mark-and-excursion | 162 | (save-mark-and-excursion |
179 | (indent-region (point-min) (point-max)) | 163 | (indent-region (point-min) (point-max)) |
180 | (if (apply #'derived-mode-p space-indent-modes) | 164 | (if indent-tabs-mode |
181 | (untabify (point-min) (point-max)) | 165 | (tabify (point-min) (point-max)) |
182 | (tabify (point-min) (point-max))))) | 166 | (untabify (point-min) (point-max))) |
167 | (replace-regexp-in-region " $" "" (point-min) (point-max)))) | ||
183 | 168 | ||
184 | (defun package-ensure (pkg) | 169 | (defun package-ensure (pkgspec &optional require) |
185 | "Install PKG if it's not already installed." | 170 | "Install PKG if it's not already installed. |
186 | (unless (package-installed-p pkg) | 171 | REQUIRE means require it after ensuring it's installed." |
187 | (package-vc-install pkg))) | 172 | (let ((pkg (if (listp pkgspec) (car pkgspec) pkgspec))) |
173 | (unless (package-installed-p pkg) | ||
174 | (if (symbolp pkgspec) | ||
175 | (or (ignore-errors | ||
176 | (package-install pkg) | ||
177 | t) | ||
178 | (ignore-errors | ||
179 | (message "Package `%s' not found, refreshing packages" pkg) | ||
180 | (package-refresh-contents) | ||
181 | (package-install pkg) | ||
182 | t) | ||
183 | (ignore-errors | ||
184 | (message "Package `%s' still not found, trying `%s'" | ||
185 | pkg 'pkg-vc-install) | ||
186 | (package-vc-install pkgspec) | ||
187 | t) | ||
188 | (if no-error nil | ||
189 | (error "Can't find package: %s" pkg)))) | ||
190 | (package-vc-install pkgspec)) | ||
191 | (when require (require pkg)))) | ||
188 | 192 | ||
189 | (defun minibuffer-delete-directory () | 193 | (defun minibuffer-delete-directory () |
190 | "Delete the last directory in a file-completing minibuffer." | 194 | "Delete the last directory in a file-completing minibuffer." |
@@ -214,3 +218,203 @@ If ARG is 16, kill emacs without asking about processes." | |||
214 | 218 | ||
215 | (defun regexp-concat (&rest regexps) | 219 | (defun regexp-concat (&rest regexps) |
216 | (string-join regexps "\\|")) | 220 | (string-join regexps "\\|")) |
221 | |||
222 | ;; There is a bug in M-x finger | ||
223 | (defun acdw/finger (user host) | ||
224 | "Finger USER on HOST. | ||
225 | This command uses `finger-X.500-host-regexps' | ||
226 | and `network-connection-service-alist', which see." | ||
227 | ;; One of those great interactive statements that's actually | ||
228 | ;; longer than the function call! The idea is that if the user | ||
229 | ;; uses a string like "pbreton@cs.umb.edu", we won't ask for the | ||
230 | ;; host name. If we don't see an "@", we'll prompt for the host. | ||
231 | (interactive | ||
232 | (let* ((answer (let ((default (ffap-url-at-point))) | ||
233 | (read-string (format-prompt "Finger User" default) nil nil default))) | ||
234 | (index (string-match (regexp-quote "@") answer))) | ||
235 | (if index | ||
236 | (list (substring answer 0 index) | ||
237 | (substring answer (1+ index))) | ||
238 | (list answer | ||
239 | (let ((default (ffap-machine-at-point))) | ||
240 | (read-string (format-prompt "At Host" default) nil nil default)))))) | ||
241 | (let* ((user-and-host (concat user "@" host)) | ||
242 | (process-name (concat "Finger [" user-and-host "]")) | ||
243 | (regexps finger-X.500-host-regexps) | ||
244 | ) ;; found | ||
245 | (and regexps | ||
246 | (while (not (string-match (car regexps) host)) | ||
247 | (setq regexps (cdr regexps)))) | ||
248 | (when regexps | ||
249 | (setq user-and-host user)) | ||
250 | (run-network-program | ||
251 | process-name | ||
252 | host | ||
253 | (cdr (assoc 'finger network-connection-service-alist)) | ||
254 | user-and-host))) | ||
255 | |||
256 | (advice-add 'finger :override #'acdw-finger) | ||
257 | |||
258 | (defun hide-minor-mode (mode &optional hook) | ||
259 | "Hide MODE from the mode-line. | ||
260 | HOOK is used to trigger the action, and defaults to MODE-hook." | ||
261 | (setf (alist-get mode minor-mode-alist) (list "")) | ||
262 | (add-hook (intern (or hook (format "%s-hook" mode))) | ||
263 | (lambda () (hide-minor-mode mode)))) | ||
264 | |||
265 | (defun switch-to-other-buffer () | ||
266 | "Switch to the `other-buffer'." | ||
267 | (interactive) | ||
268 | (switch-to-buffer nil)) | ||
269 | |||
270 | (defun popup-eshell (arg) | ||
271 | "Popup an eshell buffer in the current window." | ||
272 | (interactive "P") | ||
273 | (let ((dd default-directory)) | ||
274 | (eshell arg) | ||
275 | (unless (equal dd default-directory) | ||
276 | (setq default-directory dd) | ||
277 | ;; Is this a good idea, really? | ||
278 | (eshell-bol) | ||
279 | (unless (eolp) | ||
280 | (insert "# ")) | ||
281 | (eshell-send-input)))) | ||
282 | |||
283 | (defun vc-jump (arg) | ||
284 | "Jump to the current project's VC buffer. | ||
285 | With ARG, prompt for the directory." | ||
286 | (interactive "P") | ||
287 | (if arg | ||
288 | (let ((current-prefix-arg nil)) | ||
289 | (call-interactively #'vc-dir)) | ||
290 | (project-vc-dir))) | ||
291 | |||
292 | (defun custom-show-all-widgets () | ||
293 | "toggle all \"More/Hide\" widgets in the current buffer." | ||
294 | ;; From unpackaged | ||
295 | (interactive) | ||
296 | (widget-map-buttons (lambda (widget _) | ||
297 | (pcase (widget-get widget :off) | ||
298 | ("More" (widget-apply-action widget))) | ||
299 | nil))) | ||
300 | |||
301 | (defun quit-minibuffer () | ||
302 | (interactive) | ||
303 | (switch-to-minibuffer) | ||
304 | (minibuffer-keyboard-quit)) | ||
305 | |||
306 | (defun keyboard-quit* (arg) | ||
307 | (interactive "P") | ||
308 | (if arg | ||
309 | (quit-minibuffer) | ||
310 | (keyboard-quit))) | ||
311 | |||
312 | (defun sort-sexps (beg end) | ||
313 | "Sort sexps in region. | ||
314 | Comments stay with the code below." | ||
315 | ;; From unpackaged | ||
316 | (interactive "r") | ||
317 | (cl-flet ((skip-whitespace () (while (looking-at (rx (1+ (or space "\n")))) | ||
318 | (goto-char (match-end 0)))) | ||
319 | (skip-both () (while (cond ((or (nth 4 (syntax-ppss)) | ||
320 | (ignore-errors | ||
321 | (save-excursion | ||
322 | (forward-char 1) | ||
323 | (nth 4 (syntax-ppss))))) | ||
324 | (forward-line 1)) | ||
325 | ((looking-at (rx (1+ (or space "\n")))) | ||
326 | (goto-char (match-end 0))))))) | ||
327 | (save-excursion | ||
328 | (save-restriction | ||
329 | (narrow-to-region beg end) | ||
330 | (goto-char beg) | ||
331 | (skip-both) | ||
332 | (cl-destructuring-bind (sexps markers) | ||
333 | (cl-loop do (skip-whitespace) | ||
334 | for start = (point-marker) | ||
335 | for sexp = (ignore-errors | ||
336 | (read (current-buffer))) | ||
337 | for end = (point-marker) | ||
338 | while sexp | ||
339 | ;; Collect the real string, then one used for sorting. | ||
340 | collect (cons (buffer-substring (marker-position start) | ||
341 | (marker-position end)) | ||
342 | (save-excursion | ||
343 | (goto-char (marker-position start)) | ||
344 | (skip-both) | ||
345 | (buffer-substring (point) | ||
346 | (marker-position end)))) | ||
347 | into sexps | ||
348 | collect (cons start end) | ||
349 | into markers | ||
350 | finally return (list sexps markers)) | ||
351 | (setq sexps (sort sexps (lambda (a b) | ||
352 | (string< (cdr a) (cdr b))))) | ||
353 | (cl-loop for (real . sort) in sexps | ||
354 | for (start . end) in markers | ||
355 | do (progn | ||
356 | (goto-char (marker-position start)) | ||
357 | (insert-before-markers real) | ||
358 | (delete-region (point) (marker-position end))))))))) | ||
359 | |||
360 | (defun ^turn-off (mode) | ||
361 | "Higher-order function: returns a lambda to turn off MODE." | ||
362 | (lambda () | ||
363 | (funcall mode -1))) | ||
364 | |||
365 | (defun ^local-hook (hook fn) | ||
366 | "Hook FN to HOOK locally in a lambda. | ||
367 | Good for adding to an add-hook." | ||
368 | (lambda () (add-hook hook fn t))) | ||
369 | |||
370 | (defun ^local-unhook (hook fn) | ||
371 | "Remove FN from HOOK locally." | ||
372 | (lambda () (remove-hook hook fn t))) | ||
373 | |||
374 | ;; This needs to be a macro to take advantage of setf magic | ||
375 | (defmacro setf/alist (alist key val &optional testfn) | ||
376 | `(setf (alist-get ,key ,alist nil nil (or ,testfn #'equal)) | ||
377 | ,val)) | ||
378 | |||
379 | (defun unfill-region (beg end) | ||
380 | (interactive "*r") | ||
381 | (let ((fill-column most-positive-fixnum)) | ||
382 | (fill-region beg end))) | ||
383 | |||
384 | (defun unfill-paragraph () | ||
385 | (interactive) | ||
386 | (let ((fill-column most-positive-fixnum)) | ||
387 | (fill-paragraph beg end))) | ||
388 | |||
389 | (defun unfill-buffer () | ||
390 | (interactive) | ||
391 | (unfill-region (point-min) (point-max))) | ||
392 | |||
393 | (defun unfill-buffer/force () | ||
394 | (interactive) | ||
395 | (let ((buffer-read-only nil)) | ||
396 | (unfill-buffer) | ||
397 | (visual-line-mode t))) | ||
398 | |||
399 | (defmacro after (event &rest body) | ||
400 | "Do BODY after EVENT, which can be: | ||
401 | - A feature | ||
402 | - A hook -- if it requires arguments they'll be in the list `args' | ||
403 | - The symbol 'init, which runs on after-init-hook" | ||
404 | (declare (indent 1)) | ||
405 | (let ((lambda-form `(lambda (&rest args) ,@body))) | ||
406 | (pcase event | ||
407 | (`(timer ,ev) `(run-with-timer ,ev nil ,lambda-form)) | ||
408 | (`(idle ,ev) `(run-with-idle-timer ,ev nil ,lambda-form)) | ||
409 | (`(hook ,ev) `(add-hook ',ev ,lambda-form)) | ||
410 | (`init `(after (hook after-init-hook) ,@body)) | ||
411 | ((pred numberp) `(after (timer ,event) ,@body)) | ||
412 | ((pred (lambda (ev) | ||
413 | (and (symbolp ev) | ||
414 | (or (string-suffix-p "-hook" (symbol-name ev)) | ||
415 | (string-suffix-p "-function" (symbol-name ev)) | ||
416 | (string-suffix-p "-functions" (symbol-name ev)))))) | ||
417 | `(after (hook ,event) ,@body)) | ||
418 | ((pred symbolp) `(with-eval-after-load ',event ,@body)) | ||
419 | |||
420 | (_ (error "Can't determine event type" event))))) | ||