xFA25E / skempo

Enhance Emacs skeleton, tempo and abbrev.
GNU General Public License v3.0
35 stars 2 forks source link

+TITLE: Skempo

This is an attempt to improve Emacs built-in /skeleton/ and /tempo/ templates. It tries to make a unified syntax for template definitions. Adds tags and marks support for /skeleton/ and abbrev support for /tempo/.

Every symbol is well documented. See Commentary and documentation strings.

** Define templates

+begin_src emacs-lisp

;; Multiple modes! (skempo-define-tempo let (:tag t :abbrev t :mode (emacs-lisp-mode lisp-mode)) "(let ((" p "))" n> r> ")")

;; Skeletons too! With mark jumping! (skempo-define-skeleton defun (:tag t :abbrev t :mode emacs-lisp-mode) "Name: " "(defun " str " (" @ - ")" \n @ _ ")" \n)

;; Clever tempo templates! (skempo-define-tempo defvar (:tag t :abbrev t :mode emacs-lisp-mode) "(defvar " (string-trim-right (buffer-name) (rx ".el" eos)) "-" p n> r> ")")

;; Define tags and abbrevs for existing skeletons and tempo templates! (skempo-define-function shcase (:tag t :abbrev t :mode sh-mode) sh-case)

;; This will override emacs-lisp's "defvar", but you can always call it by ;; function name (or by tag/abbrev if they were defined). (skempo-define-tempo defvar (:tag t :mode lisp-interaction-mode) "(defvar var" p n> r> ")")

+end_src

** Call templates

+begin_src emacs-lisp

(add-hook 'emacs-lisp-mode 'skempo-mode) (add-hook 'lisp-interaction-mode 'skempo-mode)

(with-eval-after-load 'skempo (easy-mmode-define-keymap '(("\C-z" . skempo-complete-tag-or-call-on-region) ("\M-g\M-e" . skempo-forward-mark) ("\M-g\M-a" . skempo-backward-mark)) nil skempo-mode-map))

(custom-set-variables '(skempo-completing-read t) '(skempo-delete-duplicate-marks t) '(skempo-update-identical-tags t) '(skempo-skeleton-marks-support t) '(skempo-mode-lighter " Sk") '(skempo-enable-tempo-elements t))

+end_src

+begin_src elisp

(skempo-define-skeleton someskel () nil "(:option" ;; insert until empty string is given ("Insert symbol: " " #:" str) ")")

(skempo-define-tempo sometemp () "(:option" ;; insert until C-M-g key is pressed (:while ("Insert symbol: " sym) " #:" (s sym)) ")")

+end_src

The main difference is in the abortion. Skeleton aborts on empty string. I think that empty input is necessary, sometimes, so I choose the approach of binding a dedicate key for abortion. This key is displayed in the prompt and can be customized with ~skempo-tempo-else-key~.

Some more complicated behavior:

+begin_src elisp

(skempo-define-skeleton someskel () nil "(:option" ;; insert until empty string is given ("Insert symbol: " " #:" str) & ")" ;; or don't insert at all. It is a hack that physically removes "(:option" ;; string by deleting 8 characters if previous statements didn't move the ;; point | -8)

(skempo-define-tempo sometemp () ;; If input was not aborted, start inserting (:when ("Insert symbol: " sym) "(:option #:" (s sym) ;; Continue inserting until C-M-g is pressed (:while ("Insert symbol: " sym) " #:" (s sym)) ")"))

+end_src

There is also an ~:if~ element, that can execute else branch if input was aborted.

+begin_src elisp

(skempo-define-tempo sometemp () (:if ("Insert symbol: " sym) ;; Use l element to group elements together (l "insert " (s sym)) "something else"))

+end_src

** Disable expansion on non space characters for lisp modes

+begin_src elisp

(let ((enable-fn (lambda () (or (eq this-command 'expand-abbrev) (eql ?\s last-command-event))))) (dolist (mode '(lisp-mode emacs-lisp-mode)) (let ((table (symbol-value (skempo--abbrev-table mode)))) (abbrev-table-put table :enable-function enable-fn))))

+end_src

This solution will disable automatic expansion on, for example, ~let*~ template.

** Modify syntax for these characters to make them words

+begin_src elisp

(defun modify-lisp-syntax-tables () (modify-syntax-entry ?* "w" (syntax-table)) (modify-syntax-entry ?- "w" (syntax-table)))

(dolist (hook '(lisp-mode-hook emacs-lisp-mode-hook)) (add-hook hook #'modify-lisp-syntax-tables))

+end_src

This solution will treat ~some-long-symbol~ as a single word. You can change only ~~ character, but in that case ~let-~ will trigger ~let~.

** Bind ~expand-abbrev~ on some key https://www.emacswiki.org/emacs/AbbrevMode#h5o-11