diff --git a/modules/feature/eval/autoload/build.el b/modules/feature/eval/autoload/build.el index 489bf3fdc..a39aa8cee 100644 --- a/modules/feature/eval/autoload/build.el +++ b/modules/feature/eval/autoload/build.el @@ -1,13 +1,31 @@ -;;; feature/repl/autoload/build.el +;;; feature/eval/autoload/build.el -;; TODO +(defvar-local +eval-last-builder nil + "The last builder run in the current buffer.") + +(defun +eval--read-builder () + (let ((builders (cl-remove-if-not + (lambda (plist) + (when-let (pred (plist-get (cdr plist) :when)) + (eval pred))) + (cdr (assq major-mode +eval-builders))))) + (completing-read + "Build: " + (mapcar 'car builders) + nil t))) ;;;###autoload -(defun +eval/build ()) +(defun +eval/build (&optional builder) + (interactive (list (+eval--read-builder))) + (unless builder + (error "No builder for this buffer")) + (let ((desc (assq builder (assq major-mode +eval-builders)))) + (unless desc + (error "Builder not found in registered builders")) + (message "Running %s" builder))) ;;;###autoload -(defun +eval/rebuild ()) - -;;;###autoload -(defun +eval-get-builder ()) +(defun +eval/rebuild (&optional builder) + (interactive (list +eval-last-builder)) + (+eval/build +eval-last-builder)) diff --git a/modules/feature/eval/autoload/eval.el b/modules/feature/eval/autoload/eval.el index c31e9878f..644382275 100644 --- a/modules/feature/eval/autoload/eval.el +++ b/modules/feature/eval/autoload/eval.el @@ -1,15 +1,15 @@ -;;; feature/repl/autoload/eval.el +;;; feature/eval/autoload/eval.el ;;;###autoload -(defun +repl/eval-buffer () +(defun +eval/buffer () "Evaluate the whole buffer." (interactive) (cond ((eq major-mode 'emacs-lisp-mode) - (+repl/eval-region (point-min) (point-max))) + (+eval/region (point-min) (point-max))) (t (quickrun)))) ;;;###autoload -(defun +repl/eval-region (beg end) +(defun +eval/region (beg end) "Evaluate a region and, if large enough, prints its output to a popup buffer (if an elisp buffer). Otherwise forward the region to Quickrun." (interactive "r") @@ -24,8 +24,8 @@ elisp buffer). Otherwise forward the region to Quickrun." (read-only-mode -1) (setq-local scroll-margin 0) (erase-buffer) + (set-syntax-table emacs-lisp-mode-syntax-table) (prin1 result buf) - (emacs-lisp-mode) (pp-buffer) (read-only-mode 1) (setq lines (count-lines (point-min) (point-max))) @@ -38,7 +38,7 @@ elisp buffer). Otherwise forward the region to Quickrun." (t (quickrun-region beg end))))) ;;;###autoload -(defun +repl/eval-region-and-replace (beg end) +(defun +eval/region-and-replace (beg end) (interactive "r") (cond ((eq major-mode 'emacs-lisp-mode) (kill-region beg end) diff --git a/modules/feature/eval/autoload/evil.el b/modules/feature/eval/autoload/evil.el index 77a1e8c50..1d52590f7 100644 --- a/modules/feature/eval/autoload/evil.el +++ b/modules/feature/eval/autoload/evil.el @@ -1,14 +1,14 @@ ;;; feature/eval/autoload/evil.el -;;;###autoload (autoload '+repl:eval-region "feature/eval/autoload/evil" nil t) -(evil-define-operator +repl:eval-region (beg end) +;;;###autoload (autoload '+eval:region "feature/eval/autoload/evil" nil t) +(evil-define-operator +eval:region (beg end) + "Send region to the currently open repl, if available." :move-point nil (interactive "") - (+repl/eval-region beg end)) + (+eval/region beg end)) -;;;###autoload (autoload '+repl:eval-region-and-replace "feature/eval/autoload/evil" nil t) -(evil-define-operator +repl:eval-region-and-replace (beg end) +;;;###autoload (autoload '+eval:replace-region "feature/eval/autoload/evil" nil t) +(evil-define-operator +eval:replace-region (beg end) :move-point nil (interactive "") - (+repl/eval-region-and-replace beg end)) - + (+eval/region-and-replace beg end)) diff --git a/modules/feature/eval/autoload/repl.el b/modules/feature/eval/autoload/repl.el index 2345baa7e..870bbce08 100644 --- a/modules/feature/eval/autoload/repl.el +++ b/modules/feature/eval/autoload/repl.el @@ -1,45 +1,30 @@ -;;; feature/repl/autoload/repl.el +;;; feature/eval/autoload/repl.el + +(defvar +eval-last-repl-buffer nil + "The buffer of the last open repl.") ;;;###autoload -(defun +repl/open () +(defun +eval/repl () + "Open the REPL associated with the current major-mode. If selection is active, +send region to repl." (interactive) - ;; TODO - (error "Not implemented")) + (when-let (command (cdr (assq major-mode +eval-repls))) + (let ((repl-buffer (save-window-excursion (funcall command)))) + (unless (bufferp repl-buffer) + (error "REPL command didn't return a buffer")) + (with-current-buffer repl-buffer (+eval-repl-mode +1)) + (setq +eval-last-repl-buffer repl-buffer) + (doom-popup-buffer repl-buffer)))) ;;;###autoload -(defun +repl/send () - (interactive) - ;; TODO - (error "Not implemented")) +(defun +eval/repl-send-region (beg end &optional inhibit-run-p) + "Send a selection to the REPL." + (interactive "r") + (when +eval-last-repl-buffer + (let ((selection (buffer-substring-no-properties beg end))) + (select-window (get-buffer-window +eval-last-repl-buffer)) + (goto-char (point-max)) + (insert selection) + (when (and (featurep 'evil) evil-mode) + (evil-change-state 'insert))))) -;;;###autoload (autoload '+repl:open "feature/repl/autoload/repl" nil t) -;;;###autoload (autoload '+repl:send "feature/repl/autoload/repl" nil t) - -(after! evil - (evil-define-command +repl:open (&optional bang command) - :repeat nil - (interactive "") - (if (and doom-repl-buffer (buffer-live-p doom-repl-buffer)) - (doom-popup-buffer doom-repl-buffer) - (rtog/toggle-repl (if (use-region-p) 4)) - (setq doom-repl-buffer (current-buffer)) - (when command - (with-current-buffer doom-repl-buffer - (insert command) - (unless bang (comint-send-input)))))) - - (evil-define-operator +repl:eval (&optional beg end bang) - :type inclusive :repeat nil - (interactive "") - (let ((region-p (use-region-p)) - (selection (s-trim (buffer-substring-no-properties beg end)))) - (doom:repl bang) - (when (and region-p beg end) - (let* ((buf doom-repl-buffer) - (win (get-buffer-window buf))) - (unless (eq buf (doom-popup-p (get-buffer-window buf))) - (doom-popup-buffer buf)) - (when (and doom-repl-buffer (buffer-live-p doom-repl-buffer)) - (with-current-buffer doom-repl-buffer - (goto-char (point-max)) - (insert selection)))))))) diff --git a/modules/feature/eval/config.el b/modules/feature/eval/config.el index bd5ba6c3c..0d757bebc 100644 --- a/modules/feature/eval/config.el +++ b/modules/feature/eval/config.el @@ -1,60 +1,80 @@ -;;; feature/repl/config.el +;;; feature/eval/config.el -;; + Running inline code using `quickrun' -;; + REPLs using `repl-toggle' +;; This module creates a centralized way of invoking/sending selections to REPLs +;; (with `repl-toggle'), building projects/files, and running code (with +;; `quickrun'), revealing its output in a popup window. -(defvar doom-repl-buffer nil - "The current REPL buffer.") +;; +;; Code building +;; -(defvar +eval-builders (make-hash-table :test 'equal) - "A hash-table of plists, containing functions for building source code. Used -by `+eval/build', and filled with the `:build' setting") +(defvar +eval-builders nil + "A nested alist, mapping major modes to build function plists. Used by +`+eval/build' and filled with the `:build' setting.") -;; remove ellipsis when printing sexp in message buffer +(def-setting! :build (name mode fn &rest plist) + "Define a build function (FN) for MODE (can be major or minor) called NAME. + +PLIST accepts the following properties: + + :when FORM A predicate to determine if the builder is appropriate for this + buffer." + `(progn + (unless (assq ',mode +eval-builders) + (push (list ',mode nil) +eval-builders)) + (push (list ',name :fn #',fn ,@plist) + (cdr (assq ',mode +eval-builders))))) + + +;; +;; REPLs +;; + +(defvar +eval-repls nil + "An alist mapping major modes to plists that describe REPLs. Used by +`+eval/repl' and filled with the `:repl' setting.") + +(define-minor-mode +eval-repl-mode + "A minor mode for REPL buffers." + :init-value nil) + +(def-setting! :repl (mode command) + "Define a REPL for a mode. MODE is a major mode and COMMAND is a function that +invokes the repl. Takes the same arguements as `rtog/add-repl'." + `(push ',(cons mode command) +eval-repls)) + +(set! :popup + '(:custom (lambda (b &rest _) (buffer-local-value '+eval-repl-mode b))) + :size 16 :noesc t) + + +;; +;; Evaluation +;; + +;; remove ellipsis when printing sexps in message buffer (setq eval-expression-print-length nil eval-expression-print-level nil) -(def-setting! :repl (mode command) - "Define a REPL for a mode. Takes the same arguements as `rtog/add-repl'." - `(after! repl-toggle - (rtog/add-repl ',mode ',command))) - -(def-setting! :build (name mode pred-fn &optional build-fn) - "Define a build command function (BUILD-FN) for major-mode MODE, called NAME --- a symbol -- PRED-FN is a predicate function that determines this builder's -suitability for the current buffer." - (unless build-fn - (setq build-fn pred-fn - pred-fn nil)) - `(puthash ',(cons name mode) - (list :predicate ,pred-fn :fn ,build-fn) - +eval-builders)) - (def-setting! :eval (mode command) - "Define a code evaluator for `quickrun'. + "Define a code evaluator for major mode MODE with `quickrun'. 1. If MODE is a string and COMMAND is the string, MODE is a file regexp and COMMAND is a string key for an entry in `quickrun-file-alist'. 2. If MODE is not a string and COMMAND is a string, MODE is a major-mode symbol - and COMMAND is a key; they will be registered in - `quickrun--major-mode-alist'. -3. If MODE is not a string and COMMAND is a list, use `quickrun-add-command'. e.g. + and COMMAND is a key (for `quickrun--language-alist'), and will be registered + in `quickrun--major-mode-alist'. +3. If MODE is not a string and COMMAND is an alist, see `quickrun-add-command': (quickrun-add-command MODE COMMAND :mode MODE)" - (if (stringp command) - `(after! quickrun - (push ',(cons mode command) - ,(if (stringp mode) - 'quickrun-file-alist - 'quickrun--major-mode-alist))) - `(after! quickrun - (quickrun-add-command - ,(symbol-name mode) - ',command :mode ',mode)))) - - -;; -;; Packages -;; + `(after! quickrun + ,(if (stringp command) + `(push ',(cons mode command) + ,(if (stringp mode) + 'quickrun-file-alist + 'quickrun--major-mode-alist)) + `(quickrun-add-command + ,(symbol-name mode) + ',command :mode ',mode)))) (def-package! quickrun :commands (quickrun @@ -64,47 +84,29 @@ suitability for the current buffer." quickrun-compile-only quickrun-replace-region) :init + ;; Use standard linum-mode for quickrun eval windows; so we can have different + ;; rules for it. (add-hook 'quickrun/mode-hook 'linum-mode) + :config (set! :popup "*quickrun*" :size 10) ;; don't auto-focus quickrun windows. Shackle handles that for us. (setq quickrun-focus-p nil) - (defun +repl*quickrun-close-popup (&optional _ _ _ _) + (defun +eval*quickrun-auto-close (&rest _) "Allows us to silently re-run quickrun from within the quickrun buffer." - (awhen (get-buffer-window quickrun/buffer-name) - (let (message-log-max) - (quickrun/kill-running-process) + (when-let (win (get-buffer-window quickrun--buffer-name)) + (let ((inhibit-message t)) + (quickrun--kill-running-process) (message "")) - (doom/popup-close it))) + (delete-window win))) + (advice-add 'quickrun :before '+eval*quickrun-auto-close) + (advice-add 'quickrun-region :before '+eval*quickrun-auto-close) - (defun +repl|quickrun-scroll-to-bof () + (defun +eval|quickrun-scroll-to-bof () "Ensures window is scrolled to BOF on invocation." - (with-selected-window (get-buffer-window quickrun/buffer-name) + (with-selected-window (get-buffer-window quickrun--buffer-name) (goto-char (point-min)))) - - ;;; Popup hacks - (advice-add 'quickrun :before '+repl*quickrun-close-popup) - (advice-add 'quickrun-region :before '+repl*quickrun-close-popup) - ;; Ensures window is scrolled to BOF - (add-hook 'quickrun-after-run-hook '+repl|quickrun-scroll-to-bof)) - - -(def-package! repl-toggle - :commands rtog/toggle-repl - :preface (defvar rtog/mode-repl-alist nil) - :config - (set! :popup - '(:custom (lambda (b &rest _) - (when (and (featurep 'repl-toggle) - (string-prefix-p "*" (buffer-name (get-buffer b)))) - (buffer-local-value 'repl-toggle-mode b)))) - :size 16) - - (map! :map repl-toggle-mode-map - :ei "C-n" 'comint-next-input - :ei "C-p" 'comint-previous-input - :ei "" 'comint-next-input - :ei "" 'comint-previous-input)) + (add-hook 'quickrun-after-run-hook '+eval|quickrun-scroll-to-bof)) diff --git a/modules/feature/eval/packages.el b/modules/feature/eval/packages.el index f02a86bce..d3c231de6 100644 --- a/modules/feature/eval/packages.el +++ b/modules/feature/eval/packages.el @@ -1,6 +1,5 @@ ;; -*- no-byte-compile: t; -*- -;;; feature/repl/packages.el +;;; feature/eval/packages.el (package! quickrun) -(package! repl-toggle)