Skip to content

Commit

Permalink
Merge pull request #61221 from genlu/MoreContextSharing
Browse files Browse the repository at this point in the history
More SyntaxContext sharing among CompletionProviders
  • Loading branch information
genlu authored May 13, 2022
2 parents 45d468d + 9ebd02c commit 69446b8
Show file tree
Hide file tree
Showing 18 changed files with 53 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,13 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC
var position = completionContext.Position;
var document = completionContext.Document;
var cancellationToken = completionContext.CancellationToken;
var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);

if (!completionContext.CompletionOptions.ShowNameSuggestions)
{
return;
}

var context = CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken);
var context = (CSharpSyntaxContext)await completionContext.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false);
if (context.IsInNonUserCode)
{
return;
Expand All @@ -69,7 +68,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC
// Do not show name suggestions for unbound "async" identifier.
// Most likely user is writing an async method, so name suggestion will just interfere him
if (context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.AsyncKeyword) &&
semanticModel.GetSymbolInfo(context.TargetToken).GetAnySymbol() is null)
context.SemanticModel.GetSymbolInfo(context.TargetToken).GetAnySymbol() is null)
{
return;
}
Expand All @@ -85,7 +84,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC
AddNamesFromExistingOverloads(context, partialSemanticModel, result, cancellationToken);
}

var baseNames = GetBaseNames(semanticModel, nameInfo);
var baseNames = GetBaseNames(context.SemanticModel, nameInfo);
if (baseNames != default)
{
await GetRecommendedNamesAsync(baseNames, nameInfo, context, document, result, cancellationToken).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
if (tree.IsInNonUserCode(position, cancellationToken))
return;

var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);
var syntaxContext = CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken);
var syntaxContext = await context.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false);
var semanticModel = syntaxContext.SemanticModel;

if (syntaxContext.IsInTaskLikeTypeContext)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
return;

var recommender = document.GetRequiredLanguageService<IRecommendationService>();

var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var syntaxContext = await context.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false);
var semanticModel = syntaxContext.SemanticModel;

var options = context.CompletionOptions.ToRecommendationServiceOptions();
var recommendedSymbols = recommender.GetRecommendedSymbolsAtPosition(document, semanticModel, position, options, cancellationToken);
var recommendedSymbols = recommender.GetRecommendedSymbolsInContext(syntaxContext, options, cancellationToken);

AddUnnamedSymbols(context, position, semanticModel, recommendedSymbols.UnnamedSymbols, cancellationToken);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)

context.AddItems(await document.GetUnionItemsFromDocumentAndLinkedDocumentsAsync(
UnionCompletionItemComparer.Instance,
d => GetSnippetsForDocumentAsync(d, position, cancellationToken)).ConfigureAwait(false));
d => GetSnippetsForDocumentAsync(d, context, cancellationToken)).ConfigureAwait(false));
}
}
catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, ErrorSeverity.General))
Expand All @@ -75,8 +75,9 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
}

private static async Task<ImmutableArray<CompletionItem>> GetSnippetsForDocumentAsync(
Document document, int position, CancellationToken cancellationToken)
Document document, CompletionContext completionContext, CancellationToken cancellationToken)
{
var position = completionContext.Position;
var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
var semanticFacts = document.GetRequiredLanguageService<ISemanticFactsService>();
Expand All @@ -93,8 +94,9 @@ private static async Task<ImmutableArray<CompletionItem>> GetSnippetsForDocument
return ImmutableArray<CompletionItem>.Empty;
}

var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);
var context = CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken);
var context = await completionContext.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false);
var semanticModel = context.SemanticModel;

if (context.IsInTaskLikeTypeContext)
return ImmutableArray<CompletionItem>.Empty;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers
{
Expand All @@ -43,11 +44,10 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
try
{
var document = context.Document;
var position = context.Position;
var cancellationToken = context.CancellationToken;

var showSpeculativeT = await document.IsValidContextForDocumentOrLinkedDocumentsAsync(
(doc, ct) => ShouldShowSpeculativeTCompletionItemAsync(doc, position, ct),
(doc, ct) => ShouldShowSpeculativeTCompletionItemAsync(doc, context, ct),
cancellationToken).ConfigureAwait(false);

if (showSpeculativeT)
Expand All @@ -63,8 +63,9 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
}
}

