Skip to content

Commit

Permalink
Merge pull request #73676 from CyrusNajmabadi/tagNode
Browse files Browse the repository at this point in the history
Convert interval-tree type to a struct
  • Loading branch information
CyrusNajmabadi authored May 25, 2024
2 parents 6c9936c + bb87e4d commit 122dc90
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,36 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;

namespace Microsoft.CodeAnalysis.Editor.Shared.Tagging;

internal partial class TagSpanIntervalTree<TTag>
{
private class TagNode(ITagSpan<TTag> ts, SpanTrackingMode trackingMode)
private readonly struct TagNode(ITagSpan<TTag> tagSpan, SpanTrackingMode trackingMode)
{
public readonly TTag Tag = ts.Tag;
public readonly ITrackingSpan Span = ts.Span.CreateTrackingSpan(trackingMode);
private SnapshotSpan _snapshotSpan = ts.Span;
private readonly ITagSpan<TTag> _originalTagSpan = tagSpan;
private readonly SpanTrackingMode _trackingMode = trackingMode;

private SnapshotSpan GetSnapshotSpan(ITextSnapshot textSnapshot)
{
var localSpan = _snapshotSpan;
if (localSpan.Snapshot == textSnapshot)
{
return localSpan;
}
else if (localSpan.Snapshot != null)
{
_snapshotSpan = default;
}

return default;
}
public TTag Tag => _originalTagSpan.Tag;

internal int GetStart(ITextSnapshot textSnapshot)
{
var localSpan = this.GetSnapshotSpan(textSnapshot);
return localSpan.Snapshot == textSnapshot
? localSpan.Start
: this.Span.GetStartPoint(textSnapshot);
}
public TagSpan<TTag> GetTranslatedTagSpan(ITextSnapshot textSnapshot)
=> new(GetTranslatedSpan(textSnapshot), Tag);

internal int GetLength(ITextSnapshot textSnapshot)
public SnapshotSpan GetTranslatedSpan(ITextSnapshot textSnapshot)
{
var localSpan = this.GetSnapshotSpan(textSnapshot);
var localSpan = _originalTagSpan.Span;

return localSpan.Snapshot == textSnapshot
? localSpan.Length
: this.Span.GetSpan(textSnapshot).Length;
? localSpan
: localSpan.TranslateTo(textSnapshot, _trackingMode);
}

public int GetStart(ITextSnapshot textSnapshot)
=> GetTranslatedSpan(textSnapshot).Start;

public int GetLength(ITextSnapshot textSnapshot)
=> GetTranslatedSpan(textSnapshot).Length;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ private void AppendIntersectingSpansInSortedOrder(SnapshotSpan snapshotSpan, Seg
snapshotSpan.Start, snapshotSpan.Length, ref intersectingIntervals.AsRef(), new IntervalIntrospector(snapshot));

foreach (var tagNode in intersectingIntervals)
result.Add(new TagSpan<TTag>(tagNode.Span.GetSpan(snapshot), tagNode.Tag));
result.Add(tagNode.GetTranslatedTagSpan(snapshot));
}

public IEnumerable<ITagSpan<TTag>> GetSpans(ITextSnapshot snapshot)
=> _tree.Select(tn => new TagSpan<TTag>(tn.Span.GetSpan(snapshot), tn.Tag));
=> _tree.Select(tn => tn.GetTranslatedTagSpan(snapshot));

public bool IsEmpty()
=> _tree.IsEmpty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Shared.Collections;
Expand Down Expand Up @@ -54,10 +55,11 @@ protected static bool Contains<TIntrospector>(T value, int start, int length, in
var otherStart = start;
var otherEnd = start + length;

var thisEnd = GetEnd(value, in introspector);
var thisStart = introspector.GetStart(value);
var thisEnd = thisStart + introspector.GetLength(value);

// make sure "Contains" test to be same as what TextSpan does
// TODO(cyrusn): This doesn't actually seem to match what TextSpan.Contains does. It doesn't specialize empty
// length in any way. Preserving this behavior for now, but we should consider changing this.
if (length == 0)
{
return thisStart <= otherStart && otherEnd < thisEnd;
Expand All @@ -72,8 +74,8 @@ private static bool IntersectsWith<TIntrospector>(T value, int start, int length
var otherStart = start;
var otherEnd = start + length;

var thisEnd = GetEnd(value, in introspector);
var thisStart = introspector.GetStart(value);
var thisEnd = thisStart + introspector.GetLength(value);

return otherStart <= thisEnd && otherEnd >= thisStart;
}
Expand All @@ -84,13 +86,13 @@ private static bool OverlapsWith<TIntrospector>(T value, int start, int length,
var otherStart = start;
var otherEnd = start + length;

var thisEnd = GetEnd(value, in introspector);
var thisStart = introspector.GetStart(value);
var thisEnd = thisStart + introspector.GetLength(value);

// TODO(cyrusn): This doesn't actually seem to match what TextSpan.OverlapsWith does. It doesn't specialize empty
// length in any way. Preserving this behavior for now, but we should consider changing this.
if (length == 0)
{
return thisStart < otherStart && otherStart < thisEnd;
}

var overlapStart = Math.Max(thisStart, otherStart);
var overlapEnd = Math.Min(thisEnd, otherEnd);
Expand Down

0 comments on commit 122dc90

Please sign in to comment.