-
Notifications
You must be signed in to change notification settings - Fork 1
/
.emacs
3849 lines (3321 loc) · 151 KB
/
.emacs
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
;;; .emacs -*- lexical-binding: t; -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Citations ;;
;; ;;
;; "Show me your ~/.emacs and I will tell you who you are." -Bogdan Maryniuk ;;
;; ;;
;; "Emacs is like a laser guided missile. It only has to be slightly ;;
;; mis-configured to ruin your whole day." -Sean McGrath ;;
;; ;;
;; "While any text editor can save your files, only Emacs can save your soul.";;
;; -Per Abrahamsen ;;
;; ;;
;; "Anyone with a 4-line .emacs file is suspicious." -Dino Chiesa ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; HISTORY IDEAS FIXME TODO
;; history-length
;; list-command-history-max
;; savehist-minibuffer-history-variables
;; dmm/discover-my-mode-history
;; Man-topic-history
;; flycheck-read-checker-history
;; imenu--history-list
;; regexp-history
;; read-expression-history
;; face-name-history
;; buffer-name-history
;; read-envvar-name-history
;; shell-command-history
;; read-number-history
;; file-name-history
;; query-replace-history
;; minibuffer-history
;; extended-command-history
;; ido-file-history
;; ido-buffer-history
;; search-ring
;; regexp-search-ring
;; Notes
;; C-h b for all bindings
;; Or M-x where-is function
;; C-h a regex, search for that in commands ie anything with rot13
;; C-x z, repeat last command
;; M-x man, man in emacs
;; C-j, newline
;; C-m, newline-and-indent
;; C-h l, last inputs
;; C-u M-!, insert shell command into buffer
;; C-x 8 C-h, list of special characters; RET to enter by name
;; http://www.emacswiki.org/emacs/EmacsNewbieKeyReference
;; http://www.rgrjr.com/emacs/emacs_cheat.html
;; http://www.emacswiki.org/emacs/Reference_Sheet_by_Aaron_Hawley
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Regexp-Backslash.html
;; Should really look into and make use of f, s, dash
;; Am I just going to keep improving this until it matches spacemacs?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;; BEGIN
;; Enter debugger on error
(setq debug-on-error t
init-file-debug t)
;; UTF-8 always, always, always
(set-default-coding-systems 'utf-8)
(setq-default locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(set-language-environment "UTF-8")
;; (setq-default buffer-file-coding-system 'utf-8-unix)
;; Local lisp, will take precedence over elpa directory
(add-to-list 'load-path (expand-file-name "site-lisp" user-emacs-directory))
;; Prefer newer files even if not .elc
(setq load-prefer-newer t)
;; Customizations from within emacs interface
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file t)
;; Prepare for themes
(setq
;; Custom theme directory, in case
custom-theme-directory (expand-file-name "themes" user-emacs-directory)
;; Treat all themes as safe
custom-safe-themes t)
;; Define a hook to run after we load a theme, pretty useful! But for real, I
;; need to get over my hesitancy to use custom.el and just put faces there
;; sometimes and not over-use this.
(defvar after-load-theme-hook nil
"Hook run after a color theme is loaded using `load-theme'.
See also `enable-theme-functions' and `disable-theme-functions'")
;; (defadvice load-theme (after run-after-load-theme-hook activate)
;; "Run `after-load-theme-hook'."
;; (run-hooks 'after-load-theme-hook))
(advice-add 'load-theme :after
(lambda (&rest _)
"Run `after-load-theme-hook'."
(run-hooks 'after-load-theme-hook)))
;; Some polyfills for emacs older than 27.1; toolforge is on 26.1
(unless (fboundp 'seq-remove)
(defalias 'seq-remove 'gnus-remove-if))
(unless (fboundp 'seq-filter)
(defalias 'seq-filter 'gnus-remove-if-not))
;; Modeline customizations
;; Probably kind of pointless given I'm manually defining the format below?
;; Time
;; Lots more to customize FIXME TODO
(setq display-time-day-and-date t
display-time-24hr-format t
display-time-default-load-average nil)
(display-time-mode t)
;; Battery percentage
(when (not (equal "Battery status not available" (battery)))
(require 'battery)
(display-battery-mode)
(setq battery-update-interval 240 ;; Default 60s
battery-mode-line-limit 85 ;; Default 100
battery-load-low 25 ;; Default 25
battery-load-critical 10)) ;; Default 10
;; Show column-number, size in the mode line
(column-number-mode 1) ; performance hit?
(size-indication-mode t)
;; Tighten things up when there's not enough space. At least, in theory; in
;; practice, it doesn't like the ending "%-" aka "fill with -"
;; (setq mode-line-compact 'long)
;; Need to define this ahead of time, so I can apply it to the mode-line section
;; here, then make use of it later in the flycheck section.
;; Weird, can I do just the text, not background? Or, need to customize I
;; guess, that's a flycheck-color-mode-line thing I think FIXME TODO
(defface flycheck-mode-line-color-face
'((t))
"Face with which to color the Flycheck mode-line status text
via flycheck-color-mode-line-mode."
:group 'flycheck-faces)
;;; Actual Modeline custom format
;; CONSIDER: Colors faded when inactive, see emacs se TODO
;; Consider using timu-macos colors for more variety? TODO
;; Git:main? or eh? See also vc faces in customize
;;; https://stackoverflow.com/q/28468975/2521092
;;; https://emacs.stackexchange.com/q/17439/2051
;;; https://kitchingroup.cheme.cmu.edu/blog/2014/09/19/A-git-status-Emacs-modeline/
;; Can also do (for the above) global-mode-string
;; Also: follow-mode. Check out minor-mode-alist for other ideas
;; which-function-mode yeah for sure TODO
;; FOR REFERENCE: https://www.emacswiki.org/emacs/ModeLineConfiguration
;; Also: https://www.gnu.org/software/emacs/manual/html_node/elisp/_0025_002dConstructs.html
;; Also: https://www.gnu.org/software/emacs/manual/html_node/elisp/Mode-Line-Variables.html
;; Check out moody? TODO
(setq-default mode-line-format
(list
;; Error if memory full?
"%e"
;; window-numbering-mode will insert itself here, which is fine. Uses
;; window-numbering-face, which is also fine, but I've set it to
;; mode-line-buffer-id, in order to be bold, which is also fine. The
;; main point is that by using the various built-in mode-line faces, it
;; dims when inactive, which is ideal.
;; Toss in @ if we're editing via emacsclient server, otherwise -, the
;; former borrowed from `mode-line-remote'. Serves as sort of a spacer
;; that recapitulates `mode-line-client' in the place of
;; `mode-line-front-space'
'(:eval (propertize (if
(frame-parameter nil 'client)
;; This should be subtle since it's technically
;; unnecessary
"@" "-") 'face 'font-lock-comment-face))
;; Buffer name. Not using mode-line-buffer-identification but doing it
;; manually means that the mode-line-buffer-id face gets separated from
;; this. Maybe that's not so bad?! It'd be nice to truncate the buffer
;; name (e.g. `(format-mode-line '(-20 "%b ") font-lock-keyword-face)' or
;; whatever) but it's a drag for helpful mode, etc. Not sure if there's
;; a good way to have this be mode-specific, or if it's worth it. TODO
;; Additionally, I want different a different face depending on whether
;; we're in light or night mode.
'(:eval (if (equal timu-macos-flavour "dark")
(propertize "%b " 'face 'font-lock-function-name-face)
(propertize "%b " 'face 'font-lock-keyword-face)))
;; The below largely amounts to a more concise way of doing
;; `mode-line-position', giving line and column and percent.
;; Line and (1-based) column. Could do '%02' to always do two
;; characters, but it'll change (especially the line counter) at three
;; digits anyway, so I don't think I really care that much.
"("
(propertize "%l" 'face 'font-lock-keyword-face)
","
(propertize "%02C" 'face 'font-lock-keyword-face)
") "
;; Position and file length/size
"["
;; %p is percent of buffer above top of window (or Top/Bot/All), and %o
;; is travel? Automatically accounts for two characters? Ugh. %q is
;; neat, does the range
(propertize "%o" 'face 'font-lock-constant-face)
;; Total count of lines, formatted nicely
'(:eval (concat "/" (propertize (file-size-human-readable (line-number-at-pos (point-max)) 'si) 'face 'font-lock-constant-face)))
;; Size in bytes, abbreviated. Not particularly useful?
;; (concat "/" (propertize "%I" 'face 'font-lock-constant-face))
"] "
"["
;; The current major mode for the buffer; `mode-line-modes' is too much
;; Use builtin-face? FIXME Actually, reassess all the below faces TODO
;; '(:eval (propertize "%m" 'face 'font-lock-string-face))
;; '(:eval (propertize "%m" 'face 'font-lock-builtin-face))
'(:eval (propertize "%m" 'face 'font-lock-keyword-face))
;; Should these be turned off for dashboard, paradox? Weird to see 'em
;; there.... FIXME TODO
;; Was this buffer modified since the last save? Differs from `mode-line-modified'
'(:eval (when (buffer-modified-p)
(concat "," (propertize "Mod" 'face 'font-lock-constant-face))))
;; A few other modes worth noting
'(:eval (when defining-kbd-macro
(concat "," (propertize "Macro" 'face 'font-lock-constant-face))))
;; Is this buffer read-only?
'(:eval (when buffer-read-only
(concat "," (propertize "RO" 'face 'font-lock-type-face))))
;; %n introduces a leading space, so do this instead
'(:eval (when (buffer-narrowed-p)
(concat "," (propertize "Nar" 'face 'font-lock-type-face))))
'(:eval (when (bound-and-true-p follow-mode)
(concat "," (propertize "Fol" 'face 'font-lock-type-face))))
"]"
;; Flycheck status, manually since not using minor-mode-alist and because
;; it's smort.
;; Can't remove the annoying leading space? Ugh.
;; What I want: gone when nothing? No leading space. Color.
;; Could put up with major mode? Eh. Wrap in <>? Would have to
;; redefine 'cause of the space, but I might have to anyway... TODO
;; Maybe also include info in modeline? Otherwise colored but no number,
;; is that weird? Or expected?
'(:eval (when (and
flycheck-mode
;; Confirm flycheck isn't running but without a valid
;; checker configuration
(not (string= "no-checker" flycheck-last-status-change))
(not (string= "not-checked" flycheck-last-status-change)))
;; (flycheck-mode-line-status-text)))
(propertize (flycheck-mode-line-status-text) 'face 'flycheck-mode-line-color-face)))
" "
;; Add the time, date, and emacs server uptime (maybe dumb). The uptime
;; is dependent on server status, and although I like the emacsclient @
;; above (a la mode-line-client), this does the same thing, really, and
;; it's not super necessary. The date/time/battery largely recapitulates
;; global-mode-string, but with some more sensible spacing and
;; arrangement.
'(:eval (propertize (format-time-string "%R %a %h %-d")))
'(:eval (when (frame-parameter nil 'client)
(concat ", Up " (emacs-uptime "%D, %z%h:%.2m"))))
;; This format always seems simpler but more opaque. Spaces included.
'(battery-mode-line-string (" " battery-mode-line-string))
"--"
;; List of minor modes. I don't want them, but maybe it's useful to note them here?
;; Diminish still removes most of them, but maybe it's kind of unnecessary
;; minor-mode-alist
mode-line-end-spaces ;; fill with '-'
))
;; Time. Pointless given the above?
;; (setq display-time-day-and-date t
;; display-time-24hr-format t
;; display-time-default-load-average nil)
;; (display-time-mode t)
;; Show column-number, size in the mode line. Both pointless given the above
;; (column-number-mode 1) ; performance hit?
;; (size-indication-mode t)
;; Add macOS keychain to auth sources?
(add-to-list 'auth-sources 'macos-keychain-generic)
(add-to-list 'auth-sources 'macos-keychain-internet)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Package
(require 'package)
;; Fix for annoying ELPA failure, supposedly fixed upstream but still an issue
;; for me on 27.1; see https://github.com/syl20bnr/spacemacs/issues/12535 and
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=34341
;; Something to do with a specific GNUTLS version (currently 30615), this exact check is from
;; https://github.com/syl20bnr/spacemacs/blob/d46eacd83842815b24afcb2e1fee5c80c38187c5/core/core-emacs-backports.el
;; Still necessary in 29+? FIXME
(unless (<= libgnutls-version 30603)
(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
;; In theory it'd be nice to set `package-archive-priorities', but in practice
;; there's no overlap between MELPA and GNU ELPA. Added in 25.1. However, with
;; the new nongnu, I'd like to; at the moment it causes issues. FIXME TODO
(unless (assoc-default "melpa" package-archives)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/")))
;; Only include releases
;; (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/"))
;; Manual package installs, ideally some can be removed
;; 'hide-comnt
;; 'keep-buffers
;; 'keywiz
;; 'kill-ring-ido
;; 'perltidy
;; 'pick-backup
;; 'tidy
;; 'u-mandelbrot
;; Packages and paradox customizations in custom.el
;; Set by custom rather than by hand to make installation easier
;; This must come before configurations of installed packages
(package-initialize)
;; Should probably install auto-package-update
;; https://github.com/rranelli/auto-package-update.el
;; use-package https://github.com/jwiegley/use-package
;; install (if not already present) and require, must be around for everyone else
;; Should actually use this... FIXME TODO
;; (dolist (package '(use-package))
;; (unless (package-installed-p package)
;; (package-install package)
;; (require 'use-package)
;; (setq use-package-always-ensure t
;; use-package-verbose t
;; ;; In theory, calling `use-package-report' would display some info
;; use-package-compute-statistics t)))
;; paradox https://github.com/Malabarba/paradox
(require 'paradox)
(setq paradox-github-token
;; Stored in a place like ~/.authinfo.gpg, ~/.authinfo, etc. See
;; https://github.com/Malabarba/paradox/issues/147#issuecomment-409336111
;; Off since the ability to automatically star (via
;; paradox-automatically-star) is slow and minimally valuable atm.
;; (cadr(auth-source-user-and-password "api.github.com" "paradox")))
;; Setting to t turns off asking
t)
;; Quieter startup, see https://github.com/Malabarba/paradox/pull/183
;; (setq paradox-less-verbose t)
(defun paradox--override-definition (sym newdef)
"Temporarily override SYM's function definition with NEWDEF.
Record that in `paradox--backups', but do nothing if
`paradox--backups' reports that it is already overriden."
(unless (memq sym paradox--backups)
(advice-add sym :override newdef '((name . :paradox-override)))
(add-to-list 'paradox--backups sym)))
;; Enable sorting on the downloads column, https://github.com/Malabarba/paradox/pull/190
(paradox--define-sort paradox--column-name-download "d")
;; Refresh stars and downloads. Probably only need to do once per session to
;; get around https://github.com/Malabarba/paradox/issues/176. Can't seem to
;; hook this into something like `paradox-menu-mode-hook' or make use of
;; advice-add without causing errors, so just run manually for now.
;; (paradox--refresh-remote-data)
;; Can set a key, though. Will only run if data not loaded
(defun my/paradox-menu-refresh-data ()
"Refresh stars and downloads. Will only run once."
(interactive)
;; Hash table only populated once data refreshed
(when (eq 0 (hash-table-count paradox--star-count))
(paradox--refresh-remote-data)
;; Remove the mode-map definition now that we've run it once
(define-key paradox-menu-mode-map "r" nil)))
(define-key paradox-menu-mode-map "r" #'my/paradox-menu-refresh-data)
(paradox-enable)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;; themes ;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; I love the kaolin themes. Some thoughts:
;; - ocean (low contrast, blues and greens; should turn background black)
;; - galaxy (high contrast, blues and greens; should turn background black)
;; - aurora (nah)
;; - bubblegum (eh. Seems nice but blurs together)
;; - valley dark (like a better temple?)
;; - temple (okay? if turn background black. lotta pinks)
;; - dark (okay: low contrast, greens, kind of a low ocean)
;; Would use this were it not for timu-macos. Still, should find a better
;; light-mode version of it. Regardless, themes loaded automatically when using
;; auto-dark below.
;; (load-theme 'kaolin-galaxy)
(require 'timu-macos-theme)
(load-theme 'timu-macos)
;; Want different color highlighting (`region' face) depending on light or night
;; mode, and at the moment, timu-macos doesn't have a hook (it should!), so
;; let's define some advice for it. We could define our own hook and have it
;; run that, but, you know, six and one half. `defadvice' still seems easier...
(advice-add 'timu-macos-toggle-dark-light :after
(lambda ()
"Set face attribute for `region' based on `timu-macos-flavour'."
;; equal not eq
(if (equal timu-macos-flavour "dark")
(set-face-attribute 'region nil :background "color-237")
(set-face-attribute 'region nil :background "color-252"))))
(require 'auto-dark)
(setq
;; ns-do-applescript not available. Could also just set
;; auto-dark-detection-method to osascript, but this is the proper way
auto-dark-allow-osascript t
;; The default of 5 seems like it should be fine, but it seems to have a quite
;; noticable slowdown. Setting it higher isn't a big deal, so it's a
;; no-brainer. Could definitely go higher!
auto-dark-polling-interval-seconds 10
;; The timu-macos theme is weird, with just one actual theme but a function to
;; toggle between dark and light mode. Thankfully, auto-dark offers hooks!
;; Can use nil after https://github.com/LionyxML/auto-dark-emacs/issues/41 TODO
auto-dark-dark-theme 'timu-macos
auto-dark-light-theme 'timu-macos)
(add-hook 'auto-dark-dark-mode-hook #'timu-macos-toggle-dark-light)
(add-hook 'auto-dark-light-mode-hook #'timu-macos-toggle-dark-light)
(auto-dark-mode t)
;; When I was a child, I spake as a child,
;; I understood as a child, I thought as a child:
;; but when I became a man, I put away childish things.
;; -- 1 Corinthians, 13:11
;; No menu bar
(dolist (mode '(menu-bar-mode tool-bar-mode scroll-bar-mode tooltip-mode))
(when (fboundp mode) (funcall mode -1)))
;; Prevent the startup message and splash screen
(setq inhibit-startup-echo-area-message user-login-name
inhibit-startup-screen t)
;; But use a neat dashboard https://github.com/emacs-dashboard/emacs-dashboard
(require 'dashboard)
;; Required for emacsclient
(setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))
;; List files in current directory https://github.com/emacs-dashboard/dashboard-ls
;; (require 'dashboard-ls)
;; Doesn't work well for emacsclient since 'default-directory is set when emacs
;; is started. Ideally, with something like the below (inspired by startup.el),
;; dashboard-ls could take a function to determine the working directory when
;; called, then do something like the above lambda here as well, but in practice
;; emacsclient doesn't have good, fresh access to the PWD on loading. Maybe if
;; aliased to a function that takes the PWD when opening? Awkward.
;; (let* ((path
;; (cond ((stringp dashboard-ls-path)
;; (symbol-value 'dashboard-ls-path))
;; ((functionp dashboard-ls-path)
;; (funcall dashboard-ls-path))
;; ((booleanp dashboard-ls-path)
;; (symbol-value 'default-directory)))
;; ))
;; ;; (message "%s" path)
;; (setq dashboard-ls--record-path path))
;; Tip of the day taken from
;; https://github.com/emacs-dashboard/emacs-dashboard/issues/26 and
;; https://gist.github.com/saintaardvark/375aa054c15f02c42f45
;; Not perfect, should ideally limit self to a modemap or something
(defun totd()
"Display a 'Tip of the Day' message with a random command.
Used for insertion into the dashboard."
(let* ((commands (cl-loop for s being the symbols
when (commandp s) collect s))
(command (nth (random (length commands)) commands)))
(insert
(format "** Tip of the day: **\nCommand: %s\n\n%s\n\nInvoke with: "
command (documentation command)))
(where-is command t)))
(defun dashboard-insert-totd (list-size)
"Shim to insert the Tip of the Day into the dashboard."
(totd))
(add-to-list 'dashboard-item-generators '(totd . dashboard-insert-totd))
;; Expand recents; not using bookmarks or agenda, but keep here as reminder to
;; do so. Should use projectile at some point
(setq dashboard-items '((recents . 15)
;; (ls-files . 5)
;; (ls-directories . 3)
;; (bookmarks . 5)
;; (projects . 5) ; Depends on projectile
;; (agenda . 5)
;; (registers . 5)
(totd . 1)))
;; Weird that this gets the startup time wrong with emacsclient, not sure of why
;; since it appears to be using the right hooks
(dashboard-setup-startup-hook)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Indent if possible but complete otherwise
(setq-default tab-always-indent 'complete)
;; Start a split-below window at the same point. Setting to nil shows more
;; information, and is what I've used for a while, but for someone reason this
;; feels more intuitive now, and I find I've been wanting it for a while.
(setq split-window-keep-point t)
;; Resize windows when splitting; annoying if want certain size, but let's be
;; honest, I usually want some balance. Besides, on a small screen, it's not
;; a big difference, and on a big screen, it doesn't matter!
(setq-default window-combination-resize t)
;; ignore case when completing, including buffers and filenames
(setq completion-ignore-case t
read-file-name-completion-ignore-case t
read-buffer-completion-ignore-case t)
;; Completion in mini-buffer
(icomplete-mode t)
;; Auto complete
;; http://cx4a.org/software/auto-complete/manual.html#Configuration
(require 'auto-complete-config)
(ac-config-default)
;; start automatically after 3, not 2 characters
(setq ac-auto-start 3)
;; Characters entered before started, up=efficient, down=slower
;; (setq ac-auto-start 5) ;Default 2
;; Turn auto menu off
;; (setq ac-auto-start nil)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; yasnippet, loaded here to allow in hippie-expand
;; https://github.com/joaotavora/yasnippet and
;; https://github.com/AndreaCrotti/yasnippet-snippets
(require 'yasnippet)
(setq yas-wrap-around-region t ; Set region to $0
yas-verbosity 2 ; Fewer messages on startup
yas-triggers-in-field t) ; Allow nested expansions
(define-key yas-minor-mode-map (kbd "C-c C-i") 'yas-insert-snippet) ; C-c tab
(yas-reload-all)
(yas-global-mode)
;; Default M-/ is dabbrev-expand, but this is broken? ;;;;; ###### FIXME TODO
;; (global-set-key "\M-/" 'auto-complete)
;; Hippie expand expands lines, kind of like above but indiscriminate
(global-set-key "\M-?" 'hippie-expand)
;; Unexpand. This only works if you use hippie, I want a
;; dabbrev-unexpand, sort of like unexpand-abbrev
(defun hippie-unexpand ()
"Unexpand a `hippie-expand' expansion."
(interactive)
(hippie-expand 0))
(global-set-key "\M-\"" 'hippie-unexpand)
;; Sometimes hippie is a little TOO hip. Reorder the list so that expand-line
;; and expand-list come much, much later, definitely after expand-dabbrev
(setq hippie-expand-try-functions-list '(yas-hippie-try-expand
try-complete-file-name-partially
try-complete-file-name
try-expand-all-abbrevs
try-expand-dabbrev
try-expand-dabbrev-all-buffers
try-expand-dabbrev-from-kill
try-complete-lisp-symbol-partially
try-complete-lisp-symbol
try-expand-line
try-expand-list))
;; Use ido for hippie-expand via C-c / -- not ideal. Seems to swallow any
;; opening paren? ;;;;;; ##### FIXME TODO
(defun my-hippie-expand-completions (&optional hippie-expand-function)
"Return the full list of possible completions generated by `hippie-expand'.
The optional argument can be generated with `make-hippie-expand-function'."
(let ((this-command 'my-hippie-expand-completions)
(last-command last-command)
(buffer-modified (buffer-modified-p))
(hippie-expand-function (or hippie-expand-function 'hippie-expand)))
(cl-flet ((ding)) ; avoid the (ding) when hippie-expand exhausts its options.
(while (progn
(funcall hippie-expand-function nil)
(setq last-command 'my-hippie-expand-completions)
(not (equal he-num -1)))))
;; Evaluating the completions modifies the buffer,
;; however we will finish
;; up in the same state that we began.
(set-buffer-modified-p buffer-modified)
;; Provide the options in the order in which they
;; are normally generated.
(delete he-search-string (reverse he-tried-table))))
(defmacro my-ido-hippie-expand-with (hippie-expand-function)
"Generate an interactively-callable function that offers ido-based completion
using the specified hippie-expand function."
`(call-interactively
(lambda (&optional selection)
(interactive
(let ((options (my-hippie-expand-completions ,hippie-expand-function)))
(if options
(list (ido-completing-read "Completions: " options)))))
(if selection
(he-substitute-string selection t)
(message "No expansion found")))))
(defun my-ido-hippie-expand ()
"Offer ido-based completion for the word at point."
(interactive)
(my-ido-hippie-expand-with 'hippie-expand))
(global-set-key (kbd "C-c /") 'my-ido-hippie-expand)
;; Expand region highlights intelligently
;; https://github.com/magnars/expand-region.el
(require 'expand-region)
(global-set-key (kbd "M-m") 'er/expand-region)
(global-set-key (kbd "M-M") 'er/contract-region)
;; Possible keybindings?
;; (global-set-key (kbd "C-+") 'er/expand-region)
;; Mouse stuff doesn't work in OSX terminal
;; (setq focus-follows-mouse 1
;; mouse-autoselect-window 1)
;; Protect special buffers
(require 'keep-buffers)
(keep-buffers-mode 1)
;; Default 10
(setq buffers-menu-max-size 30)
;; Subword mode (consider CamelCase chunks as words)
(global-subword-mode 1)
;;;;;;;;;;;;;;;;;;;
;; JavaScript stuff
;; Use js2-mode instead of js-mode https://github.com/mooz/js2-mode
(require 'js2-mode)
(setq
js-switch-indent-offset 8 ; tab indent switch cases
js-chain-indent t ; line up successive indents with .
js-indent-level 2 ; Not everyone's got an editorconfig, tighter
js2-highlight-level 3 ; highlight more built-in functions
js2-mode-indent-ignore-first-tab t ; make first tab doesn't toggle between valid indents
js2-strict-inconsistent-return-warning nil ; warning if both return and return foo
js2-strict-trailing-comma-warning t) ; trailing commas in array/objects
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
(add-to-list 'interpreter-mode-alist '("node\\|nodejs" . js2-mode))
;; js2-jump-to-definition takes this over, annoying given everyone else respects it
(define-key js2-mode-map (kbd "M-.") 'end-of-buffer)
;; Okay weird but maybe? Lots of clobbering elsewhere...
(define-key js2-mode-map (kbd "C-c 2") 'js2-jump-to-definition)
;; Error checking can be slow on large files, slightly increase this
;; (setq js2-idle-timer-delay 0.5)
(setq js2-dynamic-idle-timer-adjust 10000)
;; Alternatively, just turn off error checking entirely and rely on flycheck's
;; javascript-eslint to let eslint handle things proper like. Also js2-mode's
;; checker doesn't update for new errors?
(setq js2-mode-show-strict-warnings nil
js2-mode-show-parse-errors nil)
;; If one weren't doing that and were letting js2-mode handle errors, make up
;; for the lack of eslint's `env' with the below. Note that
;; `js2-include-browser-externs' is on by default.
;; (setq js2-include-node-externs t) ; require, exports, etc
;; Define jest, qunit, and mocha externals as with node. Taken from
;; <https://github.com/sindresorhus/globals>, which is what eslint uses
(when js2-include-node-externs
(defvar js2-qunit-externs
(mapcar 'symbol-name
'(asyncTest deepEqual equal expect module notDeepEqual notEqual notOk
notPropEqual notStrictEqual ok propEqual QUnit raises
start stop strictEqual test throws))
"QUnit externs.")
(defvar js2-jest-externs
(mapcar 'symbol-name '(afterAll afterEach beforeAll beforeEach describe expect
fdescribe fit it jest pit require test
xdescribe xit xtest))
"Jest externs.")
(defvar js2-mocha-externs
(mapcar 'symbol-name
'(after afterEach before beforeEach context describe it mocha run
setup specify suite suiteSetup suiteTeardown teardown test
xcontext xdescribe xit xspecify))
"Mocha externs.")
(setq js2-global-externs (append js2-qunit-externs js2-jest-externs js2-mocha-externs)))
;; Part of js2-mode package
(require 'js2-imenu-extras)
(add-hook 'js2-mode-hook 'js2-imenu-extras-mode)
(js2-imenu-extras-setup)
;; I *like* having them, but it's annoying that they're sorted first
(setq js2-imenu-show-other-functions nil)
;; js2-refactor https://github.com/magnars/js2-refactor.el
;; Requires yasnippet and multiple-cursors
;; https://github.com/magnars/multiple-cursors.el (maybe do C-c m or something?)
;; Full list at https://github.com/magnars/js2-refactor.el#refactorings
(require 'js2-refactor)
(add-hook 'js2-mode-hook #'js2-refactor-mode)
(setq js2r-prefered-quote-type 2) ; single, not double
(js2r-add-keybindings-with-prefix "C-c m")
;; js2-refactor does not work in a buffer that has Javascript parse
;; errors. This tells js2-mode to treat octothorpes and hashbangs as comments,
;; preventing them from causing parse errors
(setq js2-skip-preprocessor-directives t)
;; jsdoc https://github.com/mooz/js-doc
;; old and creaky, but okay enough for now. Honestly, maybe yas would just be better?
(require 'js-doc)
(add-hook 'js2-mode-hook
#'(lambda ()
;; clobbers js-set-js-context, whatever that did
(define-key js2-mode-map "\C-c\C-j" 'js-doc-insert-function-doc)
(define-key js2-mode-map "@" 'js-doc-insert-tag)))
;;;;;;;;;;;;;;;;;;;
;; emmet-mode expansions, super cool if I ever remember (use C-j)
;; https://github.com/smihica/emmet-mode
(autoload 'emmet-mode "emmet-mode")
(add-hook 'html-mode-hook 'emmet-mode)
(add-hook 'css-mode-hook 'emmet-mode)
;; json-mode https://github.com/joshwnj/json-mode
;; C-c C- map has fun things like reformatting, toggling booleans
;; Consider json-navigator https://github.com/DamienCassou/json-navigator
(autoload 'json-mode "json-mode" "Major mode for editing JSON files." t)
(add-to-list 'auto-mode-alist '("\\.json\\'" . json-mode))
;; mhtml-mode probably better (default since 26), but some bug (maybe with
;; fic-mode?) makes it wig out. Regex taken from auto-mode-alist itself
(add-to-list 'auto-mode-alist '("\\.[sx]?html?\\(\\.[a-zA-Z_]+\\)?\\'" . html-mode))
;; php-mode https://github.com/emacs-php/php-mode
(autoload 'php-mode "php-mode" "Major mode for editing PHP code." t)
(add-to-list 'auto-mode-alist '("\\.php\\'" . php-mode))
(add-to-list 'auto-mode-alist '("\\.inc\\'" . php-mode))
;; yaml-mode
(autoload 'yaml-mode "yaml-mode" "Simple mode to edit YAML." t)
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-mode))
;; ess-mode aka R mode https://ess.r-project.org/
;; Should probably customize the linters used (like not whining about `=` for
;; assignment) but I don't use R enough to care, and certainly not in Emacs.
;; No need for flymake
(eval-after-load 'ess
(setq ess-use-flymake nil))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Flycheck stuff
;;; https://github.com/flycheck/flycheck and https://www.flycheck.org
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; FIXME TODO:
;;; Consider turning margin on? Big change for everybody but maybe worth it long-term?
;;; Possible additional extensions: flycheck-inline, flycheck-elsa/elsa,
;;; flycheck-grammarly FIXME TODO
(require 'flycheck)
;; Turn on for everybody
(add-hook 'after-init-hook #'global-flycheck-mode)
;; I don't rightly care about ensuring lisps have the "proper" package comments
(setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc))
(setq flycheck-emacs-lisp-load-path 'inherit
flycheck-check-syntax-automatically '(save idle-change idle-buffer-switch new-line mode-enabled)
flycheck-relevant-error-other-file-minimum-level 'warning
flycheck-standard-error-navigation t
flycheck-display-errors-function 'flycheck-display-error-messages-unless-error-list
flycheck-display-errors-delay 0.25
flycheck-idle-buffer-switch-delay 2
;; Static analysis can be slow and delay text entry, besides rarely need
;; right away? Maybe this is excessively cautious?
flycheck-idle-change-delay 2
;; Pointless atm since no margin enabled...
flycheck-indication-mode 'left-margin
;; '(flycheck-highlighting-mode 'lines
;; Slow in python mode, but neat regardless
flycheck-highlighting-mode 'sexps
flycheck-mode-line-prefix "Fly" ;default FlyC
;; Recently in <https://github.com/flycheck/flycheck/pull/2035> the
;; default behavior of no errors defaulted to showing ":0" so we can undo
;; that, but also maybe a checkmark is nice? Not like me to use emoji.
;; flycheck-mode-success-indicator ""
flycheck-mode-success-indicator "✔︎"
flycheck-markdown-markdownlint-cli-config ".markdownlintrc"
;; Tell flycheck that sometimes Perl modules are in ./lib, or ../lib if
;; you're in a testing directory /t. Used with perl -I
flycheck-perl-include-path (list "./lib/" "../lib")
flycheck-perlcritic-severity 2)
;; Some additional checkers; could probably just run these straight-up
(with-eval-after-load 'flycheck
;; Add hook to `recenter' frame after jumping to an error from the list, just
;; like `occur-mode-find-occurrence-hook'. This seems to work just fine, and
;; I have the code written locally in list-jump-hook; I opened
;; https://github.com/flycheck/flycheck/issues/1874
(defcustom flycheck-error-list-after-jump-hook nil
"Functions to run after jumping to the error from the error list.
This hook is run after moving to the error. A possible idea
is to adjust the frame to bring the full context into view.
This variable is a normal hook. See Info node `(elisp)Hooks'."
:group 'flycheck
:type 'hook
:risky t
:package-version '(flycheck . "0.33"))
(add-hook 'flycheck-error-list-after-jump-hook 'recenter)
;; Of course, it means overwriting `flycheck-error-list-goto-error'
(defun flycheck-error-list-goto-error (&optional pos)
"Go to the location of the error at POS in the error list.
POS defaults to `point'."
(interactive)
(when-let* ((error (tabulated-list-get-id pos)))
(flycheck-jump-to-error error)))
;; flycheck-bashisms https://github.com/cuonglm/flycheck-checkbashisms
;; Ensure no bashisms in sh code, no shisms in bash code; mostly the former
(flycheck-checkbashisms-setup)
(setq flycheck-checkbashisms-posix t
flycheck-checkbashisms-newline t)
;; flycheck-relint https://github.com/purcell/flycheck-relint
;; Check elisp regexes with relint
(flycheck-relint-setup)
;; flycheck-relint assumes that emacs-lisp-checkdoc is enabled, so we need to
;; manually set the checker to follow emacs-lisp
(flycheck-add-next-checker 'emacs-lisp 'emacs-lisp-relint)
;; Enable flycheck-color-mode-line, with our custom face
;; https://github.com/flycheck/flycheck-color-mode-line
(add-hook 'flycheck-mode-hook 'flycheck-color-mode-line-mode)
(with-eval-after-load 'flycheck-color-mode-line
(setq flycheck-color-mode-line-face-to-color 'flycheck-mode-line-color-face
flycheck-color-mode-line-show-running t
)))
;; Annoyingly, flycheck doesn't automatically include proselint as a
;; next-checker for markdown mode (okay, fine, I guess it makes sense), so let's
;; just make it so. Clearly depends on the proselint executable (via pip,
;; macports, brew, etc). https://github.com/amperser/proselint/
;; Consider textlint as well? Less popular/fewer stars, but more actively developed
;; https://github.com/textlint/textlint
(flycheck-add-next-checker 'markdown-markdownlint-cli 'proselint)
;; flycheck-hl-todo: Show fixmes and todos in flycheck
;; https://github.com/alvarogonzalezsotillo/flycheck-hl-todo
;; Should really figure out hl-todo below before turning this on, the two are
;; connected TODO
;; (require 'flycheck-hl-todo)
;; Should create a toggle function to disable via variable
;; `flycheck-hl-todo-enabled' rather than using flycheck-disable-checker
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; relint: Check elisp regexes https://elpa.gnu.org/packages/relint.html
;; `relint-buffer' is a better name than `relint-current-buffer', but that's taken
(defalias 'relint-buffer-current 'relint-current-buffer)
;; Good place to note pcre2el, mainly via rxt-mode then C-c /
;; Maybe add hook to elisp and perl modes? TODO
;; https://github.com/joddie/pcre2el
;; editorconfig https://editorconfig.org/
;; https://github.com/editorconfig/editorconfig-emacs
(require 'editorconfig)
(editorconfig-mode 1)
;; Whitespace
(require 'whitespace)
;; Turn on globally, probably if better just for programming and text modes
;; (global-whitespace-mode t)
(add-hook 'prog-mode-hook 'whitespace-mode)
(add-hook 'text-mode-hook 'whitespace-mode)
;; Highlight empty lines, trailing whitespace, inappropriate indentation
;; trailing lines (lines or lines-tail) gets annoying, redundant to fci-mode
;; big-indent nice idea, but too quick to become an issue
(setq whitespace-style '(face empty trailing indentation space-before-tab space-after-tab))
;; Not needed in emacsen >= 23 because of lines-tail above
;; Auto-color lines over 80 in length .\{81\}
;; M-x highlight-lines-matching-regex or C-x w l
;; Unhighlight with unhighlight-regex or C-x w r
;; (add-hook 'emacs-lisp-mode-hook '(lambda () (highlight-lines-matching-regexp ".\\{81\\}" 'hi-green-b)))
;; (add-hook 'perl-mode-hook '(lambda () (highlight-lines-matching-regexp ".\\{81\\}" 'hi-red-b)))
;;; Highlight TODOs, FIXMEs, etc.
;; There are a lot of ways to do this. Previously I used fic-mode
;; (<https://github.com/lewang/fic-mode>) which is fairly bare-bones. It looks
;; like hl-todo (<hl-todo better? https://github.com/tarsius/hl-todo>) is better
;; (more customization options, ability to jump to each in turn), as is
;; hl-prog-extra, but, honestly, I kind of just like the simple method here. In
;; the end, that means something roughly at the level of fic-mode, but probably
;; less-well integrated. If I really need something more full-fledged, I can
;; just use hl-todo, with something like the below:
;; (setq hl-todo '(t (:foreground "#e55c7a" :weight normal)))
;; (setq hl-todo-wrap-movement t)
(setq hl-todo-keyword-faces ; FIXME TODO unused?
'(("TODO" . "pink")
("FIXME" . "#cc9393")
("XXX" . "#1E90FF")))
(defun my/add-watchwords ()
"Function to highlight specific watchwords, rather than use `fic-mode' or
`hl-todo' or something."
(font-lock-add-keywords
;; \\<, \\> are empty string at beginning, end of word
nil '(("\\<\\(FIXME\\|TODO\\|XXX\\)\\>"
;; 1 '((:inherit 'font-lock-keyword-face) (:weight bold)) t))))
;; 1 '((:foreground (face-foreground 'font-lock-keyword-face)) (:weight bold)) t))))
;; Want to inherit the foreground from font-lock-keyword-face, but not
;; sure how to get that! These don't work FIXME TODO
1 '((:foreground "pink") (:weight bold)) t))))
(add-hook 'prog-mode-hook 'my/add-watchwords)
(add-hook 'text-mode-hook 'my/add-watchwords)
;; FIXME
(font-lock-add-keywords 'emacs-lisp-mode '(("autoload" . font-lock-keyword-face)))
;; https://github.com/dgutov/highlight-escape-sequences
;; (require 'highlight-escape-sequences)
;; (setq hes-simple-modes '(emacs-lisp-mode))
;; (hes-mode)
;; Color for backslash and escapes in lisp regexps,
;; `font-lock-regexp-grouping-backslash' and
;; `font-lock-regexp-grouping-construct', set in custom.el. Wish cperl-mode had
;; something similar...
;; (require 'applescript-mode)
;; Make life easier, in all lisps
(define-key lisp-mode-shared-map (kbd "C-x e") 'eval-region)
(define-key lisp-mode-shared-map (kbd "C-x C-e") 'eval-buffer)
;; Use buffer name as window title for window-system
;; (setq frame-title-format "%b - emacs")
;; (setq frame-title-format '(buffer-file-name "%f" ("%b")))
;; (let ((name (assq 'name default-frame-alist)))
;; (when (consp name)
;; (setcdr name ())))
;; (modify-frame-parameters (selected-frame) '((name)))
;; Send buffer name to xterm directly to rename tabs, from:
;; https://www.emacswiki.org/emacs/FrameTitle#h5o-6
;; Behaves somewhat badly when multiple emacsclient instances are in use
(defun xterm-title-update ()
"Rename terminal tabs by sending the buffer name to xterm directly."
(interactive)
;; xterm escape sequences: https://tldp.org/HOWTO/Xterm-Title-3.html
;; 0: icon name and window title, 1 icon name, 2 window title
(send-string-to-terminal (concat "\033]1; " (buffer-name) "\007"))
(if buffer-file-name