Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Change Log

## master (unreleased)

### Changes

* Require CIDER 2.0.

### Bugs fixed

* Fix `sayid-tree-inspect` (`c i` in the tree view) and `sayid-buf-inspect-at-point` never opening the inspector: they passed an expression string to `cider-inspect`, an API that CIDER dropped years ago. They now go through `cider-inspect-expr`.
* The inspect commands now signal a clear error when there's no value to inspect at point, instead of defing and inspecting a garbage value.

## [0.7.1] - 2026-07-11

### Bugs fixed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ the whole execution as data, after the fact, to query however you want.
### Requirements

Basic usage requires Clojure 1.10+ running on Java 8 or newer. The optional
nREPL middleware requires nREPL 1.0+, and the Emacs client requires CIDER 1.0+
nREPL middleware requires nREPL 1.0+, and the Emacs client requires CIDER 2.0+
on Emacs 28+.

(Sayid is tested against Clojure 1.10, 1.11 and 1.12 on a range of JDKs. Older
Expand Down
44 changes: 27 additions & 17 deletions src/el/sayid.el
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
;; Maintainer: Bozhidar Batsov <bozhidar@batsov.dev>
;; Version: 0.7.1
;; URL: https://github.com/clojure-emacs/sayid
;; Package-Requires: ((emacs "28") (cider "1.23"))
;; Package-Requires: ((emacs "28") (cider "2.0"))
;; Keywords: clojure, cider, debugger

;; Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -254,6 +254,15 @@ resolved automatically."
(message fail-msg)
(message x))))

(defun sayid--inspect-value (trace-id path)
"Inspect the value at PATH of the recorded call TRACE-ID.
Defs the value to `$s/*' server-side, then hands the live object to
`cider-inspect-expr'."
(sayid-send-and-message (list "op" "sayid-def-value"
"trace-id" trace-id
"path" path))
(cider-inspect-expr "$s/*" (cider-current-ns)))

(defun sayid-try-goto-prop (prop val)
"Move cursor to first position where property PROP has value VAL."
(let ((p 1))
Expand Down Expand Up @@ -571,23 +580,23 @@ TITLE is shown in the header line."
(if (string-empty-p mod) "" (format " [%s]" mod))))

