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

Move interval trees over to being TextSpan based #73714

Merged
merged 4 commits 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 @@ -16,6 +16,7 @@
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.CodeAnalysis.Utilities;
using Microsoft.CodeAnalysis.Workspaces;
Expand Down Expand Up @@ -275,10 +276,7 @@ async Task AddEmbeddedClassificationsAsync()

private readonly struct ClassificationTagSpanIntervalIntrospector : IIntervalIntrospector<TagSpan<IClassificationTag>>
{
public int GetStart(TagSpan<IClassificationTag> value)
=> value.Span.Start;

public int GetLength(TagSpan<IClassificationTag> value)
=> value.Span.Length;
public TextSpan GetSpan(TagSpan<IClassificationTag> value)
=> value.Span.Span.ToTextSpan();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

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

namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename;

internal sealed class TrackingSpanIntrospector(ITextSnapshot snapshot) : IIntervalIntrospector<ITrackingSpan>
{
private readonly ITextSnapshot _snapshot = snapshot;

public int GetStart(ITrackingSpan value)
=> value.GetStartPoint(_snapshot);

public int GetLength(ITrackingSpan value)
=> value.GetSpan(_snapshot).Length;
public TextSpan GetSpan(ITrackingSpan value)
=> value.GetSpan(snapshot).Span.ToTextSpan();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// 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.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;

Expand All @@ -15,10 +17,7 @@ private readonly struct IntervalIntrospector(
SpanTrackingMode trackingMode)
: IIntervalIntrospector<ITagSpan<TTag>>
{
public int GetStart(ITagSpan<TTag> value)
=> GetTranslatedSpan(value, snapshot, trackingMode).Start;

public int GetLength(ITagSpan<TTag> value)
=> GetTranslatedSpan(value, snapshot, trackingMode).Length;
public TextSpan GetSpan(ITagSpan<TTag> value)
=> GetTranslatedSpan(value, snapshot, trackingMode).Span.ToTextSpan();
}
}
17 changes: 6 additions & 11 deletions src/EditorFeatures/Test/Collections/IntervalTreeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,18 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.UnitTests.Collections;

public class IntervalTreeTests
public sealed class IntervalTreeTests
{
private readonly struct TupleIntrospector<T> : IIntervalIntrospector<Tuple<int, int, T>>
{
public int GetStart(Tuple<int, int, T> value)
=> value.Item1;

public int GetLength(Tuple<int, int, T> value)
=> value.Item2;
public TextSpan GetSpan(Tuple<int, int, T> value)
=> new(value.Item1, value.Item2);
}

private static IEnumerable<SimpleIntervalTree<Tuple<int, int, string>, TupleIntrospector<string>>> CreateTrees(params Tuple<int, int, string>[] values)
Expand Down Expand Up @@ -260,11 +258,8 @@ public void TestEmptySpanAtStart()

private readonly struct Int32Introspector : IIntervalIntrospector<int>
{
public int GetLength(int value)
=> 0;

public int GetStart(int value)
=> value;
public TextSpan GetSpan(int value)
=> new(value, 0);
}

private static IntervalTree<int> CreateIntTree(params int[] values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// 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.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.VisualStudio.LanguageServices.DocumentOutline;

Expand All @@ -14,10 +16,7 @@ internal sealed partial class DocumentOutlineViewModel
/// </summary>
private readonly struct IntervalIntrospector : IIntervalIntrospector<DocumentSymbolDataViewModel>
{
public int GetStart(DocumentSymbolDataViewModel value)
=> value.Data.RangeSpan.Start;

public int GetLength(DocumentSymbolDataViewModel value)
=> value.Data.RangeSpan.Length;
public TextSpan GetSpan(DocumentSymbolDataViewModel value)
=> value.Data.RangeSpan.Span.ToTextSpan();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
// 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.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin.MarginGlyph;
using Microsoft.VisualStudio.Text;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin;

internal partial class InheritanceGlyphManager
{
private record GlyphData
private sealed record GlyphData
{
public SnapshotSpan SnapshotSpan { get; }
public InheritanceMarginGlyph Glyph { get; }
Expand All @@ -30,10 +32,7 @@ public void Deconstruct(out SnapshotSpan span, out InheritanceMarginGlyph glyph)

private readonly struct GlyphDataIntrospector : IIntervalIntrospector<GlyphData>
{
public int GetStart(GlyphData data)
=> data.SnapshotSpan.Start;

public int GetLength(GlyphData data)
=> data.SnapshotSpan.Length;
public TextSpan GetSpan(GlyphData data)
=> data.SnapshotSpan.Span.ToTextSpan();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@
// See the LICENSE file in the project root for more information.

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

namespace Microsoft.CodeAnalysis.Classification;

internal static partial class ClassifierHelper
{
private readonly struct ClassifiedSpanIntervalIntrospector : IIntervalIntrospector<ClassifiedSpan>
{
public static readonly ClassifiedSpanIntervalIntrospector Instance = new ClassifiedSpanIntervalIntrospector();
public static readonly ClassifiedSpanIntervalIntrospector Instance = new();

public int GetLength(ClassifiedSpan value)
=> value.TextSpan.Length;

public int GetStart(ClassifiedSpan value)
=> value.TextSpan.Start;
public TextSpan GetSpan(ClassifiedSpan value)
=> value.TextSpan;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ internal class TextChangeMerger
{
private readonly struct IntervalIntrospector : IIntervalIntrospector<TextChange>
{
int IIntervalIntrospector<TextChange>.GetStart(TextChange value) => value.Span.Start;
int IIntervalIntrospector<TextChange>.GetLength(TextChange value) => value.Span.Length;
public TextSpan GetSpan(TextChange value)
=> value.Span;
}

private readonly Document _oldDocument;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
// 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.Text;

namespace Microsoft.CodeAnalysis.Shared.Collections;

internal interface IIntervalIntrospector<T>
{
int GetStart(T value);
int GetLength(T value);
TextSpan GetSpan(T value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ protected static bool Contains<TIntrospector>(T value, int start, int length, in
var otherStart = start;
var otherEnd = start + length;

var thisStart = introspector.GetStart(value);
var thisEnd = thisStart + introspector.GetLength(value);
var thisSpan = introspector.GetSpan(value);
var thisStart = thisSpan.Start;
var thisEnd = thisSpan.End;

// 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.
Expand All @@ -82,8 +83,9 @@ private static bool IntersectsWith<TIntrospector>(T value, int start, int length
var otherStart = start;
var otherEnd = start + length;

var thisStart = introspector.GetStart(value);
var thisEnd = thisStart + introspector.GetLength(value);
var thisSpan = introspector.GetSpan(value);
var thisStart = thisSpan.Start;
var thisEnd = thisSpan.End;

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

var thisStart = introspector.GetStart(value);
var thisEnd = thisStart + introspector.GetLength(value);
var thisSpan = introspector.GetSpan(value);
var thisStart = thisSpan.Start;
var thisEnd = thisSpan.End;

// 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.
Expand Down Expand Up @@ -251,7 +254,7 @@ private static bool ShouldExamineRight<TIntrospector>(
{
// right children's starts will never be to the left of the parent's start so we should consider right
// subtree only if root's start overlaps with interval's End,
if (introspector.GetStart(currentNode.Value) <= end)
if (introspector.GetSpan(currentNode.Value).Start <= end)
{
right = currentNode.Right;
if (right != null && GetEnd(right.MaxEndNode.Value, in introspector) >= start)
Expand Down Expand Up @@ -282,7 +285,7 @@ private static bool ShouldExamineLeft<TIntrospector>(
protected static Node Insert<TIntrospector>(Node? root, Node newNode, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
{
var newNodeStart = introspector.GetStart(newNode.Value);
var newNodeStart = introspector.GetSpan(newNode.Value).Start;
return Insert(root, newNode, newNodeStart, in introspector);
}

Expand All @@ -296,7 +299,7 @@ private static Node Insert<TIntrospector>(Node? root, Node newNode, int newNodeS

Node? newLeft, newRight;

if (newNodeStart < introspector.GetStart(root.Value))
if (newNodeStart < introspector.GetSpan(root.Value).Start)
{
newLeft = Insert(root.Left, newNode, newNodeStart, in introspector);
newRight = root.Right;
Expand Down Expand Up @@ -383,7 +386,7 @@ IEnumerator IEnumerable.GetEnumerator()

protected static int GetEnd<TIntrospector>(T value, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
=> introspector.GetStart(value) + introspector.GetLength(value);
=> introspector.GetSpan(value).End;

protected static int MaxEndValue<TIntrospector>(Node? node, in TIntrospector introspector)
where TIntrospector : struct, IIntervalIntrospector<T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ namespace Microsoft.CodeAnalysis.Shared.Collections;

internal readonly struct TextSpanIntervalIntrospector : IIntervalIntrospector<TextSpan>
{
public int GetStart(TextSpan value)
=> value.Start;

public int GetLength(TextSpan value)
=> value.Length;
public TextSpan GetSpan(TextSpan value)
=> value;
}

internal sealed class TextSpanIntervalTree(IEnumerable<TextSpan>? values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,18 @@ private class AnchorData(AnchorIndentationOperation operation, SyntaxToken ancho
public int OriginalColumn { get; } = originalColumn;
}

private readonly struct FormattingContextIntervalIntrospector
: IIntervalIntrospector<AnchorData>,
private readonly struct FormattingContextIntervalIntrospector :
IIntervalIntrospector<AnchorData>,
IIntervalIntrospector<IndentationData>,
IIntervalIntrospector<RelativeIndentationData>
{
int IIntervalIntrospector<AnchorData>.GetStart(AnchorData value)
=> value.TextSpan.Start;
TextSpan IIntervalIntrospector<AnchorData>.GetSpan(AnchorData value)
=> value.TextSpan;

int IIntervalIntrospector<AnchorData>.GetLength(AnchorData value)
=> value.TextSpan.Length;
TextSpan IIntervalIntrospector<IndentationData>.GetSpan(IndentationData value)
=> value.TextSpan;

int IIntervalIntrospector<IndentationData>.GetStart(IndentationData value)
=> value.TextSpan.Start;

int IIntervalIntrospector<IndentationData>.GetLength(IndentationData value)
=> value.TextSpan.Length;

int IIntervalIntrospector<RelativeIndentationData>.GetStart(RelativeIndentationData value)
=> value.InseparableRegionSpan.Start;

int IIntervalIntrospector<RelativeIndentationData>.GetLength(RelativeIndentationData value)
=> value.InseparableRegionSpan.Length;
TextSpan IIntervalIntrospector<RelativeIndentationData>.GetSpan(RelativeIndentationData value)
=> value.InseparableRegionSpan;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,17 @@
// See the LICENSE file in the project root for more information.

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

namespace Microsoft.CodeAnalysis.Formatting;

internal readonly struct SuppressIntervalIntrospector :
IIntervalIntrospector<SuppressSpacingData>,
IIntervalIntrospector<SuppressWrappingData>
{
int IIntervalIntrospector<SuppressSpacingData>.GetStart(SuppressSpacingData value)
=> value.TextSpan.Start;
TextSpan IIntervalIntrospector<SuppressSpacingData>.GetSpan(SuppressSpacingData value)
=> value.TextSpan;

int IIntervalIntrospector<SuppressSpacingData>.GetLength(SuppressSpacingData value)
=> value.TextSpan.Length;

int IIntervalIntrospector<SuppressWrappingData>.GetStart(SuppressWrappingData value)
=> value.TextSpan.Start;

int IIntervalIntrospector<SuppressWrappingData>.GetLength(SuppressWrappingData value)
=> value.TextSpan.Length;
TextSpan IIntervalIntrospector<SuppressWrappingData>.GetSpan(SuppressWrappingData value)
=> value.TextSpan;
}
Loading
Loading