diff --git a/CHANGELOG.md b/CHANGELOG.md index fd149c183..d6864b8af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### New features +* [#1545](https://github.com/clojure-emacs/cider/pull/1545): New feature: Enlighten. See the new Readme section for more information. * [#1169](https://github.com/clojure-emacs/cider/pull/1169): New command `cider-eval-defun-to-comment`. * Change default value of `cider-overlays-use-font-lock` to `t`. Unlike before, a value of `t`, causes `cider-result-overlay-face` is to be prepended to the font-lock faces (instead of just not being used). * `cider-result-overlay-face` default value changed to a background and a box, so it can be prepended to other faces without overriding the foreground. diff --git a/README.md b/README.md index 234ad3200..518e28732 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ specific CIDER release.** - [Value Inspection](#value-inspection) - [Running Tests](#running-tests) - [Navigating Stacktraces](#navigating-stacktraces) + - [Enlighten (display local values)](#enlighten-display-local-values) - [Debugging](#debugging) - [Code reloading](#code-reloading) - [Managing multiple connections](#managing-multiple-connections) @@ -808,6 +809,26 @@ will be used. (setq cider-stacktrace-fill-column 80) ``` +### Enlighten (display local values) + +This feature displays the value of locals in realtime, as your code is being +executed. This is somewhat akin to one of the features of the Light Table +editor. + +- To turn it on, issue `M-x cider-enlighten-mode`. +- To use it, evaluate your functions one at a time (e.g., use `C-M-x` or `C-x C-e`, because `C-c C-k` won't work). + +That's it! Once your code executes, the regular old buffer on the left will turn +into the brilliant show of lights on the right. + +

+ + +

