From 259363fd4f21d796c3c6a35be6398aed3f493a73 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Tue, 3 Jan 2023 23:02:26 -0600 Subject: bleh --- lisp/def.el | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 lisp/def.el (limited to 'lisp/def.el') diff --git a/lisp/def.el b/lisp/def.el new file mode 100644 index 0000000..0bf91b2 --- /dev/null +++ b/lisp/def.el @@ -0,0 +1,142 @@ +;;; def.el --- defining macros -*- lexical-binding: t; -*- + +;;; Code: + +(require 'cl-lib) + +;;; Utility + +(defun def--assert-args (pred args &optional error-type &rest error-args) + "Assert that ARGS follows PRED. +If it doesn't, raise an error. ERROR-TYPE will be the type of +that error (defaults to `user-error'), and it and ERROR-ARGS are +passed in a list to `signal'." + (unless (funcall pred args) + (funcall #'signal + (or error-type 'user-error) + (or error-args + (list "Wrong arguments" args))))) + +(defmacro o (&rest fns) + "Compose FNS into a new function for one argument." + (if (null fns) + `(lambda (&rest args) args) + `(lambda (&rest args) + (apply + #',(car fns) + (ensure-list (apply (o ,@(cdr fns)) args)))))) + +;; TODO: I need to figure out where this function goes. +(defun def--delete2 (list &rest elems) + "Delete each element of ELEMS, and the next item, from LIST." + (let ((r nil)) + (while (consp list) + (if (member (car list) elems) + (setf list (cdr list)) + (setf r (cons (car list) r))) + (setf list (cdr list))) + (reverse r))) + +;;; Keybindings + +(defmacro defkeys (maps &rest bindings) + "Define key BINDINGS in MAPS. +If MAPS is nil or t, bind to `current-global-map'. Otherwise, +bind each of BINDINGS to the map or list of maps provided. + +BINDINGS is a `setq'-style list of pairs of keys and definitions. +The key part of each binding can be a string, in which case it's +passed to `kbd', or a vector or anything else `define-key' +accepts in the KEY position. The definition part, likewise, can +be any form `define-key' accepts in that position, with this +addition: if the form is a `defun' form, it will be defined +before any keys are bound." + (declare (indent 1)) + (def--assert-args (o cl-evenp length) bindings + 'wrong-number-of-arguments 'defkeys 'evenp (length bindings)) + `(progn + ,@(cl-loop + for map in (ensure-list maps) + for first-map-p = t then nil + append + (cl-loop + for (keys def) on bindings by #'cddr + for defp = (memq (car-safe def) '(defmap defun defmacro)) + if (and defp first-map-p) collect def into defuns + append + (cl-loop + for key in (ensure-list keys) + collect (list 'define-key + (if (memq map '(t nil)) + '(current-global-map) + (or (car-safe map) map)) + (if (stringp key) + `(kbd ,key) + key) + (if defp + (cl-case (car def) + ((defmap) (cadr def)) + ((defun defmacro) `#',(cadr def)) + (otherwise (error "Bad def type: %S" + (car def)))) + def))) + into keydefs + finally return + (let ((all (append defuns keydefs))) + (if-let ((after (plist-get (cdr-safe map) :after))) + `((eval-after ,after + ,@all)) + all)))))) + +(defmacro defmap (name docstring &rest bindings) + "Define a keymap named NAME, with BINDINGS." + (declare (indent 1) (doc-string 2)) + `(,(if (boundp name) 'setq 'defvar) ,name + ;;; ^ probably a terrible hack + (let ((map (make-sparse-keymap))) + (defkeys map ,@bindings) + map) + ,@(unless (boundp name) (list docstring)))) + +;;; Hooks + +(defmacro defhook (hooks &rest body) + "Define a function to hook into HOOKS. +NAME and ARGS are passed to the generated `defun' form. +Each hook in HOOKS can be the name of a hook or a list of the form +(HOOK DEPTH LOCAL), where each argument is the same as in +`add-hook'." + (declare (indent 1)) + (let* ((name (or (plist-get body :name) + (intern (format "%s/h" + (mapconcat + (lambda (h) + (string-remove-suffix + "-hook" (symbol-name (or (car-safe h) + h)))) + (ensure-list hooks) + "|"))))) + (args (or (plist-get body :args) nil)) + (doc (or (plist-get body :doc) nil)) + (forms ; (DEFUN . FUNCS) + (cl-loop for form in (def--delete2 body :name :args :doc) + if (eq (car form) 'function) + collect form into funcs + else collect form into defuns + finally return (cons defuns funcs))) + (defun-forms (car forms)) + (func-forms (cdr forms))) + `(progn + ,@(when defun-forms + `((defun ,name ,args ,@(when doc (list doc)) ,@defun-forms))) + ,@(cl-loop for hook in (ensure-list hooks) + for h = (or (car-safe hook) hook) + for ha = (cdr-safe hook) + if defun-forms + collect `(add-hook ',h #',name ,@ha) + append + (cl-loop for fn in func-forms + collect `(add-hook ',h ,fn ,@ha)))))) + +(provide 'def) +;;; def.el ends here -- cgit 1.4.1-21-gabe81