diff options
-rw-r--r-- | README.md | 589 |
1 files changed, 368 insertions, 221 deletions
diff --git a/README.md b/README.md index 02029dc..d6b2457 100644 --- a/README.md +++ b/README.md | |||
@@ -69,7 +69,7 @@ Now, I'll *try* running it regular-style, ignoring the errors. If it | |||
69 | doesn't work, I'll call git directly and clone the repo myself. | 69 | doesn't work, I'll call git directly and clone the repo myself. |
70 | 70 | ||
71 | (unless (ignore-errors (acdw/bootstrap-straight)) | 71 | (unless (ignore-errors (acdw/bootstrap-straight)) |
72 | (message "Straight.el didn't bootstrap correctly. Cloning directly...") | 72 | (message "%s" "Straight.el didn't bootstrap correctly. Cloning directly...") |
73 | (call-process "git" nil | 73 | (call-process "git" nil |
74 | (get-buffer-create "*bootstrap-straight-messages*") nil | 74 | (get-buffer-create "*bootstrap-straight-messages*") nil |
75 | "clone" | 75 | "clone" |
@@ -79,7 +79,10 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
79 | (acdw/bootstrap-straight)) | 79 | (acdw/bootstrap-straight)) |
80 | 80 | ||
81 | 81 | ||
82 | ## Customize macro | 82 | ## Customize macros |
83 | |||
84 | |||
85 | ### Emulate use-package’s `:custom` | ||
83 | 86 | ||
84 | (defmacro cuss (var val &optional docstring) | 87 | (defmacro cuss (var val &optional docstring) |
85 | "Basically, `:custom' from `use-package', but without `use-package'." | 88 | "Basically, `:custom' from `use-package', but without `use-package'." |
@@ -89,76 +92,36 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
89 | ',var ,val)) | 92 | ',var ,val)) |
90 | 93 | ||
91 | 94 | ||
92 | ## Clean `.emacs.d` | 95 | ### Emulate use-package’s `:custom-face`, but better |
93 | 96 | ||
94 | (straight-use-package 'no-littering) | 97 | (defvar acdw--custom-faces () |
95 | (require 'no-littering) | 98 | "List of custom faces to run through acdw/set-custom-faces.") |
96 | 99 | ||
97 | 100 | (defun acdw/set-custom-faces () | |
98 | ## Look and feel | 101 | "Run `customize-set-faces' on `acdw--custom-faces'." |
102 | (message "%s" "Customizing faces...") | ||
103 | (apply #'custom-set-faces acdw--custom-faces)) | ||
104 | |||
105 | (defun cussface (spec) | ||
106 | "Add SPEC to `acdw--custom-faces', and add a hook to run | ||
107 | `acdw/set-custom-faces' after init." | ||
108 | (add-to-list 'acdw--custom-faces spec) | ||
109 | (add-hook 'after-init-hook #'acdw/set-custom-faces)) | ||
99 | 110 | ||
100 | 111 | ||
101 | ### Fonts | 112 | ## Clean `.emacs.d` |
102 | 113 | ||
103 | 1. Define fonts | 114 | (straight-use-package 'no-littering) |
115 | (require 'no-littering) | ||
104 | 116 | ||
105 | (defun set-face-from-alternatives (face fonts) | ||
106 | (catch :return | ||
107 | (dolist (font fonts) | ||
108 | (when (find-font (font-spec :family (car font))) | ||
109 | (apply #'set-face-attribute `(,face nil | ||
110 | :family ,(car font) | ||
111 | ,@(cdr font))) | ||
112 | (throw :return font))))) | ||
113 | |||
114 | (defun acdw/setup-fonts () | ||
115 | "Setup fonts. This has to happen after the frame is setup for | ||
116 | the first time, so it should be added to `window-setup-hook'. It | ||
117 | removes itself from that hook." | ||
118 | (interactive) | ||
119 | (when (display-graphic-p) | ||
120 | (set-face-from-alternatives 'default | ||
121 | '(("Libertinus Mono" | ||
122 | :height 110) | ||
123 | ("Linux Libertine Mono O" | ||
124 | :height 110) | ||
125 | ("Go Mono" | ||
126 | :height 100) | ||
127 | ("Consolas" | ||
128 | :height 100))) | ||
129 | |||
130 | (set-face-from-alternatives 'fixed-pitch | ||
131 | '(("Libertinus Mono" | ||
132 | :height 110) | ||
133 | ("Linux Libertine Mono O" | ||
134 | :height 110) | ||
135 | ("Go Mono" | ||
136 | :height 100) | ||
137 | ("Consolas" | ||
138 | :height 100))) | ||
139 | |||
140 | (set-face-from-alternatives 'variable-pitch | ||
141 | '(("Libertinus Serif" | ||
142 | :height 120) | ||
143 | ("Linux Libertine O" | ||
144 | :height 120) | ||
145 | ("Georgia" | ||
146 | :height 110))) | ||
147 | |||
148 | (remove-function after-focus-change-function #'acdw/setup-fonts))) | ||
149 | |||
150 | (add-function :before after-focus-change-function #'acdw/setup-fonts) | ||
151 | 117 | ||
152 | 2. Line spacing | 118 | ### Don’t clutter `init.el` with customizations |
153 | 119 | ||
154 | (cuss line-spacing 0.1 | 120 | (with-eval-after-load 'no-littering |
155 | "Add 10% extra space below each line.") | 121 | (cuss custom-file (no-littering-expand-etc-file-name "custom.el"))) |
156 | 122 | ||
157 | 3. Unicode Fonts | ||
158 | 123 | ||
159 | (straight-use-package 'unicode-fonts) | 124 | ## Look and feel |
160 | (require 'unicode-fonts) | ||
161 | (unicode-fonts-setup) | ||
162 | 125 | ||
163 | 126 | ||
164 | ### Cursor | 127 | ### Cursor |
@@ -166,7 +129,7 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
166 | (cuss cursor-type 'bar | 129 | (cuss cursor-type 'bar |
167 | "Show a vertical bar for the cursor.") | 130 | "Show a vertical bar for the cursor.") |
168 | 131 | ||
169 | (cuss cursor-in-non-selected-windows 'hollow | 132 | (cuss cursor-in-non-selected-windows 'hbar |
170 | "Show an empty box in inactive windows.") | 133 | "Show an empty box in inactive windows.") |
171 | 134 | ||
172 | ;; Don't blink the cursor | 135 | ;; Don't blink the cursor |
@@ -344,17 +307,68 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
344 | 307 | ||
345 | (load-theme 'modus-operandi t) | 308 | (load-theme 'modus-operandi t) |
346 | 309 | ||
347 | 2. Change themes based on time of day | ||
348 | 310 | ||
349 | (cuss calendar-latitude 30.4515) | 311 | ### Fonts |
350 | (cuss calendar-longitude -91.1871) | 312 | |
313 | 1. Define fonts | ||
314 | |||
315 | (defun set-face-from-alternatives (face fonts) | ||
316 | (catch :return | ||
317 | (dolist (font fonts) | ||
318 | (when (find-font (font-spec :family (car font))) | ||
319 | (apply #'set-face-attribute `(,face | ||
320 | nil | ||
321 | :family (car font) | ||
322 | ,@(cdr font))) | ||
323 | (throw :return font))))) | ||
324 | |||
325 | (defun acdw/setup-fonts () | ||
326 | "Setup fonts. This has to happen after the frame is setup for | ||
327 | the first time, so it should be added to `window-setup-hook'. It | ||
328 | removes itself from that hook." | ||
329 | (interactive) | ||
330 | (when (display-graphic-p) | ||
331 | (set-face-from-alternatives 'default | ||
332 | '(("Input Mono" | ||
333 | :height 105) | ||
334 | ("Go Mono" | ||
335 | :height 100) | ||
336 | ("Consolas" | ||
337 | :height 100))) | ||
351 | 338 | ||
352 | (straight-use-package 'circadian) | 339 | (set-face-from-alternatives 'fixed-pitch |
340 | '(("Input Mono") | ||
341 | ("Go Mono") | ||
342 | ("Consolas"))) | ||
353 | 343 | ||
354 | (cuss circadian-themes '((:sunrise . modus-operandi) | 344 | (set-face-from-alternatives 'variable-pitch |
355 | (:sunset . modus-vivendi))) | 345 | '(("Input Serif") |
346 | ("Georgia"))) | ||
356 | 347 | ||
357 | (circadian-setup) | 348 | (remove-function after-focus-change-function #'acdw/setup-fonts))) |
349 | |||
350 | (add-function :before after-focus-change-function #'acdw/setup-fonts) | ||
351 | |||
352 | 2. Custom faces | ||
353 | |||
354 | (cussface '(font-lock-comment-face | ||
355 | ((t (:inherit (custom-comment italic variable-pitch)))))) | ||
356 | |||
357 | 3. Line spacing | ||
358 | |||
359 | (cuss line-spacing 0.1 | ||
360 | "Add 10% extra space below each line.") | ||
361 | |||
362 | 4. Underlines | ||
363 | |||
364 | (cuss x-underline-at-descent-line t | ||
365 | "Draw the underline at the same place as the descent line.") | ||
366 | |||
367 | 5. Unicode Fonts | ||
368 | |||
369 | (straight-use-package 'unicode-fonts) | ||
370 | (require 'unicode-fonts) | ||
371 | (unicode-fonts-setup) | ||
358 | 372 | ||
359 | 373 | ||
360 | ## Interactivity | 374 | ## Interactivity |
@@ -374,13 +388,18 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
374 | (cuss read-buffer-completion-ignore-case t) | 388 | (cuss read-buffer-completion-ignore-case t) |
375 | (cuss read-file-name-completion-ignore-case t) | 389 | (cuss read-file-name-completion-ignore-case t) |
376 | 390 | ||
377 | 3. Selectrum | 391 | 3. Minibuffer recursivity |
392 | |||
393 | (cuss enable-recursive-minibuffers t) | ||
394 | (minibuffer-depth-indicate-mode +1) | ||
395 | |||
396 | 4. Selectrum | ||
378 | 397 | ||
379 | (straight-use-package 'selectrum) | 398 | (straight-use-package 'selectrum) |
380 | (require 'selectrum) | 399 | (require 'selectrum) |
381 | (selectrum-mode +1) | 400 | (selectrum-mode +1) |
382 | 401 | ||
383 | 4. Prescient | 402 | 5. Prescient |
384 | 403 | ||
385 | (straight-use-package 'prescient) | 404 | (straight-use-package 'prescient) |
386 | (require 'prescient) | 405 | (require 'prescient) |
@@ -390,7 +409,7 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
390 | (require 'selectrum-prescient) | 409 | (require 'selectrum-prescient) |
391 | (selectrum-prescient-mode +1) | 410 | (selectrum-prescient-mode +1) |
392 | 411 | ||
393 | 5. Consult | 412 | 6. Consult |
394 | 413 | ||
395 | (straight-use-package '(consult | 414 | (straight-use-package '(consult |
396 | :host github | 415 | :host github |
@@ -402,27 +421,27 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
402 | :repo "minad/consult")) | 421 | :repo "minad/consult")) |
403 | (require 'consult-selectrum) | 422 | (require 'consult-selectrum) |
404 | 423 | ||
405 | (define-key ctl-x-map "b" #'consult-buffer) | 424 | (with-eval-after-load 'consult |
406 | (define-key ctl-x-map (kbd "C-r") #'consult-buffer) | 425 | (define-key ctl-x-map "b" #'consult-buffer) |
407 | (define-key ctl-x-map "4b" #'consult-buffer-other-window) | 426 | (define-key ctl-x-map (kbd "C-r") #'consult-buffer) |
408 | (define-key ctl-x-map "5b" #'consult-buffer-other-frame) | 427 | (define-key ctl-x-map "4b" #'consult-buffer-other-window) |
428 | (define-key ctl-x-map "5b" #'consult-buffer-other-frame) | ||
409 | 429 | ||
410 | (define-key goto-map "o" #'consult-outline) | 430 | (define-key goto-map "o" #'consult-outline) |
411 | (define-key goto-map "g" #'consult-line) | 431 | (define-key goto-map "g" #'consult-line) |
412 | (define-key goto-map (kbd "M-g") #'consult-line) | 432 | (define-key goto-map (kbd "M-g") #'consult-line) |
413 | (define-key goto-map "l" #'consult-line) | 433 | (define-key goto-map "l" #'consult-line) |
414 | (define-key goto-map "m" #'consult-mark) | 434 | (define-key goto-map "m" #'consult-mark) |
415 | (define-key goto-map "k" #'consult-global-mark) | 435 | (define-key goto-map "i" #'consult-imenu) |
416 | (define-key goto-map "i" #'consult-imenu) | 436 | (define-key goto-map "e" #'consult-error) |
417 | (define-key goto-map "e" #'consult-error) | ||
418 | 437 | ||
419 | (global-set-key (kbd "M-y") #'consult-yank-pop) | 438 | (global-set-key (kbd "M-y") #'consult-yank-pop) |
420 | 439 | ||
421 | (define-key help-map "a" #'consult-apropos) | 440 | (define-key help-map "a" #'consult-apropos) |
422 | 441 | ||
423 | (fset 'multi-occur #'consult-multi-occur) | 442 | (fset 'multi-occur #'consult-multi-occur)) |
424 | 443 | ||
425 | 6. Marginalia | 444 | 7. Marginalia |
426 | 445 | ||
427 | (straight-use-package '(marginalia | 446 | (straight-use-package '(marginalia |
428 | :host github | 447 | :host github |
@@ -436,6 +455,11 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
436 | (marginalia-mode +1) | 455 | (marginalia-mode +1) |
437 | 456 | ||
438 | 457 | ||
458 | ### Completion | ||
459 | |||
460 | (global-set-key (kbd "M-/") #'hippie-expand) | ||
461 | |||
462 | |||
439 | ## Keyboard | 463 | ## Keyboard |
440 | 464 | ||
441 | 465 | ||
@@ -457,6 +481,22 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
457 | (run-hooks 'acdw/map-defined-hook) | 481 | (run-hooks 'acdw/map-defined-hook) |
458 | 482 | ||
459 | 483 | ||
484 | ## Mouse | ||
485 | |||
486 | |||
487 | ### Preserve screen position when scrolling with the mouse wheel | ||
488 | |||
489 | from [u/TheFrenchPoulp](https://www.reddit.com/r/emacs/comments/km9by4/weekly_tipstricketc_thread/ghg2c9d/). | ||
490 | |||
491 | (advice-add 'mwheel-scroll :around #'me/mwheel-scroll) | ||
492 | |||
493 | (defun me/mwheel-scroll (original &rest arguments) | ||
494 | "Like `mwheel-scroll' but preserve screen position. | ||
495 | See `scroll-preserve-screen-position'." | ||
496 | (let ((scroll-preserve-screen-position :always)) | ||
497 | (apply original arguments))) | ||
498 | |||
499 | |||
460 | ## Persistence | 500 | ## Persistence |
461 | 501 | ||
462 | 502 | ||
@@ -637,11 +677,22 @@ doesn't work, I'll call git directly and clone the repo myself. | |||
637 | 677 | ||
638 | ### Searching & Replacing | 678 | ### Searching & Replacing |
639 | 679 | ||
640 | (straight-use-package 'visual-regexp) | 680 | 1. Replace with Anzu |
641 | (require 'visual-regexp) | 681 | |
642 | 682 | (straight-use-package 'anzu) | |
643 | (with-eval-after-load 'visual-regexp | 683 | (require 'anzu) |
644 | (global-set-key (kbd "M-C-%") #'vr/query-replace)) | 684 | |
685 | ;; show search count in the modeline | ||
686 | (global-anzu-mode +1) | ||
687 | |||
688 | (cuss anzu-replace-to-string-separator " → " | ||
689 | "What to separate the search from the replacement.") | ||
690 | |||
691 | (global-set-key [remap query-replace] #'anzu-query-replace) | ||
692 | (global-set-key [remap query-replace-regexp] #'anzu-query-replace-regexp) | ||
693 | |||
694 | (define-key isearch-mode-map [remap isearch-query-replace] #'anzu-isearch-query-replace) | ||
695 | (define-key isearch-mode-map [remap isearch-query-replace-regexp] #'anzu-isearch-query-replace-regexp) | ||
645 | 696 | ||
646 | 697 | ||
647 | # Programming | 698 | # Programming |
@@ -731,7 +782,11 @@ This has to be done *before* loading the package. It's included in `visual-fill | |||
731 | (global-set-key [left-margin mouse-wheel-down-event] #'mwheel-scroll) | 782 | (global-set-key [left-margin mouse-wheel-down-event] #'mwheel-scroll) |
732 | (global-set-key [left-margin mouse-wheel-up-event] #'mwheel-scroll) | 783 | (global-set-key [left-margin mouse-wheel-up-event] #'mwheel-scroll) |
733 | (global-set-key [left-margin wheel-down] #'mwheel-scroll) | 784 | (global-set-key [left-margin wheel-down] #'mwheel-scroll) |
734 | (global-set-key [left-margin wheel-up] #'mwheel-scroll)) | 785 | (global-set-key [left-margin wheel-up] #'mwheel-scroll) |
786 | (global-set-key [right-margin mouse-4] #'mwheel-scroll) | ||
787 | (global-set-key [right-margin mouse-5] #'mwheel-scroll) | ||
788 | (global-set-key [left-margin mouse-4] #'mwheel-scroll) | ||
789 | (global-set-key [left-margin mouse-5] #'mwheel-scroll)) | ||
735 | 790 | ||
736 | 791 | ||
737 | ### Load the package | 792 | ### Load the package |
@@ -753,6 +808,11 @@ This has to be done *before* loading the package. It's included in `visual-fill | |||
753 | ## Typographical niceties | 808 | ## Typographical niceties |
754 | 809 | ||
755 | 810 | ||
811 | ### Variable pitch in text-modes | ||
812 | |||
813 | (add-hook 'text-mode-hook #'variable-pitch-mode) | ||
814 | |||
815 | |||
756 | ### Typo mode | 816 | ### Typo mode |
757 | 817 | ||
758 | (straight-use-package 'typo) | 818 | (straight-use-package 'typo) |
@@ -783,147 +843,225 @@ I’ve put org mode under Applications, as opposed to Writing, because it’s m | |||
783 | (cuss org-fontify-whole-heading-line t) | 843 | (cuss org-fontify-whole-heading-line t) |
784 | (cuss org-fontify-quote-and-verse-blocks t) | 844 | (cuss org-fontify-quote-and-verse-blocks t) |
785 | (cuss org-pretty-entities t) | 845 | (cuss org-pretty-entities t) |
786 | (cuss org-num-mode +1) | ||
787 | (cuss org-src-tab-acts-natively t) | 846 | (cuss org-src-tab-acts-natively t) |
788 | (cuss org-src-fontify-natively t) | 847 | (cuss org-src-fontify-natively t) |
789 | (cuss org-src-window-setup 'current-window) | 848 | (cuss org-src-window-setup 'current-window) |
790 | (cuss org-confirm-babel-evaluate nil) | 849 | (cuss org-confirm-babel-evaluate nil) |
791 | (cuss org-directory "~/Org") | 850 | (cuss org-directory "~/Org") |
851 | (cuss org-ellipsis "…") | ||
792 | 852 | ||
853 | 1. Tags | ||
793 | 854 | ||
794 | ### Org Agenda | 855 | (cuss org-tags-column 0 |
856 | "Show tags directly after the headline. | ||
857 | This is the best-looking option with variable-pitch fonts.") | ||
858 | |||
859 | (cussface | ||
860 | '(org-tag | ||
861 | ((t | ||
862 | (:height 0.8 :weight normal :slant italic :foreground "grey40" :inherit | ||
863 | (variable-pitch)))))) | ||
795 | 864 | ||
796 | (cuss org-agenda-files (no-littering-expand-etc-file-name "agenda-files")) | ||
797 | |||
798 | (if (and (stringp org-agenda-files) | ||
799 | (not (file-exists-p org-agenda-files))) | ||
800 | (with-temp-buffer (write-file org-agenda-files))) | ||
801 | |||
802 | (define-key acdw/map (kbd "C-a") #'org-agenda) | ||
803 | 865 | ||
866 | ### General | ||
804 | 867 | ||
805 | ### [A better return in Org mode](http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/) | 868 | 1. [Org Return: DWIM](https://github.com/alphapapa/unpackaged.el#org-return-dwim) |
806 | 869 | ||
807 | (require 'org-inlinetask) | 870 | (defun unpackaged/org-element-descendant-of (type element) |
808 | 871 | "Return non-nil if ELEMENT is a descendant of TYPE. | |
809 | (defun scimax/org-return (&optional ignore) | 872 | TYPE should be an element type, like `item' or `paragraph'. |
810 | "Add new list item, heading or table row with RET. | 873 | ELEMENT should be a list like that returned by `org-element-context'." |
811 | A double return on an empty element deletes it. | 874 | ;; MAYBE: Use `org-element-lineage'. |
812 | Use a prefix arg to get regular RET." | 875 | (when-let* ((parent (org-element-property :parent element))) |
813 | (interactive "P") | 876 | (or (eq type (car parent)) |
814 | (if ignore | 877 | (unpackaged/org-element-descendant-of type parent)))) |
815 | (org-return) | 878 | |
816 | (cond | 879 | ;;;###autoload |
880 | (defun unpackaged/org-return-dwim (&optional default) | ||
881 | "A helpful replacement for `org-return'. With prefix, call `org-return'. | ||
882 | |||
883 | On headings, move point to position after entry content. In | ||
884 | lists, insert a new item or end the list, with checkbox if | ||
885 | appropriate. In tables, insert a new row or end the table." | ||
886 | ;; Inspired by John Kitchin: http://kitchingroup.cheme.cmu.edu/blog/2017/04/09/A-better-return-in-org-mode/ | ||
887 | (interactive "P") | ||
888 | (if default | ||
889 | (org-return) | ||
890 | (cond | ||
891 | ;; Act depending on context around point. | ||
892 | |||
893 | ;; NOTE: I prefer RET to not follow links, but by uncommenting this block, links will be | ||
894 | ;; followed. | ||
895 | |||
896 | ;; ((eq 'link (car (org-element-context))) | ||
897 | ;; ;; Link: Open it. | ||
898 | ;; (org-open-at-point-global)) | ||
899 | |||
900 | ((org-at-heading-p) | ||
901 | ;; Heading: Move to position after entry content. | ||
902 | ;; NOTE: This is probably the most interesting feature of this function. | ||
903 | (let ((heading-start (org-entry-beginning-position))) | ||
904 | (goto-char (org-entry-end-position)) | ||
905 | (cond ((and (org-at-heading-p) | ||
906 | (= heading-start (org-entry-beginning-position))) | ||
907 | ;; Entry ends on its heading; add newline after | ||
908 | (end-of-line) | ||
909 | (insert "\n\n")) | ||
910 | (t | ||
911 | ;; Entry ends after its heading; back up | ||
912 | (forward-line -1) | ||
913 | (end-of-line) | ||
914 | (when (org-at-heading-p) | ||
915 | ;; At the same heading | ||
916 | (forward-line) | ||
917 | (insert "\n") | ||
918 | (forward-line -1)) | ||
919 | ;; FIXME: looking-back is supposed to be called with more arguments. | ||
920 | (while (not (looking-back (rx (repeat 3 (seq (optional blank) "\n"))) nil)) | ||
921 | (insert "\n")) | ||
922 | (forward-line -1))))) | ||
923 | |||
924 | ((org-at-item-checkbox-p) | ||
925 | ;; Checkbox: Insert new item with checkbox. | ||
926 | (org-insert-todo-heading nil)) | ||
927 | |||
928 | ((org-in-item-p) | ||
929 | ;; Plain list. Yes, this gets a little complicated... | ||
930 | (let ((context (org-element-context))) | ||
931 | (if (or (eq 'plain-list (car context)) ; First item in list | ||
932 | (and (eq 'item (car context)) | ||
933 | (not (eq (org-element-property :contents-begin context) | ||
934 | (org-element-property :contents-end context)))) | ||
935 | (unpackaged/org-element-descendant-of 'item context)) ; Element in list item, e.g. a link | ||
936 | ;; Non-empty item: Add new item. | ||
937 | (org-insert-item) | ||
938 | ;; Empty item: Close the list. | ||
939 | ;; TODO: Do this with org functions rather than operating on the text. Can't seem to find the right function. | ||
940 | (delete-region (line-beginning-position) (line-end-position)) | ||
941 | (insert "\n")))) | ||
942 | |||
943 | ((when (fboundp 'org-inlinetask-in-task-p) | ||
944 | (org-inlinetask-in-task-p)) | ||
945 | ;; Inline task: Don't insert a new heading. | ||
946 | (org-return)) | ||
947 | |||
948 | ((org-at-table-p) | ||
949 | (cond ((save-excursion | ||
950 | (beginning-of-line) | ||
951 | ;; See `org-table-next-field'. | ||
952 | (cl-loop with end = (line-end-position) | ||
953 | for cell = (org-element-table-cell-parser) | ||
954 | always (equal (org-element-property :contents-begin cell) | ||
955 | (org-element-property :contents-end cell)) | ||
956 | while (re-search-forward "|" end t))) | ||
957 | ;; Empty row: end the table. | ||
958 | (delete-region (line-beginning-position) (line-end-position)) | ||
959 | (org-return)) | ||
960 | (t | ||
961 | ;; Non-empty row: call `org-return'. | ||
962 | (org-return)))) | ||
963 | (t | ||
964 | ;; All other cases: call `org-return'. | ||
965 | (org-return))))) | ||
966 | |||
967 | (with-eval-after-load 'org | ||
968 | (define-key org-mode-map (kbd "RET") #'unpackaged/org-return-dwim)) | ||
969 | |||
970 | 2. Insert blank lines around headers | ||
971 | |||
972 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents). | ||
817 | 973 | ||
818 | ((eq 'line-break (car (org-element-context))) | 974 | ;;;###autoload |
819 | (org-return t)) | 975 | (defun unpackaged/org-fix-blank-lines (&optional prefix) |
976 | "Ensure that blank lines exist between headings and between headings and their contents. | ||
977 | With prefix, operate on whole buffer. Ensures that blank lines | ||
978 | exist after each headings's drawers." | ||
979 | (interactive "P") | ||
980 | (org-map-entries (lambda () | ||
981 | (org-with-wide-buffer | ||
982 | ;; `org-map-entries' narrows the buffer, which prevents us | ||
983 | ;; from seeing newlines before the current heading, so we | ||
984 | ;; do this part widened. | ||
985 | (while (not (looking-back "\n\n" nil)) | ||
986 | ;; Insert blank lines before heading. | ||
987 | (insert "\n"))) | ||
988 | (let ((end (org-entry-end-position))) | ||
989 | ;; Insert blank lines before entry content | ||
990 | (forward-line) | ||
991 | (while (and (org-at-planning-p) | ||
992 | (< (point) (point-max))) | ||
993 | ;; Skip planning lines | ||
994 | (forward-line)) | ||
995 | (while (re-search-forward org-drawer-regexp end t) | ||
996 | ;; Skip drawers. You might think that `org-at-drawer-p' | ||
997 | ;; would suffice, but for some reason it doesn't work | ||
998 | ;; correctly when operating on hidden text. This | ||
999 | ;; works, taken from `org-agenda-get-some-entry-text'. | ||
1000 | (re-search-forward "^[ \t]*:END:.*\n?" end t) | ||
1001 | (goto-char (match-end 0))) | ||
1002 | (unless (or (= (point) (point-max)) | ||
1003 | (org-at-heading-p) | ||
1004 | (looking-at-p "\n")) | ||
1005 | (insert "\n")))) | ||
1006 | t (if prefix | ||
1007 | nil | ||
1008 | 'tree))) | ||
820 | 1009 | ||
821 | ;; Open links like usual, unless point is at the end of a line. | 1010 | 1. Add a before-save-hook |
822 | ;; and if at beginning of line, just press enter. | ||
823 | ((or (and (eq 'link (car (org-element-context))) (not (eolp))) | ||
824 | (bolp)) | ||
825 | (org-return)) | ||
826 | 1011 | ||
827 | ;; It doesn't make sense to add headings in inline tasks. Thanks Anders | 1012 | (defun cribbed/org-mode-fix-blank-lines () |
828 | ;; Johansson! | 1013 | (when (eq major-mode 'org-mode) |
829 | ((org-inlinetask-in-task-p) | 1014 | (let ((current-prefix-arg 4)) ; Emulate C-u |
830 | (org-return)) | 1015 | (call-interactively 'unpackaged/org-fix-blank-lines)))) |
1016 | |||
1017 | (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines) | ||
1018 | |||
1019 | |||
1020 | ### Org Agenda | ||
1021 | |||
1022 | (cuss org-agenda-files | ||
1023 | (let ((list)) | ||
1024 | (dolist (file '(;; add more files to this list | ||
1025 | "home.org" | ||
1026 | "work.org") | ||
1027 | list) | ||
1028 | (push (expand-file-name file org-directory) list)))) | ||
831 | 1029 | ||
832 | ;; checkboxes too | 1030 | (define-key acdw/map (kbd "C-a") #'org-agenda) |
833 | ((org-at-item-checkbox-p) | ||
834 | (org-insert-todo-heading nil)) | ||
835 | 1031 | ||
836 | ;; lists end with two blank lines, so we need to make sure we are also not | 1032 | (cuss org-todo-keywords |
837 | ;; at the beginning of a line to avoid a loop where a new entry gets | 1033 | '((sequence "RECUR(r)" "TODO(t)" "|" "DONE(d)") |
838 | ;; created with only one blank line. | 1034 | (sequence "|" "CANCELLED(c)"))) |
839 | ((org-in-item-p) | 1035 | |
840 | (if (save-excursion (beginning-of-line) (org-element-property :contents-begin (org-element-context))) | 1036 | |
841 | (org-insert-heading) | 1037 | ### TODO Capture |
842 | (beginning-of-line) | 1038 | |
843 | (delete-region (line-beginning-position) (line-end-position)) | 1039 | |
844 | (org-return))) | 1040 | ## Git |
1041 | |||
1042 | (straight-use-package 'magit) | ||
845 | 1043 | ||
846 | ;; org-heading | 1044 | (define-key acdw/map "g" #'magit-status) |
847 | ((org-at-heading-p) | 1045 | |
848 | (if (not (string= "" (org-element-property :title (org-element-context)))) | 1046 | |
849 | (progn (org-end-of-meta-data) | 1047 | ## Beancount mode |
850 | (org-insert-heading-respect-content) | 1048 | |
851 | (outline-show-entry)) | 1049 | (straight-use-package '(beancount-mode |
852 | (beginning-of-line) | 1050 | :host github |
853 | (setf (buffer-substring | 1051 | :repo "beancount/beancount-mode")) |
854 | (line-beginning-position) (line-end-position)) ""))) | 1052 | (require 'beancount) |
855 | 1053 | ||
856 | ;; tables | 1054 | (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode)) |
857 | ((org-at-table-p) | ||
858 | (if (-any? | ||
859 | (lambda (x) (not (string= "" x))) | ||
860 | (nth | ||
861 | (- (org-table-current-dline) 1) | ||
862 | (org-table-to-lisp))) | ||
863 | (org-return) | ||
864 | ;; empty row | ||
865 | (beginning-of-line) | ||
866 | (setf (buffer-substring | ||
867 | (line-beginning-position) (line-end-position)) "") | ||
868 | (org-return))) | ||
869 | 1055 | ||
870 | ;; fall-through case | 1056 | (defun acdw/disable-aggressive-indent () |
871 | (t | 1057 | "Turn `aggressive-indent-mode' off for a buffer." |
872 | (org-return))))) | 1058 | (aggressive-indent-mode -1)) |
873 | 1059 | ||
1060 | (add-hook 'beancount-mode-hook #'outline-minor-mode) | ||
1061 | (add-hook 'beancount-mode-hook #'acdw/disable-aggressive-indent) | ||
874 | 1062 | ||
875 | (define-key org-mode-map (kbd "RET") | 1063 | (define-key beancount-mode-map (kbd "M-n") #'outline-next-visible-heading) |
876 | 'scimax/org-return) | 1064 | (define-key beancount-mode-map (kbd "M-p") #'outline-previous-visible-heading) |
877 | |||
878 | |||
879 | ### Insert blank lines around headers | ||
880 | |||
881 | from [unpackaged.el](https://github.com/alphapapa/unpackaged.el#ensure-blank-lines-between-headings-and-before-contents). | ||
882 | |||
883 | ;;;###autoload | ||
884 | (defun unpackaged/org-fix-blank-lines (&optional prefix) | ||
885 | "Ensure that blank lines exist between headings and between headings and their contents. | ||
886 | With prefix, operate on whole buffer. Ensures that blank lines | ||
887 | exist after each headings's drawers." | ||
888 | (interactive "P") | ||
889 | (org-map-entries (lambda () | ||
890 | (org-with-wide-buffer | ||
891 | ;; `org-map-entries' narrows the buffer, which prevents us | ||
892 | ;; from seeing newlines before the current heading, so we | ||
893 | ;; do this part widened. | ||
894 | (while (not (looking-back "\n\n" nil)) | ||
895 | ;; Insert blank lines before heading. | ||
896 | (insert "\n"))) | ||
897 | (let ((end (org-entry-end-position))) | ||
898 | ;; Insert blank lines before entry content | ||
899 | (forward-line) | ||
900 | (while (and (org-at-planning-p) | ||
901 | (< (point) (point-max))) | ||
902 | ;; Skip planning lines | ||
903 | (forward-line)) | ||
904 | (while (re-search-forward org-drawer-regexp end t) | ||
905 | ;; Skip drawers. You might think that `org-at-drawer-p' | ||
906 | ;; would suffice, but for some reason it doesn't work | ||
907 | ;; correctly when operating on hidden text. This | ||
908 | ;; works, taken from `org-agenda-get-some-entry-text'. | ||
909 | (re-search-forward "^[ \t]*:END:.*\n?" end t) | ||
910 | (goto-char (match-end 0))) | ||
911 | (unless (or (= (point) (point-max)) | ||
912 | (org-at-heading-p) | ||
913 | (looking-at-p "\n")) | ||
914 | (insert "\n")))) | ||
915 | t (if prefix | ||
916 | nil | ||
917 | 'tree))) | ||
918 | |||
919 | 1. Add a before-save-hook | ||
920 | |||
921 | (defun cribbed/org-mode-fix-blank-lines () | ||
922 | (when (eq major-mode 'org-mode) | ||
923 | (let ((current-prefix-arg 4)) ; Emulate C-u | ||
924 | (call-interactively 'unpackaged/org-fix-blank-lines)))) | ||
925 | |||
926 | (add-hook 'before-save-hook #'cribbed/org-mode-fix-blank-lines) | ||
927 | 1065 | ||
928 | 1066 | ||
929 | # Appendices | 1067 | # Appendices |
@@ -968,7 +1106,7 @@ I realized I didn’t need `early-init.el`, since it really only set `load-prefe | |||
968 | (when (file-newer-than-file-p config (expand-file-name | 1106 | (when (file-newer-than-file-p config (expand-file-name |
969 | "README.md" | 1107 | "README.md" |
970 | user-emacs-directory)) | 1108 | user-emacs-directory)) |
971 | (message "Exporting README.md...") | 1109 | (message "%s" "Exporting README.md...") |
972 | (require 'ox-md) | 1110 | (require 'ox-md) |
973 | (with-demoted-errors "Problem exporting README.md: %S" | 1111 | (with-demoted-errors "Problem exporting README.md: %S" |
974 | (org-md-export-to-markdown))) | 1112 | (org-md-export-to-markdown))) |
@@ -976,11 +1114,11 @@ I realized I didn’t need `early-init.el`, since it really only set `load-prefe | |||
976 | (when (file-newer-than-file-p config (expand-file-name | 1114 | (when (file-newer-than-file-p config (expand-file-name |
977 | "config.el" | 1115 | "config.el" |
978 | user-emacs-directory)) | 1116 | user-emacs-directory)) |
979 | (message "Tangling config.org...") | 1117 | (message "%s" "Tangling config.org...") |
980 | (require 'org) | 1118 | (require 'org) |
981 | (let ((inits (org-babel-tangle))) | 1119 | (let ((inits (org-babel-tangle))) |
982 | ;; byte-compile resulting files | 1120 | ;; byte-compile resulting files |
983 | (message "Byte-compiling...") | 1121 | (message "%s" "Byte-compiling...") |
984 | (dolist (f inits) | 1122 | (dolist (f inits) |
985 | (when (string-match "\\.el\\'" f) | 1123 | (when (string-match "\\.el\\'" f) |
986 | (byte-compile-file f (not disable-load))))))))))) | 1124 | (byte-compile-file f (not disable-load))))))))))) |
@@ -991,7 +1129,7 @@ I realized I didn’t need `early-init.el`, since it really only set `load-prefe | |||
991 | 1129 | ||
992 | ### emacsdc | 1130 | ### emacsdc |
993 | 1131 | ||
994 | Here's a wrapper script that'll start `emacs –daemon` if there isn't | 1132 | Here's a wrapper script that'll start `emacs --daemon` if there isn't |
995 | one, and then launch `emacsclient` with the arguments. I'd recommend | 1133 | one, and then launch `emacsclient` with the arguments. I'd recommend |
996 | installing with either `ln -s bin/emacsdc $HOME/.local/bin/`, or | 1134 | installing with either `ln -s bin/emacsdc $HOME/.local/bin/`, or |
997 | adding `$HOME/.local/bin` to your `$PATH`. | 1135 | adding `$HOME/.local/bin` to your `$PATH`. |
@@ -1004,9 +1142,18 @@ adding `$HOME/.local/bin` to your `$PATH`. | |||
1004 | 1142 | ||
1005 | ### Emacs.cmd | 1143 | ### Emacs.cmd |
1006 | 1144 | ||
1007 | Here’s a wrapper script that’ll run Emacs on Windows, with a custom `$HOME`. I have mine setup like this: Emacs is downloaded from [the GNU mirror](https://mirrors.tripadvisor.com/gnu/emacs/windows/emacs-27/emacs-27.1-x86_64.zip) and unzipped to `~/Downloads/emacs/`. `Emacs.cmd` sets `$HOME` to `~/Downloads/emacshome/`, which is where `.emacs.d` is, and whatever else I might want to throw in there. | 1145 | Here's a wrapper script that'll run Emacs on Windows, with a custom |
1146 | `$HOME`. I have mine setup like this: Emacs is downloaded from [the | ||
1147 | GNU mirror](https://mirrors.tripadvisor.com/gnu/emacs/windows/emacs-27/emacs-27.1-x86_64.zip) and unzipped to `~/Downloads/emacs/`. `Emacs.cmd` sets | ||
1148 | `$HOME` to `~/Downloads/emacshome/`, which is where `.emacs.d` is, and | ||
1149 | whatever else I might want to throw in there. | ||
1008 | 1150 | ||
1009 | set HOME=%~dp0..\..\emacshome | 1151 | set HOME=%~dp0..\..\emacshome |
1152 | |||
1153 | REM Run "Quick Mode" | ||
1154 | REM "%~dp0runemacs.exe" -Q %* | ||
1155 | |||
1156 | REM Regular | ||
1010 | "%~dp0runemacs.exe" %* | 1157 | "%~dp0runemacs.exe" %* |
1011 | 1158 | ||
1012 | 1159 | ||