/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/CSharpTest/Completion/CompletionServiceTests.cs
234 строки
9 KB
Cyrus Najmabadi
Make sealed
26 мар 2025, 22:21
26 мар 2025, 22:21
d99b771
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion; [UseExportProvider] [Trait(Traits.Feature, Traits.Features.Completion)] public sealed class CompletionServiceTests { [Fact] public void AcquireCompletionService() { var workspace = new AdhocWorkspace(); var document = workspace .AddProject("TestProject", LanguageNames.CSharp) .AddDocument("TestDocument.cs", ""); var service = CompletionService.GetService(document); Assert.NotNull(service); } [Fact] public void FindCompletionProvider() { using var workspace = new TestWorkspace(composition: FeaturesTestCompositions.Features.AddParts(typeof(ThirdPartyCompletionProvider))); var text = SourceText.From("class C { }"); var document = workspace.CurrentSolution .AddProject("TestProject", "Assembly", LanguageNames.CSharp) .AddDocument("TestDocument.cs", text); var service = CompletionService.GetService(document); // Create an item with ProviderName set to ThirdPartyCompletionProvider // We should be able to find the provider object without calling into CompletionService for other operations. var item = CompletionItem.Create("ThirdPartyCompletionProviderItem"); item.ProviderName = typeof(ThirdPartyCompletionProvider).FullName; var provider = service.GetProvider(item, document.Project); Assert.True(provider is ThirdPartyCompletionProvider); } [ExportCompletionProvider(nameof(ThirdPartyCompletionProvider), LanguageNames.CSharp)] [ExtensionOrder(After = nameof(KeywordCompletionProvider))] [Shared] private sealed class ThirdPartyCompletionProvider : CompletionProvider { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public ThirdPartyCompletionProvider() { } public override Task ProvideCompletionsAsync(CompletionContext context) => Task.CompletedTask; public override bool ShouldTriggerCompletion(SourceText text, int caretPosition, CompletionTrigger trigger, OptionSet options) { Assert.Equal(1, options.GetOption(new OptionKey(ThirdPartyOption.Instance, LanguageNames.CSharp))); return true; } } private sealed class ThirdPartyOption : IOption { public static ThirdPartyOption Instance = new(); public string Feature => "TestOptions"; public string Name => "Option"; public Type Type => typeof(int); public object DefaultValue => 0; public bool IsPerLanguage => true; public ImmutableArray<OptionStorageLocation> StorageLocations => []; } /// <summary> /// Ensure that 3rd party can set options on solution and access them from within a custom completion provider. /// </summary> [Fact] public async Task PassThroughOptions1() { using var workspace = new TestWorkspace(composition: FeaturesTestCompositions.Features.AddParts(typeof(ThirdPartyCompletionProvider))); var text = SourceText.From("class C { }"); var document = workspace.CurrentSolution .AddProject("TestProject", "Assembly", LanguageNames.CSharp) .AddDocument("TestDocument.cs", text); var service = CompletionService.GetService(document); var options = new TestOptionSet(ImmutableDictionary<OptionKey, object>.Empty.Add(new OptionKey(ThirdPartyOption.Instance, LanguageNames.CSharp), 1)); service.ShouldTriggerCompletion(text, 1, CompletionTrigger.Invoke, options: options); #pragma warning disable RS0030 // Do not used banned APIs await service.GetCompletionsAsync(document, 1, CompletionTrigger.Invoke, options: options); #pragma warning restore } /// <summary> /// Ensure that 3rd party can set options on solution and access them from within a custom completion provider. /// </summary> [Fact] public async Task PassThroughOptions2() { using var workspace = new EditorTestWorkspace(composition: EditorTestCompositions.EditorFeatures.AddParts(typeof(ThirdPartyCompletionProvider))); var testDocument = new EditorTestHostDocument("class C {}"); var project = new EditorTestHostProject(workspace, testDocument, name: "project1"); workspace.AddTestProject(project); workspace.OpenDocument(testDocument.Id); Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions( workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(ThirdPartyOption.Instance, LanguageNames.CSharp), 1)))); var document = workspace.CurrentSolution.GetDocument(testDocument.Id); var text = await document.GetTextAsync(); var service = CompletionService.GetService(document); service.ShouldTriggerCompletion(text, 1, CompletionTrigger.Invoke, options: null); #pragma warning disable RS0030 // Do not used banned APIs await service.GetCompletionsAsync(document, 1, CompletionTrigger.Invoke, options: null); #pragma warning restore } [Theory, CombinatorialData] public async Task GettingCompletionListPerformSort(bool performSort) { var sourceMarkup = """ using System; namespace N { public class C { void M() { $$ } } } """; MarkupTestFile.GetPosition(sourceMarkup.NormalizeLineEndings(), out var source, out int? position); var workspace = new AdhocWorkspace(); var document = workspace .AddProject("TestProject", LanguageNames.CSharp) .AddDocument("TestDocument.cs", source); var completionService = document.GetLanguageService<CompletionService>(); var options = CompletionOptions.Default with { PerformSort = performSort }; var completionList = await completionService.GetCompletionsAsync(document, position.Value, options, OptionSet.Empty); var completionListManuallySorted = completionList.ItemsList.ToList(); completionListManuallySorted.Sort(); Assert.True(performSort == completionList.ItemsList.SequenceEqual(completionListManuallySorted)); } [Theory, CombinatorialData] public async Task GettingCompletionListShouldNotRunSourceGenerator(bool forkBeforeFreeze) { var sourceMarkup = """ using System; namespace N { public class C1 { $$ } } """; MarkupTestFile.GetPosition(sourceMarkup.NormalizeLineEndings(), out var source, out int? position); var generatorRanCount = 0; var generator = new CallbackGenerator(onInit: _ => { }, onExecute: _ => Interlocked.Increment(ref generatorRanCount)); using var workspace = WorkspaceTestUtilities.CreateWorkspaceWithPartialSemantics(); var analyzerReference = new TestGeneratorReference(generator); var project = SolutionUtilities.AddEmptyProject(workspace.CurrentSolution) .AddAnalyzerReference(analyzerReference) .AddDocument("Document1.cs", sourceMarkup, filePath: "Document1.cs").Project; Assert.True(workspace.SetCurrentSolution(_ => project.Solution, WorkspaceChangeKind.SolutionChanged)); var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); var completionService = document.GetLanguageService<CompletionService>(); Assert.Equal(0, generatorRanCount); if (forkBeforeFreeze) { // Forking before freezing means we'll have to do extra work to produce the final compilation, // but we should still not be running generators. document = document.WithText(SourceText.From(sourceMarkup.Replace("C1", "C2"))); } // We want to make sure import completion providers are also participating. var options = CompletionOptions.Default with { ShowItemsFromUnimportedNamespaces = true }; var completionList = await completionService.GetCompletionsAsync(document, position.Value, options, OptionSet.Empty); // We expect completion to run on frozen partial semantic, which won't run source generator. Assert.Equal(0, generatorRanCount); var expectedItem = forkBeforeFreeze ? "C2" : "C1"; Assert.True(completionList.ItemsList.Select(item => item.DisplayText).Contains(expectedItem)); } }