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

    Introduce clojure-ts-add-arity refactoring command
---
 CHANGELOG.md                                    |   1 +
 README.md                                       |   8 +
 clojure-ts-mode.el                              | 213 ++++++++++++--
 test/clojure-ts-mode-refactor-add-arity-test.el | 350 ++++++++++++++++++++++++
 test/samples/refactoring.clj                    |  40 +++
 test/test-helper.el                             |   2 +-
 6 files changed, 584 insertions(+), 30 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2413e2459..600eabd011 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 - [#90](https://github.com/clojure-emacs/clojure-ts-mode/pull/90): Introduce 
`clojure-ts-cycle-privacy`.
 - [#91](https://github.com/clojure-emacs/clojure-ts-mode/pull/91): Introduce 
`clojure-ts-cycle-keyword-string`.
 - [#92](https://github.com/clojure-emacs/clojure-ts-mode/pull/92): Add 
commands to convert between collections types.
+- [#93](https://github.com/clojure-emacs/clojure-ts-mode/pull/93): Introduce 
`clojure-ts-add-arity`.
 
 ## 0.3.0 (2025-04-15)
 
diff --git a/README.md b/README.md
index 0891515b4f..75972c7405 100644
--- a/README.md
+++ b/README.md
@@ -411,6 +411,13 @@ set. The following commands are available:
 - `clojure-ts-convert-collection-to-vector`
 - `clojure-ts-convert-collection-to-set`
 
+### Add arity to a function or macro
+
+`clojure-ts-add-arity`: Add a new arity to an existing single-arity or
+multi-arity function or macro. Function can be defined using `defn`, `fn` or
+`defmethod` form. This command also supports functions defined inside forms 
like
+`letfn`, `defprotol`, `reify` or `proxy`.
+
 ### Default keybindings
 
 | Keybinding                  | Command                                        
|
@@ -427,6 +434,7 @@ set. The following commands are available:
 | `C-c C-r {` / `C-c C-r C-{` | `clojure-ts-convert-collection-to-map`         
|
 | `C-c C-r [` / `C-c C-r C-[` | `clojure-ts-convert-collection-to-vector`      
|
 | `C-c C-r #` / `C-c C-r C-#` | `clojure-ts-convert-collection-to-set`         
|
+| `C-c C-r a` / `C-c C-r C-a` | `clojure-ts-add-arity`                         
|
 
 ### Customize refactoring commands prefix
 
diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el
index 204126c2f0..56fcd07bdc 100644
--- a/clojure-ts-mode.el
+++ b/clojure-ts-mode.el
@@ -757,6 +757,10 @@ literals with regex grammar."
   "Return non-nil if NODE is a Clojure list."
   (string-equal "list_lit" (treesit-node-type node)))
 
+(defun clojure-ts--vec-node-p (node)
+  "Return non-nil if NODE is a Clojure vector."
+  (string-equal "vec_lit" (treesit-node-type node)))
+
 (defun clojure-ts--anon-fn-node-p (node)
   "Return non-nil if NODE is a Clojure function literal."
   (string-equal "anon_fn_lit" (treesit-node-type node)))
@@ -1471,6 +1475,27 @@ If JUSTIFY is non-nil, justify as well as fill the 
paragraph."
           (fill-paragraph justify)))
     t))
 
