Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance cider-connect to show all nREPLs available ports, instead of only Leiningen ones #3399

Merged
merged 6 commits into from
Aug 11, 2023
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
28 changes: 20 additions & 8 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ commands:
name: Run Elisp-lint
command: eldev lint
- run:
name: Byte-compile `.el' files
name: Byte-compile .el files
command: eldev -dtT compile --warnings-as-errors

jobs:
Expand Down Expand Up @@ -110,13 +110,25 @@ jobs:
- lint

workflows:
version: 2
version: 2.1
ci-test-matrix:
jobs:
- test-ubuntu-emacs-26
- test-ubuntu-emacs-27
- test-ubuntu-emacs-28
- test-ubuntu-emacs-master
- test-lint
- test-macos-emacs-latest
- test-windows-emacs-latest
- test-ubuntu-emacs-26:
requires:
- test-lint
- test-ubuntu-emacs-27:
requires:
- test-lint
- test-ubuntu-emacs-28:
requires:
- test-lint
- test-ubuntu-emacs-master:
requires:
- test-lint
- test-windows-emacs-latest:
requires:
- test-lint
- test-macos-emacs-latest:
requires:
- test-ubuntu-emacs-28
16 changes: 8 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
### New features

- [#3352](https://github.com/clojure-emacs/cider/pull/3352) Add CIDER Log Mode, a major mode that allows you to capture, debug, inspect and view log events emitted by Java logging frameworks.
- [#3354](https://github.com/clojure-emacs/cider/issues/3354): Add new customization variable `cider-reuse-dead-repls` to control how dead REPL buffers are reused on new connections.
- `cider-test`: add timing information.
- `cider-test`: only show diffs for collections.
- `cider-test`: fail-fast by default, as controlled by the new `cider-test-fail-fast` defcustom and `cider-test-toggle-fail-fast` keybinding.
- Infer indentation specs when possible ([doc](https://docs.cider.mx/cider/indent_spec.html#indentation-inference)).
- Add new customization variable `cider-clojurec-eval-destination` to allow specifying which REPL CLJC evals are sent to.
- Improve the presentation of `xref` data.
- [#3354](https://github.com/clojure-emacs/cider/issues/3354): Add new customization variable `cider-reuse-dead-repls` to control how dead REPL buffers are reused on new connections.

### Bugs fixed

Expand All @@ -20,19 +18,21 @@
- [#3355](https://github.com/clojure-emacs/cider/pull/3355): Fix `cider-mode` disabling itself after a disconnect when `cider-auto-mode` is set to nil.
- [#3362](https://github.com/clojure-emacs/cider/issues/3362): Fix `sesman-restart` regression issue.
- [#3236](https://github.com/clojure-emacs/cider/issues/3236): `cider-repl-set-ns` no longer changes the repl session type from `cljs:shadow` to `clj`.
- [#3383](https://github.com/clojure-emacs/cider/issues/3383): `cider-connect-clj&cljs`: don't render `"ClojureScript REPL type:" for JVM repls.
- [#3383](https://github.com/clojure-emacs/cider/issues/3383): `cider-connect-clj&cljs`: don't render `"ClojureScript REPL type:"` for JVM repls.
- [#3331](https://github.com/clojure-emacs/cider/issues/3331): `cider-eval`: never jump to spurious locations, as sometimes conveyed by nREPL.
- [#3112](https://github.com/clojure-emacs/cider/issues/3112): Fix the CIDER `xref-find-references` backend to return correct filenames.
- [#3393](https://github.com/clojure-emacs/cider/issues/3393): recompute namespace info on each shadow-cljs recompilation or evaluation.
- Fix the `xref-find-definitions` CIDER backend to return correct filenames.
- Fix the `cider-xref-fn-deps` buttons to direct to the right file.

- [#3393](https://github.com/clojure-emacs/cider/issues/3393): recompute namespace info on each shadow-cljs recompilation or evaluation.

### Changes

- Improve `nrepl-dict` error reporting.
- [#3390](https://github.com/clojure-emacs/cider/issues/3390): Enhance `cider-connect` to show all nREPLs available ports, instead of only Leiningen ones.
- Preserve the `:cljs-repl-type` more reliably.
- [#3375](https://github.com/clojure-emacs/cider/pull/3375): `cider-test`: don't render a newline between expected and actual, most times.
- Improve the presentation of `xref` data.
- `cider-test`: only show diffs for collections.
- [#3375](https://github.com/clojure-emacs/cider/pull/3375): `cider-test`: don't render a newline between expected and actual, most times.
- Improve `nrepl-dict` error reporting.
- Bump the injected `cider-nrepl` to [0.35](https://github.com/clojure-emacs/cider-nrepl/blob/v0.35.0/CHANGELOG.md#0350-2023-08-09).
- Improves indentation, font-locking and other metadata support for ClojureScript.
- Updates [Orchard](https://github.com/clojure-emacs/orchard/blob/v0.14.2/CHANGELOG.md)
Expand Down
121 changes: 101 additions & 20 deletions cider.el
Original file line number Diff line number Diff line change
Expand Up @@ -349,14 +349,14 @@ The repl dependendcies are most likely to be nREPL middlewares."
:safe #'booleanp
:version '(cider . "0.15.0"))

(defvar cider-ps-running-nrepls-command "ps u | grep leiningen"
(defvar cider-ps-running-lein-nrepls-command "ps u | grep leiningen"
"Process snapshot command used in `cider-locate-running-nrepl-ports'.")

(defvar cider-ps-running-nrepl-path-regexp-list
(defvar cider-ps-running-lein-nrepl-path-regexp-list
'("\\(?:leiningen.original.pwd=\\)\\(.+?\\) -D"
"\\(?:-classpath +:?\\(.+?\\)/self-installs\\)")
"Regexp list to get project paths.
Extract project paths from output of `cider-ps-running-nrepls-command'.
Extract project paths from output of `cider-ps-running-lein-nrepls-command'.
Sub-match 1 must be the project path.")

(defvar cider-host-history nil
Expand Down Expand Up @@ -1798,30 +1798,111 @@ of remote SSH hosts."
(or (eq ?s filetype)
(eq ?d filetype))))))

(defun cider--path->path-port-pairs (path)
"Given PATH, returns all the possible <path, port> pairs."
(thread-last path
cider--file-path
nrepl-extract-ports
(mapcar (lambda (port)
(list path port)))
;; remove nils that may have been returned due to permission errors:
(seq-filter #'identity)))

(defun cider--invoke-running-nrepl-path (f)
"Invokes F safely.

Necessary since we run some OS-specific commands that may fail."
(condition-case nil
(let* ((x (funcall f)))
(mapcar (lambda (v)
(if (and (listp v)
(not (file-exists-p (car v))))
nil
v))
x))
(error nil)))

(defun cider-locate-running-nrepl-ports (&optional dir)
"Locate ports of running nREPL servers.
When DIR is non-nil also look for nREPL port files in DIR. Return a list
of list of the form (project-dir port)."
(let* ((paths (cider--running-nrepl-paths))
(proj-ports (apply #'append
(mapcar (lambda (d)
(mapcar (lambda (p) (list (file-name-nondirectory (directory-file-name d)) p))
(and d (nrepl-extract-ports (cider--file-path d)))))
(cons (clojure-project-dir dir) paths)))))
(seq-uniq (delq nil proj-ports))))
(let* ((pairs (cider--running-nrepl-paths))
(pairs (if-let (c (and dir (clojure-project-dir dir)))
(cons (cider--path->path-port-pairs c) pairs)
pairs)))
(thread-last pairs
(delq nil)
(mapcar (lambda (x)
(list (file-name-nondirectory (directory-file-name (car x)))
(nth 1 x))))
(seq-uniq))))

(defun cider--running-lein-nrepl-paths ()
"Retrieve project paths of running lein nREPL servers.
Use `cider-ps-running-lein-nrepls-command' and
`cider-ps-running-lein-nrepl-path-regexp-list'."
(unless (eq system-type 'windows-nt)
(let (paths)
(with-temp-buffer
(insert (shell-command-to-string cider-ps-running-lein-nrepls-command))
(dolist (regexp cider-ps-running-lein-nrepl-path-regexp-list)
(goto-char 1)
(while (re-search-forward regexp nil t)
(setq paths (cons (match-string 1) paths)))))
(seq-mapcat (lambda (path)
(cider--path->path-port-pairs path))
paths))))

(defun cider--running-non-lein-nrepl-paths ()
"Retrieve (directory, port) pairs of running nREPL servers other than Lein ones."
(unless (eq system-type 'windows-nt)
(let ((non-lein-nrepl-pids
(thread-last (split-string
(shell-command-to-string "ps u | grep java | grep -v leiningen | grep nrepl.cmdline")
"\n")
(mapcar (lambda (s)
(nth 1 (split-string s " "))))
(seq-filter #'identity))))
(when non-lein-nrepl-pids
(mapcar (lambda (pid)
(let* ((directory (thread-last (split-string (shell-command-to-string (concat "lsof -a -d cwd -n -Fn -p " pid))
"\n")
(seq-map (lambda (s)
(when (string-prefix-p "n" s)
(replace-regexp-in-string "^n" "" s))))
(seq-filter #'identity)
car))
(port (thread-last (split-string (shell-command-to-string (concat "lsof -n -Fn -i -a -p " pid))
"\n")
(seq-map (lambda (s)
(when (string-prefix-p "n" s)
(replace-regexp-in-string ".*:" "" s))))
(seq-filter #'identity)
car)))
(list directory port)))
non-lein-nrepl-pids)))))

(defun cider--running-local-nrepl-paths ()
"Retrieve project paths of running nREPL servers.
Do it by looping over the open REPL buffers."
(thread-last (buffer-list)
(seq-filter
(lambda (b)
(string-prefix-p "*cider-repl" (buffer-name b))))
(seq-map
(lambda (b)
(with-current-buffer b
(when-let ((dir (plist-get (cider--gather-connect-params) :project-dir))
(port (plist-get (cider--gather-connect-params) :port)))
(list dir (prin1-to-string port))))))
(seq-filter #'identity)))

(defun cider--running-nrepl-paths ()
"Retrieve project paths of running nREPL servers.
Use `cider-ps-running-nrepls-command' and
`cider-ps-running-nrepl-path-regexp-list'."
(let (paths)
(with-temp-buffer
(insert (shell-command-to-string cider-ps-running-nrepls-command))
(dolist (regexp cider-ps-running-nrepl-path-regexp-list)
(goto-char 1)
(while (re-search-forward regexp nil t)
(setq paths (cons (match-string 1) paths)))))
(seq-uniq paths)))
Search for lein or java processes including nrepl.command nREPL."
(append (cider--invoke-running-nrepl-path #'cider--running-lein-nrepl-paths)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't expect duplicates here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't expect duplicates here?

They're handled in the cider--running-nrepl-paths callsite. Which seems better since cider-locate-running-nrepl-ports can cons an element to it, so you'd have to run seq-uniq again

(cider--invoke-running-nrepl-path #'cider--running-local-nrepl-paths)
(cider--invoke-running-nrepl-path #'cider--running-non-lein-nrepl-paths)))

(defun cider--identify-buildtools-present (&optional project-dir)
"Identify build systems present by their build files in PROJECT-DIR.
Expand Down
24 changes: 15 additions & 9 deletions nrepl-client.el
Original file line number Diff line number Diff line change
Expand Up @@ -232,18 +232,24 @@ PARAMS is as in `nrepl-make-buffer-name'."

(defun nrepl-extract-port (dir)
"Read port from applicable repl-port file in directory DIR."
(or (nrepl--port-from-file (expand-file-name "repl-port" dir))
(nrepl--port-from-file (expand-file-name ".nrepl-port" dir))
(nrepl--port-from-file (expand-file-name "target/repl-port" dir))
(nrepl--port-from-file (expand-file-name ".shadow-cljs/nrepl.port" dir))))
(condition-case nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking to tackle this with with-demoted-errors (although also ignore-errors would do here if you don't want to let the user know about what caused the ignored error): maybe it saves some code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Sounds reasonable.

Now that I think it, we may want to catch errors for each individual directory entry, instead of for the whole call, so that one bad dir doesn't cause all its neighbors to be excluded.

(or (nrepl--port-from-file (expand-file-name "repl-port" dir))
(nrepl--port-from-file (expand-file-name ".nrepl-port" dir))
(nrepl--port-from-file (expand-file-name "target/repl-port" dir))
(nrepl--port-from-file (expand-file-name ".shadow-cljs/nrepl.port" dir)))
;; This operation can hit permission errors, particularly on macOS:
(error nil)))

(defun nrepl-extract-ports (dir)
"Read ports from applicable repl-port files in directory DIR."
(delq nil
(list (nrepl--port-from-file (expand-file-name "repl-port" dir))
(nrepl--port-from-file (expand-file-name ".nrepl-port" dir))
(nrepl--port-from-file (expand-file-name "target/repl-port" dir))
(nrepl--port-from-file (expand-file-name ".shadow-cljs/nrepl.port" dir)))))
(condition-case nil
(delq nil
(list (nrepl--port-from-file (expand-file-name "repl-port" dir))
(nrepl--port-from-file (expand-file-name ".nrepl-port" dir))
(nrepl--port-from-file (expand-file-name "target/repl-port" dir))
(nrepl--port-from-file (expand-file-name ".shadow-cljs/nrepl.port" dir))))
;; This operation can hit permission errors, particularly on macOS:
(error nil)))

(make-obsolete 'nrepl-extract-port 'nrepl-extract-ports "1.5.0")

Expand Down
Loading