clojure-emacs / cider

The Clojure Interactive Development Environment that Rocks for Emacs
https://cider.mx
GNU General Public License v3.0
3.52k stars 643 forks source link

cider-find-var on lib added via clojure.repl.deps/add-libs #3640

Open filipesilva opened 2 months ago

filipesilva commented 2 months ago

Expected behavior

Using cider-find-var navigates to source of lib added with clojure.repl.deps/add-libs.

Actual behavior

Doesn't seem to find the source. cider-doc seems to work so the symbol seems to be loaded correctly.

Steps to reproduce the problem

;; needs clojure.core 1.12.0-alpha5
(require '[clojure.repl.deps :as deps])
(deps/add-libs {'org.clojure/data.csv {:mvn/version "1.1.0"}})
(require '[clojure.data.csv :as csv])

;; cider-find-var does not work, I guess because of the deps/add-libs
csv/read-csv

Environment & Version information

CIDER version information

;; CIDER 1.14.0-snapshot (package: 1.14.0-snapshot), nREPL 1.1.1
;; Clojure 1.12.0-alpha5, Java 21.0.2

Lein / Clojure CLI version

Clojure CLI version 1.11.1.1435

Emacs version

29.2

Operating system

Mac Sonoma 14.4.1

JDK distribution

openjdk 21.0.2 2024-01-16

vemv commented 2 months ago

Interesting, we'd have to investigate. Should be a thin/easy fix.

Can you paste (meta #'csv/read-csv) here? Following the same prior steps

Thanks - V

filipesilva commented 2 months ago

Thanks for taking a look!

Here's the result of meta:

(meta #'csv/read-csv)
;; => {:arglists ([input & options]),
;;     :doc
;;     "Reads CSV-data from input (String or java.io.Reader) into a lazy\n  sequence of vectors.\n\n   Valid options are\n     :separator (default \\,)\n     :quote (default \\\")",
;;     :line 87,
;;     :column 1,
;;     :file "clojure/data/csv.clj",
;;     :name read-csv,
;;     :ns #namespace[clojure.data.csv]}
vemv commented 2 months ago

Thanks! That looks right.

My actual question should have been:

What's the result of (clojure.java.io/resource "clojure/data/csv.clj")?

yuhan0 commented 2 months ago

I noticed this a while ago too with the new sync-deps functionality in Clojure 1.12, the new libraries are added to the classpath but Cider couldn't find their source definition without a full REPL restart.

Looked into it a little and the issue appeared to be something to do with orchard.info/file-info which cider-nrepl's info middleware uses to transform the :file key - for some strange reason it exhibited different behaviour when called from a info op vs a regular eval op. Might have something to do with interactive vs tooling sessions or thread-local vars?

vemv commented 2 months ago

Could be anything. It might as well have to do with the classpath - probably Orchard's classpath matches 1:1 the JVM classpath at startup time, but newer Clojure features make it grow?

Let me know about (clojure.java.io/resource "clojure/data/csv.clj")

If my diagnostic is right, the fix could be potentially as simlpe as choosing one classloader over the other.

We might also ask around for best practices around classloader choice starting from Clojure 1.12.

yuhan0 commented 2 months ago

Dug up my experiment log and ran it again using clojure.data.csv as the repro:

(ns repro)
(require '[clojure.repl.deps :as deps])
(deps/add-libs {'org.clojure/data.csv {:mvn/version "1.1.0"}})

(require '[clojure.data.csv :as csv])

(require '[clojure.java.io])
(clojure.java.io/resource "clojure/data/csv.clj")
;; => #object[java.net.URL 0x56797bbd "jar:file:/Users/yuhan/.m2/repository/org/clojure/data.csv/1.1.0/data.csv-1.1.0.jar!/clojure/data/csv.clj"]

(require '[orchard.info :as info])
(info/file-info "clojure/data/csv.clj")
;; => {:file
;;     #object[java.net.URL 0x56b9da93 "jar:file:/Users/yuhan/.m2/repository/org/clojure/data.csv/1.1.0/data.csv-1.1.0.jar!/clojure/data/csv.clj"],
;;     :resource "clojure/data/csv.clj"}

(require '[cider.nrepl.middleware.info :as c.info])
(-> (c.info/info-reply {:ns "repro"
                        :sym "csv/read-csv"})
    (select-keys ["file"]))
;; => {"file"
;;     "jar:file:/Users/yuhan/.m2/repository/org/clojure/data.csv/1.1.0/data.csv-1.1.0.jar!/clojure/data/csv.clj"}

;; but cider-find-var on this doesn't work:
csv/read-csv

From an elisp scratch buffer:

(with-current-buffer "repro.clj"
  (nrepl-dict-get
   (cider-var-info "inc")
   "file"))
;; =>
;; "jar:file:/Users/yuhan/.m2/repository/org/clojure/clojure/1.12.0-alpha8/clojure-1.12.0-alpha8.jar!/clojure/core.clj"

(with-current-buffer "repro.clj"
  (nrepl-dict-get
   (cider-var-info "csv/read-csv")
   "file"))
;; =>
;; "clojure/data/csv.clj"

(with-current-buffer "repro.clj" 
  (nrepl-dict-get  
   (cider-sync-tooling-eval
    "(:file (orchard.info/file-info \"clojure/data/csv.clj\"))"
    ;; "(:file (cider.nrepl.middleware.info/info-reply \"clojure/core/logic.clj\"))"
    )
   "value"))
;; =>
;; "#object[java.net.URL 0x7de93292 \"jar:file:/Users/yuhan/.m2/repository/org/clojure/data.csv/1.1.0/data.csv-1.1.0.jar!/clojure/data/csv.clj\"]"

At some point I used cider-debug to instrument orchard.info/file-info and found it was indeed returning different results depending on the context it was called from.. didn't have the time to investigate any further but I hope this helps with narrowing down the cause !

vemv commented 2 months ago

Thanks! Seems a good starting point.