tools/eval: associate plist with repl handlers

Also consolidates all REPLs (opened through the :tools eval module)
under one popup rule, which inhibits ESC from prematurely closing
them (#1944), and cleans up after their buffers *only* if their handlers
weren't specified to :persist, e.g.

  (set-repl-handler! 'some-mode #'some-repl-handler :persist t)

Also standardized ESS's REPL commands.
This commit is contained in:
Henrik Lissner 2019-10-23 21:39:19 -04:00
parent e6094f262f
commit 0b67251159
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
9 changed files with 71 additions and 47 deletions

View file

@ -17,8 +17,8 @@
:hook (clojure-mode-local-vars . cider-mode) :hook (clojure-mode-local-vars . cider-mode)
:init :init
(after! clojure-mode (after! clojure-mode
(set-repl-handler! 'clojure-mode #'+clojure/open-repl) (set-repl-handler! 'clojure-mode #'+clojure/open-repl :persist t)
(set-repl-handler! 'clojurescript-mode #'+clojure/open-cljs-repl) (set-repl-handler! 'clojurescript-mode #'+clojure/open-cljs-repl :persist t)
(set-eval-handler! '(clojure-mode clojurescript-mode) #'cider-eval-region)) (set-eval-handler! '(clojure-mode clojurescript-mode) #'cider-eval-region))
:config :config
(add-hook 'cider-mode-hook #'eldoc-mode) (add-hook 'cider-mode-hook #'eldoc-mode)

View file

@ -1,11 +1,15 @@
;;; lang/ess/autoload.el -*- lexical-binding: t; -*- ;;; lang/ess/autoload.el -*- lexical-binding: t; -*-
;;;###autoload ;;;###autoload
(defun +ess-repl-buffer (&optional start-args) (defun +ess/open-julia-repl (&optional arg)
"Returns an R/Julia REPL buffer." "Open an ESS Julia REPL"
(interactive "P") (interactive "P")
(pcase major-mode (run-ess-julia arg)
('ess-r-mode (run-ess-r start-args)) (current-buffer))
('ess-julia-mode (run-ess-julia start-args))
(_ (inferior-ess nil nil t))) ;;;###autoload
(defun +ess/open-r-repl (&optional arg)
"Open an ESS R REPL"
(interactive "P")
(run-ess-r arg)
(current-buffer)) (current-buffer))

View file

@ -21,7 +21,8 @@
ess-default-style 'DEFAULT ess-default-style 'DEFAULT
ess-history-directory (expand-file-name "ess-history/" doom-cache-dir)) ess-history-directory (expand-file-name "ess-history/" doom-cache-dir))
(set-repl-handler! '(ess-r-mode ess-julia-mode) #'+ess-repl-buffer) (set-repl-handler! 'ess-r-mode #'+ess/open-r-repl)
(set-repl-handler! 'ess-julia-mode #'+ess/open-julia-repl)
(set-lookup-handlers! '(ess-r-mode ess-julia-mode) (set-lookup-handlers! '(ess-r-mode ess-julia-mode)
:documentation #'ess-display-help-on-object) :documentation #'ess-display-help-on-object)

View file

@ -21,19 +21,9 @@
(set-file-template! 'haskell-mode (set-file-template! 'haskell-mode
:trigger #'haskell-auto-insert-module-template :trigger #'haskell-auto-insert-module-template
:project t) :project t)
(set-repl-handler! '(haskell-mode (set-repl-handler!
haskell-cabal-mode '(haskell-mode haskell-cabal-mode literate-haskell-mode)
literate-haskell-mode) #'+haskell/open-repl :persist t)
#'+haskell/open-repl)
;; Prevent the 'Kill the whole session (y or n)?' prompt caused by the popup
;; manager auto-killing haskell-interactive-mode's popup buffer (and process)
;; by settings :ttl to nil.
(set-popup-rule!
(lambda (bname _action)
(eq (buffer-local-value 'major-mode (get-buffer bname))
'haskell-interactive-mode))
:select t :ttl nil :quit nil)
(add-hook! 'haskell-mode-hook (add-hook! 'haskell-mode-hook
#'haskell-collapse-mode ; support folding haskell code blocks #'haskell-collapse-mode ; support folding haskell code blocks

View file

@ -21,7 +21,7 @@ called.")
(setq python-environment-directory doom-cache-dir (setq python-environment-directory doom-cache-dir
python-indent-guess-indent-offset-verbose nil) python-indent-guess-indent-offset-verbose nil)
:config :config
(set-repl-handler! 'python-mode #'+python/open-repl) (set-repl-handler! 'python-mode #'+python/open-repl :persist t)
(set-docsets! 'python-mode "Python 3" "NumPy" "SciPy") (set-docsets! 'python-mode "Python 3" "NumPy" "SciPy")
(set-pretty-symbols! 'python-mode (set-pretty-symbols! 'python-mode

View file

@ -10,7 +10,6 @@
(use-package! racket-mode (use-package! racket-mode
:hook (racket-repl-mode . racket-unicode-input-method-enable) :hook (racket-repl-mode . racket-unicode-input-method-enable)
:config :config
(set-popup-rule! "^\\*Racket REPL" :size 10 :select t)
(set-repl-handler! 'racket-mode #'+racket/open-repl) (set-repl-handler! 'racket-mode #'+racket/open-repl)
(set-lookup-handlers! 'racket-mode (set-lookup-handlers! 'racket-mode
:definition #'racket-visit-definition :definition #'racket-visit-definition

View file

@ -3,10 +3,12 @@
(defvar +eval-repl-buffers (make-hash-table :test 'equal) (defvar +eval-repl-buffers (make-hash-table :test 'equal)
"The buffer of the last open repl.") "The buffer of the last open repl.")
(defvar-local +eval-repl-plist nil)
(define-minor-mode +eval-repl-mode (define-minor-mode +eval-repl-mode
"A minor mode for REPL buffers.") "A minor mode for REPL buffers.")
(defun +eval--ensure-in-repl-buffer (&optional command displayfn) (defun +eval--ensure-in-repl-buffer (&optional fn plist displayfn)
(maphash (lambda (key buffer) (maphash (lambda (key buffer)
(unless (buffer-live-p buffer) (unless (buffer-live-p buffer)
(remhash key +eval-repl-buffers))) (remhash key +eval-repl-buffers)))
@ -21,14 +23,16 @@
buffer buffer
(setq buffer (setq buffer
(save-window-excursion (save-window-excursion
(if (commandp command) (if (commandp fn)
(call-interactively command) (call-interactively fn)
(funcall command)))) (funcall fn))))
(cond ((null buffer) (cond ((null buffer)
(error "REPL handler %S couldn't open the REPL buffer" command)) (error "REPL handler %S couldn't open the REPL buffer" fn))
((not (bufferp buffer)) ((not (bufferp buffer))
(error "REPL handler %S failed to return a buffer" command))) (error "REPL handler %S failed to return a buffer" fn)))
(with-current-buffer buffer (with-current-buffer buffer
(when plist
(setq +eval-repl-plist plist))
(+eval-repl-mode +1)) (+eval-repl-mode +1))
(puthash key buffer +eval-repl-buffers) (puthash key buffer +eval-repl-buffers)
buffer))) buffer)))
@ -42,11 +46,10 @@
buffer))) buffer)))
(defun +eval-open-repl (prompt-p &optional displayfn) (defun +eval-open-repl (prompt-p &optional displayfn)
(let ((command (cdr (assq major-mode +eval-repls))) (cl-destructuring-bind (mode fn . plist)
(region (if (use-region-p) (or (assq major-mode +eval-repls)
(buffer-substring-no-properties (region-beginning) (list))
(region-end))))) (when (or (not fn) prompt-p)
(when (or (not command) prompt-p)
(let* ((choices (or (cl-loop for sym being the symbols (let* ((choices (or (cl-loop for sym being the symbols
for sym-name = (symbol-name sym) for sym-name = (symbol-name sym)
if (string-match "^\\(?:\\+\\)?\\([^/]+\\)/open-\\(?:\\(.+\\)-\\)?repl$" sym-name) if (string-match "^\\(?:\\+\\)?\\([^/]+\\)/open-\\(?:\\(.+\\)-\\)?repl$" sym-name)
@ -60,20 +63,27 @@
(choice-split (split-string choice " " t)) (choice-split (split-string choice " " t))
(module (car choice-split)) (module (car choice-split))
(repl (substring (cadr choice-split) 1 -1))) (repl (substring (cadr choice-split) 1 -1)))
(setq command (setq fn
(intern-soft (intern-soft
(format "+%s/open-%srepl" module (format "+%s/open-%srepl" module
(if (string= repl "default") (if (string= repl "default")
"" ""
repl)))))) repl))))))
(unless (commandp command) (let ((region (if (use-region-p)
(error "Couldn't find a valid REPL for %s" major-mode)) (buffer-substring-no-properties (region-beginning)
(with-current-buffer (+eval--ensure-in-repl-buffer command displayfn) (region-end)))))
(when (bound-and-true-p evil-mode) (unless (commandp fn)
(call-interactively #'evil-append-line)) (error "Couldn't find a valid REPL for %s" major-mode))
(when region (with-current-buffer (+eval--ensure-in-repl-buffer fn plist displayfn)
(insert region)) (when (bound-and-true-p evil-mode)
t))) (call-interactively #'evil-append-line))
(when region
(insert region))
t))))
;;
;;; Commands
;;;###autoload ;;;###autoload
(defun +eval/open-repl-same-window (&optional arg) (defun +eval/open-repl-same-window (&optional arg)

View file

@ -8,7 +8,7 @@
`+eval/open-repl-other-window' and filled with the `:repl' setting.") `+eval/open-repl-other-window' and filled with the `:repl' setting.")
;;;###autodef ;;;###autodef
(defun set-repl-handler! (modes command) (defun set-repl-handler! (modes command &rest plist)
"Defines a REPL for MODES. "Defines a REPL for MODES.
MODES is either a single major mode symbol or a list of them. COMMAND is a MODES is either a single major mode symbol or a list of them. COMMAND is a
@ -16,10 +16,17 @@ function that creates and returns the REPL buffer.
COMMAND can either be a function that takes no arguments, or an interactive 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 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." 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."
(declare (indent defun)) (declare (indent defun))
(dolist (mode (doom-enlist modes)) (dolist (mode (doom-enlist modes))
(setf (alist-get mode +eval-repls) command))) (setf (alist-get mode +eval-repls)
(cons command plist))))
;; ;;

View file

@ -8,6 +8,19 @@
;; ;;
;; Packages ;; Packages
(set-popup-rule!
(lambda (bufname _)
(when (boundp '+eval-repl-mode)
(buffer-local-value '+eval-repl-mode (get-buffer bufname))))
:ttl (lambda (buf)
(unless (plist-get +eval-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)
(after! quickrun (after! quickrun
(setq quickrun-focus-p nil) (setq quickrun-focus-p nil)