From e5c025158ef6e383aa892ef88e63a2a32de3d7fb Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Tue, 18 Oct 2022 12:08:21 -0500 Subject: Add a bunch of other stuff or whatever --- early-init.el | 10 +-- init.el | 174 ++++++++++++++++++++++++++++++++++++++++++++-- lisp/+emacs.el | 4 +- lisp/+flyspell-correct.el | 24 +++++++ lisp/+org-capture.el | 164 +++++++++++++++++++++++++++++++++++++++++++ lisp/+org.el | 44 ++++++++++++ lisp/+ox.el | 29 ++++++++ lisp/+titlecase.el | 32 +++++++++ lisp/acdw.el | 48 ++++++++----- lisp/private.el | 23 ++++++ 10 files changed, 524 insertions(+), 28 deletions(-) create mode 100644 lisp/+flyspell-correct.el create mode 100644 lisp/+org-capture.el create mode 100644 lisp/+org.el create mode 100644 lisp/+ox.el create mode 100644 lisp/+titlecase.el create mode 100644 lisp/private.el 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." (push (locate-user-emacs-file "lisp") load-path) (require 'acdw) -(+define-dir .etc (locate-user-emacs-file ".etc") +(+define-dir .etc (locate-user-emacs-file "etc") "Directory for all of Emacs's various files. See `no-littering' for examples.") @@ -81,13 +81,13 @@ See `no-littering' for examples.") (yoke compat "https://git.sr.ht/~pkal/compat") (yoke no-littering "https://github.com/emacscollective/no-littering" - (require 'no-littering) (setq no-littering-etc-directory .etc no-littering-var-directory .etc custom-file (.etc "custom.el")) + (require 'no-littering) + (when (boundp 'native-comp-eln-load-path) + (setcar native-comp-eln-load-path (expand-file-name (.etc "eln-cache" t)))) (when (boundp 'comp-eln-load-path) (setcar comp-eln-load-path (expand-file-name (.etc "eln-cache" t)))) (when (fboundp 'startup-redirect-eln-cache) - (startup-redirect-eln-cache - (convert-standard-filename - (.etc "eln-cache/"))))) + (startup-redirect-eln-cache (convert-standard-filename (.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 @@ "M-/" #'hippie-expand "M-=" #'count-words "C-x C-b" #'ibuffer -"C-x 4 n" #'clone-buffer + "C-x 4 n" #'clone-buffer "S-" #'mouse-set-mark "C-x 0" #'+delete-window-or-bury-buffer "M-j" nil @@ -42,7 +42,9 @@ (let ((buffer-quit-function #'ignore)) (apply fn r))) ;; Themes - (load-theme 'modus-operandi)) + (load-theme 'modus-operandi) + (set-face-attribute 'default nil :family "Comic Code" :height 100) + (set-face-attribute 'variable-pitch nil :family "Comic Code" :height 100)) (yoke isearch nil (define-keys (current-global-map) @@ -64,7 +66,7 @@ tab-always-indent 'complete completion-in-region-function #'consult-completion-in-region consult-narrow-key "<" - consult--regexp-compiler #'consult--orderless-regexp-compiler) + consult--regexp-compiler #'consult--default-regexp-compiler) (advice-add #'register-preview :override #'consult-register-window) (define-keys (current-global-map) ;; C-c bindings (mode-specific-map) @@ -118,7 +120,7 @@ (define-key org-mode-map (kbd "M-g o") #'consult-org-heading))) (yoke orderless "https://github.com/oantolin/orderless" -(require 'orderless) + (require 'orderless) (setq completion-styles '(substring orderless basic) completion-category-defaults nil completion-category-overrides '((file (styles basic partial-completion))) @@ -205,3 +207,167 @@ (locate-user-emacs-file "yoke/with-editor/lisp"))) (autoload #'transient--with-suspended-override "transient") (autoload #'magit "magit" nil :interactive)) + +(yoke visual-fill-column "https://codeberg.org/joostkremers/visual-fill-column" + (setq visual-fill-column-center-text t) + (add-hook* 'visual-fill-column-mode-hook #'visual-line-mode) + (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)) + +(yoke org "https://git.savannah.gnu.org/git/emacs/org-mode.git" + :load (locate-user-emacs-file "yoke/org/lisp/") + :depends ((org-contrib "https://git.sr.ht/~bzg/org-contrib" + (locate-user-emacs-file "yoke/org-contrib/lisp"))) + ;; DON'T load system org + (setq load-path + (cl-remove-if (lambda (path) (string-match-p "lisp/org\\'" path)) load-path)) + (setq org-adapt-indentation nil + org-auto-align-tags t + org-archive-mark-done t + org-fold-catch-invisible-edits 'show-and-error + org-clock-clocked-in-display 'mode-line + org-clock-frame-title-format (cons + '(t org-mode-line-string) + (cons " --- " frame-title-format)) + org-clock-string-limit 7 ; just the clock bit + ;; org-clock-string-limit 25 ; gives enough information + org-clock-persist nil + org-confirm-babel-evaluate nil + org-cycle-separator-lines 0 + org-directory (sync/ "org/" t) + org-ellipsis (or truncate-string-ellipsis "…") + org-fontify-done-headline t + org-fontify-quote-and-verse-blocks t + org-fontify-whole-heading-line t + org-hide-emphasis-markers t + org-html-coding-system 'utf-8-unix + org-image-actual-width (list (* (window-font-width) + (- fill-column 8))) + org-imenu-depth 3 + org-indent-indentation-per-level 0 + org-indent-mode-turns-on-hiding-stars nil + org-insert-heading-respect-content t + org-list-demote-modify-bullet '(("-" . "+") + ("+" . "-")) + org-log-done 'time + org-log-into-drawer t + org-num-skip-commented t + org-num-skip-unnumbered t + org-num-skip-footnotes t + org-outline-path-complete-in-steps nil + org-pretty-entities t + org-pretty-entities-include-sub-superscripts nil + org-refile-targets '((nil . (:maxlevel . 2)) + (org-agenda-files . (:maxlevel . 1))) + org-refile-use-outline-path 'file + org-special-ctrl-a/e t + org-special-ctrl-k t + org-src-fontify-natively t + org-src-tab-acts-natively t + org-src-window-setup 'current-window + org-startup-truncated nil + org-startup-with-inline-images t + org-tags-column -77 ;; (- (- fill-column 1 (length org-ellipsis))) + org-todo-keywords + '((sequence "TODO(t)" "WAIT(w@/!)" "ONGOING(o@)" + "|" "DONE(d!)" "ASSIGNED(a@/!)") + (sequence "|" "CANCELED(k@)") + (sequence "MEETING(m)")) + org-use-speed-commands t + org-emphasis-alist '(("*" org-bold) + ("/" org-italic) + ("_" org-underline) + ("=" org-verbatim) + ("~" org-code) + ("+" org-strikethrough))) + ;; (setq org-todo-keywords + ;; '((sequence + ;; "TODO(t)" + ;; "NEXT(n!)" ; next action + ;; "DONE(d)" ; done) + ;; (sequence + ;; "WAIT(w@)" ; waiting to be actionable again + ;; "HOLD(h@/!)" ; actinable, but will do later + ;; "IDEA(i)" ; maybe someday + ;; "KILL(k@/!)" ; cancelled, aborted or is no longer applicable + ;; )))))) + (add-hook* 'org-mode-hook + #'variable-pitch-mode + #'visual-fill-column-mode + #'turn-off-auto-fill + #'org-indent-mode + #'prettify-symbols-mode + #'abbrev-mode) + (eval-after org + (require '+org) + (define-keys org-mode-map + "C-M-k" #'kill-paragraph + "C-M-t" #'transpose-paragraphs) + (org-clock-persistence-insinuate))) + +(yoke org-agenda nil + (setq org-agenda-skip-deadline-if-done t + org-agenda-skip-scheduled-if-done t + org-agenda-span 10 + org-agenda-block-separator ?─ + org-agenda-time-grid + '((daily today require-timed) + (800 1000 1200 1400 1600 1800 2000) + " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄") + org-agenda-current-time-string + "← now ─────────────────────────────────────────────────" + org-agenda-include-diary nil ; I use the org-diary features + org-agenda-todo-ignore-deadlines 'near + org-agenda-todo-ignore-scheduled 'future + org-agenda-include-deadlines t + org-deadline-warning-days 0 + org-agenda-show-future-repeats 'next + org-agenda-window-setup 'current-window) + (setq-local-hook org-agenda-mode-hook + truncate-lines t) + (add-hook 'org-agenda-after-show-hook #'org-narrow-to-subtree)) + +(yoke ox nil ; org-export + (eval-after org (require 'ox)) + (eval-after ox + (require '+ox) + (require 'ox-md nil :noerror) + (+org-export-pre-hooks-insinuate)) + (setq org-export-coding-system 'utf-8-unix + org-export-headline-levels 8 + org-export-with-drawers nil + org-export-with-section-numbers nil + org-export-with-smart-quotes t + org-export-with-sub-superscripts t + org-export-with-toc nil)) + +(yoke _work (sync/ "emacs/private") + :depends ((+org-capture (locate-user-emacs-file "lisp")) + (private (locate-user-emacs-file "lisp")) + (bbdb "https://git.savannah.nongnu.org/git/bbdb.git" + (locate-user-emacs-file "yoke/bbdb/lisp")) + (bbdb-vcard "https://github.com/tohojo/bbdb-vcard/")) + (require 'bbdb) + (require 'private) + (require '_work) + (bbdb-initialize 'gnus 'message) + (setq bbdb-complete-mail-allow-cycling t)) + +(yoke org-taskwise "https://codeberg.org/acdw/org-taskwise.el") + +(yoke titlecase "https://codeberg.org/acdw/titlecase.el" + (eval-after org (require 'titlecase)) + (eval-after titlecase + (require '+titlecase) + (add-to-list* 'titlecase-skip-words-regexps (rx word-boundary + (+ (any upper digit)) + word-boundary)))) + +(yoke flyspell-correct "https://github.com/duckwork/flyspell-correct" + (eval-after flyspell + (require 'flyspell-correct) + (require '+flyspell-correct) + (define-keys flyspell-mode-map + "C-;" #'flyspell-correct-wrapper + "" #'+flyspell-correct-buffer)) + (add-hook 'org-mode-hook #'flyspell-mode) + (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'." (defun +yank@indent (&rest _) "Indent the current region." (indent-region (min (point) (mark)) (max (point) (mark)))) -(advice-add #'yank :after #'+yank@indent) -(advice-add #'yank-pop :after #'+yank@indent) +;; (advice-add #'yank :after #'+yank@indent) +;; (advice-add #'yank-pop :after #'+yank@indent) ;;; 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 @@ +;;; +flyspell-correct.el --- -*- lexical-binding: t; -*- + +;;; Code: + +(require 'flyspell-correct) + +(defun +flyspell-correct-buffer (&optional prefix) + "Run `flyspell-correct-wrapper' on all misspelled words in the buffer. +With PREFIX, prompt to change the current dictionary." + (interactive "P") + (flyspell-buffer) + (when prefix + (let ((current-prefix-arg nil)) + (call-interactively #'ispell-change-dictionary))) + (+with-message "Checking spelling" + (flyspell-correct-move (point-min) :forward :rapid))) + +(defun +flyspell-correct-buffer-h (&rest _) + "Run `+flyspell-correct-buffer'. +This is suitable for placement in a hook." + (+flyspell-correct-buffer)) + +(provide '+flyspell-correct) +;;; +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 @@ +;;; +org-capture.el -*- lexical-binding: t; -*- + +;;; Code: + +(require 'cl-lib) +(require 'acdw) +;; We don't require `org-capture' here because I'll have to require this library +;; to init.el /before/ org-capture is fully needed. But I do need to declare +;; `org-capture-templates'. +(defvar org-capture-templates nil) + +(defun +org-capture--get (key &optional list) + "Find KEY in LIST, or return nil. +LIST defaults to `org-capture-templates'." + (alist-get key (or list org-capture-templates) nil nil #'equal)) + +;; Set it up as a generic value. Based on the one for `alist-get'. +(gv-define-expander +org-capture--get + (lambda (do key &optional alist) + (setq alist (or alist org-capture-templates)) + (macroexp-let2 macroexp-copyable-p k key + (gv-letplace (getter setter) alist + (macroexp-let2 nil p `(assoc ,k ,getter 'equal) + (funcall do `(cdr ,p) + (lambda (v) + (macroexp-let2 nil v v + (let ((set-exp + `(if ,p (setcdr ,p ,v) + ,(funcall setter + `(cons (setq ,p (cons ,k ,v)) + ,getter))))) + `(progn + ,set-exp + ,v)))))))))) + +(defun +org-capture-sort (&optional list) + "Sort LIST by string keys. +LIST is a symbol and defaults to `org-capture-templates'." + (setq list (or list 'org-capture-templates)) + (set list (sort (symbol-value list) (lambda (a b) + (string< (car a) (car b)))))) + +(defun +org-capture-sort-after-init (&optional list) + "Sort LIST with `+org-capture-sort' after Emacs init." + (+ensure-after-init #'+org-capture-sort)) + +;;;###autoload +(defun +org-capture-templates-setf (key value &optional list sort-after) + "Add KEY to LIST, using `setf'. +LIST is a symbol and defaults to `org-capture-templates' -- so +this function sets values on a list that's structured as such. + +Thus, KEY is a string key. If it's longer than one character, +this function will search LIST for each successive run of +characters before the final, ensuring sub-lists exist of the +form (CHARS DESCRIPTION). + +For example, if KEY is \"abc\", first a LIST item of the form (a +DESCRIPTION), if non-existant, will be added to the list (with a +default description), then an item of the +form (\"ab\" DESCRIPTION), before adding (KEY VALUE) to the LIST. + +VALUE is the template or group header required for +`org-capture-templates', which see. + +SORT-AFTER, when set to t, will call +`+org-capture-templates-sort' after setting, to ensure org can +properly process the variable." + ;; LIST defaults to `org-capture-templates' + (declare (indent 2)) + (unless list (setq list 'org-capture-templates)) + ;; Ensure VALUE is a list to cons properly + (unless (listp value) (setq value (list value))) + (when (> (length key) 1) + ;; Check for existence of groups. + (let ((expected (cl-loop for i from 1 to (1- (length key)) + collect (substring key 0 i) into keys + finally return keys))) + (cl-loop for ek in expected + if (not (+org-capture--get ek (symbol-value list))) do + (setf (+org-capture--get ek (symbol-value list)) + (list (format "(Group %s)" ek)))))) + (prog1 ;; Set KEY to VALUE + (setf (+org-capture--get key (symbol-value list)) value) + ;; Sort after, maybe + (when sort-after (+org-capture-sort list)))) + +(defun +org-template--ensure-path (keys &optional list) + "Ensure path of keys exists in `org-capture-templates'." + (unless list (setq list 'org-capture-templates)) + (when (> (length key) 1) + ;; Check for existence of groups. + (let ((expected (cl-loop for i from 1 to (1- (length key)) + collect (substring key 0 i) into keys + finally return keys))) + (cl-loop for ek in expected + if (not (+org-capture--get ek (symbol-value list))) do + (setf (+org-capture--get ek (symbol-value list)) + (list (format "(Group %s)" ek))))))) + +(defcustom +org-capture-default-type 'entry + "Default template for `org-capture-templates'." + :type '(choice (const :tag "Entry" entry) + (const :tag "Item" item) + (const :tag "Check Item" checkitem) + (const :tag "Table Line" table-line) + (const :tag "Plain Text" plain))) + +(defcustom +org-capture-default-target "" + "Default target for `org-capture-templates'." + ;; TODO: type + ) + +(defcustom +org-capture-default-template nil + "Default template for `org-capture-templates'." + ;; TODO: type + ) + +(defun +org-define-capture-templates-group (keys description) + "Add a group title to `org-capture-templates'." + (setf (+org-capture--get keys org-capture-templates) + (list description))) + +;; [[https://github.com/cadadr/configuration/blob/39813a771286e542af3aa333172858532c3bb257/emacs.d/gk/gk-org.el#L1573][from cadadr]] +(defun +org-define-capture-template (keys description &rest args) + "Define a capture template and necessary antecedents. +ARGS is a plist, which in addition to the additional options +`org-capture-templates' accepts, takes the following and places +them accordingly: :type, :target, and :template. Each of these +corresponds to the same field in `org-capture-templates's +docstring, which see. Likewise with KEYS and DESCRIPTION, which +are passed separately to the function. + +This function will also create all the necessary intermediate +capture keys needed for `org-capture'; that is, if KEYS is +\"wcp\", entries for \"w\" and \"wc\" will both be ensured in +`org-capture-templates'." + (declare (indent 2)) + ;; Check for existence of parent groups + (when (> (length keys) 1) + (let ((expected (cl-loop for i from 1 to (1- (length keys)) + collect (substring 0 i) into keys + finally return keys))) + (cl-loop + for ek in expected + if (not (+org-capture--get ek org-capture-templates)) + do (+org-define-capture-templates-group ek (format "(Group %s)" ek))))) + (if (null args) + ;; Add the title + (+org-define-capture-templates-group keys description) + ;; Add the capture template. + (setf (+org-capture--get keys org-capture-templates) + (append (list (or (plist-get args :type) + +org-capture-default-type) + (or ( plist-get args :target) + +org-capture-default-target) + (or (plist-get args :template) + +org-capture-default-template)) + (cl-loop for (key val) on args by #'cddr + unless (member key '(:type :target :template)) + append (list key val)))))) + +(provide '+org-capture) +;;; +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 @@ +;;; +org.el --- -*- lexical-binding: t -*- + +;;; Copy org trees as HTML + +;; Thanks to Oleh Krehel, via [[https://emacs.stackexchange.com/questions/54292/copy-results-of-org-export-directly-to-clipboard][this StackExchange question]]. +(defun +org-export-clip-to-html + (&optional async subtreep visible-only body-only ext-plist post-process) + "Export region to HTML, and copy it to the clipboard. +Arguments ASYNC, SUBTREEP, VISIBLE-ONLY, BODY-ONLY, EXT-PLIST, +and POST-PROCESS are passed to `org-export-to-file'." + (interactive) ; XXX: hould this be interactive? + (message "Exporting Org to HTML...") + (let ((org-tmp-file "/tmp/org.html")) + (org-export-to-file 'html org-tmp-file + async subtreep visible-only body-only ext-plist post-process) + (start-process "xclip" "*xclip*" + "xclip" "-verbose" + "-i" org-tmp-file + "-t" "text/html" + "-selection" "clipboard")) + (message "Exporting Org to HTML...done.")) + +;; Specialized functions +(defun +org-export-clip-subtree-to-html () + "Export current subtree to HTML." + (interactive) + (+org-export-clip-to-html nil :subtree)) + +;;; Unsmartify quotes and dashes and stuff. + +(defun +org-unsmartify () + "Replace \"smart\" punctuation with their \"dumb\" counterparts." + (interactive) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward "[“”‘’–—]" nil t) + (let ((replace (pcase (match-string 0) + ((or "“" "”") "\"") + ((or "‘" "’") "'") + ("–" "--") + ("—" "---")))) + (replace-match replace nil nil))))) + +(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 @@ +;;; +ox.el --- org-export helpers -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'ox) + +;;; Run hooks before doing any exporting at all + +(defcustom +org-export-pre-hook nil + "Functions to run /before/ `org-export-as' does anything. +These will run on the buffer about to be exported, NOT a copy." + :type 'hook) + +(defun +org-export-pre-run-hooks (&rest _) + "Run hooks in `+org-export-pre-hook'." + (run-hooks '+org-export-pre-hook)) + +(defun +org-export-pre-hooks-insinuate () + "Advise `org-export-as' to run `+org-export-pre-hook'." + (advice-add 'org-export-as :before #'+org-export-pre-run-hooks)) + +(defun +org-export-pre-hooks-remove () + "Remove pre-hook advice on `org-export-as'." + (advice-remove 'org-export-as #'+org-export-pre-run-hooks)) + +(provide '+ox) +;;; +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 @@ +;;; +titlecase.el --- Titlecase extras -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'titlecase) + +(defun +titlecase-sentence-style-dwim (&optional arg) + "Titlecase a sentence. +With prefix ARG, toggle the value of +`titlecase-downcase-sentences' before sentence-casing." + (interactive "P") + (let ((titlecase-downcase-sentences (if arg (not titlecase-downcase-sentences) + titlecase-downcase-sentences))) + (titlecase-dwim 'sentence))) + +(defun +titlecase-org-headings () + (interactive) + (save-excursion + (goto-char (point-min)) + ;; See also `org-map-tree'. I'm not using that function because I want to + ;; skip the first headline. A better solution would be to patch + ;; `titlecase-line' to ignore org-mode metadata (TODO cookies, tags, etc). + (let ((level (funcall outline-level))) + (while (and (progn (outline-next-heading) + (> (funcall outline-level) level)) + (not (eobp))) + (titlecase-line))))) + +(provide '+titlecase) +;;; +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'." map) key def))))) -(defmacro setq-local-hook (hook &rest args) - "Run `setq-local' on ARGS when running HOOK." - (declare (indent 1)) - (let ((fn (intern (format "%s-setq-local" hook)))) - (when (and (fboundp fn) - (functionp fn)) - (setq args (append (function-get fn 'setq-local-hook-settings) args))) - (unless (and (< 0 (length args)) - (zerop (mod (length args) 2))) - (user-error "Wrong number of arguments: %S" (length args))) - `(progn - (defun ,fn () - ,(format "Set local variables after `%s'." hook) - (setq-local ,@args)) - (function-put ',fn 'setq-local-hook-settings ',args) - (add-hook ',hook #',fn)))) - (unless (fboundp 'ensure-list) ;; Just in case we're using an old version of Emacs. (defun ensure-list (object) @@ -89,3 +72,34 @@ form (FUNCTION &optional DEPTH LOCAL)." (dolist (hook (ensure-list hooks)) (dolist (fn functions) (apply #'add-hook hook (ensure-list fn))))) + +;;; Convenience macros + +(defmacro setq-local-hook (hook &rest args) + "Run `setq-local' on ARGS when running HOOK." + (declare (indent 1)) + (let ((fn (intern (format "%s-setq-local" hook)))) + (when (and (fboundp fn) + (functionp fn)) + (setq args (append (function-get fn 'setq-local-hook-settings) args))) + (unless (and (< 0 (length args)) + (zerop (mod (length args) 2))) + (user-error "Wrong number of arguments: %S" (length args))) + `(progn + (defun ,fn () + ,(format "Set local variables after `%s'." hook) + (setq-local ,@args)) + (function-put ',fn 'setq-local-hook-settings ',args) + (add-hook ',hook #',fn)))) + +(defmacro with-message (message &rest body) + "Execute BODY, with MESSAGE. +If body executes without errors, MESSAGE...Done will be displayed." + (declare (indent 1)) + (let ((msg (gensym))) + `(let ((,msg ,message)) + (condition-case e + (progn (message "%s..." ,msg) + ,@body) + (:success (message "%s...done" ,msg)) + (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 @@ +;;; private.el -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'acdw) + +(defgroup private nil + "Private things are private. Shhhhh....") + +;; Private directory + +(+define-dir private/ (sync/ "emacs/private") + "Private secretive secrets inside.") +(add-to-list 'load-path private/) + +;; Load random private stuff + +(require '_acdw) + +(provide 'private) +;;; private.el ends here -- cgit 1.4.1-21-gabe81