Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce size of interval tree used for tagging #73703

Merged
merged 1 commit into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@

using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;

namespace Microsoft.CodeAnalysis.Editor.Shared.Tagging;

internal partial class TagSpanIntervalTree<TTag>
{
private readonly struct IntervalIntrospector(ITextSnapshot snapshot) : IIntervalIntrospector<TagNode>
private readonly struct IntervalIntrospector(
ITextSnapshot snapshot,
SpanTrackingMode trackingMode)
: IIntervalIntrospector<ITagSpan<TTag>>
{
public readonly ITextSnapshot Snapshot = snapshot;
public int GetStart(ITagSpan<TTag> value)
=> GetTranslatedSpan(value, snapshot, trackingMode).Start;

public int GetStart(TagNode value)
=> value.GetStart(this.Snapshot);

public int GetLength(TagNode value)
=> value.GetLength(this.Snapshot);
public int GetLength(ITagSpan<TTag> value)
=> GetTranslatedSpan(value, snapshot, trackingMode).Length;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,30 @@ namespace Microsoft.CodeAnalysis.Editor.Shared.Tagging;
/// tracked. That way you can query for intersecting/overlapping spans in a different snapshot
/// than the one for the tag spans that were added.
/// </summary>
internal partial class TagSpanIntervalTree<TTag> where TTag : ITag
internal sealed partial class TagSpanIntervalTree<TTag>(
ITextBuffer textBuffer,
SpanTrackingMode trackingMode,
IEnumerable<ITagSpan<TTag>>? values = null) where TTag : ITag
{
private readonly IntervalTree<TagNode> _tree;
private readonly ITextBuffer _textBuffer;
private readonly SpanTrackingMode _spanTrackingMode;

public TagSpanIntervalTree(ITextBuffer textBuffer,
SpanTrackingMode trackingMode,
IEnumerable<ITagSpan<TTag>>? values = null)
private readonly ITextBuffer _textBuffer = textBuffer;
private readonly SpanTrackingMode _spanTrackingMode = trackingMode;
private readonly IntervalTree<ITagSpan<TTag>> _tree = IntervalTree.Create(
new IntervalIntrospector(textBuffer.CurrentSnapshot, trackingMode),
values);

private static SnapshotSpan GetTranslatedSpan(
ITagSpan<TTag> originalTagSpan, ITextSnapshot textSnapshot, SpanTrackingMode trackingMode)
{
_textBuffer = textBuffer;
_spanTrackingMode = trackingMode;

var nodeValues = values?.Select(ts => new TagNode(ts, trackingMode));
var localSpan = originalTagSpan.Span;

_tree = IntervalTree.Create(new IntervalIntrospector(textBuffer.CurrentSnapshot), nodeValues);
return localSpan.Snapshot == textSnapshot
? localSpan
: localSpan.TranslateTo(textSnapshot, trackingMode);
}

private static TagSpan<TTag> GetTranslatedTagSpan(ITagSpan<TTag> originalTagSpan, ITextSnapshot textSnapshot, SpanTrackingMode trackingMode)
=> new(GetTranslatedSpan(originalTagSpan, textSnapshot, trackingMode), originalTagSpan.Tag);

public ITextBuffer Buffer => _textBuffer;

public SpanTrackingMode SpanTrackingMode => _spanTrackingMode;
Expand All @@ -48,7 +54,7 @@ public bool HasSpanThatContains(SnapshotPoint point)
var snapshot = point.Snapshot;
Debug.Assert(snapshot.TextBuffer == _textBuffer);

return _tree.HasIntervalThatContains(point.Position, length: 0, new IntervalIntrospector(snapshot));
return _tree.HasIntervalThatContains(point.Position, length: 0, new IntervalIntrospector(snapshot, _spanTrackingMode));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of the node needing to hold onto the tracing mode, we just pass it through with the introspector (a lightweight struct) when queryign the tree.

}

public IList<TagSpan<TTag>> GetIntersectingSpans(SnapshotSpan snapshotSpan)
Expand All @@ -66,16 +72,18 @@ private void AppendIntersectingSpansInSortedOrder(SnapshotSpan snapshotSpan, Seg
var snapshot = snapshotSpan.Snapshot;
Debug.Assert(snapshot.TextBuffer == _textBuffer);

using var intersectingIntervals = TemporaryArray<TagNode>.Empty;
using var intersectingIntervals = TemporaryArray<ITagSpan<TTag>>.Empty;
_tree.FillWithIntervalsThatIntersectWith(
snapshotSpan.Start, snapshotSpan.Length, ref intersectingIntervals.AsRef(), new IntervalIntrospector(snapshot));
snapshotSpan.Start, snapshotSpan.Length,
ref intersectingIntervals.AsRef(),
new IntervalIntrospector(snapshot, _spanTrackingMode));

foreach (var tagNode in intersectingIntervals)
result.Add(tagNode.GetTranslatedTagSpan(snapshot));
foreach (var tagSpan in intersectingIntervals)
result.Add(GetTranslatedTagSpan(tagSpan, snapshot, _spanTrackingMode));
}

public IEnumerable<ITagSpan<TTag>> GetSpans(ITextSnapshot snapshot)
=> _tree.Select(tn => tn.GetTranslatedTagSpan(snapshot));
=> _tree.Select(tn => GetTranslatedTagSpan(tn, snapshot, _spanTrackingMode));

public bool IsEmpty()
=> _tree.IsEmpty();
Expand Down
Loading