Subject: Re: bug#76398: treesit-aggregated-outline-predicate

From: Yuan Fu <[email-masked]>
Date: Tue, 11 Mar 2025 01:46:47 -0700

> On Mar 2, 2025, at 9:27 AM, Juri Linkov <[email-masked]> wrote:
> 
>>> Maybe a better name would be 'treesit-outline--closest-range-boundary'?
>>> 
>>> It's needed to prevent skipping the range boundaries that
>>> treesit-navigate-thing does by default, e.g. instead of
>>> 
>>> from (the starting point of the navigation)
>>> range_1_beg
>>> range_1_end
>>> to (the ending point of the navigation)
> 
> But currently handling a local parser is not possible in the above function.
> For example, I tried:
> 
>  (mapcar #'treesit-parser-included-ranges
>          (append (treesit-parser-list)
>                  (treesit-local-parsers-at (point))))
> 
> But it's position-dependent and can't find the next local-parser
> when the position of point is before the start of the local-parser.
> 
>>> Like you noticed, we need to check ranges, not parsers anyway,
>>> so treesit-local-parsers-at can't help here.
>> 
>> Combine with your reply above, it seems we need some information attached
>> to the ranges (with either overlay or text property), rather than to the
>> parser. Is that right?
> 
> Maybe.  One of two needs I have discovered so far is the ability
> to find the next/previous range boundary.

I pushed some changes. Now this should be doable by searching for overlays boundaries. Both local and non-local parsers now have an overlay that spans the range they’re in. In the case of non-local parsers, each range it parses gets an overlay. You can search for the comment that’s marked with (ref:local-parser-overlay) in treesit.el to see the properties I put on the overlays.

>> When updating ranges, we can mark the ranges covered
>> by each embed parser, and perhaps link the text prop or overlay to the
>> “parent node”.
> 
> The ability to find the parent node of the range is another need.

I think your use-case is to continue searching upwards across parser boundary, right? I wonder if having access to the parent parser is enough? Because once you know the parser, you can just get the node-at-point.

>>>>>> I bring this up because this tree structure would’ve
>>>>>> solved your problem here as well, once we have it.
> 
> A tree of ranges (including ranges of local parsers)
> would solve the problem indeed.

This should be possible now by going over the overlays at point and find the parser that has a -1 embed level. There should only one parser with -1 embed level.

>> I’ve mostly worked out arbitrary nesting of embedded parsers.
>> I tested with markdown -> javascript -> jsdoc, and it seems to work
>> fine.  What I’m not sure about right now is the navigation part.
> 
> I tested on mhtml-ts-mode -> javascript -> jsdoc and
> (mapcar 'treesit-parser-embed-level (treesit-local-parsers-at (point)))
> correctly returns (2).  Whereas
> (mapcar 'treesit-parser-parent-node (treesit-local-parsers-at (point)))
> returns (nil).
> 
> BTW, while deleting a jsdoc block I get such backtrace:
> 
> Debugger entered--Lisp error: (treesit-parser-deleted #<treesit-parser for jsdoc>)
>  treesit-node-on(1 202 #<treesit-parser for jsdoc>)
>  treesit--explorer-refresh()
>  apply(treesit--explorer-refresh nil)
>  timer-event-handler([t ...
> 
> and while editing a jsdoc block (inserting a newline before it):
> 
> Debugger entered--Lisp error: (treesit-no-parser jsdoc)
>  treesit-buffer-root-node(jsdoc)
>  treesit-node-on(60 60)
>  treesit--indent-1()
>  treesit-indent()
>  indent-according-to-mode()
>  electric-indent-post-self-insert-function()
>  newline(nil 1)
>  funcall-interactively(newline nil 1)
>  command-execute(newline)

Thanks, these should be fixed now.

Yuan