File: Completion\CompletionTests.cs
Web Access
Project: src\src\LanguageServer\ProtocolUnitTests\Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj (Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests)
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.LanguageServer.Handler.Completion;
using Microsoft.CodeAnalysis.Options;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
using LSP = Roslyn.LanguageServer.Protocol;
 
namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Completion;
 
public sealed class CompletionTests : AbstractLanguageServerProtocolTests
{
    private static readonly LSP.VSInternalClientCapabilities s_vsCompletionCapabilities = CreateCoreCompletionCapabilities();
 
    private static LSP.VSInternalClientCapabilities CreateCoreCompletionCapabilities()
        => new()
        {
            SupportsVisualStudioExtensions = true,
            TextDocument = new()
            {
                Completion = new VSInternalCompletionSetting()
                {
                    CompletionListSetting = new CompletionListSetting()
                    {
                        ItemDefaults = [CompletionCapabilityHelper.EditRangePropertyName],
                    },
                    CompletionItemKind = new(),
 
                    CompletionList = new VSInternalCompletionListSetting()
                    {
                        CommitCharacters = true,
                    }
                },
            },
        };
 
    public CompletionTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
    {
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsAsync_PromotesCommitCharactersToListAsync(bool mutatingLspWorkspace, bool isPublicDefaultCommitChars)
    {
        var itemDefaultArray = isPublicDefaultCommitChars
            ? new string[] { CompletionCapabilityHelper.EditRangePropertyName, CompletionCapabilityHelper.CommitCharactersPropertyName }
            : [CompletionCapabilityHelper.EditRangePropertyName];
 
        var clientCapabilities = new LSP.VSInternalClientCapabilities
        {
            SupportsVisualStudioExtensions = true,
            TextDocument = new LSP.TextDocumentClientCapabilities
            {
                Completion = new LSP.VSInternalCompletionSetting
                {
                    CompletionListSetting = new LSP.CompletionListSetting
                    {
                        ItemDefaults = itemDefaultArray
 
                    },
                    CompletionList = isPublicDefaultCommitChars ? null : new LSP.VSInternalCompletionListSetting { CommitCharacters = true }
                },
            }
        };
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, clientCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync(label: "A", kind: LSP.CompletionItemKind.Class, tags: ["Class", "Internal"],
            request: completionParams, document: document, commitCharacters: CompletionRules.Default.DefaultCommitCharacters).ConfigureAwait(false);
        var expectedCommitCharacters = expected.CommitCharacters;
 
        // Null out the commit characters since we're expecting the commit characters will be lifted onto the completion list.
        expected.CommitCharacters = null;
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
        var vsCompletionList = Assert.IsAssignableFrom<LSP.VSInternalCompletionList>(results);
 
        if (isPublicDefaultCommitChars)
            Assert.Equal(expectedCommitCharacters, vsCompletionList.ItemDefaults.CommitCharacters);
        else
            Assert.Equal(expectedCommitCharacters, vsCompletionList.CommitCharacters.Value.First);
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletions_PromotesNothingWhenNoCommitCharactersAsync(bool mutatingLspWorkspace)
    {
        var clientCapabilities = new LSP.VSInternalClientCapabilities
        {
            SupportsVisualStudioExtensions = true,
            TextDocument = new LSP.TextDocumentClientCapabilities
            {
                Completion = new LSP.VSInternalCompletionSetting
                {
                    CompletionListSetting = new LSP.CompletionListSetting
                    {
                        ItemDefaults = [CompletionCapabilityHelper.EditRangePropertyName]
                    },
                    CompletionList = new LSP.VSInternalCompletionListSetting
                    {
                        CommitCharacters = true,
                    }
                },
            }
        };
        var markup =
@"namespace M
{{|caret:|}
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, clientCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync(label: "A", kind: LSP.CompletionItemKind.Class, tags: ["Class", "Internal"],
            request: completionParams, document: document, commitCharacters: CompletionRules.Default.DefaultCommitCharacters).ConfigureAwait(false);
        var expectedCommitCharacters = expected.CommitCharacters;
 
        // Null out the commit characters since we're expecting the commit characters will be lifted onto the completion list.
        expected.CommitCharacters = null;
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.All(results.Items, item => Assert.Null(item.CommitCharacters));
        var vsCompletionList = Assert.IsAssignableFrom<LSP.VSInternalCompletionList>(results);
        Assert.Equal(expectedCommitCharacters, vsCompletionList.CommitCharacters.Value.First);
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync(label: "A", kind: LSP.CompletionItemKind.Class, tags: ["Class", "Internal"],
            request: completionParams, document: document, commitCharacters: null).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
        Assert.NotNull(results.ItemDefaults.EditRange);
    }
 
    [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1777096")]
    public async Task TestGetExtensionMethodCoreLsp(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M(A a)
    {
        a.{|caret:|}
    }
}
 
static class Extensions
{
    public static void Goo(this A a) { }
}
";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync(label: "Goo", kind: LSP.CompletionItemKind.Method, tags: ["ExtensionMethod", "Public"],
            request: completionParams, document: document, commitCharacters: null).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.Single(i => i.Label == "Goo"));
        Assert.NotNull(results.ItemDefaults.EditRange);
    }
 
    [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1777096")]
    public async Task TestGetExtensionMethodCoreVSLsp(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M(A a)
    {
        a.{|caret:|}
    }
}
 
static class Extensions
{
    public static void Goo(this A a) { }
}
";
 
        // If the client supports more completion kinds, then we can give a more precise answer.
        var capabilities = CreateCoreCompletionCapabilities();
        capabilities.TextDocument.Completion.CompletionItemKind.ValueSet = [LSP.CompletionItemKind.ExtensionMethod];
 
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, capabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync(label: "Goo", kind: LSP.CompletionItemKind.ExtensionMethod, tags: ["ExtensionMethod", "Public"],
            request: completionParams, document: document, commitCharacters: null).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.Single(i => i.Label == "Goo"));
        Assert.NotNull(results.ItemDefaults.EditRange);
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsTypingAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        A{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "A",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync(label: "A", kind: LSP.CompletionItemKind.Class, tags: ["Class", "Internal"],
            request: completionParams, document: document, commitCharacters: null).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsDoesNotIncludeUnimportedTypesAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var solution = testLspServer.TestWorkspace.CurrentSolution;
 
        // Make sure the unimported types option is on
        testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, true);
        testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(CompletionOptionsStorage.ForceExpandedCompletionIndexCreation, true);
 
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams);
        Assert.False(results.Items.Any(item => "Console" == item.Label));
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsUsesSnippetOptionAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    {|caret:|}
}";
 
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
 
        testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(CompletionOptionsStorage.SnippetsBehavior, LanguageNames.CSharp, SnippetsRule.NeverInclude);
 
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams);
        Assert.False(results.Items.Any(item => "ctor" == item.Label));
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsWithPreselectAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        A classA = new {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync("A", LSP.CompletionItemKind.Class, ["Class", "Internal"],
            completionParams, document, preselect: true, commitCharacters: ImmutableArray.Create(' ', '(', '[', '{', ';', '.')).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsIsInSuggestionMode(bool mutatingLspWorkspace)
    {
        var markup =
@"
using System.Collections.Generic;
using System.Linq; 
namespace M
{
    class Item
    {
        void M()
        {
            var items = new List<Item>();
            items.Count(i{|caret:|}
        }
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "i",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = (LSP.VSInternalCompletionList)await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.Items.Any());
        Assert.True(results.SuggestionMode);
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetDateAndTimeCompletionsAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
class A
{
    void M()
    {
        DateTime.Now.ToString(""{|caret:|});
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "\"",
            triggerKind: LSP.CompletionTriggerKind.TriggerCharacter);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync(
            label: "d", kind: LSP.CompletionItemKind.Text, tags: ["Text"], request: completionParams, document: document, sortText: "0000",
            labelDetails: new() { Description = "shortdate" }).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
    }
 
    [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/59453")]
    public async Task TestGetDateAndTimeCompletionOnGuid(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
class A
{
    void M()
    {
        Guid.NewGuid().ToString(""{|caret:|});
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "\"",
            triggerKind: LSP.CompletionTriggerKind.TriggerCharacter);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Null(results);
    }
 
    [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/50964")]
    public async Task TestGetRegexCompletionsAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System.Text.RegularExpressions;
class A
{
    void M()
    {
        new Regex(""{|caret:|}"");
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var solution = testLspServer.GetCurrentSolution();
        var document = solution.Projects.First().Documents.First();
 
        var defaultRange = new LSP.Range
        {
            Start = new LSP.Position { Line = 5, Character = 19 },
            End = new LSP.Position { Line = 5, Character = 19 }
        };
 
        var expected = await CreateCompletionItemAsync(
            label: @"\A", kind: LSP.CompletionItemKind.Text, tags: ["Text"], request: completionParams, document: document, textEditText: @"\\A",
            sortText: "0000", labelDetails: new() { Description = "startofstringonly" }).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
        Assert.Equal(defaultRange, results.ItemDefaults.EditRange);
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetRegexLiteralCompletionsAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System.Text.RegularExpressions;
class A
{
    void M()
    {
        new Regex(@""\{|caret:|}"");
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\\",
            triggerKind: LSP.CompletionTriggerKind.TriggerCharacter);
 
        var solution = testLspServer.GetCurrentSolution();
        var document = solution.Projects.First().Documents.First();
 
        var defaultRange = new LSP.Range
        {
            Start = new LSP.Position { Line = 5, Character = 21 },
            End = new LSP.Position { Line = 5, Character = 21 }
        };
 
        var expected = await CreateCompletionItemAsync(
            label: @"\A", kind: LSP.CompletionItemKind.Text, tags: ["Text"], request: completionParams, document: document,
            sortText: "0000", vsResolveTextEditOnCommit: true, labelDetails: new() { Description = "startofstringonly" }).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
        Assert.Equal(defaultRange, results.ItemDefaults.EditRange);
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetRegexCompletionsReplaceTextAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System.Text.RegularExpressions;
class A
{
    void M()
    {
        Regex r = new(""\\{|caret:|}"");
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "\\",
            triggerKind: LSP.CompletionTriggerKind.TriggerCharacter);
 
        var solution = testLspServer.GetCurrentSolution();
        var document = solution.Projects.First().Documents.First();
 
        var defaultRange = new LSP.Range
        {
            Start = new LSP.Position { Line = 5, Character = 25 },
            End = new LSP.Position { Line = 5, Character = 25 }
        };
 
        var expected = await CreateCompletionItemAsync(
            label: @"\A", kind: LSP.CompletionItemKind.Text, tags: ["Text"], request: completionParams, document: document,
            sortText: "0000", vsResolveTextEditOnCommit: true, labelDetails: new() { Description = "startofstringonly" }).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
        Assert.Equal(defaultRange, results.ItemDefaults.EditRange);
    }
 
    [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/50964")]
    public async Task TestGetRegexCompletionsWithoutItemDefaultSupportAsync(bool mutatingLspWorkspace)
    {
        var clientCapabilities = new LSP.VSInternalClientCapabilities
        {
            SupportsVisualStudioExtensions = true,
            TextDocument = new LSP.TextDocumentClientCapabilities
            {
                Completion = new LSP.VSInternalCompletionSetting
                {
                    CompletionListSetting = new LSP.CompletionListSetting
                    {
                        ItemDefaults = null,
                    },
 
                    CompletionList = new VSInternalCompletionListSetting
                    {
                        CommitCharacters = true,
                    }
                },
 
            }
        };
 
        var markup =
@"using System.Text.RegularExpressions;
class A
{
    void M()
    {
        new Regex(""{|caret:|}"");
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, clientCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var solution = testLspServer.GetCurrentSolution();
        var document = solution.Projects.First().Documents.First();
 
        var textEdit = GenerateTextEdit(@"\\A", startLine: 5, startChar: 19, endLine: 5, endChar: 19);
 
        var expected = await CreateCompletionItemAsync(
            label: @"\A", kind: LSP.CompletionItemKind.Text, tags: ["Text"], request: completionParams, document: document, textEdit: textEdit,
            sortText: "0000", labelDetails: new() { Description = "startofstringonly" }).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        AssertJsonEquals(expected, results.Items.First());
        Assert.Null(results.ItemDefaults);
    }
 
    [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/46694")]
    public async Task TestCompletionListCacheAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var cache = GetCompletionListCache(testLspServer);
        Assert.NotNull(cache);
 
        var testAccessor = cache.GetTestAccessor();
 
        // This test assumes that the maximum cache size is 3, and will have to modified if this number changes.
        Assert.True(testAccessor.MaximumCacheSize == 3);
 
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        // 1 item in cache
        await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        var completionList = cache.GetCachedEntry(0).CompletionList;
        Assert.NotNull(completionList);
        Assert.Single(testAccessor.GetCacheContents());
 
        // 2 items in cache
        await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        completionList = cache.GetCachedEntry(0).CompletionList;
        Assert.NotNull(completionList);
        completionList = cache.GetCachedEntry(1).CompletionList;
        Assert.NotNull(completionList);
        Assert.True(testAccessor.GetCacheContents().Count == 2);
 
        // 3 items in cache
        await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        completionList = cache.GetCachedEntry(0).CompletionList;
        Assert.NotNull(completionList);
        completionList = cache.GetCachedEntry(1).CompletionList;
        Assert.NotNull(completionList);
        completionList = cache.GetCachedEntry(2).CompletionList;
        Assert.NotNull(completionList);
        Assert.True(testAccessor.GetCacheContents().Count == 3);
 
        // Maximum size of cache (3) should not be exceeded - oldest item should be ejected
        await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        var cacheEntry = cache.GetCachedEntry(0);
        Assert.Null(cacheEntry);
        completionList = cache.GetCachedEntry(1).CompletionList;
        Assert.NotNull(completionList);
        completionList = cache.GetCachedEntry(2).CompletionList;
        Assert.NotNull(completionList);
        completionList = cache.GetCachedEntry(3).CompletionList;
        Assert.NotNull(completionList);
        Assert.True(testAccessor.GetCacheContents().Count == 3);
    }
 
    [Theory, CombinatorialData]
    public async Task TestGetCompletionsWithDeletionInvokeKindAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Deletion,
            triggerCharacter: "M",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var expected = await CreateCompletionItemAsync("A", LSP.CompletionItemKind.Class, ["Class", "Internal"],
            completionParams, document, commitCharacters: CompletionRules.Default.DefaultCommitCharacters).ConfigureAwait(false);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
 
        // By default, completion doesn't trigger on deletion.
        Assert.Null(results);
    }
 
    [Theory, CombinatorialData]
    public async Task TestDoNotProvideOverrideTextEditsOrInsertTextAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"abstract class A
{
    public abstract void M();
}
 
class B : A
{
    override {|caret:|}
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Null(results.Items.First().TextEdit);
        Assert.Null(results.Items.First().InsertText);
        Assert.True(((LSP.VSInternalCompletionItem)results.Items.First()).VsResolveTextEditOnCommit);
    }
 
    [Theory, CombinatorialData]
    public async Task TestDoNotProvidePartialMethodTextEditsOrInsertTextAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"partial class C
{
    partial void Method();
}
 
partial class C
{
    partial {|caret:|}
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Null(results.Items.First().TextEdit);
        Assert.Null(results.Items.First().InsertText);
    }
 
    [Theory, CombinatorialData]
    public async Task TestSoftSelectedItemsHaveNoCommitCharactersWithoutVSCapabilityAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System.Text.RegularExpressions;
class A
{
    void M()
    {
        new Regex(""[{|caret:|}"")
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "[",
            triggerKind: LSP.CompletionTriggerKind.TriggerCharacter);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.NotNull(results);
        Assert.NotEmpty(results.Items);
        Assert.All(results.Items, (item) => Assert.Empty(item.CommitCharacters));
    }
 
    [Theory, CombinatorialData]
    public async Task TestLargeCompletionListIsMarkedIncompleteAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M()
    {
        T{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "T",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
    }
 
    [Theory, CombinatorialData]
    public async Task TestIncompleteCompletionListContainsPreselectedItemAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    class W
    {
    }
    void M()
    {
        W someW = new {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var caretLocation = testLspServer.GetLocations("caret").Single();
 
        var completionParams = CreateCompletionParams(
            caretLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: " ",
            triggerKind: LSP.CompletionTriggerKind.TriggerCharacter);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        var itemW = results.Items.Single(item => item.Label == "W");
        Assert.True(itemW.Preselect);
    }
 
    [Theory, CombinatorialData]
    public async Task TestRequestForIncompleteListIsFilteredDownAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M()
    {
        T{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var caretLocation = testLspServer.GetLocations("caret").Single();
        await testLspServer.OpenDocumentAsync(caretLocation.Uri);
 
        var completionParams = CreateCompletionParams(
            caretLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "T",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Equal("T", results.Items.First().Label);
 
        await testLspServer.InsertTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, "a"));
 
        completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "a",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
 
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.True(results.Items.Length < 1000);
        Assert.Contains("ta", results.Items.First().Label, StringComparison.OrdinalIgnoreCase);
    }
 
    [Theory, CombinatorialData]
    public async Task TestIncompleteCompletionListFiltersWithPatternMatchingAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M()
    {
        T{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var caretLocation = testLspServer.GetLocations("caret").Single();
        await testLspServer.OpenDocumentAsync(caretLocation.Uri);
 
        var completionParams = CreateCompletionParams(
            caretLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "T",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Equal("T", results.Items.First().Label);
 
        await testLspServer.InsertTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, "C"));
 
        completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "C",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
 
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.True(results.Items.Length < 1000);
        Assert.Equal("TaiwanCalendar", results.Items.First().Label);
    }
 
    [Theory, CombinatorialData]
    public async Task TestIncompleteCompletionListWithDeletionAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M()
    {
        T{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var caretLocation = testLspServer.GetLocations("caret").Single();
        await testLspServer.OpenDocumentAsync(caretLocation.Uri);
 
        // Insert 'T' to make 'T' and trigger completion.
        var completionParams = CreateCompletionParams(
            caretLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "T",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Equal("T", results.Items.First().Label);
 
        // Insert 'ask' to make 'Task' and trigger completion.
        await testLspServer.InsertTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, "ask"));
        completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "k",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.True(results.Items.Length < 1000);
        Assert.Equal("Task", results.Items.First().Label);
 
        // Delete 'ask' to make 'T' and trigger completion on deletion.
        await testLspServer.DeleteTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, caretLocation.Range.End.Line, caretLocation.Range.End.Character + 3));
        completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Deletion,
            triggerCharacter: "a",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Equal("T", results.Items.First().Label);
 
        // Insert 'i' to make 'Ti' and trigger completion.
        await testLspServer.InsertTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, "i"));
        completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "i",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.True(results.Items.Length < 1000);
        Assert.Equal("Timeout", results.Items.First().Label);
    }
 
    [Theory, CombinatorialData]
    public async Task TestNewCompletionRequestDoesNotUseIncompleteListAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M()
    {
        T{|firstCaret:|}
    }
 
    void M2()
    {
        Console.WH{|secondCaret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var firstCaret = testLspServer.GetLocations("firstCaret").Single();
        await testLspServer.OpenDocumentAsync(firstCaret.Uri);
 
        // Make a completion request that returns an incomplete list.
        var completionParams = CreateCompletionParams(
            firstCaret,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "T",
            triggerKind: LSP.CompletionTriggerKind.TriggerCharacter);
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Contains(results.Items, i => i.Label == "T"); // It's client's responsibility to sort, so we can't assume the best match is the first item.
 
        // Make a second completion request, but not for the original incomplete list.
        completionParams = CreateCompletionParams(
            testLspServer.GetLocations("secondCaret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "H",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.False(results.IsIncomplete);
        Assert.True(results.Items.Length < 1000);
        Assert.Contains(results.Items, i => i.Label == "WindowHeight"); // It's client's responsibility to sort, so we can't assume the best match is the first item.
    }
 
    [Theory, CombinatorialData]
    public async Task TestRequestForIncompleteListWhenMissingCachedListAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M()
    {
        Ta{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var caretLocation = testLspServer.GetLocations("caret").Single();
 
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "a",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.True(results.Items.Length < 1000);
        Assert.Contains("ta", results.Items.First().Label, StringComparison.OrdinalIgnoreCase);
    }
 
    [Theory, CombinatorialData]
    public async Task TestRequestForIncompleteListUsesCorrectCachedListAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M1()
    {
        int Taaa = 1;
        T{|firstCaret:|}
    }
 
    void M2()
    {
        int Saaa = 1;
        {|secondCaret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var firstCaretLocation = testLspServer.GetLocations("firstCaret").Single();
        await testLspServer.OpenDocumentAsync(firstCaretLocation.Uri);
 
        // Create request to on insertion of 'T'
        var completionParams = CreateCompletionParams(
            firstCaretLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "T",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Equal("T", results.Items.First().Label);
        Assert.Single(results.Items, item => item.Label == "Taaa");
 
        // Insert 'S' at the second caret
        var secondCaretLocation = testLspServer.GetLocations("secondCaret").Single();
        await testLspServer.InsertTextAsync(secondCaretLocation.Uri, (secondCaretLocation.Range.End.Line, secondCaretLocation.Range.End.Character, "S"));
 
        // Trigger completion on 'S'
        var triggerLocation = GetLocationPlusOne(secondCaretLocation);
        completionParams = CreateCompletionParams(
            triggerLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "S",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Equal("Saaa", results.Items.First().Label);
 
        // Now type 'a' in M1 after 'T'
        await testLspServer.InsertTextAsync(firstCaretLocation.Uri, (firstCaretLocation.Range.End.Line, firstCaretLocation.Range.End.Character, "a"));
 
        // Trigger completion on 'a' (using incomplete as we previously returned incomplete completions from 'T').
        triggerLocation = GetLocationPlusOne(firstCaretLocation);
        completionParams = CreateCompletionParams(
            triggerLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "a",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
 
        // Verify we get completions for 'Ta' and not from the 'S' location in M2
        Assert.True(results.IsIncomplete);
        Assert.True(results.Items.Length < 1000);
        Assert.DoesNotContain(results.Items, item => item.Label == "Saaa");
        Assert.Contains(results.Items, item => item.Label == "Taaa");
    }
 
    [Theory, CombinatorialData]
    public async Task TestCompletionRequestRespectsListSizeOptionAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var globalOptions = testLspServer.TestWorkspace.GetService<IGlobalOptionService>();
        var listMaxSize = 1;
 
        globalOptions.SetGlobalOption(LspOptionsStorage.MaxCompletionListSize, listMaxSize);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.Equal(listMaxSize, results.Items.Length);
    }
 
    [Theory, CombinatorialData]
    public async Task TestRequestForIncompleteListFiltersDownToEmptyAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Media;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class A
{
    void M()
    {
        T{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var caretLocation = testLspServer.GetLocations("caret").Single();
        await testLspServer.OpenDocumentAsync(caretLocation.Uri);
 
        var completionParams = CreateCompletionParams(
            caretLocation,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "T",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(1000, results.Items.Length);
        Assert.True(results.IsIncomplete);
        Assert.Equal("T", results.Items.First().Label);
 
        await testLspServer.InsertTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, "z"));
 
        completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "z",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
 
        results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.True(results.IsIncomplete);
        Assert.Empty(results.Items);
    }
 
    [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1755138")]
    public async Task TestHasSuggestionModeItemAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"using System.Threading.Tasks;
class A
{
    void M()
    {
        Task.Run(abcdefg{|caret:|}
    }
}";
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Typing,
            triggerCharacter: "g",
            triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions);
 
        var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        var list = (LSP.VSInternalCompletionList)results;
        Assert.False(list.IsIncomplete);
        Assert.NotEmpty(list.Items); // it client's responsibility to filter, server should return all items available regardless of the filter text (unless item counts exceeds the limit)
        Assert.True(list.SuggestionMode);
    }
 
    [Theory, CombinatorialData]
    public async Task EditRangeShouldNotEndAtCursorPosition(bool mutatingLspWorkspace)
    {
        var markup =
@"public class C1 {}
 
pub{|caret:|}class";
 
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, s_vsCompletionCapabilities);
        var caret = testLspServer.GetLocations("caret").Single();
        testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(CompletionOptionsStorage.SnippetsBehavior, LanguageNames.CSharp, SnippetsRule.NeverInclude);
 
        var completionParams = CreateCompletionParams(
            caret,
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams);
        AssertEx.NotNull(results);
        Assert.NotEmpty(results.Items);
        Assert.Equal(new() { Start = new(2, 0), End = new(2, 8) }, results.ItemDefaults.EditRange.Value.First);
    }
 
    [Theory, CombinatorialData]
    public async Task TestHasInsertTextModeIfSupportedAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        var capabilities = CreateCoreCompletionCapabilities();
        capabilities.TextDocument.Completion.CompletionItem = new LSP.CompletionItemSetting
        {
            InsertTextModeSupport = new LSP.InsertTextModeSupportSetting { ValueSet = [LSP.InsertTextMode.AsIs] }
        };
 
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, capabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Equal(LSP.InsertTextMode.AsIs, results.ItemDefaults.InsertTextMode);
    }
 
    [Theory, CombinatorialData]
    public async Task TestDoesNotHaveInsertTextModeIfNotSupportedAsync(bool mutatingLspWorkspace)
    {
        var markup =
@"class A
{
    void M()
    {
        {|caret:|}
    }
}";
        var capabilities = CreateCoreCompletionCapabilities();
        capabilities.TextDocument.Completion.CompletionItem = new LSP.CompletionItemSetting
        {
            InsertTextModeSupport = new LSP.InsertTextModeSupportSetting { ValueSet = [] }
        };
 
        await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, capabilities);
        var completionParams = CreateCompletionParams(
            testLspServer.GetLocations("caret").Single(),
            invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit,
            triggerCharacter: "\0",
            triggerKind: LSP.CompletionTriggerKind.Invoked);
 
        var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false);
        Assert.Null(results.ItemDefaults.InsertTextMode);
    }
 
    internal static Task<LSP.CompletionList> RunGetCompletionsAsync(TestLspServer testLspServer, LSP.CompletionParams completionParams)
    {
        return testLspServer.ExecuteRequestAsync<LSP.CompletionParams, LSP.CompletionList>(LSP.Methods.TextDocumentCompletionName,
            completionParams, CancellationToken.None);
    }
 
    private static CompletionListCache GetCompletionListCache(TestLspServer testLspServer)
    {
        var cache = testLspServer.GetRequiredLspService<CompletionListCache>();
        return cache;
    }
}