summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--early-init.el10
-rw-r--r--init.el174
-rw-r--r--lisp/+emacs.el4
-rw-r--r--lisp/+flyspell-correct.el24
-rw-r--r--lisp/+org-capture.el164
-rw-r--r--lisp/+org.el44
-rw-r--r--lisp/+ox.el29
-rw-r--r--lisp/+titlecase.el32
-rw-r--r--lisp/acdw.el48
-rw-r--r--lisp/private.el23
10 files changed, 524 insertions, 28 deletions
diff --git a/early-init.el b/early-init.el index 340cbf7..bc4ccdd 100644 --- a/early-init.el +++ b/early-init.el
@@ -64,7 +64,7 @@ restore that."
64(push (locate-user-emacs-file "lisp") load-path) 64(push (locate-user-emacs-file "lisp") load-path)
65(require 'acdw) 65(require 'acdw)
66 66
67(+define-dir .etc (locate-user-emacs-file ".etc") 67(+define-dir .etc (locate-user-emacs-file "etc")
68 "Directory for all of Emacs's various files. 68 "Directory for all of Emacs's various files.
69See `no-littering' for examples.") 69See `no-littering' for examples.")
70 70
@@ -81,13 +81,13 @@ See `no-littering' for examples.")
81(yoke compat "https://git.sr.ht/~pkal/compat") 81(yoke compat "https://git.sr.ht/~pkal/compat")
82 82
83(yoke no-littering "https://github.com/emacscollective/no-littering" 83(yoke no-littering "https://github.com/emacscollective/no-littering"
84 (require 'no-littering)
85 (setq no-littering-etc-directory .etc 84 (setq no-littering-etc-directory .etc
86 no-littering-var-directory .etc 85 no-littering-var-directory .etc
87 custom-file (.etc "custom.el")) 86 custom-file (.etc "custom.el"))
87 (require 'no-littering)
88 (when (boundp 'native-comp-eln-load-path)
89 (setcar native-comp-eln-load-path (expand-file-name (.etc "eln-cache" t))))
88 (when (boundp 'comp-eln-load-path) 90 (when (boundp 'comp-eln-load-path)
89 (setcar comp-eln-load-path (expand-file-name (.etc "eln-cache" t)))) 91 (setcar comp-eln-load-path (expand-file-name (.etc "eln-cache" t))))
90 (when (fboundp 'startup-redirect-eln-cache) 92 (when (fboundp 'startup-redirect-eln-cache)
91 (startup-redirect-eln-cache 93 (startup-redirect-eln-cache (convert-standard-filename (.etc "eln-cache/")))))
92 (convert-standard-filename
93 (.etc "eln-cache/")))))
diff --git a/init.el b/init.el index 843fae9..3cbf3cc 100644 --- a/init.el +++ b/init.el
@@ -25,7 +25,7 @@
25 "M-/" #'hippie-expand 25 "M-/" #'hippie-expand
26 "M-=" #'count-words 26 "M-=" #'count-words
27 "C-x C-b" #'ibuffer 27 "C-x C-b" #'ibuffer
28"C-x 4 n" #'clone-buffer 28 "C-x 4 n" #'clone-buffer
29 "S-<down-mouse-1>" #'mouse-set-mark 29 "S-<down-mouse-1>" #'mouse-set-mark
30 "C-x 0" #'+delete-window-or-bury-buffer 30 "C-x 0" #'+delete-window-or-bury-buffer
31 "M-j" nil 31 "M-j" nil
@@ -42,7 +42,9 @@
42 (let ((buffer-quit-function #'ignore)) 42 (let ((buffer-quit-function #'ignore))
43 (apply fn r))) 43 (apply fn r)))
44 ;; Themes 44 ;; Themes
45 (load-theme 'modus-operandi)) 45 (load-theme 'modus-operandi)
46 (set-face-attribute 'default nil :family "Comic Code" :height 100)
47 (set-face-attribute 'variable-pitch nil :family "Comic Code" :height 100))
46 48
47(yoke isearch nil 49(yoke isearch nil
48 (define-keys (current-global-map) 50 (define-keys (current-global-map)
@@ -64,7 +66,7 @@
64 tab-always-indent 'complete 66 tab-always-indent 'complete
65 completion-in-region-function #'consult-completion-in-region 67 completion-in-region-function #'consult-completion-in-region
66 consult-narrow-key "<" 68 consult-narrow-key "<"
67 consult--regexp-compiler #'consult--orderless-regexp-compiler) 69 consult--regexp-compiler #'consult--default-regexp-compiler)
68 (advice-add #'register-preview :override #'consult-register-window) 70 (advice-add #'register-preview :override #'consult-register-window)
69 (define-keys (current-global-map) 71 (define-keys (current-global-map)
70 ;; C-c bindings (mode-specific-map) 72 ;; C-c bindings (mode-specific-map)
@@ -118,7 +120,7 @@
118 (define-key org-mode-map (kbd "M-g o") #'consult-org-heading))) 120 (define-key org-mode-map (kbd "M-g o") #'consult-org-heading)))
119 121
120(yoke orderless "https://github.com/oantolin/orderless" 122(yoke orderless "https://github.com/oantolin/orderless"
121(require 'orderless) 123 (require 'orderless)
122 (setq completion-styles '(substring orderless basic) 124 (setq completion-styles '(substring orderless basic)
123 completion-category-defaults nil 125 completion-category-defaults nil
124 completion-category-overrides '((file (styles basic partial-completion))) 126 completion-category-overrides '((file (styles basic partial-completion)))
@@ -205,3 +207,167 @@
205 (locate-user-emacs-file "yoke/with-editor/lisp"))) 207 (locate-user-emacs-file "yoke/with-editor/lisp")))
206 (autoload #'transient--with-suspended-override "transient") 208 (autoload #'transient--with-suspended-override "transient")
207 (autoload #'magit "magit" nil :interactive)) 209 (autoload #'magit "magit" nil :interactive))
210
211(yoke visual-fill-column "https://codeberg.org/joostkremers/visual-fill-column"
212 (setq visual-fill-column-center-text t)
213 (add-hook* 'visual-fill-column-mode-hook #'visual-line-mode)
214 (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust))
215
216(yoke org "https://git.savannah.gnu.org/git/emacs/org-mode.git"
217 :load (locate-user-emacs-file "yoke/org/lisp/")
218 :depends ((org-contrib "https://git.sr.ht/~bzg/org-contrib"
219 (locate-user-emacs-file "yoke/org-contrib/lisp")))
220 ;; DON'T load system org
221 (setq load-path
222 (cl-remove-if (lambda (path) (string-match-p "lisp/org\\'" path)) load-path))
223 (setq org-adapt-indentation nil
224 org-auto-align-tags t
225 org-archive-mark-done t
226 org-fold-catch-invisible-edits 'show-and-error
227 org-clock-clocked-in-display 'mode-line
228 org-clock-frame-title-format (cons
229 '(t org-mode-line-string)
230 (cons " --- " frame-title-format))
231 org-clock-string-limit 7 ; just the clock bit
232 ;; org-clock-string-limit 25 ; gives enough information
233 org-clock-persist nil
234 org-confirm-babel-evaluate nil
235 org-cycle-separator-lines 0
236 org-directory (sync/ "org/" t)
237 org-ellipsis (or truncate-string-ellipsis "…")
238 org-fontify-done-headline t
239 org-fontify-quote-and-verse-blocks t
240 org-fontify-whole-heading-line t
241 org-hide-emphasis-markers t
242 org-html-coding-system 'utf-8-unix
243 org-image-actual-width (list (* (window-font-width)
244 (- fill-column 8)))
245 org-imenu-depth 3
246 org-indent-indentation-per-level 0
247 org-indent-mode-turns-on-hiding-stars nil
248 org-insert-heading-respect-content t
249 org-list-demote-modify-bullet '(("-" . "+")
250 ("+" . "-"))
251 org-log-done 'time
252 org-log-into-drawer t
253 org-num-skip-commented t
254 org-num-skip-unnumbered t
255 org-num-skip-footnotes t
256 org-outline-path-complete-in-steps nil
257 org-pretty-entities t
258 org-pretty-entities-include-sub-superscripts nil
259 org-refile-targets '((nil . (:maxlevel . 2))
260 (org-agenda-files . (:maxlevel . 1)))
261 org-refile-use-outline-path 'file
262 org-special-ctrl-a/e t
263 org-special-ctrl-k t
264 org-src-fontify-natively t
265 org-src-tab-acts-natively t
266 org-src-window-setup 'current-window
267 org-startup-truncated nil
268 org-startup-with-inline-images t
269 org-tags-column -77 ;; (- (- fill-column 1 (length org-ellipsis)))
270 org-todo-keywords
271 '((sequence "TODO(t)" "WAIT(w@/!)" "ONGOING(o@)"
272 "|" "DONE(d!)" "ASSIGNED(a@/!)")
273 (sequence "|" "CANCELED(k@)")
274 (sequence "MEETING(m)"))
275 org-use-speed-commands t
276 org-emphasis-alist '(("*" org-bold)
277 ("/" org-italic)
278 ("_" org-underline)
279 ("=" org-verbatim)
280 ("~" org-code)
281 ("+" org-strikethrough)))
282 ;; (setq org-todo-keywords
283 ;; '((sequence
284 ;; "TODO(t)"
285 ;; "NEXT(n!)" ; next action
286 ;; "DONE(d)" ; done)
287 ;; (sequence
288 ;; "WAIT(w@)" ; waiting to be actionable again
289 ;; "HOLD(h@/!)" ; actinable, but will do later
290 ;; "IDEA(i)" ; maybe someday
291 ;; "KILL(k@/!)" ; cancelled, aborted or is no longer applicable
292 ;; ))))))
293 (add-hook* 'org-mode-hook
294 #'variable-pitch-mode
295 #'visual-fill-column-mode
296 #'turn-off-auto-fill
297 #'org-indent-mode
298 #'prettify-symbols-mode
299 #'abbrev-mode)
300 (eval-after org
301 (require '+org)
302 (define-keys org-mode-map
303 "C-M-k" #'kill-paragraph
304 "C-M-t" #'transpose-paragraphs)
305 (org-clock-persistence-insinuate)))
306
307(yoke org-agenda nil
308 (setq org-agenda-skip-deadline-if-done t
309 org-agenda-skip-scheduled-if-done t
310 org-agenda-span 10
311 org-agenda-block-separator ?─
312 org-agenda-time-grid
313 '((daily today require-timed)
314 (800 1000 1200 1400 1600 1800 2000)
315 " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")
316 org-agenda-current-time-string
317 "← now ─────────────────────────────────────────────────"
318 org-agenda-include-diary nil ; I use the org-diary features
319 org-agenda-todo-ignore-deadlines 'near
320 org-agenda-todo-ignore-scheduled 'future
321 org-agenda-include-deadlines t
322 org-deadline-warning-days 0
323 org-agenda-show-future-repeats 'next
324 org-agenda-window-setup 'current-window)
325 (setq-local-hook org-agenda-mode-hook
326 truncate-lines t)
327 (add-hook 'org-agenda-after-show-hook #'org-narrow-to-subtree))
328
329(yoke ox nil ; org-export
330 (eval-after org (require 'ox))
331 (eval-after ox
332 (require '+ox)
333 (require 'ox-md nil :noerror)
334 (+org-export-pre-hooks-insinuate))
335 (setq org-export-coding-system 'utf-8-unix
336 org-export-headline-levels 8
337 org-export-with-drawers nil
338 org-export-with-section-numbers nil
339 org-export-with-smart-quotes t
340 org-export-with-sub-superscripts t
341 org-export-with-toc nil))
342
343(yoke _work (sync/ "emacs/private")
344 :depends ((+org-capture (locate-user-emacs-file "lisp"))
345 (private (locate-user-emacs-file "lisp"))
346 (bbdb "https://git.savannah.nongnu.org/git/bbdb.git"
347 (locate-user-emacs-file "yoke/bbdb/lisp"))
348 (bbdb-vcard "https://github.com/tohojo/bbdb-vcard/"))
349 (require 'bbdb)
350 (require 'private)
351 (require '_work)
352 (bbdb-initialize 'gnus 'message)
353 (setq bbdb-complete-mail-allow-cycling t))
354
355(yoke org-taskwise "https://codeberg.org/acdw/org-taskwise.el")
356
357(yoke titlecase "https://codeberg.org/acdw/titlecase.el"
358 (eval-after org (require 'titlecase))
359 (eval-after titlecase
360 (require '+titlecase)
361 (add-to-list* 'titlecase-skip-words-regexps (rx word-boundary
362 (+ (any upper digit))
363 word-boundary))))
364
365(yoke flyspell-correct "https://github.com/duckwork/flyspell-correct"
366 (eval-after flyspell
367 (require 'flyspell-correct)
368 (require '+flyspell-correct)
369 (define-keys flyspell-mode-map
370 "C-;" #'flyspell-correct-wrapper
371 "<f7>" #'+flyspell-correct-buffer))
372 (add-hook 'org-mode-hook #'flyspell-mode)
373 (setq flyspell-correct--cr-key ";"))
diff --git a/lisp/+emacs.el b/lisp/+emacs.el index 6f37b83..6f40cf0 100644 --- a/lisp/+emacs.el +++ b/lisp/+emacs.el
@@ -301,8 +301,8 @@ ARG is passed to `backward-kill-word'."
301(defun +yank@indent (&rest _) 301(defun +yank@indent (&rest _)
302 "Indent the current region." 302 "Indent the current region."
303 (indent-region (min (point) (mark)) (max (point) (mark)))) 303 (indent-region (min (point) (mark)) (max (point) (mark))))
304(advice-add #'yank :after #'+yank@indent) 304;; (advice-add #'yank :after #'+yank@indent)
305(advice-add #'yank-pop :after #'+yank@indent) 305;; (advice-add #'yank-pop :after #'+yank@indent)
306 306
307 307
308;;; Extra functions 308;;; Extra functions
diff --git a/lisp/+flyspell-correct.el b/lisp/+flyspell-correct.el new file mode 100644 index 0000000..f4fc956 --- /dev/null +++ b/lisp/+flyspell-correct.el
@@ -0,0 +1,24 @@
1;;; +flyspell-correct.el --- -*- lexical-binding: t; -*-
2
3;;; Code:
4
5(require 'flyspell-correct)
6
7(defun +flyspell-correct-buffer (&optional prefix)
8 "Run `flyspell-correct-wrapper' on all misspelled words in the buffer.
9With PREFIX, prompt to change the current dictionary."
10 (interactive "P")
11 (flyspell-buffer)
12 (when prefix
13 (let ((current-prefix-arg nil))
14 (call-interactively #'ispell-change-dictionary)))
15 (+with-message "Checking spelling"
16 (flyspell-correct-move (point-min) :forward :rapid)))
17
18(defun +flyspell-correct-buffer-h (&rest _)
19 "Run `+flyspell-correct-buffer'.
20This is suitable for placement in a hook."
21 (+flyspell-correct-buffer))
22
23(provide '+flyspell-correct)
24;;; +flyspell-correct.el ends here
diff --git a/lisp/+org-capture.el b/lisp/+org-capture.el new file mode 100644 index 0000000..7ed4e00 --- /dev/null +++ b/lisp/+org-capture.el
@@ -0,0 +1,164 @@
1;;; +org-capture.el -*- lexical-binding: t; -*-
2
3;;; Code:
4
5(require 'cl-lib)
6(require 'acdw)
7;; We don't require `org-capture' here because I'll have to require this library
8;; to init.el /before/ org-capture is fully needed. But I do need to declare
9;; `org-capture-templates'.
10(defvar org-capture-templates nil)
11
12(defun +org-capture--get (key &optional list)
13 "Find KEY in LIST, or return nil.
14LIST defaults to `org-capture-templates'."
15 (alist-get key (or list org-capture-templates) nil nil #'equal))
16
17;; Set it up as a generic value. Based on the one for `alist-get'.
18(gv-define-expander +org-capture--get
19 (lambda (do key &optional alist)
20 (setq alist (or alist org-capture-templates))
21 (macroexp-let2 macroexp-copyable-p k key
22 (gv-letplace (getter setter) alist
23 (macroexp-let2 nil p `(assoc ,k ,getter 'equal)
24 (funcall do `(cdr ,p)
25 (lambda (v)
26 (macroexp-let2 nil v v
27 (let ((set-exp
28 `(if ,p (setcdr ,p ,v)
29 ,(funcall setter
30 `(cons (setq ,p (cons ,k ,v))
31 ,getter)))))
32 `(progn
33 ,set-exp
34 ,v))))))))))
35
36(defun +org-capture-sort (&optional list)
37 "Sort LIST by string keys.
38LIST is a symbol and defaults to `org-capture-templates'."
39 (setq list (or list 'org-capture-templates))
40 (set list (sort (symbol-value list) (lambda (a b)
41 (string< (car a) (car b))))))
42
43(defun +org-capture-sort-after-init (&optional list)
44 "Sort LIST with `+org-capture-sort' after Emacs init."
45 (+ensure-after-init #'+org-capture-sort))
46
47;;;###autoload
48(defun +org-capture-templates-setf (key value &optional list sort-after)
49 "Add KEY to LIST, using `setf'.
50LIST is a symbol and defaults to `org-capture-templates' -- so
51this function sets values on a list that's structured as such.
52
53Thus, KEY is a string key. If it's longer than one character,
54this function will search LIST for each successive run of
55characters before the final, ensuring sub-lists exist of the
56form (CHARS DESCRIPTION).
57
58For example, if KEY is \"abc\", first a LIST item of the form (a
59DESCRIPTION), if non-existant, will be added to the list (with a
60default description), then an item of the
61form (\"ab\" DESCRIPTION), before adding (KEY VALUE) to the LIST.
62
63VALUE is the template or group header required for
64`org-capture-templates', which see.
65
66SORT-AFTER, when set to t, will call
67`+org-capture-templates-sort' after setting, to ensure org can
68properly process the variable."
69 ;; LIST defaults to `org-capture-templates'
70 (declare (indent 2))
71 (unless list (setq list 'org-capture-templates))
72 ;; Ensure VALUE is a list to cons properly
73 (unless (listp value) (setq value (list value)))
74 (when (> (length key) 1)
75 ;; Check for existence of groups.
76 (let ((expected (cl-loop for i from 1 to (1- (length key))
77 collect (substring key 0 i) into keys
78 finally return keys)))
79 (cl-loop for ek in expected
80 if (not (+org-capture--get ek (symbol-value list))) do
81 (setf (+org-capture--get ek (symbol-value list))
82 (list (format "(Group %s)" ek))))))
83 (prog1 ;; Set KEY to VALUE
84 (setf (+org-capture--get key (symbol-value list)) value)
85 ;; Sort after, maybe
86 (when sort-after (+org-capture-sort list))))
87
88(defun +org-template--ensure-path (keys &optional list)
89 "Ensure path of keys exists in `org-capture-templates'."
90 (unless list (setq list 'org-capture-templates))
91 (when (> (length key) 1)
92 ;; Check for existence of groups.
93 (let ((expected (cl-loop for i from 1 to (1- (length key))
94 collect (substring key 0 i) into keys
95 finally return keys)))
96 (cl-loop for ek in expected
97 if (not (+org-capture--get ek (symbol-value list))) do
98 (setf (+org-capture--get ek (symbol-value list))
99 (list (format "(Group %s)" ek)))))))
100
101(defcustom +org-capture-default-type 'entry
102 "Default template for `org-capture-templates'."
103 :type '(choice (const :tag "Entry" entry)
104 (const :tag "Item" item)
105 (const :tag "Check Item" checkitem)
106 (const :tag "Table Line" table-line)
107 (const :tag "Plain Text" plain)))
108
109(defcustom +org-capture-default-target ""
110 "Default target for `org-capture-templates'."
111 ;; TODO: type
112 )
113
114(defcustom +org-capture-default-template nil
115 "Default template for `org-capture-templates'."
116 ;; TODO: type
117 )
118
119(defun +org-define-capture-templates-group (keys description)
120 "Add a group title to `org-capture-templates'."
121 (setf (+org-capture--get keys org-capture-templates)
122 (list description)))
123
124;; [[https://github.com/cadadr/configuration/blob/39813a771286e542af3aa333172858532c3bb257/emacs.d/gk/gk-org.el#L1573][from cadadr]]
125(defun +org-define-capture-template (keys description &rest args)
126 "Define a capture template and necessary antecedents.
127ARGS is a plist, which in addition to the additional options
128`org-capture-templates' accepts, takes the following and places
129them accordingly: :type, :target, and :template. Each of these
130corresponds to the same field in `org-capture-templates's
131docstring, which see. Likewise with KEYS and DESCRIPTION, which
132are passed separately to the function.
133
134This function will also create all the necessary intermediate
135capture keys needed for `org-capture'; that is, if KEYS is
136\"wcp\", entries for \"w\" and \"wc\" will both be ensured in
137`org-capture-templates'."
138 (declare (indent 2))
139 ;; Check for existence of parent groups
140 (when (> (length keys) 1)
141 (let ((expected (cl-loop for i from 1 to (1- (length keys))
142 collect (substring 0 i) into keys
143 finally return keys)))
144 (cl-loop
145 for ek in expected
146 if (not (+org-capture--get ek org-capture-templates))
147 do (+org-define-capture-templates-group ek (format "(Group %s)" ek)))))
148 (if (null args)
149 ;; Add the title
150 (+org-define-capture-templates-group keys description)
151 ;; Add the capture template.
152 (setf (+org-capture--get keys org-capture-templates)
153 (append (list (or (plist-get args :type)
154 +org-capture-default-type)
155 (or ( plist-get args :target)
156 +org-capture-default-target)
157 (or (plist-get args :template)
158 +org-capture-default-template))
159 (cl-loop for (key val) on args by #'cddr
160 unless (member key '(:type :target :template))
161 append (list key val))))))
162
163(provide '+org-capture)
164;;; +org-capture.el ends here
diff --git a/lisp/+org.el b/lisp/+org.el new file mode 100644 index 0000000..b17a1fa --- /dev/null +++ b/lisp/+org.el
@@ -0,0 +1,44 @@
1;;; +org.el --- -*- lexical-binding: t -*-
2
3;;; Copy org trees as HTML
4
5;; Thanks to Oleh Krehel, via [[https://emacs.stackexchange.com/questions/54292/copy-results-of-org-export-directly-to-clipboard][this StackExchange question]].
6(defun +org-export-clip-to-html
7 (&optional async subtreep visible-only body-only ext-plist post-process)
8 "Export region to HTML, and copy it to the clipboard.
9Arguments ASYNC, SUBTREEP, VISIBLE-ONLY, BODY-ONLY, EXT-PLIST,
10and POST-PROCESS are passed to `org-export-to-file'."
11 (interactive) ; XXX: hould this be interactive?
12 (message "Exporting Org to HTML...")
13 (let ((org-tmp-file "/tmp/org.html"))
14 (org-export-to-file 'html org-tmp-file
15 async subtreep visible-only body-only ext-plist post-process)
16 (start-process "xclip" "*xclip*"
17 "xclip" "-verbose"
18 "-i" org-tmp-file
19 "-t" "text/html"
20 "-selection" "clipboard"))
21 (message "Exporting Org to HTML...done."))
22
23;; Specialized functions
24(defun +org-export-clip-subtree-to-html ()
25 "Export current subtree to HTML."
26 (interactive)
27 (+org-export-clip-to-html nil :subtree))
28
29;;; Unsmartify quotes and dashes and stuff.
30
31(defun +org-unsmartify ()
32 "Replace \"smart\" punctuation with their \"dumb\" counterparts."
33 (interactive)
34 (save-excursion
35 (goto-char (point-min))
36 (while (re-search-forward "[“”‘’–—]" nil t)
37 (let ((replace (pcase (match-string 0)
38 ((or "“" "”") "\"")
39 ((or "‘" "’") "'")
40 ("–" "--")
41 ("—" "---"))))
42 (replace-match replace nil nil)))))
43
44(provide '+org)
diff --git a/lisp/+ox.el b/lisp/+ox.el new file mode 100644 index 0000000..8748a55 --- /dev/null +++ b/lisp/+ox.el
@@ -0,0 +1,29 @@
1;;; +ox.el --- org-export helpers -*- lexical-binding: t; -*-
2
3;;; Commentary:
4
5;;; Code:
6
7(require 'ox)
8
9;;; Run hooks before doing any exporting at all
10
11(defcustom +org-export-pre-hook nil
12 "Functions to run /before/ `org-export-as' does anything.
13These will run on the buffer about to be exported, NOT a copy."
14 :type 'hook)
15
16(defun +org-export-pre-run-hooks (&rest _)
17 "Run hooks in `+org-export-pre-hook'."
18 (run-hooks '+org-export-pre-hook))
19
20(defun +org-export-pre-hooks-insinuate ()
21 "Advise `org-export-as' to run `+org-export-pre-hook'."
22 (advice-add 'org-export-as :before #'+org-export-pre-run-hooks))
23
24(defun +org-export-pre-hooks-remove ()
25 "Remove pre-hook advice on `org-export-as'."
26 (advice-remove 'org-export-as #'+org-export-pre-run-hooks))
27
28(provide '+ox)
29;;; +ox.el ends here
diff --git a/lisp/+titlecase.el b/lisp/+titlecase.el new file mode 100644 index 0000000..9266807 --- /dev/null +++ b/lisp/+titlecase.el
@@ -0,0 +1,32 @@
1;;; +titlecase.el --- Titlecase extras -*- lexical-binding: t; -*-
2
3;;; Commentary:
4
5;;; Code:
6
7(require 'titlecase)
8
9(defun +titlecase-sentence-style-dwim (&optional arg)
10 "Titlecase a sentence.
11With prefix ARG, toggle the value of
12`titlecase-downcase-sentences' before sentence-casing."
13 (interactive "P")
14 (let ((titlecase-downcase-sentences (if arg (not titlecase-downcase-sentences)
15 titlecase-downcase-sentences)))
16 (titlecase-dwim 'sentence)))
17
18(defun +titlecase-org-headings ()
19 (interactive)
20 (save-excursion
21 (goto-char (point-min))
22 ;; See also `org-map-tree'. I'm not using that function because I want to
23 ;; skip the first headline. A better solution would be to patch
24 ;; `titlecase-line' to ignore org-mode metadata (TODO cookies, tags, etc).
25 (let ((level (funcall outline-level)))
26 (while (and (progn (outline-next-heading)
27 (> (funcall outline-level) level))
28 (not (eobp)))
29 (titlecase-line)))))
30
31(provide '+titlecase)
32;;; +titlecase.el ends here
diff --git a/lisp/acdw.el b/lisp/acdw.el index f972d08..444f249 100644 --- a/lisp/acdw.el +++ b/lisp/acdw.el
@@ -45,23 +45,6 @@ Convenience wrapper around `define-key'."
45 map) 45 map)
46 key def))))) 46 key def)))))
47 47
48(defmacro setq-local-hook (hook &rest args)
49 "Run `setq-local' on ARGS when running HOOK."
50 (declare (indent 1))
51 (let ((fn (intern (format "%s-setq-local" hook))))
52 (when (and (fboundp fn)
53 (functionp fn))
54 (setq args (append (function-get fn 'setq-local-hook-settings) args)))
55 (unless (and (< 0 (length args))
56 (zerop (mod (length args) 2)))
57 (user-error "Wrong number of arguments: %S" (length args)))
58 `(progn
59 (defun ,fn ()
60 ,(format "Set local variables after `%s'." hook)
61 (setq-local ,@args))
62 (function-put ',fn 'setq-local-hook-settings ',args)
63 (add-hook ',hook #',fn))))
64
65(unless (fboundp 'ensure-list) 48(unless (fboundp 'ensure-list)
66 ;; Just in case we're using an old version of Emacs. 49 ;; Just in case we're using an old version of Emacs.
67 (defun ensure-list (object) 50 (defun ensure-list (object)
@@ -89,3 +72,34 @@ form (FUNCTION &optional DEPTH LOCAL)."
89 (dolist (hook (ensure-list hooks)) 72 (dolist (hook (ensure-list hooks))
90 (dolist (fn functions) 73 (dolist (fn functions)
91 (apply #'add-hook hook (ensure-list fn))))) 74 (apply #'add-hook hook (ensure-list fn)))))
75
76;;; Convenience macros
77
78(defmacro setq-local-hook (hook &rest args)
79 "Run `setq-local' on ARGS when running HOOK."
80 (declare (indent 1))
81 (let ((fn (intern (format "%s-setq-local" hook))))
82 (when (and (fboundp fn)
83 (functionp fn))
84 (setq args (append (function-get fn 'setq-local-hook-settings) args)))
85 (unless (and (< 0 (length args))
86 (zerop (mod (length args) 2)))
87 (user-error "Wrong number of arguments: %S" (length args)))
88 `(progn
89 (defun ,fn ()
90 ,(format "Set local variables after `%s'." hook)
91 (setq-local ,@args))
92 (function-put ',fn 'setq-local-hook-settings ',args)
93 (add-hook ',hook #',fn))))
94
95(defmacro with-message (message &rest body)
96 "Execute BODY, with MESSAGE.
97If body executes without errors, MESSAGE...Done will be displayed."
98 (declare (indent 1))
99 (let ((msg (gensym)))
100 `(let ((,msg ,message))
101 (condition-case e
102 (progn (message "%s..." ,msg)
103 ,@body)
104 (:success (message "%s...done" ,msg))
105 (t (signal (car e) (cdr e)))))))
diff --git a/lisp/private.el b/lisp/private.el new file mode 100644 index 0000000..4f6115e --- /dev/null +++ b/lisp/private.el
@@ -0,0 +1,23 @@
1;;; private.el -*- lexical-binding: t; -*-
2
3;;; Commentary:
4
5;;; Code:
6
7(require 'acdw)
8
9(defgroup private nil
10 "Private things are private. Shhhhh....")
11
12;; Private directory
13
14(+define-dir private/ (sync/ "emacs/private")
15 "Private secretive secrets inside.")
16(add-to-list 'load-path private/)
17
18;; Load random private stuff
19
20(require '_acdw)
21
22(provide 'private)
23;;; private.el ends here