After updating to version 21.1.2, the cursor position is wrong.

SyntaxEditor for WPF Forum

The latest build of this product (v21.1.2) was released 19 days ago, which was before this thread was created.
Posted 15 days ago by Sunshine - Appeon
Version: 21.1.2
Platform: .NET 5.0
Environment: Windows 10 (64-bit)
Avatar

After I did a text replacement, I repositioned the cursor position, but the cursor position I saw was still at the end of the replacement text.

For example:

I inserted a commented snippet in the code:

( '|' Represents the position of the cursor)

/// <summary>
/// |
/// </summary>

After replacing the text once, I repositioned the cursor on the second line. But the new version will position him at the end of the replacement text

like this:

/// <summary>
/// 
/// </summary>|

My code runs completely normal in the v2021.1.1 version, and the above problem occurs when I upgrade to v2021.1.2

Comments (10)

Posted 15 days ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

Can you post exact code to place in one of our samples to see this scenario occur so that we can debug the scenario?  Thanks!


Actipro Software Support

Posted 12 days ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

Thank you for the sample via a support ticket.  In that sample, you were handling the SyntaxEditor.DocumentTextChanged event and were applying an AutoComplete text change in response to it.  Right after calling the ITextChange.Apply() method, you set the editor.ActiveView.Selection.CaretOffset to a value.

In v21.1.2, we had to make a change to ensure that SyntaxEditor.DocumentTextChanged events fire in proper version order.  The issue in prior versions was this kind of scenario:

  • Some text change occurs, causing snapshot version 2.
  • The SyntaxEditor.DocumentTextChanged event is raised for version 2 and some watchers (like for selection changes, etc.) start handling it.
  • Code that provides auto-completion (or other things like auto-correct, auto-indent) also handles the SyntaxEditor.DocumentTextChanged event and applies another text change to further alter the original text change's text results.  This causes snapshot version 3.
  • The SyntaxEditor.DocumentTextChanged event is immediately raised for version 3 and all watchers handle version 3's event completely.
  • Now that the auto-completion logic's Apply() method returns, any remaining watchers of the DocumentTextChanged event that have not yet handled the event that was raised for version 2 get that event.
  • At this point, some event watchers will have received version 3's event before version 2 and things like outlining or selection could get out of whack.

The change we made in v21.1.2 was to ensure that SyntaxEditor.DocumentTextChanged always fires events in version order.  If it's in the middle of raising snapshot version 2's event, it will queue up snapshot version 3's event to fire only after ALL handlers of the event have first seen version 2's event.

In your scenario, this means that the view is not yet aware of the new snapshot created by your auto-complete, even though it returned from that text change's Apply() method.  Thus trying to set the view's selection with editor.ActiveView.Selection.CaretOffset based on the results of the auto-complete snapshot (which the view doesn't yet know about) will fail to work properly.

Instead of setting the CaretOffset property, you can do this kind of thing before your auto-completion's Apply() call, and it will work:

yourAutoCompleteChange.PostSelectionPositionRanges = TextPositionRange.CreateCollection(new TextPositionRange(0, 5, 0, 5), isBlock: false);

That allows you to specify what the resulting selection should be after the text change is applied.  There it's saying it should be a zero-length selection at position (0, 5).

I hope that helps explain why the event logic change was necessary and how to alter your selection code appropriately.


Actipro Software Support

Posted 10 days ago by Sunshine - Appeon
Avatar

According to the solution you provided, I seem to have encountered a new problem:

Still this scene:

I want to insert a comment snippet and then reposition the cursor.

When I enter the third'/', I need to insert this snippet.

/// <summary>
/// |
/// </summary>

In the data I obtained, I only know that the position of the cursor needs to be positioned at offset 19.

So I need to convert the offset to position,but at this time I cannot get the snapshot after inserting the snippet.

Even if I call myAutoCompleteChange.Replace().

The content of myAutoCompleteChange.Snapshot still does not change, which prevents me from getting the correct position after inserting the snippet.

///

How to deal with this situation?

Posted 9 days ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Yes, snapshots cannot be immediately updated in the view in auto-complete scenarios for the reason previously described where changed event handlers for the original primary text change were not able to fully complete prior to the auto-complete text change triggering another snapshot and its changed event handlers were all executed.  This would lead to some view and language services getting changed events firing out of snapshot version order, which could lead to problems, some of which could be bad.

Due to the recent change, while the view hasn't yet updated itself to the latest document snapshot (since the event handler where it updates hasn't executed yet in this scenario), the document itself will have the latest snapshot on its CurrentSnapshot property.  Thus instead of using editor.ActiveView.CurrentSnapshot, you should be able to look at editor.Document.CurrentSnapshot instead.


