From 5b909fef271c16d02a04ecf07ffc93e70f262801 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Fri, 29 Nov 2024 14:10:45 -0800 Subject: [PATCH] Allow treesit-simple-indent's rule to be a single function * lisp/treesit.el (treesit-simple-indent-rules): Allow the rule to be a single function. Also replace cl-loop with dolist plus catch-throw. (treesit--indent-rules-optimize): Handle the case when a rule is a function. * doc/lispref/modes.texi (Parser-based Indentation): Update manuel. (cherry picked from commit 1e44c63fcadeb89a9a9c9208f221970e6fcc774c) --- doc/lispref/modes.texi | 21 ++++++++--- lisp/treesit.el | 85 ++++++++++++++++++++++++------------------ 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index d7909e78384..5ef3de665d9 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -5207,11 +5207,13 @@ more complex indentation engines. @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 @@ -5241,6 +5243,15 @@ and @var{anchor} should return a buffer position. 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 diff --git a/lisp/treesit.el b/lisp/treesit.el index d3413d1590f..be6df5a576b 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1555,13 +1555,13 @@ START and END mark the current to-be-propertized region." (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 @@ -1578,7 +1578,12 @@ ANCHOR and adds OFFSET to it, and indents to that column. OFFSET 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 @@ -2113,31 +2118,37 @@ OFFSET." (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. @@ -2193,12 +2204,14 @@ RULES." (_ 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 -- 2.39.5