Subject: Re: bug#77256: Treesit language-at-point

From: Juri Linkov <[email-masked]>
Date: Wed, 09 Apr 2025 09:18:52 +0300

>>> This would make sure the `treesit-language-at-point-function` is good
>>> enough to implement the default behavior and it makes it possible to use
>>> `add-function` on `treesit-language-at-point-function`.
>>
>> Unfortunately, it's too late to add a non-nil default value.
>
> Is it?
> I can't find any code out there that seems to care what is its default
> value (all users set it but doesn't look at it, AFAICT).

This case looks problematic:

                 ;; LANG can be nil.  We don't want to use the fallback
                 ;; in `treesit-language-at', so here we call
                 ;; `treesit-language-at-point-function' directly.
                 (let* ((lang (and treesit-language-at-point-function
                                   (funcall treesit-language-at-point-function
                                            pos)))

But I believe this whole 'let*' can be replaced by just
the 'treesit-parsers-at' call.  I hope Yuan could confirm this.

>> I already tried to do the same by adding 'forward-sexp-default-function'
>> to 'forward-sexp-function', but failed spectacularly (bug#70426),
>
> Yes, that one is much harder (it's one of the first I tried to change),

If no more code outside relies on the default value,
the default function could be added like this:
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 8e57a6dae14..a010ff24afb 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -165,7 +165,8 @@ treesit-primary-parser
 The primary parser should be a parser that parses the entire buffer, as
 opposed to embedded parsers which parses only part of the buffer.")
 
-(defvar-local treesit-language-at-point-function nil
+(defvar-local treesit-language-at-point-function
+    #'treesit-language-at-point-default
   "A function that returns the language at point.
 This is used by `treesit-language-at', which is used by various
 functions to determine which parser to use at point.
@@ -183,10 +184,13 @@ treesit-language-at
 the most relevant parser (hence language) by their embed level.
 If `treesit-language-at-point-function' is non-nil, return
 the return value of that function instead."
-  (if treesit-language-at-point-function
-      (funcall treesit-language-at-point-function position)
-    (treesit-parser-language
-     (car (treesit-parsers-at position)))))
+  (funcall treesit-language-at-point-function position))
+
+(defun treesit-language-at-point-default (position)
+  "Default function for `treesit-language-at-point-function'.
+Return the deepest parser by embed level."
+  (treesit-parser-language
+   (car (treesit-parsers-at position))))
 
 ;;; Node API supplement
 
BTW, I noticed now that indentation of the default value
in defvar-local is incorrect and differs from defvar and defcustom.
Here is the fix:

diff --git a/lisp/subr.el b/lisp/subr.el
index be847aab28a..164ed2a6f63 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -197,7 +197,7 @@ defvar-local
 Like `defvar' but additionally marks the variable as being automatically
 buffer-local wherever it is set.
 \n(fn SYMBOL &optional VALUE DOCSTRING)"
-  (declare (debug defvar) (doc-string 3) (indent 2))
+  (declare (debug defvar) (doc-string 3) (indent defun))
   ;; Can't use backquote here, it's too early in the bootstrap.
   (let ((value (car-safe args))
         (docstring (car-safe (cdr-safe args))))