From a0495c6df3fa3f258df985440e4c5361cd82d314 Mon Sep 17 00:00:00 2001 From: Madeleine Sydney Date: Sat, 15 Feb 2025 16:26:19 -0700 Subject: [PATCH] wip(emacs): Abstract strategy-running - Show docs and goto def are confirmed working --- .../emacs/lib/syd-strategies-lookup.el | 70 +++++++++++++++++++ .../programs/emacs/lib/syd-strategies-repl.el | 8 +++ .../programs/emacs/lib/syd-strategies.el | 37 ++++++++++ 3 files changed, 115 insertions(+) create mode 100644 users/crumb/programs/emacs/lib/syd-strategies-lookup.el create mode 100644 users/crumb/programs/emacs/lib/syd-strategies-repl.el create mode 100644 users/crumb/programs/emacs/lib/syd-strategies.el diff --git a/users/crumb/programs/emacs/lib/syd-strategies-lookup.el b/users/crumb/programs/emacs/lib/syd-strategies-lookup.el new file mode 100644 index 0000000..05e37bf --- /dev/null +++ b/users/crumb/programs/emacs/lib/syd-strategies-lookup.el @@ -0,0 +1,70 @@ +;; syd-strategies-lookup.el -*- lexical-binding: t; -*- + +(require 'syd-strategies) +(use-package better-jumper) +(require 'better-jumper) + +(defun syd-strat--run-lookup-strategy (strategy identifier origin) + "Safely call a lookup STRATEGY. A lookup strategy, if it is not an +interactive command, will be called with the lone argument IDENTIFIER. +Interactive or not, the procedure is expected to return nil on failure. Returns +a marker if STRATEGY returns a buffer or marker, or nil on failure. +Modifications to the window configuration will be discarded if STRATEGY fails to +return a buffer or marker." + (condition-case-unless-debug e + (let ((wconf (current-window-configuration)) + (result (condition-case-unless-debug e + (if (commandp strategy) + (call-interactively strategy) + (funcall strategy identifier)) + (error + (message "Lookup strategy %S threw an error: %s" strategy e) + 'fail)))) + (cond ((eq result 'fail) + (set-window-configuration wconf) + nil) + ((bufferp result) + (with-current-buffer result + (point-marker))) + ((or result + (null origin) + (/= (point-marker) origin)) + (prog1 (point-marker) + (set-window-configuration wconf))))) + ((error user-error) + (message "Lookup strategy %S: %s" strategy e) + nil))) + +(cl-defun syd-strat--lookup-and-jump-to + (strategy-category identifier &key (display-fn #'switch-to-buffer)) + (let* ((origin (point-marker)) + (strategies (alist-get strategy-category syd-strat--strategies)) + ;; TODO: If called with a prefix argument, prompt the user to select a + ;; strategy. + (result (syd-strat-try-functions-wrapped + #'syd-strat--run-lookup-strategy + strategies identifier origin))) + (unwind-protect + (when (cond ((null result) + (message "No lookup strategy could find %S" identifier) + nil) + ((markerp result) + (funcall display-fn (marker-buffer result)) + (goto-char result) + result) + (result)) + (with-current-buffer (marker-buffer origin) + (better-jumper-set-jump (marker-position origin))) + result)) + (set-marker origin nil))) + +(defun syd-strat-lookup-documentation (identifier) + (interactive (list (syd-thing-at-point-or-region))) + (syd-strat--lookup-and-jump-to :documentation identifier + :display-fn #'pop-to-buffer)) + +(defun syd-strat-lookup-definition (identifier) + (interactive (list (syd-thing-at-point-or-region))) + (syd-strat--lookup-and-jump-to :definition identifier)) + +(provide 'syd-strategies-lookup) diff --git a/users/crumb/programs/emacs/lib/syd-strategies-repl.el b/users/crumb/programs/emacs/lib/syd-strategies-repl.el new file mode 100644 index 0000000..43d306f --- /dev/null +++ b/users/crumb/programs/emacs/lib/syd-strategies-repl.el @@ -0,0 +1,8 @@ +;; syd-strategies-repl.el -*- lexical-binding: t; -*- + +;;;###autoload +(define-minor-mode syd-repl-mode + "A minor mode for repl buffers. One use is to universally customise the +display of all repl buffers.") + +(provide 'syd-strategies-repl) diff --git a/users/crumb/programs/emacs/lib/syd-strategies.el b/users/crumb/programs/emacs/lib/syd-strategies.el new file mode 100644 index 0000000..08de120 --- /dev/null +++ b/users/crumb/programs/emacs/lib/syd-strategies.el @@ -0,0 +1,37 @@ +;; syd-strategies.el -*- lexical-binding: t; -*- + +(require 'syd-text) + +;; :documentation : Identifier -> Marker +(defvar-local syd-strat--strategies nil) + +(defun syd-strat-try-functions-wrapped (wrapper fns &rest args) + "For each FN in FNS, call WRAPPER with the arguments FN followed by ARGS, +until a FN returns non-nil." + (cl-loop for fn in fns + with r = nil + do (setq r (apply wrapper fn args)) + until r + finally return r)) + +(defun syd-strat--set-strategies (category strategies) + (cl-loop for ref in-ref syd-strat--strategies + until (eq category (car ref)) + finally do (pp ref))) + +(defun syd-set-strategies (modes &rest args) + (dolist (mode (ensure-list modes)) + (let ((hook (intern (format "%s-hook" mode))) + (fn-name (intern (format "syd-strat--init-for-%s-h" mode)))) + (unless (cl-evenp (length args)) + (signal 'wrong-number-of-arguments args)) + ;; We use this `defalias' incantation instead of a raw `fset' because the + ;; former will properly associate a source location to the definition. + (defalias fn-name + (function + (lambda () + (cl-loop for (category strategies) on args by (lambda (x) (-drop 2 x)) + do (syd-strat--set-strategies category strategies))))) + (add-hook hook fn-name)))) + +(provide 'syd-strategies)