-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathinit.el
2590 lines (2153 loc) · 113 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
;; [[file:init.org::#Personal-instructions-for-a-new-machine][Personal instructions for a new machine:4]]
(setq org-image-actual-width nil)
;; Personal instructions for a new machine:4 ends here
;; [[file:init.org::#Personal-instructions-for-a-new-machine][Personal instructions for a new machine:5]]
;; Clicking on a URL, or running M-x browse-url, should open the URL *within* Emacs.
(setq browse-url-browser-function #'xwidget-webkit-browse-url)
;; (use-package xwwp) ;; Enhance the Emacs xwidget-webkit browser
;; Personal instructions for a new machine:5 ends here
(setq custom-file "~/.emacs.d/custom.el")
(ignore-errors (load custom-file)) ;; It may not yet exist.
(setq user-full-name "Musa Al-hassy"
user-mail-address "[email protected]")
;; Make all commands of the “package” module present.
(require 'package)
;; Internet repositories for new packages.
(setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "http://melpa.org/packages/")))
;; Update local list of available packages:
;; Get descriptions of all configured ELPA packages,
;; and make them available for download.
(package-refresh-contents)
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
(use-package auto-package-update
:config
;; Delete residual old versions
(setq auto-package-update-delete-old-versions t)
;; Do not bother me when updates have taken place.
(setq auto-package-update-hide-results t)
;; Update installed packages at startup if there is an update pending.
(auto-package-update-maybe))
;; Making it easier to discover Emacs key presses.
(use-package which-key
:config (which-key-mode)
(which-key-setup-side-window-bottom)
(setq which-key-idle-delay 0.05))
(use-package dash) ;; “A modern list library for Emacs”
(use-package s) ;; “The long lost Emacs string manipulation library”.
(use-package f) ;; Library for working with system files; ;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden?
(defvar my/personal-machine?
(equal "Musa’s MacBook Air " (s-collapse-whitespace (shell-command-to-string "scutil --get ComputerName")))
"Is this my personal machine, or my work machine?
At one point, on my work machine I run the following command to give the machine a sensible name.
sudo scutil --set ComputerName work-machine
dscacheutil -flushcache")
(defvar my/work-machine? (not my/personal-machine?))
;; Allow tree-semantics for undo operations.
(use-package undo-tree
:bind ("C-x u" . undo-tree-visualize)
:config
;; Each node in the undo tree should have a timestamp.
(setq undo-tree-visualizer-timestamps t)
;; Show a diff window displaying changes between undo nodes.
(setq undo-tree-visualizer-diff t)
;; Prevent undo tree files from polluting your git repo
(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))))
;; Always have it on
(global-undo-tree-mode)
;; Execute (undo-tree-visualize) then navigate along the tree to witness
;; changes being made to your file live!
(use-package quelpa
:custom (quelpa-upgrade-p t "Always try to update packages")
:config
;; Get ‘quelpa-use-package’ via ‘quelpa’
(quelpa
'(quelpa-use-package
:fetcher git
:url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package))
;; Auto installing OS system packages
(use-package system-packages)
;; Ensure our operating system is always up to date.
;; This is run whenever we open Emacs & so wont take long if we're up to date.
;; It happens in the background ^_^
;;
;; After 5 seconds of being idle, after starting up.
(run-with-idle-timer 5 nil #'system-packages-update) ;; ≈ (async-shell-command "brew update && brew upgrade")
(defvar my/installed-packages
(shell-command-to-string "brew list")
"What is on my machine already?
Sometimes when I install a GUI based application and do not have access to it everywhere in my path,
it may seem that I do not have that application installed. For instance,
(system-packages-package-installed-p \"google-chrome\")
returns nil, even though Google Chrome is on my machine.
As such, we advise the `system-packages-ensure' installtion method to only do
installs of packages that are not in our `my/installed-packages' listing.
")
(advice-add 'system-packages-ensure :before-until (lambda (pkg) (s-contains-p pkg my/installed-packages)))
;; Please don't bother me when shell buffer names are in use, just make a new buffer.
(setq async-shell-command-buffer 'new-buffer)
;; Display the output buffer for asynchronous shell commands only when the
;; command generates output.
(setq async-shell-command-display-buffer nil)
;; Don't ask me if I want to kill a buffer with a live process attached to it;
;; just kill it please.
(setq kill-buffer-query-functions
(remq 'process-kill-buffer-query-function
kill-buffer-query-functions))
;; An Emacs-based interface to the package manager of your operating system.
(use-package helm-system-packages)
(setq system-packages-noconfirm :do-not-prompt-me-about-confirms)
;; After 1 minute after startup, kill all buffers created by ensuring system
;; packages are present.
(run-with-timer 60 nil
(lambda () (kill-matching-buffers ".*system-packages.*" t :kill-without-confirmation)))
;; Unlike the Helm variant, we need to specify our OS pacman.
(setq system-packages-package-manager 'brew)
;; If the given system package doesn't exist; install it.
;; (system-packages-ensure "amethyst") ;; This is a MacOS specific package.
;; (ignore-errors (system-packages-ensure "google-chrome")) ;; My choice of web browser
;; (system-packages-ensure "microsoft-teams") ;; For remote work meetings
;; Gif maker; needs privileges to capture screen.
;;
;; ⇒ Move the screen capture frame while recording.
;; ⇒ Pause and restart recording, with optional inserted text messages.
;; ⇒ Global hotkey (shift+space) to toggle pausing while recording
(system-packages-ensure "licecap") ;; Use: ⌘-SPACE licecap
;; Pack, ship and run any application as a lightweight container
;; (system-packages-ensure "docker")
;; Free universal database tool and SQL client
;; (system-packages-ensure "dbeaver-community")
;; Kubernetes IDE
;; (system-packages-ensure "lens")
;; Platform built on V8 to build network applications
;; Also known as: node.js, node@16, nodejs, npm
(system-packages-ensure "node") ;; https://nodejs.org/
;; Nice: https://nodesource.com/blog/an-absolute-beginners-guide-to-using-npm/
;; Manage multiple Node.js versions
;; (shell-command "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash")
;; According to https://github.com/nvm-sh/nvm, nvm shouldn't be installed via brew.
;; ;; Use “brew cask install” instead of “brew install” for installing programs.;
;; (setf (nth 2 (assoc 'brew system-packages-supported-package-managers))
;; '(install . "brew cask install"))
;; By default, say, (async-shell-command "date") produces a buffer
;; with the result. In general, such commands in my init.el are for
;; updating/installing things to make sure I have the same up-to-date
;; setup where-ever I use my Emacs. As such, I don't need to see such buffers.
(add-to-list 'display-buffer-alist
'("\\*Async Shell Command\\*.*" display-buffer-no-window))
;; For an approach that does not inhibit async-shell-command this way,
;; see https://emacs.stackexchange.com/questions/299/how-can-i-run-an-async-process-in-the-background-without-popping-up-a-buffer
(use-package exec-path-from-shell
:init
(when (memq window-system '(mac ns x))
(exec-path-from-shell-initialize)))
;; Provides only the command “restart-emacs”.
(use-package restart-emacs
;; If I ever close Emacs, it's likely because I want to restart it.
:bind ("C-x C-c" . restart-emacs)
;; Let's define an alias so there's no need to remember the order.
:config (defalias 'emacs-restart #'restart-emacs))
(setq-default save-place t)
(setq save-place-file "~/.emacs.d/etc/saveplace")
(use-package helm
:init (helm-mode t)
:bind (("M-x" . helm-M-x)
("C-x C-f" . helm-find-files)
("C-x b" . helm-mini) ;; See buffers & recent files; more useful.
("C-x r b" . helm-filtered-bookmarks)
("C-x C-r" . helm-recentf) ;; Search for recently edited files
("C-c i" . helm-imenu) ;; C.f. “C-x t m” (imenu-list)
;; ("C-u C-c i" . imenu-list) ;; TODO FIXME Key sequence C-u C-c i starts with non-prefix key C-u
("C-h a" . helm-apropos)
;; Look at what was cut recently & paste it in.
("M-y" . helm-show-kill-ring)
("C-x C-x" . helm-all-mark-rings)
:map helm-map
;; We can list ‘actions’ on the currently selected item by C-z.
("C-z" . helm-select-action)
;; Let's keep tab-completetion anyhow.
("TAB" . helm-execute-persistent-action)
("<tab>" . helm-execute-persistent-action)))
;; Show me nice file icons when using, say, “C-x C-f” or “C-x b”
;; (use-package helm-icons
;; :custom (helm-icons-provider 'all-the-icons)
;; :config (helm-icons-enable))
;; When I want to see the TOC of an Org file, show me down to 3 subheadings.
(setq org-imenu-depth 7)
(setq helm-mini-default-sources '(helm-source-buffers-list
helm-source-recentf
helm-source-bookmarks
helm-source-bookmark-set
helm-source-buffer-not-found))
(use-package helm-swoop
:bind (("C-s" . 'helm-swoop) ;; search current buffer
("C-M-s" . 'helm-multi-swoop-all) ;; Search all buffer
;; Go back to last position where ‘helm-swoop’ was called
("C-S-s" . 'helm-swoop-back-to-last-point)
;; swoop doesn't work with PDFs, use Emacs' default isearch instead.
; :map pdf-view-mode-map ("C-s" . isearch-forward)
)
:custom (helm-swoop-speed-or-color nil "Give up colour for speed.")
(helm-swoop-split-with-multiple-windows nil "Do not split window inside the current window."))
(system-packages-ensure "ag")
;; Save/mark a location with “C-u M-m”, jump back to it with “M-m”.
(bind-key* "M-m"
(lambda ()
(interactive)
(if (not current-prefix-arg)
(helm-mark-ring)
(push-mark)
(message "[To return to this location, press M-m] ∷ %s"
(s-trim (substring-no-properties (thing-at-point 'line)))))))
(use-package emacs
:ensure org-contrib
:config (require 'ox-extra)
(ox-extras-activate '(ignore-headlines)))
;; Replace the content marker, “⋯”, with a nice unicode arrow.
(setq org-ellipsis " ⮛")
;; Other candidates:
;; (setq org-ellipsis " 📖")
;; (setq org-ellipsis " ◦◦◦")
;; (setq org-ellipsis " ⟨🫣⟩")
;; (setq org-ellipsis " ⟨👀⟩")
;; (setq org-ellipsis " ⤵")
;; Fold all source blocks on startup.
(setq org-hide-block-startup t)
;; Lists may be labelled with letters.
(setq org-list-allow-alphabetical t)
;; Avoid accidentally editing folded regions, say by adding text after an Org “⋯”.
(setq org-catch-invisible-edits 'show)
;; I use indentation-sensitive programming languages.
;; Tangling should preserve my indentation.
(setq org-src-preserve-indentation t)
;; Tab should do indent in code blocks
(setq org-src-tab-acts-natively t)
;; Give quote and verse blocks a nice look.
(setq org-fontify-quote-and-verse-blocks t)
;; Pressing ENTER on a link should follow it.
(setq org-return-follows-link t)
(setq initial-major-mode 'org-mode)
(defun org-special-block-extras-short-names ())
;;
;; org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks
(setq o--supported-blocks nil)
;; TODO org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks
;;
(use-package org-special-block-extras
:hook (org-mode . org-special-block-extras-mode)
:custom
;; The places where I keep my ‘#+documentation’
(org-special-block-extras--docs-libraries
'("~/org-special-block-extras/documentation.org"))
;; Disable the in-Emacs fancy-links feature?
(org-special-block-extras-fancy-links
'(elisp badge kbd link-here doc tweet))
;; Details heading “flash pink” whenever the user hovers over them?
(org-html-head-extra (concat org-html-head-extra "<style> summary:hover {background:pink;} </style>"))
;; The message prefixing a ‘tweet:url’ badge
(org-special-block-extras-link-twitter-excitement
"This looks super neat (•̀ᴗ•́)و:")
:config
;; Use short names like ‘defblock’ instead of the fully qualified name
;; ‘org-special-block-extras--defblock’
(org-special-block-extras-short-names))
;; Let's execute Lisp code with links, as in “elisp:view-hello-file”.
(setq org-confirm-elisp-link-function nil)
;; Invoke all possible key extensions having a common prefix by
;; supplying the prefix only once.
(use-package hydra)
;; Show hydras overlayed in the middle of the frame
(use-package hydra-posframe
:disabled "TODO Fix me, breaking Github Actions test setup"
:quelpa (hydra-posframe :fetcher git :url
"https://github.com/Ladicle/hydra-posframe.git")
:hook (after-init . hydra-posframe-mode)
:custom (hydra-posframe-border-width 5))
;; Neato doc strings for hydras
(use-package pretty-hydra)
;; TODO convert my existing defhydras to my/defhydra.
(defmacro my/defhydra (key title icon-name &rest body)
"Make a hydra whose heads appear in a pretty pop-up window.
Heads are signalled by keywords and the hydra has an icon in its title.
KEY [String]: Global keybinding for the new hydra.
TITLE [String]: Either a string or a plist, as specified for pretty-hydra-define.
The underlying Lisp function's name is derived from the TITLE;
which is intentional since hydra's are for interactive, pretty, use.
One uses a plist TITLE to specify what a hydra should do *before*
any options, or to specify an alternate quit key (:q by default).
ICON-NAME [Symbol]: Possible FontAwesome icon-types: C-h v `all-the-icons-data/fa-icon-alist'.
BODY: A list of columns and entries. Keywords indicate the title
of a column; 3-lists (triples) indicate an entry key and
the associated operation to perform and, optionally, a name
to be shown in the pop-up. See DEFHYDRA for more details.
For instance, the verbose mess:
;; Use ijkl to denote ↑←↓→ arrows.
(global-set-key
(kbd \"C-c w\")
(pretty-hydra-define my/hydra/\\t\\tWindow\\ Adjustment
;; Omitting extra work to get an icon into the title.
(:title \"\t\tWindow Adjustment\" :quit-key \"q\")
(\"Both\"
((\"b\" balance-windows \"balance\")
(\"s\" switch-window-then-swap-buffer \"swap\"))
\"Vertical adjustment\"
((\"h\" enlarge-window \"heighten\")
(\"l\" shrink-window \"lower\"))
\"Horizontal adjustment\"
((\"n\" shrink-window-horizontally \"narrow\")
(\"w\" enlarge-window-horizontally \"widen\" )))))
Is replaced by:
;; Use ijkl to denote ↑←↓→ arrows.
(my/defhydra \"C-c w\" \"\t\tWindow Adjustment\" windows
:Both
(\"b\" balance-windows \"balance\")
(\"s\" switch-window-then-swap-buffer \"swap\")
:Vertical_adjustment
(\"h\" enlarge-window \"heighten\")
(\"l\" shrink-window \"lower\")
:Horizontal_adjustment
(\"n\" shrink-window-horizontally \"narrow\")
(\"w\" enlarge-window-horizontally \"widen\"))"
(let* ((name (intern (concat "my/hydra/"
(if (stringp title)
title
(plist-get title :title)))))
(icon-face `(:foreground ,(face-background 'highlight)))
(iconised-title
(concat
(when icon-name
(require 'all-the-icons)
(concat
(all-the-icons-faicon (format "%s" icon-name) :face icon-face :height 1.0 :v-adjust -0.1)
" "))
(propertize title 'face icon-face))))
`(global-set-key
(kbd ,key)
(pretty-hydra-define ,name
,(if (stringp title)
(list :title iconised-title
:quit-key "q")
title)
,(thread-last body
(-partition-by-header #'keywordp)
(--map (cons (s-replace "_" " " (s-chop-prefix ":" (symbol-name (car it)))) (list (cdr it))))
(-flatten-n 1))))))
;; C-n, next line, inserts newlines when at the end of the buffer
(setq next-line-add-newlines t)
;; Use ijkl to denote ↑←↓→ arrows.
(my/defhydra "C-c w" "\t\tWindow Adjustment" windows
:Both
("b" balance-windows "balance")
("s" switch-window-then-swap-buffer "swap")
:Vertical_adjustment
("h" enlarge-window "heighten")
("l" shrink-window "lower")
:Horizontal_adjustment
("n" shrink-window-horizontally "narrow")
("w" enlarge-window-horizontally "widen"))
;; Provides a *visual* way to choose a window to switch to.
;; (use-package switch-window )
;; :bind (("C-x o" . switch-window)
;; ("C-x w" . switch-window-then-swap-buffer))
;; Have a thick ruler between vertical windows
(window-divider-mode)
;; change all prompts to y or n
(fset 'yes-or-no-p 'y-or-n-p)
;; Make RETURN key act the same way as “y” key for “y-or-n” prompts.
;; E.g., (y-or-n-p "Happy?") accepts RETURN as “yes”.
(define-key y-or-n-p-map [return] 'act)
;; Enable all ‘possibly confusing commands’ such as helpful but
;; initially-worrisome “narrow-to-region”, C-x n n.
(setq-default disabled-command-function nil)
(use-package vterm) ;; Shell with a nearly universal compatibility with terminal applications 💝
;; "Intelligent" switching to vterm; eg creates it if it's not open, non-intrusive windowing, saves window setup, etc.
(use-package vterm-toggle
:bind* ("C-t" . vterm-toggle))
;; Be default, Emacs please use zsh
;; E.g., M-x shell
(unless noninteractive (setq shell-file-name "/bin/zsh"))
(system-packages-ensure "tldr")
(system-packages-ensure "hr") ;; ≈ brew install hr
;; Usage: M-x helm-shell-history
(use-package helm-shell-history
:config
(setq helm-shell-history-file "~/.zsh_history")
(bind-key "M-r" #'helm-shell-history shell-mode-map))
;; MacOS's default ⌘-SPC does not let us do either of the following scenarios:
;; Usage: M-x helm-osx-app RET preferences bat RET ⇒ See battery preferences settings
;; Another Usage: M-x helm-osx-app RET ⇒ See all apps, maybe we forgot about one of them from an install a long time ago, and open it
;; See https://www.alfredapp.com/ as an alternative (for non-Emacs users), which can do more.
(use-package helm-osx-app)
;; For non-MacOS, we can use [[https://github.com/d12frosted/counsel-osx-app][counsel-osx-app]], whose name is misleading.
;; [[file:init.org::#Manipulating-Sections][Manipulating Sections:1]]
(setq org-use-speed-commands t)
;; Manipulating Sections:1 ends here
;; [[file:init.org::#Manipulating-Sections][Manipulating Sections:2]]
;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful.
;; When I'm refiling my TODOS, then give me all the freedom.
(setq org-refile-targets '((nil :maxlevel . 1)
(org-agenda-files :maxlevel . 9)))
;; Maybe I want to refile into a new heading; confirm with me.
(setq org-refile-allow-creating-parent-nodes 'confirm)
;; Use full outline paths for refile targets
;; When refiling, using Helm, show me the hierarchy paths
(setq org-outline-path-complete-in-steps nil)
(setq org-refile-use-outline-path 'file-path)
;; Manipulating Sections:2 ends here
;; [[file:init.org::#Manipulating-Sections][Manipulating Sections:3]]
;; TODO FIXME Crashes upon startup.
(when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property)))
;; Use ‘:’ and ‘e’ to set tags and effort, respectively.
;; Manipulating Sections:3 ends here
;; [[file:init.org::#Seamless-Navigation-Between-Source-Blocks][Seamless Navigation Between Source Blocks:1]]
;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill.
;;
(require 'org)
(use-package emacs
:bind (:map org-mode-map
("s-p" . org-babel-previous-src-block)
("s-n" . org-babel-next-src-block)
("s-e" . org-edit-special)
:map org-src-mode-map
("s-e" . org-edit-src-exit)))
;; Seamless Navigation Between Source Blocks:1 ends here
;; [[file:init.org::#Modifying-return][Modifying [[kbd:⟨return⟩]]:1]]
(add-hook 'org-mode-hook '(lambda ()
(local-set-key (kbd "<return>") 'org-return-indent))
(local-set-key (kbd "C-M-<return>") 'electric-indent-just-newline))
;; Modifying [[kbd:⟨return⟩]]:1 ends here
;; [[file:init.org::#Executing-code-from-src-blocks][Executing code from ~src~ blocks:1]]
;; Seamless use of babel: No confirmation upon execution.
;; Downside: Could accidentally evaluate harmful code.
(setq org-confirm-babel-evaluate nil)
;; Never evaluate code blocks upon export and replace results when evaluation does occur.
;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’.
(setq org-babel-default-header-args
'((:results . "replace")
(:session . "none")
(:exports . "both")
(:cache . "no")
(:noweb . "no")
(:hlines . "no")
(:tangle . "no")
(:eval . "never-export")))
;; Executing code from ~src~ blocks:1 ends here
;; [[file:init.org::#Executing-code-from-src-blocks][Executing code from ~src~ blocks:2]]
(defvar my/programming-languages
'(emacs-lisp shell python haskell
;; rust ;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust
ruby ocaml dot latex org js css
sqlite C) ;; Captial “C” gives access to C, C++, D
"List of languages I have used in Org-mode, for literate programming.")
;; Load all the languagues
;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust
(ignore-errors (cl-loop for lang in my/programming-languages
do (require (intern (format "ob-%s" lang)))))
;;
(org-babel-do-load-languages
'org-babel-load-languages
(--map (cons it t) my/programming-languages))
;; Preserve my indentation for source code during export.
(setq org-src-preserve-indentation t)
;; The export process hangs Emacs, let's avoid this.
;; MA: For one reason or another, this crashes more than I'd like.
;; (setq org-export-in-background t)
;; Executing code from ~src~ blocks:2 ends here
;; [[file:init.org::#Executing-all-name-startup-code-for-local-configurations][Executing all =#+name: startup-code= for local configurations:1]]
(defun my/execute-startup-blocks ()
"Execute all startup blocks, those named ‘startup-code’.
I could not use ORG-BABEL-GOTO-NAMED-SRC-BLOCK since it only goes
to the first source block with the given name, whereas I'd like to
visit all blocks with such a name."
(interactive)
(save-excursion
(goto-char 0)
(while (ignore-errors (re-search-forward "^\\#\\+name: startup-code"))
(org-babel-execute-src-block))))
;; Executing all =#+name: startup-code= for local configurations:1 ends here
;; [[file:init.org::#Executing-all-name-startup-code-for-local-configurations][Executing all =#+name: startup-code= for local configurations:2]]
;; Please ask me on a file by file basis whether its local variables are ‘safe’
;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried
;; again for the same file.
(setq enable-local-variables t)
;; Executing all =#+name: startup-code= for local configurations:2 ends here
;; [[file:init.org::#Prettify-inline-source-code][Prettify inline source code:1]]
;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”.
;;
(font-lock-add-keywords 'org-mode
'(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)"
(1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾"))
(2 '(face (:foreground "blue")))
(3 '(face (:inherit (bold) :foreground "gray65") display "﴿"))
)))
;;
;; Let's do this for all my languages:
;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”.
(cl-loop for lang in my/programming-languages
do (font-lock-add-keywords 'org-mode
`(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang)
(1 '(face (:inherit (bold) :foreground "gray65") display "﴾"))
(2 '(face (:foreground "blue")))
(3 '(face (:inherit (bold) :foreground "gray65") display "﴿"))
))))
;;
(defun my/toggle-line-fontification ()
"Toggle the fontification of the current line"
(interactive)
(defvar my/toggle-fontify/current-line -1)
(defvar my/toggle-fontify/on? nil)
(add-to-list 'font-lock-extra-managed-props 'display)
(let ((start (line-beginning-position)) (end (line-end-position)))
(cond
;; Are we toggling the current line?
((= (line-number-at-pos) my/toggle-fontify/current-line)
(if my/toggle-fontify/on?
(font-lock-fontify-region start end)
(font-lock-unfontify-region start end))
(setq my/toggle-fontify/on? (not my/toggle-fontify/on?)))
;; Nope, we've moved on to another line.
(:otherwise
(setq my/toggle-fontify/current-line (line-number-at-pos)
my/toggle-fontify/on? :yes_please_fontify)
(font-lock-unfontify-region start end)))))
;; TODO FIXME; maybe ignore: Wasted too much time here already.
;; (add-hook 'post-command-hook #'my/toggle-line-fontification nil t)
;; (font-lock-add-keywords nil '((my/toggle-line-fontification)) t)
;; Prettify inline source code:1 ends here
;; [[file:init.org::*Unfold Org Headings when I perform a search][Unfold Org Headings when I perform a search:1]]
(setq org-fold-core-style 'overlays)
;; Unfold Org Headings when I perform a search:1 ends here
;; [[file:init.org::*The “∶Disabled∶” tag ---Stolen from AlBasmala.el, and improved][The “∶Disabled∶” tag ---Stolen from AlBasmala.el, and improved:1]]
(defmacro org-deftag (name args docstring &rest body)
"Re-render an Org section in any way you like, by tagging the section with NAME.
That is to say, we essentially treat tags as functions that act on Org headings:
We redefine Org sections for the same purposes as Org special blocks.
The “arguments” to the function-tag can be declared as Org properties, then
the function can access them using the `o-properties' keyword as in
(-let [(&plist :file :date :color) o-properties]
(insert \"%s: %s\" file date))
Anyhow:
ARGS are the sequence of items seperated by underscores after the NAME of the new tag.
BODY is a form that may anaphorically mention:
- O-BACKEND: The backend we are exporting to, such as `latex' or `html'.
- O-HEADING: The string denoting the title of the tagged section heading.
- O-PROPERTIES: A plist of the Org properties at point.
DOCSTRING is mandatory; everything should be documented for future maintainability.
The result of this anaphoric macro is a symbolic function name `org-deftag/NAME',
which is added to `org-export-before-parsing-hook'.
----------------------------------------------------------------------
Below is the motivating reason for inventing this macro. It is used:
** Interesting, but low-priority, content :details_red:
Blah blah blah blah blah blah blah blah blah blah blah.
Blah blah blah blah blah blah blah blah blah blah blah.
Here is the actual implementation:
(org-deftag details (color)
\"HTML export a heading as if it were a <details> block; COLOR is an optional
argument indicating the background colour of the resulting block.\"
(insert \"\n#+html:\"
(format \"<details style=\\\"background-color: %s\\\">\" color)
\"<summary>\" (s-replace-regexp \"^\** \" \"\" o-heading) \"</summary>\")
(org-next-visible-heading 1)
(insert \"#+html: </details>\"))
"
(let ((func-name (intern (format "org-deftag/%s" name))))
`(progn
(cl-defun ,func-name (o-backend)
,docstring
(outline-show-all)
(org-map-entries
(lambda ()
(-let [(&alist ,@ (mapcar #'symbol-name args)) (map-apply (lambda (k v) (cons (downcase k) v)) (org-entry-properties (point)))]
;; MA: Maybe get rid of o-heading and o-properties and let people operate on raw Org secitons
;; as they do with org-agenda. That might provide a more unified approach.
(let ((o-properties (map-into (map-apply (lambda (k v) (cons (intern (concat ":" (downcase k))) v)) (org-entry-properties (point))) 'plist))
(o-heading (progn (kill-line) (car kill-ring))))
(if (not (s-contains? (format ":%s" (quote ,name)) o-heading 'ignoring-case))
(insert o-heading)
(setq o-heading (s-replace-regexp (format ":%s[^:]*:" (quote ,name)) "" o-heading))
,@body)
;; Otherwise we impede on the auto-inserted “* footer :ignore:”
(insert "\n"))))))
(add-hook 'org-export-before-parsing-hook (quote ,func-name))
)))
;; MA: This is new stuff.
(put 'org-deflink 'lisp-indent-function 'defun)
(put 'org-deftag 'lisp-indent-function 'defun)
;; Example use
(org-deftag identity ()
"Do nothing to Org headings"
(insert o-heading)) ;; Wait, I think this strips tags?
(org-deftag disabled (color)
"Render the body of a heading in a <details> element, titled “Disabled”.
The heading remains in view, and so appears in the TOC."
(insert "\n") (insert o-heading) (insert "\n")
(insert "\n#+html:"
(format "<div> <details class=\"float-child\" style=\"background-color: %s\">"
(or color "pink"))
"<summary> <strong> <font face=\"Courier\" size=\"3\" color=\"green\">"
"Details ﴾This is disabled, I'm not actively using it.﴿"
"</font> </strong> </summary>")
;; Something to consider: (org-set-property "UNNUMBERED" "nil")
(org-next-visible-heading 1)
(insert "#+html: </details> </div>"))
;; The “∶Disabled∶” tag ---Stolen from AlBasmala.el, and improved:1 ends here
;; [[file:init.org::#Jumping-to-extreme-semantic-units][Jumping to extreme semantic units:1]]
;; M-< and M-> jump to first and final semantic units.
;; If pressed twice, they go to physical first and last positions.
(use-package beginend
:config (beginend-global-mode))
;; Jumping to extreme semantic units:1 ends here
;; [[file:init.org::#Folding-within-a-subtree][Folding within a subtree:1]]
(bind-key "C-c C-h"
(defun my/org-fold-current-subtree-anywhere-in-it ()
(interactive)
(save-excursion (save-restriction
(org-narrow-to-subtree)
(org-shifttab)
(widen))))
org-mode-map)
;; Folding within a subtree:1 ends here
;; [[file:init.org::#Draw-pretty-unicode-tables-in-org-mode][Draw pretty unicode tables in org-mode:1]]
(quelpa '(org-pretty-table
:repo "Fuco1/org-pretty-table"
:fetcher github))
(add-hook 'org-mode-hook 'org-pretty-table-mode)
;; Draw pretty unicode tables in org-mode:1 ends here
;; [[file:init.org::#Buffer-defaults][Buffer default mode is org-mode:1]]
(setq-default major-mode 'org-mode)
;; Buffer default mode is org-mode:1 ends here
;; [[file:init.org::#Use-Org-Mode-links-in-other-modes-Links-can-be-opened-and-edited-like-in-Org-Mode][Use Org Mode links in other modes: Links can be opened and edited like in Org Mode.:1]]
;; E.g., in ELisp mode, the following is clickable and looks nice: [[info:man][Read the docs!]]
;;
;; In particular, when I tangle my init.org into a Lisp file, init.el, it has Org links
;; back to the original source section in Org, which I can then click to jump to, quickly.
;;
(use-package orglink
:config
(global-orglink-mode)
;; Only enable this in Emacs Lisp mode, for now.
(setq orglink-activate-in-modes '(emacs-lisp-mode)))
;; Use Org Mode links in other modes: Links can be opened and edited like in Org Mode.:1 ends here
;; [[file:init.org::*No code evaluation upon export][No code evaluation upon export:1]]
;; Ignore all header arguments relating to “:eval”. Do not evaluate code when I export to HTML or LaTeX or anything else.
(setq org-export-use-babel nil)
;; No code evaluation upon export:1 ends here
;; [[file:init.org::#Undo-tree-Very-Local-Version-Control][Undo-tree: Very Local Version Control:2]]
;; By default C-z is suspend-frame, i.e., minimise, which I seldom use.
(global-set-key (kbd "C-z")
(lambda () (interactive)
(undo-tree-mode) ;; Ensure the mode is on
(undo-tree-visualize)))
;; Undo-tree: Very Local Version Control:2 ends here
;; [[file:init.org::#Automatic-Backups][Automatic Backups:1]]
;; New location for backups.
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
;; Silently delete execess backup versions
(setq delete-old-versions t)
;; Only keep the last 1000 backups of a file.
(setq kept-old-versions 1000)
;; Even version controlled files get to be backed up.
(setq vc-make-backup-files t)
;; Use version numbers for backup files.
(setq version-control t)
;; Automatic Backups:1 ends here
;; [[file:init.org::#Automatic-Backups][Automatic Backups:2]]
(setq confirm-kill-processes nil
create-lockfiles nil)
;; Automatic Backups:2 ends here
;; [[file:init.org::#What-changed][What changed? ---Walking through backups:1]]
(use-package backup-walker
:commands backup-walker-start)
;; What changed? ---Walking through backups:1 ends here
;; [[file:init.org::#Save-Backup][Save ≈ Backup:1]]
;; Make Emacs backup everytime I save
(defun my/force-backup-of-buffer ()
"Lie to Emacs, telling it the curent buffer has yet to be backed up."
(setq buffer-backed-up nil))
(add-hook 'before-save-hook 'my/force-backup-of-buffer)
;; [Default settings]
;; Autosave when idle for 30sec or 300 input events performed
(setq auto-save-timeout 30
auto-save-interval 300)
;; Save ≈ Backup:1 ends here
;; [[file:init.org::#delete-by-moving-to-trash-t][delete-by-moving-to-trash t:1]]
;; Move to OS’ trash can when deleting stuff
;; instead of deleting things outright!
(setq delete-by-moving-to-trash t
trash-directory "~/.Trash/")
;; delete-by-moving-to-trash t:1 ends here
;; [[file:init.org::*Intro][Intro:1]]
;; Bottom of Emacs will show what branch you're on
;; and whether the local file is modified or not.
(use-package magit
:bind (("C-c M-g" . magit-file-dispatch))
:config (global-set-key (kbd "C-x g") 'magit-status)
:custom ;; Do not ask about this variable when cloning.
(magit-clone-set-remote.pushDefault t))
;; Intro:1 ends here
;; [[file:init.org::*Intro][Intro:2]]
;; When we invoke magit-status, show green/red the altered lines, with extra
;; green/red on the subparts of a line that got alerted.
(system-packages-ensure "git-delta")
(use-package magit-delta
:hook (magit-mode . magit-delta-mode))
;; Don't forget to copy/paste the delta config into the global ~/.gitconfig file.
;; Copy/paste this: https://github.com/dandavison/delta#get-started
;; Intro:2 ends here
;; [[file:init.org::#Credentials-I-am-who-I-am][Credentials: I am who I am:1]]
;; Only set these creds up if there is no Git email set up ---ie at work I have an email set up, so don't
;; override it with my personal creds.
;;
;; See here for a short & useful tutorial:
;; https://alvinalexander.com/git/git-show-change-username-email-address
(when (equal "" (shell-command-to-string "git config user.email "))
(shell-command (format "git config --global user.name \"%s\"" user-full-name))
(shell-command (format "git config --global user.email \"%s\"" user-mail-address)))
;; Credentials: I am who I am:1 ends here
;; [[file:init.org::#Credentials-I-am-who-I-am][Credentials: I am who I am:2]]
;; We want to reuse an existing Emacs process from the command line
;; E.g., emacsclient --eval '(+ 1 2)' # ⇒ 3
(server-start)
;; Or use it whenever we are editing a git message from the terminal
(shell-command "git config --global core.editor 'emacsclient -t -a=\\\"\\\"'")
;; Credentials: I am who I am:2 ends here
;; [[file:init.org::#Encouraging-useful-commit-messages][Encouraging useful commit messages:1]]
(defun my/git-commit-reminder ()
(insert "\n\n# The commit subject line ought to finish the phrase:
# “If applied, this commit will ⟪your subject line here⟫.” ")
(beginning-of-buffer))
(add-hook 'git-commit-setup-hook 'my/git-commit-reminder)
;; Encouraging useful commit messages:1 ends here
;; [[file:init.org::#Maybe-clone-everything][Maybe clone ... everything?:1]]
;; Clone git repo from clipboard
(cl-defun maybe-clone (remote &optional local)
"Clone a REMOTE repository [from clipboard] if the LOCAL directory does not exist.
If called interactively, clone URL in clipboard into ~/Downloads then open in dired.
Yields ‘repo-already-exists’ when no cloning transpires, otherwise yields ‘cloned-repo’.
LOCAL is optional and defaults to the base name; e.g.,
if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y."
(interactive "P")
(when (interactive-p)
(setq remote (substring-no-properties (current-kill 0)))
(cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" remote) nil "No URL in clipboard"))
(unless local
(setq local (concat "~/" (if (interactive-p) "Downloads/" "") (file-name-base remote))))
;; (require 'magit-repos) ;; Gets us the magit-repository-directories variable.
;; (add-to-list 'magit-repository-directories `(,local . 0))
(if (file-directory-p local)
'repo-already-exists
(shell-command (concat "git clone " remote " " local))
(dired local)
'cloned-repo))
(maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d")
(maybe-clone "https://github.com/alhassy/alhassy.github.io" "~/blog")
;; (maybe-clone "https://github.com/alhassy/holy-books")
;; Maybe clone ... everything?:1 ends here
;; [[file:init.org::#Maybe-clone-everything][Maybe clone ... everything?:2]]
;; (maybe-clone "https://github.com/alhassy/melpa")
(maybe-clone "https://github.com/alhassy/org-special-block-extras")
;; (maybe-clone "https://github.com/alhassy/next-700-module-systems-proposal.git" "~/thesis-proposal")
;; (maybe-clone "https://github.com/JacquesCarette/MathScheme")
;; (maybe-clone "https://github.com/alhassy/gentle-intro-to-reflection" "~/reflection/")
;; (maybe-clone "https://github.com/alhassy/org-agda-mode")
;; (maybe-clone "https://github.com/JacquesCarette/TheoriesAndDataStructures")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/RATH/RATH-Agda" "~/RATH-Agda")
;; (maybe-clone "https://github.com/alhassy/MyUnicodeSymbols") ;; Deleted?
(maybe-clone "https://github.com/alhassy/islam")
;; (maybe-clone "https://github.com/alhassy/CheatSheet")
;; (maybe-clone "https://github.com/alhassy/ElispCheatSheet")
;; (maybe-clone "https://github.com/alhassy/CatsCheatSheet")
;; (maybe-clone "https://github.com/alhassy/OCamlCheatSheet")
;; (maybe-clone "https://github.com/alhassy/AgdaCheatSheet")
;; (maybe-clone "https://github.com/alhassy/RubyCheatSheet")
;; (maybe-clone "https://github.com/alhassy/PrologCheatSheet")
;; (maybe-clone "https://github.com/alhassy/FSharpCheatSheet")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/armstmp/cs3mi3.git" "~/3mi3")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/CAS781" "~/cas781") ;; cat adventures
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/carette/cs3fp3.git" "~/3fp3")
;; (maybe-clone "https://github.com/alhassy/interactive-way-to-c")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-distribution.git" "~/3ea3/assignment-distribution")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/notes.git" "~/3ea3/notes")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-development.git" "~/3ea3/assignment-development")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/kandeeps.git" "~/3ea3/sujan")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/horsmane.git" "~/3ea3/emily")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/anderj12.git" "~/3ea3/jacob")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/3EA3.git" "~/3ea3/_2018")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/2DM3/LectureNotes.git" "~/2dm3")
;; Maybe clone ... everything?:2 ends here
;; [[file:init.org::#Gotta-love-that-time-machine][Gotta love that time machine:1]]
(use-package git-timemachine )
;; Gotta love that time machine:1 ends here
;; [[file:init.org::#Pretty-Magit-Commit-Leaders][Pretty Magit Commit Leaders:1]]
(cl-defmacro pretty-magit (WORD ICON PROPS &optional (description "") NO-PROMPT?)
"Replace sanitized WORD with ICON, PROPS and by default add to prompts."
`(prog1
(add-to-list 'pretty-magit-alist
(list (rx bow (group ,WORD (eval (if ,NO-PROMPT? "" ":"))))
,ICON ',PROPS))
(unless ,NO-PROMPT?
(add-to-list 'pretty-magit-prompt (cons (concat ,WORD ": ") ,description)))))
(setq pretty-magit-alist nil)
(setq pretty-magit-prompt nil)
;; Pretty Magit Commit Leaders:1 ends here
;; [[file:init.org::#Pretty-Magit-Commit-Leaders][Pretty Magit Commit Leaders:2]]
(pretty-magit "Add" ?➕ (:foreground "#375E97" :height 1.2) "✅ Create a capability e.g. feature, test, dependency.")
(pretty-magit "Delete" ?❌ (:foreground "#375E97" :height 1.2) "❌ Remove a capability e.g. feature, test, dependency.")
(pretty-magit "Fix" ?🔨 (:foreground "#FB6542" :height 1.2) "🐛 Fix an issue e.g. bug, typo, accident, misstatement.")
(pretty-magit "Clean" ?🧹 (:foreground "#FFBB00" :height 1.2) "✂ Refactor code; reformat say by altering whitespace; refactor performance.")
(pretty-magit "Document" ?📚 (:foreground "#3F681C" :height 1.2) "ℹ Refactor of documentation, e.g. help files.")
(pretty-magit "Feature" ?⛲ (:foreground "slate gray" :height 1.2) "⛳ 🇮🇶🇨🇦 A milestone commit - flagpost")
(pretty-magit "Generate" ?🔭 (:foreground "slate gray" :height 1.2) "Export PDF/HTML or tangle raw code from a literate program") ;; Generating artefacts
(pretty-magit "master" ? (:box t :height 1.2) "" t)
(pretty-magit "origin" ?🐙 (:box t :height 1.2) "" t)
;; Commit leader examples: https://news.ycombinator.com/item?id=13889155.
;;
;; Cut ~ Remove a capability e.g. feature, test, dependency.
;; Bump ~ Increase the version of something e.g. dependency.
;; Make ~ Change the build process, or tooling, or infra.
;; Start ~ Begin doing something; e.g. create a feature flag.
;; Stop ~ End doing something; e.g. remove a feature flag.
;; Pretty Magit Commit Leaders:2 ends here
;; [[file:init.org::#Pretty-Magit-Commit-Leaders][Pretty Magit Commit Leaders:3]]
(defun add-magit-faces ()
"Add face properties and compose symbols for buffer from pretty-magit."
(interactive)
(with-silent-modifications