Skip to content

Commit 4175aee

Browse files
committed
Make jump-to-definition work in projects needing cider-path-translations
i.e. Dockerized projects. Fixes #3413
1 parent 3e54af3 commit 4175aee

File tree

11 files changed

+110
-18
lines changed

11 files changed

+110
-18
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- [#3393](https://github.com/clojure-emacs/cider/issues/3393): Recompute namespace info on each shadow-cljs recompilation or evaluation.
2727
- Recompute namespace info on each fighweel-main recompilation.
2828
- [#3250](https://github.com/clojure-emacs/cider/issues/3250): don't lose the CIDER session over TRAMP files.
29+
- [#3413](https://github.com/clojure-emacs/cider/issues/3413): Make jump-to-definition work in projects needing `cider-path-translations` (i.e. Dockerized projects).
2930
- Fix the `xref-find-definitions` CIDER backend to return correct filenames.
3031
- Fix the `cider-xref-fn-deps` buttons to direct to the right file.
3132
- Make TRAMP functionality work when using non-standard ports.

cider-common.el

+33-12
Original file line numberDiff line numberDiff line change
@@ -286,22 +286,38 @@ in the container, the alist would be `((\"/src\" \"~/projects/foo/src\"))."
286286
:group 'cider
287287
:package-version '(cider . "0.23.0"))
288288

289-
(defun cider--translate-path (path direction)
290-
"Attempt to translate the PATH in the given DIRECTION.
289+
(defun cider--translate-path (path direction &optional return-all)
290+
"Attempt to translate the PATH in the given DIRECTION, optionally RETURN-ALL.
291291
Looks at `cider-path-translations' for (container . host) alist of path
292292
prefixes and translates PATH from container to host or vice-versa depending on
293293
whether DIRECTION is 'from-nrepl or 'to-nrepl."
294294
(seq-let [from-fn to-fn path-fn] (cond ((eq direction 'from-nrepl) '(car cdr identity))
295295
((eq direction 'to-nrepl) '(cdr car expand-file-name)))
296-
(let ((path (funcall path-fn path)))
297-
(seq-some (lambda (translation)
298-
(let ((prefix (file-name-as-directory (expand-file-name (funcall from-fn translation)))))
299-
(when (string-prefix-p prefix path)
300-
(replace-regexp-in-string (format "^%s" (regexp-quote prefix))
301-
(file-name-as-directory
302-
(expand-file-name (funcall to-fn translation)))
303-
path))))
304-
cider-path-translations))))
296+
(let ((f (lambda (translation)
297+
(let ((path (funcall path-fn path))
298+
(prefix (file-name-as-directory (expand-file-name (funcall from-fn translation)))))
299+
(when (string-prefix-p prefix path)
300+
(replace-regexp-in-string (format "^%s" (regexp-quote prefix))
301+
(file-name-as-directory
302+
(expand-file-name (funcall to-fn translation)))
303+
path))))))
304+
(if return-all
305+
(seq-filter #'identity (mapcar f cider-path-translations))
306+
(seq-some f cider-path-translations)))))
307+
308+
(defun cider--all-path-translations ()
309+
"Returns `cider-path-translations' if non-empty, else seeks a present value."
310+
(or cider-path-translations
311+
;; cider-path-translations often is defined as a directory-local variable,
312+
;; so after jumping to a .jar file, its value can be lost,
313+
;; so we have to figure out a possible translation:
314+
(thread-last (buffer-list)
315+
(seq-map (lambda (buffer)
316+
(buffer-local-value 'cider-path-translations buffer)))
317+
(seq-filter #'identity)
318+
(seq-uniq)
319+
(apply #'append)
320+
(seq-uniq))))
305321

306322
(defun cider--translate-path-from-nrepl (path)
307323
"Attempt to translate the nREPL PATH to a local path."
@@ -336,7 +352,12 @@ If no local or remote file exists, return nil."
336352
((and tramp-path (file-exists-p tramp-path))
337353
tramp-path)
338354
((and local-path (file-exists-p local-path))
339-
local-path))))
355+
local-path)
356+
(t
357+
(when-let* ((cider-path-translations (cider--all-path-translations)))
358+
(thread-last (cider--translate-path local-path 'from-nrepl :return-all)
359+
(seq-filter #'file-exists-p)
360+
car))))))
340361

341362
(declare-function archive-extract "arc-mode")
342363
(declare-function archive-zip-extract "arc-mode")

cider-connection.el

+30-3
Original file line numberDiff line numberDiff line change
@@ -614,8 +614,9 @@ REPL defaults to the current REPL."
614614
(sesman-more-recent-p (cdr session1) (cdr session2)))
615615

616616
(declare-function cider-classpath-entries "cider-client")
617-
(cl-defmethod sesman-friendly-session-p ((_system (eql CIDER)) session)
618-
"Check if SESSION is a friendly session."
617+
618+
(defun cider--sesman-friendly-session-p (session &optional debug)
619+
"Check if SESSION is a friendly session, DEBUG optionally."
619620
(setcdr session (seq-filter #'buffer-live-p (cdr session)))
620621
(when-let* ((repl (cadr session))
621622
(proc (get-buffer-process repl))
@@ -644,7 +645,33 @@ REPL defaults to the current REPL."
644645
(or (seq-find (lambda (path) (string-prefix-p path file))
645646
classpath)
646647
(seq-find (lambda (path) (string-prefix-p path file))
647-
classpath-roots))))))
648+
classpath-roots)
649+
(when-let* ((cider-path-translations (cider--all-path-translations))
650+
(translated (cider--translate-path file 'to-nrepl :return-all)))
651+
(seq-find (lambda (translated-path)
652+
(or (seq-find (lambda (path)
653+
(string-prefix-p path translated-path))
654+
classpath)
655+
(seq-find (lambda (path)
656+
(string-prefix-p path translated-path))
657+
classpath-roots)))
658+
translated))
659+
(when debug
660+
(list file "was not determined to belong to classpath:" classpath "or classpath-roots:" classpath-roots)))))))
661+
662+
(defun cider-debug-sesman-friendly-session-p ()
663+
"`message's debugging information relative to friendly sessions.
664+
665+
This is useful for when one sees 'No linked CIDER sessions'
666+
in an unexpected place."
667+
(interactive)
668+
(message (prin1-to-string (mapcar (lambda (session)
669+
(cider--sesman-friendly-session-p session t))
670+
(sesman--all-system-sessions 'CIDER)))))
671+
672+
(cl-defmethod sesman-friendly-session-p ((_system (eql CIDER)) session)
673+
"Check if SESSION is a friendly session."
674+
(cider--sesman-friendly-session-p session))
648675

649676
(defvar cider-sesman-browser-map
650677
(let ((map (make-sparse-keymap)))
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
((nil . ((eval . (customize-set-variable 'cider-path-translations
2+
(list
3+
(cons "/src" (clojure-project-dir))
4+
(cons "/root/.m2" (concat (getenv "HOME") "/.m2"))))))))

dev/docker-sample-project/Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM clojure:temurin-17-lein-bullseye
2+
ENV DEBIAN_FRONTEND=noninteractive
3+
ENV NREPL_PORT=7888
4+
WORKDIR /root/app
5+
COPY . /root/app
6+
RUN lein deps
7+
EXPOSE 7888
8+
RUN lein classpath
9+
CMD ["lein", "repl", ":headless", ":host", "0.0.0.0", ":port", "7888"]

dev/docker-sample-project/Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
build:
2+
DOCKER_BUILDKIT=0 docker build --no-cache -t cider-docker-dev .
3+
4+
run: build
5+
docker run -v $$PWD/src:/app/src -p 7888:7888 cider-docker-dev

dev/docker-sample-project/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
This project spins up a Clojure project within a Docker image.
2+
3+
The Docker image exposes an nREPL server.
4+
5+
This way, for development purposes, we can exercise CIDER's Docker-related capabilities.
6+
7+
To get started:
8+
9+
* From a terminal tab, run `make run` to run the Docker image
10+
* Note that it has a volume mapping for `src`, so any local changes will be visible in the Docker image.
11+
* Also note that the root of this subproject has a .dir-locals.el setting up `cider-path-translations`.
12+
* `M-x cider-connect-clj`, choose localhost, 7888
13+
* `M-x cider-load-buffer` the foo.clj namespace.
14+
* From now on, you can `M-.` (jump to definition) recursively, starting from `clj-http.client`.

dev/docker-sample-project/project.clj

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(defproject cider-docker-dev "0"
2+
:dependencies [[org.clojure/clojure "1.11.1"]
3+
[clj-http "3.12.3"]]
4+
:source-paths ["src"]
5+
:plugins [[cider/cider-nrepl "0.35.0"]])

dev/docker-sample-project/src/bar.clj

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(ns bar
2+
(:require [foo]))

dev/docker-sample-project/src/foo.clj

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(ns foo
2+
(:require
3+
[clj-http.client :as client]))

doc/modules/ROOT/pages/config/basic_config.adoc

+4-3
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,10 @@ To prefer local resources to remote resources (tramp) when both are available:
201201

202202
== Translate File Paths
203203

204-
If you wish to translate file paths from your running instance you may use the
205-
`cider-path-translations` defcustom to do so. For instance, suppose your app is
206-
running in a docker container with your source directories mounted there. The
204+
If you are running Clojure within a Docker image, or doing something similar (i.e. you're `cider-connect`ing to a process,
205+
and there's a directory mapping for your source paths), you typically need to set `cider-path-translations`
206+
for jump-to-definition to properly work. For instance, suppose your app is
207+
running in a docker container with your source directories mounted there as volumes. The
207208
navigation paths you'd get from nREPL will be relative to the source in the
208209
docker container rather than the correct path on your host machine. You can add
209210
translation mappings easily by setting the following (typically in `.dir-locals.el`):

0 commit comments

Comments
 (0)