Skip to content

Commit 9dff9d1

Browse files
committed
Improve performance of semantic indentation by caching rules
1 parent 9542792 commit 9dff9d1

File tree

3 files changed

+76
-24
lines changed

3 files changed

+76
-24
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- [#70](https://github.com/clojure-emacs/clojure-ts-mode/pull/70): Add support for nested indentation rules.
1919
- [#71](https://github.com/clojure-emacs/clojure-ts-mode/pull/71): Properly highlight function name in `letfn` form.
2020
- [#72](https://github.com/clojure-emacs/clojure-ts-mode/pull/72): Pass fully qualified symbol to `clojure-ts-get-indent-function`.
21+
- Improve performance of semantic indentation by caching rules.
2122

2223
## 0.2.3 (2025-03-04)
2324

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,24 @@ For example:
210210
- `defn` and `fn` have a rule `((:inner 0))`.
211211
- `letfn` has a rule `((:block 1) (:inner 2 0))`.
212212

213+
Note that `clojure-ts-semantic-indent-rules` should be set using the
214+
customization interface or `setopt`; otherwise, it will not be applied
215+
correctly.
216+
217+
#### Project local indentation
218+
219+
Custom indentation rules can be set for individual projects. To achieve this,
220+
you need to create a `.dir-locals.el` file in the project root. The content
221+
should look like:
222+
223+
```emacs-lisp
224+
((clojure-ts-mode . ((clojure-ts-semantic-indent-rules . (("with-transaction" . ((:block 1)))
225+
("with-retry" . ((:block 1))))))))
226+
```
227+
228+
In order to apply directory-local variables to existing buffers, they must be
229+
reverted.
230+
213231
### Font Locking
214232

215233
To highlight entire rich `comment` expression with the comment font face, set

clojure-ts-mode.el

+57-24
Original file line numberDiff line numberDiff line change
@@ -125,26 +125,6 @@ double quotes on the third column."
125125
:type 'boolean
126126
:package-version '(clojure-ts-mode . "0.3"))
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-
:safe #'listp
139-
:type '(alist :key-type string
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))))
146-
:package-version '(clojure-ts-mode . "0.3"))
147-
148128
(defvar clojure-ts-mode-remappings
149129
'((clojure-mode . clojure-ts-mode)
150130
(clojurescript-mode . clojure-ts-clojurescript-mode)
@@ -864,6 +844,52 @@ The format reflects cljfmt indentation rules. All the default rules are
864844
aligned with
865845
https://github.com/weavejester/cljfmt/blob/0.13.0/cljfmt/resources/cljfmt/indents/clojure.clj")
866846

847+
(defvar-local clojure-ts--semantic-indent-rules-cache nil)
848+
849+
(defun clojure-ts--compute-semantic-indentation-rules-cache (rules)
850+
"Compute the combined semantic indentation rules cache.
851+
852+
If RULES are not provided, this function computes the union of
853+
`clojure-ts-semantic-indent-rules' and
854+
`clojure-ts--semantic-indent-rules-defaults', prioritizing user-defined
855+
rules. If RULES are provided, this function uses them instead of
856+
`clojure-ts-semantic-indent-rules'.
857+
858+
This function is called when the `clojure-ts-semantic-indent-rules'
859+
variable is customized using setopt or the Emacs customization
860+
interface. It is also called when file-local variables are updated.
861+
This ensures that updated indentation rules are always precalculated."
862+
(seq-union rules
863+
clojure-ts--semantic-indent-rules-defaults
864+
(lambda (e1 e2) (equal (car e1) (car e2)))))
865+
866+
(defcustom clojure-ts-semantic-indent-rules nil
867+
"Custom rules to extend default indentation rules for `semantic' style.
868+
869+
Each rule is an alist entry which looks like `(\"symbol-name\"
870+
. (rule-type rule-value))', where rule-type is one either `:block' or
871+
`:inner' and rule-value is an integer. The semantic is similar to
872+
cljfmt indentation rules.
873+
874+
Default set of rules is defined in
875+
`clojure-ts--semantic-indent-rules-defaults'."
876+
:safe #'listp
877+
:type '(alist :key-type string
878+
:value-type (repeat (choice (list (choice (const :tag "Block indentation rule" :block)
879+
(const :tag "Inner indentation rule" :inner))
880+
integer)
881+
(list (const :tag "Inner indentation rule" :inner)
882+
integer
883+
integer))))
884+
:package-version '(clojure-ts-mode . "0.3")
885+
:set (lambda (symbol value)
886+
(set-default-toplevel-value symbol value)
887+
;; Update cache in every `clojure-ts-mode' buffer.
888+
(let ((new-cache (clojure-ts--compute-semantic-indentation-rules-cache value)))
889+
(dolist (buf (buffer-list))
890+
(when (buffer-local-boundp 'clojure-ts--semantic-indent-rules-cache buf)
891+
(setq clojure-ts--semantic-indent-rules-cache new-cache))))))
892+
867893
(defun clojure-ts--match-block-0-body (bol first-child)
868894
"Match if expression body is not at the same line as FIRST-CHILD.
869895
@@ -929,7 +955,7 @@ For example, (1 ((:defn)) nil) is converted to ((:block 1) (:inner 2)).
929955
930956
If NS is defined, then the fully qualified symbol is passed to
931957
`clojure-ts-get-indent-function'."
932-
(when (functionp clojure-ts-get-indent-function)
958+
(when (and sym (functionp clojure-ts-get-indent-function))
933959
(let* ((full-symbol (if ns
934960
(concat ns "/" sym)
935961
sym))
@@ -964,9 +990,7 @@ only if the CURRENT-DEPTH matches the rule's required depth."
964990
(idx (- (treesit-node-index node) 2)))
965991
(if-let* ((rule-set (or (clojure-ts--dynamic-indent-for-symbol symbol-name symbol-namespace)
966992
(alist-get symbol-name
967-
(seq-union clojure-ts-semantic-indent-rules
968-
clojure-ts--semantic-indent-rules-defaults
969-
(lambda (e1 e2) (equal (car e1) (car e2))))
993+
clojure-ts--semantic-indent-rules-cache
970994
nil
971995
nil
972996
#'equal))))
@@ -1311,6 +1335,15 @@ See `clojure-ts--font-lock-settings' for usage of MARKDOWN-AVAILABLE."
13111335

13121336
(treesit-major-mode-setup)
13131337

1338+
(setq clojure-ts--semantic-indent-rules-cache
1339+
(clojure-ts--compute-semantic-indentation-rules-cache clojure-ts-semantic-indent-rules))
1340+
(add-hook 'hack-local-variables-hook
1341+
(lambda ()
1342+
(setq clojure-ts--semantic-indent-rules-cache
1343+
(clojure-ts--compute-semantic-indentation-rules-cache clojure-ts-semantic-indent-rules)))
1344+
0
1345+
t)
1346+
13141347
;; Workaround for treesit-transpose-sexps not correctly working with
13151348
;; treesit-thing-settings on Emacs 30.
13161349
;; Once treesit-transpose-sexps it working again this can be removed

0 commit comments

Comments
 (0)