# HG changeset patch # User Richard Westhaver # Date 1718495858 14400 # Node ID d70be963bfb1107954b30ebc2052095a5d589231 # Parent 2d74d85d7031af7757617a90fe1924d0b781aa9b# Parent b9e2f76128bba61cd7b4f0c8d4cb8eba1072f2b5 sbcl diff -r 2d74d85d7031 -r d70be963bfb1 .emacs.d/ellis.el --- a/.emacs.d/ellis.el Wed Jun 05 23:31:48 2024 +0000 +++ b/.emacs.d/ellis.el Sat Jun 15 19:57:38 2024 -0400 @@ -33,9 +33,9 @@ (setopt default-theme 'modus-vivendi-tritanopia user-lab-directory (join-paths user-home-directory "lab") - company-source-directory (join-paths user-lab-directory "comp")) + company-source-directory (join-paths user-home-directory "comp")) -(unless (display-graphic-p) (setq default-theme 'wheatgrass)) +;; (unless (display-graphic-p) (setq default-theme 'wheatgrass)) (when (linux-p) (setq dired-listing-switches "-alsh")) @@ -52,11 +52,11 @@ (keymap-set user-map "e c" #'edit-emacs-config) (keymap-set emacs-lisp-mode-map "C-c C-l" #'load-file) (keymap-set emacs-lisp-mode-map "C-c M-k" #'elisp-byte-compile-file) +(keymap-set user-map "v t" #'org-tags-view) (require 'paredit) -(add-hook 'common-lisp-mode-hook #'enable-paredit-mode) -(add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode) - +(add-hook 'lisp-mode-hook #'enable-paredit-mode) +(add-hook 'slime-editing-mode-hook #'enable-paredit-mode) (repeat-mode) (defun remember-project () @@ -68,25 +68,21 @@ (interactive) (project-remember-projects-under user-lab-directory t)) +(defun remember-comp-projects () + (interactive) + (project-remember-projects-under company-source-directory t)) + (keymap-global-set "C-" #'hippie-expand) (keymap-set minibuffer-local-map "C-" #'hippie-expand) (keymap-set ctl-x-x-map "p p" #'remember-project) (keymap-set ctl-x-x-map "p l" #'remember-lab-projects) -(add-hook 'prog-mode-hook #'skt-mode) -(add-hook 'org-mode-hook #'skt-mode) +(add-hook 'prog-mode-hook #'skel-minor-mode) +(add-hook 'org-mode-hook #'skel-minor-mode) (add-hook 'prog-mode-hook #'company-mode) (add-hook 'notmuch-message-mode-hook #'turn-on-orgtbl) -(setopt skt-enable-tempo-elements t - skt-completing-read t - skt-delete-duplicate-marks t) - -(keymap-set skt-mode-map "C-c M-b" #'tempo-backward-mark) -(keymap-set skt-mode-map "C-c M-f" #'tempo-forward-mark) -(keymap-set skt-mode-map "C-c M-a" #'tempo-complete-tag) - (use-package markdown-mode :ensure t) (use-package ol-notmuch :ensure t) @@ -101,7 +97,7 @@ smtpmail-debug-info t message-default-mail-headers "Cc: \nBcc: \n" message-kill-buffer-on-exit t - user-mail-address "ellis@rwest.io" + user-mail-address "richard.westhaver@gmail.com" user-full-name "Richard Westhaver" notmuch-hello-sections '(notmuch-hello-insert-saved-searches notmuch-hello-insert-search @@ -194,8 +190,8 @@ :init (defun yt-dl-it (url) "Downloads the URL in an async shell" - (let ((default-directory "~/media/yt")) - (async-shell-command (format "youtube-dl %s" url)))) + (let ((default-directory user-stash-directory)) + (async-shell-command (format "yt-dlp %s" url)))) (defun elfeed-youtube-dl (&optional use-generic-p) "Youtube-DL link" @@ -212,25 +208,30 @@ (keymap-set user-map "e f" #'elfeed) (keymap-set user-map "e F" #'elfeed-update)) +(use-package elfeed-tube + :ensure t + :after elfeed + :config + ;; (elfeed-tube-setup) + (elfeed-tube-add-feeds '("detroit techno" "boiler room dj" "brad mehldau" "chris 'daddy' dave")) + :bind (:map elfeed-show-mode-map + ("F" . elfeed-tube-fetch) + ([remap save-buffer] . elfeed-tube-save) + :map elfeed-search-mode-map + ("F" . elfeed-tube-fetch) + ([remap save-buffer] . elfeed-tube-save))) + +(use-package elfeed-tube-mpv + :ensure t + :bind (:map elfeed-show-mode-map + ("C-c C-f" . elfeed-tube-mpv-follow-mode) + ("C-c C-w" . elfeed-tube-mpv-where))) + (use-package org-mime :ensure t) (use-package sh-script :hook (sh-mode . flymake-mode)) -(use-package tempo - :custom - tempo-interactive t - :config - (tempo-define-template - "org:readme" - '("#+TITLE: " p n> - "#+AUTHOR: " user-full-name " <" user-mail-address ">" n>) - "org:readme" - "Insert a readme.org file template.") - (tempo-define-template "org:src" - '("#+begin_src " p n> - "#+end_src" n>) - "org:src")) ;;; Org Config (keymap-set user-map "t" #'org-todo) @@ -242,6 +243,7 @@ (lisp . t) (org . t) (eshell . t) + (calc . t) (sed . t) (awk . t) (dot . t) @@ -284,7 +286,448 @@ (add-hook 'ibuffer-mode-hook #'all-the-icons-ibuffer-mode)) ;; strangerdanger -(setq slime-enable-evaluate-in-emacs t) +;; (setq slime-enable-evaluate-in-emacs t) + +(defun org-word-count (beg end + &optional count-latex-macro-args? + count-footnotes?) + "Report the number of words in the Org mode buffer or selected region. +Ignores: +- comments +- tables +- source code blocks (#+BEGIN_SRC ... #+END_SRC, and inline blocks) +- hyperlinks (but does count words in hyperlink descriptions) +- tags, priorities, and TODO keywords in headers +- sections tagged as 'not for export'. + +The text of footnote definitions is ignored, unless the optional argument +COUNT-FOOTNOTES? is non-nil. + +If the optional argument COUNT-LATEX-MACRO-ARGS? is non-nil, the word count +includes LaTeX macro arguments (the material between {curly braces}). +Otherwise, and by default, every LaTeX macro counts as 1 word regardless +of its arguments." + (interactive "r") + (unless mark-active + (setf beg (point-min) + end (point-max))) + (let ((wc 0) + (latex-macro-regexp "\\\\[A-Za-z]+\\(\\[[^]]*\\]\\|\\){\\([^}]*\\)}")) + (save-excursion + (goto-char beg) + (while (< (point) end) + (cond + ;; Ignore comments. + ((or (org-in-commented-line) (org-at-table-p)) + nil) + ;; Ignore hyperlinks. But if link has a description, count + ;; the words within the description. + ((looking-at org-bracket-link-analytic-regexp) + (when (match-string-no-properties 5) + (let ((desc (match-string-no-properties 5))) + (save-match-data + (cl-incf wc (length (remove "" (org-split-string + desc "\\W"))))))) + (goto-char (match-end 0))) + ((looking-at org-any-link-re) + (goto-char (match-end 0))) + ;; Ignore source code blocks. + ((org-in-regexps-block-p "^#\\+BEGIN_SRC\\W" "^#\\+END_SRC\\W") + nil) + ;; Ignore inline source blocks, counting them as 1 word. + ((save-excursion + (backward-char) + (looking-at org-babel-inline-src-block-regexp)) + (goto-char (match-end 0)) + (setf wc (+ 2 wc))) + ;; Count latex macros as 1 word, ignoring their arguments. + ((save-excursion + (backward-char) + (looking-at latex-macro-regexp)) + (goto-char (if count-latex-macro-args? + (match-beginning 2) + (match-end 0))) + (setf wc (+ 2 wc))) + ;; Ignore footnotes. + ((and (not count-footnotes?) + (or (org-footnote-at-definition-p) + (org-footnote-at-reference-p))) + nil) + (t + (let ((contexts (org-context))) + (cond + ;; Ignore tags and TODO keywords, etc. + ((or (assoc :todo-keyword contexts) + (assoc :priority contexts) + (assoc :keyword contexts) + (assoc :checkbox contexts)) + nil) + ;; Ignore sections marked with tags that are + ;; excluded from export. + ((assoc :tags contexts) + (if (intersection (org-get-tags-at) org-export-exclude-tags + :test 'equal) + (org-forward-same-level 1) + nil)) + (t + (cl-incf wc)))))) + (re-search-forward "\\w+\\W*"))) + (message (format "%d words in %s." wc + (if mark-active "region" "buffer"))))) + +(defun org-check-misformatted-subtree () + "Check misformatted entries in the current buffer." + (interactive) + (show-all) + (org-map-entries + (lambda () + (when (and (move-beginning-of-line 2) + (not (looking-at org-heading-regexp))) + (if (or (and (org-get-scheduled-time (point)) + (not (looking-at (concat "^.*" org-scheduled-regexp)))) + (and (org-get-deadline-time (point)) + (not (looking-at (concat "^.*" org-deadline-regexp))))) + (when (y-or-n-p "Fix this subtree? ") + (message "Call the function again when you're done fixing this subtree.") + (recursive-edit)) + (message "All subtrees checked.")))))) + +(defun org-sort-list-by-checkbox-type () + "Sort list items according to Checkbox state." + (interactive) + (org-sort-list + nil ?f + (lambda () + (if (looking-at org-list-full-item-re) + (cdr (assoc (match-string 3) + '(("[X]" . 1) ("[-]" . 2) ("[ ]" . 3) (nil . 4)))) + 4)))) + +(defun org-time-string-to-seconds (s) + "Convert a string HH:MM:SS to a number of seconds." + (cond + ((and (stringp s) + (string-match "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)" s)) + (let ((hour (string-to-number (match-string 1 s))) + (min (string-to-number (match-string 2 s))) + (sec (string-to-number (match-string 3 s)))) + (+ (* hour 3600) (* min 60) sec))) + ((and (stringp s) + (string-match "\\([0-9]+\\):\\([0-9]+\\)" s)) + (let ((min (string-to-number (match-string 1 s))) + (sec (string-to-number (match-string 2 s)))) + (+ (* min 60) sec))) + ((stringp s) (string-to-number s)) + (t s))) + +(defun org-time-seconds-to-string (secs) + "Convert a number of seconds to a time string." + (cond ((>= secs 3600) (format-seconds "%h:%.2m:%.2s" secs)) + ((>= secs 60) (format-seconds "%m:%.2s" secs)) + (t (format-seconds "%s" secs)))) + +(defmacro with-time (time-output-p &rest exprs) + "Evaluate an org-table formula, converting all fields that look +like time data to integer seconds. If TIME-OUTPUT-P then return +the result as a time value." + (list + (if time-output-p 'org-time-seconds-to-string 'identity) + (cons 'progn + (mapcar + (lambda (expr) + `,(cons (car expr) + (mapcar + (lambda (el) + (if (listp el) + (list 'with-time nil el) + (org-time-string-to-seconds el))) + (cdr expr)))) + `,@exprs)))) + +(defun org-hex-strip-lead (str) + (if (and (> (length str) 2) (string= (substring str 0 2) "0x")) + (substring str 2) str)) + +(defun org-hex-to-hex (int) + (format "0x%x" int)) + +(defun org-hex-to-dec (str) + (cond + ((and (stringp str) + (string-match "\\([0-9a-f]+\\)" (setf str (org-hex-strip-lead str)))) + (let ((out 0)) + (mapc + (lambda (ch) + (setf out (+ (* out 16) + (if (and (>= ch 48) (<= ch 57)) (- ch 48) (- ch 87))))) + (coerce (match-string 1 str) 'list)) + out)) + ((stringp str) (string-to-number str)) + (t str))) + +(defmacro with-hex (hex-output-p &rest exprs) + "Evaluate an org-table formula, converting all fields that look + like hexadecimal to decimal integers. If HEX-OUTPUT-P then + return the result as a hex value." + (list + (if hex-output-p 'org-hex-to-hex 'identity) + (cons 'progn + (mapcar + (lambda (expr) + `,(cons (car expr) + (mapcar (lambda (el) + (if (listp el) + (list 'with-hex nil el) + (org-hex-to-dec el))) + (cdr expr)))) + `,@exprs)))) + +(require 'mm-url) ; to include mm-url-decode-entities-string + +(defun org-insert-link-with-title () + "Insert org link where default description is set to html title." + (interactive) + (let* ((url (read-string "URL: ")) + (title (get-html-title-from-url url))) + (org-insert-link nil url title))) + +(defun get-html-title-from-url (url) + "Return content in tag." + (let (x1 x2 (download-buffer (url-retrieve-synchronously url))) + (save-excursion + (set-buffer download-buffer) + (beginning-of-buffer) + (setq x1 (search-forward "<title>")) + (search-forward "") + (setq x2 (search-backward "<")) + (mm-url-decode-entities-string (buffer-substring-no-properties x1 x2))))) + +(defun org-remove-empty-propert-drawers () + "*Remove all empty property drawers in current file." + (interactive) + (unless (eq major-mode 'org-mode) + (error "You need to turn on Org mode for this function.")) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward ":PROPERTIES:" nil t) + (save-excursion + (org-remove-empty-drawer-at "PROPERTIES" (match-beginning 0)))))) + +(defun check-for-clock-out-note () + (interactive) + (save-excursion + (org-back-to-heading) + (let ((tags (org-get-tags))) + (and tags (message "tags: %s " tags) + (when (member "clocknote" tags) + (org-add-note)))))) + +(add-hook 'org-clock-out-hook 'check-for-clock-out-note) + +(defun org-list-files (dirs ext) + "Function to create list of org files in multiple subdirectories. +This can be called to generate a list of files for +org-agenda-files or org-refile-targets. + +DIRS is a list of directories. + +EXT is a list of the extensions of files to be included." + (let ((dirs (if (listp dirs) + dirs + (list dirs))) + (ext (if (listp ext) + ext + (list ext))) + files) + (mapc + (lambda (x) + (mapc + (lambda (y) + (setq files + (append files + (file-expand-wildcards + (concat (file-name-as-directory x) "*" y))))) + ext)) + dirs) + (mapc + (lambda (x) + (when (or (string-match "/.#" x) + (string-match "#$" x)) + (setq files (delete x files)))) + files) + files)) + +(defvar org-agenda-directories (list org-directory user-lab-directory) + "List of directories containing org files.") +(defvar org-agenda-extensions '(".org") + "List of extensions of agenda files") + +(defun org-set-agenda-files () + (interactive) + (setq org-agenda-files (org-list-files + org-agenda-directories + org-agenda-extensions))) + +(add-hook 'after-init-hook 'org-set-agenda-files) + +;;; Skel Config +(use-package skel + :requires skel + :load-path user-emacs-lib-directory + :custom + tempo-interactive t + auto-insert 'no-modify + auto-insert-query nil) + +(use-package skt + :requires (skel skt) + :load-path user-emacs-lib-directory + :custom + skt-enable-tempo-elements t + skt-delete-duplicate-marks t + :config + (defvar skt-default-version "0.1.0") + (keymap-set skt-minor-mode-map "b" #'tempo-backward-mark) + (keymap-set skt-minor-mode-map "f" #'tempo-forward-mark) + (keymap-set skt-minor-mode-map "SPC" #'tempo-complete-tag) + (keymap-set skt-minor-mode-map "t" #'skt-add-tag) + + (defvar skt-skeleton-path-function #'abbreviate-file-name + "Function to be called when expanding file-header skeletons. Useful to +rebind locally inside a project or module, where you want to delete some +prefix or replace it.") + + (defun skt-buffer-path (&optional function) + (let ((path (or buffer-file-name (format "%s.lisp" (gensym "scratch-"))))) + (funcall (or function skt-skeleton-path-function) path))) + + (defun skt-skelfile-path () + (if (string= (file-name-nondirectory buffer-file-name) "skelfile") + "skelfile" + (skt-buffer-path))) + + ;; functions + (skt-define-function capture (:abbrev "capture" :tag t) org-capture) + (skt-define-function agenda (:abbrev "agenda" :tag t) org-agenda) + (skt-define-function mjump (:abbrev "mjump" :tag t) bookmark-jump) + (skt-define-function bjump (:abbrev "bjump" :tag t) ibuffer-jump) + (skt-define-function rjump (:abbrev "rjump" :tag t) + (lambda () (jump-to-register (read-char "register: ")))) + (skt-define-function pjump (:abbrev "pjump" :tag t) (lambda () (project-switch-project default-directory))) + + ;; templates + (skt-define-template readme (:mode org-mode :tag t) + "#+title: " (p "title: ") n + "#+description: " (p "description: ") n + "#+author: " user-full-name n + "#+email:" user-mail-address n + "#+setupfile: clean.theme" n + "#+export_file_name: index" n> + p n> n> + ":info:" n> + "+ version :: " skt-default-version n + ":end:" n>) + + (skt-define-template clean.theme (:mode org-mode :tag t) + "#+setupfile: " (join-paths company-cdn-url "org/clean.theme")) + + ;; TODO 2024-06-04: + ;; (skt-define-template defsystem (:mode lisp-mode :tag t :abbrev "defsystem")) + ;; (skt-define-template defpackage (:mode lisp-mode :tag t :abbrev "defpackage")) + ;; (skt-define-template defpkg (:mode lisp-mode :tag t :abbrev "defpkg")) + + (skt-define-template defmacro (:abbrev "(defmacro" :tag t :mode lisp-mode) + "(defmacro " (p "Name: ") " (" (p "Args: ") ")" > n> r ")") + + (skt-define-template defun (:abbrev "(defun" :tag t :mode lisp-mode) + "(defun " (p "Name: ") " (" (p "Args: ") ")" > n> r ")") + + (skt-define-template defvar (:abbrev "(defvar" :tag t :mode lisp-mode) + > "(defvar " > r ")") + + ;; skeletons + (skt-define-skeleton head (:abbrev "head" :mode lisp-mode) + "description: " + ";;; " (skt-buffer-path 'file-name-nondirectory) " --- " str \n \n ";; " _ \n \n ";;; Code:" \n >) + + (skt-define-skeleton head (:abbrev "head" :mode skel-mode) + "description: " + ";;; " (skt-skelfile-path) " --- " str " -*- mode: skel; -*-" \n _) + + (skt-define-skeleton head (:abbrev "head" :mode org-mode) + "title: " + "#+title: " str \n + "#+author: " (skeleton-read "author: ") \n + "#+description: " (skeleton-read "description: ") \n + "#+setupfile: clean.theme" \n > _) + + (skt-define-skeleton head (:abbrev "head" :mode rust-mode) + "description: " + "//! " (skt-buffer-path 'file-name-nondirectory) " --- " str \n \n "// " _ \n \n "//! Code: " \n >) + + (skt-define-skeleton system-head (:abbrev "system-head" :mode lisp-mode) + "system-name: " + ";;; " (skt-buffer-path) " --- " + '(setq v1 (file-name-base (skt-buffer-path))) (capitalize v1) + " Sytem Definitions" \n + > "(defsystem :" v1 \n + > ":depends-on (:std :log)" \n + > ":components ((:file \"pkg\")" _ "))") + + (skt-define-skeleton pkg-head (:abbrev "pkg-head" :mode lisp-mode) + "ignored" + ";;; " (skt-buffer-path 'file-name-nondirectory) " --- " + '(setq v1 (skeleton-read "name: ")) v1 " Package Definitions" \n + > "(defpkg :" v1 \n + > ":use (:std :log))" \n \n + > "(in-package :" v1 ")" \n >) + + (skt-define-skeleton crate-head (:abbrev "crate-head" :mode conf-toml-mode) + "ignored" + "### " (skt-buffer-path 'file-name-nondirectory) " --- " + '(setq v1 (skeleton-read "name: ")) v1 " Cargo Manifest" \n > + "[package]" \n + "name = \"" v1 "\"" \n + "version = \"" skt-default-version "\"" \n + "[dependencies]" \n >) + + (skt-define-skeleton local-vars + (:tag t :abbrev "local-vars" + :docstring "Insert a local variables section. Use current comment syntax if any.") + (completing-read "Mode: " obarray + (lambda (symbol) + (if (commandp symbol) + (string-match "-mode$" (symbol-name symbol)))) + t) + '(save-excursion + (if (re-search-forward page-delimiter nil t) + (error "Not on last page"))) + comment-start "Local Variables:" comment-end \n + comment-start "mode: " str + & -5 | '(kill-line 0) & -1 | comment-end \n + ( (completing-read (format "Variable, %s: " skeleton-subprompt) + obarray + (lambda (symbol) + (or (eq symbol 'eval) + (custom-variable-p symbol))) + t) + comment-start str ": " + (read-from-minibuffer "Expression: " nil read-expression-map nil + 'read-expression-history) | _ + comment-end \n) + resume: + comment-start "End:" comment-end \n) + + ;; autoinsert + (skt-register-auto-insert "skelfile" #'skt-template-skel-head) + (skt-register-auto-insert "readme.org" #'skt-template-org-readme) + (skt-register-auto-insert "Cargo.toml" #'skt-template-conf-toml-crate-head) + (skt-register-auto-insert "pkg.lisp" #'skt-template-lisp-pkg-head) + (skt-register-auto-insert ".*[.]asd" #'skt-template-lisp-system-head) + (skt-register-auto-insert ".*[.]lisp" #'skt-template-lisp-head) + (skt-register-auto-insert ".*[.].rs" #'skt-template-rust-head) + (auto-insert-mode t) + (keymap-set skel-minor-mode-map "C-" 'company-tempo)) (provide 'ellis) ;;; ellis.el ends here diff -r 2d74d85d7031 -r d70be963bfb1 .sbclrc --- a/.sbclrc Wed Jun 05 23:31:48 2024 +0000 +++ b/.sbclrc Sat Jun 15 19:57:38 2024 -0400 @@ -1,16 +1,23 @@ ;;; .sbclrc --- sbcl init file -*- mode: common-lisp; -*- +(in-package :cl-user) (require :asdf) - +(require :sb-concurrency) +(require :sb-cltl2) +(require :sb-rotate-byte) +(require :sb-sprof) (setq *debug-beginner-help-p* nil ;; *print-case* :downcase *print-level* 32 *print-length* 256) +(defvar *quicklisp-setup* (or (probe-file #P"/usr/local/share/lisp/quicklisp/setup.lisp") + (probe-file (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))))) + #-quicklisp -(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" "/usr/local/share/lisp/"))) - (when (probe-file quicklisp-init) - (load quicklisp-init))) +(when *quicklisp-setup* + (load *quicklisp-setup*)) ;; (ql:quickload :clouseau) + (defun include-projects-from (path) "Add PATH to QL;*LOCAL-PROJECT-DIRECTORIES* and ASDF:*CENTRAL-REGISTRY*." (pushnew path ql:*local-project-directories*)