-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Interval tree refactorings. #73846
Interval tree refactorings. #73846
Changes from 17 commits
eca0bb5
182c222
c84f885
1368a36
f300853
70f097b
dfd72e7
99771b6
3f7fb88
2d8d581
208c467
c59d72c
663d1fe
b78a75f
70bf293
b11bf5e
f9f2c87
d73ad9b
6fae781
4d43964
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ internal sealed partial class TagSpanIntervalTree<TTag>(SpanTrackingMode spanTra | |
public static readonly TagSpanIntervalTree<TTag> Empty = new(SpanTrackingMode.EdgeInclusive); | ||
|
||
private readonly SpanTrackingMode _spanTrackingMode = spanTrackingMode; | ||
private readonly IntervalTree<TagSpan<TTag>> _tree = IntervalTree<TagSpan<TTag>>.Empty; | ||
private readonly BinaryIntervalTree<TagSpan<TTag>> _tree = BinaryIntervalTree<TagSpan<TTag>>.Empty; | ||
|
||
public TagSpanIntervalTree( | ||
ITextSnapshot textSnapshot, | ||
|
@@ -39,7 +39,7 @@ public TagSpanIntervalTree( | |
// routines), and allows us to build the balanced tree directly without having to do any additional work. | ||
values.Sort(static (t1, t2) => t1.Span.Start.Position - t2.Span.Start.Position); | ||
|
||
_tree = IntervalTree<TagSpan<TTag>>.CreateFromSorted( | ||
_tree = BinaryIntervalTree<TagSpan<TTag>>.CreateFromSorted( | ||
new IntervalIntrospector(textSnapshot, trackingMode), values); | ||
} | ||
|
||
|
@@ -57,10 +57,10 @@ private static TagSpan<TTag> GetTranslatedTagSpan(TagSpan<TTag> originalTagSpan, | |
: new(GetTranslatedSpan(originalTagSpan, textSnapshot, trackingMode), originalTagSpan.Tag); | ||
|
||
public bool HasSpanThatContains(SnapshotPoint point) | ||
=> _tree.HasIntervalThatContains(point.Position, length: 0, new IntervalIntrospector(point.Snapshot, _spanTrackingMode)); | ||
=> _tree.Algorithms.HasIntervalThatContains(point.Position, length: 0, new IntervalIntrospector(point.Snapshot, _spanTrackingMode)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these helpers could not be extensions for two reasons:
|
||
|
||
public bool HasSpanThatIntersects(SnapshotPoint point) | ||
=> _tree.HasIntervalThatIntersectsWith(point.Position, new IntervalIntrospector(point.Snapshot, _spanTrackingMode)); | ||
=> _tree.Algorithms.HasIntervalThatIntersectsWith(point.Position, new IntervalIntrospector(point.Snapshot, _spanTrackingMode)); | ||
|
||
/// <summary> | ||
/// Gets all the spans that intersect with <paramref name="snapshotSpan"/> in sorted order and adds them to | ||
|
@@ -72,7 +72,7 @@ public void AddIntersectingTagSpans(SnapshotSpan snapshotSpan, SegmentedList<Tag | |
var snapshot = snapshotSpan.Snapshot; | ||
|
||
using var intersectingIntervals = TemporaryArray<TagSpan<TTag>>.Empty; | ||
_tree.FillWithIntervalsThatIntersectWith( | ||
_tree.Algorithms.FillWithIntervalsThatIntersectWith( | ||
snapshotSpan.Start, snapshotSpan.Length, | ||
ref intersectingIntervals.AsRef(), | ||
new IntervalIntrospector(snapshot, _spanTrackingMode)); | ||
|
@@ -132,7 +132,7 @@ public void RemoveIntersectingTagSpans( | |
buffer.Clear(); | ||
|
||
var textSnapshot = snapshotSpan.Snapshot; | ||
_tree.FillWithIntervalsThatIntersectWith( | ||
_tree.Algorithms.FillWithIntervalsThatIntersectWith( | ||
snapshotSpan.Span.Start, | ||
snapshotSpan.Span.Length, | ||
ref buffer.AsRef(), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
using System.Text; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.Collections; | ||
using Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.VirtualChars; | ||
using Microsoft.CodeAnalysis.CSharp.Extensions; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
@@ -477,15 +478,15 @@ private static void AppendFullLine(StringBuilder builder, TextLine line) | |
private static (TextSpanIntervalTree interpolationInteriorSpans, TextSpanIntervalTree restrictedSpans) GetInterpolationSpans( | ||
InterpolatedStringExpressionSyntax stringExpression, CancellationToken cancellationToken) | ||
{ | ||
var interpolationInteriorSpans = new TextSpanIntervalTree(); | ||
var restrictedSpans = new TextSpanIntervalTree(); | ||
var interpolationInteriorSpans = new SegmentedList<TextSpan>(); | ||
var restrictedSpans = new SegmentedList<TextSpan>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moving to tree construction at end, instead of as we go along. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tree construction at end. this will move to the FlatArrayIntervalTree in next pr. |
||
|
||
SourceText? text = null; | ||
foreach (var content in stringExpression.Contents) | ||
{ | ||
if (content is InterpolationSyntax interpolation) | ||
{ | ||
interpolationInteriorSpans.AddIntervalInPlace(TextSpan.FromBounds(interpolation.OpenBraceToken.Span.End, interpolation.CloseBraceToken.Span.Start)); | ||
interpolationInteriorSpans.Add(TextSpan.FromBounds(interpolation.OpenBraceToken.Span.End, interpolation.CloseBraceToken.Span.Start)); | ||
|
||
// We don't want to touch any nested strings within us, mark them as off limits. note, we only care if | ||
// the nested strings actually span multiple lines. A nested string on a single line is safe to move | ||
|
@@ -506,14 +507,14 @@ private static (TextSpanIntervalTree interpolationInteriorSpans, TextSpanInterva | |
var start = startLine.GetFirstNonWhitespacePosition() == descendantSpan.Start | ||
? startLine.Start | ||
: descendantSpan.Start; | ||
restrictedSpans.AddIntervalInPlace(TextSpan.FromBounds(start, descendantSpan.End)); | ||
restrictedSpans.Add(TextSpan.FromBounds(start, descendantSpan.End)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return (interpolationInteriorSpans, restrictedSpans); | ||
return (new TextSpanIntervalTree(interpolationInteriorSpans), new TextSpanIntervalTree(restrictedSpans)); | ||
} | ||
|
||
private static InterpolatedStringExpressionSyntax CleanInterpolatedString( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
using System.Threading.Tasks; | ||
using System.Windows; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Collections; | ||
using Microsoft.CodeAnalysis.Editor.Shared.Extensions; | ||
using Microsoft.CodeAnalysis.Editor.Shared.Tagging; | ||
using Microsoft.CodeAnalysis.Editor.Shared.Utilities; | ||
|
@@ -19,6 +20,7 @@ | |
using Microsoft.CodeAnalysis.Shared.Collections; | ||
using Microsoft.CodeAnalysis.Shared.TestHooks; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Microsoft.CodeAnalysis.Utilities; | ||
using Microsoft.VisualStudio.LanguageServer.Client; | ||
using Microsoft.VisualStudio.LanguageServices.Utilities; | ||
using Microsoft.VisualStudio.Text; | ||
|
@@ -318,10 +320,11 @@ private async ValueTask ComputeViewStateAsync(CancellationToken cancellationToke | |
newItems: newViewModelItems); | ||
} | ||
|
||
// Now create an interval tree out of the view models. This will allow us to easily find the intersecting | ||
// view models given any position in the file with any particular text snapshot. | ||
var intervalTree = SimpleIntervalTree.Create(new IntervalIntrospector(), Array.Empty<DocumentSymbolDataViewModel>()); | ||
AddToIntervalTree(newViewModelItems); | ||
// Now create an interval tree out of the view models. This will allow us to easily find the intersecting view | ||
// models given any position in the file with any particular text snapshot. | ||
using var _ = SegmentedListPool.GetPooledList<DocumentSymbolDataViewModel>(out var models); | ||
AddAllModels(newViewModelItems, models); | ||
var intervalTree = BinaryIntervalTree.Create(new IntervalIntrospector(), models); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tree construction at end. this will move to the FlatArrayIntervalTree in next pr. |
||
|
||
var newViewState = new DocumentOutlineViewState( | ||
newTextSnapshot, | ||
|
@@ -348,12 +351,12 @@ private async ValueTask ComputeViewStateAsync(CancellationToken cancellationToke | |
|
||
return; | ||
|
||
void AddToIntervalTree(ImmutableArray<DocumentSymbolDataViewModel> viewModels) | ||
static void AddAllModels(ImmutableArray<DocumentSymbolDataViewModel> viewModels, SegmentedList<DocumentSymbolDataViewModel> result) | ||
{ | ||
foreach (var model in viewModels) | ||
{ | ||
intervalTree.AddIntervalInPlace(model); | ||
AddToIntervalTree(model.Children); | ||
result.Equals(model); | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
AddAllModels(model.Children, result); | ||
} | ||
} | ||
|
||
|
@@ -462,7 +465,7 @@ private async ValueTask UpdateSelectionAsync(CancellationToken cancellationToken | |
{ | ||
// Treat the caret as if it has length 1. That way if it is in between two items, it will naturally | ||
// only intersect right the item on the right of it. | ||
var overlappingModels = modelTree.GetIntervalsThatOverlapWith( | ||
var overlappingModels = modelTree.Algorithms.GetIntervalsThatOverlapWith( | ||
caretPosition.Position, 1, new IntervalIntrospector()); | ||
|
||
if (overlappingModels.Length == 0) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,7 +43,11 @@ internal sealed partial class InheritanceGlyphManager : IDisposable | |
private readonly IEditorFormatMap _editorFormatMap; | ||
private readonly IAsynchronousOperationListener _listener; | ||
private readonly Canvas _glyphsContainer; | ||
private readonly SimpleIntervalTree<GlyphData, GlyphDataIntrospector> _glyphDataTree; | ||
|
||
/// <summary> | ||
/// Mutable. Must only be accessed from the UI thread. | ||
/// </summary> | ||
private SimpleBinaryIntervalTree<GlyphData, GlyphDataIntrospector> _glyphDataTree; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tree construction at end. this will move to the FlatArrayIntervalTree in next pr. |
||
|
||
public InheritanceGlyphManager( | ||
Workspace workspace, | ||
|
@@ -71,7 +75,7 @@ public InheritanceGlyphManager( | |
_heightAndWidthOfTheGlyph = heightAndWidthOfTheGlyph; | ||
_editorFormatMap.FormatMappingChanged += FormatMappingChanged; | ||
|
||
_glyphDataTree = new SimpleIntervalTree<GlyphData, GlyphDataIntrospector>(new GlyphDataIntrospector(), values: null); | ||
_glyphDataTree = new(new GlyphDataIntrospector(), values: null); | ||
UpdateBackgroundColor(); | ||
} | ||
|
||
|
@@ -111,7 +115,7 @@ public void RemoveGlyphs(SnapshotSpan snapshotSpan) | |
} | ||
|
||
var remainingGlyphData = _glyphDataTree.Except(glyphDataToRemove).ToImmutableArray(); | ||
_glyphDataTree.ClearInPlace(); | ||
_glyphDataTree = new(new GlyphDataIntrospector(), values: null); | ||
foreach (var glyphData in remainingGlyphData) | ||
{ | ||
_glyphDataTree.AddIntervalInPlace(glyphData); | ||
|
@@ -130,7 +134,7 @@ public void SetSnapshotAndUpdate(ITextSnapshot snapshot, IList<ITextViewLine> ne | |
{ | ||
// Go through all the existing visuals and invalidate or transform as appropriate. | ||
var allGlyphData = _glyphDataTree.ToImmutableArray(); | ||
_glyphDataTree.ClearInPlace(); | ||
_glyphDataTree = new(new GlyphDataIntrospector(), values: null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
foreach (var (span, glyph) in allGlyphData) | ||
{ | ||
var newSpan = span.TranslateTo(snapshot, SpanTrackingMode.EdgeInclusive); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
using Microsoft.CodeAnalysis.Shared.Collections; | ||
using Microsoft.CodeAnalysis.Shared.Extensions; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Microsoft.CodeAnalysis.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.Classification; | ||
|
||
|
@@ -238,18 +239,20 @@ public static void MergeParts<TClassifiedSpan, TClassifiedSpanIntervalIntrospect | |
{ | ||
// Create an interval tree so we can easily determine which semantic parts intersect with the | ||
// syntactic parts we're looking at. | ||
var semanticPartsTree = new SimpleIntervalTree<TClassifiedSpan, TClassifiedSpanIntervalIntrospector>(default, values: null); | ||
using var _1 = SegmentedListPool.GetPooledList<TClassifiedSpan>(out var semanticSpans); | ||
|
||
// Add all the non-empty semantic parts to the tree. | ||
foreach (var part in semanticParts) | ||
{ | ||
if (!getSpan(part).IsEmpty) | ||
{ | ||
semanticPartsTree.AddIntervalInPlace(part); | ||
semanticSpans.Add(part); | ||
finalParts.Add(part); | ||
} | ||
} | ||
|
||
var semanticPartsTree = new SimpleBinaryIntervalTree<TClassifiedSpan, TClassifiedSpanIntervalIntrospector>(default, values: semanticSpans); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tree construction at end. this will move to the FlatArrayIntervalTree in next pr. |
||
|
||
using var tempBuffer = TemporaryArray<TClassifiedSpan>.Empty; | ||
|
||
foreach (var syntacticPart in syntaxParts) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
namespace Microsoft.CodeAnalysis.Shared.Collections; | ||
|
||
/// <summary> | ||
/// Generic function representing the type of interval testing operation that can be performed on an interval tree. For | ||
/// example checking if an interval 'contains', 'intersects', or 'overlaps' with a requested span. | ||
/// </summary> | ||
internal delegate bool TestInterval<T, TIntrospector>(T value, int start, int length, in TIntrospector introspector) | ||
where TIntrospector : struct, IIntervalIntrospector<T>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new core types taht power the BinaryIntervalTree, and will power the upcoming FlatArrayIntervalTree. |
||
|
||
/// <summary> | ||
/// Base interface all interval trees need to implement to get full functionality. Callers are not expected to use | ||
/// these methods directly. Instead, they are the low level building blocks that the higher level extension methods are | ||
/// built upon. Consumers of an interface tree should use <c>.Algorithms</c> on the instance to get access to a wealth | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// of fast operations through the <see cref="IntervalTreeAlgorithms{T, TIntervalTree}"/> type. | ||
/// </summary> | ||
internal interface IIntervalTree<T> | ||
{ | ||
/// <summary> | ||
/// Adds all intervals within the tree within the given start/length pair that match the given <paramref | ||
/// name="testInterval"/> predicate. Results are added to the <paramref name="builder"/> array. The <paramref | ||
/// name="stopAfterFirst"/> indicates if the search should stop after the first interval is found. Results will be | ||
/// returned in a sorted order based on the start point of the interval. | ||
/// </summary> | ||
/// <returns>The number of matching intervals found by the method.</returns> | ||
int FillWithIntervalsThatMatch<TIntrospector>( | ||
int start, int length, TestInterval<T, TIntrospector> testInterval, | ||
ref TemporaryArray<T> builder, in TIntrospector introspector, | ||
bool stopAfterFirst) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. keeping the same logic for now. will change after flag goes in. |
||
where TIntrospector : struct, IIntervalIntrospector<T>; | ||
|
||
/// <summary> | ||
/// Practically equivalent to <see cref="FillWithIntervalsThatMatch{TIntrospector}"/> with a check that at least one | ||
/// item was found. However, separated out as a separate method as implementations can often be more efficient just | ||
/// answering this question, versus the more complex "full with intervals" question above. | ||
CyrusNajmabadi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// </summary> | ||
bool Any<TIntrospector>(int start, int length, TestInterval<T, TIntrospector> testInterval, in TIntrospector introspector) | ||
where TIntrospector : struct, IIntervalIntrospector<T>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renaming to add 'Binary' for clarity here. The upcoming interval tree will probably be called FlatArrayIntervalTree or something like that.