-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathinit.el
6377 lines (5012 loc) · 219 KB
/
init.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;; -*- lexical-binding: t; -*-
;;;; Prologue
;; Prefer loading a newer .el to an older .elc. Probably keeps me
;; from getting in trouble if I forget to byte compile.
(setq load-prefer-newer t)
(when (fboundp 'native-compile)
;; I believe the primary author of the native-comp branch explicitly
;; does not recommend speed 3 for normal use, maybe only for use in
;; special cases where extra performance is needed.
;;(setq comp-speed 3)
;; Emacs is going to be running ld early and often, it needs to run
;; the one from Homebrew that might know about libgccjit. Note that
;; this will later be overwritten by my use of exec-path-from-shell.
(setenv "PATH" (concat "/opt/homebrew/bin:/usr/local/bin:"
(or (getenv "PATH") "/bin:/usr/bin")))
;; Don't pop up *Warnings* for native-comp warnings because the
;; async native-comp warnings are far too numerous.
(with-eval-after-load 'warnings
(add-to-list 'warning-suppress-types '(comp))))
(require 'cl-lib)
(require 'subr-x)
(require 'seq)
;; Load local init.el stuff early, since it might e.g. set up SSL
;; settings or something.
(load (expand-file-name "init-local" user-emacs-directory) t)
;;; Local packages
(defvar my:local-packages-dir (expand-file-name "lisp" user-emacs-directory))
(add-to-list 'load-path my:local-packages-dir)
(defvar my:local-packages-autoload-file
(expand-file-name "autoloads.el" my:local-packages-dir))
(defvar generated-autoload-file)
(defun my:update-local-package-autoloads ()
(interactive)
(let ((kill-buffer-after
(not (get-file-buffer my:local-packages-autoload-file))))
(let* ((generated-autoload-file my:local-packages-autoload-file))
(update-directory-autoloads my:local-packages-dir))
(when-let ((buf (and kill-buffer-after
(get-file-buffer my:local-packages-autoload-file))))
(kill-buffer buf))
(load my:local-packages-autoload-file)))
(if (file-exists-p my:local-packages-autoload-file)
(load my:local-packages-autoload-file)
(my:update-local-package-autoloads))
;;; "Recipes"
(defvar my:recipes-dir
(expand-file-name "recipes" (file-name-directory load-file-name)))
(defun my:load-recipe (recipe)
(cl-assert (symbolp recipe) nil "expected symbol not %S" recipe)
(load (expand-file-name (symbol-name recipe) my:recipes-dir)))
(defun my:load-recipes (&rest recipes)
(dolist (recipe recipes)
(my:load-recipe recipe)))
;;; Add time stamps to *Messages*
;; Loading this early because it can be very useful to see time stamps
;; on messages.
(my:load-recipe 'timestamp-messages)
;;; Set custom-file
;; Set this early before anything in customize can haul off and do
;; stupid shit I will later find disagreeable.
(setq custom-file (expand-file-name "customizations.el" user-emacs-directory))
;; I can't remember why I have to do this myself. I think it's just
;; the way things are meant to work.
(load custom-file)
;;; straight.el, for all your packaging needs
(setq straight-repository-branch "develop")
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el"
user-emacs-directory))
(bootstrap-version 6))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
;; I'm going to get auto-compile very early and turn it on, in hopes
;; that it'll auto-compile anything that needs it. This is probably
;; pointless, at least for packages installed by straight.el, but it
;; should also be harmless. I suspect nothing will need to get
;; compiled until I start doing some `my:load-recipes' later on.
(straight-use-package 'auto-compile)
(auto-compile-on-load-mode)
(auto-compile-on-save-mode)
;; I'm (possibly) using emacs-lsp-booster and lsp-mode. To use
;; emacs-lsp-booster, I need lsp-mode to be compiled with plist
;; support, apparently. That means getting some of these variables
;; set early.
;;
;; Make sure that I have my (probably) XDG user bin dir on `exec-path'
;; so that I can find emacs-lsp-booster.
(let ((xdg-user-bin (expand-file-name "~/.local/bin")))
(unless (seq-some (apply-partially #'string-match-p
(rx string-start
(literal xdg-user-bin)
(? ?/)
string-end))
exec-path)
(push xdg-user-bin exec-path)))
(defvar my:use-emacs-lsp-booster (executable-find "emacs-lsp-booster"))
(when my:use-emacs-lsp-booster
(setenv "LSP_USE_PLISTS" "true")
(setq lsp-use-plists t))
;; As of Emacs 485622bbd1a you/I need AUCTeX > 13.0.5, or else you
;; get errors when reftex tries to create labels.
;;
;; On macOS, MacTeX might not be on my path by the time you get here.
;; The AUCTeX build process wants to use pdftex, so let's temporarily
;; put it on the path.
(defvar my:texbin-dir "/Library/TeX/texbin")
(defvar my:tex-available-p (file-directory-p my:texbin-dir))
;; AUCTeX will not build without LaTeX.
(when my:tex-available-p
(let ((process-environment process-environment))
(setenv "PATH" (concat (or (getenv "PATH") "") ":" my:texbin-dir))
;; The el-get recipe won't work for AUCTeX because (I assume) el-get
;; runs Elisp right out of the repo it clones to, which contains
;; support files (ex. styles/*.el) that won't be there if you use
;; that same recipe with straight.el. Hence we make our own recipe.
(straight-use-package '(auctex :source el-get
:files ("*.el" "*.info" "dir"
"doc" "etc" "images" "latex" "style")))
;; See the :load bits of
;; https://github.com/dimitri/el-get/blob/master/recipes/auctex.rcp,
;; which are not supported by straight.el as of this writing. Without
;; these you will get built-in Emacs LaTeX modes, not AUCTeX.
(require 'tex-site)
(require 'preview-latex)))
;; Let's install some packages.
(straight-use-package '(org :repo "git@github-personal:dsedivec/org-mode.git"))
(straight-use-package 'org-contrib)
(dolist (pkg-def '(
(eltu :files (:defaults "eltu_update_tags.py"))
ns-copy-html
smart-tabs
sticky-region
)
)
(straight-use-package (nconc (if (consp pkg-def)
(copy-tree pkg-def)
(list pkg-def))
(list :host 'github
:repo (format "dsedivec/%s"
(if (consp pkg-def)
(car pkg-def)
pkg-def))))))
(defun my:straight-use-packages (packages)
(mapc #'straight-use-package packages))
(my:straight-use-packages '(
ace-window
adaptive-wrap
aggressive-indent
all-the-icons
all-the-icons-completion
anaconda-mode
apheleia
atomic-chrome
auto-highlight-symbol
auto-yasnippet
avy
blackout
bm
buttercup
cider
clean-aindent-mode
clj-refactor
command-log-mode
comment-dwim-2
company
company-anaconda
company-prescient
company-shell
company-terraform
company-web
corfu
crontab-mode
crux
csv-mode
ctrlf
dash
deft
diff-hl
dired-narrow
dired-ranger
dired-subtree
dockerfile-mode
dotenv-mode
dtrt-indent
dumb-jump
edit-indirect
editorconfig
el-patch
ellama
embrace
emmet-mode
envrc
eterm-256color
exec-path-from-shell
expand-region
fennel-mode
filladapt
;; Need this recipe to get the dynamic
;; module in the "bin" directory.
(flx-rs
:repo "jcs-elpa/flx-rs"
:fetcher github
:files (:defaults "bin"))
flycheck
flycheck-clj-kondo
flycheck-haskell
flycheck-package
flycheck-pos-tip
free-keys
fussy
git-link
go-mode
gptel
graphviz-dot-mode
groovy-mode
haskell-snippets
hcl-mode
highlight-indent-guides
highlight-indentation
highlight-parentheses
hindent
hl-todo
hlint-refactor
htmlize
hydra
imenu-list
impatient-mode
js2-mode
js2-refactor
json-mode
key-chord
link-hint
literate-calc-mode
loccur
lorem-ipsum
lsp-mssql
lsp-mode
lsp-pyright
lsp-treemacs
lsp-ui
lua-mode
macrostep
magit
markdown-mode
minions
modus-themes
monroe
move-text
multiple-cursors
mwim
nftables-mode
nhexl-mode
obsidian
olivetti
org-download
(org-roam :branch "main")
orgtbl-aggregate
osx-dictionary
pandoc-mode
paredit
phi-search
prescient
projectile
python
pyvenv
rainbow-mode
reformatter
rg
rustic
scss-mode
shackle
shift-number
sly
smartparens
sql-indent
sqlup-mode
string-inflection
systemd
terraform-doc
terraform-mode
transpose-frame
treemacs
treemacs-projectile
treepy
treesit-auto
typescript-mode
undo-tree
unfill
vcl-mode
volatile-highlights
vterm
web-beautify
web-mode
;; Seems like these folks have changed the
;; default branch from "master" to "main",
;; but maybe the MELPA recipe didn't get
;; updated?
(webpaste :branch "main")
wgrep
which-key
winum
yaml-mode
yasnippet
yasnippet-snippets
zop-to-char
))
(when my:tex-available-p
;; These are guarded so that straight doesn't try to pull in AUCTeX
;; against my will.
(my:straight-use-packages '(
company-reftex
)))
;; Completion framework
(defvar my:completion-framework 'vertico)
(cl-assert (memq my:completion-framework '(ivy vertico)))
(cl-ecase my:completion-framework
(ivy
(my:straight-use-packages '(
amx
counsel
counsel-css
counsel-projectile
flx
ivy
ivy-avy
ivy-hydra
ivy-prescient
ivy-xref
ivy-yasnippet
lsp-ivy
swiper)
)
(when my:tex-available-p
(my:straight-use-packages '(ivy-bibtex))))
(vertico
(my:straight-use-packages '(
consult
consult-flycheck
consult-lsp
embark
embark-consult
marginalia
orderless
vertico
))))
;;; Utility functions
(require 'dsedivec-utils)
;;;; Emacs built-ins
;; My nav-stack package uses circular lists, try not to stack overflow
;; when I accidentally end up printing one during debugging. (Why is
;; this nil by default, anyway?)
(setq print-circle t)
;; Debugging faces, `symbol-plist' often needs to print quite a lot.
;; 10 is the default as of this writing.
(setq print-length 100)
;; Doubling these as I kept running out of undo history (at least, I
;; did with undo-tree).
(setq undo-limit (* 2 80000)
undo-strong-limit (* 2 120000))
;; Resize windows proportionally for the whole frame, not just the
;; window being split.
(setq window-combination-resize t)
;; Default is to convert yanked text in a search to lower case. That
;; really screws me up, turn that off.
(setq search-upper-case t)
;; In fact, having a search/replace for "foo bar" → "baz eek" convert
;; to replacing "Foo bar" → "baz eek" seems dangerous, let's just make
;; case folding explicit (M-c).
(setq-default case-fold-search nil)
(put 'downcase-region 'disabled nil)
(put 'erase-buffer 'disabled nil)
(put 'list-timers 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(defun my:show-trailing-white-space ()
;; setq-local instead of setq superfluous!
(setq-local show-trailing-whitespace t))
(add-hook 'text-mode-hook #'my:show-trailing-white-space)
(add-hook 'prog-mode-hook #'my:show-trailing-white-space)
(when (eq window-system 'ns)
(setq ns-use-native-fullscreen nil
;; This defaults to '(t) which tries to use appropriate icons
;; from the system in the title bar. Unfortunately lots of
;; these icons on my system are the icon of a blank page, and
;; this makes it hard to pick out Emacs in e.g. Witch or other
;; application switchers. Let's just disable file type icons.
ns-icon-type-alist nil)
;; If you google "emacs mac x-colors" you will see lots of people
;; commenting that they're missing colors. I think there's some
;; kind of timing bug during build, or something else isn't
;; configured right, that leaves you with just the colors from the
;; "Developer" colors in your `x-colors' variable. This is the
;; quick-and-dirty fix, I hope.
(setq x-colors (ns-list-colors))
;; Demand higher contrast, particularly when running under
;; (invert-face 'default). This was determined by looking at the
;; `color-distance' between the region face's background and
;; font-lock-comment-face's foreground, which is currently >150,000
;; for me. For reference, perhaps:
;;
;; (color-distance "black" "white") → 589800
(setq face-near-same-color-threshold 160000)
;; Docstring for `face-near-same-color-threshold' says to do this.
(clear-face-cache)
;; Per NEWS.28, this should get me Emoji support? Please?
(set-fontset-font t 'emoji '("Apple Color Emoji" . "iso10646-1") nil 'prepend)
;; Per people (rightfully?) bitching in
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54970, it seems I
;; also need to do this for the "symbol script". (You can see this
;; in `describe-char' output.)
(set-fontset-font t 'symbol '("Apple Color Emoji" . "iso10646-1") nil 'prepend)
;; I added these two when I found the SQUARED LATIN CAPITAL LETTER M
;; (U+1F13C) (🄼) was no longer displaying, maybe after I upgraded to
;; Ventura. Apple Symbols has it, but I *think* I prefer the SF Pro
;; version of it.
(set-fontset-font t 'symbol '("Apple Symbols" . "iso10646-1") nil 'prepend)
(set-fontset-font t 'symbol '("SF Pro" . "iso10646-1") nil 'prepend)
;; As of 9370a4763aac, `ns-popup-font-panel' is apparently gone.
;; The menu bar has its own `menu-set-font' that I'm going to crib
;; from.
(unless (commandp 'ns-popup-font-panel)
(autoload 'menu-set-font "menu-bar")
(bind-key "s-t" 'menu-set-font))
(my:load-recipes 'ns-paste-as-org))
;; macOS trashing: inspired first by
;; https://github.com/emacsorphanage/osx-trash, but then by
;; https://gist.github.com/dabrahams/14fedc316441c350b382528ea64bc09c
;; (from https://apple.stackexchange.com/a/162354).
;; `ns-do-applescript' seems fast enough to me.
(defun my:ns-move-files-to-trash (&rest paths)
(let ((as-paths
(mapconcat
(lambda (path)
(format "the POSIX file \"%s\"" (replace-regexp-in-string
(rx (group (any ?\\ ?\")))
"\\\\\\1"
(expand-file-name path))))
paths
", ")))
(ns-do-applescript
(format "tell application \"Finder\" to move {%s} to trash" as-paths))))
(when (eq system-type 'darwin)
(let ((emacs-29 (>= emacs-major-version 29))
(trash-defined (fboundp 'system-move-file-to-trash)))
(if trash-defined
(if emacs-29
(message "Using Emacs 29 built-in trash rather than AppleScript")
(warn (concat "`system-move-file-to-trash' shouldn't be defined,"
" check your init.el")))
(when emacs-29
(warn (concat "Using AppleScript trash support, but it should be"
" built-in for Emacs 29; maybe build newer Emacs?")))
(defalias 'system-move-file-to-trash #'my:ns-move-files-to-trash))))
(setq delete-by-moving-to-trash t)
;;; el-patch
;; This package is way, way early because I'm about to use it to patch
;; `treepy-remove', and I needd my treepy-remove-fix recipe. In
;; general, it needs to be early because I use this a decent number of
;; times throughout my init.el.
;; Without this, `el-patch-defvar' rarely does anything for me.
(setq el-patch-use-aggressive-defvar t)
;; Setting this early, since after I switched to straight, something
;; is hitting this all the time. I strongly suspect it's el-patch.
(setq vc-follow-symlinks t)
(my:load-recipe 'el-patch-dont-kill-my-buffers)
;;; bind-key
;; Temporary (hopefully) fix for #74660
(el-patch-feature bind-key)
(with-eval-after-load 'bind-key
(el-patch-defun bind-keys-form (args keymap)
"Bind multiple keys at once.
Accepts keyword arguments:
:map MAP - a keymap into which the keybindings should be
added
:prefix KEY - prefix key for these bindings
:prefix-map MAP - name of the prefix map that should be created
for these bindings
:prefix-docstring STR - docstring for the prefix-map variable
:menu-name NAME - optional menu string for prefix map
:repeat-docstring STR - docstring for the repeat-map variable
:repeat-map MAP - name of the repeat map that should be created
for these bindings. If specified, the
`repeat-map' property of each command bound
(within the scope of the `:repeat-map' keyword)
is set to this map.
:exit BINDINGS - Within the scope of `:repeat-map' will bind the
key in the repeat map, but will not set the
`repeat-map' property of the bound command.
:continue BINDINGS - Within the scope of `:repeat-map' forces the
same behavior as if no special keyword had
been used (that is, the command is bound, and
it's `repeat-map' property set)
:filter FORM - optional form to determine when bindings apply
The rest of the arguments are conses of keybinding string and a
function symbol (unquoted)."
(let (map
prefix-doc
prefix-map
prefix
repeat-map
repeat-doc
repeat-type ;; Only used internally
filter
menu-name
pkg)
;; Process any initial keyword arguments
(let ((cont t)
(arg-change-func 'cddr))
(while (and cont args)
(if (cond ((and (eq :map (car args))
(not prefix-map))
(setq map (cadr args)))
((eq :prefix-docstring (car args))
(setq prefix-doc (cadr args)))
((and (eq :prefix-map (car args))
(not (memq map '(global-map
override-global-map))))
(setq prefix-map (cadr args)))
((eq :repeat-docstring (car args))
(setq repeat-doc (cadr args)))
((and (eq :repeat-map (car args))
(not (memq map '(global-map
override-global-map))))
(setq repeat-map (cadr args))
(setq map repeat-map))
((eq :continue (car args))
(setq repeat-type :continue
arg-change-func 'cdr))
((eq :exit (car args))
(setq repeat-type :exit
arg-change-func 'cdr))
((eq :prefix (car args))
(setq prefix (cadr args)))
((eq :filter (car args))
(setq filter (cadr args)) t)
((eq :menu-name (car args))
(setq menu-name (cadr args)))
((eq :package (car args))
(setq pkg (cadr args))))
(setq args (funcall arg-change-func args))
(setq cont nil))))
(when (or (and prefix-map (not prefix))
(and prefix (not prefix-map)))
(error "Both :prefix-map and :prefix must be supplied"))
(when repeat-type
(unless repeat-map
(error ":continue and :exit require specifying :repeat-map")))
(when (and menu-name (not prefix))
(error "If :menu-name is supplied, :prefix must be too"))
(unless map (setq map keymap))
;; Process key binding arguments
(let (first next)
(while args
(if (keywordp (car args))
(progn
(setq next args)
(setq args nil))
(if first
(nconc first (list (car args)))
(setq first (list (car args))))
(setq args (cdr args))))
(cl-flet
((wrap (map bindings)
(if (and map pkg (not (memq map '(global-map
override-global-map))))
`((if (boundp ',map)
,(macroexp-progn bindings)
(eval-after-load
,(if (symbolp pkg) `',pkg pkg)
',(macroexp-progn bindings))))
bindings)))
(append
(when prefix-map
`((defvar ,prefix-map)
,@(when prefix-doc `((put ',prefix-map 'variable-documentation ,prefix-doc)))
,@(if menu-name
`((define-prefix-command ',prefix-map nil ,menu-name))
`((define-prefix-command ',prefix-map)))
,@(if (and map (not (eq map 'global-map)))
(wrap map `((bind-key ,prefix ',prefix-map ,map ,filter)))
`((bind-key ,prefix ',prefix-map nil ,filter)))))
(when repeat-map
`((el-patch-wrap 2 0
(unless (boundp ',repeat-map)
(defvar ,repeat-map (make-sparse-keymap)
,@(when repeat-doc `(,repeat-doc)))))))
(wrap map
(cl-mapcan
(lambda (form)
(let ((fun (and (cdr form) (list 'function (cdr form)))))
(if prefix-map
`((bind-key ,(car form) ,fun ,prefix-map ,filter))
(if (and map (not (eq map 'global-map)))
;; Only needed in this branch, since when
;; repeat-map is non-nil, map is always
;; non-nil
`(,@(when (and repeat-map (not (eq repeat-type :exit)))
`((put ,fun 'repeat-map ',repeat-map)))
(bind-key ,(car form) ,fun ,map ,filter))
`((bind-key ,(car form) ,fun nil ,filter))))))
first))
(when next
(bind-keys-form `(,@(when repeat-map `(:repeat-map ,repeat-map))
,@(if pkg
(cons :package (cons pkg next))
next)) map)))))))
(el-patch-validate 'bind-keys-form 'defun t))
;;; Mode line mods
;; (Couldn't require this at top, has to come after packages are
;; installed.)
(require 'dash)
;; Fix for https://github.com/volrath/treepy.el/issues/9, necessary
;; for treepy-remove to work right.
(my:load-recipes 'treepy-remove-fix)
;; Don't take up mode line space if encoding is unspecified or Unicode-ish.
(unless
(my:treepy-edit-mode-line-var
((default-value 'mode-line-mule-info) zip)
(equal (treepy-node zip) "%z")
(treepy-replace zip
`(:eval (let ((coding-info (format-mode-line
,(treepy-node zip))))
(unless (string-match-p "^[-U]$" coding-info)
coding-info)))))
(warn "couldn't make \"%%z\" conditional in `mode-line-mule-info'"))
;; Same with EOL, don't need to see it unless it's weird.
(unless
(my:treepy-edit-mode-line-var
((default-value 'mode-line-mule-info) zip)
(equal (treepy-node zip) '(:eval (mode-line-eol-desc)))
(treepy-replace zip
'(:eval (let ((eol-desc (mode-line-eol-desc)))
(unless (equal eol-desc ":")
eol-desc)))))
(warn "couldn't make EOL info conditional in `mode-line-mule-info'"))
;; Don't take up mode line space if current file is local. (BTW, %@
;; seems to be undocumented? Read src/xdisp.c.)
(unless
(my:treepy-edit-mode-line-var
((default-value 'mode-line-remote) zip)
(equal (treepy-node zip) "%1@")
(treepy-replace zip
;; FYI this is approximately the same logic as
;; src/xdisp.c uses.
`(:eval (when (and (stringp default-directory)
(file-remote-p default-directory))
,(treepy-node zip)))))
(warn "couldn't make remote indicator conditional in `mode-line-remote'"))
;; Move the buffer percentage way over to the right on the mode line.
(let ((percent-spec
(my:treepy-edit-mode-line-var
(mode-line-position zip)
(pcase (treepy-node zip)
(`(:propertize ("" mode-line-percent-position) . ,_) t))
(treepy-remove zip))))
(if (not percent-spec)
(warn (concat "couldn't find `mode-line-percent-position'"
" in `mode-line-position'"))
(unless (my:treepy-edit-mode-line-var
((default-value 'mode-line-format) zip)
(eq (treepy-node zip) 'mode-line-end-spaces)
(treepy-insert-left zip percent-spec))
(warn (concat "couldn't find `mode-line-end-spaces',"
" appending percentage to mode line"))
;; We already removed it from `mode-line-position', above.
;; Need to put it somewhere so it's not totally lost.
(nconc mode-line-format (list percent-spec)))))
;; Remove extra spaces between buffer ID and position.
(unless
(my:treepy-edit-mode-line-var
((default-value 'mode-line-format) zip)
(let ((node (treepy-node zip)))
(and (stringp node)
(string-match-p "^ \\{2,\\}$" node)
(eq (-some-> zip
treepy-right
treepy-node)
'mode-line-position)))
(treepy-replace zip " "))
(warn "couldn't remove spaces before buffer position in mode line"))
;; I am no longer seeing excessive spacing around the (line,col)
;; position information in Emacs 28.0.50. Check (probably) new
;; variable `mode-line-position-column-line-format' if it becomes a
;; problem again.
(when (< emacs-major-version 28)
;; Remove fixed width for position output, causes too much extra
;; space after (line,col) in mode line.
(unless
(my:treepy-edit-mode-line-var
(mode-line-position zip)
;; Note there is also a %C variant which I don't use.
(and (equal (treepy-node zip) " (%l,%c)")
(treepy-up zip)
(-some-> zip treepy-left treepy-node numberp))
(treepy-replace (treepy-up zip) (treepy-node zip)))
(warn "couldn't remove width specification from `mode-line-position'")))
;;; Themes
(add-to-list 'custom-theme-load-path
(expand-file-name "themes" user-emacs-directory))
(defvar my:themes '(modus-operandi . modus-vivendi)
"(LIGHT-THEME . DARK-THEME)")
(defun my:set-theme-for-macos-system-theme (&optional toggle force)
(interactive "P")
(let* ((scpt (concat "tell application \"System Events\" to"
" get the dark mode of appearance preferences"
" as integer"))
(emacs-theme (cond
((and (boundp 'custom-enabled-themes) custom-enabled-themes)
(cond
((> (length custom-enabled-themes) 1)
(user-error (concat "More than one theme enabled,"
" can't determine current theme")))
((eq (car custom-enabled-themes) (car my:themes))
'light)
((eq (car custom-enabled-themes) (cdr my:themes))
'dark)
(t
(user-error "Unknown theme %S"
(car custom-enabled-themes)))))
((member (downcase (face-background 'default))
'("white" "#fff" "#ffffff"))
'light)
;; When I originally wrote this, I just
;; defaulted to dark. Don't know if that's
;; really appropriate, but it hasn't been a
;; problem yet.
(t 'dark)))
(target-theme (cond
(toggle (cl-ecase emacs-theme
(light 'dark)
(dark 'light)))
((zerop (ns-do-applescript scpt))
'light)
(t 'dark))))
(cl-assert (memq emacs-theme '(light dark)))
(cl-assert (memq target-theme '(light dark)))
(if (and (not force) (eq emacs-theme target-theme))
(message "Emacs already configured for %s theme, no changes." target-theme)
(message "Configuring Emacs for %s theme." target-theme)
(modify-all-frames-parameters `((ns-appearance . ,target-theme)))
(cl-ecase target-theme
(light
(disable-theme (cdr my:themes))
(load-theme (car my:themes) t))
(dark
(disable-theme (car my:themes))
(load-theme (cdr my:themes) t)))
;; Function `org-mode' sets up face org-hide based on the
;; current background color. Changing the background color thus
;; requires restarting org-mode. I think I can do this in just
;; one buffer and it'll change the org-hide face globally.
(when-let ((org-buf (seq-some (lambda (buf)
(with-current-buffer buf
(when (derived-mode-p 'org-mode)
buf)))
(buffer-list))))
(with-current-buffer org-buf
(org-mode-restart))))))
;; Forcibly set the correct theme whenever starting Emacs. Forcing is
;; important because desktop.el restores my theme, with all its
;; parameters, including its background color, so the code in
;; `my:set-theme-for-macos-system-theme' thinks the theme is set. But
;; it's not, and you end up with the first frame looking half-right,
;; successive frames looking unthemed.
(add-hook 'after-init-hook (lambda () (my:set-theme-for-macos-system-theme nil t)))
;;;; Configure various packages
;;; which-key
(which-key-mode 1)
(my:load-recipes 'which-key-some-prefixes-are-fast)
;; `defun' by default, which seems a little weird. I prefer this
;; indentation.
(put 'which-key-add-key-based-replacements 'lisp-indent-function 0)
;; Fill in some explanations for Emacs built-in prefixes, or prefixes
;; that are kind of shared like C-x r.
(which-key-add-key-based-replacements
"C-x 4" "other window"
"C-x 5" "other frame"
"C-x n" "narrow"
"C-x r" "register/rectangle"
"C-x w" "highlight/winum")
;;; "Leader" keys setup
;; Inspired by Spacemacs.
(define-prefix-command 'my:global-leader-map)
(bind-key "M-m" 'my:global-leader-map)
(which-key-add-key-based-replacements
"M-m a" "apps"
"M-m h" "help"
"M-m i" "insert"
"M-m j" "jump/join"
"M-m m" "major mode"
"M-m s" "search"
"M-m u" "utils"
"M-m v" "vc"
"M-m x" "smartparens")
;;; wspc-hydra
;; This package is out of order because I use
;; `my:warn-white-space-mode' all over the place.
(setq wspc-hydra-buffer-local-whitespace-style t)
(defun my:warn-white-space-mode ()
(wspc-hydra-apply-style 'warn-white-space)
(display-fill-column-indicator-mode 1))
;;; exec-path-from-shell
;; This package is out of alphabetical order in this file because it's
;; probably important to get `exec-path' set early before you, for
;; example, try to launch a spelling checker via `flyspell-mode'.
(when (memq window-system '(mac ns x))
(exec-path-from-shell-initialize)
;; Need to pick up SSH_ASKPASS from shell on Darwin, which may be set
;; to the path to my own ssh-askpass clone that uses pinentry since we
;; have no good ssh-askpass on macOS AFAIK.
(exec-path-from-shell-copy-envs '("SSH_ASKPASS")))
;;; AUCTeX, RefTeX, and other LaTeX-related stuff
(setq TeX-newline-function 'newline-and-indent
;; "AUCTeX depends heavily on being able to extract information
;; from the buffers by parsing them. Since parsing the buffer
;; can be somewhat slow, the parsing is initially disabled. You
;; are encouraged to enable them by adding the following lines
;; to your '.emacs' file."
TeX-parse-self t
TeX-auto-save t)
(setq LaTeX-includegraphics-read-file
#'LaTeX-includegraphics-read-file-relative)
(setq font-latex-fontify-sectioning 1.3)
(with-eval-after-load 'font-latex
(font-latex-update-sectioning-faces))
;; Let company-mode start idle completion after typing a hyphen, such
;; as in "\gls{foo-".
(put 'LaTeX-babel-insert-hyphen 'company-begin t)
(with-eval-after-load 'latex
;; This auto-insert skeleton only loaded after 'latex because it
;; (ab)uses `LaTeX-arg-usepackage-read-packages-with-options'.
(with-eval-after-load 'autoinsert
(setf (alist-get "\\.[Ss][Tt][Yy]\\'" auto-insert-alist nil nil #'equal)
'(nil
"\\NeedsTeXFormat{LaTeX2e}[1994/06/01]\n"
"\\ProvidesPackage{"
(if buffer-file-name
(file-name-base buffer-file-name)
(read-string "Package name: "))
"}\n"
;; AUCTeX doesn't help us indent here...
;; >
;; ...so we'll just insert our own indent.
" ["
(format-time-string "%Y/%m/%d")
" v001 "
(read-string "This package's description: ")
"]\n\n"
(cl-loop
for (packages . options)
= (LaTeX-arg-usepackage-read-packages-with-options)
while packages