private static async Task<bool> ShouldShowSpeculativeTCompletionItemAsync(Document document, int position, CancellationToken cancellationToken)
private static async Task<bool> ShouldShowSpeculativeTCompletionItemAsync(Document document, CompletionContext completionContext, CancellationToken cancellationToken)
{
var position = completionContext.Position;
var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
if (syntaxTree.IsInNonUserCode(position, cancellationToken) ||
syntaxTree.IsPreProcessorDirectiveContext(position, cancellationToken))
Expand All @@ -75,10 +76,8 @@ private static async Task<bool> ShouldShowSpeculativeTCompletionItemAsync(Docume
// We could be in the middle of a ref/generic/tuple type, instead of a simple T case.
// If we managed to walk out and get a different SpanStart, we treat it as a simple $$T case.

var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
var semanticModel = await document.ReuseExistingSpeculativeModelAsync(token.Parent, cancellationToken).ConfigureAwait(false);
var context = await completionContext.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false);

var context = CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken);
if (context.IsInTaskLikeTypeContext)
return false;

Expand All @@ -87,7 +86,7 @@ private static async Task<bool> ShouldShowSpeculativeTCompletionItemAsync(Docume
{
var oldSpanStart = spanStart;

spanStart = WalkOutOfGenericType(syntaxTree, spanStart, semanticModel, cancellationToken);
spanStart = WalkOutOfGenericType(syntaxTree, spanStart, context.SemanticModel, cancellationToken);
spanStart = WalkOutOfTupleType(syntaxTree, spanStart, cancellationToken);
spanStart = WalkOutOfRefType(syntaxTree, spanStart, cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers
{
Expand All @@ -41,12 +42,12 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC
try
{
var document = completionContext.Document;
var position = completionContext.Position;
var cancellationToken = completionContext.CancellationToken;

var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);
var context = await completionContext.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false) as CSharpSyntaxContext;
Contract.ThrowIfNull(context);

var context = CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken);
var semanticModel = context.SemanticModel;

var index = GetElementIndex(context);
if (index == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Recommendations;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
Expand Down Expand Up @@ -916,7 +917,9 @@ private async Task<SyntaxNode> GenerateInferredCallsiteExpressionAsync(
var recommender = document.GetRequiredLanguageService<IRecommendationService>();

var options = RecommendationServiceOptions.From(document.Project);
var recommendations = recommender.GetRecommendedSymbolsAtPosition(document, semanticModel, position, options, cancellationToken).NamedSymbols;

var context = document.GetRequiredLanguageService<ISyntaxContextService>().CreateContext(document, semanticModel, position, cancellationToken);
var recommendations = recommender.GetRecommendedSymbolsInContext(context, options, cancellationToken).NamedSymbols;

var sourceSymbols = recommendations.Where(r => r.IsNonImplicitAndFromSource());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ public sealed override async Task ProvideCompletionsAsync(CompletionContext cont
if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken))
return;

var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);
var syntaxContext = document.GetRequiredLanguageService<ISyntaxContextService>().CreateContext(document, semanticModel, position, cancellationToken);
var syntaxContext = await context.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false);

var isAwaitKeywordContext = IsAwaitKeywordContext(syntaxContext);
var dotAwaitContext = GetDotAwaitKeywordContext(syntaxContext, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,14 @@ public override async Task ProvideCompletionsAsync(CompletionContext context)
{
context.AddItems(await context.Document.GetUnionItemsFromDocumentAndLinkedDocumentsAsync(
s_comparer,
d => RecommendCompletionItemsAsync(d, context.Position, cancellationToken)).ConfigureAwait(false));
d => RecommendCompletionItemsAsync(d, context, cancellationToken)).ConfigureAwait(false));
}
}

