emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/inf-clojure 8ad2242b75: [#209] Implement a command that st


From: ELPA Syncer
Subject: [nongnu] elpa/inf-clojure 8ad2242b75: [#209] Implement a command that starts a socket REPL (inf-clojure-socket-repl) (#210)
Date: Sun, 26 Mar 2023 16:01:38 -0400 (EDT)

branch: elpa/inf-clojure
commit 8ad2242b75ea1a06fad391deb8f858a392408a94
Author: Michael Bruce <mikepjb@gmail.com>
Commit: GitHub <noreply@github.com>

    [#209] Implement a command that starts a socket REPL 
(inf-clojure-socket-repl) (#210)
---
 CHANGELOG.md   |   1 +
 README.md      |   3 +
 inf-clojure.el | 178 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 156 insertions(+), 26 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a77b1e5075..836cd49da4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 * [#202](https://github.com/clojure-emacs/inf-clojure/issues/202): Add 
ClojureCLR support.
 * [#204](https://github.com/clojure-emacs/inf-clojure/issues/204): Scroll repl 
buffer on insert commands
 * [#208](https://github.com/clojure-emacs/inf-clojure/pull/208) Display 
message after setting repl.
+* [#210](https://github.com/clojure-emacs/inf-clojure/pull/210) Include 
`inf-clojure-socket-repl` to create a socket REPL and connect to it from inside 
Emacs. 
 
 
 ## 3.2.1 (2022-07-22)
diff --git a/README.md b/README.md
index a0ec0dd969..7a022aa3a1 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,9 @@ common startup forms. You can select one of these or type 
in your own
 custom startup. This will start a REPL process for the current project
 and you can start interacting with it.
 
+If you want to use a socket REPL server, use `M-x inf-clojure-socket-repl`
+which will start a socket server and connect to it for you.
+
 If you've already started a socket REPL server, use `M-x inf-clojure-connect`
 and enter its host and port numbers.
 
diff --git a/inf-clojure.el b/inf-clojure.el
index 3e86834de8..6f8d2169a2 100644
--- a/inf-clojure.el
+++ b/inf-clojure.el
@@ -8,7 +8,7 @@
 ;; URL: http://github.com/clojure-emacs/inf-clojure
 ;; Keywords: processes, comint, clojure
 ;; Version: 3.2.1
-;; Package-Requires: ((emacs "25.1") (clojure-mode "5.11"))
+;; Package-Requires: ((emacs "26.2") (clojure-mode "5.11"))
 
 ;; This file is not part of GNU Emacs.
 
@@ -74,7 +74,7 @@
 (defvar inf-clojure-startup-forms '((lein . "lein repl")
                                     (boot . "boot repl")
                                     (clojure . "clojure")
-                                    (cljs . "clojure -m cljs.main -r")
+                                    (cljs . "clojure -M -m cljs.main -r")
                                     (lein-clr . "lein clr repl")
                                     (planck . "planck -d")
                                     (babashka . "bb")
@@ -140,6 +140,17 @@
                  (set-ns . "(clojure.core/in-ns '%s)")
                  (macroexpand . "(clojure.core/macroexpand '%s)")
                  (macroexpand-1 . "(clojure.core/macroexpand-1 '%s)")))
+    (node-babashka . ((load . "(clojure.core/load-file \"%s\")")
+                 (doc . "(clojure.repl/doc %s)")
+                 (source . "(clojure.repl/source %s)")
+                 (arglists .
+                           "(try (-> '%s clojure.core/resolve 
clojure.core/meta :arglists)
+                              (catch Throwable e nil))")
+                 (apropos . "(doseq [var (sort (clojure.repl/apropos \"%s\"))] 
(println (str var)))")
+                 (ns-vars . "(clojure.repl/dir %s)")
+                 (set-ns . "(clojure.core/in-ns '%s)")
+                 (macroexpand . "(clojure.core/macroexpand '%s)")
+                 (macroexpand-1 . "(clojure.core/macroexpand-1 '%s)")))
     (clojure . ((load . "(clojure.core/load-file \"%s\")")
                 (doc . "(clojure.repl/doc %s)")
                 (source . "(clojure.repl/source %s)")
@@ -641,33 +652,34 @@ Customization: Entry to this mode runs the hooks on 
`comint-mode-hook' and
 
 You can send text to the inferior Clojure process from other buffers containing
 Clojure source.
-    `inf-clojure-switch-to-repl' switches the current buffer to the Clojure 
process buffer.
+    `inf-clojure-switch-to-repl' switches the current buffer to the Clojure
+    process buffer.
     `inf-clojure-eval-defun' sends the current defun to the Clojure process.
     `inf-clojure-eval-region' sends the current region to the Clojure process.
 
     Prefixing the inf-clojure-eval/defun/region commands with
-    a \\[universal-argument] causes a switch to the Clojure process buffer 
after sending
-    the text.
+    a \\[universal-argument] causes a switch to the Clojure process buffer 
after
+    sending the text.
 
 Commands:\\<inf-clojure-mode-map>
-\\[comint-send-input] after the end of the process' output sends the text from 
the
-    end of process to point.
-\\[comint-send-input] before the end of the process' output copies the sexp 
ending at point
-    to the end of the process' output, and sends it.
-\\[comint-copy-old-input] copies the sexp ending at point to the end of the 
process' output,
-    allowing you to edit it before sending it.
-If `comint-use-prompt-regexp' is nil (the default), \\[comint-insert-input] on 
old input
-   copies the entire old input to the end of the process' output, allowing
-   you to edit it before sending it.  When not used on old input, or if
-   `comint-use-prompt-regexp' is non-nil, \\[comint-insert-input] behaves 
according to
-   its global binding.
+\\[comint-send-input] after the end of the process' output sends the text from
+    the end of process to point.
+\\[comint-send-input] before the end of the process' output copies the sexp
+    ending at point to the end of the process' output, and sends it.
+\\[comint-copy-old-input] copies the sexp ending at point to the end of the
+    process' output,allowing you to edit it before sending it.
+If `comint-use-prompt-regexp' is nil (the default), \\[comint-insert-input] on
+   old input copies the entire old input to the end of the process' output,
+   allowing you to edit it before sending it.  When not used on old input, or 
if
+   `comint-use-prompt-regexp' is non-nil, \\[comint-insert-input] behaves
+   according to its global binding.
 \\[backward-delete-char-untabify] converts tabs to spaces as it moves back.
 \\[clojure-indent-line] indents for Clojure; with argument, shifts rest
     of expression rigidly with the current line.
-\\[indent-sexp] does \\[clojure-indent-line] on each line starting within 
following expression.
-Paragraphs are separated only by blank lines.  Semicolons start comments.
-If you accidentally suspend your process, use \\[comint-continue-subjob]
-to continue it."
+\\[indent-sexp] does \\[clojure-indent-line] on each line starting within
+  following expression.  Paragraphs are separated only by blank lines.
+  Semicolons start comments.  If you accidentally suspend your process,
+  use \\[comint-continue-subjob] to continue it."
   (setq comint-input-sender 'inf-clojure--send-string)
   (setq comint-prompt-regexp inf-clojure-comint-prompt-regexp)
   (setq mode-line-process '(":%s"))
@@ -807,9 +819,11 @@ process buffer for a list of commands.)"
                                           nil
                                           'confirm-after-completion))))
   (let* ((project-dir (clojure-project-dir))
-         (process-buffer-name (if project-dir
-                                  (format "inf-clojure %s" 
(inf-clojure--project-name project-dir))
-                                "inf-clojure"))
+         (process-buffer-name (or
+                               inf-clojure-custom-repl-name
+                               (if project-dir
+                                   (format "inf-clojure %s" 
(inf-clojure--project-name project-dir))
+                                 "inf-clojure")))
          ;; comint adds the asterisks to both sides
          (repl-buffer-name (format "*%s*" process-buffer-name)))
     ;; Create a new comint buffer if needed
@@ -819,10 +833,11 @@ process buffer for a list of commands.)"
             (cmdlist (if (consp cmd)
                          (list cmd)
                        (split-string-and-unquote cmd)))
-            (repl-type (or (unless prefix-arg
+            (repl-type (or inf-clojure-socket-repl-type
+                           (unless prefix-arg
                              inf-clojure-custom-repl-type)
-                           (car (rassoc cmd inf-clojure-startup-forms))
-                           (inf-clojure--prompt-repl-type))))
+                        (car (rassoc cmd inf-clojure-startup-forms))
+                        (inf-clojure--prompt-repl-type))))
         (message "Starting Clojure REPL via `%s'..." cmd)
         (with-current-buffer (apply #'make-comint
                                     process-buffer-name (car cmdlist) nil (cdr 
cmdlist))
@@ -843,6 +858,117 @@ HOST is the host the process is running on, PORT is where 
it's listening."
   (interactive "shost: \nnport: ")
   (inf-clojure (cons host port)))
 
+(defvar-local inf-clojure-socket-callback nil
+  "Used to transfer state between the socket process buffer & REPL buffer.")
+
+(defvar-local inf-clojure-socket-buffer nil
+  "Used to kill the associated socket buffer when it's REPL buffer is killed.")
+
+(defun inf-clojure-socket-filter (process output)
+  "A filter that gets triggered each time the socket receives new OUTPUT.
+This function prints out the output received but also
+watches for a prompt using the `inf-clojure-prompt' regexp, once
+this happens a callback is triggered if available.  The callback
+is intended to be used to trigger a `inf-clojure-connect' once we
+can determine that a socket REPL is ready to receive a
+connection.
+
+PROCESS is the process object that is being filtered.
+
+OUTPUT is the latest data received from the process"
+  (let ((server-buffer (process-buffer process)))
+    (when (buffer-live-p server-buffer)
+      (with-current-buffer server-buffer
+        (insert output)))
+    (let ((prompt-displayed (string-match inf-clojure-prompt output)))
+      (when prompt-displayed
+       (message (format "Socket REPL startup detected for %s" (process-name 
process)))
+       (with-current-buffer server-buffer
+         (when inf-clojure-socket-callback
+           (funcall inf-clojure-socket-callback)))))))
+
+(defun inf-clojure-socket-repl-sentinel (process event)
+  "Ensures socket REPL are cleaned up when the REPL buffer is closed.
+
+PROCESS is the process object that is connected to a socket REPL.
+
+EVENT is the event that triggered this function to be called."
+  (when (not (process-live-p process))
+    (let ((repl-buffer (process-buffer process)))
+      (with-current-buffer repl-buffer
+        (when inf-clojure-socket-buffer
+          (kill-buffer inf-clojure-socket-buffer))))))
+
+(defvar inf-clojure-socket-repl-startup-forms
+  '((lein . "JVM_OPTS='-Dclojure.server.repl={:port %d :accept 
clojure.core.server/repl}' lein repl")
+    (boot . "export BOOT_JVM_OPTIONS='-Dclojure.server.repl=\"{:port %d 
:accept clojure.core.server/repl}\"' boot repl")
+    (clojure . "clojure -J-Dclojure.server.repl=\"{:port %d :accept 
clojure.core.server/repl}\"")
+    (cljs . "clojure -J-Dclojure.server.repl=\"{:port %d :accept 
cljs.server.browser/repl}\"")
+    (lein-clr . "JVM_OPTS='-Dclojure.server.repl={:port %d :accept 
clojure.core.server/repl}' lein clr repl")
+    (planck . "planck -n %d")
+    (babashka . "bb socket-repl %d")))
+
+(defcustom inf-clojure-socket-repl-port
+  nil
+  "Port to be used when creating a socket REPL via `inf-clojure-socket-repl'.
+If left as nil a random port will be selected between 5500-6000."
+  :type '(choice integer (const nil))
+  :package-version '(inf-clojure . "3.3"))
+
+;;;###autoload
+(defun inf-clojure-socket-repl (cmd)
+  "Start a socket REPL server and connect to it via `inf-clojure'.
+CMD is the command line used to start the socket REPL, if this
+isn't provided you will be prompted to select from the defaults
+provided in `inf-clojure-socket-repl-startup-forms' or
+`inf-clojure-custom-startup' if this is defined."
+  (interactive (list (or (unless current-prefix-arg
+                           inf-clojure-custom-startup)
+                         (completing-read "Select Clojure socket REPL startup 
command: "
+                                          (mapcar #'cdr 
inf-clojure-socket-repl-startup-forms)
+                                          nil
+                                          'confirm-after-completion))))
+  (let* ((host "localhost")
+         (port (or inf-clojure-socket-repl-port (+ 5500 (random 500))))
+         (project-dir (clojure-project-dir))
+         (repl-type (or (unless prefix-arg
+                          inf-clojure-custom-repl-type)
+                        (car (rassoc cmd 
inf-clojure-socket-repl-startup-forms))
+                        (inf-clojure--prompt-repl-type)))
+         (project-name (inf-clojure--project-name (or project-dir 
"standalone")))
+         (socket-process-name (format "*%s-%s-socket-server*" project-name 
repl-type))
+         (socket-buffer-name (format "*%s-%s-socket*" project-name repl-type))
+         (socket-buffer (get-buffer-create socket-buffer-name))
+         (repl-buffer-name (format "%s-%s-repl" project-name repl-type))
+         (socket-form (or cmd
+                          (cdr (assoc repl-type 
inf-clojure-socket-repl-startup-forms))
+                          inf-clojure-custom-startup))
+         (socket-cmd (format socket-form port))
+         (sock (let ((default-directory (or project-dir default-directory)))
+                 (start-file-process-shell-command
+                  socket-process-name socket-buffer
+                  socket-cmd))))
+    (with-current-buffer socket-buffer
+      (setq-local
+       inf-clojure-socket-callback
+       (lambda ()
+         (let ((with-process-repl-buffer-name (concat "*" repl-buffer-name 
"*")))
+           (setq inf-clojure-socket-repl-type
+                 repl-type
+                 inf-clojure-custom-repl-name
+                 repl-buffer-name
+                 repl-buffer
+                 (get-buffer-create with-process-repl-buffer-name))
+           (inf-clojure-connect host port)
+           (with-current-buffer with-process-repl-buffer-name
+             (setq inf-clojure-socket-buffer socket-buffer))
+           (set-process-sentinel
+            (get-buffer-process (get-buffer with-process-repl-buffer-name))
+            #'inf-clojure-socket-repl-sentinel)))))
+    (set-process-filter sock #'inf-clojure-socket-filter)
+    (message "Starting %s socket REPL server at %s:%d with %s" repl-type host 
port socket-cmd)))
+
+
 (defun inf-clojure--forms-without-newlines (str)
   "Remove newlines between toplevel forms.
 STR is a string of contents to be evaluated.  When sending



reply via email to

[Prev in Thread] Current Thread [Next in Thread]