+ +You can also trigger this on specific functions (without having to turn on the +minor mode) by writing `#light` before the `(def` and reevaluating it. + ### Debugging The debugger can be invoked in several ways, the simplest one is to type diff --git a/cider-debug.el b/cider-debug.el index 81430f5b8..08c5364f0 100644 --- a/cider-debug.el +++ b/cider-debug.el @@ -63,6 +63,23 @@ :group 'cider-debug :package-version '(cider . "0.10.0")) +(defface cider-enlightened + '((((class color) (background light)) :inherit cider-result-overlay-face + :box (:color "darkorange" :line-width -1)) + (((class color) (background dark)) :inherit cider-result-overlay-face + ;; "#dd0" is a dimmer yellow. + :box (:color "#dd0" :line-width -1))) + "Face used to mark enlightened sexps and their return values." + :group 'cider-debug + :package-version '(cider . "0.11.0")) + +(defface cider-enlightened-local + '((((class color) (background light)) :weight bold :foreground "darkorange") + (((class color) (background dark)) :weight bold :foreground "yellow")) + "Face used to mark enlightened locals (not their values)." + :group 'cider-debug + :package-version '(cider . "0.11.0")) + (defcustom cider-debug-prompt 'overlay "If and where to show the keys while debugging. If `minibuffer', show it in the minibuffer along with the return value. @@ -127,6 +144,8 @@ This variable must be set before starting the repl connection." (defun cider--debug-response-handler (response) "Handle responses from the cider.debug middleware." (nrepl-dbind-response response (status id causes) + (when (member "enlighten" status) + (cider--handle-enlighten response)) (when (or (member "eval-error" status) (member "stack" status)) ;; TODO: Make the error buffer a bit friendlier when we're just printing @@ -585,6 +604,36 @@ needed. It is expected to contain at least \"key\", \"input-type\", and (error (cider-debug-mode-send-reply ":quit" key) (message "Error encountered while handling the debug message: %S" e))))) +(defun cider--handle-enlighten (response) + "Handle an enlighten notification. +RESPONSE is a message received from the nrepl describing the value and +coordinates of a sexp. Create an overlay after the specified sexp +displaying its value." + (when-let ((marker (cider--debug-find-source-position response))) + (with-current-buffer (marker-buffer marker) + (save-excursion + (goto-char marker) + (clojure-backward-logical-sexp 1) + (nrepl-dbind-response response (debug-value erase-previous) + (when erase-previous + (remove-overlays (point) marker 'cider-type 'enlighten)) + (when debug-value + (if (memq (char-before marker) '(?\) ?\] ?})) + ;; Enlightening a sexp looks like a regular return value, except + ;; for a different border. + (cider--make-result-overlay (cider-font-lock-as-clojure debug-value) + :where (cons marker marker) + :type 'enlighten + :prepend-face 'cider-enlightened) + ;; Enlightening a symbol uses a more abbreviated format. The + ;; result face is the same as a regular result, but we also color + ;; the symbol with `cider-enlightened-local'. + (cider--make-result-overlay (cider-font-lock-as-clojure debug-value) + :format "%s" + :where (cons (point) marker) + :type 'enlighten + 'face 'cider-enlightened-local)))))))) + ;;; Move here command ;; This is the inverse of `cider--debug-move-point'. However, that algorithm is diff --git a/cider-mode.el b/cider-mode.el index 9eaa71d8b..5999e98ce 100644 --- a/cider-mode.el +++ b/cider-mode.el @@ -360,14 +360,18 @@ The value can also be t, which means to font-lock as much as possible." (let ((cider-font-lock-dynamically (if (eq cider-font-lock-dynamically t) '(function var macro core deprecated) cider-font-lock-dynamically)) - deprecated + deprecated enlightened macros functions vars instrumented traced) (when (memq 'core cider-font-lock-dynamically) (while core-plist (let ((sym (pop core-plist)) (meta (pop core-plist))) - (when (nrepl-dict-get meta "cider.nrepl.middleware.util.instrument/breakfunction") - (push sym instrumented)) + (pcase (nrepl-dict-get meta "cider.nrepl.middleware.util.instrument/breakfunction") + (`nil nil) + (`"#'cider.nrepl.middleware.debug/breakpoint-if-interesting" + (push sym instrumented)) + (`"#'cider.nrepl.middleware.enlighten/light-form" + (push sym enlightened))) (when (or (nrepl-dict-get meta "clojure.tools.trace/traced") (nrepl-dict-get meta "cider.inlined-deps.clojure.tools.trace/traced")) (push sym traced)) @@ -383,8 +387,12 @@ The value can also be t, which means to font-lock as much as possible." (while symbols-plist (let ((sym (pop symbols-plist)) (meta (pop symbols-plist))) - (when (nrepl-dict-get meta "cider.nrepl.middleware.util.instrument/breakfunction") - (push sym instrumented)) + (pcase (nrepl-dict-get meta "cider.nrepl.middleware.util.instrument/breakfunction") + (`nil nil) + (`"#'cider.nrepl.middleware.debug/breakpoint-if-interesting" + (push sym instrumented)) + (`"#'cider.nrepl.middleware.enlighten/light-form" + (push sym enlightened))) (when (or (nrepl-dict-get meta "clojure.tools.trace/traced") (nrepl-dict-get meta "cider.inlined-deps.clojure.tools.trace/traced")) (push sym traced)) @@ -414,6 +422,9 @@ The value can also be t, which means to font-lock as much as possible." ,@(when deprecated `((,(regexp-opt deprecated 'symbols) 0 (cider--unless-local-match cider-deprecated-properties) append))) + ,@(when enlightened + `((,(regexp-opt enlightened 'symbols) 0 + (cider--unless-local-match 'cider-enlightened) append))) ,@(when instrumented `((,(regexp-opt instrumented 'symbols) 0 (cider--unless-local-match 'cider-instrumented-face) append))) @@ -423,7 +434,7 @@ The value can also be t, which means to font-lock as much as possible." (defconst cider--static-font-lock-keywords (eval-when-compile - `((,(regexp-opt '("#break" "#dbg") 'symbols) 0 font-lock-warning-face))) + `((,(regexp-opt '("#break" "#dbg" "#light") 'symbols) 0 font-lock-warning-face))) "Default expressions to highlight in CIDER mode.") (defvar-local cider--dynamic-font-lock-keywords nil) diff --git a/doc/images/enlighten-off.png b/doc/images/enlighten-off.png new file mode 100644 index 000000000..5033e7da2 Binary files /dev/null and b/doc/images/enlighten-off.png differ diff --git a/doc/images/enlighten-on.png b/doc/images/enlighten-on.png new file mode 100644 index 000000000..82995a4f2 Binary files /dev/null and b/doc/images/enlighten-on.png differ diff --git a/nrepl-client.el b/nrepl-client.el index 26e64b52e..abb1afada 100644 --- a/nrepl-client.el +++ b/nrepl-client.el @@ -999,6 +999,9 @@ Register CALLBACK as the response handler." callback connection)) +(define-minor-mode cider-enlighten-mode nil nil (cider-mode " light") + :global t) + (defun nrepl--eval-request (input session &optional ns line column) "Prepare :eval request message for INPUT. SESSION and NS provide context for the request. @@ -1008,6 +1011,8 @@ If LINE and COLUMN are non-nil and current buffer is a file buffer, \"line\", (list "op" "eval" "session" session "code" input) + (when cider-enlighten-mode + (list "enlighten" "true")) (let ((file (or (buffer-file-name) (buffer-name)))) (when (and line column file) (list "file" file