diff --git a/modules/feature/eval/autoload/build.el b/modules/feature/eval/autoload/build.el new file mode 100644 index 000000000..489bf3fdc --- /dev/null +++ b/modules/feature/eval/autoload/build.el @@ -0,0 +1,13 @@ +;;; feature/repl/autoload/build.el + +;; TODO + +;;;###autoload +(defun +eval/build ()) + +;;;###autoload +(defun +eval/rebuild ()) + +;;;###autoload +(defun +eval-get-builder ()) + diff --git a/modules/feature/eval/autoload/eval.el b/modules/feature/eval/autoload/eval.el new file mode 100644 index 000000000..edce543d2 --- /dev/null +++ b/modules/feature/eval/autoload/eval.el @@ -0,0 +1,70 @@ +;;; feature/repl/autoload/eval.el + +;;;###autoload +(defun +repl/eval-buffer () + "Evaluate the whole buffer." + (interactive) + (cond ((eq major-mode 'emacs-lisp-mode) + (+repl/eval-region (point-min) (point-max))) + (t (quickrun)))) + +;;;###autoload +(defun +repl/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") + (let ((load-file-name buffer-file-name)) + (cond ((eq major-mode 'emacs-lisp-mode) + (require 'pp) + (let ((result (eval (read (buffer-substring-no-properties beg end)))) + lines) + (let ((buf (get-buffer-create "*eval*"))) + (with-current-buffer buf + ;; FIXME messy! + (read-only-mode -1) + (setq-local scroll-margin 0) + (erase-buffer) + (prin1 result buf) + (emacs-lisp-mode) + (pp-buffer) + (read-only-mode 1) + (setq lines (count-lines (point-min) (point-max))) + (goto-char (point-min)) + (when (< lines 5) + (message "%s" (buffer-substring (point-min) (point-max))) + (kill-buffer buf))) + (unless (< lines 5) + (doom-popup-buffer buf))))) + (t (quickrun-region beg end))))) + +;;;###autoload +(defun +repl/eval-region-and-replace (beg end) + (interactive "r") + (cond ((eq major-mode 'emacs-lisp-mode) + (kill-region beg end) + (condition-case nil + (prin1 (eval (read (current-kill 0))) + (current-buffer)) + (error (message "Invalid expression") + (insert (current-kill 0))))) + (t (quickrun-replace-region beg end)))) + + +;;;###autoload (autoload '+repl:eval-region "feature/repl/autoload/repl" nil t) +;;;###autoload (autoload '+repl:eval-region-and-replace "feature/repl/autoload/eval" nil t) + +(@after evil + (evil-set-command-properties '+repl/eval-buffer :move-point nil :repeat nil) + (evil-set-command-properties '+repl/eval-region :move-point nil :repeat nil) + + (evil-define-operator +repl: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." + :move-point nil :repeat nil + (interactive "") + (+repl/eval-region beg end)) + + (evil-define-operator +repl:eval-region-and-replace (beg end) + (interactive "") + (+repl/eval-region-and-replace beg end))) + diff --git a/modules/feature/eval/autoload/repl.el b/modules/feature/eval/autoload/repl.el new file mode 100644 index 000000000..3bccbf71d --- /dev/null +++ b/modules/feature/eval/autoload/repl.el @@ -0,0 +1,45 @@ +;;; feature/repl/autoload/repl.el + +;;;###autoload +(defun +repl/open () + (interactive) + ;; TODO + (error "Not implemented")) + +;;;###autoload +(defun +repl/send () + (interactive) + ;; TODO + (error "Not implemented")) + +;;;###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 new file mode 100644 index 000000000..90cc99e1f --- /dev/null +++ b/modules/feature/eval/config.el @@ -0,0 +1,106 @@ +;;; feature/repl/config.el + +;; + Running inline code using `quickrun' +;; + REPLs using `repl-toggle' + +(defvar doom-repl-buffer nil + "The current REPL buffer.") + +(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--runners nil + "A list of `quickrun-add-command' arguments.") + +(defvar +eval--repls nil + "A list of `rtog/add-repl' arguments.") + +;; remove ellipsis when printing sexp 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 same arguements as `rtog/add-repl'." + (if (featurep 'repl-toggle) + (list 'rtog/add-repl mode command) + `(push ',(list mode command) +eval--repls))) + +(@def-setting :build (name mode pred-fn 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." + `(puthash ',(cons name mode) + (list :predicate ,pred-fn :fn ,build-fn) + +eval-builders)) + +(@def-setting :eval (name alist &rest plist) + "Define a code evaluator for `quickrun'. Takes the same arguments as +`quickrun-add-command'." + (if (featurep 'quickrun) + (apply 'quickrun-add-command name alist plist) + `(push ',(list name alist plist) +eval--runners))) + + +;; +;; Packages +;; + +(@def-package quickrun + :commands (quickrun + quickrun-region + quickrun-with-arg + quickrun-shell + quickrun-compile-only + quickrun-replace-region) + :init (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) + + (dolist (runner +eval--runners) + (apply 'quickrun-add-command runner)) + + (defun +repl*quickrun-close-popup (&optional _ _ _ _) + "Allows us to re-run quickrun from inside the quickrun buffer (silently)." + (awhen (get-buffer-window quickrun/buffer-name) + (let (message-log-max) + (quickrun/kill-running-process) + (message "")) + (doom/popup-close it nil))) + + (defun +repl|quickrun-scroll-to-bof () + "Ensures window is scrolled to BOF" + (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) + :init (@add-hook repl-toggle-mode (evil-initialize-state 'emacs)) + :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)))) + :popup t :size 16) + + (dolist (repl +eval--repls) + (apply 'rtog/add-repl repl)) + + (@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)) + diff --git a/modules/feature/eval/packages.el b/modules/feature/eval/packages.el new file mode 100644 index 000000000..fb82835e8 --- /dev/null +++ b/modules/feature/eval/packages.el @@ -0,0 +1,6 @@ +;; -*- no-byte-compile: t; -*- +;;; feature/repl/packages.el + +(@package quickrun) +(@package repl-toggle) +