fix: Repls finally respect popup rules TwT
- Required patching on.el to run their hooks at the correct time.
This commit is contained in:
@@ -13,16 +13,16 @@
|
||||
;; Must come before the rest!
|
||||
(require 'syd-use-package)
|
||||
|
||||
(eval-when-compile
|
||||
(add-to-list 'load-path (file-name-concat user-emacs-directory "modules" "syd-popup")))
|
||||
(use-package doom-popup
|
||||
;; :defer t
|
||||
:load-path "/persist/dots/users/crumb/programs/emacs/modules/doom-popup"
|
||||
:straight nil
|
||||
;; :straight
|
||||
;; (:type nil
|
||||
;; :local-repo "/persist/dots/users/crumb/programs/emacs/modules/doom-popup")
|
||||
)
|
||||
;; `on.el' provies a collection of utility hooks and functions ported from Doom
|
||||
;; Emacs. The hooks make it easier to speed up Emacs startup by providing
|
||||
;; finer-grained control of the timing at which packages are loaded.
|
||||
(use-package on
|
||||
:straight (:type git
|
||||
:host gitlab
|
||||
:repo "crumbtoo/on.el"))
|
||||
|
||||
;; Used in many other modules, so it comes first.
|
||||
(require 'syd-popups)
|
||||
|
||||
(require 'syd-age)
|
||||
(require 'syd-autosave)
|
||||
|
||||
@@ -4,6 +4,28 @@
|
||||
(require 'syd-prelude)
|
||||
(require 'syd-project)
|
||||
|
||||
|
||||
|
||||
(defun syd--set-popup-rules-for-repls-h ()
|
||||
(require 'doom-popup)
|
||||
(set-popup-rule!
|
||||
(lambda (bufname _)
|
||||
(when (boundp 'syd-repl-mode)
|
||||
(buffer-local-value 'syd-repl-mode (get-buffer bufname))))
|
||||
:ttl (lambda (buf)
|
||||
(unless (plist-get syd-repl-plist :persist)
|
||||
(when-let (process (get-buffer-process buf))
|
||||
(set-process-query-on-exit-flag process nil)
|
||||
(kill-process process)
|
||||
(kill-buffer buf))))
|
||||
:size 0.25
|
||||
:quit nil))
|
||||
|
||||
(add-hook 'on-init-ui-hook #'syd--set-popup-rules-for-repls-h 'append)
|
||||
|
||||
|
||||
;;; State & settings
|
||||
|
||||
(defvar +syd-major-mode-repl-alist '()
|
||||
"TODO: An alist pairing major-modes (symbols) with plists describing REPLs.")
|
||||
|
||||
@@ -12,7 +34,41 @@
|
||||
buffers. Indeed, this implies a single REPL per language per project. Is this
|
||||
limitation worth overcoming? I'm not sure! I've yet to butt heads with it.")
|
||||
|
||||
(defvar-local syd-repl-plist nil)
|
||||
(defvar-local syd-repl-plist nil
|
||||
"A plist describing the repl associated with the current buffer.
|
||||
|
||||
This is little more than a cache. Its value can almost always be equivalently
|
||||
derived from `+syd-major-mode-repl-alist'.")
|
||||
|
||||
(defun set-repl-handler! (modes command &rest plist)
|
||||
"Defines a REPL for MODES.
|
||||
|
||||
MODES is either a single major mode symbol or a list of them. COMMAND is a
|
||||
function that creates and returns the REPL buffer.
|
||||
|
||||
COMMAND can either be a function that takes no arguments, or an interactive
|
||||
command that will be called interactively. COMMANDS must return either the repl
|
||||
buffer or a function that takes no arguments and returns the repl buffer.
|
||||
|
||||
PLIST is a property list that map special attributes to this repl. These are
|
||||
recognized:
|
||||
|
||||
:persist BOOL
|
||||
If non-nil, this REPL won't be killed when its window is closed.
|
||||
:send-region FUNC
|
||||
A function that accepts a BEG and END, and sends the contents of the region
|
||||
to the REPL. Defaults to `+eval/send-region-to-repl'.
|
||||
:send-buffer FUNC
|
||||
A function of no arguments that sends the contents of the buffer to the REPL.
|
||||
Defaults to `+eval/region', which will run the :send-region specified function
|
||||
or `+eval/send-region-to-repl'."
|
||||
(declare (indent defun))
|
||||
(dolist (mode (ensure-list modes))
|
||||
(setf (alist-get mode +eval-repls)
|
||||
(cons command plist))))
|
||||
|
||||
|
||||
;;; Repls
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode syd-repl-mode
|
||||
@@ -36,36 +92,8 @@ not-alive buffer."
|
||||
(defun syd--get-repl-key ()
|
||||
(cons major-mode (syd-project-root)))
|
||||
|
||||
(defun syd--call-repl-handler (repl-handler &optional plist maybe-repl-buffer)
|
||||
"Call REPL-HANDLER, and error out if it does not return a buffer.
|
||||
|
||||
REPL-HANDLER will be called interactively if supported."
|
||||
(let ((repl-buffer
|
||||
(if (buffer-live-p maybe-repl-buffer)
|
||||
maybe-repl-buffer
|
||||
(let ((repl-buffer*
|
||||
(save-window-excursion
|
||||
(if (commandp repl-handler)
|
||||
(call-interactively repl-handler)
|
||||
(funcall repl-handler)))))
|
||||
(cond ((null repl-buffer*)
|
||||
(error "REPL handler %S couldn't open the REPL buffer" repl-handler))
|
||||
((not (bufferp repl-buffer*))
|
||||
(error "REPL handler %S failed to return a buffer" repl-handler))
|
||||
(t repl-buffer*))))))
|
||||
;; Repl buffers are to be saved in `+syd-repl-buffers'; we've just
|
||||
;; opened one, so do so.
|
||||
(puthash (syd--get-repl-key) repl-buffer +syd-repl-buffers)
|
||||
;; If it isn't a buffer, we return nil.
|
||||
(when (bufferp repl-buffer)
|
||||
(with-current-buffer repl-buffer
|
||||
(when plist
|
||||
(setq syd-repl-plist plist))
|
||||
(syd-repl-mode 1))
|
||||
repl-buffer)))
|
||||
|
||||
(defun syd--goto-end-of-repl ()
|
||||
"Move point to the last comint prompt or the end of the buffer."
|
||||
"Try to move point to the last comint prompt or the end of the buffer."
|
||||
(unless (or (derived-mode-p 'term-mode)
|
||||
(eq (current-local-map) (bound-and-true-p term-raw-map)))
|
||||
(goto-char (if (and (derived-mode-p 'comint-mode)
|
||||
@@ -73,10 +101,62 @@ REPL-HANDLER will be called interactively if supported."
|
||||
(cdr comint-last-prompt)
|
||||
(point-max)))))
|
||||
|
||||
(cl-defun syd--call-repl-handler (repl-handler &key plist repl-key)
|
||||
"Spawn a new repl buffer using REPL-HANDLER. REPL-HANDLER's return value will
|
||||
be returned.
|
||||
|
||||
If REPL-HANDLER fails to return a buffer, this `syd--call-repl-handler' will
|
||||
throw an error. `syd-repl-mode' will be enabled in the new buffer, and the
|
||||
buffer will be cached in `+syd-repl-buffers'.
|
||||
|
||||
REPL-HANDLER will be called interactively if supported."
|
||||
(let ((repl-buffer (save-window-excursion
|
||||
(if (commandp repl-handler)
|
||||
(call-interactively repl-handler)
|
||||
(funcall repl-handler)))))
|
||||
(unless repl-buffer
|
||||
(error "REPL handler %S couldn't open the REPL buffer" fn))
|
||||
(unless (bufferp repl-buffer)
|
||||
(error "REPL handler %S failed to return a buffer" fn))
|
||||
(with-current-buffer repl-buffer
|
||||
;; It is important that `syd-repl-mode' is enabled before the buffer is
|
||||
;; displayed by `display-fn'.
|
||||
(syd-repl-mode 1)
|
||||
(when plist
|
||||
;; Cache the plist at `syd-repl-plist'.
|
||||
(setq syd-repl-plist plist)))
|
||||
(puthash repl-key repl-buffer +syd-repl-buffers)
|
||||
repl-buffer))
|
||||
|
||||
;; #+begin_src dot :file /tmp/repl.svg :results file graphics
|
||||
;; digraph {
|
||||
;; bgcolor="transparent"
|
||||
;;
|
||||
;; node [
|
||||
;; fillcolor=gray95
|
||||
;; color=black
|
||||
;; shape=record
|
||||
;; ]
|
||||
;;
|
||||
;; "Start" [shape=diamond]
|
||||
;; "Start" -> x1
|
||||
;; x1 [label="Is the user currently in the repl buffer,\nOR has a repl handler NOT been provided?"]
|
||||
;;
|
||||
;; x1 -> x2 [label="Yes"]
|
||||
;; x1 -> x3 [label="No"]
|
||||
;; x2 [label="Find entry in +syd-repl-buffers;\nis it a live buffer?"]
|
||||
;; x2 -> x4 [label="Yes"]
|
||||
;; x4 [label="Call display-fn on the\n+syd-repl-buffers entry, and use the result"]
|
||||
;; x2 -> x5 [label="No"]
|
||||
;; x5 [label="Call the provided repl-handler. Ensure it returns\na valid buffer, and pass the resultto display-fn.\nSet the plist, enable repl mode, update\n+syd-repl-buffers. Use the buffer returned by display-fn"]
|
||||
;; x3 [label="Use entry found in +syd-repl-buffers"]
|
||||
;; }
|
||||
;; #+end_src
|
||||
|
||||
(cl-defun syd--ensure-in-repl-buffer
|
||||
(&key repl-handler plist (display-fn #'get-buffer-create))
|
||||
"TODO: Display the repl buffer associated with the current major mode and
|
||||
project. A repl buffer will be created (using REPL-HANDLER) if necessary.
|
||||
"Display the repl buffer associated with the current major mode and project.
|
||||
A repl buffer will be created (using REPL-HANDLER) if necessary.
|
||||
|
||||
If an active repl buffer is found in `+syd-repl-buffers', it will be displayed
|
||||
by the given DISPLAY-FN.
|
||||
@@ -86,16 +166,28 @@ PLIST is a plist of repl-specific options."
|
||||
(let* ((repl-key (syd--get-repl-key))
|
||||
(maybe-repl-buffer (gethash repl-key +syd-repl-buffers)))
|
||||
(cl-check-type maybe-repl-buffer (or buffer null))
|
||||
(unless (or (eq maybe-repl-buffer (current-buffer))
|
||||
(let ((repl-buffer
|
||||
(if (or (eq maybe-repl-buffer (current-buffer))
|
||||
(null repl-handler))
|
||||
(let* ((repl-buffer (syd--call-repl-handler repl-handler
|
||||
plist
|
||||
maybe-repl-buffer))
|
||||
(displayed-repl-buffer (funcall display-fn repl-buffer)))
|
||||
(when (bufferp displayed-repl-buffer)
|
||||
(with-current-buffer displayed-repl-buffer
|
||||
;; * If the current buffer is the repl buffer, we can be sure
|
||||
;; that it is not nil and can be returned as-is.
|
||||
;; * If we were not given a repl-handler, there's nothing else we
|
||||
;; can do. Return what was found in `+syd-repl-buffers', and
|
||||
;; hope it's the right thing.
|
||||
maybe-repl-buffer
|
||||
;; If the repl buffer found in `+syd-repl-buffers' is live and
|
||||
;; well, we can return that. If not, we're going to have to spawn
|
||||
;; a new repl buffer with `repl-handler' and `display-fn'.
|
||||
(if (buffer-live-p maybe-repl-buffer)
|
||||
(funcall display-fn maybe-repl-buffer)
|
||||
(funcall display-fn
|
||||
(syd--call-repl-handler repl-handler
|
||||
:plist plist
|
||||
:repl-key repl-key))))))
|
||||
(when (bufferp repl-buffer)
|
||||
(with-current-buffer repl-buffer
|
||||
(syd--goto-end-of-repl))
|
||||
displayed-repl-buffer)))))
|
||||
repl-buffer))))
|
||||
|
||||
(defun syd--known-repls ()
|
||||
"Return a list of all known mode-repl pairs, each as a two-element list.
|
||||
@@ -133,6 +225,11 @@ function."
|
||||
(user-error "Aborting"))))
|
||||
(cadr (assoc choice repls))))
|
||||
|
||||
(defun syd-send-region-to-repl (beg end)
|
||||
(interactive "r")
|
||||
(let ((selection (buffer-substring-no-properties beg end))
|
||||
(buffer (syd--ensure-in-repl-buffer)))))
|
||||
|
||||
(cl-defun +syd-open-repl (&key prompt-p display-fn)
|
||||
"TODO: Open a repl via DISPLAY-FN. When PROMPT-P, the user will be
|
||||
unconditionally prompted for a repl choice.
|
||||
@@ -168,21 +265,4 @@ prefix argument is given, in which case the user will be prompted for a repl."
|
||||
|
||||
|
||||
|
||||
(set-popup-rule!
|
||||
(lambda (bufname _)
|
||||
(pp bufname)
|
||||
(with-current-buffer bufname
|
||||
(pp (boundp 'syd-repl-mode))
|
||||
(pp syd-repl-mode))
|
||||
(when (boundp 'syd-repl-mode)
|
||||
(buffer-local-value 'syd-repl-mode (get-buffer bufname))))
|
||||
:ttl (lambda (buf)
|
||||
(unless (plist-get syd-repl-plist :persist)
|
||||
(when-let (process (get-buffer-process buf))
|
||||
(set-process-query-on-exit-flag process nil)
|
||||
(kill-process process)
|
||||
(kill-buffer buf))))
|
||||
:size 0.25
|
||||
:quit nil)
|
||||
|
||||
(provide 'syd-handle-repl)
|
||||
|
||||
@@ -12,9 +12,4 @@
|
||||
,todo
|
||||
(error ,todo))))
|
||||
|
||||
;; `on.el' provies a collection of utility hooks and functions ported from Doom
|
||||
;; Emacs. The hooks make it easier to speed up Emacs startup by providing
|
||||
;; finer-grained control of the timing at which packages are loaded.
|
||||
(use-package on)
|
||||
|
||||
(provide 'syd-prelude)
|
||||
|
||||
@@ -76,6 +76,8 @@ adjustment.")
|
||||
:init-value nil
|
||||
:global t
|
||||
:keymap doom-popup-mode-map
|
||||
(progn
|
||||
(message "doom-popup-mode: %S" doom-popup-mode)
|
||||
(cond (doom-popup-mode
|
||||
(add-hook 'doom-escape-hook #'doom-popup-close-on-escape-h 'append)
|
||||
(setq doom-popup--old-display-buffer-alist display-buffer-alist
|
||||
@@ -90,7 +92,7 @@ adjustment.")
|
||||
(doom-popup-cleanup-rules-h)
|
||||
(dolist (prop doom-popup-window-parameters)
|
||||
(delq (assq prop window-persistent-parameters)
|
||||
window-persistent-parameters)))))
|
||||
window-persistent-parameters))))))
|
||||
|
||||
(define-minor-mode doom-popup-buffer-mode
|
||||
"Minor mode for individual popup windows.
|
||||
|
||||
@@ -191,7 +191,7 @@ used.
|
||||
(setq display-buffer-alist doom-popup--display-buffer-alist))
|
||||
doom-popup--display-buffer-alist)
|
||||
|
||||
;;;###autodef
|
||||
;;;###autoload
|
||||
(defun set-popup-rules! (&rest rulesets)
|
||||
"Defines multiple popup rules.
|
||||
|
||||
|
||||
@@ -25,15 +25,6 @@
|
||||
|
||||
(require 'doom-popup-settings)
|
||||
|
||||
(use-package popper
|
||||
:custom ((popper-display-control nil)
|
||||
(popper-reference-buffers
|
||||
(list (lambda (buf)
|
||||
(with-current-buffer buf
|
||||
(bound-and-true-p doom-popup-mode))))))
|
||||
:config
|
||||
(popper-mode 1))
|
||||
|
||||
(defvar doom-popup--internal nil)
|
||||
|
||||
(defun doom-popup--remember (windows)
|
||||
|
||||
24
users/crumb/programs/emacs/modules/syd-popups.el
Normal file
24
users/crumb/programs/emacs/modules/syd-popups.el
Normal file
@@ -0,0 +1,24 @@
|
||||
;; syd-popups.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package popper
|
||||
:disabled
|
||||
:init
|
||||
(setq popper-display-control nil
|
||||
popper-reference-buffers
|
||||
(list (lambda (buf)
|
||||
(with-current-buffer buf
|
||||
(bound-and-true-p doom-popup-mode)))))
|
||||
:config
|
||||
(popper-mode 1))
|
||||
|
||||
(use-package doom-popup
|
||||
;; :after popper
|
||||
:load-path "/persist/dots/users/crumb/programs/emacs/modules/doom-popup"
|
||||
:straight nil
|
||||
;; :straight
|
||||
;; (:type nil
|
||||
;; :local-repo "/persist/dots/users/crumb/programs/emacs/modules/doom-popup")
|
||||
)
|
||||
|
||||
(provide 'syd-popups)
|
||||
;;; syd-popups.el ends here
|
||||
Reference in New Issue
Block a user