(defun sayid-tree-inspect (&optional arg)
"Inspect a captured value of the call at point with `cider-inspect'.
Defs the value to `$s/*' server-side and hands the live object to CIDER's
inspector. By default inspects the return value (or the thrown error); with a
prefix ARG, prompt for which value - the return, the throw, or a named argument."
"Inspect a captured value of the call at point in CIDER's inspector.
Defs the value to `$s/*' server-side and hands the live object to
`cider-inspect-expr'. By default inspects the return value (or the thrown
error); with a prefix ARG, prompt for which value - the return, the throw, or
a named argument."
(interactive "P")
(let* ((call (sayid-tree--call-at-point))
(targets (sayid-tree--value-targets call))
(targets (or (sayid-tree--value-targets call)
(user-error "This call has no values to inspect")))
(path (if arg
(cdr (assoc (completing-read "Inspect value: " targets nil t)
targets))
(or (cdr (assoc (completing-read "Inspect value: " targets nil t)
targets))
(user-error "No value selected"))
(or (cdr (assoc "return" targets))
(cdr (assoc "throw" targets))
(user-error "This call has no return value to inspect")))))
(sayid-send-and-message (list "op" "sayid-def-value"
"trace-id" (nrepl-dict-get call "id")
"path" path))
(cider-inspect "$s/*")))
(sayid--inspect-value (nrepl-dict-get call "id") path)))

(defun sayid-tree-query-fn (&optional arg)
"Re-render the tree with every recorded call of the function at point.
Expand Down Expand Up @@ -902,12 +911,13 @@ With a prefix ARG, also prompt for a query modifier."

;;;###autoload
(defun sayid-buf-inspect-at-point ()
"Def value at point and pass to `cider-inspect'."
"Def value at point and open it in CIDER's inspector."
(interactive)
(sayid-send-and-message (list "op" "sayid-def-value"
"trace-id" (get-text-property (point) 'id)
"path" (get-text-property (point) 'path)))
(cider-inspect "$s/*"))
(let ((id (get-text-property (point) 'id))
(path (get-text-property (point) 'path)))
(unless (and id path)
(user-error "No inspectable value at point"))
(sayid--inspect-value id path)))

;;;###autoload
(defun sayid-buf-pprint-at-point ()
Expand Down
59 changes: 59 additions & 0 deletions test/el/sayid-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,65 @@
(expect (cdr (assoc "throw" targets)) :to-equal '("throw"))
(expect (cdr (assoc "arg x" targets)) :to-equal '("arg-map" "x")))))

(describe "sayid-tree-inspect"
(before-each
(spy-on 'sayid-send-and-message)
(spy-on 'cider-inspect-expr)
(spy-on 'cider-current-ns :and-return-value "user"))

(it "defs the return value server-side and opens it in the inspector"
(spy-on 'sayid-tree--call-at-point :and-return-value
(nrepl-dict "id" "7" "return" "42"))
(sayid-tree-inspect)
(expect 'sayid-send-and-message :to-have-been-called-with
'("op" "sayid-def-value" "trace-id" "7" "path" ("return")))
(expect 'cider-inspect-expr :to-have-been-called-with "$s/*" "user"))

(it "falls back to the throw when the call has no return"
(spy-on 'sayid-tree--call-at-point :and-return-value
(nrepl-dict "id" "7" "throw" (nrepl-dict "cause" "boom")))
(sayid-tree-inspect)
(expect 'sayid-send-and-message :to-have-been-called-with
'("op" "sayid-def-value" "trace-id" "7" "path" ("throw")))
(expect 'cider-inspect-expr :to-have-been-called-with "$s/*" "user"))

(it "errors without touching the server when there is nothing to inspect"
(spy-on 'sayid-tree--call-at-point :and-return-value (nrepl-dict "id" "7"))
(expect (sayid-tree-inspect) :to-throw 'user-error)
(expect 'sayid-send-and-message :not :to-have-been-called)
(expect 'cider-inspect-expr :not :to-have-been-called))

(it "errors when the prefix-arg prompt selects nothing"
(spy-on 'sayid-tree--call-at-point :and-return-value
(nrepl-dict "id" "7" "return" "42"))
(spy-on 'completing-read :and-return-value "")
(expect (sayid-tree-inspect '(4)) :to-throw 'user-error)
(expect 'sayid-send-and-message :not :to-have-been-called)
(expect 'cider-inspect-expr :not :to-have-been-called)))

(describe "sayid-buf-inspect-at-point"
(before-each
(spy-on 'sayid-send-and-message)
(spy-on 'cider-inspect-expr)
(spy-on 'cider-current-ns :and-return-value "user"))

(it "defs the value at point server-side and opens it in the inspector"
(with-temp-buffer
(insert (propertize "42" 'id "7" 'path '("return")))
(goto-char (point-min))
(sayid-buf-inspect-at-point))
(expect 'sayid-send-and-message :to-have-been-called-with
'("op" "sayid-def-value" "trace-id" "7" "path" ("return")))
(expect 'cider-inspect-expr :to-have-been-called-with "$s/*" "user"))

(it "errors without touching the server when point has no value"
(with-temp-buffer
(insert "no properties here")
(goto-char (point-min))
(expect (sayid-buf-inspect-at-point) :to-throw 'user-error))
(expect 'sayid-send-and-message :not :to-have-been-called)
(expect 'cider-inspect-expr :not :to-have-been-called)))

(describe "sayid-tree--query-title"
(it "names the query kind and selector"
(expect (sayid-tree--query-title "fn" "my.ns/foo" "")
Expand Down
Loading