@@ -125,6 +125,22 @@ double quotes on the third column."
125125 :type 'boolean
126126 :package-version '(clojure-ts-mode . " 0.2.4" ))
127127
128+ (defcustom clojure-ts-semantic-indent-rules nil
129+ " Custom rules to extend default indentation rules for `semantic' style.
130+
131+ Each rule is an alist entry which looks like `(\" symbol-name\"
132+ . (rule-type rule-value))', where rule-type is one either `:block' or
133+ `:inner' and rule-value is an integer. The semantic is similar to
134+ cljfmt indentation rules.
135+
136+ Default set of rules is defined in
137+ `clojure-ts--semantic-indent-rules-defaults' ."
138+ :type '(alist :key-type string
139+ :value-type (list (choice (const :tag " Block indentation rule" :block )
140+ (const :tag " Inner indentation rule" :inner ))
141+ integer))
142+ :package-version '(clojure-ts-mode . " 0.2.4" ))
143+
128144(defvar clojure-ts-mode-remappings
129145 '((clojure-mode . clojure-ts-mode)
130146 (clojurescript-mode . clojure-ts-clojurescript-mode)
@@ -182,7 +198,6 @@ Only intended for use at development time.")
182198 table)
183199 " Syntax table for `clojure-ts-mode' ." )
184200
185-
186201(defconst clojure-ts--builtin-dynamic-var-regexp
187202 (eval-and-compile
188203 (concat " ^"
@@ -746,34 +761,129 @@ The possible values for this variable are
746761 ((parent-is " list_lit" ) parent 1 )
747762 ((parent-is " set_lit" ) parent 2 ))))
748763
749- (defvar clojure-ts--symbols-with-body-expressions-regexp
750- (eval-and-compile
751- (rx (or
752- ; ; Match def* symbols,
753- ; ; we also explicitly do not match symbols beginning with
754- ; ; "default" "deflate" and "defer", like cljfmt
755- (and line-start " def" )
756- ; ; Match with-* symbols
757- (and line-start " with-" )
758- ; ; Exact matches
759- (and line-start
760- (or " alt!" " alt!!" " are" " as->"
761- " binding" " bound-fn"
762- " case" " catch" " comment" " cond" " condp" " cond->" " cond->>"
763- " delay" " do" " doseq" " dotimes" " doto"
764- " extend" " extend-protocol" " extend-type"
765- " fdef" " finally" " fn" " for" " future"
766- " go" " go-loop"
767- " if" " if-let" " if-not" " if-some"
768- " let" " letfn" " locking" " loop"
769- " match" " ns" " proxy" " reify" " struct-map"
770- " testing" " thread" " try"
771- " use-fixtures"
772- " when" " when-first" " when-let" " when-not" " when-some" " while" )
773- line-end))))
774- " A regex to match symbols that are functions/macros with a body argument.
775- Taken from cljfmt:
776- https://github.com/weavejester/cljfmt/blob/fb26b22f569724b05c93eb2502592dfc2de898c3/cljfmt/resources/cljfmt/indents/clojure.clj" )
764+ (defvar clojure-ts--semantic-indent-rules-defaults
765+ '((" alt!" . (:block 0 ))
766+ (" alt!!" . (:block 0 ))
767+ (" comment" . (:block 0 ))
768+ (" cond" . (:block 0 ))
769+ (" delay" . (:block 0 ))
770+ (" do" . (:block 0 ))
771+ (" finally" . (:block 0 ))
772+ (" future" . (:block 0 ))
773+ (" go" . (:block 0 ))
774+ (" thread" . (:block 0 ))
775+ (" try" . (:block 0 ))
776+ (" with-out-str" . (:block 0 ))
777+ (" defprotocol" . (:block 1 ))
778+ (" binding" . (:block 1 ))
779+ (" defprotocol" . (:block 1 ))
780+ (" binding" . (:block 1 ))
781+ (" case" . (:block 1 ))
782+ (" cond->" . (:block 1 ))
783+ (" cond->>" . (:block 1 ))
784+ (" doseq" . (:block 1 ))
785+ (" dotimes" . (:block 1 ))
786+ (" doto" . (:block 1 ))
787+ (" extend" . (:block 1 ))
788+ (" extend-protocol" . (:block 1 ))
789+ (" extend-type" . (:block 1 ))
790+ (" for" . (:block 1 ))
791+ (" go-loop" . (:block 1 ))
792+ (" if" . (:block 1 ))
793+ (" if-let" . (:block 1 ))
794+ (" if-not" . (:block 1 ))
795+ (" if-some" . (:block 1 ))
796+ (" let" . (:block 1 ))
797+ (" letfn" . (:block 1 ))
798+ (" locking" . (:block 1 ))
799+ (" loop" . (:block 1 ))
800+ (" match" . (:block 1 ))
801+ (" ns" . (:block 1 ))
802+ (" struct-map" . (:block 1 ))
803+ (" testing" . (:block 1 ))
804+ (" when" . (:block 1 ))
805+ (" when-first" . (:block 1 ))
806+ (" when-let" . (:block 1 ))
807+ (" when-not" . (:block 1 ))
808+ (" when-some" . (:block 1 ))
809+ (" while" . (:block 1 ))
810+ (" with-local-vars" . (:block 1 ))
811+ (" with-open" . (:block 1 ))
812+ (" with-precision" . (:block 1 ))
813+ (" with-redefs" . (:block 1 ))
814+ (" defrecord" . (:block 2 ))
815+ (" deftype" . (:block 2 ))
816+ (" are" . (:block 2 ))
817+ (" as->" . (:block 2 ))
818+ (" catch" . (:block 2 ))
819+ (" condp" . (:block 2 ))
820+ (" bound-fn" . (:inner 0 ))
821+ (" def" . (:inner 0 ))
822+ (" defmacro" . (:inner 0 ))
823+ (" defmethod" . (:inner 0 ))
824+ (" defmulti" . (:inner 0 ))
825+ (" defn" . (:inner 0 ))
826+ (" defn-" . (:inner 0 ))
827+ (" defonce" . (:inner 0 ))
828+ (" deftest" . (:inner 0 ))
829+ (" fdef" . (:inner 0 ))
830+ (" fn" . (:inner 0 ))
831+ (" reify" . (:inner 0 ))
832+ (" use-fixtures" . (:inner 0 )))
833+ " Default semantic indentation rules.
834+
835+ The format reflects cljfmt indentation rules. All the default rules are
836+ aligned with
837+ https://github.com/weavejester/cljfmt/blob/0.13.0/cljfmt/resources/cljfmt/indents/clojure.clj" )
838+
839+ (defun clojure-ts--match-block-0-body (bol first-child )
840+ " Match if expression body is not at the same line as FIRST-CHILD.
841+
842+ If there is no body, check that BOL is not at the same line."
843+ (let* ((body-pos (if-let* ((body (treesit-node-next-sibling first-child)))
844+ (treesit-node-start body)
845+ bol)))
846+ (< (line-number-at-pos (treesit-node-start first-child))
847+ (line-number-at-pos body-pos))))
848+
849+ (defun clojure-ts--node-pos-match-block (node parent bol block )
850+ " Return TRUE if NODE index in the PARENT matches requested BLOCK.
851+
852+ NODE might be nil (when we insert an empty line for example), in this
853+ case we look for next available child node in the PARENT after BOL
854+ position.
855+
856+ The first node in the expression is usually an opening paren, the last
857+ node is usually a closing paren (unless some automatic parens mode is
858+ not enabled). If requested BLOCK is 1, the NODE index should be at
859+ least 3 (first node is opening paren, second node is matched symbol,
860+ third node is first argument, and the rest is body which should be
861+ indented.)"
862+ (if node
863+ (> (treesit-node-index node) (1+ block))
864+ (when-let* ((node-after-bol (treesit-node-first-child-for-pos parent bol)))
865+ (> (treesit-node-index node-after-bol) (1+ block)))))
866+
867+ (defun clojure-ts--match-form-body (node parent bol )
868+ (and (clojure-ts--list-node-p parent)
869+ (let ((first-child (clojure-ts--node-child-skip-metadata parent 0 )))
870+ (when-let* ((rule (alist-get (clojure-ts--named-node-text first-child)
871+ (seq-union clojure-ts-semantic-indent-rules
872+ clojure-ts--semantic-indent-rules-defaults
873+ (lambda (e1 e2 ) (equal (car e1) (car e2))))
874+ nil
875+ nil
876+ #'equal )))
877+ (and (not (clojure-ts--match-with-metadata node))
878+ (let ((rule-type (car rule))
879+ (rule-value (cadr rule)))
880+ (if (equal rule-type :block )
881+ (if (zerop rule-value)
882+ ; ; Special treatment for block 0 rule.
883+ (clojure-ts--match-block-0-body bol first-child)
884+ (clojure-ts--node-pos-match-block node parent bol rule-value))
885+ ; ; Return true for any inner rule.
886+ t )))))))
777887
778888(defun clojure-ts--match-function-call-arg (node parent _bol )
779889 " Match NODE if PARENT is a list expressing a function or macro call."
@@ -787,24 +897,6 @@ https://github.com/weavejester/cljfmt/blob/fb26b22f569724b05c93eb2502592dfc2de89
787897 (clojure-ts--keyword-node-p first-child)
788898 (clojure-ts--var-node-p first-child)))))
789899
790- (defun clojure-ts--match-expression-in-body (node parent _bol )
791- " Match NODE if it is an expression used in a body argument.
792- PARENT is expected to be a list literal.
793- See `treesit-simple-indent-rules' ."
794- (and
795- (clojure-ts--list-node-p parent)
796- (let ((first-child (clojure-ts--node-child-skip-metadata parent 0 )))
797- (and
798- (not
799- (clojure-ts--symbol-matches-p
800- ; ; Symbols starting with this are false positives
801- (rx line-start (or " default" " deflate" " defer" ))
802- first-child))
803- (not (clojure-ts--match-with-metadata node))
804- (clojure-ts--symbol-matches-p
805- clojure-ts--symbols-with-body-expressions-regexp
806- first-child)))))
807-
808900(defun clojure-ts--match-method-body (_node parent _bol )
809901 " Matches a `NODE' in the body of a `PARENT' method implementation.
810902A method implementation referes to concrete implementations being defined in
@@ -885,7 +977,7 @@ forms like deftype, defrecord, reify, proxy, etc."
885977 (clojure-ts--match-docstring parent 0 )
886978 ; ; https://guide.clojure.style/#body-indentation
887979 (clojure-ts--match-method-body parent 2 )
888- (clojure-ts--match-expression-in -body parent 2 )
980+ (clojure-ts--match-form -body parent 2 )
889981 ; ; https://guide.clojure.style/#threading-macros-alignment
890982 (clojure-ts--match-threading-macro-arg prev-sibling 0 )
891983 ; ; https://guide.clojure.style/#vertically-align-fn-args
0 commit comments