2019-04-21 19:59:44 -04:00
|
|
|
;;; tools/eval/autoload/repl.el -*- lexical-binding: t; -*-
|
2017-02-13 16:57:08 -05:00
|
|
|
|
2018-09-11 21:24:56 +01:00
|
|
|
(defvar +eval-repl-buffers (make-hash-table :test 'equal)
|
2017-03-04 18:28:51 -05:00
|
|
|
"The buffer of the last open repl.")
|
2017-02-13 16:57:08 -05:00
|
|
|
|
2019-10-23 21:39:19 -04:00
|
|
|
(defvar-local +eval-repl-plist nil)
|
|
|
|
|
2018-05-14 01:22:33 +02:00
|
|
|
(define-minor-mode +eval-repl-mode
|
|
|
|
"A minor mode for REPL buffers.")
|
|
|
|
|
2019-10-23 21:39:19 -04:00
|
|
|
(defun +eval--ensure-in-repl-buffer (&optional fn plist displayfn)
|
2018-09-11 21:24:56 +01:00
|
|
|
(maphash (lambda (key buffer)
|
|
|
|
(unless (buffer-live-p buffer)
|
|
|
|
(remhash key +eval-repl-buffers)))
|
|
|
|
+eval-repl-buffers)
|
2018-09-28 13:54:20 -04:00
|
|
|
(let* ((project-root (doom-project-root))
|
2018-07-01 01:10:51 +02:00
|
|
|
(key (cons major-mode project-root))
|
2018-09-11 21:24:56 +01:00
|
|
|
(buffer (gethash key +eval-repl-buffers)))
|
|
|
|
(cl-check-type buffer (or buffer null))
|
2019-10-24 16:55:25 -04:00
|
|
|
(unless (or (eq buffer (current-buffer))
|
|
|
|
(null fn))
|
|
|
|
(setq buffer
|
|
|
|
(funcall (or displayfn #'get-buffer-create)
|
|
|
|
(if (buffer-live-p buffer)
|
|
|
|
buffer
|
|
|
|
(setq buffer
|
|
|
|
(save-window-excursion
|
|
|
|
(if (commandp fn)
|
|
|
|
(call-interactively fn)
|
|
|
|
(funcall fn))))
|
|
|
|
(cond ((null buffer)
|
|
|
|
(error "REPL handler %S couldn't open the REPL buffer" fn))
|
|
|
|
((not (bufferp buffer))
|
|
|
|
(error "REPL handler %S failed to return a buffer" fn)))
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(when plist
|
|
|
|
(setq +eval-repl-plist plist))
|
|
|
|
(+eval-repl-mode +1))
|
|
|
|
(puthash key buffer +eval-repl-buffers)
|
|
|
|
buffer))))
|
|
|
|
(when (bufferp buffer)
|
|
|
|
(with-current-buffer 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)
|
|
|
|
(cdr comint-last-prompt))
|
|
|
|
(cdr comint-last-prompt)
|
|
|
|
(point-max)))))
|
2018-07-01 01:10:51 +02:00
|
|
|
buffer)))
|
2017-05-12 14:18:00 +02:00
|
|
|
|
2019-10-23 14:06:56 -04:00
|
|
|
(defun +eval-open-repl (prompt-p &optional displayfn)
|
2019-11-23 00:52:36 -05:00
|
|
|
(cl-destructuring-bind (_mode fn . plist)
|
2019-10-23 21:39:19 -04:00
|
|
|
(or (assq major-mode +eval-repls)
|
2020-06-19 14:24:09 +10:00
|
|
|
(list nil nil))
|
2019-10-23 21:39:19 -04:00
|
|
|
(when (or (not fn) prompt-p)
|
2019-02-18 01:56:38 -05:00
|
|
|
(let* ((choices (or (cl-loop for sym being the symbols
|
|
|
|
for sym-name = (symbol-name sym)
|
|
|
|
if (string-match "^\\(?:\\+\\)?\\([^/]+\\)/open-\\(?:\\(.+\\)-\\)?repl$" sym-name)
|
|
|
|
collect
|
|
|
|
(format "%s (%s)"
|
|
|
|
(match-string-no-properties 1 sym-name)
|
|
|
|
(or (match-string-no-properties 2 sym-name) "default")))
|
|
|
|
(user-error "There are no known available REPLs")))
|
|
|
|
(choice (or (completing-read "Open a REPL for: " choices)
|
|
|
|
(user-error "Aborting")))
|
|
|
|
(choice-split (split-string choice " " t))
|
|
|
|
(module (car choice-split))
|
|
|
|
(repl (substring (cadr choice-split) 1 -1)))
|
2019-10-23 21:39:19 -04:00
|
|
|
(setq fn
|
2019-02-18 01:56:38 -05:00
|
|
|
(intern-soft
|
|
|
|
(format "+%s/open-%srepl" module
|
|
|
|
(if (string= repl "default")
|
|
|
|
""
|
2021-03-13 12:17:42 +07:00
|
|
|
(concat repl "-")))))))
|
2019-10-23 21:39:19 -04:00
|
|
|
(let ((region (if (use-region-p)
|
|
|
|
(buffer-substring-no-properties (region-beginning)
|
|
|
|
(region-end)))))
|
|
|
|
(unless (commandp fn)
|
|
|
|
(error "Couldn't find a valid REPL for %s" major-mode))
|
|
|
|
(with-current-buffer (+eval--ensure-in-repl-buffer fn plist displayfn)
|
|
|
|
(when (bound-and-true-p evil-mode)
|
|
|
|
(call-interactively #'evil-append-line))
|
|
|
|
(when region
|
|
|
|
(insert region))
|
|
|
|
t))))
|
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;;; Commands
|
2019-02-18 01:56:38 -05:00
|
|
|
|
2017-02-13 16:57:08 -05:00
|
|
|
;;;###autoload
|
2019-02-18 01:56:38 -05:00
|
|
|
(defun +eval/open-repl-same-window (&optional arg)
|
2017-05-12 14:18:00 +02:00
|
|
|
"Opens (or reopens) the REPL associated with the current major-mode and place
|
2018-01-24 00:57:52 -05:00
|
|
|
the cursor at the prompt.
|
|
|
|
|
2019-02-18 01:56:38 -05:00
|
|
|
If ARG (universal argument), prompt for a specific REPL to open."
|
|
|
|
(interactive "P")
|
2019-10-23 14:06:56 -04:00
|
|
|
(+eval-open-repl arg #'switch-to-buffer))
|
2019-02-18 01:56:38 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +eval/open-repl-other-window (&optional arg)
|
|
|
|
"Does `+eval/open-repl', but in a popup window.
|
|
|
|
|
|
|
|
If ARG (universal argument), prompt for a specific REPL to open."
|
2018-01-24 00:57:52 -05:00
|
|
|
(interactive "P")
|
2019-10-23 14:06:56 -04:00
|
|
|
(+eval-open-repl arg #'pop-to-buffer))
|
2017-02-13 16:57:08 -05:00
|
|
|
|
2017-03-04 18:28:51 -05:00
|
|
|
;;;###autoload
|
2019-10-23 14:06:56 -04:00
|
|
|
(defun +eval/send-region-to-repl (beg end &optional inhibit-auto-execute-p)
|
|
|
|
"Execute the selected region in the REPL.
|
|
|
|
Opens a REPL if one isn't already open. If AUTO-EXECUTE-P, then execute it
|
|
|
|
immediately after."
|
|
|
|
(interactive "rP")
|
|
|
|
(let ((selection (buffer-substring-no-properties beg end))
|
|
|
|
(buffer (+eval--ensure-in-repl-buffer)))
|
|
|
|
(unless buffer
|
2017-05-12 14:18:00 +02:00
|
|
|
(error "No REPL open"))
|
2019-11-16 20:57:04 -05:00
|
|
|
(let ((origin-window (selected-window))
|
|
|
|
(selection
|
|
|
|
(with-temp-buffer
|
|
|
|
(insert selection)
|
|
|
|
(goto-char (point-min))
|
|
|
|
(when (> (skip-chars-forward "\n") 0)
|
|
|
|
(delete-region (point-min) (point)))
|
|
|
|
(indent-rigidly (point) (point-max)
|
|
|
|
(- (skip-chars-forward " \t")))
|
2019-11-17 01:15:51 -05:00
|
|
|
(concat (string-trim-right (buffer-string))
|
|
|
|
"\n"))))
|
2019-11-16 20:57:04 -05:00
|
|
|
(with-selected-window (get-buffer-window buffer)
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(dolist (line (split-string selection "\n"))
|
|
|
|
(insert line)
|
|
|
|
(if inhibit-auto-execute-p
|
|
|
|
(insert "\n")
|
2021-03-28 00:32:27 -04:00
|
|
|
;; Can't use `comint-send-input' b/c there's no guarantee the
|
|
|
|
;; current REPL uses comint. Even if it did, no telling if they
|
|
|
|
;; have their own `comint-send-input' wrapper, so to be safe, I
|
|
|
|
;; simply emulate the keypress.
|
|
|
|
(call-interactively (doom-lookup-key (kbd "RET"))))
|
2019-11-16 20:57:04 -05:00
|
|
|
(sit-for 0.001)
|
|
|
|
(redisplay 'force)))
|
|
|
|
(when (and (eq origin-window (selected-window))
|
|
|
|
(bound-and-true-p evil-local-mode))
|
|
|
|
(call-interactively #'evil-append-line))))))
|