Override GetTags fails to show Popups for SquiggleTags

SyntaxEditor for Windows Forms Forum

Posted 4 years ago by AF
Version: 20.1.0402
Platform: .NET 4.8
Environment: Windows 10 (64-bit)
Avatar

The CustomSquiggleTagger example is not very useful as it assumes you willl only need to update the Squiggles after some external event. i.e. the MainControl constructor just calls 'this.RefreshSquiggleTags();', rather than after a document text change.

If instead, we move the contents of RefreshSquiggleTags() into an overide of GetTags in CustomSquiggleTagger, the squiggles are shown correctly but hovering the mouse does not produce the expected popup, e.g.  'Instance number 1'.

Here is my code for the GetTags override method in CustomSquiggleTagger:

public override IEnumerable<TagSnapshotRange<ISquiggleTag>> GetTags(NormalizedTextSnapshotRangeCollection snapshotRanges, object parameter)
{
    if (snapshotRanges != null)
    {
        foreach (TextSnapshotRange range in snapshotRanges)
        {
            var snapshotText = range.GetText(LineTerminator.Newline);

            // Look for regex pattern matches
            var matches = Regex.Matches(snapshotText, @"\bActipro\b", RegexOptions.IgnoreCase);
            for (var matchIndex = 0; matchIndex < matches.Count; matchIndex++)
            {
                var match = matches[matchIndex];

                // Create a version range for the match
                var snapshotRange = TextSnapshotRange.FromSpan(range.Snapshot, range.StartOffset + match.Index, match.Length);

                // Create a tag, and include a quick info tip if specified
                var tag = new SquiggleTag();
                tag.ClassificationType = ClassificationTypes.Warning;  // This classification type is mapped in the tagger to a Green color
                tag.ContentProvider = new PlainTextContentProvider(String.Format("Instance number {0}", matchIndex + 1));

                // Add the tag to the tagger
                yield return new TagSnapshotRange<ISquiggleTag>(snapshotRange, tag);
            }
        }
    }
}

Comments (5)

Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

If you debug the GetTags method, you see that it's called with a single zero-length snapshot range in the mouse over scenario, which is the offset the mouse is over. 

But per the code above, the snapshotText variable will be an empty string, so no match is found.  If a zero-length snapshot range is passed, you might want to take an alternate code path and get the text range of the snapshot line that contains that offset instead.  Then scan that line's text to see if any results cover the offset passed in, and only return results that do.


Actipro Software Support

Posted 4 years ago by AF
Avatar

Hi, thanks for your response.

I have updated the code as suggested, but when the mouse hovers over the squiggle the popup flickers on and off as if in a loop constantly calling GetTags.

Please note, this is the same issue I have in my real app which worked OK in Actipro WPF v18 but flickers after migrating to Winforms v20.

Updated code...

public override IEnumerable<TagSnapshotRange<ISquiggleTag>> GetTags(NormalizedTextSnapshotRangeCollection snapshotRanges, object parameter)
{
    if (snapshotRanges != null)
    {
        foreach (TextSnapshotRange range in snapshotRanges)
        {
            var snapshotText = range.GetText(LineTerminator.Newline);

            if (snapshotText.Length == 0)
            {
                var textRange = this.Document.CurrentSnapshot.GetWordTextRange(range.StartOffset);
                snapshotText = this.Document.CurrentSnapshot.GetSubstring(textRange);
            }

            // Look for regex pattern matches
            var matches = Regex.Matches(snapshotText, @"\bActipro\b", RegexOptions.IgnoreCase);
            for (var matchIndex = 0; matchIndex < matches.Count; matchIndex++)
            {
                var match = matches[matchIndex];

                // Create a version range for the match
                var snapshotRange = TextSnapshotRange.FromSpan(range.Snapshot, range.StartOffset + match.Index, match.Length);

                // Create a tag, and include a quick info tip if specified
                var tag = new SquiggleTag();
                tag.ClassificationType = ClassificationTypes.Warning;  // This classification type is mapped in the tagger to a Green color
                tag.ContentProvider = new PlainTextContentProvider(String.Format("Instance number {0}", matchIndex + 1));

                // Add the tag to the tagger
                yield return new TagSnapshotRange<ISquiggleTag>(snapshotRange, tag);
            }
        }
    }
}
Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

The code is the same in WPF, so this shouldn't have worked there either.  Although WPF might not have raised an immediate mouse move event like WinForms seems to do when the quick info opens, and a check in that mouse move event thinks the tags are different so it closes.  The reason the tags are different is because you create a new tag instance on each yield.  

If you want to use quick info, you might need to cache the last TagSnapshotRange instance returned from GetTags for a zero-length snapshot range.  And if subsequent zero-length snapshot range request is within the same snapshot range (including same snapshot version, meaning no new edits have been made), then you return your cached TagSnapshotRange instance.  Our code will know the same tag is being returned and it won't need to close for the mouse move.


Actipro Software Support

Posted 4 years ago by AF
Avatar

Thanks, but wouldn't it be better if your code only closed the popup when the mouse pointer moved away from the squiggle text?

Posted 4 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

The problem there is that it's using the generic quick info API and you could theoretically have two ranges of text that provide quick info but that partially overlap.  In that scenario, as you move the mouse from a lower priority offset to one in the overlapping part that is higher priority, we would want the quick info to change even though it was still in the text range of the original one.  That's not a common scenario, but it could happen and is why it's designed how it is.

Normally you don't need to do any of the goofy caching mentioned above when you are using a collection tagger, since those instances are retained in the collection and reused.  It's only in this case, you are dynamically creating the tags, which is why it needs to be done.


Actipro Software Support

The latest build of this product (v24.1.0) 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.