|
// 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.
using System;
using System.Linq;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Projection;
namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions;
internal enum BufferMapDirection
{
Identity,
Down,
Up,
Unrelated
}
internal static class IBufferGraphExtensions
{
public static SnapshotSpan? MapUpOrDownToFirstMatch(this IBufferGraph bufferGraph, SnapshotSpan span, Predicate<ITextSnapshot> match)
{
var spans = bufferGraph.MapDownToFirstMatch(span, SpanTrackingMode.EdgeExclusive, match);
if (spans.Count == 0)
{
spans = bufferGraph.MapUpToFirstMatch(span, SpanTrackingMode.EdgeExclusive, match);
}
return spans.Count > 0 ? spans[0] : null;
}
public static SnapshotSpan? MapUpOrDownToBuffer(this IBufferGraph bufferGraph, SnapshotSpan span, ITextBuffer targetBuffer)
{
var direction = ClassifyBufferMapDirection(span.Snapshot.TextBuffer, targetBuffer);
switch (direction)
{
case BufferMapDirection.Identity:
return span;
case BufferMapDirection.Down:
{
var spans = bufferGraph.MapDownToBuffer(span, SpanTrackingMode.EdgeExclusive, targetBuffer);
return spans.Count > 0 ? spans[0] : null;
}
case BufferMapDirection.Up:
{
var spans = bufferGraph.MapUpToBuffer(span, SpanTrackingMode.EdgeExclusive, targetBuffer);
return spans.Count > 0 ? spans[0] : null;
}
default:
return null;
}
}
public static SnapshotPoint? MapUpOrDownToBuffer(this IBufferGraph bufferGraph, SnapshotPoint point, ITextBuffer targetBuffer)
{
var direction = ClassifyBufferMapDirection(point.Snapshot.TextBuffer, targetBuffer);
switch (direction)
{
case BufferMapDirection.Identity:
return point;
case BufferMapDirection.Down:
{
// TODO (https://github.com/dotnet/roslyn/issues/5281): Remove try-catch.
try
{
return bufferGraph.MapDownToInsertionPoint(point, PointTrackingMode.Positive, s => s == targetBuffer.CurrentSnapshot);
}
catch (ArgumentOutOfRangeException) when (bufferGraph.TopBuffer.ContentType.TypeName == "Interactive Content")
{
// Suppress this to work around DevDiv #144964.
// Note: Other callers might be affected, but this is the narrowest workaround for the observed problems.
// A fix is already being reviewed, so a broader change is not required.
return null;
}
}
case BufferMapDirection.Up:
{
return bufferGraph.MapUpToBuffer(point, PointTrackingMode.Positive, PositionAffinity.Predecessor, targetBuffer);
}
default:
return null;
}
}
public static BufferMapDirection ClassifyBufferMapDirection(ITextBuffer startBuffer, ITextBuffer destinationBuffer)
{
if (startBuffer == destinationBuffer)
{
return BufferMapDirection.Identity;
}
// Are we trying to map down or up?
if (startBuffer is IProjectionBufferBase startProjBuffer && IsSourceBuffer(startProjBuffer, destinationBuffer))
{
return BufferMapDirection.Down;
}
if (destinationBuffer is IProjectionBufferBase destProjBuffer && IsSourceBuffer(destProjBuffer, startBuffer))
{
return BufferMapDirection.Up;
}
return BufferMapDirection.Unrelated;
}
private static bool IsSourceBuffer(IProjectionBufferBase top, ITextBuffer bottom)
{
return top.SourceBuffers.Contains(bottom) ||
top.SourceBuffers.OfType<IProjectionBufferBase>().Any(b => IsSourceBuffer(b, bottom));
}
}
|