Skip to content

Commit 6537c50

Browse files
committed
Drop clojure-expected-ns' delegation from cider-expected-ns'
Inline the path-based namespace derivation that was previously delegated to `clojure-expected-ns'. The new internal helper `cider--ns-from-path' uses `cider-project-dir' to find the project root, then applies the same drop-first-dir + path-to-ns + strip-known-prefixes algorithm clojure-mode uses (with prefixes now owned by cider via `cider-directory-prefixes'). Behavior is unchanged for files on the classpath (still preferred) and for files in a recognized project layout. Tests rewritten to assert the new behavior directly instead of the old "delegates to clojure-expected-ns" coupling.
1 parent 9c9d401 commit 6537c50

3 files changed

Lines changed: 75 additions & 30 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
### Changes
3535

3636
- Project root detection no longer goes through `clojure-mode'/`clojure-ts-mode'. New `cider-project-dir' built on top of `project.el' is used instead, with `cider-build-tool-files' as the extra root markers. This works identically whether the user is in `clojure-mode', `clojure-ts-mode', or even a buffer not visiting a Clojure file (e.g. an `M-x cider-connect' from Dired), and respects any `project-find-functions' the user has configured.
37+
- The path-based fallback in `cider-expected-ns' no longer delegates to `clojure-expected-ns'. Inline the same algorithm using `cider-project-dir' and a new `cider-directory-prefixes' defcustom (mirroring `clojure-directory-prefixes' but owned by cider). No behavior change for files on the classpath (still preferred) or in a recognized project layout; removes the runtime dependency on `clojure-mode' for ns derivation.
3738
- [#710](https://github.com/clojure-emacs/cider-nrepl/issues/710): Use namespaced nREPL ops (e.g. `cider/info` instead of `info`) to match cider-nrepl 0.59+.
3839
- Bump the injected `nrepl` to [1.7.0](https://github.com/nrepl/nrepl/blob/master/CHANGELOG.md#170-2026-04-14).
3940
- Bump the injected `cider-nrepl` to [0.59.0](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0590-2026-04-14).

lisp/cider-client.el

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -152,27 +152,59 @@ Remove extension and substitute \"/\" with \".\", \"_\" with \"-\"."
152152
(replace-regexp-in-string "/" ".")
153153
(replace-regexp-in-string "_" "-")))
154154

155+
(defcustom cider-directory-prefixes
156+
'("\\`clj[scxd]?\\.")
157+
"Namespace prefixes to strip after deriving a ns from a file path.
158+
Used by `cider-expected-ns' to discard intermediate source directories
159+
that aren't really part of the namespace, e.g. a file at
160+
\"src/clj/foo/bar.clj\" should give the namespace \"foo.bar\" rather
161+
than \"clj.foo.bar\"."
162+
:type '(repeat string)
163+
:group 'cider
164+
:safe (lambda (value)
165+
(and (listp value)
166+
(cl-every #'stringp value))))
167+
168+
(defun cider--ns-from-path (path)
169+
"Derive a Clojure namespace from PATH using project layout heuristics.
170+
PATH is expected to be an absolute file path inside a project (see
171+
`cider-project-dir'). The first directory component of the project-
172+
relative path is dropped (typically `src' or `test') and any prefix
173+
matching an entry in `cider-directory-prefixes' is stripped from the
174+
result. Returns nil when PATH isn't inside a recognized project."
175+
(when-let* ((proj (cider-project-dir (file-name-directory path)))
176+
(relative (file-relative-name path proj))
177+
(drop-first (mapconcat #'identity
178+
(cdr (split-string relative "/"))
179+
"/")))
180+
(cl-reduce (lambda (acc re) (replace-regexp-in-string re "" acc))
181+
cider-directory-prefixes
182+
:initial-value (cider-path-to-ns drop-first))))
183+
155184
(defun cider-expected-ns (&optional path)
156185
"Return the namespace string matching PATH, or nil if not found.
157-
If PATH is nil, use the path to the file backing the current buffer. The
158-
command falls back to `clojure-expected-ns' in the absence of an active
159-
nREPL connection."
160-
(if (cider-connected-p)
161-
(let* ((path (file-truename (or path buffer-file-name)))
162-
(relpath (thread-last
163-
(cider-classpath-entries)
164-
(seq-filter #'file-directory-p)
165-
(seq-map (lambda (dir)
166-
(when (file-in-directory-p path dir)
167-
(file-relative-name path dir))))
168-
(seq-filter #'identity)
169-
(seq-sort (lambda (a b)
170-
(< (length a) (length b))))
171-
(car))))
172-
(if relpath
173-
(cider-path-to-ns relpath)
174-
(clojure-expected-ns path)))
175-
(clojure-expected-ns path)))
186+
If PATH is nil, use the path to the file backing the current buffer.
187+
188+
When an nREPL connection is active, the namespace is preferentially
189+
derived from the connection's classpath entries. Otherwise (or when
190+
PATH isn't on the classpath) it falls back to `cider--ns-from-path',
191+
which uses project layout heuristics."
192+
(when-let* ((path (file-truename (or path buffer-file-name))))
193+
(if (cider-connected-p)
194+
(let ((relpath (thread-last
195+
(cider-classpath-entries)
196+
(seq-filter #'file-directory-p)
197+
(seq-map (lambda (dir)
198+
(when (file-in-directory-p path dir)
199+
(file-relative-name path dir))))
200+
(seq-filter #'identity)
201+
(seq-sort (lambda (a b)
202+
(< (length a) (length b))))
203+
(car))))
204+
(if relpath
205+
(cider-path-to-ns relpath)
206+
(cider--ns-from-path path)))
207+
(cider--ns-from-path path))))
176208

177209
(defun cider--fallback-op (op connection)
178210
"Return the effective op name for OP on CONNECTION.

test/cider-client-tests.el

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,11 @@
243243
(spy-on 'file-in-directory-p :and-call-fake (lambda (file dir)
244244
(string-prefix-p dir file)))
245245
(spy-on 'file-relative-name :and-call-fake (lambda (file dir)
246-
(substring file (+ 1 (length dir))))))
246+
(substring file (+ 1 (length dir)))))
247+
;; The path-based fallback uses `cider-project-dir' to compute a
248+
;; relative path; stub it so we don't accidentally pick up the
249+
;; surrounding cider repo as a "project".
250+
(spy-on 'cider-project-dir :and-return-value "/cider--proj/"))
247251

248252
(it "returns the namespace matching the given string path"
249253
(expect (cider-expected-ns "/cider--a/foo/bar/baz_utils.clj") :to-equal
@@ -256,14 +260,22 @@
256260
(expect (cider-expected-ns "/cider--c/foo/bar/baz") :to-equal
257261
"foo.bar.baz")
258262
(expect (cider-expected-ns "/cider--base/clj-dev/foo/bar.clj") :to-equal
259-
"foo.bar")
260-
(expect (cider-expected-ns "/cider--not/in/classpath.clj") :to-equal
261-
(clojure-expected-ns "/cider--not/in/classpath.clj")))
262-
263-
(it "returns nil if it cannot find the namespace"
264-
(expect (cider-expected-ns "/cider--z/abc/def") :to-equal ""))
265-
266-
(it "falls back on `clojure-expected-ns' in the absence of an active nREPL connection"
263+
"foo.bar"))
264+
265+
(it "falls back on project-relative path heuristics when the file is not on the classpath"
266+
;; With our `cider-project-dir' stub returning "/cider--proj/", the
267+
;; mocked `file-relative-name' makes "/cider--proj/src/foo/bar.clj"
268+
;; relative to "/cider--proj/" -> "src/foo/bar.clj". The path-based
269+
;; helper drops the first directory ("src") and produces "foo.bar".
270+
(expect (cider-expected-ns "/cider--proj/src/foo/bar.clj") :to-equal
271+
"foo.bar"))
272+
273+
(it "strips known directory prefixes after deriving the ns"
274+
;; Files under e.g. src/clj/... should not produce a "clj." prefix.
275+
(expect (cider-expected-ns "/cider--proj/src/clj/foo/bar.clj") :to-equal
276+
"foo.bar"))
277+
278+
(it "uses the path-based fallback in the absence of an active nREPL connection"
267279
(spy-on 'cider-connected-p :and-return-value nil)
268-
(spy-on 'clojure-expected-ns :and-return-value "clojure-expected-ns")
269-
(expect (cider-expected-ns "foo") :to-equal "clojure-expected-ns")))
280+
(expect (cider-expected-ns "/cider--proj/src/foo/bar.clj") :to-equal
281+
"foo.bar")))

0 commit comments

Comments
 (0)