changelog shortlog graph tags branches changeset files revisions annotate raw help

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

changeset 94: 978ce75e54af
parent: a86cb552d6df
child: f69061a590da
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 30 Aug 2024 17:07:30 -0400
permissions: -rw-r--r--
description: add stumpwm modules and org-timeline
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 "inbox" :query "tag:inbox" :key "i")
116  (:name "unread" :query "tag:unread" :key "u")
117  (:name "new" :query "tag:new" :key "n")
118  (:name "sent" :query "tag:sent" :key "e")
119  (:name "drafts" :query "tag:draft" :key "d")
120  (:name "all mail" :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  ;; comp
191  ;; ("https://lab.rwest.io/comp.atom?feed_token=pHu9qwLkjy4CWJHx9rrJ" comp vc)
192  ("https://www.reddit.com/r/listentothis/.rss" music reddit)
193  ("https://www.ftc.gov/feeds/press-release-consumer-protection.xml" gov ftc)
194  ("https://api2.fcc.gov/edocs/public/api/v1/rss/" gov fcc)
195  )
196  :init
197  (defun yt-dl-it (url)
198  "Downloads the URL in an async shell"
199  (let ((default-directory user-stash-directory))
200  (async-shell-command (format "yt-dlp %s" url))))
201 
202  (defun elfeed-youtube-dl (&optional use-generic-p)
203  "Youtube-DL link"
204  (interactive "P")
205  (let ((entries (elfeed-search-selected)))
206  (cl-loop for entry in entries
207  do (elfeed-untag entry 'unread)
208  when (elfeed-entry-link entry)
209  do (yt-dl-it it))
210  (mapc #'elfeed-search-update-entry entries)
211  (unless (use-region-p) (forward-line))))
212  :config
213  (keymap-set elfeed-search-mode-map "d" 'elfeed-youtube-dl)
214  (keymap-set user-map "e f" #'elfeed)
215  (keymap-set user-map "e F" #'elfeed-update))
216 
217 (use-package elfeed-tube
218  :ensure t
219  :after elfeed
220  :config
221  ;; (elfeed-tube-setup)
222  (elfeed-tube-add-feeds '("detroit techno" "boiler room dj" "brad mehldau" "chris 'daddy' dave"))
223  :bind (:map elfeed-show-mode-map
224  ("F" . elfeed-tube-fetch)
225  ([remap save-buffer] . elfeed-tube-save)
226  :map elfeed-search-mode-map
227  ("F" . elfeed-tube-fetch)
228  ([remap save-buffer] . elfeed-tube-save)))
229 
230 (use-package elfeed-tube-mpv
231  :ensure t
232  :bind (:map elfeed-show-mode-map
233  ("C-c C-f" . elfeed-tube-mpv-follow-mode)
234  ("C-c C-w" . elfeed-tube-mpv-where)))
235 
236 (use-package org-mime :ensure t)
237 
238 (use-package sh-script
239  :hook (sh-mode . flymake-mode))
240 
241 ;;; Diary
242 (setq diary-list-include-blanks t)
243 ;;; Org Config
244 (setq publish-dir "/ssh:rurik:/srv/http/compiler.company")
245 
246 ;; populate org-babel
247 (org-babel-do-load-languages
248  ;; TODO 2021-10-24: bqn, apl, k
249  'org-babel-load-languages '((shell . t)
250  (emacs-lisp . t)
251  (lisp . t)
252  (org . t)
253  (eshell . t)
254  (calc . t)
255  (sed . t)
256  (awk . t)
257  (dot . t)
258  (js . t)
259  (C . t)
260  (python . t)
261  (lua . t)
262  (lilypond . t)))
263 
264 ;; timeline
265 (use-package org-timeline
266  :load-path user-emacs-lib-directory
267  :hook (org-agenda-finalize . org-timeline-insert-timeline)
268  :init
269  (setq
270  org-timeline-insert-before-text "›"
271  org-timeline-beginning-of-day-hour 8
272  org-timeline-keep-elapsed 2
273  org-timeline-start-hour 5
274  org-timeline-show-text-in-blocks t
275  org-timeline-prepend t))
276 
277 ;;; IRC
278 (setq erc-format-nick-function 'erc-format-@nick)
279 
280 (defun start-erc ()
281  "Connect to IRC."
282  (interactive)
283  (erc-tls :server "irc.libera.chat" :port 6697
284  :client-certificate '("/mnt/y/data/private/krypt/libera.pem"))
285  (setq erc-autojoin-channels-alist '(("irc.libera.chat" "#emacs")
286  ("irc.libera.chat" "#linux")
287  ("irc.libera.chat" "#rust")
288  ("irc.libera.chat" "#btrfs")
289  ("irc.libera.chat" "#lisp")
290  ("irc.libera.chat" "#sbcl")
291  ("irc.oftc.net" "#llvm"))))
292 ;;; Tags
293 ;;;###autoload
294 (defun refresh-tags ()
295  "Refresh TAGS database in `user-emacs-directory'."
296  (interactive)
297  (let ((default-directory user-emacs-directory))
298  (async-shell-command
299  "etags ./*.el \\
300 ./lib/*.el \\
301 ~/comp/core/emacs/*.el \\
302 ~/comp/core/emacs/lib/*.el \\
303 -o TAGS")))
304 
305 (unless (string-equal "hyde" system-name)
306  (add-hook 'dired-mode-hook #'all-the-icons-dired-mode)
307  (add-hook 'ibuffer-mode-hook #'all-the-icons-ibuffer-mode))
308 
309 ;; strangerdanger
310 ;; (setq slime-enable-evaluate-in-emacs t)
311 
312 (defun org-word-count (beg end
313  &optional count-latex-macro-args?
314  count-footnotes?)
315  "Report the number of words in the Org mode buffer or selected region.
316 Ignores:
317 - comments
318 - tables
319 - source code blocks (#+BEGIN_SRC ... #+END_SRC, and inline blocks)
320 - hyperlinks (but does count words in hyperlink descriptions)
321 - tags, priorities, and TODO keywords in headers
322 - sections tagged as 'not for export'.
323 
324 The text of footnote definitions is ignored, unless the optional argument
325 COUNT-FOOTNOTES? is non-nil.
326 
327 If the optional argument COUNT-LATEX-MACRO-ARGS? is non-nil, the word count
328 includes LaTeX macro arguments (the material between {curly braces}).
329 Otherwise, and by default, every LaTeX macro counts as 1 word regardless
330 of its arguments."
331  (interactive "r")
332  (unless mark-active
333  (setf beg (point-min)
334  end (point-max)))
335  (let ((wc 0)
336  (latex-macro-regexp "\\\\[A-Za-z]+\\(\\[[^]]*\\]\\|\\){\\([^}]*\\)}"))
337  (save-excursion
338  (goto-char beg)
339  (while (< (point) end)
340  (cond
341  ;; Ignore comments.
342  ((or (org-in-commented-line) (org-at-table-p))
343  nil)
344  ;; Ignore hyperlinks. But if link has a description, count
345  ;; the words within the description.
346  ((looking-at org-bracket-link-analytic-regexp)
347  (when (match-string-no-properties 5)
348  (let ((desc (match-string-no-properties 5)))
349  (save-match-data
350  (cl-incf wc (length (remove "" (org-split-string
351  desc "\\W")))))))
352  (goto-char (match-end 0)))
353  ((looking-at org-any-link-re)
354  (goto-char (match-end 0)))
355  ;; Ignore source code blocks.
356  ((org-in-regexps-block-p "^#\\+BEGIN_SRC\\W" "^#\\+END_SRC\\W")
357  nil)
358  ;; Ignore inline source blocks, counting them as 1 word.
359  ((save-excursion
360  (backward-char)
361  (looking-at org-babel-inline-src-block-regexp))
362  (goto-char (match-end 0))
363  (setf wc (+ 2 wc)))
364  ;; Count latex macros as 1 word, ignoring their arguments.
365  ((save-excursion
366  (backward-char)
367  (looking-at latex-macro-regexp))
368  (goto-char (if count-latex-macro-args?
369  (match-beginning 2)
370  (match-end 0)))
371  (setf wc (+ 2 wc)))
372  ;; Ignore footnotes.
373  ((and (not count-footnotes?)
374  (or (org-footnote-at-definition-p)
375  (org-footnote-at-reference-p)))
376  nil)
377  (t
378  (let ((contexts (org-context)))
379  (cond
380  ;; Ignore tags and TODO keywords, etc.
381  ((or (assoc :todo-keyword contexts)
382  (assoc :priority contexts)
383  (assoc :keyword contexts)
384  (assoc :checkbox contexts))
385  nil)
386  ;; Ignore sections marked with tags that are
387  ;; excluded from export.
388  ((assoc :tags contexts)
389  (if (intersection (org-get-tags-at) org-export-exclude-tags
390  :test 'equal)
391  (org-forward-same-level 1)
392  nil))
393  (t
394  (cl-incf wc))))))
395  (re-search-forward "\\w+\\W*")))
396  (message (format "%d words in %s." wc
397  (if mark-active "region" "buffer")))))
398 
399 (defun org-check-misformatted-subtree ()
400  "Check misformatted entries in the current buffer."
401  (interactive)
402  (show-all)
403  (org-map-entries
404  (lambda ()
405  (when (and (move-beginning-of-line 2)
406  (not (looking-at org-heading-regexp)))
407  (if (or (and (org-get-scheduled-time (point))
408  (not (looking-at (concat "^.*" org-scheduled-regexp))))
409  (and (org-get-deadline-time (point))
410  (not (looking-at (concat "^.*" org-deadline-regexp)))))
411  (when (y-or-n-p "Fix this subtree? ")
412  (message "Call the function again when you're done fixing this subtree.")
413  (recursive-edit))
414  (message "All subtrees checked."))))))
415 
416 (defun org-sort-list-by-checkbox-type ()
417  "Sort list items according to Checkbox state."
418  (interactive)
419  (org-sort-list
420  nil ?f
421  (lambda ()
422  (if (looking-at org-list-full-item-re)
423  (cdr (assoc (match-string 3)
424  '(("[X]" . 1) ("[-]" . 2) ("[ ]" . 3) (nil . 4))))
425  4))))
426 
427 (defun org-time-string-to-seconds (s)
428  "Convert a string HH:MM:SS to a number of seconds."
429  (cond
430  ((and (stringp s)
431  (string-match "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)" s))
432  (let ((hour (string-to-number (match-string 1 s)))
433  (min (string-to-number (match-string 2 s)))
434  (sec (string-to-number (match-string 3 s))))
435  (+ (* hour 3600) (* min 60) sec)))
436  ((and (stringp s)
437  (string-match "\\([0-9]+\\):\\([0-9]+\\)" s))
438  (let ((min (string-to-number (match-string 1 s)))
439  (sec (string-to-number (match-string 2 s))))
440  (+ (* min 60) sec)))
441  ((stringp s) (string-to-number s))
442  (t s)))
443 
444 (defun org-time-seconds-to-string (secs)
445  "Convert a number of seconds to a time string."
446  (cond ((>= secs 3600) (format-seconds "%h:%.2m:%.2s" secs))
447  ((>= secs 60) (format-seconds "%m:%.2s" secs))
448  (t (format-seconds "%s" secs))))
449 
450 (defmacro with-time (time-output-p &rest exprs)
451  "Evaluate an org-table formula, converting all fields that look
452 like time data to integer seconds. If TIME-OUTPUT-P then return
453 the result as a time value."
454  (list
455  (if time-output-p 'org-time-seconds-to-string 'identity)
456  (cons 'progn
457  (mapcar
458  (lambda (expr)
459  `,(cons (car expr)
460  (mapcar
461  (lambda (el)
462  (if (listp el)
463  (list 'with-time nil el)
464  (org-time-string-to-seconds el)))
465  (cdr expr))))
466  `,@exprs))))
467 
468 (defun org-hex-strip-lead (str)
469  (if (and (> (length str) 2) (string= (substring str 0 2) "0x"))
470  (substring str 2) str))
471 
472 (defun org-hex-to-hex (int)
473  (format "0x%x" int))
474 
475 (defun org-hex-to-dec (str)
476  (cond
477  ((and (stringp str)
478  (string-match "\\([0-9a-f]+\\)" (setf str (org-hex-strip-lead str))))
479  (let ((out 0))
480  (mapc
481  (lambda (ch)
482  (setf out (+ (* out 16)
483  (if (and (>= ch 48) (<= ch 57)) (- ch 48) (- ch 87)))))
484  (coerce (match-string 1 str) 'list))
485  out))
486  ((stringp str) (string-to-number str))
487  (t str)))
488 
489 (defmacro with-hex (hex-output-p &rest exprs)
490  "Evaluate an org-table formula, converting all fields that look
491  like hexadecimal to decimal integers. If HEX-OUTPUT-P then
492  return the result as a hex value."
493  (list
494  (if hex-output-p 'org-hex-to-hex 'identity)
495  (cons 'progn
496  (mapcar
497  (lambda (expr)
498  `,(cons (car expr)
499  (mapcar (lambda (el)
500  (if (listp el)
501  (list 'with-hex nil el)
502  (org-hex-to-dec el)))
503  (cdr expr))))
504  `,@exprs))))
505 
506 (require 'mm-url) ; to include mm-url-decode-entities-string
507 
508 (defun org-insert-link-with-title ()
509  "Insert org link where default description is set to html title."
510  (interactive)
511  (let* ((url (read-string "URL: "))
512  (title (get-html-title-from-url url)))
513  (org-insert-link nil url title)))
514 
515 (defun get-html-title-from-url (url)
516  "Return content in <title> tag."
517  (let (x1 x2 (download-buffer (url-retrieve-synchronously url)))
518  (save-excursion
519  (set-buffer download-buffer)
520  (beginning-of-buffer)
521  (setq x1 (search-forward "<title>"))
522  (search-forward "</title>")
523  (setq x2 (search-backward "<"))
524  (mm-url-decode-entities-string (buffer-substring-no-properties x1 x2)))))
525 
526 (defun org-remove-empty-propert-drawers ()
527  "*Remove all empty property drawers in current file."
528  (interactive)
529  (unless (eq major-mode 'org-mode)
530  (error "You need to turn on Org mode for this function."))
531  (save-excursion
532  (goto-char (point-min))
533  (while (re-search-forward ":PROPERTIES:" nil t)
534  (save-excursion
535  (org-remove-empty-drawer-at "PROPERTIES" (match-beginning 0))))))
536 
537 (defun check-for-clock-out-note ()
538  (interactive)
539  (save-excursion
540  (org-back-to-heading)
541  (let ((tags (org-get-tags)))
542  (and tags (message "tags: %s " tags)
543  (when (member "clocknote" tags)
544  (org-add-note))))))
545 
546 (add-hook 'org-clock-out-hook 'check-for-clock-out-note)
547 
548 (defun org-list-files (dirs ext)
549  "Function to create list of org files in multiple subdirectories.
550 This can be called to generate a list of files for
551 org-agenda-files or org-refile-targets.
552 
553 DIRS is a list of directories.
554 
555 EXT is a list of the extensions of files to be included."
556  (let ((dirs (if (listp dirs)
557  dirs
558  (list dirs)))
559  (ext (if (listp ext)
560  ext
561  (list ext)))
562  files)
563  (mapc
564  (lambda (x)
565  (mapc
566  (lambda (y)
567  (setq files
568  (append files
569  (file-expand-wildcards
570  (concat (file-name-as-directory x) "*" y)))))
571  ext))
572  dirs)
573  (mapc
574  (lambda (x)
575  (when (or (string-match "/.#" x)
576  (string-match "#$" x))
577  (setq files (delete x files))))
578  files)
579  files))
580 
581 (defvar org-agenda-directories (list (join-paths company-source-directory "org/plan")
582  (join-paths company-source-directory "org/plan/tasks"))
583  "List of directories containing org files.")
584 
585 (defvar org-agenda-extensions '(".org")
586  "List of extensions of agenda files")
587 
588 (defun org-set-agenda-files ()
589  (interactive)
590  (setq org-agenda-files
591  (cons org-inbox-file
592  (cl-remove-if (lambda (x) (string= "readme.org" (file-name-nondirectory x)))
593  (org-list-files
594  org-agenda-directories
595  org-agenda-extensions)))))
596 
597 (with-eval-after-load 'org
598  (org-set-agenda-files))
599 
600 ;;; Skel Config
601 (use-package skel
602  :requires skel
603  :load-path user-emacs-lib-directory
604  :custom
605  tempo-interactive t
606  auto-insert 'no-modify
607  auto-insert-query nil)
608 
609 (use-package skt
610  :requires (skel skt)
611  :load-path user-emacs-lib-directory
612  :custom
613  skt-enable-tempo-elements t
614  skt-delete-duplicate-marks t
615  :config
616  (defvar skt-default-version "0.1.0")
617  (keymap-set skt-minor-mode-map "b" #'tempo-backward-mark)
618  (keymap-set skt-minor-mode-map "f" #'tempo-forward-mark)
619  (keymap-set skt-minor-mode-map "SPC" #'tempo-complete-tag)
620  (keymap-set skt-minor-mode-map "t" #'skt-add-tag)
621 
622  (defvar skt-skeleton-path-function #'abbreviate-file-name
623  "Function to be called when expanding file-header skeletons. Useful to
624 rebind locally inside a project or module, where you want to delete some
625 prefix or replace it.")
626 
627  (defun skt-buffer-path (&optional function)
628  (let ((path (or buffer-file-name (format "%s.lisp" (gensym "scratch-")))))
629  (funcall (or function skt-skeleton-path-function) path)))
630 
631  (defun skt-skelfile-path ()
632  (if (string= (file-name-nondirectory buffer-file-name) "skelfile")
633  "skelfile"
634  (skt-buffer-path)))
635 
636  ;; functions
637  (skt-define-function capture (:abbrev "capture" :tag t) org-capture)
638  (skt-define-function agenda (:abbrev "agenda" :tag t) org-agenda)
639  (skt-define-function mjump (:abbrev "mjump" :tag t) bookmark-jump)
640  (skt-define-function bjump (:abbrev "bjump" :tag t) ibuffer-jump)
641  (skt-define-function rjump (:abbrev "rjump" :tag t)
642  (lambda () (jump-to-register (read-char "register: "))))
643  (skt-define-function pjump (:abbrev "pjump" :tag t) (lambda () (project-switch-project default-directory)))
644 
645  ;; templates
646  (skt-define-template readme (:mode org-mode :tag t)
647  "#+title: " (p "title: ") n
648  "#+description: " (p "description: ") n
649  "#+author: " user-full-name n
650  "#+email:" user-mail-address n
651  "#+setupfile: clean.theme" n
652  "#+export_file_name: index" n>
653  p n> n>
654  ":info:" n>
655  "+ version :: " skt-default-version n
656  ":end:" n>)
657 
658  (skt-define-template clean.theme (:mode org-mode :tag t)
659  "#+setupfile: " (join-paths company-cdn-url "org/clean.theme"))
660 
661  ;; TODO 2024-06-04:
662  ;; (skt-define-template defsystem (:mode lisp-mode :tag t :abbrev "defsystem"))
663  ;; (skt-define-template defpackage (:mode lisp-mode :tag t :abbrev "defpackage"))
664  ;; (skt-define-template defpkg (:mode lisp-mode :tag t :abbrev "defpkg"))
665 
666  (skt-define-template defmacro (:abbrev "(defmacro" :tag t :mode lisp-mode)
667  "(defmacro " (p "Name: ") " (" (p "Args: ") ")" > n> r ")")
668 
669  (skt-define-template defun (:abbrev "(defun" :tag t :mode lisp-mode)
670  "(defun " (p "Name: ") " (" (p "Args: ") ")" > n> r ")")
671 
672  (skt-define-template defvar (:abbrev "(defvar" :tag t :mode lisp-mode)
673  > "(defvar " > r ")")
674 
675  ;; skeletons
676  (skt-define-skeleton head (:abbrev "head" :mode lisp-mode)
677  "description: "
678  ";;; " (skt-buffer-path 'file-name-nondirectory) " --- " str \n \n ";; " _ \n \n ";;; Code:" \n >)
679 
680  (skt-define-skeleton head (:abbrev "head" :mode skel-mode)
681  "description: "
682  ";;; " (skt-skelfile-path) " --- " str " -*- mode: skel; -*-" \n _)
683 
684  (skt-define-skeleton head (:abbrev "head" :mode org-mode)
685  "title: "
686  "#+title: " str \n
687  "#+author: " (skeleton-read "author: ") \n
688  "#+description: " (skeleton-read "description: ") \n
689  "#+setupfile: clean.theme" \n > _)
690 
691  (skt-define-skeleton head (:abbrev "head" :mode rust-mode)
692  "description: "
693  "//! " (skt-buffer-path 'file-name-nondirectory) " --- " str \n \n "// " _ \n \n "//! Code: " \n >)
694 
695  (skt-define-skeleton system-head (:abbrev "system-head" :mode lisp-mode)
696  "system-name: "
697  ";;; " (skt-buffer-path) " --- "
698  '(setq v1 (file-name-base (skt-buffer-path))) (capitalize v1)
699  " Sytem Definitions" \n
700  > "(defsystem :" v1 \n
701  > ":depends-on (:std :log)" \n
702  > ":components ((:file \"pkg\")" _ "))")
703 
704  (skt-define-skeleton pkg-head (:abbrev "pkg-head" :mode lisp-mode)
705  "ignored"
706  ";;; " (skt-buffer-path 'file-name-nondirectory) " --- "
707  '(setq v1 (skeleton-read "name: ")) v1 " Package Definitions" \n
708  > "(defpkg :" v1 \n
709  > ":use (:std :log))" \n \n
710  > "(in-package :" v1 ")" \n >)
711 
712  (skt-define-skeleton crate-head (:abbrev "crate-head" :mode conf-toml-mode)
713  "ignored"
714  "### " (skt-buffer-path 'file-name-nondirectory) " --- "
715  '(setq v1 (skeleton-read "name: ")) v1 " Cargo Manifest" \n >
716  "[package]" \n
717  "name = \"" v1 "\"" \n
718  "version = \"" skt-default-version "\"" \n
719  "[dependencies]" \n >)
720 
721  (skt-define-skeleton local-vars
722  (:tag t :abbrev "local-vars"
723  :docstring "Insert a local variables section. Use current comment syntax if any.")
724  (completing-read "Mode: " obarray
725  (lambda (symbol)
726  (if (commandp symbol)
727  (string-match "-mode$" (symbol-name symbol))))
728  t)
729  '(save-excursion
730  (if (re-search-forward page-delimiter nil t)
731  (error "Not on last page")))
732  comment-start "Local Variables:" comment-end \n
733  comment-start "mode: " str
734  & -5 | '(kill-line 0) & -1 | comment-end \n
735  ( (completing-read (format "Variable, %s: " skeleton-subprompt)
736  obarray
737  (lambda (symbol)
738  (or (eq symbol 'eval)
739  (custom-variable-p symbol)))
740  t)
741  comment-start str ": "
742  (read-from-minibuffer "Expression: " nil read-expression-map nil
743  'read-expression-history) | _
744  comment-end \n)
745  resume:
746  comment-start "End:" comment-end \n)
747 
748  ;; autoinsert
749  (skt-register-auto-insert "skelfile" #'skt-template-skel-head)
750  (skt-register-auto-insert "readme.org" #'skt-template-org-readme)
751  (skt-register-auto-insert "Cargo.toml" #'skt-template-conf-toml-crate-head)
752  (skt-register-auto-insert "pkg.lisp" #'skt-template-lisp-pkg-head)
753  (skt-register-auto-insert ".*[.]asd" #'skt-template-lisp-system-head)
754  (skt-register-auto-insert ".*[.]lisp" #'skt-template-lisp-head)
755  (skt-register-auto-insert ".*[.].rs" #'skt-template-rust-head)
756  (auto-insert-mode t)
757  (keymap-set skel-minor-mode-map "C-<return>" 'company-tempo))
758 
759 ;;; glossary
760 ;; (with-eval-after-load 'org-glossary
761 ;; (setq org-glossary-collection-root (join-paths company-source-directory "org/meta/"))
762 ;; (cl-pushnew '("Terms" . glossary) org-glossary-headings)
763 ;; (cl-pushnew '("Acronyms" . acronym) org-glossary-headings))
764 
765 ;;; Calc
766 (setq calc-highlight-selections-with-faces t)
767 (cl-pushnew '(lisp-mode "#| " "|#
768 ") calc-embedded-open-close-mode-alist)
769 (cl-pushnew '(emacs-lisp-mode ";; " "
770 ") calc-embedded-open-close-mode-alist)
771 
772 (defun calc-eval-region (arg beg end)
773  "Calculate the region and display the result in the echo area.
774 With prefix ARG non-nil, insert the result at the end of region."
775  (interactive "P\nr")
776  (let* ((expr (buffer-substring-no-properties beg end))
777  (result (calc-eval expr)))
778  (if (null arg)
779  (message "%s = %s" expr result)
780  (goto-char end)
781  (save-excursion
782  (insert result)))))
783 
784 (defun calc-embedded-formula-to-stack ()
785  (interactive)
786  (save-excursion
787  (save-match-data
788  (calc-embedded-find-bounds)))
789  (let ((eq-str (buffer-substring calc-embed-top calc-embed-bot)))
790  (calc-eval eq-str 'push)))
791 
792 (provide 'ellis)
793 ;; ellis.el ends here