changelog shortlog graph tags branches files raw help

Mercurial > core / changeset: break out templates from sk.el to skt.el

changeset 421: 05e9cbc641dd
parent 420: 131a2a37f8d5
child 422: 3e0a434b0c56
author: Richard Westhaver <ellis@rwest.io>
date: Thu, 06 Jun 2024 17:53:22 -0400
files: emacs/default.el emacs/lib/sk.el emacs/lib/skt.el
description: break out templates from sk.el to skt.el
     1.1--- a/emacs/default.el	Thu Jun 06 17:25:21 2024 -0400
     1.2+++ b/emacs/default.el	Thu Jun 06 17:53:22 2024 -0400
     1.3@@ -918,5 +918,10 @@
     1.4       (org-agenda-remove-restriction-lock t)
     1.5       (message nil))))
     1.6 
     1.7+;;; Skel
     1.8+(add-to-load-path user-emacs-lib-directory)
     1.9+(require 'sk)
    1.10+(require 'skt)
    1.11+
    1.12 (provide 'default)
    1.13 ;; default.el ends here
     2.1--- a/emacs/lib/sk.el	Thu Jun 06 17:25:21 2024 -0400
     2.2+++ b/emacs/lib/sk.el	Thu Jun 06 17:53:22 2024 -0400
     2.3@@ -22,8 +22,9 @@
     2.4 
     2.5 ;;; Commentary:
     2.6 
     2.7+;;
     2.8+
     2.9 ;;; Code:
    2.10-
    2.11 (eval-and-compile (require 'eieio)
    2.12 		  (require 'cl-lib)
    2.13 		  (require 'sxp (expand-file-name "sxp.el" (join-paths user-emacs-directory "lib/")))
    2.14@@ -71,8 +72,7 @@
    2.15   :lighter " Sk"
    2.16   :group 'skel
    2.17   :keymap skel-minor-mode-map
    2.18-  :version skel-version
    2.19-  (keymap-local-set skel-minor-mode-map-prefix skt-minor-mode-map))
    2.20+  :version skel-version)
    2.21 
    2.22 ;; TODO 2023-09-06: 
    2.23 (define-derived-mode skel-mode lisp-mode "SKEL"
    2.24@@ -147,418 +147,6 @@
    2.25   (add-to-list 'auto-mode-alist '("skelfile" . skel-mode))
    2.26   (add-to-list 'auto-mode-alist '("\\.sk\\'" . skel-mode)))
    2.27 
    2.28-;;; Autotype
    2.29-;; ref: https://raw.githubusercontent.com/xFA25E/skempo/master/skempo.el
    2.30-
    2.31-;; (defun modify-lisp-syntax-tables ()
    2.32-;;   (modify-syntax-entry ?* "w" (syntax-table))
    2.33-;;   (modify-syntax-entry ?- "w" (syntax-table)))
    2.34-;; (dolist (hook '(lisp-mode-hook emacs-lisp-mode-hook))
    2.35-;;   (add-hook hook #'modify-lisp-syntax-tables))
    2.36-
    2.37-(defun skt--tags-variable (mode)
    2.38-  "Return a tempo tags variable's symbol for MODE."
    2.39-  (when mode
    2.40-    (intern (replace-regexp-in-string
    2.41-             (rx "-mode" eos) "-skt-tags"
    2.42-             (symbol-name mode)))))
    2.43-
    2.44-(defun skt--remove-tag-list (tag-list)
    2.45-  "Remove TAG-LIST from `tempo-local-tags'."
    2.46-  (setf (alist-get tag-list tempo-local-tags nil t) nil))
    2.47-
    2.48-(defun skt--insert-mark (marker)
    2.49-  "Insert a MARKER to `tempo-marks' while keeping it sorted.
    2.50-Remove duplicate marks from `tempo-marks'.  Set to nil removed
    2.51-markers.  This function is used as an :override advice to
    2.52-`tempo-insert-mark', because the original function does not
    2.53-remove duplicate elements.  Duplicate markers appear when the
    2.54-buffer gets smaller, markers start pointing to the same location.
    2.55-We don't want that, because a lot of useless markers can slow
    2.56-down Emacs."
    2.57-  (if (not tempo-marks)
    2.58-      (setq tempo-marks (list marker))
    2.59-    (let ((markers tempo-marks))
    2.60-      (cond
    2.61-       ((< marker (car markers))
    2.62-        (setq tempo-marks (cons marker tempo-marks)))
    2.63-       (t
    2.64-        (while (and (cdr markers) (<= (cadr markers) marker))
    2.65-          (if (/= (car markers) (cadr markers))
    2.66-              (setq markers (cdr markers))
    2.67-            (when (markerp (cadr markers)) (set-marker (cadr markers) nil))
    2.68-            (setcdr markers (cddr markers))))
    2.69-
    2.70-        (if (= marker (car markers))
    2.71-            (when (markerp marker) (set-marker marker nil))
    2.72-          (setcdr markers (cons marker (cdr markers))))))
    2.73-
    2.74-      (while (cdr markers)
    2.75-        (if (/= (car markers) (cadr markers))
    2.76-            (setq markers (cdr markers))
    2.77-          (when (markerp (cadr markers)) (set-marker (cadr markers) nil))
    2.78-          (setcdr markers (cddr markers)))))))
    2.79-
    2.80-(defun skt-add-tag (tag template &optional tag-list)
    2.81-  "Add a TEMPLATE TAG to TAG-LIST or to `tempo-tags'.
    2.82-It is an :override function for `tempo-add-tag'.  The original
    2.83-function does not update identical tags."
    2.84-  (interactive "sTag: \nCTemplate: ")
    2.85-  (let ((tag-list (or tag-list 'tempo-tags)))
    2.86-    (if-let ((value (assoc tag (symbol-value tag-list))))
    2.87-        (setcdr value template)
    2.88-      (set tag-list (cons (cons tag template) (symbol-value tag-list))))
    2.89-    (tempo-invalidate-collection)))
    2.90-
    2.91-(defun skt--list-derived-modes (mode)
    2.92-  "List all derived modes of MODE + MODE itself."
    2.93-  (let ((modes nil))
    2.94-    (while mode
    2.95-      (when-let ((alias (symbol-function mode)))
    2.96-        (when (symbolp alias)
    2.97-          (setq mode alias)))
    2.98-      (push mode modes)
    2.99-      (setq mode (get mode 'derived-mode-parent))  )
   2.100-    (nreverse modes)))
   2.101-
   2.102-;;; Commands
   2.103-(defvar-keymap skt-minor-mode-map
   2.104-  :doc "skt-minor-mode keymap."
   2.105-  :repeat (:enter)
   2.106-  :prefix 'skt-minor-mode-map
   2.107-  "a" #'edit-abbrevs)
   2.108-
   2.109-(define-minor-mode skt-minor-mode
   2.110-  "Minor mode for skt-templates."
   2.111-  :init-value nil
   2.112-  :lighter " Skt"
   2.113-  :keymap skt-minor-mode-map
   2.114-  (let* ((modes (skt--list-derived-modes major-mode))
   2.115-         (tag-vars (mapcar #'skt--tags-variable modes))
   2.116-         (bound-tag-vars (cl-delete-if-not #'boundp tag-vars)))
   2.117-    (if skt-minor-mode
   2.118-        (mapc #'tempo-use-tag-list bound-tag-vars)
   2.119-      (mapc #'skt--remove-tag-list bound-tag-vars))))
   2.120-
   2.121-(defun skt-register-auto-insert (rx template)
   2.122-  "Associate a template with a file regexp and insert into `auto-insert-alist'."
   2.123-  (cl-pushnew (cons rx template) auto-insert-alist))
   2.124-
   2.125-(defun skt--define-template (function-symbol body &optional docstring)
   2.126-  "Define a tempo template with BODY.
   2.127-This will generate a function with FUNCTION-SYMBOL and
   2.128-DOCSTRING.
   2.129-
   2.130-The main purpose of this function is to have a better controlled
   2.131-alternative to `tempo-define-template'."
   2.132-  (let ((template-symbol (gensym (symbol-name function-symbol))))
   2.133-    (set template-symbol body)
   2.134-    (defalias function-symbol
   2.135-      (lambda (&optional arg)
   2.136-        (interactive "*P")
   2.137-        (tempo-insert-template template-symbol (xor tempo-insert-region arg)))
   2.138-      docstring)))
   2.139-
   2.140-(defun skt--define-skeleton (function-symbol body &optional docstring)
   2.141-  "Define a skeleton template with BODY.
   2.142-This will generate a function with FUNCTION-SYMBOL and
   2.143-DOCSTRING.
   2.144-
   2.145-The main purpose of this function is to have a better controlled
   2.146-alternative to `define-skeleton', especially because it is a
   2.147-function instead of a macro."
   2.148-  (defalias function-symbol
   2.149-    (lambda (&optional str arg)
   2.150-      (interactive "*P\nP")
   2.151-      (skeleton-proxy-new body str arg))
   2.152-    docstring))
   2.153-
   2.154-(defun skt--define-function (function-symbol function &optional docstring)
   2.155-  "This will generate an alias to FUNCTION with FUNCTION-SYMBOL.
   2.156-DOCSTRING is used as a docstring to FUNCTION-SYMBOL."
   2.157-  (defalias function-symbol function docstring))
   2.158-
   2.159-(defun skt--mode-name (mode)
   2.160-  "Get MODE name without a -mode suffix."
   2.161-  (string-trim-right (symbol-name mode) (rx "-mode" eos)))
   2.162-
   2.163-(defun skt--function-name (name modes)
   2.164-  "Generate a name for a skt template function.
   2.165-NAME and MODES are used to generate unique, but consistent
   2.166-names."
   2.167-  (concat "skt-template-"
   2.168-          (mapconcat (lambda (mode) (concat (skt--mode-name mode) "-"))
   2.169-                     (sort modes #'string<) "")
   2.170-          name))
   2.171-
   2.172-(defun skt--mode-abbrev-table (mode)
   2.173-  "Get abbrev table for MODE or `global-abbrev-table' if nil."
   2.174-  (if mode
   2.175-      (derived-mode-abbrev-table-name mode)
   2.176-    'global-abbrev-table))
   2.177-
   2.178-(defun skt--abbrev-table (mode)
   2.179-  "Get skt abbrev table for MODE."
   2.180-  (intern (concat "skt-" (symbol-name (skt--mode-abbrev-table mode)))))
   2.181-
   2.182-(defun skt--abbrev-table-names (table)
   2.183-  "Return abbrev TABLE names."
   2.184-  (let ((names nil))
   2.185-    (mapatoms (lambda (abbrev)
   2.186-                (when (symbol-value abbrev)
   2.187-                  (push (symbol-name abbrev) names)))
   2.188-              (symbol-value table))
   2.189-    names))
   2.190-
   2.191-(defun skt--modes (mode)
   2.192-  "Normalize MODE argument."
   2.193-  (cond ((consp mode) mode)
   2.194-        ((null mode) nil)
   2.195-        ((symbolp mode) (list mode))))
   2.196-
   2.197-;;;###autoload
   2.198-(defun skt--define (define-function name modes tag abbrev docstring body)
   2.199-  "Define a skt template.
   2.200-
   2.201-DEFINE-FUNCTION is a function that takes a function symbol, BODY
   2.202-and DOCSTRING as its arguments.  It must define a new function
   2.203-with that symbol and that docstring.
   2.204-
   2.205-NAME is a string used in generating a function symbol, TAG and
   2.206-ABBREV.
   2.207-
   2.208-MODES is a list of modes for which TAG and ABBREV will be
   2.209-created.  If it's nil, TAG and ABBREV will be generated
   2.210-globally.
   2.211-
   2.212-TAG/ABBREV is a boolean, which indicates whether a tag/abbrev
   2.213-must be created for this template.
   2.214-
   2.215-DOCSTRING is a string (or nil) which will be supplied to
   2.216-DEFINE-FUNCTION.
   2.217-
   2.218-BODY is an arbitrary argument passed to DEFINE-FUNCTION."
   2.219-  (let* ((function-symbol (intern (skt--function-name name modes)))
   2.220-         (modes (or modes '(nil))))
   2.221-    (funcall define-function function-symbol body docstring)
   2.222-    (put function-symbol 'no-self-insert t)
   2.223-
   2.224-    (when tag
   2.225-      (let ((tag-symbol (gensym (symbol-name function-symbol))))
   2.226-        (if (eq #'skt--define-template define-function)
   2.227-            (set tag-symbol body)
   2.228-          (set tag-symbol `((ignore (,function-symbol)))))
   2.229-        (dolist (mode modes)
   2.230-          (let ((var (skt--tags-variable mode)))
   2.231-            (unless (boundp var)
   2.232-              (set var nil))
   2.233-            (tempo-add-tag name tag-symbol var)))
   2.234-        (dolist (buffer (buffer-list))
   2.235-          (with-current-buffer buffer
   2.236-            (when (and (or (equal '(nil) modes) (apply #'derived-mode-p modes))
   2.237-                       skt-minor-mode)
   2.238-              (skt-minor-mode -1)
   2.239-              (skt-minor-mode 1))))))
   2.240-
   2.241-    (when abbrev
   2.242-      (dolist (mode modes)
   2.243-        (let ((mode-table (skt--mode-abbrev-table mode))
   2.244-              (table (skt--abbrev-table mode)))
   2.245-          (define-abbrev-table mode-table nil)
   2.246-          (define-abbrev-table table nil :case-fixed t :skt t)
   2.247-          (define-abbrev (symbol-value table) name "" function-symbol
   2.248-            :case-fixed t :system t :skt t)
   2.249-
   2.250-          (let* ((names (skt--abbrev-table-names table))
   2.251-                 (regexp (concat (regexp-opt names "\\_<\\(") " *")))
   2.252-            (abbrev-table-put (symbol-value table) :regexp regexp))
   2.253-
   2.254-          (let ((parents (abbrev-table-get (symbol-value mode-table) :parents)))
   2.255-            (cl-pushnew (symbol-value table) parents :test #'eq)
   2.256-            (abbrev-table-put (symbol-value mode-table) :parents parents)))))
   2.257-
   2.258-    function-symbol))
   2.259-
   2.260-;;;###autoload
   2.261-(cl-defmacro skt-define-template (name (&key mode tag abbrev docstring) &body body)
   2.262-  "Define a tempo template.
   2.263-This macro defines a new tempo template or updates the old one.
   2.264-NAME is a symbol.  ARGS is a list of the form ([KEY VALUE]...)
   2.265-where each KEY can be one of :tag, :abbrev, :docstring or :mode.
   2.266-
   2.267-If KEY is :tag, VALUE should be a boolean.  If VALUE is non-nil,
   2.268-then a tempo tag with NAME will be created for this template.
   2.269-
   2.270-If KEY is :abbrev, VALUE should be a boolean.  If VALUE is
   2.271-non-nil, then a NAME abbrev will be created for this template.
   2.272-
   2.273-If KEY is :docstring, VALUE should be a string.  It will be a
   2.274-docstring of the generated function.
   2.275-
   2.276-If KEY is :mode, VALUE should be a list of modes or single mode.
   2.277-If this option is provided, than a tempo tag and an abbrev will
   2.278-be created for these modes, otherwise they will be global (if
   2.279-:tag and :abbrev options were provided, of course).
   2.280-
   2.281-BODY is a sequence of tempo elements that will be passed as a
   2.282-list directly to `tempo-define-template's second argument.
   2.283-
   2.284-Example:
   2.285-\(skt-define-template defvar (:mode emacs-lisp-mode :tag t :abbrev t
   2.286-                             :docstring \"defvar template\")
   2.287-  \"(defvar \" (string-trim-right (buffer-name) (rx \".el\" eos)) \"-\" p n>
   2.288-  r> \")\")"
   2.289-  `(skt--define 'skt--define-template ,(symbol-name name)
   2.290-               ',(skt--modes mode) ,tag ,abbrev ,docstring ',body))
   2.291-
   2.292-;;;###autoload
   2.293-(cl-defmacro skt-define-skeleton (name (&key mode tag abbrev docstring) &rest body)
   2.294-  "Define skeleton template.
   2.295-See `skt-define-template' for explanation of NAME, MODE, TAG,
   2.296-ABBREV and DOCSTRING.
   2.297-
   2.298-BODY is a sequence of skeleton elements that will be passed
   2.299-directly to `define-skeleton'.
   2.300-
   2.301-Example:
   2.302-\(skt-define-skeleton defun (:mode (emacs-lisp-mode lisp-interaction-mode)
   2.303-                               :tag t :abbrev t
   2.304-                               :docstring \"defun template\")
   2.305-  \"(defun \" str \" (\" @ - \")\" \n
   2.306-  @ _ \")\" \n)"
   2.307-  `(skt--define #'skt--define-skeleton ,(symbol-name name)
   2.308-                  ',(skt--modes mode) ,tag ,abbrev ,docstring ',body))
   2.309-
   2.310-;;;###autoload
   2.311-(cl-defmacro skt-define-function (name (&key mode tag abbrev docstring) function)
   2.312-  "Define FUNCTION template.
   2.313-See `skt-define-template' for explanation of NAME, MODE, TAG,
   2.314-ABBREV and DOCSTRING.
   2.315-
   2.316-The main purpose of this macro, is to create tempo tags and
   2.317-abbrevs for existing skeleton templates, such as `sh-case'.
   2.318-
   2.319-Example:
   2.320-\(skt-define-function shcase (:tag t :abbrev t :mode `sh-mode') `sh-case')"
   2.321-  `(skt--define #'skt--define-function ,(symbol-name name)
   2.322-                  ',(skt--modes mode) ,tag ,abbrev ,docstring ',function))
   2.323-
   2.324-(defun skt--complete-template (string tag-list)
   2.325-  "An :override advice function for `tempo-display-completions'.
   2.326-Show completion for STRING in a TAG-LIST.  After selection
   2.327-expand template.
   2.328-
   2.329-Rewritten because the original function uses an old way of
   2.330-displaying completions in a separate buffer, which is not
   2.331-clickable anyway.  Now it uses new (compared to the originial
   2.332-tempo package) and shiny `completing-read' interface."
   2.333-  (let* ((tags (mapcar #'car tag-list))
   2.334-         (tag (completing-read "Skt: " tags nil t string)))
   2.335-    (delete-char (- (length string)))
   2.336-    (tempo-insert-template (cdr (assoc tag tag-list)) nil)))
   2.337-
   2.338-;;;###autoload
   2.339-(defcustom skt-enable-tempo-elements nil
   2.340-  "Enable extra tempo elements.
   2.341-These elements add conditionals and looping support for tempo
   2.342-like those in skeleton, making skeleton pretty much obsolete.
   2.343-
   2.344-If you want to set this option from ELisp, you have to remove
   2.345-`skt-tempo-user-elements' from `tempo-user-elements' on nil
   2.346-and add it on non-nil."
   2.347-  :type '(boolean :tag "Enable tempo elements?")
   2.348-  :set (lambda (variable value)
   2.349-         (if value
   2.350-             (add-hook 'tempo-user-functions #'skt-tempo-user-elements)
   2.351-           (remove-hook 'tempo-user-functions #'skt-tempo-user-elements))
   2.352-         (set-default variable value))
   2.353-  :group 'skel)
   2.354-
   2.355-(defcustom skt-completing-read nil
   2.356-  "Override default `tempo-display-completions'.
   2.357-By default it uses a completion buffer to show completions.  This
   2.358-option overrides this function to use `completing-read' to select
   2.359-partial skt tag or complete tag on region.
   2.360-
   2.361-If you wish to set this variable from ELisp code, you have to
   2.362-remove `skt--complete-template' advice from
   2.363-`tempo-display-completions' on nil and add it as on :override
   2.364-advice on non-nil."
   2.365-  :type '(boolean :tag "Override?")
   2.366-  :set (lambda (variable value)
   2.367-         (if value
   2.368-             (advice-add 'tempo-display-completions :override #'skt--complete-template)
   2.369-           (advice-remove 'tempo-display-completions #'skt--complete-template))
   2.370-         (set-default variable value))
   2.371-  :group 'skel)
   2.372-
   2.373-(defcustom skt-delete-duplicate-marks nil
   2.374-  "Override default `tempo-insert-mark'.
   2.375-Marks are used to jump on points of interest in a template.  By
   2.376-default `tempo-insert-mark' does not remove duplicate marks.
   2.377-Duplicate marks might appear when the buffer shrinks and some of
   2.378-the marks start pointing to the same location.  This option tries
   2.379-to fix this by checking for duplicate marks every time the
   2.380-function is called.  Emacs might get slower with a lot of
   2.381-marks.
   2.382-
   2.383-If you want to set this option from ELisp, you have to remove
   2.384-`skt--insert-mark' advice from `tempo-insert-mark' on nil and
   2.385-add it as on :override advice on non-nil."
   2.386-  :type '(boolean :tag "Override?")
   2.387-  :set (lambda (variable value)
   2.388-         (if value
   2.389-             (advice-add 'tempo-insert-mark :override #'skt--insert-mark)
   2.390-           (advice-remove 'tempo-insert-mark #'skt--insert-mark))
   2.391-         (set-default variable value))
   2.392-  :group 'skel)
   2.393-
   2.394-(progn
   2.395-  (put 'tempo-define-template 'lisp-indent-function 1)
   2.396-  (put 'skt-define-template 'lisp-indent-function 2)
   2.397-  (put 'skt-define-skeleton 'lisp-indent-function 2)
   2.398-  (put 'skt-define-function 'lisp-indent-function 2))
   2.399-
   2.400-;;; Tempo Elements
   2.401-(defvar skt-tempo-else-key (kbd "C-M-g")
   2.402-  "Key used to execute else branch in tempo conditional.")
   2.403-
   2.404-(defun skt-tempo--prompt (prompt)
   2.405-  "Make prompt for tempo conditional.
   2.406-PROMPT is preceded with `skt-tempo-else-key'."
   2.407-  (concat "(" (key-description skt-tempo-else-key) " to quit) " prompt))
   2.408-
   2.409-(defun skt-tempo-user-elements (element)
   2.410-  "Support for conditional and looping tempo elements.
   2.411-The following forms are supported for ELEMENT:
   2.412-
   2.413-\(:if (PROMPT VAR) THEN ELSE)
   2.414-
   2.415-\(:when (PROMPT VAR) BODY...)
   2.416-
   2.417-\(:while (PROMPT VAR) BODY...)
   2.418-
   2.419-PROMPT is a string used to read value for VAR.  VAR is a tempo
   2.420-variable symbol.  Its value can be read with s, as usual.  BODY,
   2.421-THEN and ELSE are tempo elements.  To abort the execution of
   2.422-these elements, user must press `skt-tempo-else-key'.
   2.423-
   2.424-The main purpose of this extension is to mimic skeleton conditionals."
   2.425-  (pcase element
   2.426-    (`(:if (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) ,then ,else)
   2.427-     (let ((prompt (skt-tempo--prompt prompt))
   2.428-           (map (make-sparse-keymap)))
   2.429-       (set-keymap-parent map minibuffer-local-map)
   2.430-       (define-key map skt-tempo-else-key
   2.431-         (lambda () (interactive) (throw 'else else)))
   2.432-       (catch 'else
   2.433-         (tempo-save-named var (read-from-minibuffer prompt nil map))
   2.434-         then)))
   2.435-    (`(:when (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) . ,body)
   2.436-     `(:if (,prompt ,var) (l ,@body) (l)))
   2.437-    (`(:while (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) . ,body)
   2.438-     `(:when (,prompt ,var) ,@body ,element))))
   2.439-
   2.440 (provide 'skel)
   2.441 (provide 'sk)
   2.442 ;;; sk.el ends here
     3.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2+++ b/emacs/lib/skt.el	Thu Jun 06 17:53:22 2024 -0400
     3.3@@ -0,0 +1,442 @@
     3.4+;;; skt.el --- Skel Templates -*- lexical-binding: t; -*-
     3.5+
     3.6+;; Copyright (C) 2024  The Compiler Company
     3.7+
     3.8+;; Author: Richard Westhaver <richard.westhaver@gmail.com>
     3.9+;; Keywords: convenience
    3.10+
    3.11+;; This program is free software; you can redistribute it and/or modify
    3.12+;; it under the terms of the GNU General Public License as published by
    3.13+;; the Free Software Foundation, either version 3 of the License, or
    3.14+;; (at your option) any later version.
    3.15+
    3.16+;; This program is distributed in the hope that it will be useful,
    3.17+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.18+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.19+;; GNU General Public License for more details.
    3.20+
    3.21+;; You should have received a copy of the GNU General Public License
    3.22+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
    3.23+
    3.24+;;; Commentary:
    3.25+
    3.26+;; 
    3.27+
    3.28+;;; Code:
    3.29+(require 'skel)
    3.30+
    3.31+;; ref: https://raw.githubusercontent.com/xFA25E/skempo/master/skempo.el
    3.32+
    3.33+;; (defun modify-lisp-syntax-tables ()
    3.34+;;   (modify-syntax-entry ?* "w" (syntax-table))
    3.35+;;   (modify-syntax-entry ?- "w" (syntax-table)))
    3.36+;; (dolist (hook '(lisp-mode-hook emacs-lisp-mode-hook))
    3.37+;;   (add-hook hook #'modify-lisp-syntax-tables))
    3.38+
    3.39+(defun skt--tags-variable (mode)
    3.40+  "Return a tempo tags variable's symbol for MODE."
    3.41+  (when mode
    3.42+    (intern (replace-regexp-in-string
    3.43+             (rx "-mode" eos) "-skt-tags"
    3.44+             (symbol-name mode)))))
    3.45+
    3.46+(defun skt--remove-tag-list (tag-list)
    3.47+  "Remove TAG-LIST from `tempo-local-tags'."
    3.48+  (setf (alist-get tag-list tempo-local-tags nil t) nil))
    3.49+
    3.50+(defun skt--insert-mark (marker)
    3.51+  "Insert a MARKER to `tempo-marks' while keeping it sorted.
    3.52+Remove duplicate marks from `tempo-marks'.  Set to nil removed
    3.53+markers.  This function is used as an :override advice to
    3.54+`tempo-insert-mark', because the original function does not
    3.55+remove duplicate elements.  Duplicate markers appear when the
    3.56+buffer gets smaller, markers start pointing to the same location.
    3.57+We don't want that, because a lot of useless markers can slow
    3.58+down Emacs."
    3.59+  (if (not tempo-marks)
    3.60+      (setq tempo-marks (list marker))
    3.61+    (let ((markers tempo-marks))
    3.62+      (cond
    3.63+       ((< marker (car markers))
    3.64+        (setq tempo-marks (cons marker tempo-marks)))
    3.65+       (t
    3.66+        (while (and (cdr markers) (<= (cadr markers) marker))
    3.67+          (if (/= (car markers) (cadr markers))
    3.68+              (setq markers (cdr markers))
    3.69+            (when (markerp (cadr markers)) (set-marker (cadr markers) nil))
    3.70+            (setcdr markers (cddr markers))))
    3.71+
    3.72+        (if (= marker (car markers))
    3.73+            (when (markerp marker) (set-marker marker nil))
    3.74+          (setcdr markers (cons marker (cdr markers))))))
    3.75+
    3.76+      (while (cdr markers)
    3.77+        (if (/= (car markers) (cadr markers))
    3.78+            (setq markers (cdr markers))
    3.79+          (when (markerp (cadr markers)) (set-marker (cadr markers) nil))
    3.80+          (setcdr markers (cddr markers)))))))
    3.81+
    3.82+(defun skt-add-tag (tag template &optional tag-list)
    3.83+  "Add a TEMPLATE TAG to TAG-LIST or to `tempo-tags'.
    3.84+It is an :override function for `tempo-add-tag'.  The original
    3.85+function does not update identical tags."
    3.86+  (interactive "sTag: \nCTemplate: ")
    3.87+  (let ((tag-list (or tag-list 'tempo-tags)))
    3.88+    (if-let ((value (assoc tag (symbol-value tag-list))))
    3.89+        (setcdr value template)
    3.90+      (set tag-list (cons (cons tag template) (symbol-value tag-list))))
    3.91+    (tempo-invalidate-collection)))
    3.92+
    3.93+(defun skt--list-derived-modes (mode)
    3.94+  "List all derived modes of MODE + MODE itself."
    3.95+  (let ((modes nil))
    3.96+    (while mode
    3.97+      (when-let ((alias (symbol-function mode)))
    3.98+        (when (symbolp alias)
    3.99+          (setq mode alias)))
   3.100+      (push mode modes)
   3.101+      (setq mode (get mode 'derived-mode-parent))  )
   3.102+    (nreverse modes)))
   3.103+
   3.104+;;; Commands
   3.105+(defvar-keymap skt-minor-mode-map
   3.106+  :doc "skt-minor-mode keymap."
   3.107+  :repeat (:enter)
   3.108+  :prefix 'skt-minor-mode-map
   3.109+  "a" #'edit-abbrevs)
   3.110+
   3.111+;;;###autoload
   3.112+(define-minor-mode skt-minor-mode
   3.113+  "Minor mode for skt-templates."
   3.114+  :init-value nil
   3.115+  :lighter " Skt"
   3.116+  :keymap skt-minor-mode-map
   3.117+  (let* ((modes (skt--list-derived-modes major-mode))
   3.118+         (tag-vars (mapcar #'skt--tags-variable modes))
   3.119+         (bound-tag-vars (cl-delete-if-not #'boundp tag-vars)))
   3.120+    (if skt-minor-mode
   3.121+        (mapc #'tempo-use-tag-list bound-tag-vars)
   3.122+      (mapc #'skt--remove-tag-list bound-tag-vars))))
   3.123+
   3.124+(defun skt-register-auto-insert (rx template)
   3.125+  "Associate a template with a file regexp and insert into `auto-insert-alist'."
   3.126+  (cl-pushnew (cons rx template) auto-insert-alist))
   3.127+
   3.128+(defun skt--define-template (function-symbol body &optional docstring)
   3.129+  "Define a tempo template with BODY.
   3.130+This will generate a function with FUNCTION-SYMBOL and
   3.131+DOCSTRING.
   3.132+
   3.133+The main purpose of this function is to have a better controlled
   3.134+alternative to `tempo-define-template'."
   3.135+  (let ((template-symbol (gensym (symbol-name function-symbol))))
   3.136+    (set template-symbol body)
   3.137+    (defalias function-symbol
   3.138+      (lambda (&optional arg)
   3.139+        (interactive "*P")
   3.140+        (tempo-insert-template template-symbol (xor tempo-insert-region arg)))
   3.141+      docstring)))
   3.142+
   3.143+(defun skt--define-skeleton (function-symbol body &optional docstring)
   3.144+  "Define a skeleton template with BODY.
   3.145+This will generate a function with FUNCTION-SYMBOL and
   3.146+DOCSTRING.
   3.147+
   3.148+The main purpose of this function is to have a better controlled
   3.149+alternative to `define-skeleton', especially because it is a
   3.150+function instead of a macro."
   3.151+  (defalias function-symbol
   3.152+    (lambda (&optional str arg)
   3.153+      (interactive "*P\nP")
   3.154+      (skeleton-proxy-new body str arg))
   3.155+    docstring))
   3.156+
   3.157+(defun skt--define-function (function-symbol function &optional docstring)
   3.158+  "This will generate an alias to FUNCTION with FUNCTION-SYMBOL.
   3.159+DOCSTRING is used as a docstring to FUNCTION-SYMBOL."
   3.160+  (defalias function-symbol function docstring))
   3.161+
   3.162+(defun skt--mode-name (mode)
   3.163+  "Get MODE name without a -mode suffix."
   3.164+  (string-trim-right (symbol-name mode) (rx "-mode" eos)))
   3.165+
   3.166+(defun skt--function-name (name modes)
   3.167+  "Generate a name for a skt template function.
   3.168+NAME and MODES are used to generate unique, but consistent
   3.169+names."
   3.170+  (concat "skt-template-"
   3.171+          (mapconcat (lambda (mode) (concat (skt--mode-name mode) "-"))
   3.172+                     (sort modes #'string<) "")
   3.173+          name))
   3.174+
   3.175+(defun skt--mode-abbrev-table (mode)
   3.176+  "Get abbrev table for MODE or `global-abbrev-table' if nil."
   3.177+  (if mode
   3.178+      (derived-mode-abbrev-table-name mode)
   3.179+    'global-abbrev-table))
   3.180+
   3.181+(defun skt--abbrev-table (mode)
   3.182+  "Get skt abbrev table for MODE."
   3.183+  (intern (concat "skt-" (symbol-name (skt--mode-abbrev-table mode)))))
   3.184+
   3.185+(defun skt--abbrev-table-names (table)
   3.186+  "Return abbrev TABLE names."
   3.187+  (let ((names nil))
   3.188+    (mapatoms (lambda (abbrev)
   3.189+                (when (symbol-value abbrev)
   3.190+                  (push (symbol-name abbrev) names)))
   3.191+              (symbol-value table))
   3.192+    names))
   3.193+
   3.194+(defun skt--modes (mode)
   3.195+  "Normalize MODE argument."
   3.196+  (cond ((consp mode) mode)
   3.197+        ((null mode) nil)
   3.198+        ((symbolp mode) (list mode))))
   3.199+
   3.200+;;;###autoload
   3.201+(defun skt--define (define-function name modes tag abbrev docstring body)
   3.202+  "Define a skt template.
   3.203+
   3.204+DEFINE-FUNCTION is a function that takes a function symbol, BODY
   3.205+and DOCSTRING as its arguments.  It must define a new function
   3.206+with that symbol and that docstring.
   3.207+
   3.208+NAME is a string used in generating a function symbol, TAG and
   3.209+ABBREV.
   3.210+
   3.211+MODES is a list of modes for which TAG and ABBREV will be
   3.212+created.  If it's nil, TAG and ABBREV will be generated
   3.213+globally.
   3.214+
   3.215+TAG/ABBREV is a boolean, which indicates whether a tag/abbrev
   3.216+must be created for this template.
   3.217+
   3.218+DOCSTRING is a string (or nil) which will be supplied to
   3.219+DEFINE-FUNCTION.
   3.220+
   3.221+BODY is an arbitrary argument passed to DEFINE-FUNCTION."
   3.222+  (let* ((function-symbol (intern (skt--function-name name modes)))
   3.223+         (modes (or modes '(nil))))
   3.224+    (funcall define-function function-symbol body docstring)
   3.225+    (put function-symbol 'no-self-insert t)
   3.226+
   3.227+    (when tag
   3.228+      (let ((tag-symbol (gensym (symbol-name function-symbol))))
   3.229+        (if (eq #'skt--define-template define-function)
   3.230+            (set tag-symbol body)
   3.231+          (set tag-symbol `((ignore (,function-symbol)))))
   3.232+        (dolist (mode modes)
   3.233+          (let ((var (skt--tags-variable mode)))
   3.234+            (unless (boundp var)
   3.235+              (set var nil))
   3.236+            (tempo-add-tag name tag-symbol var)))
   3.237+        (dolist (buffer (buffer-list))
   3.238+          (with-current-buffer buffer
   3.239+            (when (and (or (equal '(nil) modes) (apply #'derived-mode-p modes))
   3.240+                       skt-minor-mode)
   3.241+              (skt-minor-mode -1)
   3.242+              (skt-minor-mode 1))))))
   3.243+
   3.244+    (when abbrev
   3.245+      (dolist (mode modes)
   3.246+        (let ((mode-table (skt--mode-abbrev-table mode))
   3.247+              (table (skt--abbrev-table mode)))
   3.248+          (define-abbrev-table mode-table nil)
   3.249+          (define-abbrev-table table nil :case-fixed t :skt t)
   3.250+          (define-abbrev (symbol-value table) name "" function-symbol
   3.251+            :case-fixed t :system t :skt t)
   3.252+
   3.253+          (let* ((names (skt--abbrev-table-names table))
   3.254+                 (regexp (concat (regexp-opt names "\\_<\\(") " *")))
   3.255+            (abbrev-table-put (symbol-value table) :regexp regexp))
   3.256+
   3.257+          (let ((parents (abbrev-table-get (symbol-value mode-table) :parents)))
   3.258+            (cl-pushnew (symbol-value table) parents :test #'eq)
   3.259+            (abbrev-table-put (symbol-value mode-table) :parents parents)))))
   3.260+
   3.261+    function-symbol))
   3.262+
   3.263+;;;###autoload
   3.264+(cl-defmacro skt-define-template (name (&key mode tag abbrev docstring) &body body)
   3.265+  "Define a tempo template.
   3.266+This macro defines a new tempo template or updates the old one.
   3.267+NAME is a symbol.  ARGS is a list of the form ([KEY VALUE]...)
   3.268+where each KEY can be one of :tag, :abbrev, :docstring or :mode.
   3.269+
   3.270+If KEY is :tag, VALUE should be a boolean.  If VALUE is non-nil,
   3.271+then a tempo tag with NAME will be created for this template.
   3.272+
   3.273+If KEY is :abbrev, VALUE should be a boolean.  If VALUE is
   3.274+non-nil, then a NAME abbrev will be created for this template.
   3.275+
   3.276+If KEY is :docstring, VALUE should be a string.  It will be a
   3.277+docstring of the generated function.
   3.278+
   3.279+If KEY is :mode, VALUE should be a list of modes or single mode.
   3.280+If this option is provided, than a tempo tag and an abbrev will
   3.281+be created for these modes, otherwise they will be global (if
   3.282+:tag and :abbrev options were provided, of course).
   3.283+
   3.284+BODY is a sequence of tempo elements that will be passed as a
   3.285+list directly to `tempo-define-template's second argument.
   3.286+
   3.287+Example:
   3.288+\(skt-define-template defvar (:mode emacs-lisp-mode :tag t :abbrev t
   3.289+                             :docstring \"defvar template\")
   3.290+  \"(defvar \" (string-trim-right (buffer-name) (rx \".el\" eos)) \"-\" p n>
   3.291+  r> \")\")"
   3.292+  `(skt--define 'skt--define-template ,(symbol-name name)
   3.293+               ',(skt--modes mode) ,tag ,abbrev ,docstring ',body))
   3.294+
   3.295+;;;###autoload
   3.296+(cl-defmacro skt-define-skeleton (name (&key mode tag abbrev docstring) &rest body)
   3.297+  "Define skeleton template.
   3.298+See `skt-define-template' for explanation of NAME, MODE, TAG,
   3.299+ABBREV and DOCSTRING.
   3.300+
   3.301+BODY is a sequence of skeleton elements that will be passed
   3.302+directly to `define-skeleton'.
   3.303+
   3.304+Example:
   3.305+\(skt-define-skeleton defun (:mode (emacs-lisp-mode lisp-interaction-mode)
   3.306+                               :tag t :abbrev t
   3.307+                               :docstring \"defun template\")
   3.308+  \"(defun \" str \" (\" @ - \")\" \n
   3.309+  @ _ \")\" \n)"
   3.310+  `(skt--define #'skt--define-skeleton ,(symbol-name name)
   3.311+                  ',(skt--modes mode) ,tag ,abbrev ,docstring ',body))
   3.312+
   3.313+;;;###autoload
   3.314+(cl-defmacro skt-define-function (name (&key mode tag abbrev docstring) function)
   3.315+  "Define FUNCTION template.
   3.316+See `skt-define-template' for explanation of NAME, MODE, TAG,
   3.317+ABBREV and DOCSTRING.
   3.318+
   3.319+The main purpose of this macro, is to create tempo tags and
   3.320+abbrevs for existing skeleton templates, such as `sh-case'.
   3.321+
   3.322+Example:
   3.323+\(skt-define-function shcase (:tag t :abbrev t :mode `sh-mode') `sh-case')"
   3.324+  `(skt--define #'skt--define-function ,(symbol-name name)
   3.325+                  ',(skt--modes mode) ,tag ,abbrev ,docstring ',function))
   3.326+
   3.327+;;;###autoload
   3.328+(defun skt--complete-template (string tag-list)
   3.329+  "An :override advice function for `tempo-display-completions'.
   3.330+Show completion for STRING in a TAG-LIST.  After selection
   3.331+expand template.
   3.332+
   3.333+Rewritten because the original function uses an old way of
   3.334+displaying completions in a separate buffer, which is not
   3.335+clickable anyway.  Now it uses new (compared to the originial
   3.336+tempo package) and shiny `completing-read' interface."
   3.337+  (let* ((tags (mapcar #'car tag-list))
   3.338+         (tag (completing-read "Skt: " tags nil t string)))
   3.339+    (delete-char (- (length string)))
   3.340+    (tempo-insert-template (cdr (assoc tag tag-list)) nil)))
   3.341+
   3.342+;;;###autoload
   3.343+(defcustom skt-enable-tempo-elements nil
   3.344+  "Enable extra tempo elements.
   3.345+These elements add conditionals and looping support for tempo
   3.346+like those in skeleton, making skeleton pretty much obsolete.
   3.347+
   3.348+If you want to set this option from ELisp, you have to remove
   3.349+`skt-tempo-user-elements' from `tempo-user-elements' on nil
   3.350+and add it on non-nil."
   3.351+  :type '(boolean :tag "Enable tempo elements?")
   3.352+  :set (lambda (variable value)
   3.353+         (if value
   3.354+             (add-hook 'tempo-user-functions #'skt-tempo-user-elements)
   3.355+           (remove-hook 'tempo-user-functions #'skt-tempo-user-elements))
   3.356+         (set-default variable value))
   3.357+  :group 'skel)
   3.358+
   3.359+(defcustom skt-completing-read nil
   3.360+  "Override default `tempo-display-completions'.
   3.361+By default it uses a completion buffer to show completions.  This
   3.362+option overrides this function to use `completing-read' to select
   3.363+partial skt tag or complete tag on region.
   3.364+
   3.365+If you wish to set this variable from ELisp code, you have to
   3.366+remove `skt--complete-template' advice from
   3.367+`tempo-display-completions' on nil and add it as on :override
   3.368+advice on non-nil."
   3.369+  :type '(boolean :tag "Override?")
   3.370+  :set (lambda (variable value)
   3.371+         (if value
   3.372+             (advice-add 'tempo-display-completions :override #'skt--complete-template)
   3.373+           (advice-remove 'tempo-display-completions #'skt--complete-template))
   3.374+         (set-default variable value))
   3.375+  :group 'skel)
   3.376+
   3.377+(defcustom skt-delete-duplicate-marks nil
   3.378+  "Override default `tempo-insert-mark'.
   3.379+Marks are used to jump on points of interest in a template.  By
   3.380+default `tempo-insert-mark' does not remove duplicate marks.
   3.381+Duplicate marks might appear when the buffer shrinks and some of
   3.382+the marks start pointing to the same location.  This option tries
   3.383+to fix this by checking for duplicate marks every time the
   3.384+function is called.  Emacs might get slower with a lot of
   3.385+marks.
   3.386+
   3.387+If you want to set this option from ELisp, you have to remove
   3.388+`skt--insert-mark' advice from `tempo-insert-mark' on nil and
   3.389+add it as on :override advice on non-nil."
   3.390+  :type '(boolean :tag "Override?")
   3.391+  :set (lambda (variable value)
   3.392+         (if value
   3.393+             (advice-add 'tempo-insert-mark :override #'skt--insert-mark)
   3.394+           (advice-remove 'tempo-insert-mark #'skt--insert-mark))
   3.395+         (set-default variable value))
   3.396+  :group 'skel)
   3.397+
   3.398+(progn
   3.399+  (put 'tempo-define-template 'lisp-indent-function 1)
   3.400+  (put 'skt-define-template 'lisp-indent-function 2)
   3.401+  (put 'skt-define-skeleton 'lisp-indent-function 2)
   3.402+  (put 'skt-define-function 'lisp-indent-function 2))
   3.403+
   3.404+;;; Tempo Elements
   3.405+(defvar skt-tempo-else-key (kbd "C-M-g")
   3.406+  "Key used to execute else branch in tempo conditional.")
   3.407+
   3.408+(defun skt-tempo--prompt (prompt)
   3.409+  "Make prompt for tempo conditional.
   3.410+PROMPT is preceded with `skt-tempo-else-key'."
   3.411+  (concat "(" (key-description skt-tempo-else-key) " to quit) " prompt))
   3.412+
   3.413+(defun skt-tempo-user-elements (element)
   3.414+  "Support for conditional and looping tempo elements.
   3.415+The following forms are supported for ELEMENT:
   3.416+
   3.417+\(:if (PROMPT VAR) THEN ELSE)
   3.418+
   3.419+\(:when (PROMPT VAR) BODY...)
   3.420+
   3.421+\(:while (PROMPT VAR) BODY...)
   3.422+
   3.423+PROMPT is a string used to read value for VAR.  VAR is a tempo
   3.424+variable symbol.  Its value can be read with s, as usual.  BODY,
   3.425+THEN and ELSE are tempo elements.  To abort the execution of
   3.426+these elements, user must press `skt-tempo-else-key'.
   3.427+
   3.428+The main purpose of this extension is to mimic skeleton conditionals."
   3.429+  (pcase element
   3.430+    (`(:if (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) ,then ,else)
   3.431+     (let ((prompt (skt-tempo--prompt prompt))
   3.432+           (map (make-sparse-keymap)))
   3.433+       (set-keymap-parent map minibuffer-local-map)
   3.434+       (define-key map skt-tempo-else-key
   3.435+         (lambda () (interactive) (throw 'else else)))
   3.436+       (catch 'else
   3.437+         (tempo-save-named var (read-from-minibuffer prompt nil map))
   3.438+         then)))
   3.439+    (`(:when (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) . ,body)
   3.440+     `(:if (,prompt ,var) (l ,@body) (l)))
   3.441+    (`(:while (,(and (pred stringp) prompt) ,(and (pred symbolp) var)) . ,body)
   3.442+     `(:when (,prompt ,var) ,@body ,element))))
   3.443+
   3.444+(provide 'skt)
   3.445+;;; skt.el ends here