Actipro Software Support

Posted 9 days ago by Sunshine - Appeon
Avatar

Thanks for your hints!

After myChange.Apply(), I set the value of myChange.PostSelectionPositionRanges to reposition the cursor position. Achieved what I wanted, is this correct?

Posted 9 days ago by Sunshine - Appeon
Avatar

Is it only SyntaxEditor.DocumentTextChanged processing that has changed?

Others, such as whether the triggering of IEditorDocumentTextChangeEventSink.NotifyDocumentTextChanged will affect.

We have a lot of code to replace text and then position the cursor. But it seems that only part of it is affected

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

Yes, setting ITextChange.PostSelectionPositionRanges is the best way to ensure the selection is at a specific place after any text change.  Setting that property will work both before and after the change made in the prior version since PostSelectionPositionRanges is used after the text change is applied to the view.

On that note, I would recommend setting it before you call ITextChange.Apply() though.  Setting it after may work in this particular scenario since the changed event is getting buffered, but in normal circumstances when there isn't already a text change being processed, setting PostSelectionPositionRanges after Apply() would be too late.

Right now only the view's DocumentTextChange events have been affected by the recent change, but looking into the core text model to possibly do something similar is something else we're considering, as the same core issue may still exist there where events could fire out of snapshot version order.  That is marked for investigation here.  Even if that does change in the future, if you set PostSelectionPositionRanges prior to calling Apply(), it should work fine in any scenario.

If you have any other suggestions for how we can make some of this easier for you, while still handling the fundamental problem described in previous replies, feel free to post here and we can discuss further.


Actipro Software Support

Posted 8 days ago by Sunshine - Appeon
Avatar

1.On that note, I would recommend setting it before you call ITextChange.Apply() though.  Setting it after may work in this particular scenario since the changed event is getting buffered, but in normal circumstances when there isn't already a text change being processed, setting PostSelectionPositionRanges after Apply() would be too late.

According to you say, I need to set the PostSelectionPositionRanges property before calling ITextChange.Apply().

But as I mentioned earlier, the data for cursor repositioning may be an offset. I need to convert him to position. I need the latest snapshot to convert the offset to the correct position.

So in this case, I can only call ITextChange.Apply() first. After getting the latest snapshot, the converted position can be obtained to reposition the cursor.

2.About the scope of influence

After this update, I temporarily found that two functions were abnormal.

(1)One is the formatting function, which I inherited from TextFormatter. In the implementation of the Format method, there is a method to reposition the cursor (reposition the cursor by setting Active.Selection.CaretOffset).

(2)The other is to insert comment snippet. This is achieved by subscribing to the SyntaxEditor.DocumentTextChanged event, and the cursor positioning fails.

In the method implemented by IEditorDocumentTextChangeEventSink. Is working as expected.

Posted 7 days ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

1) You really want to set PostSelectionPositionRanges before Apply() is called for consistent results in any scenario.  Going back to your sample above where you were inserting this in response to a "/" being typed:

/// <summary>
/// |
/// </summary>

You know the position (line/char) of the third "/" after "///" is typed since that's where the caret is when your auto-correct logic starts to execute.  Say it's on line index 9.  And you know after inserting this XML doc comment that the caret needs to end up at character 4 on the next line.  Therefore you want to set the PostSelectionPositionRanges to a zero-length selection at position (10, 4).

You may need to scan characters on the inserted text to determine the final position if you only had predetermined offset data of where the caret should end up relative to the start of this snippet.  So you scan each character and 1-up the character index after each character.  If you reach a "\n" character, 1-up the line index and reset the character index to 0.  Then you have a line/char relative to the start of the text snippet.  If you think it would be helpful, we could even provide a string helper method to do this.  Then you can use that relative line/char to help determine the position for PostSelectionPositionRanges.  

2) While IEditorDocumentTextChangeEventSink works in the latest version how you were doing things, keep in mind that may have to change in the future since those events may fire out of order as well in certain scenarios.  We are still pending investigation on that.  But as mentioned before, setting PostSelectionPositionRanges before Apply() is the best thing to do, in old versions and new versions, and it will work consistently in both.  


Actipro Software Support

Posted 6 days ago by Sunshine - Appeon
Avatar

This is equivalent to needing me to create a newest snapshot myself, and then convert the coordinates myself. This approach seems unreasonable.

The principle of PostSelectionPositionRanges is to reposition the cursor position after applying changes. Before that, he had the latest snapshot. Can we provide the property of PostSelectionRanges, we can set the range of the offset of the selection area.

Add Comment

Please log in to a validated account to post comments.