private async Task<ImmutableArray<CompletionItem>> RecommendCompletionItemsAsync(Document document, int position, CancellationToken cancellationToken)
private async Task<ImmutableArray<CompletionItem>> RecommendCompletionItemsAsync(Document document, CompletionContext context, CancellationToken cancellationToken)
{
var syntaxContextService = document.GetRequiredLanguageService<ISyntaxContextService>();
var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);
var syntaxContext = (TContext)syntaxContextService.CreateContext(document, semanticModel, position, cancellationToken);
var position = context.Position;
var syntaxContext = (TContext)await context.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(false);
var keywords = await RecommendKeywordsAsync(document, position, syntaxContext, cancellationToken).ConfigureAwait(false);
return keywords.SelectAsArray(k => CreateItem(k, syntaxContext, cancellationToken));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ public sealed override async Task ProvideCompletionsAsync(CompletionContext cont
var cancellationToken = context.CancellationToken;
var originatingDocument = context.Document;
var position = context.Position;

var semanticModel = await originatingDocument.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false);
var service = originatingDocument.GetRequiredLanguageService<ISyntaxContextService>();
var solution = originatingDocument.Project.Solution;
var syntaxContext = service.CreateContext(originatingDocument, semanticModel, position, cancellationToken);
var syntaxContext = await context.GetSyntaxContextWithExistingSpeculativeModelAsync(originatingDocument, cancellationToken).ConfigureAwait(false);
if (!syntaxContext.IsPreProcessorExpressionContext)
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal abstract class AbstractRecommendationServiceBasedCompletionProvider<TSy
{
var recommendationOptions = options.ToRecommendationServiceOptions();
var recommender = context.GetRequiredLanguageService<IRecommendationService>();
var recommendedSymbols = recommender.GetRecommendedSymbolsAtPosition(context.Document, context.SemanticModel, position, recommendationOptions, cancellationToken);
var recommendedSymbols = recommender.GetRecommendedSymbolsInContext(context, recommendationOptions, cancellationToken);

if (context.IsInTaskLikeTypeContext)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ public Task<SyntaxContext> GetSyntaxContextAsync(Document document, Cancellation
if (_document.Id != document.Id && !_lazyRelatedDocumentIds.Value.Contains(document.Id))
throw new ArgumentException("Don't support getting SyntaxContext for document unrelated to the original document");

lazyContext = GetLazySyntaxContextWithSpeculativeModel(document);
lazyContext = GetLazySyntaxContextWithSpeculativeModel(document, this);
}

return lazyContext.GetValueAsync(cancellationToken);

// Extract a local function to avoid creating a closure for code path of cache hit.
AsyncLazy<SyntaxContext> GetLazySyntaxContextWithSpeculativeModel(Document document)
static AsyncLazy<SyntaxContext> GetLazySyntaxContextWithSpeculativeModel(Document document, SharedSyntaxContextsWithSpeculativeModel self)
{
return _cache.GetOrAdd(document, d => AsyncLazy.Create(cancellationToken
=> CompletionHelper.CreateSyntaxContextWithExistingSpeculativeModelAsync(d, _position, cancellationToken), cacheResult: true));
return self._cache.GetOrAdd(document, d => AsyncLazy.Create(cancellationToken
=> CompletionHelper.CreateSyntaxContextWithExistingSpeculativeModelAsync(d, self._position, cancellationToken), cacheResult: true));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets
Return
End If

Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False)
Dim syntaxContext = VisualBasicSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken)
Dim syntaxContext = Await context.GetSyntaxContextWithExistingSpeculativeModelAsync(document, cancellationToken).ConfigureAwait(False)
If syntaxContext.IsInTaskLikeTypeContext Then
Return
End If
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ public CSharpRecommendationService()
{
}

protected override CSharpSyntaxContext CreateContext(Document document, SemanticModel semanticModel, int position, CancellationToken cancellationToken)
=> CSharpSyntaxContext.CreateContext(document, semanticModel, position, cancellationToken);

protected override AbstractRecommendationServiceRunner CreateRunner(CSharpSyntaxContext context, bool filterOutOfScopeLocals, CancellationToken cancellationToken)
=> new CSharpRecommendationServiceRunner(context, filterOutOfScopeLocals, cancellationToken);
}
Expand Down
Loading

0 comments on commit 69446b8

Please sign in to comment.