diff options
Diffstat (limited to 'lisp/+setup.el')
-rw-r--r-- | lisp/+setup.el | 216 |
1 files changed, 0 insertions, 216 deletions
diff --git a/lisp/+setup.el b/lisp/+setup.el deleted file mode 100644 index a08526a..0000000 --- a/lisp/+setup.el +++ /dev/null | |||
@@ -1,216 +0,0 @@ | |||
1 | ;;; +setup.el -- my `setup' commands -*- lexical-binding: t -*- | ||
2 | |||
3 | ;; Author: Case Duckworth <acdw@acdw.net> | ||
4 | |||
5 | ;; This file is NOT part of GNU Emacs. | ||
6 | |||
7 | ;;; License: | ||
8 | ;; Everyone is permitted to do whatever with this software, without | ||
9 | ;; limitation. This software comes without any warranty whatsoever, | ||
10 | ;; but with two pieces of advice: | ||
11 | ;; - Don't hurt yourself. | ||
12 | ;; - Make good choices. | ||
13 | |||
14 | ;;; Commentary: | ||
15 | |||
16 | ;; `setup', by Philip Kaludercic, is a wonderful package that works | ||
17 | ;; sort of like `use-package', but to my mind it's cleaner and easier | ||
18 | ;; to extend. These are my additions to the local macros provided by | ||
19 | ;; the package. | ||
20 | |||
21 | ;;; Code: | ||
22 | |||
23 | (require 'el-patch) | ||
24 | (require 'setup) | ||
25 | (require 'straight) | ||
26 | (require 'cl-lib) | ||
27 | |||
28 | (defun +setup-warn (message &rest args) | ||
29 | "Warn the user that something bad happened in `setup'." | ||
30 | (display-warning 'setup (format message args))) | ||
31 | |||
32 | (defun +setup-wrap-to-demote-errors (body name) | ||
33 | "Wrap BODY in a `with-demoted-errors' block. | ||
34 | This behavior is prevented if `setup-attributes' contains the | ||
35 | symbol `without-error-demotion'. | ||
36 | |||
37 | This function differs from `setup-wrap-to-demote-errors' in that | ||
38 | it includes the NAME of the setup form in the warning output." | ||
39 | (if (memq 'without-error-demotion setup-attributes) | ||
40 | body | ||
41 | `(with-demoted-errors ,(format "Error in setup form on line %d (%s): %%S" | ||
42 | (line-number-at-pos) | ||
43 | name) | ||
44 | ,body))) | ||
45 | |||
46 | |||
47 | ;;; New forms | ||
48 | |||
49 | (setup-define :quit | ||
50 | 'setup-quit | ||
51 | :documentation "Quit the current `setup' form. | ||
52 | Good for commenting.") | ||
53 | |||
54 | (setup-define :face | ||
55 | (lambda (face spec) | ||
56 | `(custom-set-faces (list ,face ,spec 'now "Customized by `setup'."))) | ||
57 | :documentation "Customize FACE with SPEC using `custom-set-faces'." | ||
58 | :repeatable t) | ||
59 | |||
60 | (setup-define :load-after | ||
61 | (lambda (&rest features) | ||
62 | (let ((body `(require ',(setup-get 'feature)))) | ||
63 | (dolist (feature (nreverse features)) | ||
64 | (setq body `(with-eval-after-load ',feature ,body))) | ||
65 | body)) | ||
66 | :documentation "Load the current feature after FEATURES.") | ||
67 | |||
68 | (setup-define :load-from | ||
69 | (lambda (path) | ||
70 | `(let ((path* (expand-file-name ,path))) | ||
71 | (if (file-exists-p path*) | ||
72 | (add-to-list 'load-path path*) | ||
73 | ,(setup-quit)))) | ||
74 | :documentation "Add PATH to load path. | ||
75 | This macro can be used as NAME, and it will replace itself with | ||
76 | the nondirectory part of PATH. | ||
77 | If PATH does not exist, abort the evaluation." | ||
78 | :shorthand (lambda (args) | ||
79 | (intern | ||
80 | (file-name-nondirectory | ||
81 | (directory-file-name (cadr args)))))) | ||
82 | |||
83 | (setup-define :needs | ||
84 | (lambda (executable) | ||
85 | `(unless (executable-find ,executable) | ||
86 | ,(setup-quit))) | ||
87 | :documentation "If EXECUTABLE is not in the path, stop here." | ||
88 | :repeatable 1) | ||
89 | |||
90 | |||
91 | ;;; Package integrations | ||
92 | |||
93 | ;;; Straight.el | ||
94 | |||
95 | (defun setup--straight-handle-arg (arg var) | ||
96 | (cond | ||
97 | ((and (boundp var) (symbol-value var)) t) | ||
98 | ((keywordp arg) (set var t)) | ||
99 | ((functionp arg) (set var nil) (funcall arg)) | ||
100 | ((listp arg) (set var nil) arg))) | ||
101 | |||
102 | (with-eval-after-load 'straight | ||
103 | (setup-define :straight | ||
104 | (lambda (recipe &rest predicates) | ||
105 | (let* ((skp (make-symbol "straight-keyword-p")) | ||
106 | (straight-use-p | ||
107 | (cl-mapcar | ||
108 | (lambda (f) (setup--straight-handle-arg f skp)) | ||
109 | predicates)) | ||
110 | (form `(unless (and ,@straight-use-p | ||
111 | (condition-case e | ||
112 | (straight-use-package ',recipe) | ||
113 | (error | ||
114 | (+setup-warn ":straight error: %S" | ||
115 | ',recipe) | ||
116 | ,(setup-quit)) | ||
117 | (:success t))) | ||
118 | ,(setup-quit)))) | ||
119 | ;; Keyword arguments --- :quit is special and should short-circuit | ||
120 | (if (memq :quit predicates) | ||
121 | (setq form `,(setup-quit)) | ||
122 | ;; Otherwise, handle the rest of them ... | ||
123 | (when-let ((after (cadr (memq :after predicates)))) | ||
124 | (setq form `(with-eval-after-load ,(if (eq after t) | ||
125 | (setup-get 'feature) | ||
126 | after) | ||
127 | ,form)))) | ||
128 | ;; Finally ... | ||
129 | form)) | ||
130 | :documentation "Install RECIPE with `straight-use-package'. | ||
131 | If PREDICATES are given, only install RECIPE if all of them return non-nil. | ||
132 | The following keyword arguments are also recognized: | ||
133 | - :quit --- immediately stop evaluating. Good for commenting. | ||
134 | - :after FEATURE --- only install RECIPE after FEATURE is loaded. | ||
135 | If FEATURE is t, install RECIPE after the current feature." | ||
136 | :repeatable nil | ||
137 | :indent 1 | ||
138 | :shorthand (lambda (sexp) | ||
139 | (let ((recipe (cadr sexp))) | ||
140 | (or (car-safe recipe) recipe))))) | ||
141 | |||
142 | ;;; Apheleia | ||
143 | |||
144 | (setup-define :apheleia | ||
145 | (lambda (name formatter &optional mode -pend) | ||
146 | (let* ((mode (or mode (setup-get 'mode))) | ||
147 | (current-formatters (and -pend | ||
148 | (alist-get mode apheleia-formatters)))) | ||
149 | `(with-eval-after-load 'apheleia | ||
150 | (setf (alist-get ',name apheleia-formatters) | ||
151 | ,formatter) | ||
152 | (setf (alist-get ',mode apheleia-mode-alist) | ||
153 | ',(pcase -pend | ||
154 | (:append (append (ensure-list current-formatters) | ||
155 | (list name))) | ||
156 | (:prepend (cons name (ensure-list current-formatters))) | ||
157 | ('nil name) | ||
158 | (_ (error "Improper `:apheleia' -PEND argument"))))))) | ||
159 | :documentation | ||
160 | "Register a formatter to `apheleia''s lists. | ||
161 | NAME is the name given to the formatter in `apheleia-formatters' | ||
162 | and `apheleia-mode-alist'. FORMATTER is the command paired with | ||
163 | NAME in `apheleia-formatters'. MODE is the mode or modes to add | ||
164 | NAME to in `apheleia-mode-alist'. If MODE is not given or nil, | ||
165 | use the setup form's MODE. Optional argument -PEND can be one of | ||
166 | `:append' or `:prepend', and if given will append or prepend the | ||
167 | given NAME to the current formatters for the MODE in | ||
168 | `apheleia-mode-alist', rather than replace them (the default). | ||
169 | |||
170 | Example: | ||
171 | (setup | ||
172 | (:apheleia isort (\"isort\" \"--stdout\" \"-\") | ||
173 | python-mode)) | ||
174 | ; => | ||
175 | (progn | ||
176 | (setf (alist-get 'isort apheleia-formatters) | ||
177 | '(\"isort\" \"--stdout\" \"-\")) | ||
178 | (setf (alist-get 'python-mode apheleia-mode-alist) | ||
179 | 'isort)) | ||
180 | |||
181 | This form cannot be repeated, and it cannot be used as HEAD.") | ||
182 | |||
183 | |||
184 | ;;; Redefines of `setup' forms | ||
185 | |||
186 | (setup-define :bind-into | ||
187 | (lambda (feature-or-map &rest rest) | ||
188 | (cl-loop for f/m in (ensure-list feature-or-map) | ||
189 | collect (if (string-match-p "-map\\'" (symbol-name f/m)) | ||
190 | `(:with-map ,f/m (:bind ,@rest)) | ||
191 | `(:with-feature ,f/m (:bind ,@rest))) | ||
192 | into forms | ||
193 | finally return `(progn ,@forms))) | ||
194 | :documentation "Bind into keys into the map(s) of FEATURE-OR-MAP. | ||
195 | FEATURE-OR-MAP can be a feature or map name or a list of them. | ||
196 | The arguments REST are handled as by `:bind'." | ||
197 | :debug '(sexp &rest form sexp) | ||
198 | :indent 1) | ||
199 | |||
200 | (setup-define :require | ||
201 | (lambda (&rest features) | ||
202 | (require 'cl-lib) | ||
203 | (if features | ||
204 | `(progn ,@(cl-loop for feature in features collect | ||
205 | `(unless (require ',feature nil t) | ||
206 | ,(setup-quit)))) | ||
207 | `(unless (require ',(setup-get 'feature) nil t) | ||
208 | ,(setup-quit)))) | ||
209 | :documentation "Try to require FEATURE, or stop evaluating body. | ||
210 | This macro can be used as NAME, and it will replace itself with | ||
211 | the first FEATURE." | ||
212 | :repeatable nil | ||
213 | :shorthand #'cadr) | ||
214 | |||
215 | (provide '+setup) | ||
216 | ;;; +setup.el ends here | ||