+(defun clojure-ts--list-node-sym-text (node &optional include-anon-fn-lit)
+  "Return text of the first child of the NODE if NODE is a list.
+
+Return nil if the NODE is not a list or if the first child is not a
+symbol.  Optionally if INCLUDE-ANON-FN-LIT is non-nil, return the text
+of the first symbol of a functional literal NODE."
+  (when (or (clojure-ts--list-node-p node)
+            (and include-anon-fn-lit
+                 (clojure-ts--anon-fn-node-p node)))
+    (when-let* ((first-child (clojure-ts--node-child-skip-metadata node 0))
+                ((clojure-ts--symbol-node-p first-child)))
+      (clojure-ts--named-node-text first-child))))
+
+(defun clojure-ts--list-node-sym-match-p (node regex &optional 
include-anon-fn-lit)
+  "Return TRUE if NODE is a list and its first symbol matches the REGEX.
+
+Optionally if INCLUDE-ANON-FN-LIT is TRUE, perform the same check for a
+function literal."
+  (when-let* ((sym-text (clojure-ts--list-node-sym-text node 
include-anon-fn-lit)))
+    (string-match-p regex sym-text)))
+
 (defconst clojure-ts--sexp-nodes
   '("#_" ;; transpose-sexp near a discard macro moves it around.
     "num_lit" "sym_lit" "kwd_lit" "nil_lit" "bool_lit"
@@ -1490,18 +1515,16 @@ If JUSTIFY is non-nil, justify as well as fill the 
paragraph."
 
 (defun clojure-ts--defun-node-p (node)
   "Return TRUE if NODE is a function or a var definition."
-  (and (clojure-ts--list-node-p node)
-       (let ((sym (clojure-ts--node-child-skip-metadata node 0)))
-         (string-match-p (rx bol
-                             (or "def"
-                                 "defn"
-                                 "defn-"
-                                 "definline"
-                                 "defrecord"
-                                 "defmacro"
-                                 "defmulti")
-                             eol)
-                         (clojure-ts--named-node-text sym)))))
+  (clojure-ts--list-node-sym-match-p node
+                                     (rx bol
+                                         (or "def"
+                                             "defn"
+                                             "defn-"
+                                             "definline"
+                                             "defrecord"
+                                             "defmacro"
+                                             "defmulti")
+                                         eol)))
 
 (defconst clojure-ts--markdown-inline-sexp-nodes
   '("inline_link" "full_reference_link" "collapsed_reference_link"
@@ -1727,19 +1750,23 @@ Forms between BEG and END are aligned according to
 
 ;;; Refactoring
 
+(defun clojure-ts--parent-until (pred)
+  "Return the closest parent of node at point that satisfies PRED."
+  (when-let* ((node-at-point (treesit-node-at (point) 'clojure t)))
+    (treesit-parent-until node-at-point pred t)))
+
+(defun clojure-ts--search-list-form-at-point (sym-regex &optional 
include-anon-fn-lit)
+  "Return the list node at point which first symbol matches SYM-REGEX.
+
+If INCLUDE-ANON-FN-LIT is non-nil, this function may also return a
+functional literal node."
+  (clojure-ts--parent-until
+   (lambda (node)
+     (clojure-ts--list-node-sym-match-p node sym-regex include-anon-fn-lit))))
+
 (defun clojure-ts--threading-sexp-node ()
   "Return list node at point which is a threading expression."
-  (when-let* ((node-at-point (treesit-node-at (point) 'clojure t)))
-    ;; We don't want to match `cond->' and `cond->>', so we should define a 
very
-    ;; specific regexp.
-    (let ((sym-regex (rx bol (* "some") "->" (* ">") eol)))
-      (treesit-parent-until node-at-point
-                            (lambda (node)
-                              (and (or (clojure-ts--list-node-p node)
-                                       (clojure-ts--anon-fn-node-p node))
-                                   (let ((first-child (treesit-node-child node 
0 t)))
-                                     (clojure-ts--symbol-matches-p sym-regex 
first-child))))
-                            t))))
+  (clojure-ts--search-list-form-at-point (rx bol (* "some") "->" (* ">") eol) 
t))
 
 (defun clojure-ts--delete-and-extract-sexp ()
   "Delete the surrounding sexp and return it."
@@ -1874,9 +1901,7 @@ With universal argument \\[universal-argument], fully 
unwinds thread."
            (n)
            (1)))
   (if-let* ((threading-sexp (clojure-ts--threading-sexp-node))
-            (sym (thread-first threading-sexp
-                               (treesit-node-child 0 t)
-                               (clojure-ts--named-node-text))))
+            (sym (clojure-ts--list-node-sym-text threading-sexp t)))
       (save-excursion
         (let ((beg (thread-first threading-sexp
                                  (treesit-node-start)
@@ -1962,9 +1987,7 @@ cannot be found."
   (interactive "p")
   (if-let* ((threading-sexp (clojure-ts--threading-sexp-node))
             ((clojure-ts--threadable-p threading-sexp))
-            (sym (thread-first threading-sexp
-                               (treesit-node-child 0 t)
-                               (clojure-ts--named-node-text))))
+            (sym (clojure-ts--list-node-sym-text threading-sexp t)))
       (let ((beg (thread-first threading-sexp
                                (treesit-node-start)
                                (copy-marker)))
@@ -2032,6 +2055,135 @@ value is `clojure-ts-thread-all-but-last'."
                     "-"))))
     (user-error "No defun at point")))
 
+(defun clojure-ts--node-child (node predicate)
+  "Return the first child of the NODE that matches the PREDICATE.
+
+PREDICATE can be a symbol representing a thing in
+`treesit-thing-settings', or a predicate, like regexp matching node
+type, etc.  See `treesit-thing-settings' for more details."
+  (thread-last (treesit-node-children node t)
+               (seq-find (lambda (child)
+                           (treesit-node-match-p child predicate t)))))
+
+(defun clojure-ts--node-start-skip-metadata (node)
+  "Return NODE start position optionally skipping metadata."
+  (if (clojure-ts--metadata-node-p (treesit-node-child node 0 t))
+      (treesit-node-start (treesit-node-child node 1))
+    (treesit-node-start node)))
+
+(defun clojure-ts--add-arity-internal (fn-node)
+  "Add an arity to a function defined by FN-NODE."
+  (let* ((first-coll (clojure-ts--node-child fn-node (rx bol (or "vec_lit" 
"list_lit") eol)))
+         (coll-start (clojure-ts--node-start-skip-metadata first-coll))
+         (line-parent (thread-first fn-node
+                                    (clojure-ts--node-child-skip-metadata 0)
+                                    (treesit-node-start)
+                                    (line-number-at-pos)))
+         (line-args (line-number-at-pos coll-start))
+         (same-line-p (= line-parent line-args))
+         (single-arity-p (clojure-ts--vec-node-p first-coll)))
+    (goto-char coll-start)
+    (when same-line-p
+      (newline-and-indent))
+    (when single-arity-p
+      (insert-pair 2 ?\( ?\))
+      (backward-up-list))
+    (insert "([])\n")
+    ;; Put the point between square brackets.
+    (down-list -2)))
+
+(defun clojure-ts--add-arity-defprotocol-internal (fn-node)
+  "Add an arity to a defprotocol function defined by FN-NODE."
+  (let* ((args-vec (clojure-ts--node-child fn-node (rx bol "vec_lit" eol)))
+         (args-vec-start (clojure-ts--node-start-skip-metadata args-vec))
+         (line-parent (thread-first fn-node
+                                    (clojure-ts--node-child-skip-metadata 0)
+                                    (treesit-node-start)
+                                    (line-number-at-pos)))
+         (line-args-vec (line-number-at-pos args-vec-start))
+         (same-line-p (= line-parent line-args-vec)))
+    (goto-char args-vec-start)
+    (insert "[]")
+    (if same-line-p
+        (insert " ")
+      ;; If args vector is not at the same line, respect this and place each 
new
+      ;; vector on a new line.
+      (newline-and-indent))
+    ;; Put the point between square brackets.
+    (down-list -1)))
+
+(defun clojure-ts--add-arity-reify-internal (fn-node)
+  "Add an arity to a reify function defined by FN-NODE."
+  (let* ((fn-name (clojure-ts--list-node-sym-text fn-node)))
+    (goto-char (clojure-ts--node-start-skip-metadata fn-node))
+    (insert "(" fn-name " [])")
+    (newline-and-indent)
+    ;; Put the point between sqare brackets.
+    (down-list -2)))
+
+(defun clojure-ts--letfn-defn-p (node)
+  "Return non-nil if NODE is a function definition in a letfn form."
+  (when-let* ((parent (treesit-node-parent node)))
+    (and (clojure-ts--list-node-p node)
+         (clojure-ts--vec-node-p parent)
+         (let ((grandparent (treesit-node-parent parent)))
+           (string= (clojure-ts--list-node-sym-text grandparent)
+                    "letfn")))))
+
+(defun clojure-ts--proxy-defn-p (node)
+  "Return non-nil if NODE is a function definition in a proxy form."
+  (when-let* ((parent (treesit-node-parent node)))
+    (and (clojure-ts--list-node-p node)
+         (string= (clojure-ts--list-node-sym-text parent) "proxy"))))
+
+(defun clojure-ts--defprotocol-defn-p (node)
+  "Return non-nil if NODE is a function definition in a defprotocol form."
+  (when-let* ((parent (treesit-node-parent node)))
+    (and (clojure-ts--list-node-p node)
+         (string= (clojure-ts--list-node-sym-text parent) "defprotocol"))))
+
+(defun clojure-ts--reify-defn-p (node)
+  "Return non-nil if NODE is a function definition in a reify form."
+  (when-let* ((parent (treesit-node-parent node)))
+    (and (clojure-ts--list-node-p node)
+         (string= (clojure-ts--list-node-sym-text parent) "reify"))))
+
+(defun clojure-ts-add-arity ()
+  "Add an arity to a function or macro."
+  (interactive)
+  (if-let* ((sym-regex (rx bol
+                           (or "defn"
+                               "letfn"
+                               "fn"
+                               "defmacro"
+                               "defmethod"
+                               "defprotocol"
+                               "reify"
+                               "proxy")
+                           eol))
+            (parent-def-node (clojure-ts--search-list-form-at-point sym-regex))
+            (parent-def-sym (clojure-ts--list-node-sym-text parent-def-node))
+            (fn-node (cond
+                      ((string= parent-def-sym "letfn")
+                       (clojure-ts--parent-until #'clojure-ts--letfn-defn-p))
+                      ((string= parent-def-sym "proxy")
+                       (clojure-ts--parent-until #'clojure-ts--proxy-defn-p))
+                      ((string= parent-def-sym "defprotocol")
+                       (clojure-ts--parent-until 
#'clojure-ts--defprotocol-defn-p))
+                      ((string= parent-def-sym "reify")
+                       (clojure-ts--parent-until #'clojure-ts--reify-defn-p))
+                      (t parent-def-node))))
+      (let ((beg-marker (copy-marker (treesit-node-start parent-def-node)))
+            (end-marker (copy-marker (treesit-node-end parent-def-node))))
+        (cond
+         ((string= parent-def-sym "defprotocol")
+          (clojure-ts--add-arity-defprotocol-internal fn-node))
+         ((string= parent-def-sym "reify")
+          (clojure-ts--add-arity-reify-internal fn-node))
+         (t (clojure-ts--add-arity-internal fn-node)))
+        (indent-region beg-marker end-marker))
+    (user-error "No suitable form to add an arity at point")))
+
 (defun clojure-ts-cycle-keyword-string ()
   "Convert the string at point to a keyword, or vice versa."
   (interactive)
@@ -2141,6 +2293,8 @@ before DELIM-OPEN."
     (keymap-set map "[" #'clojure-ts-convert-collection-to-vector)
     (keymap-set map "C-#" #'clojure-ts-convert-collection-to-set)
     (keymap-set map "#" #'clojure-ts-convert-collection-to-set)
+    (keymap-set map "C-a" #'clojure-ts-add-arity)
+    (keymap-set map "a" #'clojure-ts-add-arity)
     map)
   "Keymap for `clojure-ts-mode' refactoring commands.")
 
@@ -2155,6 +2309,7 @@ before DELIM-OPEN."
         ["Toggle between string & keyword" clojure-ts-cycle-keyword-string]
         ["Align expression" clojure-ts-align]
         ["Cycle privacy" clojure-ts-cycle-privacy]
+        ["Add function/macro arity" clojure-ts-add-arity]
         ("Convert collection"
          ["Convert to list" clojure-ts-convert-collection-to-list]
          ["Convert to quoted list" 
clojure-ts-convert-collection-to-quoted-list]
diff --git a/test/clojure-ts-mode-refactor-add-arity-test.el 
b/test/clojure-ts-mode-refactor-add-arity-test.el
new file mode 100644
index 0000000000..9c31f27ee4
--- /dev/null
+++ b/test/clojure-ts-mode-refactor-add-arity-test.el
@@ -0,0 +1,350 @@
+;;; clojure-ts-mode-refactor-add-arity-test.el --- Clojure[TS] Mode: refactor 
add arity test.  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025  Roman Rudakov
+
+;; Author: Roman Rudakov <rruda...@fastmail.com>
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Test for `clojure-ts-add-arity'
+
+;;; Code:
+
+(require 'clojure-ts-mode)
+(require 'buttercup)
+(require 'test-helper "test/test-helper")
+
+(describe "clojure-ts-add-arity"
+
+  (when-refactoring-with-point-it "should add an arity to a single-arity defn 
with args on same line"
+    "(defn foo [arg]
+  body|)"
+
+    "(defn foo
+  ([|])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should add an arity to a single-arity defn 
with args on next line"
+    "(defn foo
+  [arg]
+  bo|dy)"
+
+    "(defn foo
+  ([|])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity defn with a 
docstring"
+    "(defn foo
+  \"some docst|ring\"
+  [arg]
+  body)"
+
+    "(defn foo
+  \"some docstring\"
+  ([|])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity defn with 
metadata"
+    "(defn fo|o
+  ^{:bla \"meta\"}
+  [arg]
+  body)"
+
+    "(defn foo
+  ^{:bla \"meta\"}
+  ([|])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should add an arity to a multi-arity defn"
+    "(defn foo
+  ([arg1])
+  ([ar|g1 arg2]
+   body))"
+
+    "(defn foo
+  ([|])
+  ([arg1])
+  ([arg1 arg2]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a multi-arity defn with a 
docstring"
+    "(defn foo
+  \"some docstring\"
+  ([])
+  ([arg|]
+   body))"
+
+    "(defn foo
+  \"some docstring\"
+  ([|])
+  ([])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a multi-arity defn with 
metadata"
+    "(defn foo
+  \"some docstring\"
+  ^{:bla \"meta\"}
+  ([])
+  |([arg]
+   body))"
+
+    "(defn foo
+  \"some docstring\"
+  ^{:bla \"meta\"}
+  ([|])
+  ([])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity fn"
+    "(fn foo [arg]
+  body|)"
+
+    "(fn foo
+  ([|])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a multi-arity fn"
+    "(fn foo
+  ([x y]
+   body)
+  ([a|rg]
+   body))"
+
+    "(fn foo
+  ([|])
+  ([x y]
+   body)
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity defmacro"
+    "(defmacro foo [arg]
+  body|)"
+
+    "(defmacro foo
+  ([|])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a multi-arity defmacro"
+    "(defmacro foo
+  ([x y]
+   body)
+  ([a|rg]
+   body))"
+
+    "(defmacro foo
+  ([|])
+  ([x y]
+   body)
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity defmethod"
+    "(defmethod foo :bar [arg]
+  body|)"
+
+    "(defmethod foo :bar
+  ([|])
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a multi-arity defmethod"
+    "(defmethod foo :bar
+  ([x y]
+   body)
+  ([a|rg]
+   body))"
+
+    "(defmethod foo :bar
+  ([|])
+  ([x y]
+   body)
+  ([arg]
+   body))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a defn inside a reader 
conditional"
+    "#?(:clj
+   (defn foo
+     \"some docstring\"
+     ^{:bla \"meta\"}
+     |([arg]
+      body)))"
+
+    "#?(:clj
+   (defn foo
+     \"some docstring\"
+     ^{:bla \"meta\"}
+     ([|])
+     ([arg]
+      body)))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a defn inside a reader 
conditional with 2 platform tags"
+    "#?(:clj
+   (defn foo
+     \"some docstring\"
+     ^{:bla \"meta\"}
+     |([arg]
+      body))
+   :cljs
+   (defn foo
+     \"some docstring\"
+     ^{:bla \"meta\"}
+     ([arg]
+      body)))"
+
+    "#?(:clj
+   (defn foo
+     \"some docstring\"
+     ^{:bla \"meta\"}
+     ([|])
+     ([arg]
+      body))
+   :cljs
+   (defn foo
+     \"some docstring\"
+     ^{:bla \"meta\"}
+     ([arg]
+      body)))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity fn inside a 
letfn"
+    "(letfn [(foo [x]
+           bo|dy)]
+  (foo 3))"
+
+    "(letfn [(foo
+          ([|])
+          ([x]
+           body))]
+  (foo 3))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a multi-arity fn inside a 
letfn"
+    "(letfn [(foo
+          ([x]
+           body)
+          |([x y]
+           body))]
+  (foo 3))"
+
+    "(letfn [(foo
+          ([|])
+          ([x]
+           body)
+          ([x y]
+           body))]
+  (foo 3))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a proxy"
+    "(proxy [Foo] []
+  (bar [arg]
+     body|))"
+
+    "(proxy [Foo] []
+  (bar
+    ([|])
+    ([arg]
+     body)))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a defprotocol"
+    "(defprotocol Foo
+  \"some docstring\"
+  (bar [arg] [x |y] \"some docstring\"))"
+
+    "(defprotocol Foo
+  \"some docstring\"
+  (bar [|] [arg] [x y] \"some docstring\"))"
+
+    (clojure-ts-add-arity))
+
+  (when-refactoring-with-point-it "should handle a reify"
+    "(reify Foo
+  (bar [arg] body)
+  (blahs [arg]| body))"
+
+    "(reify Foo
+  (bar [arg] body)
+  (blahs [|])
+  (blahs [arg] body))"
+
+    (clojure-ts-add-arity))
+
+  (it "should signal a user error when point is not inside a function body"
+    (with-clojure-ts-buffer-point "
+(letf|n [(foo
+          ([x]
+           body)
+          ([x y]
+           body))]
+  (foo 3))"
+      (expect (clojure-ts-add-arity)
+              :to-throw
+              'user-error
+              '("No suitable form to add an arity at point")))
+
+    (with-clojure-ts-buffer-point "
+(defprotocol Fo|o
+  \"some docstring\"
+  (bar [arg] [x y] \"some docstring\"))"
+      (expect (clojure-ts-add-arity)
+              :to-throw
+              'user-error
+              '("No suitable form to add an arity at point")))))
+
+(provide 'clojure-ts-mode-refactor-add-arity-test)
+;;; clojure-ts-mode-refactor-add-arity-test.el ends here
diff --git a/test/samples/refactoring.clj b/test/samples/refactoring.clj
index d06a77db9d..641e3c56d1 100644
--- a/test/samples/refactoring.clj
+++ b/test/samples/refactoring.clj
@@ -92,3 +92,43 @@
 ;; TODO: Define indentation rule for `ns_map_lit`
 #:hello{:name "Roma"
  :world true}
+
+
+(reify
+  java.io.FileFilter
+  (accept [this f]
+    (.isDirectory f))
+
+  (hello [world]
+    false))
+
+(defmulti which-color-mm (fn [m & args] (:color m)))
+(defmethod which-color-mm :blue
+  ([m] (print m))
+  ([m f] (f m)))
+
+(letfn [(twice [x]
+          (* x 2))
+        (six-times [y]
+          (* (twice y) 3))]
+  (println "Twice 15 =" (twice 15))
+  (println "Six times 15 =" (six-times 15)))
+
+(let [p (proxy [java.io.InputStream] []
+          (read
+            ([] 1)
+            ([^bytes bytes] 2)
+            ([^bytes bytes off len] 3)))]
+  (println (.read p))
+  (println (.read p (byte-array 3)))
+  (println (.read p (byte-array 3) 0 3)))
+
+(defprotocol Fly
+  "A simple protocol for flying"
+  (fly [this]
+    "Method to fly"))
+
+(defn foo
+  ^{:bla "meta"}
+  [arg]
+  body)
diff --git a/test/test-helper.el b/test/test-helper.el
index a99ceec522..fa821e64b9 100644
--- a/test/test-helper.el
+++ b/test/test-helper.el
@@ -39,7 +39,7 @@ And evaluate BODY."
 
 TEXT is a string with a | indicating where point is.  The | will be erased
 and point left there."
-  (declare (indent 2))
+  (declare (indent 1))
   `(progn
      (with-clojure-ts-buffer ,text
        (goto-char (point-min))

Reply via email to