changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > infra > home / .emacs.d/ellis.el

changeset 95: f69061a590da
parent: 978ce75e54af
author: Richard Westhaver <ellis@rwest.io>
date: Sat, 07 Sep 2024 22:40:44 -0400
permissions: -rw-r--r--
description: moonlander concessions
1 ;;; ellis.el --- user Emacs config -*- lexical-binding: t -*-
2 
3 ;; Copyright (C) 2024
4 
5 ;; Author: Richard Westhaver <ellis@rwest.io>
6 
7 ;; This program is free software; you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation, either version 3 of the License, or
10 ;; (at your option) any later version.
11 
12 ;; This program is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
16 
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
19 
20 ;;; Commentary:
21 
22 ;; This is an example of what you may want to add to your custom
23 ;; config file. Feel free to rip.
24 
25 ;;; Code:
26 (require 'inbox)
27 (require 'sk)
28 (require 'sxp)
29 (require 'ulang)
30 
31 (defalias 'make #'compile)
32 
33 (setq default-theme 'ef-dark
34  user-lab-directory (join-paths user-home-directory "lab")
35  company-source-directory (join-paths user-home-directory "comp"))
36 
37 (when (linux-p) (setq dired-listing-switches "-alsh"))
38 
39 (defvar emacs-config-source (join-paths company-source-directory "core/emacs"))
40 
41 ;;;###autoload
42 (defun edit-emacs-config (&optional src)
43  (interactive (list current-prefix-arg))
44  (let ((file (if src
45  (expand-file-name "default.el" emacs-config-source)
46  user-custom-file)))
47  (find-file file)))
48 
49 (keymap-set user-map "e c" #'edit-emacs-config)
50 (keymap-set emacs-lisp-mode-map "C-c C-l" #'load-file)
51 (keymap-set emacs-lisp-mode-map "C-c M-k" #'elisp-byte-compile-file)
52 (keymap-set user-map "v t" #'org-tags-view)
53 
54 (require 'paredit)
55 (repeat-mode)
56 
57 (keymap-set lisp-mode-shared-map "C-(" #'paredit-open-round)
58 (keymap-set lisp-mode-shared-map "M-(" #'paredit-wrap-sexp)
59 (keymap-set lisp-mode-shared-map "M-;" #'paredit-comment-dwim)
60 (keymap-set lisp-mode-shared-map "C-{" #'paredit-backward-barf-sexp)
61 (keymap-set lisp-mode-shared-map "C-}" #'paredit-forward-barf-sexp)
62 (keymap-set lisp-mode-shared-map "C-M-{" #'paredit-forward-slurp-sexp)
63 (keymap-set lisp-mode-shared-map "C-M-}" #'paredit-backward-slurp-sexp)
64 
65 (defun remember-project ()
66  (interactive)
67  (project-remember-project (project-current))
68  project--list)
69 
70 (defun remember-lab-projects ()
71  (interactive)
72  (project-remember-projects-under user-lab-directory t))
73 
74 (defun remember-comp-projects ()
75  (interactive)
76  (project-remember-projects-under company-source-directory t))
77 
78 (keymap-global-set "C-<tab>" #'hippie-expand)
79 (keymap-set minibuffer-local-map "C-<tab>" #'hippie-expand)
80 (keymap-set ctl-x-x-map "p p" #'remember-project)
81 (keymap-set ctl-x-x-map "p l" #'remember-lab-projects)
82 (keymap-set ctl-x-x-map "p c" #'remember-comp-projects)
83 
84 (add-hook 'prog-mode-hook #'skel-minor-mode)
85 (add-hook 'org-mode-hook #'skel-minor-mode)
86 ;; (add-hook 'prog-mode-hook #'company-mode)
87 
88 (add-hook 'notmuch-message-mode-hook #'turn-on-orgtbl)
89 
90 (use-package ef-themes :ensure t)
91 
92 (use-package markdown-mode :ensure t)
93 
94 (use-package ol-notmuch :ensure t)
95 
96 (use-package notmuch
97  :ensure t
98  :init
99  (setopt
100  mail-user-agent 'message-user-agent
101  smtpmail-smtp-server "smtp.gmail.com"
102  message-send-mail-function 'message-smtpmail-send-it
103  smtpmail-debug-info t
104  message-default-mail-headers "Cc: \nBcc: \n"
105  message-kill-buffer-on-exit t
106  user-mail-address "richard.westhaver@gmail.com"
107  user-full-name "Richard Westhaver"
108  notmuch-hello-sections '(notmuch-hello-insert-saved-searches
109  notmuch-hello-insert-search
110  notmuch-hello-insert-recent-searches
111  notmuch-hello-insert-alltags)
112  notmuch-show-logo nil
113  notmuch-search-oldest-first nil
114  notmuch-hello-hide-tags '("kill")
115  notmuch-saved-searches '((:name "unread" :query "tag:unread" :key "u")
116  (:name "inbox" :query "tag:inbox" :key "i")
117  (:name "new" :query "tag:new" :key "n")
118  (:name "drafts" :query "tag:draft" :key "d")
119  (:name "sent" :query "tag:sent" :key "e")
120  (:name "all" :query "*" :key "a")
121  (:name "todo" :query "tag:todo" :key "t")))
122  :config
123  ;;;###autoload
124  (defun notmuch-exec-offlineimap ()
125  "execute offlineimap command and tag new mail with notmuch"
126  (interactive)
127  (start-process-shell-command "offlineimap"
128  "*offlineimap*"
129  "offlineimap -o")
130  (notmuch-refresh-all-buffers))
131 
132  (defun offlineimap-get-password (host port)
133  (let* ((netrc (netrc-parse (expand-file-name "~/.netrc.gpg")))
134  (hostentry (netrc-machine netrc host port port)))
135  (when hostentry (netrc-get hostentry "password"))))
136 
137  (defun mark-as-read ()
138  "mark message as read."
139  (interactive)
140  (notmuch-search-tag '("-new" "-unread" "-inbox")))
141 
142  (defun mark-as-todo ()
143  "mark message as todo."
144  (interactive)
145  (mark-as-read)
146  (notmuch-search-tag '("-new" "-unread" "-inbox" "+todo")))
147 
148  (defun mark-as-spam ()
149  "mark message as spam."
150  (interactive)
151  (mark-as-read)
152  (notmuch-search-tag (list "+spam")))
153 
154  (keymap-set user-map "e m" #'notmuch)
155  (keymap-set user-map "e M" #'notmuch-exec-offlineimap)
156  (keymap-set notmuch-search-mode-map "S" #'mark-as-spam)
157  (keymap-set notmuch-search-mode-map "R" #'mark-as-read)
158  (keymap-set notmuch-search-mode-map "T" #'mark-as-todo))
159 
160 (use-package elfeed
161  :ensure t
162  :custom
163  elfeed-feeds
164  '(("http://threesixty360.wordpress.com/feed/" blog math)
165  ("http://www.50ply.com/atom.xml" blog dev)
166  ("http://blog.cryptographyengineering.com/feeds/posts/default" blog)
167  ("http://abstrusegoose.com/feed.xml" comic)
168  ("http://accidental-art.tumblr.com/rss" image math)
169  ("http://researchcenter.paloaltonetworks.com/unit42/feed/" security)
170  ("http://curiousprogrammer.wordpress.com/feed/" blog dev)
171  ("http://feeds.feedburner.com/amazingsuperpowers" comic)
172  ("http://amitp.blogspot.com/feeds/posts/default" blog dev)
173  ("http://pages.cs.wisc.edu/~psilord/blog/rssfeed.rss" blog)
174  ("http://www.anticscomic.com/?feed=rss2" comic)
175  ("http://feeds.feedburner.com/blogspot/TPQSS" blog dev)
176  ("http://techchrunch.com/feeds" tech news)
177  ("https://rss.nytimes.com/services/xml/rss/nyt/Technology.xml" tech news)
178  ("https://static.fsf.org/fsforg/rss/news.xml" tech news)
179  ("https://feeds.npr.org/1001/rss.xml" news)
180  ("https://search.cnbc.com/rs/search/combinedcms/view.xml?partnerId=wrss01&id=10000664" fin news)
181  ("https://search.cnbc.com/rs/search/combinedcms/view.xml?partnerId=wrss01&id=19854910" tech news)
182  ("https://search.cnbc.com/rs/search/combinedcms/view.xml?partnerId=wrss01&id=100003114" us news)
183  ("http://arxiv.org/rss/cs" cs rnd)
184  ("http://arxiv.org/rss/math" math rnd)
185  ("http://arxiv.org/rss/q-fin" q-fin rnd)
186  ("http://arxiv.org/rss/stat" stat rnd)
187  ("http://arxiv.org/rss/econ" econ rnd)
188  ;; John Wiegley
189  ("http://newartisans.com/rss.xml" dev blog)
190  ("https://www.reddit.com/r/listentothis/.rss" music reddit)
191  ("https://www.ftc.gov/feeds/press-release-consumer-protection.xml" gov ftc)
192  ("https://api2.fcc.gov/edocs/public/api/v1/rss/" gov fcc)
193  )
194  :init
195  (defun yt-dl-it (url)
196  "Downloads the URL in an async shell"
197  (let ((default-directory user-stash-directory))
198  (async-shell-command (format "yt-dlp %s" url))))
199 
200  (defun elfeed-youtube-dl (&optional use-generic-p)
201  "Youtube-DL link"
202  (interactive "P")
203  (let ((entries (elfeed-search-selected)))
204  (cl-loop for entry in entries
205  do (elfeed-untag entry 'unread)
206  when (elfeed-entry-link entry)
207  do (yt-dl-it it))
208  (mapc #'elfeed-search-update-entry entries)
209  (unless (use-region-p) (forward-line))))
210  :config
211  (keymap-set elfeed-search-mode-map "d" 'elfeed-youtube-dl)
212  (keymap-set user-map "e f" #'elfeed)
213  (keymap-set user-map "e F" #'elfeed-update))
214 
215 (use-package elfeed-tube
216  :ensure t
217  :after elfeed
218  :config
219  ;; (elfeed-tube-setup)
220  (elfeed-tube-add-feeds '("detroit techno" "boiler room dj" "brad mehldau" "chris 'daddy' dave"))
221  :bind (:map elfeed-show-mode-map
222  ("F" . elfeed-tube-fetch)
223  ([remap save-buffer] . elfeed-tube-save)
224  :map elfeed-search-mode-map
225  ("F" . elfeed-tube-fetch)
226  ([remap save-buffer] . elfeed-tube-save)))
227 
228 (use-package elfeed-tube-mpv
229  :ensure t
230  :bind (:map elfeed-show-mode-map
231  ("C-c C-f" . elfeed-tube-mpv-follow-mode)
232  ("C-c C-w" . elfeed-tube-mpv-where)))
233 
234 (use-package org-mime :ensure t)
235 
236 (use-package sh-script
237  :hook (sh-mode . flymake-mode))
238 
239 ;;; Diary
240 (setq diary-list-include-blanks t)
241 ;;; Org Config
242 (setq publish-dir "/ssh:rurik:/srv/http/compiler.company")
243 
244 ;; populate org-babel
245 (org-babel-do-load-languages
246  ;; TODO 2021-10-24: bqn, apl, k
247  'org-babel-load-languages '((shell . t)
248  (emacs-lisp . t)
249  (lisp . t)
250  (org . t)
251  (eshell . t)
252  (calc . t)
253  (sed . t)
254  (awk . t)
255  (dot . t)
256  (js . t)
257  (C . t)
258  (python . t)
259  (lua . t)
260  (lilypond . t)))
261 
262 ;; timeline
263 (use-package org-timeline
264  :load-path user-emacs-lib-directory
265  :hook (org-agenda-finalize . org-timeline-insert-timeline)
266  :init
267  (setq
268  org-timeline-insert-before-text "›"
269  org-timeline-beginning-of-day-hour 8
270  org-timeline-default-duration 30
271  org-timeline-keep-elapsed 2
272  org-timeline-start-hour 8
273  org-timeline-show-text-in-blocks t
274  org-timeline-prepend nil))
275 
276 ;;; IRC
277 (setq erc-format-nick-function 'erc-format-@nick)
278 
279 (defun start-erc ()
280  "Connect to IRC."
281  (interactive)
282  (erc-tls :server "irc.libera.chat" :port 6697
283  :client-certificate '("/mnt/y/data/private/krypt/libera.pem"))
284  (setq erc-autojoin-channels-alist '(("irc.libera.chat" "#emacs")
285  ("irc.libera.chat" "#linux")
286  ("irc.libera.chat" "#rust")
287  ("irc.libera.chat" "#btrfs")
288  ("irc.libera.chat" "#lisp")
289  ("irc.libera.chat" "#sbcl")
290  ("irc.oftc.net" "#llvm"))))
291 ;;; Tags
292 ;;;###autoload
293 (defun refresh-tags ()
294  "Refresh TAGS database in `user-emacs-directory'."
295  (interactive)
296  (let ((default-directory user-emacs-directory))
297  (async-shell-command
298  "etags ./*.el \\
299 ./lib/*.el \\
300 ~/comp/core/emacs/*.el \\
301 ~/comp/core/emacs/lib/*.el \\
302 -o TAGS")))
303 
304 (unless (string-equal "hyde" system-name)
305  (add-hook 'dired-mode-hook #'all-the-icons-dired-mode)
306  (add-hook 'ibuffer-mode-hook #'all-the-icons-ibuffer-mode))
307 
308 ;; strangerdanger
309 ;; (setq slime-enable-evaluate-in-emacs t)
310 
311 (defun org-word-count (beg end
312  &optional count-latex-macro-args?
313  count-footnotes?)
314  "Report the number of words in the Org mode buffer or selected region.
315 Ignores:
316 - comments
317 - tables
318 - source code blocks (#+BEGIN_SRC ... #+END_SRC, and inline blocks)
319 - hyperlinks (but does count words in hyperlink descriptions)
320 - tags, priorities, and TODO keywords in headers
321 - sections tagged as 'not for export'.
322 
323 The text of footnote definitions is ignored, unless the optional argument
324 COUNT-FOOTNOTES? is non-nil.
325 
326 If the optional argument COUNT-LATEX-MACRO-ARGS? is non-nil, the word count
327 includes LaTeX macro arguments (the material between {curly braces}).
328 Otherwise, and by default, every LaTeX macro counts as 1 word regardless
329 of its arguments."
330  (interactive "r")
331  (unless mark-active
332  (setf beg (point-min)
333  end (point-max)))
334  (let ((wc 0)
335  (latex-macro-regexp "\\\\[A-Za-z]+\\(\\[[^]]*\\]\\|\\){\\([^}]*\\)}"))
336  (save-excursion
337  (goto-char beg)
338  (while (< (point) end)
339  (cond
340  ;; Ignore comments.
341  ((or (org-at-comment-p) (org-at-table-p))
342  nil)
343  ;; Ignore hyperlinks. But if link has a description, count
344  ;; the words within the description.
345  ((looking-at org-bracket-link-analytic-regexp)
346  (when (match-string-no-properties 5)
347  (let ((desc (match-string-no-properties 5)))
348  (save-match-data
349  (cl-incf wc (length (remove "" (org-split-string
350  desc "\\W")))))))
351  (goto-char (match-end 0)))
352  ((looking-at org-any-link-re)
353  (goto-char (match-end 0)))
354  ;; Ignore source code blocks.
355  ((org-between-regexps-p "^#\\+BEGIN_SRC\\W" "^#\\+END_SRC\\W")
356  nil)
357  ;; Ignore inline source blocks, counting them as 1 word.
358  ((save-excursion
359  (backward-char)
360  (looking-at org-babel-inline-src-block-regexp))
361  (goto-char (match-end 0))
362  (setf wc (+ 2 wc)))
363  ;; Count latex macros as 1 word, ignoring their arguments.
364  ((save-excursion
365  (backward-char)
366  (looking-at latex-macro-regexp))
367  (goto-char (if count-latex-macro-args?
368  (match-beginning 2)
369  (match-end 0)))
370  (setf wc (+ 2 wc)))
371  ;; Ignore footnotes.
372  ((and (not count-footnotes?)
373  (or (org-footnote-at-definition-p)
374  (org-footnote-at-reference-p)))
375  nil)
376  (t
377  (let ((contexts (org-context)))
378  (cond
379  ;; Ignore tags and TODO keywords, etc.
380  ((or (assoc :todo-keyword contexts)
381  (assoc :priority contexts)
382  (assoc :keyword contexts)
383  (assoc :checkbox contexts))
384  nil)
385  ;; Ignore sections marked with tags that are
386  ;; excluded from export.
387  ((assoc :tags contexts)
388  (if (intersection (org-get-tags-at) org-export-exclude-tags
389  :test 'equal)
390  (org-forward-same-level 1)
391  nil))
392  (t
393  (cl-incf wc))))))
394  (re-search-forward "\\w+\\W*")))
395  (format "%d words in %s." wc
396  (if mark-active "region" "buffer"))))
397 
398 (defun org-check-misformatted-subtree ()
399  "Check misformatted entries in the current buffer."
400  (interactive)
401  (show-all)
402  (org-map-entries
403  (lambda ()
404  (when (and (move-beginning-of-line 2)
405  (not (looking-at org-heading-regexp)))
406  (if (or (and (org-get-scheduled-time (point))
407  (not (looking-at (concat "^.*" org-scheduled-regexp))))
408  (and (org-get-deadline-time (point))
409  (not (looking-at (concat "^.*" org-deadline-regexp)))))
410  (when (y-or-n-p "Fix this subtree? ")
411  (message "Call the function again when you're done fixing this subtree.")
412  (recursive-edit))
413  (message "All subtrees checked."))))))
414 
415 (defun org-sort-list-by-checkbox-type ()
416  "Sort list items according to Checkbox state."
417  (interactive)
418  (org-sort-list
419  nil ?f
420  (lambda ()
421  (if (looking-at org-list-full-item-re)
422  (cdr (assoc (match-string 3)
423  '(("[X]" . 1) ("[-]" . 2) ("[ ]" . 3) (nil . 4))))
424  4))))
425 
426 (defun org-time-string-to-seconds (s)
427  "Convert a string HH:MM:SS to a number of seconds."
428  (cond
429  ((and (stringp s)
430  (string-match "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)" s))
431  (let ((hour (string-to-number (match-string 1 s)))
432  (min (string-to-number (match-string 2 s)))
433  (sec (string-to-number (match-string 3 s))))
434  (+ (* hour 3600) (* min 60) sec)))
435  ((and (stringp s)
436  (string-match "\\([0-9]+\\):\\([0-9]+\\)" s))
437  (let ((min (string-to-number (match-string 1 s)))
438  (sec (string-to-number (match-string 2 s))))
439  (+ (* min 60) sec)))
440  ((stringp s) (string-to-number s))
441  (t s)))
442 
443 (defun org-time-seconds-to-string (secs)
444  "Convert a number of seconds to a time string."
445  (cond ((>= secs 3600) (format-seconds "%h:%.2m:%.2s" secs))
446  ((>= secs 60) (format-seconds "%m:%.2s" secs))
447  (t (format-seconds "%s" secs))))
448 
449 (defmacro with-time (time-output-p &rest exprs)
450  "Evaluate an org-table formula, converting all fields that look
451 like time data to integer seconds. If TIME-OUTPUT-P then return
452 the result as a time value."
453  (list
454  (if time-output-p 'org-time-seconds-to-string 'identity)
455  (cons 'progn
456  (mapcar
457  (lambda (expr)
458  `,(cons (car expr)
459  (mapcar
460  (lambda (el)
461  (if (listp el)
462  (list 'with-time nil el)
463  (org-time-string-to-seconds el)))
464  (cdr expr))))
465  `,@exprs))))
466 
467 (defun org-hex-strip-lead (str)
468  (if (and (> (length str) 2) (string= (substring str 0 2) "0x"))
469  (substring str 2) str))
470 
471 (defun org-hex-to-hex (int)
472  (format "0x%x" int))
473 
474 (defun org-hex-to-dec (str)
475  (cond
476  ((and (stringp str)
477  (string-match "\\([0-9a-f]+\\)" (setf str (org-hex-strip-lead str))))
478  (let ((out 0))
479  (mapc
480  (lambda (ch)
481  (setf out (+ (* out 16)
482  (if (and (>= ch 48) (<= ch 57)) (- ch 48) (- ch 87)))))
483  (coerce (match-string 1 str) 'list))
484  out))
485  ((stringp str) (string-to-number str))
486  (t str)))
487 
488 (defmacro with-hex (hex-output-p &rest exprs)
489  "Evaluate an org-table formula, converting all fields that look
490  like hexadecimal to decimal integers. If HEX-OUTPUT-P then
491  return the result as a hex value."
492  (list
493  (if hex-output-p 'org-hex-to-hex 'identity)
494  (cons 'progn
495  (mapcar
496  (lambda (expr)
497  `,(cons (car expr)
498  (mapcar (lambda (el)
499  (if (listp el)
500  (list 'with-hex nil el)
501  (org-hex-to-dec el)))
502  (cdr expr))))
503  `,@exprs))))
504 
505 (require 'mm-url) ; to include mm-url-decode-entities-string
506 
507 (cl-defun get-first-url (&optional (match (rx bol "http" (optional "s") "://")))
508  "Return URL in clipboard, or first URL in the `kill-ring' matching MATCH."
509  (cl-loop for item in (cons (current-kill 0) kill-ring)
510  when (and item (string-match-p match item))
511  return item))
512 
513 (defun get-html-title-from-url (url)
514  "Return content in <title> tag."
515  (interactive (list (get-first-url)))
516  (let (x1 x2 (download-buffer (url-retrieve-synchronously url)))
517  (save-excursion
518  (set-buffer download-buffer)
519  (beginning-of-buffer)
520  (setq x1 (search-forward "<title>"))
521  (search-forward "</title>")
522  (setq x2 (search-backward "<"))
523  (mm-url-decode-entities-string (buffer-substring-no-properties x1 x2)))))
524 
525 (defun org-insert-link-with-title (url)
526  "Insert org link where default description is set to html title."
527  (interactive (list (get-first-url match)))
528  (let ((title (get-html-title-from-url url)))
529  (org-insert-link nil url title)))
530 
531 (defun org-insert-so-link (url)
532  (interactive (list (get-first-url (rx bol "https://" (* anychar) "stackoverflow.com"))))
533  (let ((title (get-html-title-from-url url)))
534  (org-insert-link nil url title)))
535 
536 (defun org-remove-empty-propert-drawers ()
537  "*Remove all empty property drawers in current file."
538  (interactive)
539  (unless (eq major-mode 'org-mode)
540  (error "You need to turn on Org mode for this function."))
541  (save-excursion
542  (goto-char (point-min))
543  (while (re-search-forward ":PROPERTIES:" nil t)
544  (save-excursion
545  (org-remove-empty-drawer-at "PROPERTIES" (match-beginning 0))))))
546 
547 (defun check-for-clock-out-note ()
548  (interactive)
549  (save-excursion
550  (org-back-to-heading)
551  (let ((tags (org-get-tags)))
552  (and tags (message "tags: %s " tags)
553  (when (member "clocknote" tags)
554  (org-add-note))))))
555 
556 (add-hook 'org-clock-out-hook 'check-for-clock-out-note)
557 
558 (defun org-list-files (dirs ext)
559  "Function to create list of org files in multiple subdirectories.
560 This can be called to generate a list of files for
561 org-agenda-files or org-refile-targets.
562 
563 DIRS is a list of directories.
564 
565 EXT is a list of the extensions of files to be included."
566  (let ((dirs (if (listp dirs)
567  dirs
568  (list dirs)))
569  (ext (if (listp ext)
570  ext
571  (list ext)))
572  files)
573  (mapc
574  (lambda (x)
575  (mapc
576  (lambda (y)
577  (setq files
578  (append files
579  (file-expand-wildcards
580  (concat (file-name-as-directory x) "*" y)))))
581  ext))
582  dirs)
583  (mapc
584  (lambda (x)
585  (when (or (string-match "/.#" x)
586  (string-match "#$" x))
587  (setq files (delete x files))))
588  files)
589  files))
590 
591 (defvar org-agenda-directories (list (join-paths company-source-directory "org/plan")
592  (join-paths company-source-directory "org/plan/tasks"))
593  "List of directories containing org files.")
594 
595 (defvar org-agenda-extensions '(".org")
596  "List of extensions of agenda files")
597 
598 (setq org-agenda-default-appointment-duration 30)
599 (setq org-agenda-span 5)
600 (defun org-set-agenda-files ()
601  (interactive)
602  (setq org-agenda-files
603  (cons org-inbox-file
604  (cl-remove-if (lambda (x) (string= "readme.org" (file-name-nondirectory x)))
605  (org-list-files
606  org-agenda-directories
607  org-agenda-extensions)))))
608 
609 (with-eval-after-load 'org
610  (org-set-agenda-files))
611 
612 ;; org-agenda-auto-update
613 (defvar org-agenda-update-interval 300)
614 (defvar org-agenda-update-timer nil)
615 (defvar org-agenda-update-idle t)
616 
617 (defun org-agenda-update ()
618  (org-agenda-redo-all t))
619 
620 (defun org-agenda-auto-update ()
621  (when org-agenda-update-timer
622  (setq org-agenda-update-timer
623  (cancel-timer org-agenda-update-timer)))
624  (setq org-agenda-update-timer
625  (if org-agenda-update-idle
626  (run-with-idle-timer org-agenda-update-interval t 'org-agenda-update)
627  ;; when we refresh the org-agenda buffer, also reset the timer
628  (add-hook 'org-agenda-finalize-hook 'org-agenda-auto-update)
629  (run-with-timer org-agenda-update-interval org-agenda-update-interval 'org-agenda-update))))
630 
631 (with-eval-after-load 'org-agenda
632  (org-agenda-auto-update))
633 
634 ;;; Skel Config
635 (use-package skel
636  :requires skel
637  :load-path user-emacs-lib-directory
638  :custom
639  tempo-interactive t
640  auto-insert 'no-modify
641  auto-insert-query nil)
642 
643 (use-package skt
644  :requires (skel skt)
645  :load-path user-emacs-lib-directory
646  :custom
647  skt-enable-tempo-elements t
648  skt-delete-duplicate-marks t
649  :config
650  (defvar skt-default-version "0.1.0")
651  (keymap-set skt-minor-mode-map "b" #'tempo-backward-mark)
652  (keymap-set skt-minor-mode-map "f" #'tempo-forward-mark)
653  (keymap-set skt-minor-mode-map "SPC" #'tempo-complete-tag)
654  (keymap-set skt-minor-mode-map "t" #'skt-add-tag)
655 
656  (defvar skt-skeleton-path-function #'abbreviate-file-name
657  "Function to be called when expanding file-header skeletons. Useful to
658 rebind locally inside a project or module, where you want to delete some
659 prefix or replace it.")
660 
661  (defun skt-buffer-path (&optional function)
662  (let ((path (or buffer-file-name (format "%s.lisp" (gensym "scratch-")))))
663  (funcall (or function skt-skeleton-path-function) path)))
664 
665  (defun skt-skelfile-path ()
666  (if (string= (file-name-nondirectory buffer-file-name) "skelfile")
667  "skelfile"
668  (skt-buffer-path)))
669 
670  ;; functions
671  (skt-define-function capture (:abbrev "capture" :tag t) org-capture)
672  (skt-define-function agenda (:abbrev "agenda" :tag t) org-agenda)
673  (skt-define-function mjump (:abbrev "mjump" :tag t) bookmark-jump)
674  (skt-define-function bjump (:abbrev "bjump" :tag t) ibuffer-jump)
675  (skt-define-function rjump (:abbrev "rjump" :tag t)
676  (lambda () (jump-to-register (read-char "register: "))))
677  (skt-define-function pjump (:abbrev "pjump" :tag t) (lambda () (project-switch-project default-directory)))
678 
679  ;; templates
680  (skt-define-template readme (:mode org-mode :tag t)
681  "#+title: " (p "title: ") n
682  "#+description: " (p "description: ") n
683  "#+author: " user-full-name n
684  "#+email:" user-mail-address n
685  "#+setupfile: clean.theme" n
686  "#+export_file_name: index" n>
687  p n> n>
688  ":info:" n>
689  "+ version :: " skt-default-version n
690  ":end:" n>)
691 
692  (skt-define-template clean.theme (:mode org-mode :tag t)
693  "#+setupfile: " (join-paths company-cdn-url "org/clean.theme"))
694 
695  ;; TODO 2024-06-04:
696  ;; (skt-define-template defsystem (:mode lisp-mode :tag t :abbrev "defsystem"))
697  ;; (skt-define-template defpackage (:mode lisp-mode :tag t :abbrev "defpackage"))
698  ;; (skt-define-template defpkg (:mode lisp-mode :tag t :abbrev "defpkg"))
699 
700  (skt-define-template defmacro (:abbrev "(defmacro" :tag t :mode lisp-mode)
701  "(defmacro " (p "Name: ") " (" (p "Args: ") ")" > n> r ")")
702 
703  (skt-define-template defun (:abbrev "(defun" :tag t :mode lisp-mode)
704  "(defun " (p "Name: ") " (" (p "Args: ") ")" > n> r ")")
705 
706  (skt-define-template defvar (:abbrev "(defvar" :tag t :mode lisp-mode)
707  > "(defvar " > r ")")
708 
709  ;; skeletons
710  (skt-define-skeleton head (:abbrev "head" :mode lisp-mode)
711  "description: "
712  ";;; " (skt-buffer-path 'file-name-nondirectory) " --- " str \n \n ";; " _ \n \n ";;; Code:" \n >)
713 
714  (skt-define-skeleton head (:abbrev "head" :mode skel-mode)
715  "description: "
716  ";;; " (skt-skelfile-path) " --- " str " -*- mode: skel; -*-" \n _)
717 
718  (skt-define-skeleton head (:abbrev "head" :mode org-mode)
719  "title: "
720  "#+title: " str \n
721  "#+author: " (skeleton-read "author: ") \n
722  "#+description: " (skeleton-read "description: ") \n
723  "#+setupfile: clean.theme" \n > _)
724 
725  (skt-define-skeleton head (:abbrev "head" :mode rust-mode)
726  "description: "
727  "//! " (skt-buffer-path 'file-name-nondirectory) " --- " str \n \n "// " _ \n \n "//! Code: " \n >)
728 
729  (skt-define-skeleton system-head (:abbrev "system-head" :mode lisp-mode)
730  "system-name: "
731  ";;; " (skt-buffer-path) " --- "
732  '(setq v1 (file-name-base (skt-buffer-path))) (capitalize v1)
733  " Sytem Definitions" \n
734  > "(defsystem :" v1 \n
735  > ":depends-on (:std :log)" \n
736  > ":components ((:file \"pkg\")" _ "))")
737 
738  (skt-define-skeleton pkg-head (:abbrev "pkg-head" :mode lisp-mode)
739  "ignored"
740  ";;; " (skt-buffer-path 'file-name-nondirectory) " --- "
741  '(setq v1 (skeleton-read "name: ")) v1 " Package Definitions" \n
742  > "(defpkg :" v1 \n
743  > ":use (:std :log))" \n \n
744  > "(in-package :" v1 ")" \n >)
745 
746  (skt-define-skeleton crate-head (:abbrev "crate-head" :mode conf-toml-mode)
747  "ignored"
748  "### " (skt-buffer-path 'file-name-nondirectory) " --- "
749  '(setq v1 (skeleton-read "name: ")) v1 " Cargo Manifest" \n >
750  "[package]" \n
751  "name = \"" v1 "\"" \n
752  "version = \"" skt-default-version "\"" \n
753  "[dependencies]" \n >)
754 
755  (skt-define-skeleton local-vars
756  (:tag t :abbrev "local-vars"
757  :docstring "Insert a local variables section. Use current comment syntax if any.")
758  (completing-read "Mode: " obarray
759  (lambda (symbol)
760  (if (commandp symbol)
761  (string-match "-mode$" (symbol-name symbol))))
762  t)
763  '(save-excursion
764  (if (re-search-forward page-delimiter nil t)
765  (error "Not on last page")))
766  comment-start "Local Variables:" comment-end \n
767  comment-start "mode: " str
768  & -5 | '(kill-line 0) & -1 | comment-end \n
769  ( (completing-read (format "Variable, %s: " skeleton-subprompt)
770  obarray
771  (lambda (symbol)
772  (or (eq symbol 'eval)
773  (custom-variable-p symbol)))
774  t)
775  comment-start str ": "
776  (read-from-minibuffer "Expression: " nil read-expression-map nil
777  'read-expression-history) | _
778  comment-end \n)
779  resume:
780  comment-start "End:" comment-end \n)
781 
782  ;; autoinsert
783  (skt-register-auto-insert "skelfile" #'skt-template-skel-head)
784  (skt-register-auto-insert "readme.org" #'skt-template-org-readme)
785  (skt-register-auto-insert "Cargo.toml" #'skt-template-conf-toml-crate-head)
786  (skt-register-auto-insert "pkg.lisp" #'skt-template-lisp-pkg-head)
787  (skt-register-auto-insert ".*[.]asd" #'skt-template-lisp-system-head)
788  (skt-register-auto-insert ".*[.]lisp" #'skt-template-lisp-head)
789  (skt-register-auto-insert ".*[.].rs" #'skt-template-rust-head)
790  (auto-insert-mode t)
791  ;; (keymap-set skel-minor-mode-map "C-<return>" 'company-tempo)
792  )
793 
794 ;;; ical2org
795 ;; go install github.com/rjhorniii/ical2org@latest
796 (defun ical2org (file)
797  "Convert ics FILE to an org-mode heading."
798  (interactive "ffile: ")
799  (shell-command (format "ical2org %s -a %s" file org-inbox-file)))
800 
801 ;;; glossary
802 ;; (with-eval-after-load 'org-glossary
803 ;; (setq org-glossary-collection-root (join-paths company-source-directory "org/meta/"))
804 ;; (cl-pushnew '("Terms" . glossary) org-glossary-headings)
805 ;; (cl-pushnew '("Acronyms" . acronym) org-glossary-headings))
806 
807 ;;; Calc
808 (setq calc-highlight-selections-with-faces t)
809 (cl-pushnew '(lisp-mode "#| " "|#
810 ") calc-embedded-open-close-mode-alist)
811 (cl-pushnew '(emacs-lisp-mode ";; " "
812 ") calc-embedded-open-close-mode-alist)
813 
814 (defun calc-eval-region (arg beg end)
815  "Calculate the region and display the result in the echo area.
816 With prefix ARG non-nil, insert the result at the end of region."
817  (interactive "P\nr")
818  (let* ((expr (buffer-substring-no-properties beg end))
819  (result (calc-eval expr)))
820  (if (null arg)
821  (message "%s = %s" expr result)
822  (goto-char end)
823  (save-excursion
824  (insert result)))))
825 
826 (defun calc-embedded-formula-to-stack ()
827  (interactive)
828  (save-excursion
829  (save-match-data
830  (calc-embedded-find-bounds)))
831  (let ((eq-str (buffer-substring calc-embed-top calc-embed-bot)))
832  (calc-eval eq-str 'push)))
833 
834 (provide 'ellis)
835 ;; ellis.el ends here