summary refs log tree commit diff stats
path: root/lisp
diff options
context:
space:
mode:
authorCase Duckworth2022-11-15 19:51:52 -0600
committerCase Duckworth2022-11-15 19:52:00 -0600
commit8c7871fec56b6c464bd06ba114225d7971c4699a (patch)
treef6cb5a19b151d0655148a440c99a4df5c97b90e2 /lisp
parentAdd link-hint (diff)
downloademacs-yoke.tar.gz
emacs-yoke.zip
meh yoke
Diffstat (limited to 'lisp')
-rw-r--r--lisp/+emacs.el6
-rw-r--r--lisp/acdw.el80
-rw-r--r--lisp/dawn.el84
-rw-r--r--lisp/yoke.el88
4 files changed, 205 insertions, 53 deletions
diff --git a/lisp/+emacs.el b/lisp/+emacs.el index 8817c19..870e4e2 100644 --- a/lisp/+emacs.el +++ b/lisp/+emacs.el
@@ -108,12 +108,10 @@ Do this only if the buffer is not visiting a file."
108 regexp-search-ring-max 200 108 regexp-search-ring-max 200
109 save-interprogram-paste-before-kill t 109 save-interprogram-paste-before-kill t
110 save-some-buffers-default-predicate #'+save-some-buffers-p 110 save-some-buffers-default-predicate #'+save-some-buffers-p
111 scroll-conservatively 101 111 scroll-conservatively 25
112 scroll-down-aggressively 0.01 112 scroll-margin 0
113 scroll-margin 2
114 scroll-preserve-screen-position 1 113 scroll-preserve-screen-position 1
115 scroll-step 1 114 scroll-step 1
116 scroll-up-aggressively 0.01
117 search-ring-max 200 115 search-ring-max 200
118 search-ring-max 200 116 search-ring-max 200
119 sentence-end-double-space t 117 sentence-end-double-space t
diff --git a/lisp/acdw.el b/lisp/acdw.el index 6e298b2..75e1755 100644 --- a/lisp/acdw.el +++ b/lisp/acdw.el
@@ -1,7 +1,5 @@
1;;; acdw.el -- bits and bobs -*- lexical-binding: t; -*- 1;;; acdw.el -- bits and bobs -*- lexical-binding: t; -*-
2;; by C. Duckworth <acdw@acdw.net> 2;; by C. Duckworth <acdw@acdw.net>
3(provide 'acdw)
4
5(require 'cl-lib) 3(require 'cl-lib)
6 4
7;;; Define both a directory and a function expanding to a file in that directory 5;;; Define both a directory and a function expanding to a file in that directory
@@ -30,7 +28,6 @@ the filesystem, unless INHIBIT-MKDIR is non-nil."
30 28
31;;; Evaluating things after other things 29;;; Evaluating things after other things
32 30
33
34(defun eval-after-init (fn) 31(defun eval-after-init (fn)
35 "Evaluate FN after inititation, or now if Emacs is initialized. 32 "Evaluate FN after inititation, or now if Emacs is initialized.
36FN is called with no arguments." 33FN is called with no arguments."
@@ -78,12 +75,12 @@ Convenience wrapper around `define-key'."
78(unless (fboundp 'ensure-list) 75(unless (fboundp 'ensure-list)
79 ;; Just in case we're using an old version of Emacs. 76 ;; Just in case we're using an old version of Emacs.
80 (defun ensure-list (object) 77 (defun ensure-list (object)
81 "Return OBJECT as a list. 78 "Return OBJECT as a list.
82If OBJECT is already a list, return OBJECT itself. If it's 79If OBJECT is already a list, return OBJECT itself. If it's
83not a list, return a one-element list containing OBJECT." 80not a list, return a one-element list containing OBJECT."
84 (if (listp object) 81 (if (listp object)
85 object 82 object
86 (list object)))) 83 (list object))))
87 84
88(defun add-to-list* (lists &rest things) 85(defun add-to-list* (lists &rest things)
89 "Add THINGS to LISTS. 86 "Add THINGS to LISTS.
@@ -130,8 +127,8 @@ without any separator."
130Each feature of FEATURES can also be a list of the arguments to 127Each feature of FEATURES can also be a list of the arguments to
131pass to `require', which see." 128pass to `require', which see."
132 (condition-case e 129 (condition-case e
133 (dolist (feature features) 130 (dolist (feature features)
134 (apply #'require (ensure-list feature))) 131 (apply #'require (ensure-list feature)))
135 (:success (mapcar (lambda (f) (car (ensure-list f))) features)) 132 (:success (mapcar (lambda (f) (car (ensure-list f))) features))
136 (t (signal (car e) (cdr e))))) 133 (t (signal (car e) (cdr e)))))
137 134
@@ -153,22 +150,33 @@ pass to `require', which see."
153 (add-hook 'before-save-hook #',internal-name nil :local)) 150 (add-hook 'before-save-hook #',internal-name nil :local))
154 (add-hook ',hook #',external-name)))) 151 (add-hook ',hook #',external-name))))
155 152
156(defmacro setq-local-hook (hook &rest args) 153(defmacro setq-local-hook (hooks &rest args)
157 "Run `setq-local' on ARGS when running HOOK." 154 "Run `setq-local' on ARGS when running HOOKs."
155 ;; FIXME: this is pretty messy, i think...
156 ;; The settings should be stored in an alist so that they can be deduplicated
158 (declare (indent 1)) 157 (declare (indent 1))
159 (let ((fn (intern (format "%s-setq-local" hook)))) 158 `(progn
160 (when (and (fboundp fn) 159 ,@(cl-loop for hook in (ensure-list hooks)
161 (functionp fn)) 160 collect
162 (setf args (append (function-get fn 'setq-local-hook-settings) args))) 161 (let ((fn (intern (format "%s-setq-local" hook))))
163 (unless (and (< 0 (length args)) 162 (when (and (fboundp fn)
164 (zerop (mod (length args) 2))) 163 (functionp fn))
165 (user-error "Wrong number of arguments: %S" (length args))) 164 (setf args (append (function-get fn 'setq-local-hook-settings) args)))
166 `(progn 165 (unless (and (< 0 (length args))
167 (defun ,fn () 166 (zerop (mod (length args) 2)))
168 ,(format "Set local variables after `%s'." hook) 167 (user-error "Wrong number of arguments: %S" (length args)))
169 (setq-local ,@args)) 168 `(progn
170 (function-put ',fn 'setq-local-hook-settings ',args) 169 (defun ,fn ()
171 (add-hook ',hook #',fn)))) 170 ,(format "Set local variables after `%s'." hook)
171 (setq-local ,@args))
172 (function-put ',fn 'setq-local-hook-settings ',args)
173 (dolist (buf (buffer-list))
174 (with-current-buffer buf
175 (when (derived-mode-p
176 ',(intern (replace-regexp-in-string
177 "-hook" "" (format "%s" hook))))
178 (,fn))))
179 (add-hook ',hook #',fn))))))
172 180
173(defmacro with-message (message &rest body) 181(defmacro with-message (message &rest body)
174 "Execute BODY, with MESSAGE. 182 "Execute BODY, with MESSAGE.
@@ -182,6 +190,13 @@ If body executes without errors, MESSAGE...Done will be displayed."
182 (:success (message "%s...done" ,msg)) 190 (:success (message "%s...done" ,msg))
183 (t (signal (car e) (cdr e))))))) 191 (t (signal (car e) (cdr e)))))))
184 192
193(defmacro either (&rest clauses)
194 "Return the first of CLAUSES that returns non-nil."
195 (let* ((this (gensym "either")))
196 (unless (null clauses)
197 `(let* ((,this ,(car clauses)))
198 (if ,this ,this (either ,@(cdr clauses)))))))
199
185;; https://emacs.stackexchange.com/a/39324/37239 200;; https://emacs.stackexchange.com/a/39324/37239
186;; XXX: This shit don't work rn 201;; XXX: This shit don't work rn
187(defun ignore-invisible-overlays (fn) 202(defun ignore-invisible-overlays (fn)
@@ -189,13 +204,13 @@ If body executes without errors, MESSAGE...Done will be displayed."
189FN should return a point." 204FN should return a point."
190 (let ((overlay nil) 205 (let ((overlay nil)
191 (point nil)) 206 (point nil))
192 (setq point (and (funcall fn) (point))) 207 (setq point (and (funcall fn) (point)))
193 (setq overlay (car (overlays-at (point)))) 208 (setq overlay (car (overlays-at (point))))
194 (while (and overlay (member 'invisible (overlay-properties overlay))) 209 (while (and overlay (member 'invisible (overlay-properties overlay)))
195 (goto-char (overlay-end overlay)) 210 (goto-char (overlay-end overlay))
196 (setq point (and (funcall fn) (point))) 211 (setq point (and (funcall fn) (point)))
197 (setq overlay (car (overlays-at (point))))) 212 (setq overlay (car (overlays-at (point)))))
198 point)) 213 point))
199 214
200;;; Extras 215;;; Extras
201;; Trying to avoid a whole install of crux ... 216;; Trying to avoid a whole install of crux ...
@@ -217,3 +232,6 @@ When called with prefix ARG, unconditionally switch buffer."
217 (if (or arg (one-window-p)) 232 (if (or arg (one-window-p))
218 (switch-to-buffer (other-buffer) nil t) 233 (switch-to-buffer (other-buffer) nil t)
219 (other-window 1))) 234 (other-window 1)))
235
236(provide 'acdw)
237;;; acdw.el ends here
diff --git a/lisp/dawn.el b/lisp/dawn.el new file mode 100644 index 0000000..806c422 --- /dev/null +++ b/lisp/dawn.el
@@ -0,0 +1,84 @@
1;;; dawn.el --- Do things at dawn (and dusk) -*- lexical-binding: t; -*-
2
3;;; Commentary:
4
5;; There is also circadian.el, but it doesn't quite work for me.
6;; This code comes mostly from https://gnu.xyz/auto_theme.html, but also
7;; somewhere else (which I've forgotten) and my own brain :)
8
9;;; Code:
10
11(require 'calendar)
12(require 'cl-lib)
13(require 'solar)
14
15(defvar dawn--dawn-timer nil
16 "Timer for dawn-command.")
17
18(defvar dawn--dusk-timer nil
19 "Timer for dusk-command.")
20
21(defvar dawn--reset-timer nil
22 "Timer to reset dawn at midnight.")
23
24(defun dawn-encode-time (f)
25 "Encode fractional time F."
26 (let ((hhmm (cl-floor f))
27 (date (cdddr (decode-time))))
28 (encode-time
29 (append (list 0
30 (round (* 60 (cadr hhmm)))
31 (car hhmm)
32 )
33 date))))
34
35(defun dawn-midnight ()
36 "Return the time of the /next/ midnight."
37 (let ((date (cdddr (decode-time))))
38 (encode-time
39 (append (list 0 0 0 (1+ (car date))) (cdr date)))))
40
41(defun dawn-sunrise ()
42 "Return the time of today's sunrise."
43 (dawn-encode-time (caar (solar-sunrise-sunset (calendar-current-date)))))
44
45(defun dawn-sunset ()
46 "Return the time of today's sunset."
47 (dawn-encode-time (caadr (solar-sunrise-sunset (calendar-current-date)))))
48
49(defun dawn-schedule (dawn-command dusk-command)
50 "Run DAWN-COMMAND at sunrise, and DUSK-COMMAND at dusk.
51RESET is an argument for internal use."
52 (when (or (null calendar-longitude)
53 (null calendar-latitude))
54 (user-error "`dawn' won't work without setting %s!"
55 (cond ((and (null calendar-longitude)
56 (null calendar-latitude))
57 "`calendar-longitude' and `calendar-latitude'")
58 ((null calendar-longitude)
59 "`calendar-longitude'")
60 ((null calendar-latitude)
61 "`calendar-latitude'"))))
62 (let ((dawn (dawn-sunrise))
63 (dusk (dawn-sunset)))
64 (cond
65 ((time-less-p nil dawn)
66 ;; If it isn't dawn yet, it's still dark. Run DUSK-COMMAND and schedule
67 ;; DAWN-COMMAND and DUSK-COMMAND for later.
68 (funcall dusk-command)
69 (run-at-time dawn nil dawn-command)
70 (run-at-time dusk nil dusk-command))
71 ((time-less-p nil dusk)
72 ;; If it isn't dusk yet, it's still light. Run DAWN-COMMAND and schedule
73 ;; DUSK-COMMAND.
74 (funcall dawn-command)
75 (run-at-time dusk nil dusk-command))
76 (t ;; Otherwise, it's past dusk, so run DUSK-COMMAND.
77 (funcall dusk-command)))
78 ;; Schedule a reset at midnight, to re-calculate dawn/dusk times.
79 ;(unless reset)
80 (run-at-time (dawn-midnight) nil
81 #'dawn-schedule dawn-command dusk-command)))
82
83(provide 'dawn)
84;;; dawn.el ends here
diff --git a/lisp/yoke.el b/lisp/yoke.el index 1e1bc60..f9c4d49 100644 --- a/lisp/yoke.el +++ b/lisp/yoke.el
@@ -64,8 +64,8 @@ Execute BODY afterward.
64 (url (cond ((consp package) (cdr package)) 64 (url (cond ((consp package) (cdr package))
65 (:else nil))) 65 (:else nil)))
66 (pname (intern (format "yoke:%s" pkg))) 66 (pname (intern (format "yoke:%s" pkg)))
67 (dirvar (gensym "yoke-dir-")) 67 (dirvar '$yoke-dir)
68 ;; Keyword args 68 ;; Keyword args --- TODO: Naming could probably be better.
69 (after (plist-get body :after)) 69 (after (plist-get body :after))
70 (depends (plist-get body :depends)) 70 (depends (plist-get body :depends))
71 (whenp (plist-member body :when)) 71 (whenp (plist-member body :when))
@@ -77,6 +77,7 @@ Execute BODY afterward.
77 (autoload (cond ((plist-member body :autoload) 77 (autoload (cond ((plist-member body :autoload)
78 (plist-get body :autoload)) 78 (plist-get body :autoload))
79 (:else t))) 79 (:else t)))
80 (pre (plist-get body :pre))
80 ;; Body 81 ;; Body
81 (body (cl-loop for (this next) on body by #'cddr 82 (body (cl-loop for (this next) on body by #'cddr
82 unless (keywordp this) 83 unless (keywordp this)
@@ -102,12 +103,17 @@ Execute BODY afterward.
102 `((when ,unless (cl-return-from ,pname 103 `((when ,unless (cl-return-from ,pname
103 (format "%s (abort) :unless %S" 104 (format "%s (abort) :unless %S"
104 ',pname ',unless)))))) 105 ',pname ',unless))))))
106 ;; Evaluate `:pre' forms
107 ,@pre
105 ;; Get prerequisite packages 108 ;; Get prerequisite packages
106 ,@(cl-loop 109 ,@(cl-loop
107 for (pkg* . yoke-get-args) in depends 110 for (pkg* . yoke-get-args) in depends
108 collect `(or 111 collect `(or
109 (let ((dir (yoke-get ,@yoke-get-args 112 (let* ((pkg-spec (yoke-get ,@yoke-get-args
110 :dir ,(format "%s" pkg*)))) 113 :dir ,(format "%s" pkg*)))
114 (dir (expand-file-name (or (plist-get (cdr pkg-spec) :load)
115 "")
116 (car pkg-spec))))
111 (and dir 117 (and dir
112 ,@(if autoload 118 ,@(if autoload
113 `((yoke-generate-autoloads ',pkg* dir)) 119 `((yoke-generate-autoloads ',pkg* dir))
@@ -118,13 +124,16 @@ Execute BODY afterward.
118 ',pkg*)))) 124 ',pkg*))))
119 ;; Download the package, generate autoloads 125 ;; Download the package, generate autoloads
120 ,@(when url 126 ,@(when url
121 `((let ((,dirvar (yoke-get ,@url :dir ,(format "%s" pkg)))) 127 `((let* ((pkg-spec (yoke-get ,@url :dir ,(format "%s" pkg)))
128 (,dirvar (expand-file-name (or (plist-get (cdr pkg-spec) :load)
129 "")
130 (car pkg-spec))))
122 ,@(when autoload 131 ,@(when autoload
123 `((yoke-generate-autoloads ',pkg ,dirvar))) 132 `((yoke-generate-autoloads ',pkg ,dirvar)))
124 (add-to-list 'yoke-dirs ,dirvar nil #'string=)))) 133 (add-to-list 'yoke-dirs ,dirvar nil #'string=))))
125 ;; Evaluate the body, optionally after the features in `:after' 134 ;; Evaluate the body, optionally after the features in `:after'
126 ,@(cond (after 135 ,@(cond (after
127 `((eval-after ,after ,@body))) 136 `((yoke-eval-after ,after ,@body)))
128 (:else body))) 137 (:else body)))
129 (:success ',package) 138 (:success ',package)
130 (t (message "%s: %s (%s)" ',pname (car err) (cdr err)) 139 (t (message "%s: %s (%s)" ',pname (car err) (cdr err))
@@ -144,7 +153,7 @@ ARGS is a plist with the following possible keys:
144 download URL." 153 download URL."
145 (let* ((dir (plist-get args :dir)) 154 (let* ((dir (plist-get args :dir))
146 (load (plist-get args :load)) 155 (load (plist-get args :load))
147 (type (plist-get args :type)) 156 (type (or (plist-get args :type)))
148 (path (cond 157 (path (cond
149 ((eq type 'http) (yoke-get-http url dir)) 158 ((eq type 'http) (yoke-get-http url dir))
150 ((or (eq type 'git) 159 ((or (eq type 'git)
@@ -159,7 +168,7 @@ ARGS is a plist with the following possible keys:
159 (cond 168 (cond
160 ((file-exists-p path) 169 ((file-exists-p path)
161 (add-to-list 'load-path (expand-file-name (or load "") path)) 170 (add-to-list 'load-path (expand-file-name (or load "") path))
162 path) 171 (cons path args))
163 (:else (error "Directory \"%s\" doesn't exist." path) 172 (:else (error "Directory \"%s\" doesn't exist." path)
164 nil)))) 173 nil))))
165 174
@@ -178,7 +187,18 @@ If DIR isn't given, it's guessed from the final component of the
178URL's path and placed under `yoke-dir'." 187URL's path and placed under `yoke-dir'."
179 (let* ((dir (yoke-get--guess-directory url dir)) 188 (let* ((dir (yoke-get--guess-directory url dir))
180 (basename (file-name-nondirectory url)) 189 (basename (file-name-nondirectory url))
181 (filename (expand-file-name basename dir))) 190 ;; XXX: Is this the best idea?? PROBABLY NOT!!! Ideally I'd have
191 ;; a parameter (either dynamic var or passed in) that would give the
192 ;; name of the downloaded file. But that would take a bit of
193 ;; re-engineering, I think. So for now, it stays thus.
194 (filename (expand-file-name
195 (replace-regexp-in-string
196 (rx "-" (+ digit) ; major version
197 (+ (group "." (+ digit))) ; following version numbers
198 (group "." (+ (not space)))) ; extension
199 "\\2"
200 basename)
201 dir)))
182 (cond ((file-exists-p filename) 202 (cond ((file-exists-p filename)
183 dir) 203 dir)
184 (:else 204 (:else
@@ -187,6 +207,8 @@ URL's path and placed under `yoke-dir'."
187 (url-retrieve-synchronously url)) 207 (url-retrieve-synchronously url))
188 (condition-case e 208 (condition-case e
189 (progn 209 (progn
210 (goto-char (point-min))
211 (delete-region (point) (+ 1 (re-search-forward "^$")))
190 (make-directory dir :parents) 212 (make-directory dir :parents)
191 (write-file filename 1) 213 (write-file filename 1)
192 (message "Downloading %s... Done" url)) 214 (message "Downloading %s... Done" url))
@@ -264,7 +286,7 @@ BODY after Emacs is finished initializing."
264 (rest (cdr features))) 286 (rest (cdr features)))
265 (cond ((eq this 'init) 287 (cond ((eq this 'init)
266 `(yoke--eval-after-init 288 `(yoke--eval-after-init
267 (lambda () (eval-after ,rest ,@body)))) 289 (lambda () (yoke-eval-after ,rest ,@body))))
268 (:else 290 (:else
269 `(with-eval-after-load ',this 291 `(with-eval-after-load ',this
270 (yoke-eval-after ,rest ,@body))))))) 292 (yoke-eval-after ,rest ,@body)))))))
@@ -277,21 +299,51 @@ BODY after Emacs is finished initializing."
277 (setf (alist-get "Yoke" imenu-generic-expression nil nil #'equal) 299 (setf (alist-get "Yoke" imenu-generic-expression nil nil #'equal)
278 (list (rx (: "(yoke" (+ space) (? "(") 300 (list (rx (: "(yoke" (+ space) (? "(")
279 (group (+ (not (or "(" " " "\t" "\n")))) 301 (group (+ (not (or "(" " " "\t" "\n"))))
280 (+ space) 302 (* any)))
281 (group (+ (not space)))))
282 1))) 303 1)))
283 304
284(defun yoke-compile () 305;;; Package maintenance
306
307(defvar yoke--all "*all*"
308 "Value that `yoke--prompt-for-package' uses for all packages.")
309
310(defun yoke--choose-packages (prompt &optional onep)
311 "Choose from all of yoke's installed packages."
312 (funcall (if onep #'completing-read #'completing-read-multiple)
313 prompt
314 (cons yoke--all yoke-dirs)
315 nil :require-match nil nil
316 (unless onep yoke--all)))
317
318(defun yoke--choices (&optional selections)
319 "Either the SELECTIONS given, or all of `yoke-dirs'.
320If `yoke--all' is part of SELECTIONS, or if it's not given,
321return the full list of `yoke-dirs'."
322 (cond ((or (null selections)
323 (member yoke--all selections))
324 yoke-dirs)
325 (:else selections)))
326
327(defun yoke-compile (&rest packages)
285 "Compile all elisp files in `yoke-dirs'." 328 "Compile all elisp files in `yoke-dirs'."
286 (interactive) 329 (interactive (yoke--choose-packages "Compile packages: "))
287 (dolist (dir yoke-dirs) 330 (dolist (dir (yoke--choices packages))
288 (byte-recompile-directory dir 0))) 331 (byte-recompile-directory dir 0)))
289 332
333(defun yoke-update-autoloads (&rest packages)
334 "Update the autoloads in PACKAGES' directories."
335 (interactive (yoke--choose-packages "Generate autoloads for packages: "))
336 (dolist (dir (yoke--choices packages))
337 (message "Generating autoloads for %s..." dir)
338 (yoke-generate-autoloads (file-name-nondirectory dir) dir)
339 (message "Generating autoloads for %s... Done" dir)))
340
290(defun yoke-remove (dir) 341(defun yoke-remove (dir)
342 "Remove DIR from `yoke-dir'."
291 (interactive 343 (interactive
292 (completing-read "Remove: " yoke-dirs 344 (list (completing-read "Remove: " yoke-dirs
293 nil :require-match)) 345 nil :require-match)))
294 (delete-file dir :trash)) 346 (delete-directory dir :recursive :trash))
295 347
296(provide 'yoke) 348(provide 'yoke)
297;;; yoke.el ends here 349;;; yoke.el ends here