AST update and IndentProvider

SyntaxEditor for WPF Forum

Posted 8 months ago by Bernd Timmermann - Manager, TSE GmbH
Version: 23.1.3
Platform: .NET 6.0
Environment: Windows 10 (64-bit)

I try to implement the indent provider based on the AST.

My parser uses a ThreadedParseRequestDispatcher.

When GetIndentAmount() is called, the ParseData and therefore the AST is not yet updated.

Any idea how to solve that, apart from checking all indents when beeing back in the UI thread (whicht might be CPU consuming)?

Comments (4)

Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA


That is true, since parsing is occurring asynchronously, the latest AST will almost never be available yet when GetIndentAmount() is called.  You don't want to hold up the main UI thread to wait for the parsing to complete since that will introduce editing performance issues.

In our advanced languages, I don't believe we look at the AST at all for GetIndentAmount and do token scanning only.  We figure out how to adjust the indent amount based on tokens we see since a last valid "base" indent amount, meaning things are relative to a previous line.

In our advanced language implementations for IntelliPrompt features (not auto-indent), we do get a little more complex and use a combination of tokens scanning and AST examination. For that concept, we scan the tokens backward from the starting offset.  The text and tokens are always up-to-date and reliable.  Then once you reach a line or two back where you feel the existing (not yet current) parse data might be in sync, you can translate that snapshot offset to the snapshot that created the AST in the parse data.  Do a recursive search with that translated offset into the AST to locate the deepest AST node that contains the offset.  With that, you now know the containing structure.

Actipro Software Support

Posted 8 months ago by Bernd Timmermann - Manager, TSE GmbH

Thanks. I understand your concept, but if I have a parser at hand, that knows about the structure, it should do the work.

Therefore I let it compute all indents in the "CreateParseData"-Override (still in Parser-Thread).

Currently I have the Indent mode set to "None" and go with the OnDocumentTextChanged() event alone.

While continously typing, the update works fine, and only for the last change I either need to type an additional blank or start some explizit reindenting funktion.

This works astonishingly good with one exception: The cursor jumps around on changes not done sequentially.

If I have a block and put an "If" before, and an "Endif" beyond, the cursor ("Caret") jumps back several lines after entering the "E" of the "EndIf" at the end of the block. That's when there is enough data to reindent the block.

I assume that's caused by the inserted spaces in the text and/or the asynchronous nature of "ITextSnapshotLine.IndentAmount".

Restoring the cursor position by resetting the "editor.ActiveView.Selection.CaretPosition" to the value saved on entry into "OnDocumentTextChanged()" doesn't help (the DisplayLine of the saved position is still correct, as lines didn't change).

Any idea how I could stop my solution from jumping around?

Besides: When I change indents that way, I get a lot of induced OnDocumentTextChanged() calls.
I currently get rid of this by a pause in my indent logic created by time locker at the beginning of the "OnDocumentTextChanged()" method (after calling the base method).

(Another question: Should I put such problems in a new thread, or is it OK to continue the current one?)

Thank in advance.

Answer - Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA


The ITextSnapshotLine.IndentAmount property setter will potentially trigger a text change with RetainSelection = true if indentation needs to be adjusted.  This will yield many DocumentTextChanged events if you call it repeatedly.  It's not recommended to do so.

Instead, I'd suggest that for bulk editing of indents, you create an ITextChange from ITextSnapshot.CreateTextChange.  Add an operation for each update to and indentation.  You also can set the ITextChange.PostSelectionPositionRanges property to designate exactly what the selection should be after the text change is applied.  Then call ITextChange.Apply to update all the indents in one atomic unit, and one DocumentTextChanged event.

Regarding the thread, as long as a reply is directly related to the thread's original discussion, it's fine to keep the reply in the same thread.  For something that veers off in another direction, it might be better to start a new thread.

Actipro Software Support

Posted 8 months ago by Bernd Timmermann - Manager, TSE GmbH

Thanks a lot!

That works way better and as all data are available is even easy to implement.

I think that this hint is worth to be mentioned in the docs.

The latest build of this product (v24.1.2) was released 2 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.