branch: elpa/inf-clojure
commit ece16ffeefc5ab23a31012d10765c34197510220
Author: Roman Rudakov <rruda...@fastmail.com>
Commit: Bozhidar Batsov <bozhi...@batsov.dev>

    Add clojure-ts-mode support
---
 CHANGELOG.md   |   5 +-
 README.md      |  16 +++++++
 inf-clojure.el | 148 +++++++++++++++++++++++++++++++++++++--------------------
 3 files changed, 115 insertions(+), 54 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7e8a9bc0a..e824df5fd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,13 @@
 # Changelog
 
 ## master (unreleased)
+
 * Improve support for multiple forms in the same line by replacing 
beginning-of-defun fn.
 * [#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. 
-
+* [#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.
+- [#217](https://github.com/clojure-emacs/inf-clojure/pull/217): Add 
`clojure-ts-mode` support.
 
 ## 3.2.1 (2022-07-22)
 
diff --git a/README.md b/README.md
index 7a022aa3a1..fb81008cb0 100644
--- a/README.md
+++ b/README.md
@@ -103,6 +103,10 @@ You can also add the following to your Emacs config to 
enable
 
 ```emacs-lisp
 (add-hook 'clojure-mode-hook #'inf-clojure-minor-mode)
+
+;; or if you're a `clojure-ts-mode' user:
+
+(add-hook 'clojure-ts-mode-hook #'inf-clojure-minor-mode)
 ```
 
 **Warning:** Don't enable `inf-clojure-minor-mode` and `cider-mode` at the 
same time. They
@@ -217,6 +221,18 @@ If you want to update a specific form there is a function
 (inf-clojure-update-feature 'clojure 'completion "(incomplete.core/completions 
\"%s\")")
 ```
 
+### `clojure-ts-mode` support
+
+`inf-clojure` will try to use `clojure-ts-mode` by default if it's
+available with fallback to `clojure-mode`.
+
+If you want to use `inf-clojure` with `clojure-mode` exclusively, you
+can set it to:
+
+```emacs-lisp
+(setopt inf-clojure-source-modes '(clojure-mode))
+```
+
 #### Caveats
 
 As `inf-clojure` is built on top of `comint` it has all the usual comint 
limitations -
diff --git a/inf-clojure.el b/inf-clojure.el
index 9efe1de4bc..5634f9a636 100644
--- a/inf-clojure.el
+++ b/inf-clojure.el
@@ -65,11 +65,13 @@
 
 (require 'comint)
 (require 'clojure-mode)
+(require 'clojure-ts-mode nil :no-error)
 (require 'eldoc)
 (require 'thingatpt)
 (require 'ansi-color)
 (require 'cl-lib)
 (require 'subr-x)
+(require 'project)
 
 (defvar inf-clojure-startup-forms '((lein . "lein repl")
                                     (boot . "boot repl")
@@ -193,10 +195,10 @@ either `setq-local` or an entry in `.dir-locals.el`." )
 MULTIPLE PROCESS SUPPORT
 ===========================================================================
 To run multiple Clojure processes, you start the first up
-with \\[inf-clojure].  It will be in a buffer named `*inf-clojure*'.
+with \\[inf-clojure].  It will be in a buffer named *inf-clojure*.
 Rename this buffer with \\[rename-buffer].  You may now start up a new
 process with another \\[inf-clojure].  It will be in a new buffer,
-named `*inf-clojure*'.  You can switch between the different process
+named *inf-clojure*.  You can switch between the different process
 buffers with \\[switch-to-buffer].
 
 Commands that send text from source buffers to Clojure processes --
@@ -205,7 +207,7 @@ process to send to, when you have more than one Clojure 
process around.  This
 is determined by the global variable `inf-clojure-buffer'.  Suppose you
 have three inferior Clojures running:
     Buffer              Process
-    foo                 inf-clojure
+    foo                 `inf-clojure'
     bar                 inf-clojure<2>
     *inf-clojure*     inf-clojure<3>
 If you do a \\[inf-clojure-eval-defun] command on some Clojure source code,
@@ -269,7 +271,7 @@ has been found.  See also variable `inf-clojure-buffer'."
         (error "No Clojure subprocess; see variable `inf-clojure-buffer'"))))
 
 (defun inf-clojure-repl-p (&optional buf)
-  "Indicates if BUF is an inf-clojure REPL.
+  "Indicates if BUF is an `inf-clojure' REPL.
 If BUF is nil then defaults to the current buffer.
 Checks the mode and that there is a live process."
   (let ((buf (or buf (current-buffer))))
@@ -278,7 +280,7 @@ Checks the mode and that there is a live process."
          (process-live-p (get-buffer-process buf)))))
 
 (defun inf-clojure-repls ()
-  "Return a list of all inf-clojure REPL buffers."
+  "Return a list of all `inf-clojure' REPL buffers."
   (let (repl-buffers)
     (dolist (b (buffer-list))
       (when (inf-clojure-repl-p b)
@@ -286,27 +288,27 @@ Checks the mode and that there is a live process."
     repl-buffers))
 
 (defun inf-clojure--prompt-repl-buffer (prompt)
-  "Prompt the user to select an inf-clojure repl buffer.
+  "Prompt the user to select an `inf-clojure' repl buffer.
 PROMPT is a string to prompt the user.
 Returns nil when no buffer is selected."
   (let ((repl-buffers (inf-clojure-repls)))
     (if (> (length repl-buffers) 0)
-        (when-let ((repl-buffer (completing-read prompt repl-buffers nil t)))
+        (when-let* ((repl-buffer (completing-read prompt repl-buffers nil t)))
           (get-buffer repl-buffer))
       (user-error "No buffers have an inf-clojure process"))))
 
 (defun inf-clojure-set-repl (always-ask)
-  "Set an inf-clojure buffer as the active (default) REPL.
+  "Set an `inf-clojure' buffer as the active (default) REPL.
 If in a REPL buffer already, use that unless a prefix is used (or
-ALWAYS-ASK).  Otherwise get a list of all active inf-clojure
+ALWAYS-ASK).  Otherwise get a list of all active `inf-clojure'
 REPLS and offer a choice.  It's recommended to rename REPL
 buffers after they are created with `rename-buffer'."
   (interactive "P")
-  (when-let ((new-repl-buffer
-              (if (or always-ask
-                      (not (inf-clojure-repl-p)))
-                  (inf-clojure--prompt-repl-buffer "Select default REPL: ")
-                (current-buffer))))
+  (when-let* ((new-repl-buffer
+               (if (or always-ask
+                       (not (inf-clojure-repl-p)))
+                   (inf-clojure--prompt-repl-buffer "Select default REPL: ")
+                 (current-buffer))))
     (setq inf-clojure-buffer new-repl-buffer)
     (message "Current inf-clojure REPL set to %s" new-repl-buffer)))
 
@@ -349,6 +351,14 @@ mode.  Default is whitespace followed by 0 or 1 
single-letter colon-keyword
 \(as in :a, :c, etc.)"
   :type 'regexp)
 
+(defcustom inf-clojure-source-modes '(clojure-ts-mode clojure-mode)
+  "Used to determine if a buffer contains Clojure source code.
+
+Any buffer with one of these major modes, it's considered a Clojure
+source file by all `inf-clojure' commands."
+  :type '(repeat symbol)
+  :safe #'symbolp)
+
 (defun inf-clojure--modeline-info ()
   "Return modeline info for `inf-clojure-minor-mode'.
 Either \"no process\" or \"buffer-name(repl-type)\""
@@ -453,7 +463,7 @@ The value of this variable is a mode line template as in
 `mode-line-format'.  See Info Node `(elisp)Mode Line Format' for details
 about mode line templates.
 
-Customize this variable to change how inf-clojure-minor-mode
+Customize this variable to change how `inf-clojure-minor-mode'
 displays its status in the mode line.  The default value displays
 the current REPL.  Set this variable to nil to disable the
 mode line entirely."
@@ -609,24 +619,35 @@ This should usually be a combination of 
`inf-clojure-prompt' and
   :package-version '(inf-clojure . "2.0.0"))
 
 (defcustom inf-clojure-auto-mode t
-  "Automatically enable inf-clojure-minor-mode.
+  "Automatically enable `inf-clojure-minor-mode'.
 All buffers in `clojure-mode' will automatically be in
 `inf-clojure-minor-mode' unless set to nil."
   :type 'boolean
   :safe #'booleanp
   :package-version '(inf-clojure . "3.1.0"))
 
+(defun inf-clojure--get-preferred-major-modes ()
+  "Return list of preferred major modes that are actually available."
+  (cl-remove-if-not (lambda (mode) (featurep mode))
+                    inf-clojure-source-modes))
+
+(defun inf-clojure--clojure-buffer-p ()
+  "Return TRUE if the current buffer is a Clojure buffer."
+  (derived-mode-p (inf-clojure--get-preferred-major-modes)))
+
 (defun inf-clojure--clojure-buffers ()
   "Return a list of all existing `clojure-mode' buffers."
-  (cl-remove-if-not
-   (lambda (buffer) (with-current-buffer buffer (derived-mode-p 
'clojure-mode)))
-   (buffer-list)))
+  (cl-remove-if-not (lambda (buffer)
+                      (with-current-buffer buffer
+                        (inf-clojure--clojure-buffer-p)))
+                    (buffer-list)))
 
 (defun inf-clojure-enable-on-existing-clojure-buffers ()
   "Enable inf-clojure's minor mode on existing Clojure buffers.
 See command `inf-clojure-minor-mode'."
   (interactive)
-  (add-hook 'clojure-mode-hook #'inf-clojure-minor-mode)
+  (dolist (mode (inf-clojure--get-preferred-major-modes))
+    (add-hook (derived-mode-hook-name mode) #'inf-clojure-minor-mode))
   (dolist (buffer (inf-clojure--clojure-buffers))
     (with-current-buffer buffer
       (inf-clojure-minor-mode +1))))
@@ -688,6 +709,8 @@ If `comint-use-prompt-regexp' is nil (the default), 
\\[comint-insert-input] on
   (setq comint-input-sender 'inf-clojure--send-string)
   (setq comint-prompt-regexp inf-clojure-comint-prompt-regexp)
   (setq mode-line-process '(":%s"))
+  ;; NOTE: Using Tree-sitter based syntax highlighting in comint
+  ;; buffer is currently not possible.
   (clojure-mode-variables)
   (clojure-font-lock-setup)
   (when inf-clojure-enable-eldoc
@@ -799,10 +822,24 @@ to suppress the usage of the target buffer discovery 
logic."
 The name is simply the final segment of the path."
   (file-name-nondirectory (directory-file-name dir)))
 
+(defun inf-clojure--project-dir ()
+  "Return current Clojure project root."
+  (let ((project-vc-extra-root-markers '("project.clj"      ; Leiningen
+                                         "build.boot"       ; Boot
+                                         "build.gradle"     ; Gradle
+                                         "build.gradle.kts" ; Gradle
+                                         "deps.edn"         ; Clojure CLI 
(a.k.a. tools.deps)
+                                         "shadow-cljs.edn"  ; shadow-cljs
+                                         "bb.edn"           ; babashka
+                                         "nbb.edn"          ; nbb
+                                         "basilisp.edn"     ; Basilisp (Python)
+                                         )))
+    (project-root (project-current))))
+
 ;;;###autoload
 (defun inf-clojure (cmd &optional suppress-message)
-  "Run an inferior Clojure process, input and output via buffer 
`*inf-clojure*'.
-If there is a process already running in `*inf-clojure*', just
+  "Run an inferior Clojure process, input and output via buffer *inf-clojure*.
+If there is a process already running in *inf-clojure*, just
 switch to that buffer.
 
 CMD is a string which serves as the startup command or a cons of
@@ -826,7 +863,7 @@ process buffer for a list of commands.)"
                                           (mapcar #'cdr 
inf-clojure-startup-forms)
                                           nil
                                           'confirm-after-completion))))
-  (let* ((project-dir (clojure-project-dir))
+  (let* ((project-dir (inf-clojure--project-dir))
          (process-buffer-name (or
                                inf-clojure-custom-repl-name
                                (if project-dir
@@ -850,7 +887,9 @@ process buffer for a list of commands.)"
         (with-current-buffer (apply #'make-comint
                                     process-buffer-name (car cmdlist) nil (cdr 
cmdlist))
           (inf-clojure-mode)
-          (set-syntax-table clojure-mode-syntax-table)
+          (set-syntax-table (pcase (car 
(inf-clojure--get-preferred-major-modes))
+                              ('clojure-ts-mode clojure-ts-mode-syntax-table)
+                              (_ clojure-mode-syntax-table)))
           (setq-local inf-clojure-repl-type repl-type)
           (hack-dir-local-variables-non-file-buffer))))
     ;; update the default comint buffer and switch to it
@@ -940,7 +979,7 @@ defaults provided in 
`inf-clojure-socket-repl-startup-forms'."
                                           'confirm-after-completion))))
   (let* ((host "localhost")
          (port (or inf-clojure-socket-repl-port (+ 5500 (random 500))))
-         (project-dir (clojure-project-dir))
+         (project-dir (inf-clojure--project-dir))
          (repl-type (or (unless prefix-arg
                           inf-clojure-custom-repl-type)
                         (car (rassoc cmd 
inf-clojure-socket-repl-startup-forms))
@@ -978,7 +1017,8 @@ of forms."
   (condition-case nil
       (with-temp-buffer
         (progn
-          (clojurec-mode)
+          ;; Activate preferred major mode.
+          (funcall (car (inf-clojure--get-preferred-major-modes)))
           (insert str)
           (whitespace-cleanup)
           (goto-char (point-min))
@@ -986,8 +1026,10 @@ of forms."
             (while (looking-at "\n")
               (delete-char 1))
             (unless (eobp)
-              (clojure-forward-logical-sexp))
-            (unless (eobp)
+              ;; NOTE: There is no special API for that in
+              ;; `clojure-ts-mode', so probably for now lets keep this
+              ;; `clojure-mode' function.
+              (clojure-forward-logical-sexp)
               (forward-char)))
           (buffer-substring-no-properties (point-min) (point-max))))
     (scan-error str)))
@@ -1105,13 +1147,6 @@ START and END are the beginning and end positions in the 
buffer to send."
 This holds a cons cell of the form `(DIRECTORY . FILE)'
 describing the last `inf-clojure-load-file' command.")
 
-(defcustom inf-clojure-source-modes '(clojure-mode)
-  "Used to determine if a buffer contains Clojure source code.
-If it's loaded into a buffer that is in one of these major modes, it's
-considered a Clojure source file by `inf-clojure-load-file'.
-Used by this command to determine defaults."
-  :type '(repeat symbol))
-
 (defun inf-clojure-load-file (&optional switch-to-repl file-name)
   "Load a Clojure file into the inferior Clojure process.
 
@@ -1123,7 +1158,7 @@ is present it will be used instead of the current file."
          (file-name (or file-name
                         (car (comint-get-source "Load Clojure file: " 
inf-clojure-prev-l/c-dir/file
                                                 ;; nil because doesn't need an 
exact name
-                                                inf-clojure-source-modes 
nil))))
+                                                
(inf-clojure--get-preferred-major-modes) nil))))
          (load-form (inf-clojure-get-feature proc 'load)))
     (comint-check-source file-name) ; Check to see if buffer needs saved.
     (setq inf-clojure-prev-l/c-dir/file (cons (file-name-directory    
file-name)
@@ -1132,23 +1167,32 @@ is present it will be used instead of the current file."
     (when switch-to-repl
       (inf-clojure-switch-to-repl t))))
 
+(defun inf-clojure--find-ns ()
+  "Return the namespace of the current Clojure buffer.
+
+This function delegates its job to an appropritate function, considering
+`inf-clojure-source-modes'."
+  (pcase (car (inf-clojure--get-preferred-major-modes))
+    ('clojure-ts-mode (clojure-ts-find-ns))
+    (_ (clojure-find-ns))))
+
 (defun inf-clojure-reload (arg)
   "Send a query to the inferior Clojure for reloading the namespace.
-See variable `inf-clojure-reload-form' and
+See variable `inf-clojure-reload-form' and variable
 `inf-clojure-reload-all-form'.
 
 The prefix argument ARG can change the behavior of the command:
 
-  - C-u M-x `inf-clojure-reload': prompts for a namespace name.
-  - M-- M-x `inf-clojure-reload': executes (require ... :reload-all).
-  - M-- C-u M-x `inf-clojure-reload': reloads all AND prompts."
+  - \\`C-u' \\[inf-clojure-reload]: prompts for a namespace name.
+  - \\`M--' \\[inf-clojure-reload]: executes (require ... :reload-all).
+  - \\`M--' \\`C-u' \\[inf-clojure-reload]: reloads all AND prompts."
   (interactive "P")
   (let* ((proc (inf-clojure-proc))
          (reload-all-p (or (equal arg '-) (equal arg '(-4))))
          (prompt-p (or (equal arg '(4)) (equal arg '(-4))))
          (ns (if prompt-p
-                (car (inf-clojure-symprompt "Namespace" (clojure-find-ns)))
-              (clojure-find-ns)))
+                (car (inf-clojure-symprompt "Namespace" 
(inf-clojure--find-ns)))
+              (inf-clojure--find-ns)))
          (form (if (not reload-all-p)
                    (inf-clojure-reload-form proc)
                  (inf-clojure-reload-all-form proc))))
@@ -1249,7 +1293,7 @@ STRING if present."
                             (prin1-to-string (substring-no-properties 
string))))
                   nil
                   (expand-file-name inf-clojure--log-file-name
-                                    (clojure-project-dir))
+                                    (inf-clojure--project-dir))
                   'append
                   'no-annoying-write-file-in-minibuffer)))
 
@@ -1357,11 +1401,11 @@ for evaluation, therefore FORM should not include it."
 (defun inf-clojure-arglists (fn)
   "Send a query to the inferior Clojure for the arglists for function FN.
 See variable `inf-clojure-arglists-form'."
-  (when-let ((proc (inf-clojure-proc 'no-error)))
-    (when-let ((arglists-form (inf-clojure-get-feature proc 'arglists)))
-      (thread-first (format arglists-form fn)
-        (inf-clojure--process-response proc "(" ")")
-        (inf-clojure--some)))))
+  (when-let* ((proc (inf-clojure-proc 'no-error))
+              (arglists-form (inf-clojure-get-feature proc 'arglists)))
+    (thread-first (format arglists-form fn)
+                  (inf-clojure--process-response proc "(" ")")
+                  (inf-clojure--some))))
 
 (defun inf-clojure-show-arglists (prompt-for-symbol)
   "Show the arglists for function FN in the mini-buffer.
@@ -1383,8 +1427,8 @@ prefix argument PROMPT-FOR-NS, it prompts for a namespace 
name."
   (interactive "P")
   (let* ((proc (inf-clojure-proc))
          (ns (if prompt-for-ns
-                 (car (inf-clojure-symprompt "Ns vars" (clojure-find-ns)))
-               (clojure-find-ns)))
+                 (car (inf-clojure-symprompt "Ns vars" (inf-clojure--find-ns)))
+               (inf-clojure--find-ns)))
          (ns-vars-form (inf-clojure-get-feature proc 'ns-vars)))
     (inf-clojure--send-string proc (format ns-vars-form ns))))
 
@@ -1396,8 +1440,8 @@ PROMPT-FOR-NS, it prompts for a namespace name."
   (interactive "P")
   (let* ((proc (inf-clojure-proc))
          (ns (if prompt-for-ns
-                 (car (inf-clojure-symprompt "Set ns to" (clojure-find-ns)))
-               (clojure-find-ns)))
+                 (car (inf-clojure-symprompt "Set ns to" 
(inf-clojure--find-ns)))
+               (inf-clojure--find-ns)))
          (set-ns-form (inf-clojure-get-feature proc 'set-ns)))
     (when (or (not ns) (equal ns ""))
       (user-error "No namespace selected"))

Reply via email to