diff --git a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.TagNode.cs b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.TagNode.cs index 31ad719a6512c..3e3bf0c4679e5 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.TagNode.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.TagNode.cs @@ -2,7 +2,6 @@ // 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; @@ -10,41 +9,29 @@ namespace Microsoft.CodeAnalysis.Editor.Shared.Tagging; internal partial class TagSpanIntervalTree { - private class TagNode(ITagSpan ts, SpanTrackingMode trackingMode) + private readonly struct TagNode(ITagSpan 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 _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 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; } } diff --git a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs index 28cf058ccddda..7e0c33b7fcba5 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs @@ -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(tagNode.Span.GetSpan(snapshot), tagNode.Tag)); + result.Add(tagNode.GetTranslatedTagSpan(snapshot)); } public IEnumerable> GetSpans(ITextSnapshot snapshot) - => _tree.Select(tn => new TagSpan(tn.Span.GetSpan(snapshot), tn.Tag)); + => _tree.Select(tn => tn.GetTranslatedTagSpan(snapshot)); public bool IsEmpty() => _tree.IsEmpty(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index f90939d8824f0..7dbd8c25375e0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -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; @@ -54,10 +55,11 @@ protected static bool Contains(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; @@ -72,8 +74,8 @@ private static bool IntersectsWith(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; } @@ -84,13 +86,13 @@ private static bool OverlapsWith(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);