-
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
Initial work to create a service for returning related documents for copilot purposes. #74906
Changes from 31 commits
af2b409
ac1f843
bf2dd1c
67ea275
d58e435
1f78cd8
262a509
d0db45a
d4f9e56
1a018b5
2916771
4226c0c
35f8d96
6911ac0
c03680e
0d334d6
05a2b3c
65c546d
3a99080
fd5703f
71a09a7
c65fa43
e8a4899
193f4e4
cf82fe6
e15839b
7401f5f
3314264
2f25c4d
94c4839
8013e77
9e990df
ed4f78e
00151b6
7b0129c
4e52ff7
d57aff7
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 |
---|---|---|
|
@@ -13,38 +13,39 @@ | |
<ItemGroup> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.AssetSynchronization" ClassName="Microsoft.CodeAnalysis.Remote.RemoteAssetSynchronizationService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.AsynchronousOperationListener" ClassName="Microsoft.CodeAnalysis.Remote.RemoteAsynchronousOperationListenerService+Factory" Audience="AllClientsIncludingGuests"/> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.CodeLensReferences" ClassName="Microsoft.CodeAnalysis.Remote.RemoteCodeLensReferencesService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.CompilationAvailable" ClassName="Microsoft.CodeAnalysis.Remote.RemoteCompilationAvailableService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ConvertTupleToStructCodeRefactoring" ClassName="Microsoft.CodeAnalysis.Remote.RemoteConvertTupleToStructCodeRefactoringService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DependentTypeFinder" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDependentTypeFinderService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DesignerAttributeDiscovery" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDesignerAttributeDiscoveryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DiagnosticAnalyzer" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDiagnosticAnalyzerService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SemanticClassification" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSemanticClassificationService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DocumentHighlights" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDocumentHighlightsService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.EditAndContinue" ClassName="Microsoft.CodeAnalysis.EditAndContinue.RemoteEditAndContinueService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.EncapsulateField" ClassName="Microsoft.CodeAnalysis.Remote.RemoteEncapsulateFieldService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.KeepAlive" ClassName="Microsoft.CodeAnalysis.Remote.RemoteKeepAliveService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.Renamer" ClassName="Microsoft.CodeAnalysis.Remote.RemoteRenamerService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ConvertTupleToStructCodeRefactoring" ClassName="Microsoft.CodeAnalysis.Remote.RemoteConvertTupleToStructCodeRefactoringService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SymbolFinder" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSymbolFinderService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ExtensionMethodImportCompletion" ClassName="Microsoft.CodeAnalysis.Remote.RemoteExtensionMethodImportCompletionService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.FindUsages" ClassName="Microsoft.CodeAnalysis.Remote.RemoteFindUsagesService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.FullyQualify" ClassName="Microsoft.CodeAnalysis.Remote.RemoteFullyQualifyService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.GlobalNotificationDelivery" ClassName="Microsoft.CodeAnalysis.Remote.RemoteGlobalNotificationDeliveryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.InheritanceMargin" ClassName="Microsoft.CodeAnalysis.Remote.RemoteInheritanceMarginService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.KeepAlive" ClassName="Microsoft.CodeAnalysis.Remote.RemoteKeepAliveService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.LegacySolutionEventsAggregation" ClassName="Microsoft.CodeAnalysis.Remote.RemoteLegacySolutionEventsAggregationService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.MissingImportDiscovery" ClassName="Microsoft.CodeAnalysis.Remote.RemoteMissingImportDiscoveryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.NavigateToSearch" ClassName="Microsoft.CodeAnalysis.Remote.RemoteNavigateToSearchService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.NavigationBarItem" ClassName="Microsoft.CodeAnalysis.Remote.RemoteNavigationBarItemService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.MissingImportDiscovery" ClassName="Microsoft.CodeAnalysis.Remote.RemoteMissingImportDiscoveryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ExtensionMethodImportCompletion" ClassName="Microsoft.CodeAnalysis.Remote.RemoteExtensionMethodImportCompletionService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DependentTypeFinder" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDependentTypeFinderService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.GlobalNotificationDelivery" ClassName="Microsoft.CodeAnalysis.Remote.RemoteGlobalNotificationDeliveryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.CodeLensReferences" ClassName="Microsoft.CodeAnalysis.Remote.RemoteCodeLensReferencesService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.DesignerAttributeDiscovery" ClassName="Microsoft.CodeAnalysis.Remote.RemoteDesignerAttributeDiscoveryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ProcessTelemetry" ClassName="Microsoft.CodeAnalysis.Remote.RemoteProcessTelemetryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ProjectTelemetry" ClassName="Microsoft.CodeAnalysis.Remote.RemoteProjectTelemetryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.LegacySolutionEventsAggregation" ClassName="Microsoft.CodeAnalysis.Remote.RemoteLegacySolutionEventsAggregationService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.RelatedDocuments" ClassName="Microsoft.CodeAnalysis.Remote.RemoteRelatedDocumentsService+Factory" /> | ||
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. this is new. |
||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.Renamer" ClassName="Microsoft.CodeAnalysis.Remote.RemoteRenamerService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SemanticClassification" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSemanticClassificationService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SemanticSearch" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSemanticSearchService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SourceGeneration" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSourceGenerationService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.StackTraceExplorer" ClassName="Microsoft.CodeAnalysis.Remote.RemoteStackTraceExplorerService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SymbolFinder" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSymbolFinderService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SymbolSearchUpdate" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSymbolSearchUpdateService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.TaskList" ClassName="Microsoft.CodeAnalysis.Remote.RemoteTaskListService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.EditAndContinue" ClassName="Microsoft.CodeAnalysis.EditAndContinue.RemoteEditAndContinueService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ValueTracking" ClassName="Microsoft.CodeAnalysis.Remote.RemoteValueTrackingService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.InheritanceMargin" ClassName="Microsoft.CodeAnalysis.Remote.RemoteInheritanceMarginService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.UnusedReferenceAnalysis" ClassName="Microsoft.CodeAnalysis.Remote.RemoteUnusedReferenceAnalysisService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ProcessTelemetry" ClassName="Microsoft.CodeAnalysis.Remote.RemoteProcessTelemetryService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.CompilationAvailable" ClassName="Microsoft.CodeAnalysis.Remote.RemoteCompilationAvailableService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.StackTraceExplorer" ClassName="Microsoft.CodeAnalysis.Remote.RemoteStackTraceExplorerService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.UnitTestingSearch" ClassName="Microsoft.CodeAnalysis.Remote.RemoteUnitTestingSearchService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SourceGeneration" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSourceGenerationService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.SemanticSearch" ClassName="Microsoft.CodeAnalysis.Remote.RemoteSemanticSearchService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.UnusedReferenceAnalysis" ClassName="Microsoft.CodeAnalysis.Remote.RemoteUnusedReferenceAnalysisService+Factory" /> | ||
<ServiceHubService Include="Microsoft.VisualStudio.LanguageServices.ValueTracking" ClassName="Microsoft.CodeAnalysis.Remote.RemoteValueTrackingService+Factory" /> | ||
</ItemGroup> | ||
|
||
<!-- | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// 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.Generic; | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis.CSharp.Extensions; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Host.Mef; | ||
using Microsoft.CodeAnalysis.PooledObjects; | ||
using Microsoft.CodeAnalysis.RelatedDocuments; | ||
using Microsoft.CodeAnalysis.Shared.Extensions; | ||
using Microsoft.CodeAnalysis.Shared.Utilities; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.CSharp.RelatedDocuments; | ||
|
||
[ExportLanguageService(typeof(IRelatedDocumentsService), LanguageNames.CSharp), Shared] | ||
[method: ImportingConstructor] | ||
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] | ||
internal sealed class CSharpRelatedDocumentsService() : AbstractRelatedDocumentsService | ||
{ | ||
protected override async ValueTask GetRelatedDocumentIdsInCurrentProcessAsync( | ||
Document document, | ||
int position, | ||
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. position needs to be optional. 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. it is. you can just pass 0. 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. Consider using -1 for this purpose. I think 0 is a valid position in file, so potentially we could return a different list of related files for "position 0" compare to "the entire file" |
||
Func<ImmutableArray<DocumentId>, CancellationToken, ValueTask> callbackAsync, | ||
CancellationToken cancellationToken) | ||
{ | ||
var solution = document.Project.Solution; | ||
|
||
// Don't need nullable analysis, and we're going to walk a lot of the tree, so speed things up by not doing | ||
// excess semantic work. | ||
var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); | ||
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
|
||
var seenDocumentIds = new ConcurrentSet<DocumentId>(); | ||
var seenTypeNames = new ConcurrentSet<string>(); | ||
|
||
// Bind as much as we can in parallel to get the results as quickly as possible. We call into the | ||
// ProducerConsumer overload that will batch up values into arrays, and call into the callback with them. While | ||
// that callback is executing, the ProducerConsumer continues to run, computing more results and getting them | ||
// ready for when that call returns. This approach ensures that we're not pausing while we're reporting the | ||
// results to whatever client is calling into us. | ||
await ProducerConsumer<DocumentId>.RunParallelAsync( | ||
// Order the nodes by the distance from the requested position. | ||
IteratePotentialTypeNodes(root).OrderBy(t => t.expression.SpanStart - position), | ||
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. yup. That would make more sense! |
||
produceItems: static (tuple, callback, args, cancellationToken) => | ||
{ | ||
var (solution, semanticModel, seenDocumentIds, seenTypeNames, callbackAsync) = args; | ||
ProduceItems( | ||
solution, semanticModel, tuple.expression, tuple.nameToken, callback, seenDocumentIds, seenTypeNames, cancellationToken); | ||
|
||
return Task.CompletedTask; | ||
}, | ||
consumeItems: static async (array, args, cancellationToken) => | ||
{ | ||
var (solution, semanticModel, seenDocumentIds, seenTypeNames, callbackAsync) = args; | ||
await callbackAsync(array, cancellationToken).ConfigureAwait(false); | ||
}, | ||
args: (solution, semanticModel, seenDocumentIds, seenTypeNames, callbackAsync), | ||
cancellationToken).ConfigureAwait(false); | ||
|
||
return; | ||
|
||
static IEnumerable<(ExpressionSyntax expression, SyntaxToken nameToken)> IteratePotentialTypeNodes(SyntaxNode root) | ||
{ | ||
using var _ = ArrayBuilder<SyntaxNode>.GetInstance(out var stack); | ||
stack.Push(root); | ||
|
||
while (stack.TryPop(out var current)) | ||
{ | ||
if (current is MemberAccessExpressionSyntax memberAccess) | ||
{ | ||
// Could be a static member access off of a type name. Check the left side, and if it's just a | ||
// dotted name, return that. | ||
|
||
if (IsPossibleTypeName(memberAccess.Expression, out var nameToken)) | ||
{ | ||
// Something like `X.Y.Z` where `X.Y` is a type name. Bind X.Y | ||
yield return (memberAccess.Expression, nameToken); | ||
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. For 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. not exactly. we have |
||
} | ||
else | ||
{ | ||
// Something like `(...).Y`. Recurse down the left side of the member access to see if there | ||
// are types in there. We don't want to recurse down the name portion as it will never be a | ||
// type. | ||
stack.Push(memberAccess.Expression); | ||
} | ||
|
||
continue; | ||
} | ||
else if (current is NameSyntax name) | ||
{ | ||
yield return (name, name.GetNameToken()); | ||
|
||
// Intentionally continue to recurse down the name so that if we have things like `X<Y>` we'll bind | ||
// the inner `Y` as well. | ||
} | ||
|
||
// Don't need to recurse in order as our caller is ordering results anyways. | ||
foreach (var child in current.ChildNodesAndTokens()) | ||
{ | ||
if (child.AsNode(out var childNode)) | ||
stack.Push(childNode); | ||
} | ||
} | ||
} | ||
|
||
static bool IsPossibleTypeName(ExpressionSyntax expression, out SyntaxToken nameToken) | ||
{ | ||
while (expression is MemberAccessExpressionSyntax memberAccessExpression) | ||
expression = memberAccessExpression.Expression; | ||
|
||
if (expression is not NameSyntax name) | ||
{ | ||
nameToken = default; | ||
return false; | ||
} | ||
|
||
nameToken = name.GetNameToken(); | ||
return true; | ||
} | ||
|
||
static void ProduceItems( | ||
Solution solution, | ||
SemanticModel semanticModel, | ||
ExpressionSyntax expression, | ||
SyntaxToken nameToken, | ||
Action<DocumentId> callback, | ||
ConcurrentSet<DocumentId> seenDocumentIds, | ||
ConcurrentSet<string> seenTypeNames, | ||
CancellationToken cancellationToken) | ||
{ | ||
if (nameToken.Kind() != SyntaxKind.IdentifierToken) | ||
return; | ||
|
||
if (nameToken.ValueText == "") | ||
return; | ||
// Don't rebind a type name we've already seen. Note: this is a conservative/inaccurate check. | ||
// Specifically, there could be different types with the same last name portion (from different | ||
// namespaces). In that case, we'll miss the one that is further away. We can revisit this in the | ||
// future if we think it's necessary. | ||
if (!seenTypeNames.Add(nameToken.ValueText)) | ||
return; | ||
|
||
var symbol = semanticModel.GetSymbolInfo(expression, cancellationToken).GetAnySymbol(); | ||
if (symbol is not ITypeSymbol) | ||
return; | ||
|
||
foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) | ||
{ | ||
var documentId = solution.GetDocument(syntaxReference.SyntaxTree)?.Id; | ||
if (documentId != null && seenDocumentIds.Add(documentId)) | ||
callback(documentId); | ||
} | ||
} | ||
} | ||
} |
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.
just sorted.