@@ -137,9 +137,12 @@ Default set of rules is defined in
137137`clojure-ts--semantic-indent-rules-defaults' ."
138138 :safe #'listp
139139 :type '(alist :key-type string
140- :value-type (list (choice (const :tag " Block indentation rule" :block )
141- (const :tag " Inner indentation rule" :inner ))
142- integer))
140+ :value-type (repeat (choice (list (choice (const :tag " Block indentation rule" :block )
141+ (const :tag " Inner indentation rule" :inner ))
142+ integer)
143+ (list (const :tag " Inner indentation rule" :inner )
144+ integer
145+ integer))))
143146 :package-version '(clojure-ts-mode . " 0.2.4" ))
144147
145148(defvar clojure-ts-mode-remappings
@@ -769,74 +772,73 @@ The possible values for this variable are
769772 ((parent-is " set_lit" ) parent 2 ))))
770773
771774(defvar clojure-ts--semantic-indent-rules-defaults
772- '((" alt!" . (:block 0 ))
773- (" alt!!" . (:block 0 ))
774- (" comment" . (:block 0 ))
775- (" cond" . (:block 0 ))
776- (" delay" . (:block 0 ))
777- (" do" . (:block 0 ))
778- (" finally" . (:block 0 ))
779- (" future" . (:block 0 ))
780- (" go" . (:block 0 ))
781- (" thread" . (:block 0 ))
782- (" try" . (:block 0 ))
783- (" with-out-str" . (:block 0 ))
784- (" defprotocol" . (:block 1 ))
785- (" binding" . (:block 1 ))
786- (" defprotocol" . (:block 1 ))
787- (" binding" . (:block 1 ))
788- (" case" . (:block 1 ))
789- (" cond->" . (:block 1 ))
790- (" cond->>" . (:block 1 ))
791- (" doseq" . (:block 1 ))
792- (" dotimes" . (:block 1 ))
793- (" doto" . (:block 1 ))
794- (" extend" . (:block 1 ))
795- (" extend-protocol" . (:block 1 ))
796- (" extend-type" . (:block 1 ))
797- (" for" . (:block 1 ))
798- (" go-loop" . (:block 1 ))
799- (" if" . (:block 1 ))
800- (" if-let" . (:block 1 ))
801- (" if-not" . (:block 1 ))
802- (" if-some" . (:block 1 ))
803- (" let" . (:block 1 ))
804- (" letfn" . (:block 1 ))
805- (" locking" . (:block 1 ))
806- (" loop" . (:block 1 ))
807- (" match" . (:block 1 ))
808- (" ns" . (:block 1 ))
809- (" struct-map" . (:block 1 ))
810- (" testing" . (:block 1 ))
811- (" when" . (:block 1 ))
812- (" when-first" . (:block 1 ))
813- (" when-let" . (:block 1 ))
814- (" when-not" . (:block 1 ))
815- (" when-some" . (:block 1 ))
816- (" while" . (:block 1 ))
817- (" with-local-vars" . (:block 1 ))
818- (" with-open" . (:block 1 ))
819- (" with-precision" . (:block 1 ))
820- (" with-redefs" . (:block 1 ))
821- (" defrecord" . (:block 2 ))
822- (" deftype" . (:block 2 ))
823- (" are" . (:block 2 ))
824- (" as->" . (:block 2 ))
825- (" catch" . (:block 2 ))
826- (" condp" . (:block 2 ))
827- (" bound-fn" . (:inner 0 ))
828- (" def" . (:inner 0 ))
829- (" defmacro" . (:inner 0 ))
830- (" defmethod" . (:inner 0 ))
831- (" defmulti" . (:inner 0 ))
832- (" defn" . (:inner 0 ))
833- (" defn-" . (:inner 0 ))
834- (" defonce" . (:inner 0 ))
835- (" deftest" . (:inner 0 ))
836- (" fdef" . (:inner 0 ))
837- (" fn" . (:inner 0 ))
838- (" reify" . (:inner 0 ))
839- (" use-fixtures" . (:inner 0 )))
775+ '((" alt!" . ((:block 0 )))
776+ (" alt!!" . ((:block 0 )))
777+ (" comment" . ((:block 0 )))
778+ (" cond" . ((:block 0 )))
779+ (" delay" . ((:block 0 )))
780+ (" do" . ((:block 0 )))
781+ (" finally" . ((:block 0 )))
782+ (" future" . ((:block 0 )))
783+ (" go" . ((:block 0 )))
784+ (" thread" . ((:block 0 )))
785+ (" try" . ((:block 0 )))
786+ (" with-out-str" . ((:block 0 )))
787+ (" defprotocol" . ((:block 1 ) (:inner 1 )))
788+ (" binding" . ((:block 1 )))
789+ (" case" . ((:block 1 )))
790+ (" cond->" . ((:block 1 )))
791+ (" cond->>" . ((:block 1 )))
792+ (" doseq" . ((:block 1 )))
793+ (" dotimes" . ((:block 1 )))
794+ (" doto" . ((:block 1 )))
795+ (" extend" . ((:block 1 )))
796+ (" extend-protocol" . ((:block 1 ) (:inner 1 )))
797+ (" extend-type" . ((:block 1 ) (:inner 1 )))
798+ (" for" . ((:block 1 )))
799+ (" go-loop" . ((:block 1 )))
800+ (" if" . ((:block 1 )))
801+ (" if-let" . ((:block 1 )))
802+ (" if-not" . ((:block 1 )))
803+ (" if-some" . ((:block 1 )))
804+ (" let" . ((:block 1 )))
805+ (" letfn" . ((:block 1 ) (:inner 2 0 )))
806+ (" locking" . ((:block 1 )))
807+ (" loop" . ((:block 1 )))
808+ (" match" . ((:block 1 )))
809+ (" ns" . ((:block 1 )))
810+ (" struct-map" . ((:block 1 )))
811+ (" testing" . ((:block 1 )))
812+ (" when" . ((:block 1 )))
813+ (" when-first" . ((:block 1 )))
814+ (" when-let" . ((:block 1 )))
815+ (" when-not" . ((:block 1 )))
816+ (" when-some" . ((:block 1 )))
817+ (" while" . ((:block 1 )))
818+ (" with-local-vars" . ((:block 1 )))
819+ (" with-open" . ((:block 1 )))
820+ (" with-precision" . ((:block 1 )))
821+ (" with-redefs" . ((:block 1 )))
822+ (" defrecord" . ((:block 2 ) (:inner 1 )))
823+ (" deftype" . ((:block 2 ) (:inner 1 )))
824+ (" are" . ((:block 2 )))
825+ (" as->" . ((:block 2 )))
826+ (" catch" . ((:block 2 )))
827+ (" condp" . ((:block 2 )))
828+ (" bound-fn" . ((:inner 0 )))
829+ (" def" . ((:inner 0 )))
830+ (" defmacro" . ((:inner 0 )))
831+ (" defmethod" . ((:inner 0 )))
832+ (" defmulti" . ((:inner 0 )))
833+ (" defn" . ((:inner 0 )))
834+ (" defn-" . ((:inner 0 )))
835+ (" defonce" . ((:inner 0 )))
836+ (" deftest" . ((:inner 0 )))
837+ (" fdef" . ((:inner 0 )))
838+ (" fn" . ((:inner 0 )))
839+ (" reify" . ((:inner 0 ) (:inner 1 )))
840+ (" proxy" . ((:block 2 ) (:inner 1 )))
841+ (" use-fixtures" . ((:inner 0 ))))
840842 " Default semantic indentation rules.
841843
842844The format reflects cljfmt indentation rules. All the default rules are
@@ -882,22 +884,87 @@ The returned value is expected to be the same as
882884`clojure-get-indent-function' from `clojure-mode' for compatibility
883885reasons." )
884886
887+ (defun clojure-ts--unnest-dynamic-spec (spec current-depth )
888+ " Recursively unnest SPEC, incrementally increasing the CURRENT-DEPTH.
889+
890+ This function accpets a list SPEC, like ((:defn)) and produce a proper
891+ indent rule. For example, ((:defn)) is converted to (:inner 2),
892+ and (:defn) is converted to (:inner 1)."
893+ (if (consp spec)
894+ (clojure-ts--unnest-dynamic-spec (car spec) (1+ current-depth))
895+ (cond
896+ ((equal spec :defn ) (list :inner current-depth))
897+ (t nil ))))
898+
885899(defun clojure-ts--dynamic-indent-for-symbol (symbol-name )
886- " Return dynamic indentation spec for SYMBOL-NAME if found.
900+ " Returns the dynamic indentation specification for SYMBOL-NAME, if found.
901+
902+ If the function `clojure-ts-get-indent-function' is defined, call it and
903+ produce a valid indentation specification from its return value.
887904
888- If function `clojure-ts-get-indent-function' is not nil, call it and
889- produce a valid indentation spec from the returned value.
905+ The `clojure-ts-get-indent-function' should return an indentation
906+ specification compatible with `clojure-mode' , which will then be
907+ converted to a suitable `clojure-ts-mode' specification.
890908
891- The indentation rules for `clojure-ts-mode' are simpler than for
892- `clojure-mode' so we only take the first integer N and produce `(:block
893- N)' rule. If an integer cannot be found, this function returns nil and
894- the default rule is used."
909+ For example, (1 ((:defn)) nil) is converted to ((:block 1) (:inner 2))."
895910 (when (functionp clojure-ts-get-indent-function)
896911 (let ((spec (funcall clojure-ts-get-indent-function symbol-name)))
897- (if (consp spec)
898- `(:block ,(car spec))
899- (when (integerp spec)
900- `(:block , spec ))))))
912+ (if (integerp spec)
913+ (list (list :block spec))
914+ (when (sequencep spec)
915+ (thread-last spec
916+ (seq-map (lambda (el )
917+ (cond
918+ ((integerp el) (list :block el))
919+ ((equal el :defn ) (list :inner 0 ))
920+ ((consp el) (clojure-ts--unnest-dynamic-spec el 0 ))
921+ (t nil ))))
922+ (seq-remove #'null )
923+ ; ; Always put `:block' to the beginning.
924+ (seq-sort (lambda (spec1 spec2 )
925+ (equal (car spec1) :block )))))))))
926+
927+ (defun clojure-ts--find-semantic-rule (node parent current-depth )
928+ " Returns a suitable indentation rule for NODE, considering the CURRENT-DEPTH.
929+
930+ Attempts to find an indentation rule by examining the symbol name of the
931+ PARENT's first child. If a rule is not found, it navigates up the
932+ syntax tree and recursively attempts to find a rule, incrementally
933+ increasing the CURRENT-DEPTH. If a rule is not found upon reaching the
934+ root of the syntax tree, it returns nil. A rule is considered a match
935+ only if the CURRENT-DEPTH matches the rule's required depth."
936+ (let* ((first-child (clojure-ts--node-child-skip-metadata parent 0 ))
937+ (symbol-name (clojure-ts--named-node-text first-child))
938+ (idx (- (treesit-node-index node) 2 )))
939+ (if-let* ((rule-set (or (clojure-ts--dynamic-indent-for-symbol symbol-name)
940+ (alist-get symbol-name
941+ (seq-union clojure-ts-semantic-indent-rules
942+ clojure-ts--semantic-indent-rules-defaults
943+ (lambda (e1 e2 ) (equal (car e1) (car e2))))
944+ nil
945+ nil
946+ #'equal ))))
947+ (if (zerop current-depth)
948+ (let ((rule (car rule-set)))
949+ (if (equal (car rule) :block )
950+ rule
951+ (let ((rule-depth (cadr rule))
952+ (rule-idx (caddr rule)))
953+ (when (and (equal rule-depth current-depth)
954+ (or (null rule-idx)
955+ (equal rule-idx idx)))
956+ rule))))
957+ (thread-last rule-set
958+ (seq-filter (lambda (rule )
959+ (and (equal (car rule) :inner )
960+ (equal (cadr rule) current-depth)
961+ (or (null (caddr rule))
962+ (equal (caddr rule) idx)))))
963+ (seq-first)))
964+ (when-let* ((new-parent (treesit-node-parent parent)))
965+ (clojure-ts--find-semantic-rule parent
966+ new-parent
967+ (1+ current-depth))))))
901968
902969(defun clojure-ts--match-form-body (node parent bol )
903970 " Match if NODE has to be indented as a for body.
@@ -907,16 +974,8 @@ indentation rule in `clojure-ts--semantic-indent-rules-defaults' or
907974`clojure-ts-semantic-indent-rules' check if NODE should be indented
908975according to the rule. If NODE is nil, use next node after BOL."
909976 (and (clojure-ts--list-node-p parent)
910- (let* ((first-child (clojure-ts--node-child-skip-metadata parent 0 ))
911- (symbol-name (clojure-ts--named-node-text first-child)))
912- (when-let* ((rule (or (clojure-ts--dynamic-indent-for-symbol symbol-name)
913- (alist-get symbol-name
914- (seq-union clojure-ts-semantic-indent-rules
915- clojure-ts--semantic-indent-rules-defaults
916- (lambda (e1 e2 ) (equal (car e1) (car e2))))
917- nil
918- nil
919- #'equal ))))
977+ (let* ((first-child (clojure-ts--node-child-skip-metadata parent 0 )))
978+ (when-let* ((rule (clojure-ts--find-semantic-rule node parent 0 )))
920979 (and (not (clojure-ts--match-with-metadata node))
921980 (let ((rule-type (car rule))
922981 (rule-value (cadr rule)))
@@ -940,19 +999,6 @@ according to the rule. If NODE is nil, use next node after BOL."
940999 (clojure-ts--keyword-node-p first-child)
9411000 (clojure-ts--var-node-p first-child)))))
9421001
943- (defun clojure-ts--match-method-body (_node parent _bol )
944- " Matches a `NODE' in the body of a `PARENT' method implementation.
945- A method implementation referes to concrete implementations being defined in
946- forms like deftype, defrecord, reify, proxy, etc."
947- (and
948- (clojure-ts--list-node-p parent)
949- (let* ((grandparent (treesit-node-parent parent))
950- ; ; auncle: gender neutral sibling of parent, aka child of grandparent
951- (first-auncle (treesit-node-child grandparent 0 t )))
952- (and (clojure-ts--list-node-p grandparent)
953- (clojure-ts--symbol-matches-p clojure-ts--type-symbol-regexp
954- first-auncle)))))
955-
9561002(defvar clojure-ts--threading-macro
9571003 (eval-and-compile
9581004 (rx (and " ->" (? " >" ) line-end)))
@@ -1043,7 +1089,6 @@ if NODE has metadata and its parent has type NODE-TYPE."
10431089 ((parent-is " source" ) parent-bol 0 )
10441090 (clojure-ts--match-docstring parent 0 )
10451091 ; ; https://guide.clojure.style/#body-indentation
1046- (clojure-ts--match-method-body parent 2 )
10471092 (clojure-ts--match-form-body clojure-ts--anchor-parent-skip-metadata 2 )
10481093 ; ; https://guide.clojure.style/#threading-macros-alignment
10491094 (clojure-ts--match-threading-macro-arg prev-sibling 0 )
0 commit comments