diff --git a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b513b76c16ef03419330dea43c873bdfa4bb4c3c
--- /dev/null
+++ b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs
@@ -0,0 +1,23 @@
+// 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.Composition;
+using Microsoft.CodeAnalysis.Editor.FindUsages;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.Host.Mef;
+
+namespace Microsoft.CodeAnalysis.Editor.CSharp.FindUsages
+{
+ [ExportLanguageService(typeof(IFindUsagesLSPService), LanguageNames.CSharp), Shared]
+ internal class CSharpFindUsagesLSPService : AbstractFindUsagesService
+ {
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public CSharpFindUsagesLSPService(IThreadingContext threadingContext)
+ : base(threadingContext)
+ {
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs
index 6056ce5cc41c2b22e13c128722571b585e44a069..09b272bebbece809c22e73606bd386134902b999 100644
--- a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs
+++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs
@@ -18,7 +18,7 @@
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
- internal abstract partial class AbstractFindUsagesService : IFindUsagesService
+ internal abstract partial class AbstractFindUsagesService : IFindUsagesService, IFindUsagesLSPService
{
private readonly IThreadingContext _threadingContext;
@@ -60,7 +60,7 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
}
}
- public async Task FindReferencesAsync(
+ async Task IFindUsagesService.FindReferencesAsync(
Document document, int position, IFindUsagesContext context)
{
var definitionTrackingContext = new DefinitionTrackingContext(context);
@@ -88,6 +88,18 @@ protected AbstractFindUsagesService(IThreadingContext threadingContext)
}
}
+ async Task IFindUsagesLSPService.FindReferencesAsync(
+ Document document, int position, IFindUsagesContext context)
+ {
+ // We don't need to get third party definitions when finding references in LSP.
+ // Currently, 3rd party definitions = XAML definitions, and XAML will provide
+ // references via LSP instead of hooking into Roslyn.
+ // This also means that we don't need to be on the UI thread.
+ var definitionTrackingContext = new DefinitionTrackingContext(context);
+ await FindLiteralOrSymbolReferencesAsync(
+ document, position, definitionTrackingContext).ConfigureAwait(false);
+ }
+
private async Task FindLiteralOrSymbolReferencesAsync(
Document document, int position, IFindUsagesContext context)
{
diff --git a/src/EditorFeatures/Core/FindUsages/IFindUsagesLSPService.cs b/src/EditorFeatures/Core/FindUsages/IFindUsagesLSPService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..654199a61d5d01034f76cbac5830ff2f000b2c0d
--- /dev/null
+++ b/src/EditorFeatures/Core/FindUsages/IFindUsagesLSPService.cs
@@ -0,0 +1,25 @@
+// 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.Threading.Tasks;
+using Microsoft.CodeAnalysis.FindUsages;
+using Microsoft.CodeAnalysis.Host;
+
+namespace Microsoft.CodeAnalysis.Editor.FindUsages
+{
+ internal interface IFindUsagesLSPService : ILanguageService
+ {
+ ///
+ /// Finds the references for the symbol at the specific position in the document,
+ /// pushing the results into the context instance.
+ ///
+ Task FindReferencesAsync(Document document, int position, IFindUsagesContext context);
+
+ ///
+ /// Finds the implementations for the symbol at the specific position in the document,
+ /// pushing the results into the context instance.
+ ///
+ Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context);
+ }
+}
diff --git a/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesLSPService.vb b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesLSPService.vb
new file mode 100644
index 0000000000000000000000000000000000000000..73fd13b6d4a322fc5bafbf56d1ad0caf45027d4c
--- /dev/null
+++ b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesLSPService.vb
@@ -0,0 +1,21 @@
+' 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.
+
+Imports System.Composition
+Imports Microsoft.CodeAnalysis.Editor.FindUsages
+Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities
+Imports Microsoft.CodeAnalysis.Host.Mef
+
+Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindUsages
+
+ Friend Class VisualBasicFindUsagesLSPService
+ Inherits AbstractFindUsagesService
+
+
+
+ Public Sub New(threadingContext As IThreadingContext)
+ MyBase.New(threadingContext)
+ End Sub
+ End Class
+End Namespace
diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs
index d0b73e0a453764638940ab9afc73c52c2fb799ec..cb9badd814966cdf72789e926c07592a944ac902 100644
--- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs
+++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs
@@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.Tags;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text.Adornments;
+using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
@@ -381,5 +382,77 @@ public static Glyph CompletionItemKindToGlyph(LSP.CompletionItemKind kind)
return Glyph.None;
}
}
+
+ // The mappings here are roughly based off of SymbolUsageInfoExtensions.ToSymbolReferenceKinds.
+ public static LSP.ReferenceKind[] SymbolUsageInfoToReferenceKinds(SymbolUsageInfo symbolUsageInfo)
+ {
+ var referenceKinds = ArrayBuilder.GetInstance();
+ if (symbolUsageInfo.ValueUsageInfoOpt.HasValue)
+ {
+ var usageInfo = symbolUsageInfo.ValueUsageInfoOpt.Value;
+ if (usageInfo.IsReadFrom())
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Read);
+ }
+
+ if (usageInfo.IsWrittenTo())
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Write);
+ }
+
+ if (usageInfo.IsReference())
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Reference);
+ }
+
+ if (usageInfo.IsNameOnly())
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Name);
+ }
+ }
+
+ if (symbolUsageInfo.TypeOrNamespaceUsageInfoOpt.HasValue)
+ {
+ var usageInfo = symbolUsageInfo.TypeOrNamespaceUsageInfoOpt.Value;
+ if ((usageInfo & TypeOrNamespaceUsageInfo.Qualified) != 0)
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Qualified);
+ }
+
+ if ((usageInfo & TypeOrNamespaceUsageInfo.TypeArgument) != 0)
+ {
+ referenceKinds.Add(LSP.ReferenceKind.TypeArgument);
+ }
+
+ if ((usageInfo & TypeOrNamespaceUsageInfo.TypeConstraint) != 0)
+ {
+ referenceKinds.Add(LSP.ReferenceKind.TypeConstraint);
+ }
+
+ if ((usageInfo & TypeOrNamespaceUsageInfo.Base) != 0)
+ {
+ referenceKinds.Add(LSP.ReferenceKind.BaseType);
+ }
+
+ // Preserving the same mapping logic that SymbolUsageInfoExtensions.ToSymbolReferenceKinds uses
+ if ((usageInfo & TypeOrNamespaceUsageInfo.ObjectCreation) != 0)
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Constructor);
+ }
+
+ if ((usageInfo & TypeOrNamespaceUsageInfo.Import) != 0)
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Import);
+ }
+
+ // Preserving the same mapping logic that SymbolUsageInfoExtensions.ToSymbolReferenceKinds uses
+ if ((usageInfo & TypeOrNamespaceUsageInfo.NamespaceDeclaration) != 0)
+ {
+ referenceKinds.Add(LSP.ReferenceKind.Declaration);
+ }
+ }
+
+ return referenceKinds.ToArrayAndFree();
+ }
}
}
diff --git a/src/Features/LanguageServer/Protocol/Handler/Initialize/InitializeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Initialize/InitializeHandler.cs
index a6602a1ede0524ee426992a698b269a12c6b7a37..31b7ed25b9514951b278486d336f8ca5151f27b5 100644
--- a/src/Features/LanguageServer/Protocol/Handler/Initialize/InitializeHandler.cs
+++ b/src/Features/LanguageServer/Protocol/Handler/Initialize/InitializeHandler.cs
@@ -50,6 +50,7 @@ public Task HandleRequestAsync(Solution solution, LSP.Init
DocumentRangeFormattingProvider = true,
DocumentOnTypeFormattingProvider = new LSP.DocumentOnTypeFormattingOptions { FirstTriggerCharacter = "}", MoreTriggerCharacter = new[] { ";", "\n" } },
DocumentHighlightProvider = true,
+ ReferencesProvider = true,
}
});
}
diff --git a/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5738024cc1a990ae06ef254e7c5e4ec5100f54fb
--- /dev/null
+++ b/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs
@@ -0,0 +1,208 @@
+// 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.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Classification;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Editor.FindUsages;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.FindSymbols.Finders;
+using Microsoft.CodeAnalysis.FindUsages;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.PooledObjects;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+using Microsoft.VisualStudio.Text.Adornments;
+using Roslyn.Utilities;
+using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
+
+namespace Microsoft.CodeAnalysis.LanguageServer.Handler
+{
+ [ExportLspMethod(LSP.Methods.TextDocumentReferencesName), Shared]
+ internal class FindAllReferencesHandler : IRequestHandler
+ {
+ private readonly IThreadingContext _threadingContext;
+ private readonly IMetadataAsSourceFileService _metadataAsSourceFileService;
+
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public FindAllReferencesHandler(IThreadingContext threadingContext, IMetadataAsSourceFileService metadataAsSourceFileService)
+ {
+ _threadingContext = threadingContext;
+ _metadataAsSourceFileService = metadataAsSourceFileService;
+ }
+
+ public async Task HandleRequestAsync(
+ Solution solution,
+ ReferenceParams referenceParams,
+ ClientCapabilities clientCapabilities,
+ CancellationToken cancellationToken)
+ {
+ Debug.Assert(clientCapabilities.HasVisualStudioLspCapability());
+
+ var document = solution.GetDocumentFromURI(referenceParams.TextDocument.Uri);
+ if (document == null)
+ {
+ return Array.Empty();
+ }
+
+ var findUsagesService = document.GetRequiredLanguageService();
+ var position = await document.GetPositionFromLinePositionAsync(
+ ProtocolConversions.PositionToLinePosition(referenceParams.Position), cancellationToken).ConfigureAwait(false);
+ var context = new SimpleFindUsagesContext(cancellationToken);
+
+ // Finds the references for the symbol at the specific position in the document, pushing the results to the context instance.
+ await findUsagesService.FindReferencesAsync(document, position, context).ConfigureAwait(false);
+
+ return await GetReferenceItemsAsync(document, position, context, cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task GetReferenceItemsAsync(
+ Document document,
+ int position,
+ SimpleFindUsagesContext context,
+ CancellationToken cancellationToken)
+ {
+ // Mapping each reference to its definition
+ var definitionMap = new Dictionary>();
+ foreach (var reference in context.GetReferences())
+ {
+ if (!definitionMap.ContainsKey(reference.Definition))
+ {
+ definitionMap.Add(reference.Definition, new List());
+ }
+
+ definitionMap[reference.Definition].Add(reference);
+ }
+
+ // NOTE: Parts of FAR currently do not display correctly due to a bug in LSP.VSReferenceItem.
+ // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1088938/
+ using var _ = ArrayBuilder.GetInstance(out var referenceItems);
+
+ // Each reference is assigned its own unique id
+ var id = 0;
+ foreach (var (definition, references) in definitionMap)
+ {
+ // Creating the reference item that corresponds to the definition.
+ var definitionItem = await GenerateReferenceItem(
+ id, definitionId: id, document, position, definition.SourceSpans.FirstOrDefault(), context, definition.DisplayableProperties,
+ definition.GetClassifiedText(), symbolUsageInfo: null, cancellationToken).ConfigureAwait(false);
+
+ // If we have an empty location, skip this definition and its references.
+ if (definitionItem.Location == null)
+ {
+ continue;
+ }
+
+ referenceItems.Add(definitionItem);
+ var definitionId = id;
+ id++;
+
+ // Creating a reference item for each reference.
+ foreach (var reference in references)
+ {
+ var referenceItem = await GenerateReferenceItem(
+ id, definitionId, document, position, reference.SourceSpan, context, reference.AdditionalProperties,
+ definitionText: null, reference.SymbolUsageInfo, cancellationToken).ConfigureAwait(false);
+
+ // If we have an empty location, skip this reference.
+ if (referenceItem.Location == null)
+ {
+ continue;
+ }
+
+ referenceItems.Add(referenceItem);
+ id++;
+ };
+ }
+
+ return referenceItems.ToArray();
+
+ async Task GenerateReferenceItem(
+ int id,
+ int? definitionId,
+ Document originalDocument,
+ int originalPosition,
+ DocumentSpan documentSpan,
+ SimpleFindUsagesContext context,
+ ImmutableDictionary properties,
+ ClassifiedTextElement? definitionText,
+ SymbolUsageInfo? symbolUsageInfo,
+ CancellationToken cancellationToken)
+ {
+ LSP.Location? location = null;
+
+ // If we have no source span, our location may be in metadata.
+ if (documentSpan == default)
+ {
+ var symbol = await SymbolFinder.FindSymbolAtPositionAsync(originalDocument, originalPosition, cancellationToken).ConfigureAwait(false);
+ if (symbol != null && symbol.Locations != null && !symbol.Locations.IsEmpty && symbol.Locations.First().IsInMetadata)
+ {
+ var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync(
+ originalDocument.Project, symbol, allowDecompilation: false, cancellationToken).ConfigureAwait(false);
+
+ var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
+ location = new LSP.Location
+ {
+ Uri = new Uri(declarationFile.FilePath),
+ Range = ProtocolConversions.LinePositionToRange(linePosSpan),
+ };
+ }
+ }
+ else
+ {
+ location = await ProtocolConversions.DocumentSpanToLocationAsync(documentSpan, cancellationToken).ConfigureAwait(false);
+ }
+
+ // TO-DO: The Origin property should be added once Rich-Nav is completed.
+ // https://github.com/dotnet/roslyn/issues/42847
+ var result = new LSP.VSReferenceItem
+ {
+ ContainingMember = properties.TryGetValue(
+ AbstractReferenceFinder.ContainingMemberInfoPropertyName, out var referenceContainingMember) ? referenceContainingMember : null,
+ ContainingType = properties.TryGetValue(
+ AbstractReferenceFinder.ContainingTypeInfoPropertyName, out var referenceContainingType) ? referenceContainingType : null,
+ DefinitionId = definitionId,
+ DefinitionText = definitionText, // Only definitions should have a non-null DefinitionText
+ DisplayPath = location?.Uri.LocalPath,
+ DocumentName = documentSpan == default ? null : documentSpan.Document.Name,
+ Id = id,
+ Kind = symbolUsageInfo.HasValue ? ProtocolConversions.SymbolUsageInfoToReferenceKinds(symbolUsageInfo.Value) : new ReferenceKind[] { },
+ Location = location,
+ ProjectName = documentSpan == default ? null : documentSpan.Document.Project.Name,
+ ResolutionStatus = ResolutionStatusKind.ConfirmedAsReference,
+ };
+
+ // Properly assigning the text property.
+ if (id == definitionId)
+ {
+ result.Text = definitionText;
+ }
+ else if (documentSpan != default)
+ {
+ var classifiedSpansAndHighlightSpan = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(
+ documentSpan, cancellationToken).ConfigureAwait(false);
+ var classifiedSpans = classifiedSpansAndHighlightSpan.ClassifiedSpans;
+ var docText = await documentSpan.Document.GetTextAsync(cancellationToken).ConfigureAwait(false);
+
+ result.Text = new ClassifiedTextElement(
+ classifiedSpans.Select(cspan => new ClassifiedTextRun(cspan.ClassificationType, docText.ToString(cspan.TextSpan))));
+ }
+
+ return result;
+ }
+ }
+ }
+}
diff --git a/src/Features/LanguageServer/Protocol/LanguageServerProtocol.cs b/src/Features/LanguageServer/Protocol/LanguageServerProtocol.cs
index 9f2370bbc7fd6a117cf30f1479b5fb0d4fcb80ec..b7bddb0b886c3fd51b8d6e6d6075c8459d864b1a 100644
--- a/src/Features/LanguageServer/Protocol/LanguageServerProtocol.cs
+++ b/src/Features/LanguageServer/Protocol/LanguageServerProtocol.cs
@@ -153,6 +153,18 @@ public Task FormatDocumentRangeAsync(Solution solution, LSP.Docu
public Task GetDocumentHighlightAsync(Solution solution, LSP.TextDocumentPositionParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
=> ExecuteRequestAsync(LSP.Methods.TextDocumentDocumentHighlightName, solution, request, clientCapabilities, cancellationToken);
+ ///
+ /// Answers a document references request by returning references information associated with the symbol at a given document location.
+ /// https://microsoft.github.io/language-server-protocol/specification#textDocument_references
+ ///
+ /// the solution containing the request document.
+ /// the references request.
+ /// the client capabilities for the request.
+ /// a cancellation token.
+ /// references information associated with the symbol at the given document location.
+ public Task GetDocumentReferencesAsync(Solution solution, LSP.ReferenceParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
+ => ExecuteRequestAsync(LSP.Methods.TextDocumentReferencesName, solution, request, clientCapabilities, cancellationToken);
+
///
/// Answers a document symbols request by returning a list of symbols in the document.
/// https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol
@@ -182,7 +194,7 @@ public Task GetFoldingRangeAsync(Solution solution, LSP.Fold
/// https://microsoft.github.io/language-server-protocol/specification#textDocument_hover
///
/// the solution containing any documents in the request.
- /// the hover requesst.
+ /// the hover request.
/// the client capabilities for the request.
/// a cancellation token.
/// the Hover using MarkupContent.
@@ -225,18 +237,6 @@ public Task GetWorkspaceSymbolsAsync(Solution solution,
public Task?> GoToDefinitionAsync(Solution solution, LSP.TextDocumentPositionParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
=> ExecuteRequestAsync?>(LSP.Methods.TextDocumentDefinitionName, solution, request, clientCapabilities, cancellationToken);
- ///
- /// Answers a rename request by returning the workspace edit for a given symbol.
- /// https://microsoft.github.io/language-server-protocol/specification#textDocument_rename
- ///
- /// the solution containing the request.
- /// the document position of the symbol to rename.
- /// the client capabilities for the request.
- /// a cancellation token.
- /// the workspace edits to rename the given symbol
- public Task RenameAsync(Solution solution, LSP.RenameParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
- => ExecuteRequestAsync(LSP.Methods.TextDocumentRenameName, solution, request, clientCapabilities, cancellationToken);
-
///
/// Answers a goto type definition request by returning the location of a given type definition.
/// https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition
@@ -261,6 +261,18 @@ public Task GoToTypeDefinitionAsync(Solution solution, LSP.TextD
public Task InitializeAsync(Solution solution, LSP.InitializeParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
=> ExecuteRequestAsync(LSP.Methods.InitializeName, solution, request, clientCapabilities, cancellationToken);
+ ///
+ /// Answers a rename request by returning the workspace edit for a given symbol.
+ /// https://microsoft.github.io/language-server-protocol/specification#textDocument_rename
+ ///
+ /// the solution containing the request.
+ /// the document position of the symbol to rename.
+ /// the client capabilities for the request.
+ /// a cancellation token.
+ /// the workspace edits to rename the given symbol
+ public Task RenameAsync(Solution solution, LSP.RenameParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
+ => ExecuteRequestAsync(LSP.Methods.TextDocumentRenameName, solution, request, clientCapabilities, cancellationToken);
+
///
/// Answers a request to resolve a completion item.
/// https://microsoft.github.io/language-server-protocol/specification#completionItem_resolve
diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fe8ff0f4cfbe564db009c44b939c58f16a7c2901
--- /dev/null
+++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs
@@ -0,0 +1,154 @@
+// 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.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Roslyn.Test.Utilities;
+using Xunit;
+using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
+
+namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.References
+{
+ public class FindAllReferencesHandlerTests : AbstractLanguageServerProtocolTests
+ {
+ [WpfFact]
+ public async Task TestFindAllReferencesAsync()
+ {
+ var markup =
+@"class A
+{
+ public int {|reference:someInt|} = 1;
+ void M()
+ {
+ var i = {|reference:someInt|} + 1;
+ }
+}
+class B
+{
+ int someInt = A.{|reference:someInt|} + 1;
+ void M2()
+ {
+ var j = someInt + A.{|caret:|}{|reference:someInt|};
+ }
+}";
+ using var workspace = CreateTestWorkspace(markup, out var locations);
+
+ var results = await RunFindAllReferencesAsync(workspace.CurrentSolution, locations["caret"].First());
+ AssertLocationsEqual(locations["reference"], results.Select(result => result.Location));
+
+ Assert.Equal("A", results[0].ContainingType);
+ Assert.Equal("B", results[2].ContainingType);
+ Assert.Equal("M", results[1].ContainingMember);
+ Assert.Equal("M2", results[3].ContainingMember);
+
+ AssertValidDefinitionProperties(results, 0);
+ }
+
+ [WpfFact]
+ public async Task TestFindAllReferencesAsync_MultipleDocuments()
+ {
+ var markups = new string[] {
+@"class A
+{
+ public int {|reference:someInt|} = 1;
+ void M()
+ {
+ var i = {|reference:someInt|} + 1;
+ }
+}",
+@"class B
+{
+ int someInt = A.{|reference:someInt|} + 1;
+ void M2()
+ {
+ var j = someInt + A.{|caret:|}{|reference:someInt|};
+ }
+}"
+ };
+
+ using var workspace = CreateTestWorkspace(markups, out var locations);
+
+ var results = await RunFindAllReferencesAsync(workspace.CurrentSolution, locations["caret"].First());
+ AssertLocationsEqual(locations["reference"], results.Select(result => result.Location));
+
+ Assert.Equal("A", results[0].ContainingType);
+ Assert.Equal("B", results[2].ContainingType);
+ Assert.Equal("M", results[1].ContainingMember);
+ Assert.Equal("M2", results[3].ContainingMember);
+
+ AssertValidDefinitionProperties(results, 0);
+ }
+
+ [WpfFact]
+ public async Task TestFindAllReferencesAsync_InvalidLocation()
+ {
+ var markup =
+@"class A
+{
+ {|caret:|}
+}";
+ using var workspace = CreateTestWorkspace(markup, out var locations);
+
+ var results = await RunFindAllReferencesAsync(workspace.CurrentSolution, locations["caret"].First());
+ Assert.Empty(results);
+ }
+
+ [WpfFact]
+ public async Task TestFindAllReferencesMetadataDefinitionAsync()
+ {
+ var markup =
+@"using System;
+
+class A
+{
+ void M()
+ {
+ Console.{|caret:|}{|reference:WriteLine|}(""text"");
+ }
+}";
+ using var workspace = CreateTestWorkspace(markup, out var locations);
+
+ var results = await RunFindAllReferencesAsync(workspace.CurrentSolution, locations["caret"].First());
+ Assert.NotNull(results[0].Location.Uri);
+ }
+
+ private static LSP.ReferenceParams CreateReferenceParams(LSP.Location caret) =>
+ new LSP.ReferenceParams()
+ {
+ TextDocument = CreateTextDocumentIdentifier(caret.Uri),
+ Position = caret.Range.Start,
+ Context = new LSP.ReferenceContext(),
+ };
+
+ private static async Task RunFindAllReferencesAsync(Solution solution, LSP.Location caret)
+ {
+ var vsClientCapabilities = new LSP.VSClientCapabilities
+ {
+ SupportsVisualStudioExtensions = true
+ };
+
+ return await GetLanguageServer(solution).GetDocumentReferencesAsync(solution, CreateReferenceParams(caret), vsClientCapabilities, CancellationToken.None);
+ }
+
+ private static void AssertValidDefinitionProperties(LSP.ReferenceItem[] referenceItems, int definitionIndex)
+ {
+ var definition = referenceItems[definitionIndex];
+ var definitionId = definition.DefinitionId;
+ Assert.NotNull(definition.DefinitionText);
+
+ for (var i = 0; i < referenceItems.Length; i++)
+ {
+ if (i == definitionIndex)
+ {
+ continue;
+ }
+
+ Assert.Null(referenceItems[i].DefinitionText);
+ Assert.Equal(definitionId, referenceItems[i].DefinitionId);
+ Assert.NotEqual(definitionId, referenceItems[i].Id);
+ }
+ }
+ }
+}
diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs
index fede3d013a304023941f3ea1bcade60c3e5ce54c..0478ed6d63e8441bcc07bcb890543c6b05cfeb50 100644
--- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs
+++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs
@@ -126,10 +126,17 @@ public void Exit()
[JsonRpcMethod(Methods.TextDocumentRenameName)]
public Task GetTextDocumentRenameAsync(JToken input, CancellationToken cancellationToken)
{
- var renameParams = input.ToObject();
+ var renameParams = input.ToObject(JsonSerializer);
return _protocol.RenameAsync(_workspace.CurrentSolution, renameParams, _clientCapabilities, cancellationToken);
}
+ [JsonRpcMethod(Methods.TextDocumentReferencesName)]
+ public Task GetTextDocumentReferencesAsync(JToken input, CancellationToken cancellationToken)
+ {
+ var referencesParams = input.ToObject(JsonSerializer);
+ return _protocol.GetDocumentReferencesAsync(_workspace.CurrentSolution, referencesParams, _clientCapabilities, cancellationToken);
+ }
+
[JsonRpcMethod(Methods.TextDocumentCompletionName)]
public Task?> GetTextDocumentCompletionAsync(JToken input, CancellationToken cancellationToken)
{
diff --git a/src/VisualStudio/LiveShare/Impl/Client/References/RoslynFindUsagesService.Exports.cs b/src/VisualStudio/LiveShare/Impl/Client/References/RoslynFindUsagesService.Exports.cs
deleted file mode 100644
index 32bbfac1f8d5832a785973021499a84dd2389135..0000000000000000000000000000000000000000
--- a/src/VisualStudio/LiveShare/Impl/Client/References/RoslynFindUsagesService.Exports.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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.Composition;
-using Microsoft.CodeAnalysis.Editor.FindUsages;
-using Microsoft.CodeAnalysis.Host.Mef;
-
-namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.References
-{
- [ExportLanguageService(typeof(IFindUsagesService), StringConstants.CSharpLspLanguageName), Shared]
- internal class CSharpLspFindUsagesService : RoslynFindUsagesService
- {
- [ImportingConstructor]
- [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public CSharpLspFindUsagesService(CSharpLspClientServiceFactory csharpLspClientServiceFactory, RemoteLanguageServiceWorkspace remoteLanguageServiceWorkspace)
- : base(csharpLspClientServiceFactory, remoteLanguageServiceWorkspace)
- {
- }
- }
-
- [ExportLanguageService(typeof(IFindUsagesService), StringConstants.VBLspLanguageName), Shared]
- internal class VBLspFindUsagesService : RoslynFindUsagesService
- {
- [ImportingConstructor]
- [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public VBLspFindUsagesService(VisualBasicLspClientServiceFactory vbLspClientServiceFactory, RemoteLanguageServiceWorkspace remoteLanguageServiceWorkspace)
- : base(vbLspClientServiceFactory, remoteLanguageServiceWorkspace)
- {
- }
- }
-}
diff --git a/src/VisualStudio/LiveShare/Impl/Client/References/RoslynFindUsagesService.cs b/src/VisualStudio/LiveShare/Impl/Client/References/RoslynFindUsagesService.cs
deleted file mode 100644
index b773a95843a83a265f0ab0c09659d3209ed6003e..0000000000000000000000000000000000000000
--- a/src/VisualStudio/LiveShare/Impl/Client/References/RoslynFindUsagesService.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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.Collections.Immutable;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Editor.FindUsages;
-using Microsoft.CodeAnalysis.FindUsages;
-using Microsoft.CodeAnalysis.LanguageServer;
-using Microsoft.VisualStudio.LanguageServices.LiveShare.Protocol;
-using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
-
-namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Client.References
-{
- internal class RoslynFindUsagesService : IFindUsagesService
- {
- private readonly AbstractLspClientServiceFactory _roslynLspClientServiceFactory;
- private readonly RemoteLanguageServiceWorkspace _remoteLanguageServiceWorkspace;
-
- public RoslynFindUsagesService(AbstractLspClientServiceFactory roslynLspClientServiceFactory, RemoteLanguageServiceWorkspace remoteLanguageServiceWorkspace)
- {
- _roslynLspClientServiceFactory = roslynLspClientServiceFactory ?? throw new ArgumentNullException(nameof(roslynLspClientServiceFactory));
- _remoteLanguageServiceWorkspace = remoteLanguageServiceWorkspace ?? throw new ArgumentNullException(nameof(remoteLanguageServiceWorkspace));
- }
-
- public Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context)
- {
- // Find implementations is now handled by the VS LSP client.
- return Task.CompletedTask;
- }
-
- public async Task FindReferencesAsync(Document document, int position, IFindUsagesContext context)
- {
- var text = await document.GetTextAsync().ConfigureAwait(false);
-
- var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient;
- if (lspClient == null)
- {
- return;
- }
-
- var referenceParams = new LSP.ReferenceParams
- {
- Context = new LSP.ReferenceContext { IncludeDeclaration = false },
- TextDocument = ProtocolConversions.DocumentToTextDocumentIdentifier(document),
- Position = ProtocolConversions.LinePositionToPosition(text.Lines.GetLinePosition(position))
- };
-
- var locations = await lspClient.RequestAsync(LSP.Methods.TextDocumentReferences.ToLSRequest(), referenceParams, context.CancellationToken).ConfigureAwait(false);
- if (locations == null)
- {
- return;
- }
-
- // TODO: Need to get real definition data from the server.
- var dummyDef = DefinitionItem.CreateNonNavigableItem(ImmutableArray.Empty, ImmutableArray.Empty);
- await context.OnDefinitionFoundAsync(dummyDef).ConfigureAwait(false);
-
- foreach (var location in locations)
- {
- var documentSpan = await _remoteLanguageServiceWorkspace.GetDocumentSpanFromLocationAsync(location, context.CancellationToken).ConfigureAwait(false);
- if (documentSpan == null)
- {
- continue;
- }
-
- await context.OnReferenceFoundAsync(new SourceReferenceItem(dummyDef, documentSpan.Value)).ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/src/VisualStudio/LiveShare/Impl/Client/RoslynLSPClientService.cs b/src/VisualStudio/LiveShare/Impl/Client/RoslynLSPClientService.cs
index 343d32125888753665ec11d0f756dc9b4331a132..5ede20193d386b324091e95c9506172df9243f25 100644
--- a/src/VisualStudio/LiveShare/Impl/Client/RoslynLSPClientService.cs
+++ b/src/VisualStudio/LiveShare/Impl/Client/RoslynLSPClientService.cs
@@ -97,7 +97,6 @@ public Task CreateServiceAsync(CollaborationSession colla
// Uses Roslyn client.
CodeActionProvider = true,
ExecuteCommandProvider = new ExecuteCommandOptions(),
- ReferencesProvider = true,
})));
var lifeTimeService = LspClientLifeTimeService;
diff --git a/src/VisualStudio/LiveShare/Impl/FindAllReferencesHandler.Exports.cs b/src/VisualStudio/LiveShare/Impl/FindAllReferencesHandler.Exports.cs
deleted file mode 100644
index 7a126fabd20e8b8d4d2edc6ce0582518447415f2..0000000000000000000000000000000000000000
--- a/src/VisualStudio/LiveShare/Impl/FindAllReferencesHandler.Exports.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
-using Microsoft.CodeAnalysis.Host.Mef;
-using Microsoft.VisualStudio.LanguageServer.Protocol;
-using Microsoft.VisualStudio.LiveShare.LanguageServices;
-
-namespace Microsoft.VisualStudio.LanguageServices.LiveShare
-{
- [ExportLspRequestHandler(LiveShareConstants.RoslynContractName, Methods.TextDocumentReferencesName)]
- [Obsolete("Used for backwards compatibility with old liveshare clients.")]
- internal class RoslynFindAllReferencesHandler : FindAllReferencesHandler
- {
- [ImportingConstructor]
- [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public RoslynFindAllReferencesHandler(IThreadingContext threadingContext) : base(threadingContext)
- {
- }
- }
-
- [ExportLspRequestHandler(LiveShareConstants.CSharpContractName, Methods.TextDocumentReferencesName)]
- internal class CSharpFindAllReferencesHandler : FindAllReferencesHandler
- {
- [ImportingConstructor]
- [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public CSharpFindAllReferencesHandler(IThreadingContext threadingContext) : base(threadingContext)
- {
- }
- }
-
- [ExportLspRequestHandler(LiveShareConstants.VisualBasicContractName, Methods.TextDocumentReferencesName)]
- internal class VisualBasicFindAllReferencesHandler : FindAllReferencesHandler
- {
- [ImportingConstructor]
- [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public VisualBasicFindAllReferencesHandler(IThreadingContext threadingContext) : base(threadingContext)
- {
- }
- }
-
- [ExportLspRequestHandler(LiveShareConstants.TypeScriptContractName, Methods.TextDocumentReferencesName)]
- internal class TypeScriptFindAllReferencesHandler : FindAllReferencesHandler
- {
- [ImportingConstructor]
- [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public TypeScriptFindAllReferencesHandler(IThreadingContext threadingContext) : base(threadingContext)
- {
- }
- }
-}
diff --git a/src/VisualStudio/LiveShare/Impl/FindAllReferencesHandler.cs b/src/VisualStudio/LiveShare/Impl/FindAllReferencesHandler.cs
deleted file mode 100644
index 5e2ac01ffa8cc9f1b0a007f857559a6d3eec4b2c..0000000000000000000000000000000000000000
--- a/src/VisualStudio/LiveShare/Impl/FindAllReferencesHandler.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-// 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.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Classification;
-using Microsoft.CodeAnalysis.Editor.FindUsages;
-using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
-using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
-using Microsoft.CodeAnalysis.FindUsages;
-using Microsoft.CodeAnalysis.LanguageServer;
-using Microsoft.CodeAnalysis.PooledObjects;
-using Microsoft.VisualStudio.LanguageServer.Protocol;
-using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService;
-using Microsoft.VisualStudio.LiveShare.LanguageServices;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Text.Adornments;
-using Newtonsoft.Json.Linq;
-using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
-
-namespace Microsoft.VisualStudio.LanguageServices.LiveShare
-{
- // TODO - This should move to the ILanguageClient when we remove the UI thread dependency.
- // https://github.com/dotnet/roslyn/issues/38477
- // The VS LSP client supports streaming using IProgress on various requests.
- // However, this is not yet supported through Live Share, so deserialization fails on the IProgress property.
- // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1043376 tracks Live Share support for this (committed for 16.6).
- internal class FindAllReferencesHandler : ILspRequestHandler