@cindex indentation rules, for parser-based indentation
@defvar treesit-simple-indent-rules
-This local variable stores indentation rules for every language. It
-is an alist with elements of the form @w{@code{(@var{language}
-. @var{rules})}}, where @var{language} is a language symbol, and
-@var{rules} is a list with elements of the form
-@w{@code{(@var{matcher} @var{anchor} @var{offset})}}.
+This local variable stores indentation rules for every language. It is
+an list of elements of the form @w{@code{(@var{language}
+@var{rule}...)}}, where @var{language} is a language symbol, and each
+@var{rule} is either a list with elements of the form
+@w{@code{(@var{matcher} @var{anchor} @var{offset})}}, or a function. Let
+'s focus on the list variant first, we'll come back to the function
+variant later.
First, Emacs passes the smallest tree-sitter node at the beginning of
the current line to @var{matcher}; if it returns non-@code{nil}, this
or a function that returns an integer. If it is a function, it is
passed @var{node}, @var{parent}, and @var{bol}, like matchers and
anchors.
+
+Remember that @var{rule} can also be a function. This is for the
+complex cases where a rule needs to consider the matching rule and
+anchor together. If @var{rule} is a function, it's passed the same
+argument as @var{matcher}: @var{node}, @var{parent}, and @var{bol}. If
+it matches, @var{rule} should return a cons @w{@code{(@var{anchor-pos}
+. @var{offset})}}, where @var{anchor-pos} is a buffer position, and
+@var{offset} is the indent offset. If @var{rule} does't match, it
+should return @code{nil}.
@end defvar
@defvar treesit-simple-indent-presets
(defvar-local treesit-simple-indent-rules nil
"A list of indent rule settings.
-Each indent rule setting should be (LANGUAGE . RULES),
-where LANGUAGE is a language symbol, and RULES is a list of
+Each indent rule setting should be (LANGUAGE RULE...), where LANGUAGE is
+a language symbol, and each RULE is of the form
- (MATCHER ANCHOR OFFSET).
+ (MATCHER ANCHOR OFFSET)
-MATCHER determines whether this rule applies, ANCHOR and OFFSET
-together determines which column to indent to.
+MATCHER determines whether this rule applies, ANCHOR and
+OFFSET together determines which column to indent to.
A MATCHER is a function that takes three arguments (NODE PARENT
BOL). BOL is the point where we are indenting: the beginning of
can be an integer or a variable whose value is an integer.
For MATCHER and ANCHOR, Emacs provides some convenient presets.
-See `treesit-simple-indent-presets'.")
+See `treesit-simple-indent-presets'.
+
+For complex cases, a RULE can also be a single function. This function
+should take the same argument as MATCHER or ANCHOR. If it matches,
+return a cons (ANCHOR-POS . OFFSET), where ANCHOR-POS is a position and
+OFFSET is the indent offset; if it doesn't match, return nil.")
(defvar treesit-simple-indent-presets
(list (cons 'match
(let* ((language (treesit-node-language parent))
(rules (alist-get language
treesit-simple-indent-rules)))
- (cl-loop for rule in rules
- for pred = (nth 0 rule)
- for anchor = (nth 1 rule)
- for offset = (nth 2 rule)
- if (treesit--simple-indent-eval
- (list pred node parent bol))
- do (when treesit--indent-verbose
+ (catch 'match
+ (dolist (rule rules)
+ (if (functionp rule)
+ (let ((result (funcall rule node parent bol)))
+ (when result
+ (when treesit--indent-verbose
(message "Matched rule: %S" rule))
- and
- return
- (let ((anchor-pos
- (treesit--simple-indent-eval
- (list anchor node parent bol)))
- (offset-val
- (cond ((numberp offset) offset)
- ((and (symbolp offset)
- (boundp offset))
- (symbol-value offset))
- (t (treesit--simple-indent-eval
- (list offset node parent bol))))))
- (cons anchor-pos offset-val))
- finally return
- (progn (when treesit--indent-verbose
- (message "No matched rule"))
- (cons nil nil))))))
+ (throw 'match result)))
+ (let ((pred (nth 0 rule))
+ (anchor (nth 1 rule))
+ (offset (nth 2 rule)))
+ ;; Found a match.
+ (when (treesit--simple-indent-eval
+ (list pred node parent bol))
+ (when treesit--indent-verbose
+ (message "Matched rule: %S" rule))
+ (let ((anchor-pos
+ (treesit--simple-indent-eval
+ (list anchor node parent bol)))
+ (offset-val
+ (cond ((numberp offset) offset)
+ ((and (symbolp offset)
+ (boundp offset))
+ (symbol-value offset))
+ (t (treesit--simple-indent-eval
+ (list offset node parent bol))))))
+ (throw 'match (cons anchor-pos offset-val)))))))
+ ;; Didn't find any match.
+ (when treesit--indent-verbose
+ (message "No matched rule"))
+ (cons nil nil)))))
(defun treesit--read-major-mode ()
"Read a major mode using completion.
(_ func)))
;; Optimize a rule (MATCHER ANCHOR OFFSET).
(optimize-rule (rule)
- (let ((matcher (nth 0 rule))
- (anchor (nth 1 rule))
- (offset (nth 2 rule)))
- (list (optimize-func matcher)
- (optimize-func anchor)
- offset))))
+ (if (functionp rule)
+ rule
+ (let ((matcher (nth 0 rule))
+ (anchor (nth 1 rule))
+ (offset (nth 2 rule)))
+ (list (optimize-func matcher)
+ (optimize-func anchor)
+ offset)))))
(cons lang (mapcar #'optimize-rule indent-rules)))))
;;; Search