6565(require 'cider-repl )
6666(require 'cider-mode )
6767(require 'cider-util )
68+ (require 'tramp-sh )
6869
6970(defvar cider-version " 0.8.0-snapshot"
7071 " Fallback version used when it cannot be extracted automatically.
@@ -91,14 +92,17 @@ This variable is used by `cider-connect'."
9192 :type 'list
9293 :group 'cider )
9394
94- ; ; TODO: Implement a check for `cider-lein-command' over tramp
95- (defun cider--lein-present-p ()
96- " Check if `cider-lein-command' is on the `exec-path' .
95+ (defvar cider-ps-running-nrepls-command " ps u | grep leiningen"
96+ " Process snapshot command used in `cider-locate-running-nrepl-ports' ." )
9797
98- In case `default-directory' is non-local we assume the command is available."
99- (or (file-remote-p default-directory)
100- (executable-find cider-lein-command)
101- (executable-find (concat cider-lein-command " .bat" ))))
98+ (defvar cider-ps-running-nrepl-path-regexp-list
99+ '(" \\ (?:leiningen.original.pwd=\\ )\\ ([^ ]+\\ )"
100+ " \\ (?:-classpath +:?\\ (.+\\ )/self-installs\\ )" )
101+ " Regexp list to extract project paths from output of `cider-ps-running-nrepls-command' .
102+ Sub-match 1 must be the project path." )
103+
104+ (defvar cider-host-history nil
105+ " Completion history for connection hosts." )
102106
103107;;;### autoload
104108(defun cider-version ()
@@ -129,50 +133,110 @@ start the server."
129133 (message " The %s executable (specified by `cider-lein-command' ) isn't on your exec-path "
130134 cider-lein-command)))
131135
132- (defun cider-known-endpoint-candidates ()
133- " Known endpoint candidates for establishing an nREPL connection.
134- A default will be included consisting of `nrepl-default-host' and
135- `nrepl-default-port' ."
136- (-distinct
137- (mapcar (lambda (endpoint )
138- (cider-string-join endpoint " " ))
139- (cons (list (nrepl-current-host) (nrepl-default-port))
140- cider-known-endpoints))))
141-
142- (defun cider-select-known-endpoint ()
143- " Select an endpoint from known endpoints.
144- The returned endpoint has the label removed."
145- (let ((selected-endpoint (split-string
146- (completing-read
147- " Host: " (cider-known-endpoint-candidates)))))
148- (if (= 3 (length selected-endpoint))
149- (cdr selected-endpoint)
150- selected-endpoint)))
151-
152136;;;### autoload
153137(defun cider-connect (host port )
154138 " Connect to an nREPL server identified by HOST and PORT.
155139Create REPL buffer and start an nREPL client connection."
156- (interactive (let ((known-endpoint (when cider-known-endpoints
157- (cider-select-known-endpoint))))
158- (list (or (car known-endpoint)
159- (read-string " Host: " (nrepl-current-host) nil (nrepl-current-host)))
160- (string-to-number (let ((port (or (cadr known-endpoint) (nrepl-default-port))))
161- (read-string " Port: " port nil port))))))
140+ (interactive (cider-select-endpoint))
162141 (setq cider-current-clojure-buffer (current-buffer ))
163142 (when (nrepl-check-for-repl-buffer `(, host , port ) nil )
164- (nrepl-start-client-process default-directory host port t )))
143+ (nrepl-start-client-process host port t )))
144+
145+ (defun cider-select-endpoint ()
146+ " Interactively select the host and port to connect to."
147+ (let* ((ssh-hosts (cider--ssh-hosts))
148+ (hosts (-distinct (append (when cider-host-history
149+ (list (car cider-host-history )))
150+ (list (list (nrepl-current-host)))
151+ cider-known-endpoints
152+ ssh-hosts
153+ (when (file-remote-p default-directory)
154+ ; ; add localhost even in remote buffers
155+ (list (list " localhost" ))))))
156+ (sel-host (cider--completing-read-host hosts))
157+ (host (car sel-host))
158+ (local-p (or (nrepl-local-host-p host)
159+ (not (assoc-string host ssh-hosts))))
160+ ; ; Each lein-port is a list of the form (dir port)
161+ (lein-ports (if local-p
162+ (let ((default-directory (if (file-remote-p default-directory)
163+ " ~/"
164+ default-directory)))
165+ (cider-locate-running-nrepl-ports))
166+ (let ((vec (vector " ssh" nil host " " nil )))
167+ (tramp-maybe-open-connection vec)
168+ (with-current-buffer (tramp-get-connection-buffer vec)
169+ (cider-locate-running-nrepl-ports)))))
170+ (ports (append (cdr sel-host) lein-ports))
171+ (port (cider--completing-read-port host ports)))
172+ (setq cider-host-history (cons sel-host (delete sel-host cider-host-history)))
173+ (list host port)))
174+
175+ (defun cider--ssh-hosts ()
176+ " Retrieve all ssh host from local configuration files."
177+ (-map (lambda (s ) (list (replace-regexp-in-string " :$" " " s)))
178+ (let ((tramp-completion-mode t ))
179+ (tramp-completion-handle-file-name-all-completions " " " /ssh:" ))))
180+
181+ (defun cider--completing-read-host (hosts )
182+ " Interactively select host from HOSTS.
183+ Each element in HOSTS is one of: (host), (host port) or (label host port).
184+ Return a list of the form (HOST PORT), where PORT can be nil."
185+ (let* ((sel-host (completing-read " Host: " (cider-join-with-val-prop hosts)))
186+ (host (or (get-text-property 1 :val sel-host) (list sel-host))))
187+ ; ; remove the label
188+ (if (= 3 (length host)) (cdr host) host)))
189+
190+ (defun cider--completing-read-port (host ports )
191+ " Interactively select port for HOST from PORTS."
192+ (let* ((sel-port (completing-read (format " Port for %s : " host)
193+ (cider-join-with-val-prop ports)))
194+ (port (or (get-text-property 1 :val sel-port) sel-port)))
195+ (if (listp port) (second port) port)))
196+
197+ (defun cider-locate-running-nrepl-ports ()
198+ " Locate ports of running nREPL servers.
199+ Return a list of list of the form (project-dir port)."
200+ (let ((paths (cider--get-running-nrepl-paths)))
201+ (delq nil
202+ (mapcar (lambda (f )
203+ (-when-let (port-file (or (cider--file-path (concat f " /.nrepl-port" ))
204+ (cider--file-path (concat f " /repl-port" ))))
205+ (with-temp-buffer
206+ (insert-file-contents port-file)
207+ (list (file-name-nondirectory f) (buffer-string )))))
208+ paths))))
209+
210+ (defun cider--get-running-nrepl-paths ()
211+ " Retrieve project paths of running nREPL servers.
212+ use `cider-ps-running-nrepls-command' and `cider-ps-running-nrepl-path-regexp-list' ."
213+ (let (paths)
214+ (with-temp-buffer
215+ (insert (shell-command-to-string cider-ps-running-nrepls-command))
216+ (dolist (regexp cider-ps-running-nrepl-path-regexp-list)
217+ (goto-char 1 )
218+ (while (re-search-forward regexp nil t )
219+ (setq paths (cons (match-string 1 ) paths)))))
220+ (-distinct paths)))
165221
166- (define-obsolete-function-alias
167- 'cider
168- 'cider-connect )
222+ ; ; TODO: Implement a check for `cider-lein-command' over tramp
223+ (defun cider--lein-present-p ()
224+ " Check if `cider-lein-command' is on the `exec-path' .
225+
226+ In case `default-directory' is non-local we assume the command is available."
227+ (or (file-remote-p default-directory)
228+ (executable-find cider-lein-command)
229+ (executable-find (concat cider-lein-command " .bat" ))))
169230
170231;;;### autoload
171232(eval-after-load 'clojure-mode
172233 '(progn
173234 (define-key clojure-mode-map (kbd " C-c M-j" ) 'cider-jack-in )
174235 (define-key clojure-mode-map (kbd " C-c M-c" ) 'cider-connect )))
175236
237+
238+ (define-obsolete-function-alias 'cider 'cider-connect )
239+
176240(provide 'cider )
177241
178242; ;; cider.el ends here
0 commit comments