Skip to content
This repository was archived by the owner on Sep 3, 2024. It is now read-only.

Commit bdc1c27

Browse files
committed
Import Roslyn support code for Highlight References
1 parent 42b4783 commit bdc1c27

24 files changed

+3886
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
//ROSLYN IMPORT: Microsoft.CodeAnalysis.Editor.ReferenceHighlighting.NavigateToHighlightReferenceCommandHandler
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.ComponentModel.Composition;
8+
using System.Linq;
9+
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
10+
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
11+
using Microsoft.VisualStudio.Commanding;
12+
using Microsoft.VisualStudio.Text;
13+
using Microsoft.VisualStudio.Text.Editor;
14+
using Microsoft.VisualStudio.Text.Editor.Commanding;
15+
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
16+
using Microsoft.VisualStudio.Text.Outlining;
17+
using Microsoft.VisualStudio.Text.Tagging;
18+
using Microsoft.VisualStudio.Utilities;
19+
using MonoDevelop.Xml.Editor.Tags;
20+
using VSCommanding = Microsoft.VisualStudio.Commanding;
21+
22+
namespace MonoDevelop.Xml.Editor.Commands
23+
{
24+
[Export (typeof (VSCommanding.ICommandHandler))]
25+
[ContentType (XmlContentTypeNames.XmlCore)]
26+
[Name ("Xml Navigate to Highlighted Reference Command Handler")]
27+
internal partial class NavigateToHighlightReferenceCommandHandler :
28+
VSCommanding.ICommandHandler<NavigateToNextHighlightedReferenceCommandArgs>,
29+
VSCommanding.ICommandHandler<NavigateToPreviousHighlightedReferenceCommandArgs>
30+
{
31+
private readonly IOutliningManagerService _outliningManagerService;
32+
private readonly IViewTagAggregatorFactoryService _tagAggregatorFactory;
33+
34+
public string DisplayName => "Navigate To Highlighted Reference";
35+
36+
[ImportingConstructor]
37+
public NavigateToHighlightReferenceCommandHandler (
38+
IOutliningManagerService outliningManagerService,
39+
IViewTagAggregatorFactoryService tagAggregatorFactory)
40+
{
41+
_outliningManagerService = outliningManagerService ?? throw new ArgumentNullException (nameof (outliningManagerService));
42+
_tagAggregatorFactory = tagAggregatorFactory ?? throw new ArgumentNullException (nameof (tagAggregatorFactory));
43+
}
44+
45+
public CommandState GetCommandState (NavigateToNextHighlightedReferenceCommandArgs args)
46+
{
47+
return GetCommandStateImpl (args);
48+
}
49+
50+
public CommandState GetCommandState (NavigateToPreviousHighlightedReferenceCommandArgs args)
51+
{
52+
return GetCommandStateImpl (args);
53+
}
54+
55+
private CommandState GetCommandStateImpl (EditorCommandArgs args)
56+
{
57+
using (var tagAggregator = _tagAggregatorFactory.CreateTagAggregator<NavigableHighlightTag> (args.TextView)) {
58+
var tagUnderCursor = FindTagUnderCaret (tagAggregator, args.TextView);
59+
return tagUnderCursor == null ? CommandState.Unavailable : CommandState.Available;
60+
}
61+
}
62+
63+
public bool ExecuteCommand (NavigateToNextHighlightedReferenceCommandArgs args, CommandExecutionContext context)
64+
{
65+
return ExecuteCommandImpl (args, navigateToNext: true, context);
66+
}
67+
68+
public bool ExecuteCommand (NavigateToPreviousHighlightedReferenceCommandArgs args, CommandExecutionContext context)
69+
{
70+
return ExecuteCommandImpl (args, navigateToNext: false, context);
71+
}
72+
73+
private bool ExecuteCommandImpl (EditorCommandArgs args, bool navigateToNext, CommandExecutionContext context)
74+
{
75+
using (var tagAggregator = _tagAggregatorFactory.CreateTagAggregator<NavigableHighlightTag> (args.TextView)) {
76+
var tagUnderCursor = FindTagUnderCaret (tagAggregator, args.TextView);
77+
78+
if (tagUnderCursor == null) {
79+
return false;
80+
}
81+
82+
var spans = GetTags (tagAggregator, args.TextView.TextSnapshot.GetFullSpan()).ToList ();
83+
84+
var destTag = GetDestinationTag (tagUnderCursor.Value, spans, navigateToNext);
85+
86+
if (args.TextView.TryMoveCaretToAndEnsureVisible (destTag.Start, _outliningManagerService)) {
87+
args.TextView.SetSelection (destTag);
88+
}
89+
}
90+
91+
return true;
92+
}
93+
94+
private static IEnumerable<SnapshotSpan> GetTags (
95+
ITagAggregator<NavigableHighlightTag> tagAggregator,
96+
SnapshotSpan span)
97+
{
98+
return tagAggregator.GetTags (span)
99+
.SelectMany (tag => tag.Span.GetSpans (span.Snapshot.TextBuffer))
100+
.OrderBy (tag => tag.Start);
101+
}
102+
103+
private static SnapshotSpan GetDestinationTag (
104+
SnapshotSpan tagUnderCursor,
105+
List<SnapshotSpan> orderedTagSpans,
106+
bool navigateToNext)
107+
{
108+
var destIndex = orderedTagSpans.BinarySearch (tagUnderCursor, new StartComparer ());
109+
110+
destIndex += navigateToNext ? 1 : -1;
111+
if (destIndex < 0) {
112+
destIndex = orderedTagSpans.Count - 1;
113+
} else if (destIndex == orderedTagSpans.Count) {
114+
destIndex = 0;
115+
}
116+
117+
return orderedTagSpans[destIndex];
118+
}
119+
120+
private SnapshotSpan? FindTagUnderCaret (
121+
ITagAggregator<NavigableHighlightTag> tagAggregator,
122+
ITextView textView)
123+
{
124+
// We always want to be working with the surface buffer here, so this line is correct
125+
var caretPosition = textView.Caret.Position.BufferPosition.Position;
126+
127+
var tags = GetTags (tagAggregator, new SnapshotSpan (textView.TextSnapshot, new Span (caretPosition, 0)));
128+
return tags.Any ()
129+
? tags.First ()
130+
: (SnapshotSpan?)null;
131+
}
132+
133+
private class StartComparer : IComparer<SnapshotSpan>
134+
{
135+
public int Compare (SnapshotSpan x, SnapshotSpan y)
136+
{
137+
return x.Start.CompareTo (y.Start);
138+
}
139+
}
140+
}
141+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System;
4+
using System.Linq;
5+
using Microsoft.VisualStudio.Text;
6+
using Microsoft.VisualStudio.Text.Projection;
7+
8+
namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions
9+
{
10+
internal enum BufferMapDirection
11+
{
12+
Identity,
13+
Down,
14+
Up,
15+
Unrelated
16+
}
17+
18+
internal static class IBufferGraphExtensions
19+
{
20+
public static SnapshotSpan? MapUpOrDownToFirstMatch (this IBufferGraph bufferGraph, SnapshotSpan span, Predicate<ITextSnapshot> match)
21+
{
22+
var spans = bufferGraph.MapDownToFirstMatch (span, SpanTrackingMode.EdgeExclusive, match);
23+
if (!spans.Any ()) {
24+
spans = bufferGraph.MapUpToFirstMatch (span, SpanTrackingMode.EdgeExclusive, match);
25+
}
26+
27+
return spans.Select (s => (SnapshotSpan?)s).FirstOrDefault ();
28+
}
29+
30+
public static SnapshotSpan? MapUpOrDownToBuffer (this IBufferGraph bufferGraph, SnapshotSpan span, ITextBuffer targetBuffer)
31+
{
32+
var direction = ClassifyBufferMapDirection (span.Snapshot.TextBuffer, targetBuffer);
33+
switch (direction) {
34+
case BufferMapDirection.Identity:
35+
return span;
36+
37+
case BufferMapDirection.Down: {
38+
var spans = bufferGraph.MapDownToBuffer (span, SpanTrackingMode.EdgeExclusive, targetBuffer);
39+
return spans.Select (s => (SnapshotSpan?)s).FirstOrDefault ();
40+
}
41+
42+
case BufferMapDirection.Up: {
43+
var spans = bufferGraph.MapUpToBuffer (span, SpanTrackingMode.EdgeExclusive, targetBuffer);
44+
return spans.Select (s => (SnapshotSpan?)s).FirstOrDefault ();
45+
}
46+
47+
default:
48+
return null;
49+
}
50+
}
51+
52+
public static SnapshotPoint? MapUpOrDownToBuffer (this IBufferGraph bufferGraph, SnapshotPoint point, ITextBuffer targetBuffer)
53+
{
54+
var direction = ClassifyBufferMapDirection (point.Snapshot.TextBuffer, targetBuffer);
55+
switch (direction) {
56+
case BufferMapDirection.Identity:
57+
return point;
58+
59+
case BufferMapDirection.Down: {
60+
// TODO (https://github.com/dotnet/roslyn/issues/5281): Remove try-catch.
61+
try {
62+
return bufferGraph.MapDownToInsertionPoint (point, PointTrackingMode.Positive, s => s == targetBuffer.CurrentSnapshot);
63+
} catch (ArgumentOutOfRangeException) when (bufferGraph.TopBuffer.ContentType.TypeName == "Interactive Content") {
64+
// Suppress this to work around DevDiv #144964.
65+
// Note: Other callers might be affected, but this is the narrowest workaround for the observed problems.
66+
// A fix is already being reviewed, so a broader change is not required.
67+
return null;
68+
}
69+
}
70+
71+
case BufferMapDirection.Up: {
72+
return bufferGraph.MapUpToBuffer (point, PointTrackingMode.Positive, PositionAffinity.Predecessor, targetBuffer);
73+
}
74+
75+
default:
76+
return null;
77+
}
78+
}
79+
80+
public static BufferMapDirection ClassifyBufferMapDirection (ITextBuffer startBuffer, ITextBuffer destinationBuffer)
81+
{
82+
if (startBuffer == destinationBuffer) {
83+
return BufferMapDirection.Identity;
84+
}
85+
86+
// Are we trying to map down or up?
87+
if (startBuffer is IProjectionBufferBase startProjBuffer && IsSourceBuffer (startProjBuffer, destinationBuffer)) {
88+
return BufferMapDirection.Down;
89+
}
90+
91+
if (destinationBuffer is IProjectionBufferBase destProjBuffer && IsSourceBuffer (destProjBuffer, startBuffer)) {
92+
return BufferMapDirection.Up;
93+
}
94+
95+
return BufferMapDirection.Unrelated;
96+
}
97+
98+
private static bool IsSourceBuffer (IProjectionBufferBase top, ITextBuffer bottom)
99+
{
100+
return top.SourceBuffers.Contains (bottom) ||
101+
top.SourceBuffers.OfType<IProjectionBufferBase> ().Any (b => IsSourceBuffer (b, bottom));
102+
}
103+
}
104+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using Microsoft.VisualStudio.Text;
4+
using Microsoft.VisualStudio.Text.Tagging;
5+
using Roslyn.Utilities;
6+
7+
namespace Microsoft.CodeAnalysis.Text.Shared.Extensions
8+
{
9+
internal static partial class ITextSnapshotExtensions
10+
{
11+
public static SnapshotPoint GetPoint (this ITextSnapshot snapshot, int position)
12+
=> new SnapshotPoint (snapshot, position);
13+
14+
public static SnapshotPoint? TryGetPoint (this ITextSnapshot snapshot, int lineNumber, int columnIndex)
15+
{
16+
var position = snapshot.TryGetPosition (lineNumber, columnIndex);
17+
if (position.HasValue) {
18+
return new SnapshotPoint (snapshot, position.Value);
19+
} else {
20+
return null;
21+
}
22+
}
23+
24+
/*
25+
/// <summary>
26+
/// Convert a <see cref="LinePositionSpan"/> to <see cref="TextSpan"/>.
27+
/// </summary>
28+
public static TextSpan GetTextSpan (this ITextSnapshot snapshot, LinePositionSpan span)
29+
{
30+
return TextSpan.FromBounds (
31+
GetPosition (snapshot, span.Start.Line, span.Start.Character),
32+
GetPosition (snapshot, span.End.Line, span.End.Character));
33+
}
34+
*/
35+
36+
public static int GetPosition (this ITextSnapshot snapshot, int lineNumber, int columnIndex)
37+
=> TryGetPosition (snapshot, lineNumber, columnIndex).Value;
38+
39+
public static int? TryGetPosition (this ITextSnapshot snapshot, int lineNumber, int columnIndex)
40+
{
41+
if (lineNumber < 0 || lineNumber >= snapshot.LineCount) {
42+
return null;
43+
}
44+
45+
var end = snapshot.GetLineFromLineNumber (lineNumber).Start.Position + columnIndex;
46+
if (end < 0 || end > snapshot.Length) {
47+
return null;
48+
}
49+
50+
return end;
51+
}
52+
53+
public static bool TryGetPosition (this ITextSnapshot snapshot, int lineNumber, int columnIndex, out SnapshotPoint position)
54+
{
55+
var result = 0;
56+
position = new SnapshotPoint ();
57+
58+
if (lineNumber < 0 || lineNumber >= snapshot.LineCount) {
59+
return false;
60+
}
61+
62+
var line = snapshot.GetLineFromLineNumber (lineNumber);
63+
if (columnIndex < 0 || columnIndex >= line.Length) {
64+
return false;
65+
}
66+
67+
result = line.Start.Position + columnIndex;
68+
position = new SnapshotPoint (snapshot, result);
69+
return true;
70+
}
71+
72+
public static SnapshotSpan GetSpan (this ITextSnapshot snapshot, int start, int length)
73+
=> new SnapshotSpan (snapshot, new Span (start, length));
74+
75+
public static SnapshotSpan GetSpanFromBounds (this ITextSnapshot snapshot, int start, int end)
76+
=> new SnapshotSpan (snapshot, Span.FromBounds (start, end));
77+
78+
public static SnapshotSpan GetSpan (this ITextSnapshot snapshot, Span span)
79+
=> new SnapshotSpan (snapshot, span);
80+
81+
public static ITagSpan<TTag> GetTagSpan<TTag> (this ITextSnapshot snapshot, Span span, TTag tag)
82+
where TTag : ITag
83+
{
84+
return new TagSpan<TTag> (new SnapshotSpan (snapshot, span), tag);
85+
}
86+
87+
public static SnapshotSpan GetSpan (this ITextSnapshot snapshot, int startLine, int startIndex, int endLine, int endIndex)
88+
{
89+
return TryGetSpan (snapshot, startLine, startIndex, endLine, endIndex).Value;
90+
}
91+
92+
public static SnapshotSpan? TryGetSpan (this ITextSnapshot snapshot, int startLine, int startIndex, int endLine, int endIndex)
93+
{
94+
var startPosition = snapshot.TryGetPosition (startLine, startIndex);
95+
var endPosition = snapshot.TryGetPosition (endLine, endIndex);
96+
if (startPosition == null || endPosition == null) {
97+
return null;
98+
}
99+
100+
return new SnapshotSpan (snapshot, Span.FromBounds (startPosition.Value, endPosition.Value));
101+
}
102+
103+
public static SnapshotSpan GetFullSpan (this ITextSnapshot snapshot)
104+
{
105+
Contract.ThrowIfNull (snapshot);
106+
107+
return new SnapshotSpan (snapshot, new Span (0, snapshot.Length));
108+
}
109+
110+
public static NormalizedSnapshotSpanCollection GetSnapshotSpanCollection (this ITextSnapshot snapshot)
111+
{
112+
Contract.ThrowIfNull (snapshot);
113+
114+
return new NormalizedSnapshotSpanCollection (snapshot.GetFullSpan ());
115+
}
116+
117+
public static void GetLineAndCharacter (this ITextSnapshot snapshot, int position, out int lineNumber, out int characterIndex)
118+
{
119+
var line = snapshot.GetLineFromPosition (position);
120+
121+
lineNumber = line.LineNumber;
122+
characterIndex = position - line.Start.Position;
123+
}
124+
125+
/// <summary>
126+
/// Returns the leading whitespace of the line located at the specified position in the given snapshot.
127+
/// </summary>
128+
public static string GetLeadingWhitespaceOfLineAtPosition (this ITextSnapshot snapshot, int position)
129+
{
130+
Contract.ThrowIfNull (snapshot);
131+
132+
var line = snapshot.GetLineFromPosition (position);
133+
var linePosition = line.GetFirstNonWhitespacePosition ();
134+
if (!linePosition.HasValue) {
135+
return line.GetText ();
136+
}
137+
138+
var lineText = line.GetText ();
139+
return lineText.Substring (0, linePosition.Value - line.Start);
140+
}
141+
142+
public static bool AreOnSameLine (this ITextSnapshot snapshot, int x1, int x2)
143+
=> snapshot.GetLineNumberFromPosition (x1) == snapshot.GetLineNumberFromPosition (x2);
144+
}
145+
}

0 commit comments

Comments
 (0)