This repository has been archived by the owner on Sep 3, 2024. It is now read-only.
forked from mhutch/MonoDevelop.Xml
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import Roslyn support code for Highlight References
- Loading branch information
Showing
24 changed files
with
3,886 additions
and
0 deletions.
There are no files selected for viewing
141 changes: 141 additions & 0 deletions
141
Editor/Commands/NavigateToHighlightReferenceCommandHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
//ROSLYN IMPORT: Microsoft.CodeAnalysis.Editor.ReferenceHighlighting.NavigateToHighlightReferenceCommandHandler | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel.Composition; | ||
using System.Linq; | ||
using Microsoft.CodeAnalysis.Editor.Shared.Extensions; | ||
using Microsoft.CodeAnalysis.Text.Shared.Extensions; | ||
using Microsoft.VisualStudio.Commanding; | ||
using Microsoft.VisualStudio.Text; | ||
using Microsoft.VisualStudio.Text.Editor; | ||
using Microsoft.VisualStudio.Text.Editor.Commanding; | ||
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; | ||
using Microsoft.VisualStudio.Text.Outlining; | ||
using Microsoft.VisualStudio.Text.Tagging; | ||
using Microsoft.VisualStudio.Utilities; | ||
using MonoDevelop.Xml.Editor.Tags; | ||
using VSCommanding = Microsoft.VisualStudio.Commanding; | ||
|
||
namespace MonoDevelop.Xml.Editor.Commands | ||
{ | ||
[Export (typeof (VSCommanding.ICommandHandler))] | ||
[ContentType (XmlContentTypeNames.XmlCore)] | ||
[Name ("Xml Navigate to Highlighted Reference Command Handler")] | ||
internal partial class NavigateToHighlightReferenceCommandHandler : | ||
VSCommanding.ICommandHandler<NavigateToNextHighlightedReferenceCommandArgs>, | ||
VSCommanding.ICommandHandler<NavigateToPreviousHighlightedReferenceCommandArgs> | ||
{ | ||
private readonly IOutliningManagerService _outliningManagerService; | ||
private readonly IViewTagAggregatorFactoryService _tagAggregatorFactory; | ||
|
||
public string DisplayName => "Navigate To Highlighted Reference"; | ||
|
||
[ImportingConstructor] | ||
public NavigateToHighlightReferenceCommandHandler ( | ||
IOutliningManagerService outliningManagerService, | ||
IViewTagAggregatorFactoryService tagAggregatorFactory) | ||
{ | ||
_outliningManagerService = outliningManagerService ?? throw new ArgumentNullException (nameof (outliningManagerService)); | ||
_tagAggregatorFactory = tagAggregatorFactory ?? throw new ArgumentNullException (nameof (tagAggregatorFactory)); | ||
} | ||
|
||
public CommandState GetCommandState (NavigateToNextHighlightedReferenceCommandArgs args) | ||
{ | ||
return GetCommandStateImpl (args); | ||
} | ||
|
||
public CommandState GetCommandState (NavigateToPreviousHighlightedReferenceCommandArgs args) | ||
{ | ||
return GetCommandStateImpl (args); | ||
} | ||
|
||
private CommandState GetCommandStateImpl (EditorCommandArgs args) | ||
{ | ||
using (var tagAggregator = _tagAggregatorFactory.CreateTagAggregator<NavigableHighlightTag> (args.TextView)) { | ||
var tagUnderCursor = FindTagUnderCaret (tagAggregator, args.TextView); | ||
return tagUnderCursor == null ? CommandState.Unavailable : CommandState.Available; | ||
} | ||
} | ||
|
||
public bool ExecuteCommand (NavigateToNextHighlightedReferenceCommandArgs args, CommandExecutionContext context) | ||
{ | ||
return ExecuteCommandImpl (args, navigateToNext: true, context); | ||
} | ||
|
||
public bool ExecuteCommand (NavigateToPreviousHighlightedReferenceCommandArgs args, CommandExecutionContext context) | ||
{ | ||
return ExecuteCommandImpl (args, navigateToNext: false, context); | ||
} | ||
|
||
private bool ExecuteCommandImpl (EditorCommandArgs args, bool navigateToNext, CommandExecutionContext context) | ||
{ | ||
using (var tagAggregator = _tagAggregatorFactory.CreateTagAggregator<NavigableHighlightTag> (args.TextView)) { | ||
var tagUnderCursor = FindTagUnderCaret (tagAggregator, args.TextView); | ||
|
||
if (tagUnderCursor == null) { | ||
return false; | ||
} | ||
|
||
var spans = GetTags (tagAggregator, args.TextView.TextSnapshot.GetFullSpan()).ToList (); | ||
|
||
var destTag = GetDestinationTag (tagUnderCursor.Value, spans, navigateToNext); | ||
|
||
if (args.TextView.TryMoveCaretToAndEnsureVisible (destTag.Start, _outliningManagerService)) { | ||
args.TextView.SetSelection (destTag); | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private static IEnumerable<SnapshotSpan> GetTags ( | ||
ITagAggregator<NavigableHighlightTag> tagAggregator, | ||
SnapshotSpan span) | ||
{ | ||
return tagAggregator.GetTags (span) | ||
.SelectMany (tag => tag.Span.GetSpans (span.Snapshot.TextBuffer)) | ||
.OrderBy (tag => tag.Start); | ||
} | ||
|
||
private static SnapshotSpan GetDestinationTag ( | ||
SnapshotSpan tagUnderCursor, | ||
List<SnapshotSpan> orderedTagSpans, | ||
bool navigateToNext) | ||
{ | ||
var destIndex = orderedTagSpans.BinarySearch (tagUnderCursor, new StartComparer ()); | ||
|
||
destIndex += navigateToNext ? 1 : -1; | ||
if (destIndex < 0) { | ||
destIndex = orderedTagSpans.Count - 1; | ||
} else if (destIndex == orderedTagSpans.Count) { | ||
destIndex = 0; | ||
} | ||
|
||
return orderedTagSpans[destIndex]; | ||
} | ||
|
||
private SnapshotSpan? FindTagUnderCaret ( | ||
ITagAggregator<NavigableHighlightTag> tagAggregator, | ||
ITextView textView) | ||
{ | ||
// We always want to be working with the surface buffer here, so this line is correct | ||
var caretPosition = textView.Caret.Position.BufferPosition.Position; | ||
|
||
var tags = GetTags (tagAggregator, new SnapshotSpan (textView.TextSnapshot, new Span (caretPosition, 0))); | ||
return tags.Any () | ||
? tags.First () | ||
: (SnapshotSpan?)null; | ||
} | ||
|
||
private class StartComparer : IComparer<SnapshotSpan> | ||
{ | ||
public int Compare (SnapshotSpan x, SnapshotSpan y) | ||
{ | ||
return x.Start.CompareTo (y.Start); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license 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.Any ()) { | ||
spans = bufferGraph.MapUpToFirstMatch (span, SpanTrackingMode.EdgeExclusive, match); | ||
} | ||
|
||
return spans.Select (s => (SnapshotSpan?)s).FirstOrDefault (); | ||
} | ||
|
||
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.Select (s => (SnapshotSpan?)s).FirstOrDefault (); | ||
} | ||
|
||
case BufferMapDirection.Up: { | ||
var spans = bufferGraph.MapUpToBuffer (span, SpanTrackingMode.EdgeExclusive, targetBuffer); | ||
return spans.Select (s => (SnapshotSpan?)s).FirstOrDefault (); | ||
} | ||
|
||
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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using Microsoft.VisualStudio.Text; | ||
using Microsoft.VisualStudio.Text.Tagging; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.Text.Shared.Extensions | ||
{ | ||
internal static partial class ITextSnapshotExtensions | ||
{ | ||
public static SnapshotPoint GetPoint (this ITextSnapshot snapshot, int position) | ||
=> new SnapshotPoint (snapshot, position); | ||
|
||
public static SnapshotPoint? TryGetPoint (this ITextSnapshot snapshot, int lineNumber, int columnIndex) | ||
{ | ||
var position = snapshot.TryGetPosition (lineNumber, columnIndex); | ||
if (position.HasValue) { | ||
return new SnapshotPoint (snapshot, position.Value); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
/* | ||
/// <summary> | ||
/// Convert a <see cref="LinePositionSpan"/> to <see cref="TextSpan"/>. | ||
/// </summary> | ||
public static TextSpan GetTextSpan (this ITextSnapshot snapshot, LinePositionSpan span) | ||
{ | ||
return TextSpan.FromBounds ( | ||
GetPosition (snapshot, span.Start.Line, span.Start.Character), | ||
GetPosition (snapshot, span.End.Line, span.End.Character)); | ||
} | ||
*/ | ||
|
||
public static int GetPosition (this ITextSnapshot snapshot, int lineNumber, int columnIndex) | ||
=> TryGetPosition (snapshot, lineNumber, columnIndex).Value; | ||
|
||
public static int? TryGetPosition (this ITextSnapshot snapshot, int lineNumber, int columnIndex) | ||
{ | ||
if (lineNumber < 0 || lineNumber >= snapshot.LineCount) { | ||
return null; | ||
} | ||
|
||
var end = snapshot.GetLineFromLineNumber (lineNumber).Start.Position + columnIndex; | ||
if (end < 0 || end > snapshot.Length) { | ||
return null; | ||
} | ||
|
||
return end; | ||
} | ||
|
||
public static bool TryGetPosition (this ITextSnapshot snapshot, int lineNumber, int columnIndex, out SnapshotPoint position) | ||
{ | ||
var result = 0; | ||
position = new SnapshotPoint (); | ||
|
||
if (lineNumber < 0 || lineNumber >= snapshot.LineCount) { | ||
return false; | ||
} | ||
|
||
var line = snapshot.GetLineFromLineNumber (lineNumber); | ||
if (columnIndex < 0 || columnIndex >= line.Length) { | ||
return false; | ||
} | ||
|
||
result = line.Start.Position + columnIndex; | ||
position = new SnapshotPoint (snapshot, result); | ||
return true; | ||
} | ||
|
||
public static SnapshotSpan GetSpan (this ITextSnapshot snapshot, int start, int length) | ||
=> new SnapshotSpan (snapshot, new Span (start, length)); | ||
|
||
public static SnapshotSpan GetSpanFromBounds (this ITextSnapshot snapshot, int start, int end) | ||
=> new SnapshotSpan (snapshot, Span.FromBounds (start, end)); | ||
|
||
public static SnapshotSpan GetSpan (this ITextSnapshot snapshot, Span span) | ||
=> new SnapshotSpan (snapshot, span); | ||
|
||
public static ITagSpan<TTag> GetTagSpan<TTag> (this ITextSnapshot snapshot, Span span, TTag tag) | ||
where TTag : ITag | ||
{ | ||
return new TagSpan<TTag> (new SnapshotSpan (snapshot, span), tag); | ||
} | ||
|
||
public static SnapshotSpan GetSpan (this ITextSnapshot snapshot, int startLine, int startIndex, int endLine, int endIndex) | ||
{ | ||
return TryGetSpan (snapshot, startLine, startIndex, endLine, endIndex).Value; | ||
} | ||
|
||
public static SnapshotSpan? TryGetSpan (this ITextSnapshot snapshot, int startLine, int startIndex, int endLine, int endIndex) | ||
{ | ||
var startPosition = snapshot.TryGetPosition (startLine, startIndex); | ||
var endPosition = snapshot.TryGetPosition (endLine, endIndex); | ||
if (startPosition == null || endPosition == null) { | ||
return null; | ||
} | ||
|
||
return new SnapshotSpan (snapshot, Span.FromBounds (startPosition.Value, endPosition.Value)); | ||
} | ||
|
||
public static SnapshotSpan GetFullSpan (this ITextSnapshot snapshot) | ||
{ | ||
Contract.ThrowIfNull (snapshot); | ||
|
||
return new SnapshotSpan (snapshot, new Span (0, snapshot.Length)); | ||
} | ||
|
||
public static NormalizedSnapshotSpanCollection GetSnapshotSpanCollection (this ITextSnapshot snapshot) | ||
{ | ||
Contract.ThrowIfNull (snapshot); | ||
|
||
return new NormalizedSnapshotSpanCollection (snapshot.GetFullSpan ()); | ||
} | ||
|
||
public static void GetLineAndCharacter (this ITextSnapshot snapshot, int position, out int lineNumber, out int characterIndex) | ||
{ | ||
var line = snapshot.GetLineFromPosition (position); | ||
|
||
lineNumber = line.LineNumber; | ||
characterIndex = position - line.Start.Position; | ||
} | ||
|
||
/// <summary> | ||
/// Returns the leading whitespace of the line located at the specified position in the given snapshot. | ||
/// </summary> | ||
public static string GetLeadingWhitespaceOfLineAtPosition (this ITextSnapshot snapshot, int position) | ||
{ | ||
Contract.ThrowIfNull (snapshot); | ||
|
||
var line = snapshot.GetLineFromPosition (position); | ||
var linePosition = line.GetFirstNonWhitespacePosition (); | ||
if (!linePosition.HasValue) { | ||
return line.GetText (); | ||
} | ||
|
||
var lineText = line.GetText (); | ||
return lineText.Substring (0, linePosition.Value - line.Start); | ||
} | ||
|
||
public static bool AreOnSameLine (this ITextSnapshot snapshot, int x1, int x2) | ||
=> snapshot.GetLineNumberFromPosition (x1) == snapshot.GetLineNumberFromPosition (x2); | ||
} | ||
} |
Oops, something went wrong.