1.1--- a/emacs/ellis.el Mon Nov 06 22:49:52 2023 -0500
1.2+++ b/emacs/ellis.el Tue Nov 07 18:50:41 2023 -0500
1.3@@ -28,10 +28,26 @@
1.4 (enable-paredit-mode)
1.5 (repeat-mode)
1.6
1.7+(keymap-global-set "C-<tab>" #'hippie-expand)
1.8+(keymap-set minibuffer-local-map "C-<tab>" #'hippie-expand)
1.9+
1.10+(require 'sk)
1.11+
1.12+(add-hook 'prog-mode-hook #'skt-mode)
1.13+(add-hook 'org-mode-hook #'skt-mode)
1.14+
1.15+(setopt skt-enable-tempo-elements t
1.16+ skt-completing-read t
1.17+ skt-delete-duplicate-marks t)
1.18+
1.19+(keymap-set skt-mode-map "C-c M-b" #'tempo-backward-mark)
1.20+(keymap-set skt-mode-map "C-c M-f" #'tempo-forward-mark)
1.21+(keymap-set skt-mode-map "C-c M-a" #'tempo-complete-tag)
1.22+
1.23 (use-package notmuch
1.24 :ensure t
1.25 :custom
1.26- notmuch-init-file "~/.notmuch-config"
1.27+ ;; notmuch-init-file "~/.notmuch-config"
1.28 mail-user-agent 'message-user-agent
1.29 smtpmail-smtp-server "smtp.gmail.com"
1.30 message-send-mail-function 'message-smtpmail-send-it
1.31@@ -97,5 +113,20 @@
1.32 (use-package sh-script
1.33 :hook (sh-mode . flymake-mode))
1.34
1.35+(use-package tempo
1.36+ :custom
1.37+ tempo-interactive t
1.38+ :config
1.39+ (tempo-define-template
1.40+ "org:readme"
1.41+ '("#+TITLE: " p n>
1.42+ "#+AUTHOR: " user-full-name " <" user-mail-address ">" n>)
1.43+ "org:readme"
1.44+ "Insert a readme.org file template.")
1.45+ (tempo-define-template "org:src"
1.46+ '("#+begin_src " p n>
1.47+ "#+end_src" n>)
1.48+ "org:src"))
1.49+
1.50 (provide 'ellis)
1.51 ;;; ellis.el ends here
2.1--- a/emacs/keys.el Mon Nov 06 22:49:52 2023 -0500
2.2+++ b/emacs/keys.el Tue Nov 07 18:50:41 2023 -0500
2.3@@ -12,6 +12,7 @@
2.4
2.5 ;;; User keys
2.6 ;; paredit-map
2.7+(require 'default)
2.8 (defvar-keymap parens-map
2.9 :doc "parens-minor-mode keymap."
2.10 :repeat (:enter)
3.1--- a/emacs/lib/sk.el Mon Nov 06 22:49:52 2023 -0500
3.2+++ b/emacs/lib/sk.el Tue Nov 07 18:50:41 2023 -0500
3.3@@ -1,8 +1,8 @@
3.4 ;;; sk.el --- skel Emacs Mode -*- lexical-binding: t; -*-
3.5
3.6-;; skel-mode and friends
3.7+;; skel-mode, skt-mode, sk-classes
3.8
3.9-;; Copyright (C) 2023 anticorp
3.10+;; Copyright (C) 2023 The Compiler Company
3.11
3.12 ;; Author: ellis <ellis@rwest.io>
3.13 ;; Keywords: languages, lisp
3.14@@ -22,19 +22,13 @@
3.15
3.16 ;;; Commentary:
3.17
3.18-;; TODO
3.19-
3.20-;; - auto-insert
3.21-;; - skeleton
3.22-;; - tempo
3.23-;; - d/abbrevs
3.24-
3.25 ;;; Code:
3.26
3.27 (eval-and-compile (require 'eieio)
3.28 (require 'cl-lib)
3.29- (require 'fu (expand-file-name "lisp/fu" user-emacs-directory))
3.30- (require 'sxp (expand-file-name "sxp/sxp" user-dev-directory))
3.31+ (require 'sxp (expand-file-name "sxp.el" (join-paths user-emacs-directory "lib/")))
3.32+ (require 'skeleton)
3.33+ (require 'tempo)
3.34 (defvar skel-debug nil)
3.35 (when skel-debug (require 'ede)))
3.36
3.37@@ -75,7 +69,6 @@
3.38 (define-derived-mode skel-mode lisp-data-mode "SKEL"
3.39 "skel-mode")
3.40
3.41-
3.42 (defun maybe-skel-minor-mode ()
3.43 "Check the current environment and determine if `skel-minor-mode' should
3.44 be enabled. This function is added as a hook to
3.45@@ -142,5 +135,414 @@
3.46 (add-to-list 'auto-mode-alist '("skelfile" . skel-mode))
3.47 (add-to-list 'auto-mode-alist '("\\.sk\\'" . skel-mode)))
3.48
3.49+;;; Autotype
3.50+;; From: https://github.com/xFA25E/skempo/blob/master/skempo.el
3.51+(defun modify-lisp-syntax-tables ()
3.52+ (modify-syntax-entry ?* "w" (syntax-table))
3.53+ (modify-syntax-entry ?- "w" (syntax-table)))
3.54+
3.55+(dolist (hook '(lisp-mode-hook emacs-lisp-mode-hook))
3.56+ (add-hook hook #'modify-lisp-syntax-tables))
3.57+
3.58+(defun skt--tags-variable (mode)
3.59+ "Return a tempo tags variable's symbol for MODE."
3.60+ (when mode
3.61+ (intern (replace-regexp-in-string
3.62+ (rx "-mode" eos) "-skt-tags"
3.63+ (symbol-name mode)))))
3.64+
3.65+(defun skt--remove-tag-list (tag-list)
3.66+ "Remove TAG-LIST from `tempo-local-tags'."
3.67+ (setf (alist-get tag-list tempo-local-tags nil t) nil))
3.68+
3.69+(defun skt--insert-mark (marker)
3.70+ "Insert a MARKER to `tempo-marks' while keeping it sorted.
3.71+Remove duplicate marks from `tempo-marks'. Set to nil removed
3.72+markers. This function is used as an :override advice to
3.73+`tempo-insert-mark', because the original function does not
3.74+remove duplicate elements. Duplicate markers appear when the
3.75+buffer gets smaller, markers start pointing to the same location.
3.76+We don't want that, because a lot of useless markers can slow
3.77+down Emacs."
3.78+ (if (not tempo-marks)
3.79+ (setq tempo-marks (list marker))
3.80+ (let ((markers tempo-marks))
3.81+ (cond
3.82+ ((< marker (car markers))
3.83+ (setq tempo-marks (cons marker tempo-marks)))
3.84+ (t
3.85+ (while (and (cdr markers) (<= (cadr markers) marker))
3.86+ (if (/= (car markers) (cadr markers))
3.87+ (setq markers (cdr markers))
3.88+ (when (markerp (cadr markers)) (set-marker (cadr markers) nil))
3.89+ (setcdr markers (cddr markers))))
3.90+
3.91+ (if (= marker (car markers))
3.92+ (when (markerp marker) (set-marker marker nil))
3.93+ (setcdr markers (cons marker (cdr markers))))))
3.94+
3.95+ (while (cdr markers)
3.96+ (if (/= (car markers) (cadr markers))
3.97+ (setq markers (cdr markers))
3.98+ (when (markerp (cadr markers)) (set-marker (cadr markers) nil))
3.99+ (setcdr markers (cddr markers)))))))
3.100+
3.101+(defun skt--add-tag (tag template &optional tag-list)
3.102+ "Add a TEMPLATE TAG to TAG-LIST or to `tempo-tags'.
3.103+It is an :override function for `tempo-add-tag'. The original
3.104+function does not update identical tags."
3.105+ (interactive "sTag: \nCTemplate: ")
3.106+ (let ((tag-list (or tag-list 'tempo-tags)))
3.107+ (if-let ((value (assoc tag (symbol-value tag-list))))
3.108+ (setcdr value template)
3.109+ (set tag-list (cons (cons tag template) (symbol-value tag-list))))
3.110+ (tempo-invalidate-collection)))
3.111+
3.112+(defun skt--list-derived-modes (mode)
3.113+ "List all derived modes of MODE + MODE itself."
3.114+ (let ((modes nil))
3.115+ (while mode
3.116+ (when-let ((alias (symbol-function mode)))
3.117+ (when (symbolp alias)
3.118+ (setq mode alias)))
3.119+ (push mode modes)
3.120+ (setq mode (get mode 'derived-mode-parent)) )
3.121+ (nreverse modes)))
3.122+
3.123+;;; Commands
3.124+
3.125+(defvar-keymap skt-mode-map
3.126+ :doc "skt-mode keymap."
3.127+ :repeat (:enter))
3.128+
3.129+(define-minor-mode skt-mode
3.130+ "Minor mode for skt-templates."
3.131+ :init-value nil
3.132+ :lighter " Skt"
3.133+ :keymap skt-mode-map
3.134+ (let* ((modes (skt--list-derived-modes major-mode))
3.135+ (tag-vars (mapcar #'skt--tags-variable modes))
3.136+ (bound-tag-vars (cl-delete-if-not #'boundp tag-vars)))
3.137+ (if skt-mode
3.138+ (mapc #'tempo-use-tag-list bound-tag-vars)
3.139+ (mapc #'skt--remove-tag-list bound-tag-vars))))
3.140+
3.141+(defun skt--define-tempo (function-symbol body &optional docstring)
3.142+ "Define a tempo template with BODY.
3.143+This will generate a function with FUNCTION-SYMBOL and
3.144+DOCSTRING.
3.145+
3.146+The main purpose of this function is to have a better controlled
3.147+alternative to `tempo-define-template'."
3.148+ (let ((template-symbol (gensym (symbol-name function-symbol))))
3.149+ (set template-symbol body)
3.150+ (defalias function-symbol
3.151+ (lambda (&optional arg)
3.152+ (interactive "*P")
3.153+ (tempo-insert-template template-symbol (xor tempo-insert-region arg)))
3.154+ docstring)))
3.155+
3.156+(defun skt--define-skeleton (function-symbol body &optional docstring)
3.157+ "Define a skeleton template with BODY.
3.158+This will generate a function with FUNCTION-SYMBOL and
3.159+DOCSTRING.
3.160+
3.161+The main purpose of this function is to have a better controlled
3.162+alternative to `define-skeleton', especially because it is a
3.163+function instead of a macro."
3.164+ (defalias function-symbol
3.165+ (lambda (&optional str arg)
3.166+ (interactive "*P\nP")
3.167+ (skeleton-proxy-new body str arg))
3.168+ docstring))
3.169+
3.170+(defun skt--define-function (function-symbol function &optional docstring)
3.171+ "This will generate an alias to FUNCTION with FUNCTION-SYMBOL.
3.172+DOCSTRING is used as a docstring to FUNCTION-SYMBOL."
3.173+ (defalias function-symbol function docstring))
3.174+
3.175+(defun skt--mode-name (mode)
3.176+ "Get MODE name without a -mode suffix."
3.177+ (string-trim-right (symbol-name mode) (rx "-mode" eos)))
3.178+
3.179+(defun skt--function-name (name modes)
3.180+ "Generate a name for a skt template function.
3.181+NAME and MODES are used to generate unique, but consistent
3.182+names."
3.183+ (concat "skt-template-"
3.184+ (mapconcat (lambda (mode) (concat (skt--mode-name mode) "-"))
3.185+ (sort modes #'string<) "")
3.186+ name))
3.187+
3.188+(defun skt--mode-abbrev-table (mode)
3.189+ "Get abbrev table for MODE or `global-abbrev-table' if nil."
3.190+ (if mode
3.191+ (derived-mode-abbrev-table-name mode)
3.192+ 'global-abbrev-table))
3.193+
3.194+(defun skt--abbrev-table (mode)
3.195+ "Get skt abbrev table for MODE."
3.196+ (intern (concat "skt-" (symbol-name (skt--mode-abbrev-table mode)))))
3.197+
3.198+(defun skt--abbrev-table-names (table)
3.199+ "Return abbrev TABLE names."
3.200+ (let ((names nil))
3.201+ (mapatoms (lambda (abbrev)
3.202+ (when (symbol-value abbrev)
3.203+ (push (symbol-name abbrev) names)))
3.204+ (symbol-value table))
3.205+ names))
3.206+
3.207+(defun skt--modes (mode)
3.208+ "Normalize MODE argument."
3.209+ (cond ((consp mode) mode)
3.210+ ((null mode) nil)
3.211+ ((symbolp mode) (list mode))))
3.212+
3.213+;;;###autoload
3.214+(defun skt-define (define-function name modes tag abbrev docstring body)
3.215+ "Define a skt template.
3.216+
3.217+DEFINE-FUNCTION is a function that takes a function symbol, BODY
3.218+and DOCSTRING as its arguments. It must define a new function
3.219+with that symbol and that docstring.
3.220+
3.221+NAME is a string used in generating a function symbol, TAG and
3.222+ABBREV.
3.223+
3.224+MODES is a list of modes for which TAG and ABBREV will be
3.225+created. If it's nil, TAG and ABBREV will be generated
3.226+globally.
3.227+
3.228+TAG/ABBREV is a boolean, which indicates whether a tag/abbrev
3.229+must be created for this template.
3.230+
3.231+DOCSTRING is a string (or nil) which will be supplied to
3.232+DEFINE-FUNCTION.
3.233+
3.234+BODY is an arbitrary argument passed to DEFINE-FUNCTION."
3.235+ (let* ((function-symbol (intern (skt--function-name name modes)))
3.236+ (modes (or modes '(nil))))
3.237+ (funcall define-function function-symbol body docstring)
3.238+ (put function-symbol 'no-self-insert t)
3.239+
3.240+ (when tag
3.241+ (let ((tag-symbol (gensym (symbol-name function-symbol))))
3.242+ (if (eq #'skt--define-tempo define-function)
3.243+ (set tag-symbol body)
3.244+ (set tag-symbol `((ignore (,function-symbol)))))
3.245+ (dolist (mode modes)
3.246+ (let ((var (skt--tags-variable mode)))
3.247+ (unless (boundp var)
3.248+ (set var nil))
3.249+ (tempo-add-tag name tag-symbol var)))
3.250+ (dolist (buffer (buffer-list))
3.251+ (with-current-buffer buffer
3.252+ (when (and (or (equal '(nil) modes) (apply #'derived-mode-p modes))
3.253+ skt-mode)
3.254+ (skt-mode -1)
3.255+ (skt-mode 1))))))
3.256+
3.257+ (when abbrev
3.258+ (dolist (mode modes)
3.259+ (let ((mode-table (skt--mode-abbrev-table mode))
3.260+ (table (skt--abbrev-table mode)))
3.261+ (define-abbrev-table mode-table nil)
3.262+ (define-abbrev-table table nil :case-fixed t :skt t)
3.263+ (define-abbrev (symbol-value table) name "" function-symbol
3.264+ :case-fixed t :system t :skt t)
3.265+
3.266+ (let* ((names (skt--abbrev-table-names table))
3.267+ (regexp (concat (regexp-opt names "\\_<\\(") " *")))
3.268+ (abbrev-table-put (symbol-value table) :regexp regexp))
3.269+
3.270+ (let ((parents (abbrev-table-get (symbol-value mode-table) :parents)))
3.271+ (cl-pushnew (symbol-value table) parents :test #'eq)
3.272+ (abbrev-table-put (symbol-value mode-table) :parents parents)))))
3.273+
3.274+ function-symbol))
3.275+
3.276+;;;###autoload
3.277+(cl-defmacro skt-define-tempo (name (&key mode tag abbrev docstring) &rest body)
3.278+ "Define a tempo template.
3.279+This macro defines a new tempo template or updates the old one.
3.280+NAME is a symbol. ARGS is a list of the form ([KEY VALUE]...)
3.281+where each KEY can be one of :tag, :abbrev, :docstring or :mode.
3.282+
3.283+If KEY is :tag, VALUE should be a boolean. If VALUE is non-nil,
3.284+then a tempo tag with NAME will be created for this template.
3.285+
3.286+If KEY is :abbrev, VALUE should be a boolean. If VALUE is
3.287+non-nil, then a NAME abbrev will be created for this template.
3.288+
3.289+If KEY is :docstring, VALUE should be a string. It will be a
3.290+docstring of the generated function.
3.291+
3.292+If KEY is :mode, VALUE should be a list of modes or single mode.
3.293+If this option is provided, than a tempo tag and an abbrev will
3.294+be created for these modes, otherwise they will be global (if
3.295+:tag and :abbrev options were provided, of course).
3.296+
3.297+BODY is a sequence of tempo elements that will be passed as a
3.298+list directly to `tempo-define-template's second argument.
3.299+
3.300+Example:
3.301+\(skt-define-tempo defvar (:mode `emacs-lisp-mode' :tag t :abbrev t
3.302+ :docstring \"defvar template\")
3.303+ \"(defvar \" (string-trim-right (buffer-name) (rx \".el\" eos)) \"-\" p n>
3.304+ r> \")\")"
3.305+ `(skt-define #'skt--define-tempo ,(symbol-name name)
3.306+ ',(skt--modes mode) ,tag ,abbrev ,docstring ',body))
3.307+
3.308+;;;###autoload
3.309+(cl-defmacro skt-define-skeleton (name (&key mode tag abbrev docstring) &rest body)
3.310+ "Define skeleton template.
3.311+See `skt-define-tempo' for explanation of NAME, MODE, TAG,
3.312+ABBREV and DOCSTRING.
3.313+
3.314+BODY is a sequence of skeleton elements that will be passed
3.315+directly to `define-skeleton'.
3.316+
3.317+Example:
3.318+\(skt-define-skeleton defun (:mode (emacs-lisp-mode `lisp-interaction-mode')
3.319+ :tag t :abbrev t
3.320+ :docstring \"defun template\")
3.321+ \"(defun \" str \" (\" @ - \")\" \n
3.322+ @ _ \")\" \n)"
3.323+ `(skt-define #'skt--define-skeleton ,(symbol-name name)
3.324+ ',(skt--modes mode) ,tag ,abbrev ,docstring ',body))
3.325+
3.326+;;;###autoload
3.327+(cl-defmacro skt-define-function (name (&key mode tag abbrev docstring) function)
3.328+ "Define FUNCTION template.
3.329+See `skt-define-tempo' for explanation of NAME, MODE, TAG,
3.330+ABBREV and DOCSTRING.
3.331+
3.332+The main purpose of this macro, is to create tempo tags and
3.333+abbrevs for existing skeleton templates, such as `sh-case'.
3.334+
3.335+Example:
3.336+\(skt-define-function shcase (:tag t :abbrev t :mode `sh-mode') `sh-case')"
3.337+ `(skt-define #'skt--define-function ,(symbol-name name)
3.338+ ',(skt--modes mode) ,tag ,abbrev ,docstring ',function))
3.339+
3.340+(defun skt--complete-template (string tag-list)
3.341+ "An :override advice function for `tempo-display-completions'.
3.342+Show completion for STRING in a TAG-LIST. After selection
3.343+expand template.
3.344+
3.345+Rewritten because the original function uses an old way of
3.346+displaying completions in a separate buffer, which is not
3.347+clickable anyway. Now it uses new (compared to the originial
3.348+tempo package) and shiny `completing-read' interface."
3.349+ (let* ((tags (mapcar #'car tag-list))
3.350+ (tag (completing-read "Skt: " tags nil t string)))
3.351+ (delete-char (- (length string)))
3.352+ (tempo-insert-template (cdr (assoc tag tag-list)) nil)))
3.353+
3.354+;;;###autoload
3.355+(defcustom skt-enable-tempo-elements nil
3.356+ "Enable extra tempo elements.
3.357+These elements add conditionals and looping support for tempo
3.358+like those in skeleton, making skeleton pretty much obsolete.
3.359+
3.360+If you want to set this option from ELisp, you have to remove
3.361+`skt-tempo-user-elements' from `tempo-user-elements' on nil
3.362+and add it on non-nil."
3.363+ :type '(boolean :tag "Enable tempo elements?")
3.364+ :set (lambda (variable value)
3.365+ (if value
3.366+ (add-hook 'tempo-user-elements #'skt-tempo-user-elements)
3.367+ (remove-hook 'tempo-user-elements #'skt-tempo-user-elements))
3.368+ (set-default variable value))
3.369+ :group 'skel)
3.370+
3.371+(defcustom skt-completing-read nil
3.372+ "Override default `tempo-display-completions'.
3.373+By default it uses a completion buffer to show completions. This
3.374+option overrides this function to use `completing-read' to select
3.375+partial skempo tag or complete tag on region.
3.376+
3.377+If you wish to set this variable from ELisp code, you have to
3.378+remove `skt--complete-template' advice from
3.379+`tempo-display-completions' on nil and add it as on :override
3.380+advice on non-nil."
3.381+ :type '(boolean :tag "Override?")
3.382+ :set (lambda (variable value)
3.383+ (if value
3.384+ (advice-add 'tempo-display-completions :override #'skt--complete-template)
3.385+ (advice-remove 'tempo-display-completions #'skt--complete-template))
3.386+ (set-default variable value))
3.387+ :group 'skel)
3.388+
3.389+(defcustom skempo-delete-duplicate-marks nil
3.390+ "Override default `tempo-insert-mark'.
3.391+Marks are used to jump on points of interest in a template. By
3.392+default `tempo-insert-mark' does not remove duplicate marks.
3.393+Duplicate marks might appear when the buffer shrinks and some of
3.394+the marks start pointing to the same location. This option tries
3.395+to fix this by checking for duplicate marks every time the
3.396+function is called. Emacs might get slower with a lot of
3.397+marks.
3.398+
3.399+If you want to set this option from ELisp, you have to remove
3.400+`skt--insert-mark' advice from `tempo-insert-mark' on nil and
3.401+add it as on :override advice on non-nil."
3.402+ :type '(boolean :tag "Override?")
3.403+ :set (lambda (variable value)
3.404+ (if value
3.405+ (advice-add 'tempo-insert-mark :override #'skt--insert-mark)
3.406+ (advice-remove 'tempo-insert-mark #'skt--insert-mark))
3.407+ (set-default variable value))
3.408+ :group 'skel)
3.409+
3.410+(progn
3.411+ (put 'skt-define-tempo 'lisp-indent-function 2)
3.412+ (put 'skt-define-skeleton 'lisp-indent-function 2)
3.413+ (put 'skt-define-function 'lisp-indent-function 2))
3.414+
3.415+;;; Tempo Elements
3.416+(defvar skt-tempo-else-key (kbd "C-M-g")
3.417+ "Key used to execute else branch in tempo conditional.")
3.418+
3.419+(defun skt-tempo--prompt (prompt)
3.420+ "Make prompt for tempo conditional.
3.421+PROMPT is preceded with `skt-tempo-else-key'."
3.422+ (concat "(" (key-description skt-tempo-else-key) " to quit) " prompt))
3.423+
3.424+(defun skt-tempo-user-elements (element)
3.425+ "Support for conditional and looping tempo elements.
3.426+The following forms are supported for ELEMENT:
3.427+
3.428+\(:if (PROMPT VAR) THEN ELSE)
3.429+
3.430+\(:when (PROMPT VAR) BODY...)
3.431+
3.432+\(:while (PROMPT VAR) BODY...)
3.433+
3.434+PROMPT is a string used to read value for VAR. VAR is a tempo
3.435+variable symbol. Its value can be read with s, as usual. BODY,
3.436+THEN and ELSE are tempo elements. To abort the execution of
3.437+these elements, user must press `skt-tempo-else-key'.
3.438+
3.439+The main purpose of this extension is to mimic skeleton
3.440+conditionals and iterative templats. Skeleton becomes pretty
3.441+much obsolete with this extension."
3.442+ (pcase element
3.443+ (`(:if (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) ,then ,else)
3.444+ (let ((prompt (skt-tempo--prompt prompt))
3.445+ (map (make-sparse-keymap)))
3.446+ (set-keymap-parent map minibuffer-local-map)
3.447+ (define-key map skt-tempo-else-key
3.448+ (lambda () (interactive) (throw 'else else)))
3.449+ (catch 'else
3.450+ (tempo-save-named var (read-from-minibuffer prompt nil map))
3.451+ then)))
3.452+ (`(:when (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) . ,body)
3.453+ `(:if (,prompt ,var) (l ,@body) (l)))
3.454+ (`(:while (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) . ,body)
3.455+ `(:when (,prompt ,var) ,@body ,element))))
3.456+
3.457 (provide 'skel)
3.458-;;; skel.el ends here
3.459+(provide 'sk)
3.460+;;; sk.el ends here