提交 5734bdcf 编写于 作者: C CyrusNajmabadi

Merge pull request #11413 from CyrusNajmabadi/delayLoadCompletion

Don't preload completion providers we don't need.
......@@ -15,5 +15,6 @@
<!-- See above comment under CSharp -->
<Rule Id="RS0003" Action="None" />
<Rule Id="RS0007" Action="None" />
<Rule Id="RS0005" Action="None" />
</Rules>
</RuleSet>
\ No newline at end of file
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
......@@ -8,7 +9,6 @@
using Microsoft.CodeAnalysis.CSharp.Completion;
using Microsoft.CodeAnalysis.Editor.UnitTests.Completion;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
using Xunit;
......@@ -20,6 +20,12 @@ protected AbstractCSharpCompletionProviderTests(CSharpTestWorkspaceFixture works
{
}
internal override CompletionServiceWithProviders CreateCompletionService(
Workspace workspace, ImmutableArray<CompletionProvider> exclusiveProviders)
{
return new CSharpCompletionService(workspace, exclusiveProviders);
}
protected override async Task VerifyWorkerAsync(string code, int position, string expectedItemOrNull, string expectedDescriptionOrNull, SourceCodeKind sourceCodeKind, bool usePreviousCharAsTrigger, bool checkForAbsence, bool experimental, int? glyph)
{
await VerifyAtPositionAsync(code, position, usePreviousCharAsTrigger, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, checkForAbsence, experimental, glyph);
......@@ -138,14 +144,14 @@ protected async Task VerifySendEnterThroughToEnterAsync(string initialMarkup, st
var document = workspace.CurrentSolution.GetDocument(documentId);
var position = hostDocument.CursorPosition.Value;
var completionList = await GetCompletionListAsync(document, position, CompletionTrigger.Default);
var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar));
workspace.Options = workspace.Options.WithChangedOption(
CSharpCompletionOptions.AddNewLineOnEnterAfterFullyTypedWord, sendThroughEnterEnabled);
var optionService = workspace.Services.GetService<IOptionService>();
var options = optionService.GetOptions().WithChangedOption(CSharpCompletionOptions.AddNewLineOnEnterAfterFullyTypedWord, sendThroughEnterEnabled);
optionService.SetOptions(options);
var service = GetCompletionService(workspace);
var completionList = await GetCompletionListAsync(service, document, position, CompletionTrigger.Default);
var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar));
var completionRules = CompletionHelper.GetHelper(document);
var completionRules = CompletionHelper.GetHelper(document, service);
Assert.Equal(expected, completionRules.SendEnterThroughToEditor(item, textTypedSoFar, workspace.Options));
}
}
......@@ -166,7 +172,8 @@ private async Task VerifyTextualTriggerCharacterWorkerAsync(string markup, bool
var options = workspace.Options.WithChangedOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.CSharp, triggerOnLetter);
var trigger = CompletionTrigger.CreateInsertionTrigger(text[position]);
var isTextualTriggerCharacterResult = CompletionProvider.ShouldTriggerCompletion(text, position + 1, trigger, options);
var service = GetCompletionService(workspace);
var isTextualTriggerCharacterResult = service.ShouldTriggerCompletion(text, position + 1, trigger, options: options);
if (expectedTriggerCharacter)
{
......@@ -205,10 +212,11 @@ protected async Task VerifyCommitCharactersAsync(string initialMarkup, string te
var document = workspace.CurrentSolution.GetDocument(documentId);
var position = hostDocument.CursorPosition.Value;
var completionList = await GetCompletionListAsync(document, position, CompletionTrigger.Default);
var service = GetCompletionService(workspace);
var completionList = await GetCompletionListAsync(service, document, position, CompletionTrigger.Default);
var item = completionList.Items.First(i => i.DisplayText.StartsWith(textTypedSoFar));
var completionRules = CompletionHelper.GetHelper(document);
var completionRules = CompletionHelper.GetHelper(document, service);
foreach (var ch in validChars)
{
......
......@@ -434,7 +434,8 @@ class C
var provider = new CrefCompletionProvider();
var hostDocument = workspace.DocumentWithCursor;
var document = workspace.CurrentSolution.GetDocument(hostDocument.Id);
var completionList = await GetCompletionListAsync(provider, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default);
var service = GetCompletionService(workspace);
var completionList = await GetCompletionListAsync(service, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default);
}
}
......
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp.Completion.Providers;
......@@ -576,10 +575,11 @@ void foo()
var document = workspace.CurrentSolution.GetDocument(hostDocument.Id);
var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a');
var completionList = await GetCompletionListAsync(document, position, triggerInfo);
var service = GetCompletionService(workspace);
var completionList = await GetCompletionListAsync(service, document, position, triggerInfo);
var item = completionList.Items.First();
var completionRules = CompletionHelper.GetHelper(document);
var completionRules = CompletionHelper.GetHelper(document, service);
Assert.False(completionRules.SendEnterThroughToEditor(item, string.Empty, workspace.Options), "Expected false from SendEnterThroughToEditor()");
}
......@@ -777,7 +777,8 @@ private async Task VerifyExclusiveAsync(string markup, bool exclusive)
var document = workspace.CurrentSolution.GetDocument(hostDocument.Id);
var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a');
var completionList = await GetCompletionListContextAsync(document, position, triggerInfo);
var service = GetCompletionService(workspace);
var completionList = await GetCompletionListAsync(service, document, position, triggerInfo);
if (completionList != null)
{
......
......@@ -2126,10 +2126,11 @@ public override void set_Bar(int bay, int value)
var document = solution.GetDocument(documentId);
var triggerInfo = CompletionTrigger.Default;
var completionList = await GetCompletionListAsync(document, position, triggerInfo);
var service = GetCompletionService(testWorkspace);
var completionList = await GetCompletionListAsync(service, document, position, triggerInfo);
var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Bar[int bay]"));
var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider;
var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider;
if (customCommitCompletionProvider != null)
{
var textView = testWorkspace.GetTestDocument(documentId).GetTextView();
......@@ -2385,10 +2386,11 @@ public override bool Equals(object obj)
var document = solution.GetDocument(documentId);
var triggerInfo = CompletionTrigger.Default;
var completionList = await GetCompletionListAsync(document, position, triggerInfo);
var service = GetCompletionService(testWorkspace);
var completionList = await GetCompletionListAsync(service, document, position, triggerInfo);
var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Equals(object obj)"));
var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider;
var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider;
if (customCommitCompletionProvider != null)
{
var textView = testWorkspace.GetTestDocument(documentId).GetTextView();
......@@ -2443,10 +2445,11 @@ public override bool Equals(object obj)
var document = solution.GetDocument(documentId);
var triggerInfo = CompletionTrigger.Default;
var completionList = await GetCompletionListAsync(document, cursorPosition, triggerInfo);
var service = GetCompletionService(testWorkspace);
var completionList = await GetCompletionListAsync(service, document, cursorPosition, triggerInfo);
var completionItem = completionList.Items.First(i => CompareItems(i.DisplayText, "Equals(object obj)"));
var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider;
var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider;
if (customCommitCompletionProvider != null)
{
var textView = testWorkspace.GetTestDocument(documentId).GetTextView();
......@@ -2541,20 +2544,24 @@ static void Main(string[] args)
override $$
}
}";
var workspace = await TestWorkspace.CreateAsync(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), text);
var provider = new OverrideCompletionProvider();
var testDocument = workspace.Documents.Single();
var document = workspace.CurrentSolution.GetDocument(testDocument.Id);
var completionList = await GetCompletionListAsync(provider, document, testDocument.CursorPosition.Value, CompletionTrigger.Default);
using (var workspace = await TestWorkspace.CreateAsync(LanguageNames.CSharp, new CSharpCompilationOptions(OutputKind.ConsoleApplication), new CSharpParseOptions(), text))
{
var provider = new OverrideCompletionProvider();
var testDocument = workspace.Documents.Single();
var document = workspace.CurrentSolution.GetDocument(testDocument.Id);
var service = GetCompletionService(workspace);
var completionList = await GetCompletionListAsync(service, document, testDocument.CursorPosition.Value, CompletionTrigger.Default);
var oldTree = await document.GetSyntaxTreeAsync();
var oldTree = await document.GetSyntaxTreeAsync();
var commit = await provider.GetChangeAsync(document, completionList.Items.First(i => i.DisplayText == "ToString()"), ' ');
var changes = commit.TextChanges;
var commit = await provider.GetChangeAsync(document, completionList.Items.First(i => i.DisplayText == "ToString()"), ' ');
var changes = commit.TextChanges;
// If we left the trailing trivia of the close curly of Main alone,
// there should only be one change: the replacement of "override " with a method.
Assert.Equal(changes.Single().Span, TextSpan.FromBounds(136, 145));
// If we left the trailing trivia of the close curly of Main alone,
// there should only be one change: the replacement of "override " with a method.
Assert.Equal(changes.Single().Span, TextSpan.FromBounds(136, 145));
}
}
}
}
\ No newline at end of file
......@@ -580,7 +580,10 @@ private async Task VerifyWorkerAsync(string markup, bool isBuilder)
private async Task CheckResultsAsync(Document document, int position, bool isBuilder)
{
var triggerInfo = CompletionTrigger.CreateInsertionTrigger('a');
var completionList = await GetCompletionListAsync(document, position, triggerInfo);
var service = GetCompletionService(document.Project.Solution.Workspace);
var completionList = await service.GetContextAsync(
service.ExclusiveProviders?[0], document, position, triggerInfo,
options: null, cancellationToken: CancellationToken.None);
if (isBuilder)
{
......
......@@ -29,7 +29,8 @@ protected CompletionHelper(CompletionService completionService)
_rules = CompletionService.GetRules();
}
public static CompletionHelper GetHelper(Workspace workspace, string language)
public static CompletionHelper GetHelper(
Workspace workspace, string language, CompletionService completionService)
{
var ls = workspace.Services.GetLanguageServices(language);
if (ls != null)
......@@ -37,10 +38,9 @@ public static CompletionHelper GetHelper(Workspace workspace, string language)
var factory = ls.GetService<CompletionHelperFactory>();
if (factory != null)
{
return factory.CreateCompletionHelper();
return factory.CreateCompletionHelper(completionService);
}
var completionService = ls.GetService<CompletionService>();
if (completionService != null)
{
return new CompletionHelper(completionService);
......@@ -50,9 +50,9 @@ public static CompletionHelper GetHelper(Workspace workspace, string language)
return null;
}
public static CompletionHelper GetHelper(Document document)
public static CompletionHelper GetHelper(Document document, CompletionService service)
{
return GetHelper(document.Project.Solution.Workspace, document.Project.Language);
return GetHelper(document.Project.Solution.Workspace, document.Project.Language, service);
}
public IReadOnlyList<TextSpan> GetHighlightedSpans(CompletionItem completionItem, string filterText)
......@@ -446,9 +446,10 @@ protected bool IsObjectCreationItem(CompletionItem item)
return item.Tags.Contains(CompletionTags.ObjectCreation);
}
public static async Task<TextChange> GetTextChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken))
public static async Task<TextChange> GetTextChangeAsync(
CompletionService service, Document document, CompletionItem item,
char? commitKey = null, CancellationToken cancellationToken = default(CancellationToken))
{
var service = CompletionService.GetService(document);
var change = await service.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false);
// normally the items that produce multiple changes are not expecting to trigger the behaviors that rely on looking at the text
......
// 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 Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Editor
{
internal abstract class CompletionHelperFactory : ILanguageService
{
public abstract CompletionHelper CreateCompletionHelper();
public abstract CompletionHelper CreateCompletionHelper(CompletionService completionService);
}
}
\ No newline at end of file
// 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.Threading;
using Microsoft.CodeAnalysis.Editor.Commands;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion
{
......
......@@ -13,6 +13,7 @@
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
using System.Threading;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion
{
......@@ -273,7 +274,8 @@ private CompletionHelper GetCompletionHelper()
var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
return CompletionHelper.GetHelper(document);
return CompletionHelper.GetHelper(
document, document.GetLanguageService<CompletionService>());
}
return null;
......
......@@ -36,7 +36,7 @@ public override async Task<CompletionDescription> GetDescriptionAsync(Document d
var description = await this.CompletionService.GetDescriptionAsync(document, this.Item, cancellationToken).ConfigureAwait(false);
var parts = description.TaggedParts;
var change = await CompletionHelper.GetTextChangeAsync(document, this.Item, '\t').ConfigureAwait(false);
var change = await CompletionHelper.GetTextChangeAsync(this.CompletionService, document, this.Item, '\t').ConfigureAwait(false);
var insertionText = change.NewText;
var note = string.Empty;
......
......@@ -12,7 +12,6 @@
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using VSCompletion = Microsoft.VisualStudio.Language.Intellisense.Completion;
using Microsoft.CodeAnalysis.Snippets;
namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation
{
......@@ -205,7 +204,8 @@ private CompletionHelper GetCompletionHelper()
var document = _subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
_completionHelper = CompletionHelper.GetHelper(document);
_completionHelper = CompletionHelper.GetHelper(document,
document.Project.LanguageServices.GetService<CompletionService>());
}
}
......
......@@ -28,7 +28,6 @@ public abstract class AbstractCompletionProviderTests<TWorkspaceFixture> : TestB
where TWorkspaceFixture : TestWorkspaceFixture, new()
{
protected readonly Mock<ICompletionSession> MockCompletionSession;
internal CompletionProvider CompletionProvider;
protected TWorkspaceFixture WorkspaceFixture;
protected AbstractCompletionProviderTests(TWorkspaceFixture workspaceFixture)
......@@ -36,7 +35,6 @@ protected AbstractCompletionProviderTests(TWorkspaceFixture workspaceFixture)
MockCompletionSession = new Mock<ICompletionSession>(MockBehavior.Strict);
this.WorkspaceFixture = workspaceFixture;
this.CompletionProvider = CreateCompletionProvider();
}
public override void Dispose()
......@@ -53,20 +51,28 @@ protected static async Task<bool> CanUseSpeculativeSemanticModelAsync(Document d
return !service.GetMemberBodySpanForSpeculativeBinding(node).IsEmpty;
}
internal static CompletionService GetCompletionService(Document document)
internal CompletionServiceWithProviders GetCompletionService(Workspace workspace)
{
return CompletionService.GetService(document);
return CreateCompletionService(workspace, ImmutableArray.Create(CreateCompletionProvider()));
}
internal static CompletionHelper GetCompletionHelper(Document document)
internal abstract CompletionServiceWithProviders CreateCompletionService(
Workspace workspace, ImmutableArray<CompletionProvider> exclusiveProviders);
internal static CompletionHelper GetCompletionHelper(Document document, CompletionService service)
{
return CompletionHelper.GetHelper(document);
return CompletionHelper.GetHelper(document, service);
}
internal static async Task<CompletionContext> GetCompletionListContextAsync(CompletionProvider provider, Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null)
internal static async Task<CompletionContext> GetCompletionListContextAsync(
CompletionService service,
CompletionProvider provider,
Document document,
int position,
CompletionTrigger triggerInfo,
OptionSet options = null)
{
options = options ?? document.Project.Solution.Workspace.Options;
var service = document.Project.LanguageServices.GetService<CompletionService>();
var text = await document.GetTextAsync();
var span = service.GetDefaultItemSpan(text, position);
var context = new CompletionContext(provider, document, position, span, triggerInfo, options, CancellationToken.None);
......@@ -74,26 +80,15 @@ internal static async Task<CompletionContext> GetCompletionListContextAsync(Comp
return context;
}
internal Task<CompletionContext> GetCompletionListContextAsync(Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null)
{
return GetCompletionListContextAsync(this.CompletionProvider, document, position, triggerInfo, options);
}
internal static async Task<CompletionList> GetCompletionListAsync(CompletionProvider provider, Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null)
{
var service = GetCompletionService(document);
var context = await GetCompletionListContextAsync(provider, document, position, triggerInfo, options);
var text = await document.GetTextAsync();
var span = service.GetDefaultItemSpan(text, position);
return CompletionList.Create(span, context.Items.ToImmutableArray(), service.GetRules(), context.SuggestionModeItem);
}
internal Task<CompletionList> GetCompletionListAsync(Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null)
internal Task<CompletionList> GetCompletionListAsync(
CompletionService service,
Document document, int position, CompletionTrigger triggerInfo, OptionSet options = null)
{
return GetCompletionListAsync(this.CompletionProvider, document, position, triggerInfo, options);
return service.GetCompletionsAsync(document, position, triggerInfo, options: options);
}
private async Task CheckResultsAsync(Document document, int position, string expectedItemOrNull, string expectedDescriptionOrNull, bool usePreviousCharAsTrigger, bool checkForAbsence, Glyph? glyph)
private async Task CheckResultsAsync(
Document document, int position, string expectedItemOrNull, string expectedDescriptionOrNull, bool usePreviousCharAsTrigger, bool checkForAbsence, Glyph? glyph)
{
var code = (await document.GetTextAsync()).ToString();
......@@ -104,8 +99,8 @@ private async Task CheckResultsAsync(Document document, int position, string exp
trigger = CompletionTrigger.CreateInsertionTrigger(insertedCharacter: code.ElementAt(position - 1));
}
var completionList = await GetCompletionListAsync(document, position, trigger);
var completionService = document.Project.LanguageServices.GetService<CompletionService>();
var completionService = GetCompletionService(document.Project.Solution.Workspace);
var completionList = await GetCompletionListAsync(completionService, document, position, trigger);
var items = completionList == null ? default(ImmutableArray<CompletionItem>) : completionList.Items;
if (checkForAbsence)
......@@ -306,25 +301,28 @@ protected virtual async Task VerifyCustomCommitProviderWorkerAsync(string codeBe
private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document, string codeBeforeCommit, int position, string itemToCommit, string expectedCodeAfterCommit, char? commitChar)
{
var textBuffer = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().TextBuffer;
var workspace = await WorkspaceFixture.GetWorkspaceAsync();
var textBuffer = workspace.Documents.Single().TextBuffer;
var items = (await GetCompletionListAsync(document, position, CompletionTrigger.Default)).Items;
var service = GetCompletionService(workspace);
var items = (await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)).Items;
var firstItem = items.First(i => CompareItems(i.DisplayText, itemToCommit));
var customCommitCompletionProvider = CompletionProvider as ICustomCommitCompletionProvider;
var customCommitCompletionProvider = service.ExclusiveProviders?[0] as ICustomCommitCompletionProvider;
if (customCommitCompletionProvider != null)
{
var completionRules = GetCompletionHelper(document);
var completionRules = GetCompletionHelper(document, service);
var textView = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().GetTextView();
VerifyCustomCommitWorker(customCommitCompletionProvider, firstItem, completionRules, textView, textBuffer, codeBeforeCommit, expectedCodeAfterCommit, commitChar);
}
else
{
await VerifyCustomCommitWorkerAsync(document, firstItem, codeBeforeCommit, expectedCodeAfterCommit, commitChar);
await VerifyCustomCommitWorkerAsync(service, document, firstItem, codeBeforeCommit, expectedCodeAfterCommit, commitChar);
}
}
internal virtual async Task VerifyCustomCommitWorkerAsync(
internal async Task VerifyCustomCommitWorkerAsync(
CompletionServiceWithProviders service,
Document document,
CompletionItem completionItem,
string codeBeforeCommit,
......@@ -335,7 +333,7 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document
string actualExpectedCode = null;
MarkupTestFile.GetPosition(expectedCodeAfterCommit, out actualExpectedCode, out expectedCaretPosition);
CompletionHelper completionRules = GetCompletionHelper(document);
CompletionHelper completionRules = GetCompletionHelper(document, service);
if (commitChar.HasValue && !completionRules.IsCommitCharacter(completionItem, commitChar.Value, string.Empty))
{
......@@ -343,10 +341,9 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document
return;
}
var service = CompletionService.GetService(document);
var commit = service.GetChangeAsync(document, completionItem, commitChar).Result;
var commit = await service.GetChangeAsync(document, completionItem, commitChar, CancellationToken.None);
var text = document.GetTextAsync().Result;
var text = await document.GetTextAsync();
var newText = text.WithChanges(commit.TextChanges);
var newDoc = document.WithText(newText);
document.Project.Solution.Workspace.TryApplyChanges(newDoc.Project.Solution);
......@@ -412,20 +409,22 @@ private async Task VerifyCustomCommitProviderCheckResultsAsync(Document document
private async Task VerifyProviderCommitCheckResultsAsync(Document document, int position, string itemToCommit, string expectedCodeAfterCommit, char? commitCharOpt, string textTypedSoFar)
{
var textBuffer = (await WorkspaceFixture.GetWorkspaceAsync()).Documents.Single().TextBuffer;
var workspace = await WorkspaceFixture.GetWorkspaceAsync();
var textBuffer = workspace.Documents.Single().TextBuffer;
var textSnapshot = textBuffer.CurrentSnapshot.AsText();
var items = (await GetCompletionListAsync(document, position, CompletionTrigger.Default)).Items;
var service = GetCompletionService(workspace);
var items = (await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)).Items;
var firstItem = items.First(i => CompareItems(i.DisplayText, itemToCommit));
var completionRules = GetCompletionHelper(document);
var completionRules = GetCompletionHelper(document, service);
var commitChar = commitCharOpt ?? '\t';
var text = await document.GetTextAsync();
if (commitChar == '\t' || completionRules.IsCommitCharacter(firstItem, commitChar, textTypedSoFar))
{
var textChange = CompletionHelper.GetTextChangeAsync(document, firstItem, commitChar).Result;
var textChange = CompletionHelper.GetTextChangeAsync(service, document, firstItem, commitChar).Result;
// Adjust TextChange to include commit character, so long as it isn't TAB.
if (commitChar != '\t')
......@@ -445,11 +444,10 @@ private async Task VerifyProviderCommitCheckResultsAsync(Document document, int
Assert.Equal(expectedCodeAfterCommit, text.ToString());
}
protected async Task VerifyItemInEditorBrowsableContextsAsync(string markup, string referencedCode, string item, int expectedSymbolsSameSolution, int expectedSymbolsMetadataReference,
string sourceLanguage, string referencedLanguage, bool hideAdvancedMembers = false)
protected async Task VerifyItemInEditorBrowsableContextsAsync(
string markup, string referencedCode, string item, int expectedSymbolsSameSolution, int expectedSymbolsMetadataReference,
string sourceLanguage, string referencedLanguage, bool hideAdvancedMembers = false)
{
CompletionProvider = CreateCompletionProvider();
await VerifyItemWithMetadataReferenceAsync(markup, referencedCode, item, expectedSymbolsMetadataReference, sourceLanguage, referencedLanguage, hideAdvancedMembers);
await VerifyItemWithProjectReferenceAsync(markup, referencedCode, item, expectedSymbolsSameSolution, sourceLanguage, referencedLanguage, hideAdvancedMembers);
......@@ -538,7 +536,8 @@ private Task VerifyItemInSameProjectAsync(string markup, string referencedCode,
return VerifyItemWithReferenceWorkerAsync(xmlString, expectedItem, expectedSymbols, hideAdvancedMembers);
}
private async Task VerifyItemWithReferenceWorkerAsync(string xmlString, string expectedItem, int expectedSymbols, bool hideAdvancedMembers)
private async Task VerifyItemWithReferenceWorkerAsync(
string xmlString, string expectedItem, int expectedSymbols, bool hideAdvancedMembers)
{
using (var testWorkspace = await TestWorkspace.CreateAsync(xmlString))
{
......@@ -552,8 +551,8 @@ private async Task VerifyItemWithReferenceWorkerAsync(string xmlString, string e
var triggerInfo = CompletionTrigger.Default;
var completionList = await GetCompletionListAsync(document, position, triggerInfo);
var completionService = document.Project.LanguageServices.GetService<CompletionService>();
var completionService = GetCompletionService(testWorkspace);
var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo);
if (expectedSymbols >= 1)
{
......@@ -596,7 +595,8 @@ protected Task VerifyItemWithMscorlib45Async(string markup, string expectedItem,
return VerifyItemWithMscorlib45WorkerAsync(xmlString, expectedItem, expectedDescription);
}
private async Task VerifyItemWithMscorlib45WorkerAsync(string xmlString, string expectedItem, string expectedDescription)
private async Task VerifyItemWithMscorlib45WorkerAsync(
string xmlString, string expectedItem, string expectedDescription)
{
using (var testWorkspace = await TestWorkspace.CreateAsync(xmlString))
{
......@@ -606,8 +606,8 @@ private async Task VerifyItemWithMscorlib45WorkerAsync(string xmlString, string
var document = solution.GetDocument(documentId);
var triggerInfo = CompletionTrigger.Default;
var completionList = await GetCompletionListAsync(document, position, triggerInfo);
var completionService = document.Project.LanguageServices.GetService<CompletionService>();
var completionService = GetCompletionService(testWorkspace);
var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo);
var item = completionList.Items.FirstOrDefault(i => i.DisplayText == expectedItem);
Assert.Equal(expectedDescription, (await completionService.GetDescriptionAsync(document, item)).Text);
......@@ -638,8 +638,8 @@ protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expec
var document = solution.GetDocument(currentContextDocumentId);
var triggerInfo = CompletionTrigger.Default;
var completionList = await GetCompletionListAsync(document, position, triggerInfo);
var completionService = document.Project.LanguageServices.GetService<CompletionService>();
var completionService = GetCompletionService(testWorkspace);
var completionList = await GetCompletionListAsync(completionService, document, position, triggerInfo);
var item = completionList.Items.Single(c => c.DisplayText == expectedItem);
Assert.NotNull(item);
......
......@@ -47,7 +47,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Private Sub TestMatches(v As String, wordsToMatch() As String)
Using New CultureContext("tr-TR")
Dim helper = CompletionHelper.GetHelper(New TestWorkspace, LanguageNames.CSharp)
Dim workspace = New TestWorkspace
Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp,
workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService(Of CompletionService))
For Each word In wordsToMatch
Dim item = CompletionItem.Create(word)
Assert.True(helper.MatchesFilterText(item, v, CompletionTrigger.Default, CompletionFilterReason.TypeChar), $"Expected item {word} does not match {v}")
......@@ -57,7 +59,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Private Sub TestNotMatches(v As String, wordsToNotMatch() As String)
Using New CultureContext("tr-TR")
Dim helper = CompletionHelper.GetHelper(New TestWorkspace, LanguageNames.CSharp)
Dim workspace = New TestWorkspace
Dim helper = CompletionHelper.GetHelper(workspace, LanguageNames.CSharp,
workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService(Of CompletionService))
For Each word In wordsToNotMatch
Dim item = CompletionItem.Create(word)
Assert.False(helper.MatchesFilterText(item, v, CompletionTrigger.Default, CompletionFilterReason.TypeChar), $"Unexpected item {word} matches {v}")
......
......@@ -52,7 +52,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
Dim languageServices = Me.Workspace.CurrentSolution.Projects.First().LanguageServices
Dim language = languageServices.Language
If (extraCompletionProviders IsNot Nothing) Then
If extraCompletionProviders IsNot Nothing Then
Dim completionService = DirectCast(languageServices.GetService(Of CompletionService), CompletionServiceWithProviders)
completionService.SetTestProviders(extraCompletionProviders.Select(Function(lz) lz.Value).ToList())
End If
......
......@@ -13,21 +13,17 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion
Implements ILanguageServiceFactory
Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService
Dim completionService = languageServices.GetService(Of CompletionService)
Return New VisualBasicCompletionHelperFactory(completionService)
Return New VisualBasicCompletionHelperFactory()
End Function
Private Class VisualBasicCompletionHelperFactory
Inherits CompletionHelperFactory
Private _completionService As CompletionService
Public Sub New(completionService As CompletionService)
_completionService = completionService
Public Sub New()
End Sub
Public Overrides Function CreateCompletionHelper() As CompletionHelper
Return New VisualBasicCompletionHelper(_completionService)
Public Overrides Function CreateCompletionHelper(service As CompletionService) As CompletionHelper
Return New VisualBasicCompletionHelper(service)
End Function
End Class
End Class
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Completion
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Completion
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.CompletionProviders
Public MustInherit Class AbstractVisualBasicCompletionProviderTests
......@@ -14,6 +16,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet
MyBase.New(workspaceFixture)
End Sub
Friend Overrides Function CreateCompletionService(workspace As Workspace, exclusiveProviders As ImmutableArray(Of CompletionProvider)) As CompletionServiceWithProviders
Return New VisualBasicCompletionService(workspace, exclusiveProviders)
End Function
Protected Overrides Async Function VerifyWorkerAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, experimental As Boolean, glyph As Integer?) As Threading.Tasks.Task
' Script/interactive support removed for now.
' TODO: Re-enable these when interactive is back in the product.
......@@ -73,7 +79,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet
Await MyBase.VerifyWorkerAsync(code, position, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, experimental:=experimental, glyph:=glyph)
End Function
Private Function VerifyAtEndOfFileAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, glyph As Integer?, experimental As Boolean) As Threading.Tasks.Task
Protected Function VerifyAtEndOfFileAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, glyph As Integer?, experimental As Boolean) As Threading.Tasks.Task
Return VerifyAtEndOfFileAsync(code, position, String.Empty, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, glyph, experimental)
End Function
......@@ -111,10 +117,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet
Dim document = workspace.CurrentSolution.GetDocument(documentId)
Dim position = hostDocument.CursorPosition.Value
Dim completionList = Await GetCompletionListAsync(document, position, CompletionTrigger.Default)
Dim service = GetCompletionService(workspace)
Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)
Dim item = completionList.Items.First(Function(i) i.DisplayText.StartsWith(textTypedSoFar))
Dim helper = CompletionHelper.GetHelper(document)
Dim helper = CompletionHelper.GetHelper(document, service)
Assert.Equal(expected, helper.SendEnterThroughToEditor(item, textTypedSoFar, workspace.Options))
End Using
End Function
......@@ -131,10 +138,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet
Dim document = workspace.CurrentSolution.GetDocument(documentId)
Dim position = hostDocument.CursorPosition.Value
Dim completionList = Await GetCompletionListAsync(document, position, CompletionTrigger.Default)
Dim service = GetCompletionService(workspace)
Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)
Dim item = completionList.Items.First()
Dim helper = CompletionHelper.GetHelper(document)
Dim helper = CompletionHelper.GetHelper(document, service)
For Each ch In chars
Assert.True(helper.IsCommitCharacter(item, ch, textTypedSoFar), $"Expected '{ch}' to be a commit character")
......@@ -197,7 +205,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet
Dim text = document.TextBuffer.CurrentSnapshot.AsText()
Dim options = workspace.Options.WithChangedOption(CompletionOptions.TriggerOnTypingLetters, LanguageNames.VisualBasic, triggerOnLetter)
Dim trigger = CompletionTrigger.CreateInsertionTrigger(text(position))
Dim isTextualTriggerCharacterResult = CompletionProvider.ShouldTriggerCompletion(text, position + 1, trigger, options)
Dim completionService = GetCompletionService(workspace)
Dim isTextualTriggerCharacterResult = completionService.ShouldTriggerCompletion(
text, position + 1, trigger, options:=options)
If expectedTriggerCharacter Then
Dim assertText = "'" & text.ToString(New TextSpan(position, 1)) & "' expected to be textual trigger character"
......
......@@ -429,10 +429,10 @@ End Class]]></a>.Value.NormalizeLineEndings()
' This verifies that the provider is asking for a speculative SemanticModel
' by walking to the node the documentation is attached to.
Dim provider = New CrefCompletionProvider()
Dim hostDocument = workspace.DocumentWithCursor
Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id)
Dim completionList = Await GetCompletionListAsync(provider, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default)
Dim service = GetCompletionService(workspace)
Dim completionList = Await GetCompletionListAsync(service, document, hostDocument.CursorPosition.Value, CompletionTrigger.Default)
End Using
End Function
......
......@@ -643,10 +643,9 @@ End Interface
Using workspace = Await TestWorkspace.CreateAsync(element)
Dim position = workspace.Documents.Single().CursorPosition.Value
Dim document = workspace.CurrentSolution.GetDocument(workspace.Documents.Single().Id)
Dim completionList = Await GetCompletionListAsync(document, position, CompletionTrigger.Default)
Dim service = GetCompletionService(workspace)
Dim completionList = Await GetCompletionListAsync(service, document, position, CompletionTrigger.Default)
AssertEx.Any(completionList.Items, Function(c) c.DisplayText = "Workcover")
End Using
End Function
......
......@@ -406,8 +406,8 @@ End Program</Document>
Dim hostDocument = workspace.Documents.First()
Dim caretPosition = hostDocument.CursorPosition.Value
Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id)
Dim completionList = Await GetCompletionListContextAsync(document, caretPosition, CompletionTrigger.Default)
Dim service = GetCompletionService(workspace)
Dim completionList = Await GetCompletionListAsync(service, document, caretPosition, CompletionTrigger.Default)
Assert.True(completionList Is Nothing OrElse completionList.IsExclusive, "Expected always exclusive")
End Using
End Function
......
......@@ -1746,7 +1746,8 @@ public class C
Dim caretPosition = hostDocument.CursorPosition.Value
Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id)
Dim completionList = Await GetCompletionListAsync(document, caretPosition, CompletionTrigger.Default)
Dim service = GetCompletionService(workspace)
Dim completionList = Await GetCompletionListAsync(service, document, caretPosition, CompletionTrigger.Default)
Assert.False(completionList.Items.Any(Function(c) c.DisplayText = "e"))
End Using
End Function
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Xml.Linq
Imports Microsoft.CodeAnalysis.Completion
......@@ -328,20 +329,21 @@ End Class
Await CheckResultsAsync(document2, position, isBuilder, triggerInfo, options)
End If
End Using
End Function
Private Async Function CheckResultsAsync(document As Document, position As Integer, isBuilder As Boolean, triggerInfo As CompletionTrigger?, options As OptionSet) As Task
triggerInfo = If(triggerInfo, CompletionTrigger.CreateInsertionTrigger("a"c))
Dim completionList = Await GetCompletionListAsync(document, position, triggerInfo.Value, options)
Dim service = GetCompletionService(document.Project.Solution.Workspace)
Dim context = Await service.GetContextAsync(
service.ExclusiveProviders?(0), document, position, triggerInfo.Value, options, CancellationToken.None)
If isBuilder Then
Assert.NotNull(completionList)
Assert.NotNull(completionList.SuggestionModeItem)
Assert.NotNull(context)
Assert.NotNull(context.SuggestionModeItem)
Else
If completionList IsNot Nothing Then
Assert.True(completionList.SuggestionModeItem Is Nothing, "group.Builder = " & If(completionList.SuggestionModeItem IsNot Nothing, completionList.SuggestionModeItem.DisplayText, "null"))
If context IsNot Nothing Then
Assert.True(context.SuggestionModeItem Is Nothing, "group.Builder = " & If(context.SuggestionModeItem IsNot Nothing, context.SuggestionModeItem.DisplayText, "null"))
End If
End If
End Function
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.CompletionProviders
......@@ -17,6 +18,11 @@ Namespace Tests
Return New XmlDocCommentCompletionProvider()
End Function
Protected Overrides Async Function VerifyWorkerAsync(code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, sourceCodeKind As SourceCodeKind, usePreviousCharAsTrigger As Boolean, checkForAbsence As Boolean, experimental As Boolean, glyph As Integer?) As Task
Await VerifyAtPositionAsync(code, position, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, glyph, experimental)
Await VerifyAtEndOfFileAsync(code, position, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, glyph, experimental)
End Function
Private Async Function VerifyItemsExistAsync(markup As String, ParamArray items() As String) As Task
For Each item In items
Await VerifyItemExistsAsync(markup, item)
......@@ -340,7 +346,9 @@ Class C
End Class
"
Await VerifyItemExistsAsync(text, "foo", usePreviousCharAsTrigger:=True)
Await VerifyItemExistsAsync(
text, "foo",
usePreviousCharAsTrigger:=True)
End Function
<WorkItem(638805, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638805")>
......
......@@ -50,8 +50,9 @@ internal class CSharpCompletionService : CommonCompletionService
private readonly Workspace _workspace;
public CSharpCompletionService(Workspace workspace)
: base(workspace)
public CSharpCompletionService(
Workspace workspace, ImmutableArray<CompletionProvider>? exclusiveProviders = null)
: base(workspace, exclusiveProviders)
{
_workspace = workspace;
}
......
// 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 System.Threading;
using System.Threading.Tasks;
......
......@@ -20,8 +20,10 @@ namespace Microsoft.CodeAnalysis.Completion
{
internal abstract partial class CommonCompletionService : CompletionServiceWithProviders
{
protected CommonCompletionService(Workspace workspace)
: base(workspace)
protected CommonCompletionService(
Workspace workspace,
ImmutableArray<CompletionProvider>? exclusiveProviders)
: base(workspace, exclusiveProviders)
{
}
......
......@@ -36,12 +36,23 @@ public sealed class CompletionList
/// </summary>
public CompletionItem SuggestionModeItem { get; }
private CompletionList(TextSpan defaultSpan, ImmutableArray<CompletionItem> items, CompletionRules rules, CompletionItem suggestionModeItem)
/// <summary>
/// For testing purposes only.
/// </summary>
internal bool IsExclusive { get; }
private CompletionList(
TextSpan defaultSpan,
ImmutableArray<CompletionItem> items,
CompletionRules rules,
CompletionItem suggestionModeItem,
bool isExclusive)
{
this.DefaultSpan = defaultSpan;
this.Items = items.IsDefault ? ImmutableArray<CompletionItem>.Empty : items;
this.Rules = rules ?? CompletionRules.Default;
this.SuggestionModeItem = suggestionModeItem;
this.IsExclusive = isExclusive;
}
/// <summary>
......@@ -58,7 +69,18 @@ private CompletionList(TextSpan defaultSpan, ImmutableArray<CompletionItem> item
CompletionRules rules = null,
CompletionItem suggestionModeItem = null)
{
return new CompletionList(defaultSpan, FixItemSpans(items, defaultSpan), rules, suggestionModeItem);
return Create(defaultSpan, items, rules, suggestionModeItem, isExclusive: false);
}
internal static CompletionList Create(
TextSpan defaultSpan,
ImmutableArray<CompletionItem> items,
CompletionRules rules,
CompletionItem suggestionModeItem,
bool isExclusive)
{
return new CompletionList(
defaultSpan, FixItemSpans(items, defaultSpan), rules, suggestionModeItem, isExclusive);
}
private static ImmutableArray<CompletionItem> FixItemSpans(ImmutableArray<CompletionItem> items, TextSpan defaultSpan)
......@@ -130,7 +152,8 @@ public CompletionList WithSuggestionModeItem(CompletionItem suggestionModeItem)
/// <summary>
/// The default <see cref="CompletionList"/> returned when no items are found to populate the list.
/// </summary>
public static readonly CompletionList Empty
= new CompletionList(default(TextSpan), default(ImmutableArray<CompletionItem>), CompletionRules.Default, null);
public static readonly CompletionList Empty = new CompletionList(
default(TextSpan), default(ImmutableArray<CompletionItem>), CompletionRules.Default,
suggestionModeItem: null, isExclusive: false);
}
}
......@@ -20,15 +20,38 @@ namespace Microsoft.CodeAnalysis.Completion
/// <summary>
/// A subtype of <see cref="CompletionService"/> that aggregates completions from one or more <see cref="CompletionProvider"/>s.
/// </summary>
public abstract class CompletionServiceWithProviders : CompletionService
public abstract class CompletionServiceWithProviders : CompletionService, IEqualityComparer<ImmutableHashSet<string>>
{
private static readonly Func<string, List<CompletionItem>> s_createList = _ => new List<CompletionItem>();
private IEnumerable<Lazy<CompletionProvider, CompletionProviderMetadata>> _importedProviders;
private readonly object _gate = new object();
private readonly Dictionary<string, CompletionProvider> _nameToProvider = new Dictionary<string, CompletionProvider>();
private readonly Dictionary<ImmutableHashSet<string>, ImmutableArray<CompletionProvider>> _rolesToProviders;
private readonly Func<ImmutableHashSet<string>, ImmutableArray<CompletionProvider>> _createRoleProviders;
private readonly Workspace _workspace;
/// <summary>
/// Internal for testing purposes.
/// </summary>
internal readonly ImmutableArray<CompletionProvider>? ExclusiveProviders;
private IEnumerable<Lazy<CompletionProvider, CompletionProviderMetadata>> _importedProviders;
protected CompletionServiceWithProviders(Workspace workspace)
: this(workspace, exclusiveProviders: null)
{
}
internal CompletionServiceWithProviders(
Workspace workspace,
ImmutableArray<CompletionProvider>? exclusiveProviders = null)
{
_workspace = workspace;
ExclusiveProviders = exclusiveProviders;
_rolesToProviders = new Dictionary<ImmutableHashSet<string>, ImmutableArray<CompletionProvider>>(this);
_createRoleProviders = CreateRoleProviders;
}
public override CompletionRules GetRules()
......@@ -64,45 +87,53 @@ protected virtual ImmutableArray<CompletionProvider> GetBuiltInProviders()
}
private ImmutableArray<CompletionProvider> _testProviders = ImmutableArray<CompletionProvider>.Empty;
private object p;
internal void SetTestProviders(IEnumerable<CompletionProvider> testProviders)
{
_testProviders = testProviders != null ? testProviders.ToImmutableArray() : ImmutableArray<CompletionProvider>.Empty;
_lazyNameToProviderMap = null;
lock (_gate)
{
_testProviders = testProviders != null ? testProviders.ToImmutableArray() : ImmutableArray<CompletionProvider>.Empty;
_rolesToProviders.Clear();
_nameToProvider.Clear();
}
}
private class RoleProviders
private ImmutableArray<CompletionProvider> CreateRoleProviders(ImmutableHashSet<string> roles)
{
public ImmutableArray<CompletionProvider> Providers;
var providers = GetAllProviders(roles);
foreach (var provider in providers)
{
_nameToProvider[provider.Name] = provider;
}
return providers;
}
private readonly ConditionalWeakTable<ImmutableHashSet<string>, RoleProviders> _roleProviders
= new ConditionalWeakTable<ImmutableHashSet<string>, RoleProviders>();
private ImmutableArray<CompletionProvider> GetAllProviders(ImmutableHashSet<string> roles)
{
if (ExclusiveProviders.HasValue)
{
return ExclusiveProviders.Value;
}
var builtin = GetBuiltInProviders();
var imported = GetImportedProviders()
.Where(lz => lz.Metadata.Roles == null || lz.Metadata.Roles.Length == 0 || roles.Overlaps(lz.Metadata.Roles))
.Select(lz => lz.Value);
var providers = builtin.Concat(imported).Concat(_testProviders);
return providers.ToImmutableArray();
}
protected ImmutableArray<CompletionProvider> GetProviders(ImmutableHashSet<string> roles)
{
roles = roles ?? ImmutableHashSet<string>.Empty;
RoleProviders providers;
if (!_roleProviders.TryGetValue(roles, out providers))
{
providers = _roleProviders.GetValue(roles, _ =>
{
var builtin = GetBuiltInProviders();
var imported = GetImportedProviders()
.Where(lz => lz.Metadata.Roles == null || lz.Metadata.Roles.Length == 0 || roles.Overlaps(lz.Metadata.Roles))
.Select(lz => lz.Value);
return new RoleProviders { Providers = builtin.Concat(imported).ToImmutableArray() };
});
}
if (_testProviders.Length > 0)
lock (_gate)
{
return providers.Providers.Concat(_testProviders);
}
else
{
return providers.Providers;
return _rolesToProviders.GetOrAdd(roles, _createRoleProviders);
}
}
......@@ -118,47 +149,20 @@ protected virtual ImmutableArray<CompletionProvider> GetProviders(ImmutableHashS
}
}
private ImmutableDictionary<string, CompletionProvider> _lazyNameToProviderMap = null;
private ImmutableDictionary<string, CompletionProvider> NameToProviderMap
{
get
{
if (_lazyNameToProviderMap == null)
{
Interlocked.CompareExchange(ref _lazyNameToProviderMap, CreateNameToProviderMap(), null);
}
return _lazyNameToProviderMap;
}
}
private ImmutableDictionary<string, CompletionProvider> CreateNameToProviderMap()
{
var map = ImmutableDictionary<string, CompletionProvider>.Empty;
foreach (var provider in GetBuiltInProviders().Concat(GetImportedProviders().Select(lz => lz.Value)).Concat(_testProviders))
{
if (!map.ContainsKey(provider.Name))
{
map = map.Add(provider.Name, provider);
}
}
return map;
}
internal protected CompletionProvider GetProvider(CompletionItem item)
{
string name;
CompletionProvider provider;
CompletionProvider provider = null;
if (item.Properties.TryGetValue("Provider", out name)
&& this.NameToProviderMap.TryGetValue(name, out provider))
if (item.Properties.TryGetValue("Provider", out name))
{
return provider;
lock (_gate)
{
_nameToProvider.TryGetValue(name, out provider);
}
}
return null;
return provider;
}
public override async Task<CompletionList> GetCompletionsAsync(
......@@ -201,7 +205,9 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var completionLists = new List<CompletionContext>();
foreach (var provider in triggeredProviders)
{
var completionList = await GetProviderCompletionsAsync(provider, document, caretPosition, defaultItemSpan, trigger, options, cancellationToken).ConfigureAwait(false);
var completionList = await GetContextAsync(
provider, document, caretPosition, trigger,
options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
if (completionList != null)
{
completionLists.Add(completionList);
......@@ -214,7 +220,9 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
if (firstExclusiveList != null)
{
return MergeAndPruneCompletionLists(SpecializedCollections.SingletonEnumerable(firstExclusiveList), defaultItemSpan);
return MergeAndPruneCompletionLists(
SpecializedCollections.SingletonEnumerable(firstExclusiveList), defaultItemSpan,
isExclusive: true);
}
// If no exclusive providers provided anything, then go through the remaining
......@@ -234,7 +242,7 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var nonUsedNonExclusiveLists = new List<CompletionContext>();
foreach (var provider in nonUsedProviders)
{
var completionList = await GetProviderCompletionsAsync(provider, document, caretPosition, defaultItemSpan, trigger, options, cancellationToken).ConfigureAwait(false);
var completionList = await GetContextAsync(provider, document, caretPosition, trigger, options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
if (completionList != null && !completionList.IsExclusive)
{
nonUsedNonExclusiveLists.Add(completionList);
......@@ -251,10 +259,11 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
// groups are properly ordered based on the original providers.
allProvidersAndLists.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);
return MergeAndPruneCompletionLists(allProvidersAndLists, defaultItemSpan);
return MergeAndPruneCompletionLists(allProvidersAndLists, defaultItemSpan, isExclusive: false);
}
private CompletionList MergeAndPruneCompletionLists(IEnumerable<CompletionContext> completionLists, TextSpan contextSpan)
private CompletionList MergeAndPruneCompletionLists(
IEnumerable<CompletionContext> completionLists, TextSpan contextSpan, bool isExclusive)
{
var displayNameToItemsMap = new Dictionary<string, List<CompletionItem>>();
CompletionItem suggestionModeItem = null;
......@@ -282,7 +291,9 @@ private CompletionList MergeAndPruneCompletionLists(IEnumerable<CompletionContex
var totalItems = displayNameToItemsMap.Values.Flatten().ToList();
totalItems.Sort();
return CompletionList.Create(contextSpan, totalItems.ToImmutableArray(), this.GetRules(), suggestionModeItem);
return CompletionList.Create(
contextSpan, totalItems.ToImmutableArray(), this.GetRules(), suggestionModeItem,
isExclusive);
}
private void AddToDisplayMap(
......@@ -342,16 +353,38 @@ protected virtual CompletionItem GetBetterItem(CompletionItem item, CompletionIt
return result;
}
private static async Task<CompletionContext> GetProviderCompletionsAsync(
// Internal for testing purposes only.
internal async Task<CompletionContext> GetContextAsync(
CompletionProvider provider,
Document document,
int position,
TextSpan defaultFilterSpan,
CompletionTrigger triggerInfo,
OptionSet options,
CancellationToken cancellationToken)
{
var context = new CompletionContext(provider, document, position, defaultFilterSpan, triggerInfo, options, cancellationToken);
return await GetContextAsync(
provider, document, position, triggerInfo,
options, defaultSpan: null, cancellationToken: cancellationToken).ConfigureAwait(false);
}
private async Task<CompletionContext> GetContextAsync(
CompletionProvider provider,
Document document,
int position,
CompletionTrigger triggerInfo,
OptionSet options,
TextSpan? defaultSpan,
CancellationToken cancellationToken)
{
options = options ?? document.Project.Solution.Workspace.Options;
if (defaultSpan == null)
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
defaultSpan = this.GetDefaultItemSpan(text, position);
}
var context = new CompletionContext(provider, document, position, defaultSpan.Value, triggerInfo, options, cancellationToken);
await provider.ProvideCompletionsAsync(context).ConfigureAwait(false);
return context;
}
......@@ -386,7 +419,8 @@ public override bool ShouldTriggerCompletion(SourceText text, int caretPosition,
return providers.Any(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options));
}
public override async Task<CompletionChange> GetChangeAsync(Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken)
public override async Task<CompletionChange> GetChangeAsync(
Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken)
{
var provider = GetProvider(item);
if (provider != null)
......@@ -398,5 +432,39 @@ public override async Task<CompletionChange> GetChangeAsync(Document document, C
return CompletionChange.Create(ImmutableArray.Create(new TextChange(item.Span, item.DisplayText)));
}
}
bool IEqualityComparer<ImmutableHashSet<string>>.Equals(ImmutableHashSet<string> x, ImmutableHashSet<string> y)
{
if (x == y)
{
return true;
}
if (x.Count != y.Count)
{
return false;
}
foreach (var v in x)
{
if (!y.Contains(v))
{
return false;
}
}
return true;
}
int IEqualityComparer<ImmutableHashSet<string>>.GetHashCode(ImmutableHashSet<string> obj)
{
var hash = 0;
foreach (var o in obj)
{
hash += o.GetHashCode();
}
return hash;
}
}
}
......@@ -45,8 +45,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion
Private ReadOnly _workspace As Workspace
Public Sub New(workspace As Workspace)
MyBase.New(workspace)
Public Sub New(workspace As Workspace,
Optional exclusiveProviders As ImmutableArray(Of CompletionProvider) ? = Nothing)
MyBase.New(workspace, exclusiveProviders)
_workspace = workspace
End Sub
......
......@@ -40,7 +40,8 @@ private static ICurrentWorkingDirectoryDiscoveryService GetFileSystemDiscoverySe
private static readonly ImmutableArray<CharacterSetModificationRule> s_filterRules = ImmutableArray<CharacterSetModificationRule>.Empty;
private static readonly CompletionItemRules s_rules = CompletionItemRules.Create(filterCharacterRules: s_filterRules, commitCharacterRules: s_commitRules);
private static readonly CompletionItemRules s_rules = CompletionItemRules.Create(
filterCharacterRules: s_filterRules, commitCharacterRules: s_commitRules, enterKeyRule: EnterKeyRule.Never);
public override async Task ProvideCompletionsAsync(CompletionContext context)
{
......
......@@ -57,7 +57,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense
Dim languageServices = Me.Workspace.CurrentSolution.Projects.First().LanguageServices
Dim language = languageServices.Language
If (extraCompletionProviders IsNot Nothing) Then
If extraCompletionProviders IsNot Nothing Then
Dim completionService = DirectCast(languageServices.GetService(Of CompletionService), CommonCompletionService)
completionService.SetTestProviders(extraCompletionProviders.Select(Function(lz) lz.Value).ToList())
End If
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册