diff --git a/Directory.Packages.props b/Directory.Packages.props
index 02c9a790a75..fac9962f013 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,9 +13,9 @@
<_BasicReferenceAssembliesVersion>1.7.2
<_BenchmarkDotNetPackageVersion>0.13.5.2136
<_MicrosoftVisualStudioExtensibilityTestingVersion>0.1.187-beta
- <_MicrosoftCodeAnalysisAnalyzersPackageVersion>3.11.0-beta1.24170.2
+ <_MicrosoftCodeAnalysisAnalyzersPackageVersion>3.11.0
<_MicrosoftVisualStudioLanguageServicesPackageVersion>$(MicrosoftVisualStudioLanguageServicesPackageVersion)
- <_XunitPackageVersion>2.6.3
+ <_XunitPackageVersion>2.6.6
<_MicrosoftBuildPackageVersion>17.11.0-preview-24309-01
@@ -34,7 +34,7 @@
-
+
@@ -106,7 +106,7 @@
-
+
@@ -117,7 +117,7 @@
-
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 4da4e2b24b2..0b14684cccb 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -11,82 +11,82 @@
76c417253f5b3890997a3ef4b0613c2eab73d156
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
-
+
https://github.com/dotnet/roslyn
- 7b7951aa13c50ad768538e58ed3805898b058928
+ e2d4e372f19c16f9b3dea06f7ca857ed5d42bc09
diff --git a/eng/Versions.props b/eng/Versions.props
index 94e345e7cba..9802c793093 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -53,25 +53,25 @@
9.0.0-beta.24509.3
1.0.0-beta.23475.1
1.0.0-beta.23475.1
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
- 4.12.0-3.24466.4
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
+ 4.13.0-1.24505.1
$(MicrosoftNetCompilersToolsetPackageVersion)
- 2.6.3
+ 2.6.6
1.7.0
diff --git a/eng/targets/Services.props b/eng/targets/Services.props
index 0501df0e116..f18168d032b 100644
--- a/eng/targets/Services.props
+++ b/eng/targets/Services.props
@@ -33,5 +33,6 @@
+
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorCompletionBenchmark.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorCompletionBenchmark.cs
index 7fc0caba7bc..9c718b6fbb8 100644
--- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorCompletionBenchmark.cs
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorCompletionBenchmark.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
-using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
@@ -13,6 +12,7 @@
using Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
@@ -37,13 +37,12 @@ public async Task SetupAsync()
{
var razorCompletionListProvider = RazorLanguageServerHost.GetRequiredService();
var lspServices = RazorLanguageServerHost.GetRequiredService();
- var responseRewriters = lspServices.GetRequiredServices();
var documentMappingService = lspServices.GetRequiredService();
var clientConnection = lspServices.GetRequiredService();
var completionListCache = lspServices.GetRequiredService();
var loggerFactory = lspServices.GetRequiredService();
- var delegatedCompletionListProvider = new TestDelegatedCompletionListProvider(responseRewriters, documentMappingService, clientConnection, completionListCache);
+ var delegatedCompletionListProvider = new TestDelegatedCompletionListProvider(documentMappingService, clientConnection, completionListCache);
var completionListProvider = new CompletionListProvider(razorCompletionListProvider, delegatedCompletionListProvider);
var configurationService = new DefaultRazorConfigurationService(clientConnection, loggerFactory);
var optionsMonitor = new RazorLSPOptionsMonitor(configurationService, RazorLSPOptions.Default);
@@ -141,12 +140,19 @@ public async Task RazorCompletionAsync()
private class TestDelegatedCompletionListProvider : DelegatedCompletionListProvider
{
- public TestDelegatedCompletionListProvider(IEnumerable responseRewriters, IDocumentMappingService documentMappingService, IClientConnection clientConnection, CompletionListCache completionListCache)
- : base(responseRewriters, documentMappingService, clientConnection, completionListCache)
+ public TestDelegatedCompletionListProvider(IDocumentMappingService documentMappingService, IClientConnection clientConnection, CompletionListCache completionListCache)
+ : base(documentMappingService, clientConnection, completionListCache)
{
}
- public override Task GetCompletionListAsync(int absoluteIndex, VSInternalCompletionContext completionContext, DocumentContext documentContext, VSInternalClientCapabilities clientCapabilities, Guid correlationId, CancellationToken cancellationToken)
+ public override Task GetCompletionListAsync(
+ int absoluteIndex,
+ VSInternalCompletionContext completionContext,
+ DocumentContext documentContext,
+ VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions completionOptions,
+ Guid correlationId,
+ CancellationToken cancellationToken)
{
return Task.FromResult(
new VSInternalCompletionList
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/TagHelperCompletionBenchmark.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/TagHelperCompletionBenchmark.cs
index 391bf424470..f96af4a0ed4 100644
--- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/TagHelperCompletionBenchmark.cs
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/TagHelperCompletionBenchmark.cs
@@ -29,7 +29,7 @@ public class TagHelperCompletionBenchmark
[Benchmark]
public object GetAttributeCompletions()
{
- var tagHelperCompletionService = new LspTagHelperCompletionService();
+ var tagHelperCompletionService = new TagHelperCompletionService();
var context = new AttributeCompletionContext(
TagHelperDocumentContext.Create(prefix: null, CommonResources.TelerikTagHelpers),
existingCompletions: [],
@@ -46,7 +46,7 @@ public object GetAttributeCompletions()
[Benchmark]
public object GetElementCompletions()
{
- var tagHelperCompletionService = new LspTagHelperCompletionService();
+ var tagHelperCompletionService = new TagHelperCompletionService();
var context = new ElementCompletionContext(
TagHelperDocumentContext.Create(prefix: null, CommonResources.TelerikTagHelpers),
existingCompletions: s_existingElementCompletions,
diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/CompletionListSerializationBenchmark.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/CompletionListSerializationBenchmark.cs
index 9e98822d88d..f8aa0692962 100644
--- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/CompletionListSerializationBenchmark.cs
+++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/CompletionListSerializationBenchmark.cs
@@ -22,10 +22,8 @@ public class CompletionListSerializationBenchmark
public CompletionListSerializationBenchmark()
{
- var completionService = new LspTagHelperCompletionService();
- var configurationService = new BenchmarkConfigurationSyncService();
- var optionsMonitor = new RazorLSPOptionsMonitor(configurationService, RazorLSPOptions.Default);
- var tagHelperCompletionProvider = new TagHelperCompletionProvider(completionService, optionsMonitor);
+ var completionService = new TagHelperCompletionService();
+ var tagHelperCompletionProvider = new TagHelperCompletionProvider(completionService);
var documentContent = "<";
var queryIndex = 1;
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListProvider.cs
index 9caeb7cc5de..910bba295c3 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListProvider.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListProvider.cs
@@ -2,12 +2,14 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@@ -22,25 +24,27 @@ public CompletionListProvider(RazorCompletionListProvider razorCompletionListPro
{
_razorCompletionListProvider = razorCompletionListProvider;
_delegatedCompletionListProvider = delegatedCompletionListProvider;
-
- var allTriggerCharacters = razorCompletionListProvider.TriggerCharacters.Concat(delegatedCompletionListProvider.TriggerCharacters);
- var distinctTriggerCharacters = new HashSet(allTriggerCharacters);
- AggregateTriggerCharacters = distinctTriggerCharacters.ToImmutableHashSet();
}
- public ImmutableHashSet AggregateTriggerCharacters { get; }
-
public async Task GetCompletionListAsync(
int absoluteIndex,
VSInternalCompletionContext completionContext,
DocumentContext documentContext,
VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions razorCompletionOptions,
Guid correlationId,
CancellationToken cancellationToken)
{
// First we delegate to get completion items from the individual language server
- var delegatedCompletionList = IsValidTrigger(_delegatedCompletionListProvider.TriggerCharacters, completionContext)
- ? await _delegatedCompletionListProvider.GetCompletionListAsync(absoluteIndex, completionContext, documentContext, clientCapabilities, correlationId, cancellationToken).ConfigureAwait(false)
+ var delegatedCompletionList = CompletionTriggerCharacters.IsValidTrigger(_delegatedCompletionListProvider.TriggerCharacters, completionContext)
+ ? await _delegatedCompletionListProvider.GetCompletionListAsync(
+ absoluteIndex,
+ completionContext,
+ documentContext,
+ clientCapabilities,
+ razorCompletionOptions,
+ correlationId,
+ cancellationToken).ConfigureAwait(false)
: null;
// Extract the items we got back from the delegated server, to inform tag helper completion
@@ -49,17 +53,19 @@ public CompletionListProvider(RazorCompletionListProvider razorCompletionListPro
: null;
// Now we get the Razor completion list, using information from the actual language server if necessary
- var razorCompletionList = IsValidTrigger(_razorCompletionListProvider.TriggerCharacters, completionContext)
- ? await _razorCompletionListProvider.GetCompletionListAsync(absoluteIndex, completionContext, documentContext, clientCapabilities, existingItems, cancellationToken).ConfigureAwait(false)
+ var razorCompletionList = CompletionTriggerCharacters.IsValidTrigger(_razorCompletionListProvider.TriggerCharacters, completionContext)
+ ? await _razorCompletionListProvider.GetCompletionListAsync(
+ absoluteIndex,
+ completionContext,
+ documentContext,
+ clientCapabilities,
+ existingItems,
+ razorCompletionOptions,
+ cancellationToken).ConfigureAwait(false)
: null;
var finalCompletionList = CompletionListMerger.Merge(razorCompletionList, delegatedCompletionList);
return finalCompletionList;
}
-
- private bool IsValidTrigger(ImmutableHashSet triggerCharacters, VSInternalCompletionContext completionContext)
- => completionContext.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
- completionContext.TriggerCharacter is null ||
- triggerCharacters.Contains(completionContext.TriggerCharacter);
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionItemResolver.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionItemResolver.cs
index 81d2a97b3e6..bacfb7e3969 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionItemResolver.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionItemResolver.cs
@@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionListProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionListProvider.cs
index 900b1fcc39b..e7e0e259ca1 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionListProvider.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionListProvider.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@@ -11,42 +12,34 @@
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor;
+using Microsoft.CodeAnalysis.Razor.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion.Delegation;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
+using Microsoft.CodeAnalysis.Razor.Protocol.Completion;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
internal class DelegatedCompletionListProvider
{
- private static readonly ImmutableHashSet s_razorTriggerCharacters = new[] { "@" }.ToImmutableHashSet();
- private static readonly ImmutableHashSet s_csharpTriggerCharacters = new[] { " ", "(", "=", "#", ".", "<", "[", "{", "\"", "/", ":", "~" }.ToImmutableHashSet();
- private static readonly ImmutableHashSet s_htmlTriggerCharacters = new[] { ":", "@", "#", ".", "!", "*", ",", "(", "[", "-", "<", "&", "\\", "/", "'", "\"", "=", ":", " ", "`" }.ToImmutableHashSet();
- private static readonly ImmutableHashSet s_allTriggerCharacters =
- s_csharpTriggerCharacters
- .Union(s_htmlTriggerCharacters)
- .Union(s_razorTriggerCharacters);
-
- private readonly ImmutableArray _responseRewriters;
private readonly IDocumentMappingService _documentMappingService;
private readonly IClientConnection _clientConnection;
private readonly CompletionListCache _completionListCache;
public DelegatedCompletionListProvider(
- IEnumerable responseRewriters,
IDocumentMappingService documentMappingService,
IClientConnection clientConnection,
CompletionListCache completionListCache)
{
- _responseRewriters = responseRewriters.OrderByAsArray(static x => x.Order);
_documentMappingService = documentMappingService;
_clientConnection = clientConnection;
_completionListCache = completionListCache;
}
// virtual for tests
- public virtual ImmutableHashSet TriggerCharacters => s_allTriggerCharacters;
+ public virtual FrozenSet TriggerCharacters => CompletionTriggerCharacters.AllDelegationTriggerCharacters;
// virtual for tests
public virtual async Task GetCompletionListAsync(
@@ -54,6 +47,7 @@ public DelegatedCompletionListProvider(
VSInternalCompletionContext completionContext,
DocumentContext documentContext,
VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions razorCompletionOptions,
Guid correlationId,
CancellationToken cancellationToken)
{
@@ -67,17 +61,27 @@ public DelegatedCompletionListProvider(
return null;
}
- var provisionalCompletion = await TryGetProvisionalCompletionInfoAsync(documentContext, completionContext, positionInfo, cancellationToken).ConfigureAwait(false);
+ var provisionalCompletion = await DelegatedCompletionHelper.TryGetProvisionalCompletionInfoAsync(
+ documentContext,
+ completionContext,
+ positionInfo,
+ _documentMappingService,
+ cancellationToken).ConfigureAwait(false);
TextEdit? provisionalTextEdit = null;
- if (provisionalCompletion is not null)
+ if (provisionalCompletion is { } provisionalCompletionValue)
{
- provisionalTextEdit = provisionalCompletion.ProvisionalTextEdit;
- positionInfo = provisionalCompletion.ProvisionalPositionInfo;
+ provisionalTextEdit = provisionalCompletionValue.ProvisionalTextEdit;
+ positionInfo = provisionalCompletionValue.DocumentPositionInfo;
}
- completionContext = RewriteContext(completionContext, positionInfo.LanguageKind);
+ completionContext = DelegatedCompletionHelper.RewriteContext(completionContext, positionInfo.LanguageKind);
- var shouldIncludeSnippets = await ShouldIncludeSnippetsAsync(documentContext, absoluteIndex, cancellationToken).ConfigureAwait(false);
+ var razorCodeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
+ // It's a bit confusing, but we have two different "add snippets" options - one is a part of
+ // RazorCompletionOptions and becomes a part of RazorCompletionContext and is used by
+ // RazorCompletionFactsService, and the second one below that's used for delegated completion
+ // Their values are not related in any way.
+ var shouldIncludeDelegationSnippets = DelegatedCompletionHelper.ShouldIncludeSnippets(razorCodeDocument, absoluteIndex);
var delegatedParams = new DelegatedCompletionParams(
documentContext.GetTextDocumentIdentifierAndVersion(),
@@ -85,7 +89,7 @@ public DelegatedCompletionListProvider(
positionInfo.LanguageKind,
completionContext,
provisionalTextEdit,
- shouldIncludeSnippets,
+ shouldIncludeDelegationSnippets,
correlationId);
var delegatedResponse = await _clientConnection.SendRequestAsync(
@@ -93,26 +97,16 @@ public DelegatedCompletionListProvider(
delegatedParams,
cancellationToken).ConfigureAwait(false);
- if (delegatedResponse is null)
- {
- // If we don't get a response from the delegated server, we have to make sure to return an incomplete completion
- // list. When a user is typing quickly, the delegated request from the first keystroke will fail to synchronize,
- // so if we return a "complete" list then the query won't re-query us for completion once the typing stops/slows
- // so we'd only ever return Razor completion items.
- return new VSInternalCompletionList() { IsIncomplete = true, Items = [] };
- }
-
- var rewrittenResponse = delegatedResponse;
-
- foreach (var rewriter in _responseRewriters)
- {
- rewrittenResponse = await rewriter.RewriteAsync(
- rewrittenResponse,
+ var rewrittenResponse = delegatedParams.ProjectedKind == RazorLanguageKind.CSharp
+ ? await DelegatedCompletionHelper.RewriteCSharpResponseAsync(
+ delegatedResponse,
absoluteIndex,
documentContext,
- delegatedParams,
- cancellationToken).ConfigureAwait(false);
- }
+ delegatedParams.ProjectedPosition,
+ razorCompletionOptions,
+ cancellationToken)
+ .ConfigureAwait(false)
+ : DelegatedCompletionHelper.RewriteHtmlResponse(delegatedResponse, razorCompletionOptions);
var completionCapability = clientCapabilities?.TextDocument?.Completion as VSInternalCompletionSetting;
var resolutionContext = new DelegatedCompletionResolutionContext(delegatedParams, rewrittenResponse.Data);
@@ -121,113 +115,4 @@ public DelegatedCompletionListProvider(
return rewrittenResponse;
}
-
- private async Task ShouldIncludeSnippetsAsync(DocumentContext documentContext, int absoluteIndex, CancellationToken cancellationToken)
- {
- var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
- var tree = codeDocument.GetSyntaxTree();
-
- var token = tree.Root.FindToken(absoluteIndex, includeWhitespace: false);
- var node = token.Parent;
- var startOrEndTag = node?.FirstAncestorOrSelf(n => RazorSyntaxFacts.IsAnyStartTag(n) || RazorSyntaxFacts.IsAnyEndTag(n));
-
- if (startOrEndTag is null)
- {
- return token.Kind is not (SyntaxKind.OpenAngle or SyntaxKind.CloseAngle);
- }
-
- if (startOrEndTag.Span.Start == absoluteIndex)
- {
- // We're at the start of the tag, we should include snippets. This is the case for things like $$ or $$
, since the
- // index is right associative to the token when using FindToken.
- return true;
- }
-
- return !startOrEndTag.Span.Contains(absoluteIndex);
- }
-
- private static VSInternalCompletionContext RewriteContext(VSInternalCompletionContext context, RazorLanguageKind languageKind)
- {
- if (context.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
- context.TriggerCharacter is not { } triggerCharacter)
- {
- // Non-triggered based completion, the existing context is valid.
- return context;
- }
-
- if (languageKind == RazorLanguageKind.CSharp && s_csharpTriggerCharacters.Contains(triggerCharacter))
- {
- // C# trigger character for C# content
- return context;
- }
-
- if (languageKind == RazorLanguageKind.Html && s_htmlTriggerCharacters.Contains(triggerCharacter))
- {
- // HTML trigger character for HTML content
- return context;
- }
-
- // Trigger character not associated with the current language. Transform the context into an invoked context.
- var rewrittenContext = new VSInternalCompletionContext()
- {
- InvokeKind = context.InvokeKind,
- TriggerKind = CompletionTriggerKind.Invoked,
- };
-
- if (languageKind == RazorLanguageKind.CSharp && s_razorTriggerCharacters.Contains(triggerCharacter))
- {
- // The C# language server will not return any completions for the '@' character unless we
- // send the completion request explicitly.
- rewrittenContext.InvokeKind = VSInternalCompletionInvokeKind.Explicit;
- }
-
- return rewrittenContext;
- }
-
- private async Task TryGetProvisionalCompletionInfoAsync(
- DocumentContext documentContext,
- VSInternalCompletionContext completionContext,
- DocumentPositionInfo positionInfo,
- CancellationToken cancellationToken)
- {
- if (positionInfo.LanguageKind != RazorLanguageKind.Html ||
- completionContext.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
- completionContext.TriggerCharacter != ".")
- {
- // Invalid provisional completion context
- return null;
- }
-
- if (positionInfo.Position.Character == 0)
- {
- // We're at the start of line. Can't have provisional completions here.
- return null;
- }
-
- var previousCharacterPositionInfo = await _documentMappingService
- .GetPositionInfoAsync(documentContext, positionInfo.HostDocumentIndex - 1, cancellationToken)
- .ConfigureAwait(false);
-
- if (previousCharacterPositionInfo.LanguageKind != RazorLanguageKind.CSharp)
- {
- return null;
- }
-
- var previousPosition = previousCharacterPositionInfo.Position;
-
- // Edit the CSharp projected document to contain a '.'. This allows C# completion to provide valid
- // completion items for moments when a user has typed a '.' that's typically interpreted as Html.
- var addProvisionalDot = VsLspFactory.CreateTextEdit(previousPosition, ".");
-
- var provisionalPositionInfo = new DocumentPositionInfo(
- RazorLanguageKind.CSharp,
- VsLspFactory.CreatePosition(
- previousPosition.Line,
- previousPosition.Character + 1),
- previousCharacterPositionInfo.HostDocumentIndex + 1);
-
- return new ProvisionalCompletionInfo(addProvisionalDot, provisionalPositionInfo);
- }
-
- private record class ProvisionalCompletionInfo(TextEdit ProvisionalTextEdit, DocumentPositionInfo ProvisionalPositionInfo);
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionResponseRewriter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionResponseRewriter.cs
deleted file mode 100644
index 68c04994f45..00000000000
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DelegatedCompletionResponseRewriter.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the MIT license. See License.txt in the project root for license information.
-
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Razor.ProjectSystem;
-using Microsoft.CodeAnalysis.Razor.Protocol;
-using Microsoft.VisualStudio.LanguageServer.Protocol;
-
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
-
-internal abstract class DelegatedCompletionResponseRewriter
-{
- ///
- /// Defines the order in which the rewriter will run. Implementors of should utilize
- /// the type to determine order.
- ///
- /// is only called once to determine order (needs to represent a static order).
- ///
- public abstract int Order { get; }
-
- public abstract Task RewriteAsync(
- VSInternalCompletionList completionList,
- int hostDocumentIndex,
- DocumentContext hostDocumentContext,
- DelegatedCompletionParams delegatedParameters,
- CancellationToken cancellationToken);
-
- protected static class ExecutionBehaviorOrder
- {
- public static readonly int FiltersCompletionItems = -20;
-
- public static readonly int AddsCompletionItems = -10;
-
- public static readonly int Default = 0;
-
- public static readonly int ChangesCompletionItems = 10;
- }
-}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs
index 298e83b4bb7..3990add43e4 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionEndpoint.cs
@@ -8,6 +8,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.Telemetry;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@@ -35,7 +36,7 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
serverCapabilities.CompletionProvider = new CompletionOptions()
{
ResolveProvider = true,
- TriggerCharacters = _completionListProvider.AggregateTriggerCharacters.ToArray(),
+ TriggerCharacters = CompletionTriggerCharacters.AllTriggerCharacters,
// This is the intersection of C# and HTML commit characters.
// We need to specify it so that platform can correctly calculate ApplicableToSpan in
// https://devdiv.visualstudio.com/DevDiv/_git/VSLanguageServerClient?path=/src/product/RemoteLanguage/Impl/Features/Completion/AsyncCompletionSource.cs&version=GBdevelop&line=855&lineEnd=855&lineStartColumn=9&lineEndColumn=49&lineStyle=plain&_a=contents
@@ -78,11 +79,16 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(CompletionParams request
var correlationId = Guid.NewGuid();
using var _ = _telemetryReporter?.TrackLspRequest(Methods.TextDocumentCompletionName, LanguageServerConstants.RazorLanguageServerName, correlationId);
+ var razorCompletionOptions = new RazorCompletionOptions(
+ SnippetsSupported: true,
+ AutoInsertAttributeQuotes: _optionsMonitor.CurrentValue.AutoInsertAttributeQuotes,
+ CommitElementsWithSpace: _optionsMonitor.CurrentValue.CommitElementsWithSpace);
var completionList = await _completionListProvider.GetCompletionListAsync(
hostDocumentIndex,
completionContext,
documentContext,
_clientCapabilities!,
+ razorCompletionOptions,
correlationId,
cancellationToken).ConfigureAwait(false);
return completionList;
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionItemExtensions.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionItemExtensions.cs
deleted file mode 100644
index 98f60f32985..00000000000
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionItemExtensions.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the MIT license. See License.txt in the project root for license information.
-
-using System;
-using Microsoft.CodeAnalysis.Razor.Completion;
-using Microsoft.CodeAnalysis.Razor.Tooltip;
-
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
-
-internal static class RazorCompletionItemExtensions
-{
- private readonly static string s_markupTransitionDescriptionKey = "Razor.MarkupTransitionDescription";
- private readonly static string s_tagHelperElementCompletionDescriptionKey = "Razor.TagHelperElementDescription";
-
- public static void SetMarkupTransitionCompletionDescription(this RazorCompletionItem completionItem, MarkupTransitionCompletionDescription markupTransitionCompletionDescription)
- {
- if (completionItem is null)
- {
- throw new ArgumentNullException(nameof(completionItem));
- }
-
- completionItem.Items[s_markupTransitionDescriptionKey] = markupTransitionCompletionDescription;
- }
-
- public static MarkupTransitionCompletionDescription? GetMarkupTransitionCompletionDescription(this RazorCompletionItem completionItem)
- {
- if (completionItem is null)
- {
- throw new ArgumentNullException(nameof(completionItem));
- }
-
- var markupTransitionCompletionDescription = completionItem.Items[s_markupTransitionDescriptionKey] as MarkupTransitionCompletionDescription;
- return markupTransitionCompletionDescription;
- }
-
- public static void SetTagHelperElementDescriptionInfo(this RazorCompletionItem completionItem, AggregateBoundElementDescription elementDescriptionInfo)
- {
- completionItem.Items[s_tagHelperElementCompletionDescriptionKey] = elementDescriptionInfo;
- }
-
- public static AggregateBoundElementDescription? GetTagHelperElementDescriptionInfo(this RazorCompletionItem completionItem)
- {
- if (completionItem is null)
- {
- throw new ArgumentNullException(nameof(completionItem));
- }
-
- var description = completionItem.Items[s_tagHelperElementCompletionDescriptionKey] as AggregateBoundElementDescription;
- return description;
- }
-}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionResolveEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionResolveEndpoint.cs
index 5c22672d50f..41274aae245 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionResolveEndpoint.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionResolveEndpoint.cs
@@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Extensions/IServiceCollectionExtensions.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Extensions/IServiceCollectionExtensions.cs
index 985d4395b83..429ca440bb8 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Extensions/IServiceCollectionExtensions.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Extensions/IServiceCollectionExtensions.cs
@@ -21,6 +21,7 @@
using Microsoft.AspNetCore.Razor.LanguageServer.SpellCheck;
using Microsoft.AspNetCore.Razor.ProjectEngineHost;
using Microsoft.CodeAnalysis.Razor.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion.Delegation;
using Microsoft.CodeAnalysis.Razor.Diagnostics;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Formatting;
@@ -81,15 +82,11 @@ public static void AddCompletionServices(this IServiceCollection services)
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs
index 780d31c5e56..3ec3a12e926 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs
@@ -130,7 +130,6 @@ protected override ILspServices ConstructLspServices()
services.AddSemanticTokensServices(featureOptions);
services.AddDocumentManagementServices(featureOptions);
- services.AddCompletionServices();
services.AddFormattingServices(featureOptions);
services.AddCodeActionsServices();
services.AddOptionsServices(_lspOptions);
@@ -142,6 +141,9 @@ protected override ILspServices ConstructLspServices()
// Diagnostics
services.AddDiagnosticServices();
+ // Completion
+ services.AddCompletionServices();
+
// Auto insert
services.AddSingleton();
services.AddSingleton();
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/SR.resx b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/SR.resx
index f652571c68b..c783544e730 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/SR.resx
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/SR.resx
@@ -117,9 +117,6 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- Blazor directive attributes
-
Changes:
@@ -141,18 +138,12 @@
Generate Event Handler '{0}'
-
- "Re-trigger completions..."
-
Unknown ProjectChangeKind {0}
Provided version should not be null.
-
- statement
-
Extract element to new component
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.cs.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.cs.xlf
index 993c3f2b9d1..b630702a1b3 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.cs.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.cs.xlf
@@ -2,11 +2,6 @@
-
-
- Atributy direktivy Blazor
-
-
Změny:
@@ -47,16 +42,6 @@
Generovat obslužnou rutinu události {0}
-
-
- Aktivovat znovu dokončení…
-
-
-
-
- příkaz
-
-
Neznámý ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.de.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.de.xlf
index 6e155851810..74b87e6f100 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.de.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.de.xlf
@@ -2,11 +2,6 @@
-
-
- Attribute für Blazor-Direktiven
-
-
Änderungen:
@@ -47,16 +42,6 @@
Ereignishandler "{0}" generieren
-
-
- "Abschlüsse erneut auslösen..."
-
-
-
-
- Anweisung
-
-
Unbekannte ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.es.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.es.xlf
index 786cbbdc094..6b83a1f78bf 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.es.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.es.xlf
@@ -2,11 +2,6 @@
-
-
- Atributos de directiva de Blazor
-
-
Cambios:
@@ -47,16 +42,6 @@
Generar controlador de eventos ''{0}''
-
-
- "Volver a desencadenar las finalizaciones..."
-
-
-
-
- instrucción
-
-
ProjectChangeKind desconocido {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.fr.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.fr.xlf
index 500f1d42e0e..8fb0151b276 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.fr.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.fr.xlf
@@ -2,11 +2,6 @@
-
-
- Attributs de directive Blazor
-
-
Modifications :
@@ -47,16 +42,6 @@
Générer le gestionnaire d’événements '{0}'
-
-
- « Déclencher à nouveau les complétions... »
-
-
-
-
- déclaration
-
-
ProjectChangeKind inconnu {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.it.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.it.xlf
index 9c54cc89004..066658b5ce9 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.it.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.it.xlf
@@ -2,11 +2,6 @@
-
-
- Attributo per la direttiva Blazor
-
-
Modifiche:
@@ -47,16 +42,6 @@
Genera gestore dell'evento '{0}'
-
-
- "Riattiva i completamenti..."
-
-
-
-
- istruzione
-
-
ProjectChangeKind {0} sconosciuto
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ja.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ja.xlf
index d37fd154637..3e5dd36870f 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ja.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ja.xlf
@@ -2,11 +2,6 @@
-
-
- Blazor ディレクティブ属性
-
-
変更:
@@ -47,16 +42,6 @@
イベント ハンドラー '{0}' の生成
-
-
- "再トリガーの完了..."
-
-
-
-
- ステートメント
-
-
不明な ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ko.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ko.xlf
index ff072a41e06..38e3e292241 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ko.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ko.xlf
@@ -2,11 +2,6 @@
-
-
- Blazor 지시문 특성
-
-
변경 내용:
@@ -47,16 +42,6 @@
이벤트 처리기 '{0}' 생성
-
-
- "완료된 항목 다시 트리거"
-
-
-
-
- 문
-
-
알 수 없는 ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pl.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pl.xlf
index 098d35923f5..87cef9f2e44 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pl.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pl.xlf
@@ -2,11 +2,6 @@
-
-
- Atrybut dyrektywy Blazor
-
-
Zmiany:
@@ -47,16 +42,6 @@
Generuj obsługę zdarzeń „{0}”
-
-
- "Ponownie wyzwalaj uzupełniania..."
-
-
-
-
- instrukcja
-
-
Nieznany ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pt-BR.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pt-BR.xlf
index 38674a41ddc..1546d112925 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pt-BR.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.pt-BR.xlf
@@ -2,11 +2,6 @@
-
-
- Atributos da diretiva Blazor
-
-
Alterações:
@@ -47,16 +42,6 @@
Gerar manipulador de eventos '{0}'
-
-
- "Disparar conclusões novamente..."
-
-
-
-
- instrução
-
-
ProjectChangeKind desconhecido {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ru.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ru.xlf
index 87fd1dc65e1..8e012c1442e 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ru.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.ru.xlf
@@ -2,11 +2,6 @@
-
-
- Атрибуты директивы Blazor
-
-
Изменения:
@@ -47,16 +42,6 @@
Создать обработчик событий "{0}"
-
-
- "Повторный запуск завершений..."
-
-
-
-
- инструкция
-
-
Неизвестный ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.tr.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.tr.xlf
index db1e0aba01d..5d3e4817090 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.tr.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.tr.xlf
@@ -2,11 +2,6 @@
-
-
- Blazor yönergesi öznitelikleri
-
-
Değişiklikler:
@@ -47,16 +42,6 @@
'{0}' Olay İşleyicisi Oluştur
-
-
- "Tamamlamaları yeniden tetikleyin..."
-
-
-
-
- deyim
-
-
Bilinmeyen ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hans.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hans.xlf
index 951409f4398..50adf39a2ba 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hans.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hans.xlf
@@ -2,11 +2,6 @@
-
-
- Blazor 指令特性
-
-
更改:
@@ -47,16 +42,6 @@
生成事件处理程序 "{0}"
-
-
- “重新触发完成…”
-
-
-
-
- 语句
-
-
未知的 ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hant.xlf b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hant.xlf
index c61189487d1..a34fc90c751 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hant.xlf
+++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Resources/xlf/SR.zh-Hant.xlf
@@ -2,11 +2,6 @@
-
-
- Blazor 指示詞屬性
-
-
變更:
@@ -47,16 +42,6 @@
產生事件處理常式 '{0}'
-
-
- "重新觸發完成..."
-
-
-
-
- 陳述式
-
-
未知的 ProjectChangeKind {0}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListCache.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListCache.cs
similarity index 96%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListCache.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListCache.cs
index ee163ec8b1a..149dd360c14 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListCache.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListCache.cs
@@ -4,9 +4,9 @@
using System;
using Microsoft.VisualStudio.LanguageServer.Protocol;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
-internal sealed class CompletionListCache
+internal class CompletionListCache
{
private record struct Slot(
int Id,
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListMerger.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListMerger.cs
similarity index 98%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListMerger.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListMerger.cs
index cf1344a1936..a6c511d1911 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListMerger.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListMerger.cs
@@ -4,17 +4,13 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
-using System.Text.Json.Nodes;
-using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Razor.PooledObjects;
-using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal static class CompletionListMerger
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListOptimizer.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListOptimizer.cs
similarity index 98%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListOptimizer.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListOptimizer.cs
index 0fa09dcc0b2..cdf9a3766fe 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/CompletionListOptimizer.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionListOptimizer.cs
@@ -6,7 +6,7 @@
using Microsoft.VisualStudio.LanguageServer.Protocol;
using AliasedVSCommitCharacters = Microsoft.VisualStudio.LanguageServer.Protocol.SumType;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal static class CompletionListOptimizer
{
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionTriggerCharacters.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionTriggerCharacters.cs
new file mode 100644
index 00000000000..b8603e2d1f5
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/CompletionTriggerCharacters.cs
@@ -0,0 +1,23 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Collections.Frozen;
+using System.Linq;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+
+namespace Microsoft.CodeAnalysis.Razor.Completion;
+
+internal static class CompletionTriggerCharacters
+{
+ public static FrozenSet RazorTriggerCharacters { get; } = new[] { "@", "<", ":", " " }.ToFrozenSet();
+ public static FrozenSet RazorDelegationTriggerCharacters { get; } = new[] { "@" }.ToFrozenSet();
+ public static FrozenSet CSharpTriggerCharacters { get; } = new[] { " ", "(", "=", "#", ".", "<", "[", "{", "\"", "/", ":", "~" }.ToFrozenSet();
+ public static FrozenSet HtmlTriggerCharacters { get; } = new[] { ":", "@", "#", ".", "!", "*", ",", "(", "[", "-", "<", "&", "\\", "/", "'", "\"", "=", ":", " ", "`" }.ToFrozenSet();
+ public static FrozenSet AllDelegationTriggerCharacters { get; } = RazorDelegationTriggerCharacters.Union(CSharpTriggerCharacters).Union(HtmlTriggerCharacters).ToFrozenSet();
+ public static string[] AllTriggerCharacters { get; } = RazorTriggerCharacters.Union(AllDelegationTriggerCharacters).ToArray();
+
+ public static bool IsValidTrigger(FrozenSet triggerCharacters, CompletionContext completionContext)
+ => completionContext.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
+ completionContext.TriggerCharacter is null ||
+ triggerCharacters.Contains(completionContext.TriggerCharacter);
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/DelegatedCompletionHelper.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/DelegatedCompletionHelper.cs
new file mode 100644
index 00000000000..9429a1e7709
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/DelegatedCompletionHelper.cs
@@ -0,0 +1,238 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Razor.DocumentMapping;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+using Microsoft.CodeAnalysis.Razor.Protocol;
+using Microsoft.CodeAnalysis.Razor.Protocol.Completion;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+
+namespace Microsoft.CodeAnalysis.Razor.Completion.Delegation;
+
+using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
+
+///
+/// Helper methods for C# and HTML completion ("delegated" completion) that are used both in LSP and cohosting
+/// completion handler code.
+///
+internal static class DelegatedCompletionHelper
+{
+ // Ordering should be:
+ // 1. Changes items
+ // 2. Adds items
+ // 3. Filters items
+ private static readonly ImmutableArray s_delegatedCSharpCompletionResponseRewriters =
+ [new SnippetResponseRewriter(), new TextEditResponseRewriter(), new DesignTimeHelperResponseRewriter()];
+
+ // Currently we only have one HTML response re-writer. Should we ever need more, we can create a common base and a collection
+ private static readonly HtmlCommitCharacterResponseRewriter s_delegatedHtmlCompletionResponseRewriter = new HtmlCommitCharacterResponseRewriter();
+
+ ///
+ /// Modifies completion context if needed so that it's acceptable to the delegated language.
+ ///
+ /// Original completion context passed to the completion handler
+ /// Language of the completion position
+ /// Possibly modified completion context
+ /// For example, if we invoke C# completion in Razor via @ character, we will not
+ /// want C# to see @ as the trigger character and instead will transform completion context
+ /// into "invoked" and "explicit" rather than "typing", without a trigger character
+ public static VSInternalCompletionContext RewriteContext(VSInternalCompletionContext context, RazorLanguageKind languageKind)
+ {
+ Debug.Assert(languageKind != RazorLanguageKind.Razor,
+ $"{nameof(RewriteContext)} should be called for delegated completion only");
+
+ if (context.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
+ context.TriggerCharacter is not { } triggerCharacter)
+ {
+ // Non-triggered based completion, the existing context is valid.
+ return context;
+ }
+
+ if (languageKind == RazorLanguageKind.CSharp
+ && CompletionTriggerCharacters.CSharpTriggerCharacters.Contains(triggerCharacter))
+ {
+ // C# trigger character for C# content
+ return context;
+ }
+
+ if (languageKind == RazorLanguageKind.Html
+ && CompletionTriggerCharacters.HtmlTriggerCharacters.Contains(triggerCharacter))
+ {
+ // HTML trigger character for HTML content
+ return context;
+ }
+
+ // Trigger character not associated with the current language. Transform the context into an invoked context.
+ var rewrittenContext = new VSInternalCompletionContext()
+ {
+ InvokeKind = context.InvokeKind,
+ TriggerKind = CompletionTriggerKind.Invoked,
+ };
+
+ if (languageKind == RazorLanguageKind.CSharp
+ && CompletionTriggerCharacters.RazorDelegationTriggerCharacters.Contains(triggerCharacter))
+ {
+ // The C# language server will not return any completions for the '@' character unless we
+ // send the completion request explicitly.
+ rewrittenContext.InvokeKind = VSInternalCompletionInvokeKind.Explicit;
+ }
+
+ return rewrittenContext;
+ }
+
+ ///
+ /// Modifies C# completion response to be usable by Razor.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static async ValueTask RewriteCSharpResponseAsync(
+ VSInternalCompletionList? delegatedResponse,
+ int absoluteIndex,
+ DocumentContext documentContext,
+ Position projectedPosition,
+ RazorCompletionOptions completionOptions,
+ CancellationToken cancellationToken)
+ {
+ if (delegatedResponse?.Items is null)
+ {
+ // If we don't get a response from the delegated server, we have to make sure to return an incomplete completion
+ // list. When a user is typing quickly, the delegated request from the first keystroke will fail to synchronize,
+ // so if we return a "complete" list then the query won't re-query us for completion once the typing stops/slows
+ // so we'd only ever return Razor completion items.
+ return new VSInternalCompletionList() { IsIncomplete = true, Items = [] };
+ }
+
+ var rewrittenResponse = delegatedResponse;
+
+ foreach (var rewriter in s_delegatedCSharpCompletionResponseRewriters)
+ {
+ rewrittenResponse = await rewriter.RewriteAsync(
+ rewrittenResponse,
+ absoluteIndex,
+ documentContext,
+ projectedPosition,
+ completionOptions,
+ cancellationToken).ConfigureAwait(false);
+ }
+
+ return rewrittenResponse;
+ }
+
+ public static VSInternalCompletionList RewriteHtmlResponse(
+ VSInternalCompletionList? delegatedResponse,
+ RazorCompletionOptions completionOptions)
+ {
+ if (delegatedResponse?.Items is null)
+ {
+ // If we don't get a response from the delegated server, we have to make sure to return an incomplete completion
+ // list. When a user is typing quickly, the delegated request from the first keystroke will fail to synchronize,
+ // so if we return a "complete" list then the query won't re-query us for completion once the typing stops/slows
+ // so we'd only ever return Razor completion items.
+ return new VSInternalCompletionList() { IsIncomplete = true, Items = [] };
+ }
+
+ var rewrittenResponse = s_delegatedHtmlCompletionResponseRewriter.Rewrite(
+ delegatedResponse,
+ completionOptions);
+
+ return rewrittenResponse;
+ }
+
+ ///
+ /// Returns possibly update document position info and provisional edit (if any)
+ ///
+ ///
+ ///
+ /// Original position info
+ ///
+ ///
+ ///
+ ///
+ /// Provisional completion happens when typing something like @DateTime. in a document.
+ /// In this case the '.' initially is parsed as belonging to HTML. However, we want to
+ /// show C# member completion in this case, so we want to make a temporary change to the
+ /// generated C# code so that '.' ends up in C#. This method will check for such case,
+ /// and provisional completion case is detected, will update position language from HTML
+ /// to C# and will return a temporary edit that should be made to the generated document
+ /// in order to add the '.' to the generated C# contents.
+ ///
+ public static async Task TryGetProvisionalCompletionInfoAsync(
+ DocumentContext documentContext,
+ VSInternalCompletionContext completionContext,
+ DocumentPositionInfo positionInfo,
+ IDocumentMappingService documentMappingService,
+ CancellationToken cancellationToken)
+ {
+ if (positionInfo.LanguageKind != RazorLanguageKind.Html ||
+ completionContext.TriggerKind != CompletionTriggerKind.TriggerCharacter ||
+ completionContext.TriggerCharacter != ".")
+ {
+ // Invalid provisional completion context
+ return null;
+ }
+
+ if (positionInfo.Position.Character == 0)
+ {
+ // We're at the start of line. Can't have provisional completions here.
+ return null;
+ }
+
+ var previousCharacterPositionInfo = await documentMappingService
+ .GetPositionInfoAsync(documentContext, positionInfo.HostDocumentIndex - 1, cancellationToken)
+ .ConfigureAwait(false);
+
+ if (previousCharacterPositionInfo.LanguageKind != RazorLanguageKind.CSharp)
+ {
+ return null;
+ }
+
+ var previousPosition = previousCharacterPositionInfo.Position;
+
+ // Edit the CSharp projected document to contain a '.'. This allows C# completion to provide valid
+ // completion items for moments when a user has typed a '.' that's typically interpreted as Html.
+ var addProvisionalDot = VsLspFactory.CreateTextEdit(previousPosition, ".");
+
+ var provisionalPositionInfo = new DocumentPositionInfo(
+ RazorLanguageKind.CSharp,
+ VsLspFactory.CreatePosition(
+ previousPosition.Line,
+ previousPosition.Character + 1),
+ previousCharacterPositionInfo.HostDocumentIndex + 1);
+
+ return new CompletionPositionInfo(addProvisionalDot, provisionalPositionInfo, ShouldIncludeDelegationSnippets: false);
+ }
+
+ public static bool ShouldIncludeSnippets(RazorCodeDocument razorCodeDocument, int absoluteIndex)
+ {
+ var tree = razorCodeDocument.GetSyntaxTree();
+
+ var token = tree.Root.FindToken(absoluteIndex, includeWhitespace: false);
+ var node = token.Parent;
+ var startOrEndTag = node?.FirstAncestorOrSelf(n => RazorSyntaxFacts.IsAnyStartTag(n) || RazorSyntaxFacts.IsAnyEndTag(n));
+
+ if (startOrEndTag is null)
+ {
+ return token.Kind is not (SyntaxKind.OpenAngle or SyntaxKind.CloseAngle);
+ }
+
+ if (startOrEndTag.Span.Start == absoluteIndex)
+ {
+ // We're at the start of the tag, we should include snippets. This is the case for things like $$ or $$
, since the
+ // index is right associative to the token when using FindToken.
+ return true;
+ }
+
+ return !startOrEndTag.Span.Contains(absoluteIndex);
+ }
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DesignTimeHelperResponseRewriter.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/DesignTimeHelperResponseRewriter.cs
similarity index 86%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DesignTimeHelperResponseRewriter.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/DesignTimeHelperResponseRewriter.cs
index 675b2d27148..1b57f4909c9 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/DesignTimeHelperResponseRewriter.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/DesignTimeHelperResponseRewriter.cs
@@ -5,20 +5,20 @@
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
-using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
+namespace Microsoft.CodeAnalysis.Razor.Completion.Delegation;
///
/// Removes Razor design-time helpers from a C# completion list.
///
-internal class DesignTimeHelperResponseRewriter : DelegatedCompletionResponseRewriter
+internal class DesignTimeHelperResponseRewriter : IDelegatedCSharpCompletionResponseRewriter
{
private static readonly ImmutableHashSet s_designTimeHelpers = new[]
{
@@ -32,20 +32,14 @@ internal class DesignTimeHelperResponseRewriter : DelegatedCompletionResponseRew
"BuildRenderTree"
}.ToImmutableHashSet();
- public override int Order => ExecutionBehaviorOrder.FiltersCompletionItems;
-
- public override async Task RewriteAsync(
+ public async Task RewriteAsync(
VSInternalCompletionList completionList,
int hostDocumentIndex,
DocumentContext hostDocumentContext,
- DelegatedCompletionParams delegatedParameters,
+ Position projectedPosition,
+ RazorCompletionOptions completionOptions,
CancellationToken cancellationToken)
{
- if (delegatedParameters.ProjectedKind != RazorLanguageKind.CSharp)
- {
- return completionList;
- }
-
var syntaxTree = await hostDocumentContext.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var owner = syntaxTree.Root.FindInnermostNode(hostDocumentIndex);
if (owner is null)
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/HtmlCommitCharacterResponseRewriter.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/HtmlCommitCharacterResponseRewriter.cs
similarity index 65%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/HtmlCommitCharacterResponseRewriter.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/HtmlCommitCharacterResponseRewriter.cs
index 1bd58636764..5e34b3dd8f7 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/HtmlCommitCharacterResponseRewriter.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/HtmlCommitCharacterResponseRewriter.cs
@@ -3,31 +3,19 @@
using System.Collections.Generic;
using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Razor.ProjectSystem;
-using Microsoft.CodeAnalysis.Razor.Protocol;
-using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
+namespace Microsoft.CodeAnalysis.Razor.Completion.Delegation;
-internal class HtmlCommitCharacterResponseRewriter(RazorLSPOptionsMonitor razorLSPOptionsMonitor) : DelegatedCompletionResponseRewriter
+internal class HtmlCommitCharacterResponseRewriter
{
- private readonly RazorLSPOptionsMonitor _razorLSPOptionsMonitor = razorLSPOptionsMonitor;
-
- public override int Order => ExecutionBehaviorOrder.ChangesCompletionItems;
-
- public override Task RewriteAsync(VSInternalCompletionList completionList, int hostDocumentIndex, DocumentContext hostDocumentContext, DelegatedCompletionParams delegatedParameters, CancellationToken cancellationToken)
+ public VSInternalCompletionList Rewrite(
+ VSInternalCompletionList completionList,
+ RazorCompletionOptions completionOptions)
{
- if (delegatedParameters.ProjectedKind != RazorLanguageKind.Html)
- {
- return Task.FromResult(completionList);
- }
-
- if (_razorLSPOptionsMonitor.CurrentValue.CommitElementsWithSpace)
+ if (completionOptions.CommitElementsWithSpace)
{
- return Task.FromResult(completionList);
+ return completionList;
}
string[]? itemCommitChars = null;
@@ -75,6 +63,6 @@ public override Task RewriteAsync(VSInternalCompletion
}
}
- return Task.FromResult(completionList);
+ return completionList;
}
}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/IDelegatedCSharpCompletionResponseRewriter.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/IDelegatedCSharpCompletionResponseRewriter.cs
new file mode 100644
index 00000000000..fa460cbde47
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/IDelegatedCSharpCompletionResponseRewriter.cs
@@ -0,0 +1,20 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Razor.ProjectSystem;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+
+namespace Microsoft.CodeAnalysis.Razor.Completion.Delegation;
+
+internal interface IDelegatedCSharpCompletionResponseRewriter
+{
+ public Task RewriteAsync(
+ VSInternalCompletionList completionList,
+ int hostDocumentIndex,
+ DocumentContext hostDocumentContext,
+ Position projectedPosition,
+ RazorCompletionOptions completionOptions,
+ CancellationToken cancellationToken);
+}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/SnippetResponseRewriter.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/SnippetResponseRewriter.cs
similarity index 77%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/SnippetResponseRewriter.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/SnippetResponseRewriter.cs
index 85ceaa682a6..a000ff4f0a6 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/SnippetResponseRewriter.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/SnippetResponseRewriter.cs
@@ -6,10 +6,9 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
-using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
+namespace Microsoft.CodeAnalysis.Razor.Completion.Delegation;
///
/// Modifies delegated snippet completion items
@@ -18,7 +17,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
/// At the moment primarily used to modify C# "using" snippet to "using statement" snippet
/// in order to disambiguate it from Razor "using directive" snippet
///
-internal class SnippetResponseRewriter : DelegatedCompletionResponseRewriter
+internal class SnippetResponseRewriter : IDelegatedCSharpCompletionResponseRewriter
{
private static readonly FrozenDictionary s_snippetToCompletionData = new Dictionary()
{
@@ -29,9 +28,13 @@ internal class SnippetResponseRewriter : DelegatedCompletionResponseRewriter
}
.ToFrozenDictionary();
- public override int Order => ExecutionBehaviorOrder.ChangesCompletionItems;
-
- public override Task RewriteAsync(VSInternalCompletionList completionList, int hostDocumentIndex, DocumentContext hostDocumentContext, DelegatedCompletionParams delegatedParameters, CancellationToken cancellationToken)
+ public Task RewriteAsync(
+ VSInternalCompletionList completionList,
+ int hostDocumentIndex,
+ DocumentContext hostDocumentContext,
+ Position projectedPosition,
+ RazorCompletionOptions completionOptions,
+ CancellationToken cancellationToken)
{
foreach (var item in completionList.Items)
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/TextEditResponseRewriter.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/TextEditResponseRewriter.cs
similarity index 83%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/TextEditResponseRewriter.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/TextEditResponseRewriter.cs
index fff2e226c9b..120c4793638 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/Delegation/TextEditResponseRewriter.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/Delegation/TextEditResponseRewriter.cs
@@ -4,42 +4,35 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
-using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
+namespace Microsoft.CodeAnalysis.Razor.Completion.Delegation;
-internal class TextEditResponseRewriter : DelegatedCompletionResponseRewriter
+internal class TextEditResponseRewriter : IDelegatedCSharpCompletionResponseRewriter
{
- public override int Order => ExecutionBehaviorOrder.ChangesCompletionItems;
-
- public override async Task RewriteAsync(
+ public async Task RewriteAsync(
VSInternalCompletionList completionList,
int hostDocumentIndex,
DocumentContext hostDocumentContext,
- DelegatedCompletionParams delegatedParameters,
+ Position projectedPosition,
+ RazorCompletionOptions completionOptions,
CancellationToken cancellationToken)
{
- if (delegatedParameters.ProjectedKind != RazorLanguageKind.CSharp)
- {
- return completionList;
- }
-
var sourceText = await hostDocumentContext.GetSourceTextAsync(cancellationToken).ConfigureAwait(false);
var hostDocumentPosition = sourceText.GetPosition(hostDocumentIndex);
- completionList = TranslateTextEdits(hostDocumentPosition, delegatedParameters.ProjectedPosition, completionList);
+ completionList = TranslateTextEdits(hostDocumentPosition, projectedPosition, completionList);
if (completionList.ItemDefaults?.EditRange is { } editRange)
{
if (editRange.TryGetFirst(out var range))
{
- completionList.ItemDefaults.EditRange = TranslateRange(hostDocumentPosition, delegatedParameters.ProjectedPosition, range);
+ completionList.ItemDefaults.EditRange = TranslateRange(hostDocumentPosition, projectedPosition, range);
}
else if (editRange.TryGetSecond(out var insertReplaceRange))
{
- insertReplaceRange.Insert = TranslateRange(hostDocumentPosition, delegatedParameters.ProjectedPosition, insertReplaceRange.Insert);
- insertReplaceRange.Replace = TranslateRange(hostDocumentPosition, delegatedParameters.ProjectedPosition, insertReplaceRange.Replace);
+ insertReplaceRange.Insert = TranslateRange(hostDocumentPosition, projectedPosition, insertReplaceRange.Insert);
+ insertReplaceRange.Replace = TranslateRange(hostDocumentPosition, projectedPosition, insertReplaceRange.Replace);
}
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/DirectiveAttributeTransitionCompletionItemProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveAttributeTransitionCompletionItemProvider.cs
similarity index 97%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/DirectiveAttributeTransitionCompletionItemProvider.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveAttributeTransitionCompletionItemProvider.cs
index d3a0831a7bb..164c849d249 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/DirectiveAttributeTransitionCompletionItemProvider.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveAttributeTransitionCompletionItemProvider.cs
@@ -5,10 +5,9 @@
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Text;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal class DirectiveAttributeTransitionCompletionItemProvider : DirectiveAttributeCompletionItemProviderBase
{
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveCompletionItemProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveCompletionItemProvider.cs
index 8c83d70f305..9ceab30b29c 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveCompletionItemProvider.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/DirectiveCompletionItemProvider.cs
@@ -31,7 +31,6 @@ internal class DirectiveCompletionItemProvider : IRazorCompletionItemProvider
CSharpCodeParser.UsingDirectiveDescriptor
};
-
// Test accessor
internal static IEnumerable MvcDefaultDirectives => s_mvcDefaultDirectives;
internal static IEnumerable ComponentDefaultDirectives => s_componentDefaultDirectives;
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/LspRazorCompletionFactsService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/LspRazorCompletionFactsService.cs
similarity index 78%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/LspRazorCompletionFactsService.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/LspRazorCompletionFactsService.cs
index 165084b628c..9822140d6eb 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/LspRazorCompletionFactsService.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/LspRazorCompletionFactsService.cs
@@ -3,9 +3,8 @@
using System.Collections.Generic;
using System.Collections.Immutable;
-using Microsoft.CodeAnalysis.Razor.Completion;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal sealed class LspRazorCompletionFactsService(IEnumerable providers)
: AbstractRazorCompletionFactsService(providers.ToImmutableArray())
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/MarkupTransitionCompletionDescription.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/MarkupTransitionCompletionDescription.cs
similarity index 82%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/MarkupTransitionCompletionDescription.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/MarkupTransitionCompletionDescription.cs
index 6fd0a551757..b85653659a7 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/MarkupTransitionCompletionDescription.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/MarkupTransitionCompletionDescription.cs
@@ -2,9 +2,8 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
-using Microsoft.CodeAnalysis.Razor.Completion;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal class MarkupTransitionCompletionDescription : CompletionDescription
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/MarkupTransitionCompletionItemProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/MarkupTransitionCompletionItemProvider.cs
similarity index 96%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/MarkupTransitionCompletionItemProvider.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/MarkupTransitionCompletionItemProvider.cs
index 12b105acbce..b63d6545034 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/MarkupTransitionCompletionItemProvider.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/MarkupTransitionCompletionItemProvider.cs
@@ -6,12 +6,10 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
-using Microsoft.CodeAnalysis.Razor;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.Editor.Razor;
using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal class MarkupTransitionCompletionItemProvider : IRazorCompletionItemProvider
{
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionItemExtensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionItemExtensions.cs
index bf3d8f5d7c5..f83b8e411d3 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionItemExtensions.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionItemExtensions.cs
@@ -11,45 +11,27 @@ internal static class RazorCompletionItemExtensions
{
private readonly static string s_attributeCompletionDescriptionKey = "Razor.AttributeDescription";
private readonly static string s_directiveCompletionDescriptionKey = "Razor.DirectiveDescription";
+ private readonly static string s_markupTransitionDescriptionKey = "Razor.MarkupTransitionDescription";
+ private readonly static string s_tagHelperElementCompletionDescriptionKey = "Razor.TagHelperElementDescription";
public static void SetAttributeCompletionDescription(this RazorCompletionItem completionItem, AggregateBoundAttributeDescription attributeCompletionDescription)
{
- if (completionItem is null)
- {
- throw new ArgumentNullException(nameof(completionItem));
- }
-
completionItem.Items[s_attributeCompletionDescriptionKey] = attributeCompletionDescription;
}
public static AggregateBoundAttributeDescription? GetAttributeCompletionDescription(this RazorCompletionItem completionItem)
{
- if (completionItem is null)
- {
- throw new ArgumentNullException(nameof(completionItem));
- }
-
var attributeCompletionDescription = completionItem.Items[s_attributeCompletionDescriptionKey] as AggregateBoundAttributeDescription;
return attributeCompletionDescription;
}
public static void SetDirectiveCompletionDescription(this RazorCompletionItem completionItem, DirectiveCompletionDescription attributeCompletionDescription)
{
- if (completionItem is null)
- {
- throw new ArgumentNullException(nameof(completionItem));
- }
-
completionItem.Items[s_directiveCompletionDescriptionKey] = attributeCompletionDescription;
}
public static DirectiveCompletionDescription? GetDirectiveCompletionDescription(this RazorCompletionItem completionItem)
{
- if (completionItem is null)
- {
- throw new ArgumentNullException(nameof(completionItem));
- }
-
var attributeCompletionDescription = completionItem.Items[s_directiveCompletionDescriptionKey] as DirectiveCompletionDescription;
return attributeCompletionDescription;
}
@@ -68,4 +50,26 @@ public static IEnumerable GetAttributeCompletionTypes(this RazorCompleti
yield return descriptionInfo.ReturnTypeName;
}
}
+
+ public static void SetMarkupTransitionCompletionDescription(this RazorCompletionItem completionItem, MarkupTransitionCompletionDescription markupTransitionCompletionDescription)
+ {
+ completionItem.Items[s_markupTransitionDescriptionKey] = markupTransitionCompletionDescription;
+ }
+
+ public static MarkupTransitionCompletionDescription? GetMarkupTransitionCompletionDescription(this RazorCompletionItem completionItem)
+ {
+ var markupTransitionCompletionDescription = completionItem.Items[s_markupTransitionDescriptionKey] as MarkupTransitionCompletionDescription;
+ return markupTransitionCompletionDescription;
+ }
+
+ public static void SetTagHelperElementDescriptionInfo(this RazorCompletionItem completionItem, AggregateBoundElementDescription elementDescriptionInfo)
+ {
+ completionItem.Items[s_tagHelperElementCompletionDescriptionKey] = elementDescriptionInfo;
+ }
+
+ public static AggregateBoundElementDescription? GetTagHelperElementDescriptionInfo(this RazorCompletionItem completionItem)
+ {
+ var description = completionItem.Items[s_tagHelperElementCompletionDescriptionKey] as AggregateBoundElementDescription;
+ return description;
+ }
}
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionListProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionListProvider.cs
similarity index 97%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionListProvider.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionListProvider.cs
index ea0b2a76a02..1585b98a91d 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionListProvider.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionListProvider.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
@@ -11,12 +12,11 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.LanguageServer.Protocol;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal class RazorCompletionListProvider(
IRazorCompletionFactsService completionFactsService,
@@ -33,7 +33,7 @@ internal class RazorCompletionListProvider(
};
// virtual for tests
- public virtual ImmutableHashSet TriggerCharacters => new[] { "@", "<", ":", " " }.ToImmutableHashSet();
+ public virtual FrozenSet TriggerCharacters => CompletionTriggerCharacters.RazorTriggerCharacters;
// virtual for tests
public virtual async Task GetCompletionListAsync(
@@ -42,6 +42,7 @@ internal class RazorCompletionListProvider(
DocumentContext documentContext,
VSInternalClientCapabilities clientCapabilities,
HashSet? existingCompletions,
+ RazorCompletionOptions completionOptions,
CancellationToken cancellationToken)
{
if (!IsApplicableTriggerContext(completionContext))
@@ -57,7 +58,6 @@ internal class RazorCompletionListProvider(
_ => CompletionReason.Typing,
};
- var completionOptions = new RazorCompletionOptions(SnippetsSupported: true);
var syntaxTree = await documentContext.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var tagHelperContext = await documentContext.GetTagHelperContextAsync(cancellationToken).ConfigureAwait(false);
var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex, includeWhitespace: true, walkMarkersBack: true);
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionOptions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionOptions.cs
index de2d65c2406..0980d4b7f49 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionOptions.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionOptions.cs
@@ -3,4 +3,7 @@
namespace Microsoft.CodeAnalysis.Razor.Completion;
-internal record struct RazorCompletionOptions(bool SnippetsSupported);
+internal record struct RazorCompletionOptions(
+ bool SnippetsSupported,
+ bool AutoInsertAttributeQuotes,
+ bool CommitElementsWithSpace);
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionResolveContext.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionResolveContext.cs
similarity index 73%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionResolveContext.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionResolveContext.cs
index 937b1a42709..11c10755ef7 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/RazorCompletionResolveContext.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/RazorCompletionResolveContext.cs
@@ -2,8 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
-using Microsoft.CodeAnalysis.Razor.Completion;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal record RazorCompletionResolveContext(string FilePath, ImmutableArray CompletionItems);
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/TagHelperCompletionProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/TagHelperCompletionProvider.cs
similarity index 96%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/TagHelperCompletionProvider.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/TagHelperCompletionProvider.cs
index ba07f35576c..00c4fad030d 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/TagHelperCompletionProvider.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/TagHelperCompletionProvider.cs
@@ -10,11 +10,12 @@
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Editor.Razor;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
+
+using SyntaxNode = AspNetCore.Razor.Language.Syntax.SyntaxNode;
internal class TagHelperCompletionProvider : IRazorCompletionItemProvider
{
@@ -27,14 +28,11 @@ internal class TagHelperCompletionProvider : IRazorCompletionItemProvider
private static readonly ImmutableArray s_elementCommitCharacters_WithoutSpace = RazorCommitCharacter.CreateArray([">"]);
private readonly ITagHelperCompletionService _tagHelperCompletionService;
- private readonly RazorLSPOptionsMonitor _optionsMonitor;
public TagHelperCompletionProvider(
- ITagHelperCompletionService tagHelperCompletionService,
- RazorLSPOptionsMonitor optionsMonitor)
+ ITagHelperCompletionService tagHelperCompletionService)
{
_tagHelperCompletionService = tagHelperCompletionService;
- _optionsMonitor = optionsMonitor;
}
public ImmutableArray GetCompletionItems(RazorCompletionContext context)
@@ -172,7 +170,7 @@ private ImmutableArray GetAttributeCompletions(
// Do not turn attributes into snippets if we are in an already written full attribute (https://github.com/dotnet/razor-tooling/issues/6724)
if (containingAttribute is not (MarkupTagHelperAttributeSyntax or MarkupAttributeBlockSyntax) &&
- TryResolveInsertText(insertText, attributeContext, out var snippetText))
+ TryResolveInsertText(insertText, attributeContext, options.AutoInsertAttributeQuotes, out var snippetText))
{
isSnippet = true;
insertText = snippetText;
@@ -209,11 +207,11 @@ private ImmutableArray GetAttributeCompletions(
return completionItems.DrainToImmutable();
}
- private bool TryResolveInsertText(string baseInsertText, AttributeContext context, [NotNullWhen(true)] out string? snippetText)
+ private bool TryResolveInsertText(string baseInsertText, AttributeContext context, bool autoInsertAttributeQuotes, [NotNullWhen(true)] out string? snippetText)
{
if (context == AttributeContext.FullSnippet)
{
- snippetText = _optionsMonitor.CurrentValue.AutoInsertAttributeQuotes
+ snippetText = autoInsertAttributeQuotes
? $"{baseInsertText}=\"$0\""
: $"{baseInsertText}=$0";
@@ -244,7 +242,7 @@ private ImmutableArray GetElementCompletions(
var completionResult = _tagHelperCompletionService.GetElementCompletions(elementCompletionContext);
using var completionItems = new PooledArrayBuilder();
- var commitChars = _optionsMonitor.CurrentValue.CommitElementsWithSpace
+ var commitChars = context.Options.CommitElementsWithSpace
? s_elementCommitCharacters
: s_elementCommitCharacters_WithoutSpace;
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/LspTagHelperCompletionService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/TagHelperCompletionService.cs
similarity index 98%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/LspTagHelperCompletionService.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/TagHelperCompletionService.cs
index a286267c233..e4bcd48662c 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/LspTagHelperCompletionService.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/TagHelperCompletionService.cs
@@ -8,13 +8,12 @@
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.Editor.Razor;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
-internal class LspTagHelperCompletionService : ITagHelperCompletionService
+internal class TagHelperCompletionService : ITagHelperCompletionService
{
private static readonly HashSet s_emptyHashSet = new();
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/VSInternalCompletionItemExtensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionItemExtensions.cs
similarity index 95%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/VSInternalCompletionItemExtensions.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionItemExtensions.cs
index c52fe46804c..e4f4330726c 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/VSInternalCompletionItemExtensions.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionItemExtensions.cs
@@ -4,12 +4,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
-using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Razor.PooledObjects;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.LanguageServer.Protocol;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal static class VSInternalCompletionItemExtensions
{
diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/VSInternalCompletionListExtensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionListExtensions.cs
similarity index 95%
rename from src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/VSInternalCompletionListExtensions.cs
rename to src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionListExtensions.cs
index 94a502eb190..6e4b52b9cb3 100644
--- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Completion/VSInternalCompletionListExtensions.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Completion/VSInternalCompletionListExtensions.cs
@@ -1,13 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
-using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.VisualStudio.LanguageServer.Protocol;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal static class VSInternalCompletionListExtensions
{
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/DocumentPositionInfo.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/DocumentPositionInfo.cs
index bbb745aea2c..07524302032 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/DocumentPositionInfo.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/DocumentPositionInfo.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
+using System.Text.Json.Serialization;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@@ -10,4 +11,11 @@ namespace Microsoft.CodeAnalysis.Razor.DocumentMapping;
/// Represents a position in a document. If is Razor then the position will be
/// in the host document, otherwise it will be in the corresponding generated document.
///
-internal readonly record struct DocumentPositionInfo(RazorLanguageKind LanguageKind, Position Position, int HostDocumentIndex);
+internal record struct DocumentPositionInfo(
+
+ [property:JsonPropertyName("languageKind")] RazorLanguageKind LanguageKind,
+
+ [property: JsonPropertyName("position")] Position Position,
+
+ [property:JsonPropertyName("hostDocumentIndex")] int HostDocumentIndex);
+
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/Competion/CompletionPositionInfo.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/Competion/CompletionPositionInfo.cs
new file mode 100644
index 00000000000..159bc27b2af
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/Competion/CompletionPositionInfo.cs
@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Text.Json.Serialization;
+using Microsoft.CodeAnalysis.Razor.DocumentMapping;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+
+namespace Microsoft.CodeAnalysis.Razor.Protocol.Completion;
+
+///
+/// Completion-related information about a position.
+///
+/// Text edit that should be applied to generated C# document prior to invoking completion
+///
+/// Provisional completion happens when the user just type "." in something like @DateTime.
+/// and the dot is initially in HTML rather than C#. Since we don't want HTML completions
+/// in that case, we cheat and modify C# buffer immediately but temporarily, not waiting for
+/// reparse/regen, before showing completion.
+///
+/// Document position mapping data for language mappings
+/// Indicates that snippets should be added to delegated completion list (currently for HTML only)
+internal record struct CompletionPositionInfo(
+ [property:JsonPropertyName("provisionalTextEdit")] TextEdit? ProvisionalTextEdit,
+ [property:JsonPropertyName("documentPositionInfo")] DocumentPositionInfo DocumentPositionInfo,
+ [property:JsonPropertyName("shouldIncludeDelegationSnippets")] bool ShouldIncludeDelegationSnippets);
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteCompletionService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteCompletionService.cs
new file mode 100644
index 00000000000..d57cb2f4c00
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/IRemoteCompletionService.cs
@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.ExternalAccess.Razor;
+using Microsoft.CodeAnalysis.Razor.Completion;
+using Microsoft.CodeAnalysis.Razor.Protocol.Completion;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+using Response = Microsoft.CodeAnalysis.Razor.Remote.RemoteResponse;
+
+namespace Microsoft.CodeAnalysis.Razor.Remote;
+
+internal interface IRemoteCompletionService : IRemoteJsonService
+{
+ ValueTask GetPositionInfoAsync(
+ JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
+ JsonSerializableDocumentId documentId,
+ VSInternalCompletionContext completionContext,
+ Position position,
+ CancellationToken cancellationToken);
+
+ ValueTask GetCompletionAsync(
+ JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
+ JsonSerializableDocumentId documentId,
+ CompletionPositionInfo positionInfo,
+ VSInternalCompletionContext completionContext,
+ VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions razorCompletionOptions,
+ HashSet existingHtmlCompletions,
+ CancellationToken cancellationToken);
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs
index 2e4257cdb3c..8ced461079b 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Remote/RazorServices.cs
@@ -36,6 +36,7 @@ internal static class RazorServices
(typeof(IRemoteRenameService), null),
(typeof(IRemoteGoToImplementationService), null),
(typeof(IRemoteDiagnosticsService), null),
+ (typeof(IRemoteCompletionService), null),
];
private const string ComponentName = "Razor";
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/SR.resx b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/SR.resx
index e0a0efc1cdd..3169d37cb1f 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/SR.resx
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/SR.resx
@@ -172,4 +172,13 @@
Razor TagHelper Element Glyph
+
+ "Re-trigger completions..."
+
+
+ Blazor directive attributes
+
+
+ statement
+
\ No newline at end of file
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.cs.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.cs.xlf
index 31f86168480..a525119117e 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.cs.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.cs.xlf
@@ -7,6 +7,11 @@
Hodnota nesmí být null ani prázdný řetězec.
+
+
+ Blazor directive attributes
+
+
Diagnostika po:
@@ -74,6 +79,11 @@
Proběhl dotaz na řádek {0} mimo rozsah {1} {2}. Dokument nemusí být aktuální.
+
+
+ statement
+
+
Piktogram atributu Razor TagHelper
@@ -84,6 +94,11 @@
Piktogram elementu Razor TagHelper
+
+
+ "Re-trigger completions..."
+
+
Došlo k pokusu navštívit RazorMetaCode jiný než '{' or '}'.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.de.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.de.xlf
index 1726a2f73d0..60480c673e9 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.de.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.de.xlf
@@ -7,6 +7,11 @@
Der Wert darf nicht NULL oder eine leere Zeichenfolge sein.
+
+
+ Blazor directive attributes
+
+
Diagnose nach:
@@ -74,6 +79,11 @@
Die Zeile "{0}" außerhalb des {1} Bereichs von "{2}" wurde abgefragt. Das Dokument ist möglicherweise nicht auf dem neuesten Stand.
+
+
+ statement
+
+
Razor TagHelper-Attributsymbol
@@ -84,6 +94,11 @@
Razor TagHelper-Elementsymbol
+
+
+ "Re-trigger completions..."
+
+
Es wurde versucht, einen anderen RazorMetaCode als '{' or '}' zu besuchen.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.es.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.es.xlf
index af8cec0ae9d..9505ac9e5b1 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.es.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.es.xlf
@@ -7,6 +7,11 @@
El valor no puede ser nulo ni una cadena vacía.
+
+
+ Blazor directive attributes
+
+
Diagnósticos después de:
@@ -74,6 +79,11 @@
La línea '{0}' se consultó fuera del {1} rango de '{2}'. Es posible que el documento no esté actualizado.
+
+
+ statement
+
+
Glifo del atributo TagHelper de Razor
@@ -84,6 +94,11 @@
Glifo del elemento TagHelper de Razor
+
+
+ "Re-trigger completions..."
+
+
Se intentó visitar un RazorMetaCode distinto de '{' or '}'.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.fr.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.fr.xlf
index 83af245e366..46b4b0f6049 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.fr.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.fr.xlf
@@ -7,6 +7,11 @@
La valeur ne peut pas être Null ni être une chaîne vide.
+
+
+ Blazor directive attributes
+
+
Diagnostics après :
@@ -74,6 +79,11 @@
La ligne «{0}» en dehors de la plage{1} de «{2}» a été interrogée. Le document n’est peut-être pas à jour.
+
+
+ statement
+
+
Glyphe d’attribut Razor TagHelper
@@ -84,6 +94,11 @@
Glyphe de l’élément Razor TagHelper
+
+
+ "Re-trigger completions..."
+
+
Tentative de visite d’un RazorMetaCode autre que '{' or '}'.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.it.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.it.xlf
index 5317948e62d..b531d08d38e 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.it.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.it.xlf
@@ -7,6 +7,11 @@
Il valore non può essere null o una stringa vuota.
+
+
+ Blazor directive attributes
+
+
Diagnostica dopo:
@@ -74,6 +79,11 @@
È stata eseguita una query sulla riga '{0}' non compresa nell'intervallo {1} di '{2}'. Il documento potrebbe non essere aggiornato.
+
+
+ statement
+
+
Glifo attributo TagHelper Razor
@@ -84,6 +94,11 @@
Glifo elemento TagHelper Razor
+
+
+ "Re-trigger completions..."
+
+
È stato effettuato un tentativo di visitare un elemento RazorMetaCode diverso da '{' or '}'.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ja.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ja.xlf
index 55bbf78dcd9..fe38fd32116 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ja.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ja.xlf
@@ -7,6 +7,11 @@
値を null または空の文字列にすることはできません。
+
+
+ Blazor directive attributes
+
+
次の時間が経過した後の診断:
@@ -74,6 +79,11 @@
'{2}' の {1} 範囲外の行 '{0}' がクエリされました。ドキュメントが最新ではない可能性があります。
+
+
+ statement
+
+
Razor TagHelper 属性のグリフ
@@ -84,6 +94,11 @@
Razor TagHelper 要素のグリフ
+
+
+ "Re-trigger completions..."
+
+
'{' or '}' 以外の RazorMetaCode にアクセスしようとしました。
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ko.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ko.xlf
index 2931b15ec1e..3b5af63e4f9 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ko.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ko.xlf
@@ -7,6 +7,11 @@
값은 null이거나 빈 문자열일 수 없습니다.
+
+
+ Blazor directive attributes
+
+
다음 이후 진단:
@@ -74,6 +79,11 @@
'{2}'의 {1} 범위를 벗어나는 줄 '{0}'을(를) 쿼리했습니다. 문서가 최신이 아닐 수 있습니다.
+
+
+ statement
+
+
Razor TagHelper 특성 문자 모양
@@ -84,6 +94,11 @@
Razor TagHelper 요소 문자 모양
+
+
+ "Re-trigger completions..."
+
+
''{' or '}' 이외의 RazorMetaCode를 방문하려고 했습니다.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pl.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pl.xlf
index 60f05b66521..c32348f165b 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pl.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pl.xlf
@@ -7,6 +7,11 @@
Wartość nie może być wartością null ani pustym ciągiem.
+
+
+ Blazor directive attributes
+
+
Diagnostyka po:
@@ -74,6 +79,11 @@
Wykonano zapytanie wiersza "{0}" poza zakresem {1} "{2}". Dokument może być nieaktualny.
+
+
+ statement
+
+
Symbol atrybutu pomocnika tagów składni Razor
@@ -84,6 +94,11 @@
Symbol elementu pomocnika tagów składni Razor
+
+
+ "Re-trigger completions..."
+
+
Podjęto próbę odwiedzenia elementu RazorMetaCode innego niż '{' or '}'.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pt-BR.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pt-BR.xlf
index e834585d7ed..c5489e591f9 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pt-BR.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.pt-BR.xlf
@@ -7,6 +7,11 @@
O valor não pode ser nulo ou uma cadeia de caracteres vazia.
+
+
+ Blazor directive attributes
+
+
Diagnóstico após:
@@ -74,6 +79,11 @@
A linha '{0}' fora do intervalo {1} de '{2}' foi consultada. O documento pode não estar atualizado.
+
+
+ statement
+
+
Atributo Glyph Razor TagHelper
@@ -84,6 +94,11 @@
Elemento Glyph Razor TagHelper
+
+
+ "Re-trigger completions..."
+
+
Tentativa de visitar um RazorMetaCode diferente de '{' or '}'.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ru.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ru.xlf
index b4577c8ec99..a147db65cf6 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ru.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.ru.xlf
@@ -7,6 +7,11 @@
Значение не может быть NULL или пустой строкой.
+
+
+ Blazor directive attributes
+
+
Диагностика после:
@@ -74,6 +79,11 @@
Запрошена строка "{0}" за пределами диапазона {1} "{2}". Возможно, документ не обновлен.
+
+
+ statement
+
+
Глиф атрибута TagHelper Razor
@@ -84,6 +94,11 @@
Глиф элемента TagHelper Razor
+
+
+ "Re-trigger completions..."
+
+
Предпринята попытка посетить RazorMetaCode, отличный от "{' or '}".
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.tr.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.tr.xlf
index 0faa1367043..57ed7cbb401 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.tr.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.tr.xlf
@@ -7,6 +7,11 @@
Değer null veya boş bir dize olamaz.
+
+
+ Blazor directive attributes
+
+
Şundan sonraki tanılama:
@@ -74,6 +79,11 @@
{1} / '{2}' aralığının dışındaki '{0}' satırı sorgulandı. Belge güncel olmayabilir.
+
+
+ statement
+
+
Razor TagHelper Öznitelik Karakteri
@@ -84,6 +94,11 @@
Razor TagHelper Element Karakteri
+
+
+ "Re-trigger completions..."
+
+
'{' or '}' dışında bir RazorMetaCode ziyaret edilmeye çalışıldı.
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hans.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hans.xlf
index 51764818206..e2ca9b6f9c4 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hans.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hans.xlf
@@ -7,6 +7,11 @@
值不能为 null 或空字符串。
+
+
+ Blazor directive attributes
+
+
诊断后:
@@ -74,6 +79,11 @@
查询了 "{0}" 的 {1} 范围外的行 "{2}"。文档可能不是最新的。
+
+
+ statement
+
+
Razor TagHelper 特性字形
@@ -84,6 +94,11 @@
Razor TagHelper 元素字形
+
+
+ "Re-trigger completions..."
+
+
已尝试访问除 "{' or '}" 之外的 RazorMetaCode。
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hant.xlf b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hant.xlf
index 74d00b2d74b..a7e3766365a 100644
--- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hant.xlf
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Resources/xlf/SR.zh-Hant.xlf
@@ -7,6 +7,11 @@
值不能為 Null 或空字串。
+
+
+ Blazor directive attributes
+
+
在以下事項之後的診斷:
@@ -74,6 +79,11 @@
已查詢 '{2}' 之 {1} 範圍以外的行 '{0}'。文件可能不是最新狀態。
+
+
+ statement
+
+
Razor TagHelper 屬性字元
@@ -84,6 +94,11 @@
Razor TagHelper 元素字符
+
+
+ "Re-trigger completions..."
+
+
嘗試瀏覽 '{' or '}' 除外的 RazorMetaCode。
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPCompletionListCache.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPCompletionListCache.cs
new file mode 100644
index 00000000000..6f8e4af1298
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPCompletionListCache.cs
@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Composition;
+using Microsoft.CodeAnalysis.Razor.Completion;
+
+namespace Microsoft.CodeAnalysis.Remote.Razor.Completion;
+
+[Export(typeof(CompletionListCache)), Shared]
+internal sealed class OOPCompletionListCache : CompletionListCache
+{
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionFactsService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionFactsService.cs
new file mode 100644
index 00000000000..411e1679943
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionFactsService.cs
@@ -0,0 +1,16 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Composition;
+using Microsoft.CodeAnalysis.Razor.Completion;
+
+namespace Microsoft.CodeAnalysis.Remote.Razor.Completion;
+
+[Export(typeof(IRazorCompletionFactsService)), Shared]
+[method: ImportingConstructor]
+internal sealed class OOPRazorCompletionFactsService([ImportMany] IEnumerable providers)
+ : AbstractRazorCompletionFactsService(providers.ToImmutableArray())
+{
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionItemProviders.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionItemProviders.cs
new file mode 100644
index 00000000000..438e73b4d81
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionItemProviders.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Composition;
+using Microsoft.CodeAnalysis.Razor.Completion;
+
+namespace Microsoft.CodeAnalysis.Remote.Razor.Completion;
+
+[Export(typeof(IRazorCompletionItemProvider)), Shared]
+internal sealed class OOPDirectiveCompletionItemProvider : DirectiveCompletionItemProvider;
+
+[Export(typeof(IRazorCompletionItemProvider)), Shared]
+internal sealed class OOPDirectiveAttributeCompletionItemProvider : DirectiveAttributeCompletionItemProvider;
+
+[Export(typeof(IRazorCompletionItemProvider)), Shared]
+internal sealed class OOPDirectiveAttributeParameterCompletionItemProvider : DirectiveAttributeParameterCompletionItemProvider;
+
+[Export(typeof(IRazorCompletionItemProvider)), Shared]
+internal sealed class OOPDirectiveAttributeTransitionCompletionItemProvider : DirectiveAttributeTransitionCompletionItemProvider;
+
+[Export(typeof(IRazorCompletionItemProvider)), Shared]
+internal sealed class OOPMarkupTransitionCompletionItemProvider : MarkupTransitionCompletionItemProvider;
+
+[Export(typeof(IRazorCompletionItemProvider)), Shared]
+[method: ImportingConstructor]
+internal sealed class OOPTagHelperCompletionProvider(ITagHelperCompletionService tagHelperCompletionService)
+ : TagHelperCompletionProvider(tagHelperCompletionService);
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionListProvider.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionListProvider.cs
new file mode 100644
index 00000000000..4d6776cc816
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPRazorCompletionListProvider.cs
@@ -0,0 +1,18 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Composition;
+using Microsoft.CodeAnalysis.Razor.Completion;
+using Microsoft.CodeAnalysis.Razor.Logging;
+
+namespace Microsoft.CodeAnalysis.Remote.Razor.Completion;
+
+[Export(typeof(RazorCompletionListProvider)), Shared]
+[method: ImportingConstructor]
+internal sealed class OOPRazorCompletionListProvider(
+ IRazorCompletionFactsService completionFactsService,
+ CompletionListCache completionListCache,
+ ILoggerFactory loggerFactory)
+: RazorCompletionListProvider(completionFactsService, completionListCache, loggerFactory)
+{
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPTagHelperCompletionService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPTagHelperCompletionService.cs
new file mode 100644
index 00000000000..adc9c10fa69
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/OOPTagHelperCompletionService.cs
@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Composition;
+using Microsoft.CodeAnalysis.Razor.Completion;
+
+namespace Microsoft.CodeAnalysis.Remote.Razor.Completion;
+
+[Export(typeof(ITagHelperCompletionService)), Shared]
+internal sealed class OOPTagHelperCompletionService : TagHelperCompletionService
+{
+}
diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs
new file mode 100644
index 00000000000..37a6e737c59
--- /dev/null
+++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/Completion/RemoteCompletionService.cs
@@ -0,0 +1,228 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.ExternalAccess.Razor;
+using Microsoft.CodeAnalysis.Razor.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion.Delegation;
+using Microsoft.CodeAnalysis.Razor.Protocol;
+using Microsoft.CodeAnalysis.Razor.Protocol.Completion;
+using Microsoft.CodeAnalysis.Razor.Remote;
+using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+using Response = Microsoft.CodeAnalysis.Razor.Remote.RemoteResponse;
+using RoslynCompletionContext = Roslyn.LanguageServer.Protocol.CompletionContext;
+using RoslynCompletionSetting = Roslyn.LanguageServer.Protocol.CompletionSetting;
+
+namespace Microsoft.CodeAnalysis.Remote.Razor;
+
+internal sealed class RemoteCompletionService(in ServiceArgs args) : RazorDocumentServiceBase(in args), IRemoteCompletionService
+{
+ internal sealed class Factory : FactoryBase
+ {
+ protected override IRemoteCompletionService CreateService(in ServiceArgs args)
+ => new RemoteCompletionService(in args);
+ }
+
+ private readonly RazorCompletionListProvider _razorCompletionListProvider = args.ExportProvider.GetExportedValue();
+
+ public ValueTask GetPositionInfoAsync(
+ JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
+ JsonSerializableDocumentId documentId,
+ VSInternalCompletionContext completionContext,
+ Position position,
+ CancellationToken cancellationToken)
+ => RunServiceAsync(
+ solutionInfo,
+ documentId,
+ context => GetPositionInfoAsync(context, completionContext, position, cancellationToken),
+ cancellationToken);
+
+ private async ValueTask GetPositionInfoAsync(
+ RemoteDocumentContext remoteDocumentContext,
+ VSInternalCompletionContext completionContext,
+ Position position,
+ CancellationToken cancellationToken)
+ {
+ var sourceText = await remoteDocumentContext.GetSourceTextAsync(cancellationToken).ConfigureAwait(false);
+ if (!sourceText.TryGetAbsoluteIndex(position, out var index))
+ {
+ return null;
+ }
+
+ var codeDocument = await remoteDocumentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
+
+ var positionInfo = GetPositionInfo(codeDocument, index);
+
+ if(positionInfo.LanguageKind != RazorLanguageKind.Razor
+ && await DelegatedCompletionHelper.TryGetProvisionalCompletionInfoAsync(
+ remoteDocumentContext,
+ completionContext,
+ positionInfo,
+ DocumentMappingService,
+ cancellationToken) is { } provisionalCompletionInfo)
+ {
+ return new CompletionPositionInfo(
+ provisionalCompletionInfo.ProvisionalTextEdit,
+ provisionalCompletionInfo.DocumentPositionInfo,
+ ShouldIncludeDelegationSnippets: false);
+ }
+
+ var shouldIncludeSnippets = positionInfo.LanguageKind == RazorLanguageKind.Html
+ && DelegatedCompletionHelper.ShouldIncludeSnippets(codeDocument, index);
+
+ return new CompletionPositionInfo(ProvisionalTextEdit: null, positionInfo, shouldIncludeSnippets);
+ }
+
+ public ValueTask GetCompletionAsync(
+ JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
+ JsonSerializableDocumentId documentId,
+ CompletionPositionInfo positionInfo,
+ VSInternalCompletionContext completionContext,
+ VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions razorCompletionOptions,
+ HashSet existingHtmlCompletions,
+ CancellationToken cancellationToken)
+ => RunServiceAsync(
+ solutionInfo,
+ documentId,
+ context => GetCompletionAsync(
+ context,
+ positionInfo,
+ completionContext,
+ clientCapabilities,
+ razorCompletionOptions,
+ existingHtmlCompletions,
+ cancellationToken),
+ cancellationToken);
+
+ private async ValueTask GetCompletionAsync(
+ RemoteDocumentContext remoteDocumentContext,
+ CompletionPositionInfo positionInfo,
+ VSInternalCompletionContext completionContext,
+ VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions razorCompletionOptions,
+ HashSet existingDelegatedCompletions,
+ CancellationToken cancellationToken)
+ {
+ VSInternalCompletionList? csharpCompletionList = null;
+ var documentPositionInfo = positionInfo.DocumentPositionInfo;
+ if (documentPositionInfo.LanguageKind == RazorLanguageKind.CSharp &&
+ CompletionTriggerCharacters.IsValidTrigger(CompletionTriggerCharacters.CSharpTriggerCharacters, completionContext))
+ {
+ var mappedPosition = documentPositionInfo.Position;
+ csharpCompletionList = await GetCSharpCompletionAsync(
+ remoteDocumentContext,
+ documentPositionInfo.HostDocumentIndex,
+ mappedPosition,
+ positionInfo.ProvisionalTextEdit,
+ completionContext,
+ clientCapabilities,
+ razorCompletionOptions,
+ cancellationToken);
+
+ if (csharpCompletionList is not null)
+ {
+ Debug.Assert(existingDelegatedCompletions.Count == 0, "Delegated completion should be either C# or HTML, not both");
+ existingDelegatedCompletions.UnionWith(csharpCompletionList.Items.Select((item) => item.Label));
+ }
+ }
+
+ var razorCompletionList = CompletionTriggerCharacters.IsValidTrigger(CompletionTriggerCharacters.RazorTriggerCharacters, completionContext)
+ ? await _razorCompletionListProvider.GetCompletionListAsync(
+ documentPositionInfo.HostDocumentIndex,
+ completionContext,
+ remoteDocumentContext,
+ clientCapabilities,
+ existingCompletions: existingDelegatedCompletions,
+ razorCompletionOptions,
+ cancellationToken)
+ : null;
+
+ if (CompletionListMerger.Merge(razorCompletionList, csharpCompletionList) is not { } mergedCompletionList)
+ {
+ return Response.CallHtml;
+ }
+
+ return Response.Results(mergedCompletionList);
+ }
+
+ private async ValueTask GetCSharpCompletionAsync(
+ RemoteDocumentContext remoteDocumentContext,
+ int documentIndex,
+ Position mappedPosition,
+ TextEdit? provisionalTextEdit,
+ CompletionContext completionContext,
+ VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions razorCompletionOptions,
+ CancellationToken cancellationToken)
+ {
+ var generatedDocument = await remoteDocumentContext.Snapshot
+ .GetGeneratedDocumentAsync(cancellationToken).ConfigureAwait(false);
+ if (provisionalTextEdit is not null)
+ {
+ var generatedText = await generatedDocument.GetTextAsync(cancellationToken);
+ var startIndex = generatedText.GetPosition(provisionalTextEdit.Range.Start);
+ var endIndex = generatedText.GetPosition(provisionalTextEdit.Range.End);
+ var generatedTextWithEdit = generatedText.Replace(startIndex, length: endIndex - startIndex, newText: provisionalTextEdit.NewText);
+ generatedDocument = generatedDocument.WithText(generatedTextWithEdit);
+ }
+
+ // This is, to say the least, not ideal. In future we're going to normalize on to Roslyn LSP types, and this can go.
+ var options = new JsonSerializerOptions();
+ foreach (var converter in RazorServiceDescriptorsWrapper.GetLspConverters())
+ {
+ options.Converters.Add(converter);
+ }
+
+ if (JsonSerializer.Deserialize(JsonSerializer.SerializeToDocument(completionContext), options) is not { } roslynCompletionContext)
+ {
+ return null;
+ }
+
+ if (JsonSerializer.Deserialize(JsonSerializer.SerializeToDocument(clientCapabilities.TextDocument?.Completion), options) is not { } roslynCompletionSetting)
+ {
+ return null;
+ }
+
+ var mappedLinePosition = mappedPosition.ToLinePosition();
+ var roslynCompletionList = await ExternalAccess.Razor.Cohost.Handlers.Completion.GetCompletionListAsync(
+ generatedDocument,
+ mappedLinePosition,
+ roslynCompletionContext,
+ clientCapabilities.SupportsVisualStudioExtensions,
+ roslynCompletionSetting,
+ cancellationToken);
+
+ if (roslynCompletionList is null)
+ {
+ // If we don't get a response from the delegated server, we have to make sure to return an incomplete completion
+ // list. When a user is typing quickly, the delegated request from the first keystroke will fail to synchronize,
+ // so if we return a "complete" list then the query won't re-query us for completion once the typing stops/slows
+ // so we'd only ever return Razor completion items.
+ return new VSInternalCompletionList()
+ {
+ Items = [],
+ IsIncomplete = true
+ };
+ }
+
+ var vsPlatformCompletionList = JsonSerializer.Deserialize(JsonSerializer.SerializeToDocument(roslynCompletionList), options);
+
+ var rewrittenResponse = await DelegatedCompletionHelper.RewriteCSharpResponseAsync(
+ vsPlatformCompletionList,
+ documentIndex,
+ remoteDocumentContext,
+ mappedPosition,
+ razorCompletionOptions,
+ cancellationToken);
+
+ return rewrittenResponse;
+ }
+}
diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostDocumentCompletionEndpoint.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostDocumentCompletionEndpoint.cs
new file mode 100644
index 00000000000..4dbea57e652
--- /dev/null
+++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Cohost/CohostDocumentCompletionEndpoint.cs
@@ -0,0 +1,295 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor;
+using Microsoft.AspNetCore.Razor.PooledObjects;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.ExternalAccess.Razor;
+using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
+using Microsoft.CodeAnalysis.Razor.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion.Delegation;
+using Microsoft.CodeAnalysis.Razor.Logging;
+using Microsoft.CodeAnalysis.Razor.Protocol;
+using Microsoft.CodeAnalysis.Razor.Protocol.Completion;
+using Microsoft.CodeAnalysis.Razor.Remote;
+using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+using Microsoft.VisualStudio.Razor.Settings;
+using Microsoft.VisualStudio.Razor.Snippets;
+using Response = Microsoft.CodeAnalysis.Razor.Remote.RemoteResponse;
+using RoslynCompletionParams = Roslyn.LanguageServer.Protocol.CompletionParams;
+using RoslynLspExtensions = Roslyn.LanguageServer.Protocol.RoslynLspExtensions;
+
+namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
+
+#pragma warning disable RS0030 // Do not use banned APIs
+[Shared]
+[CohostEndpoint(Methods.TextDocumentCompletionName)]
+[Export(typeof(IDynamicRegistrationProvider))]
+[ExportCohostStatelessLspService(typeof(CohostDocumentCompletionEndpoint))]
+[method: ImportingConstructor]
+#pragma warning restore RS0030 // Do not use banned APIs
+internal class CohostDocumentCompletionEndpoint(
+ IRemoteServiceInvoker remoteServiceInvoker,
+ IClientSettingsManager clientSettingsManager,
+ IHtmlDocumentSynchronizer htmlDocumentSynchronizer,
+ SnippetCompletionItemProvider snippetCompletionItemProvider,
+ LSPRequestInvoker requestInvoker,
+ ILoggerFactory loggerFactory)
+ : AbstractRazorCohostDocumentRequestHandler, IDynamicRegistrationProvider
+{
+ private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;
+ private readonly IClientSettingsManager _clientSettingsManager = clientSettingsManager;
+ private readonly IHtmlDocumentSynchronizer _htmlDocumentSynchronizer = htmlDocumentSynchronizer;
+ private readonly SnippetCompletionItemProvider _snippetCompletionItemProvider = snippetCompletionItemProvider;
+ private readonly LSPRequestInvoker _requestInvoker = requestInvoker;
+ private readonly ILogger _logger = loggerFactory.GetOrCreateLogger();
+
+ private VSInternalClientCapabilities? _clientCapabilities;
+
+ protected override bool MutatesSolutionState => false;
+
+ protected override bool RequiresLSPSolution => true;
+
+ public ImmutableArray GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
+ {
+ _clientCapabilities = clientCapabilities;
+
+ if (clientCapabilities.TextDocument?.Completion?.DynamicRegistration is true)
+ {
+ return [new Registration()
+ {
+ Method = Methods.TextDocumentCompletionName,
+ RegisterOptions = new CompletionRegistrationOptions()
+ {
+ ResolveProvider = true,
+ TriggerCharacters = CompletionTriggerCharacters.AllTriggerCharacters,
+ DocumentSelector = filter,
+ AllCommitCharacters = [" ", ">", ";", "="]
+ }
+ }];
+ }
+
+ return [];
+ }
+
+ protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(RoslynCompletionParams request)
+ => request.TextDocument is null ? null : RoslynLspExtensions.ToRazorTextDocumentIdentifier(request.TextDocument);
+
+ protected override Task HandleRequestAsync(RoslynCompletionParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
+ => HandleRequestAsync(request, context.TextDocument.AssumeNotNull(), cancellationToken);
+
+ private async Task HandleRequestAsync(RoslynCompletionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
+ {
+ if (request.Context is null || ToVsLSP(request.Context) is not VSInternalCompletionContext completionContext)
+ {
+ _logger.LogError("Completion request context is null");
+ return null;
+ }
+
+ // Return immediately if this is auto-shown completion but auto-shown completion is disallowed in settings
+ var clientSettings = _clientSettingsManager.GetClientSettings();
+ var autoShownCompletion = completionContext.TriggerKind != CompletionTriggerKind.Invoked;
+ if (autoShownCompletion && !clientSettings.ClientCompletionSettings.AutoShowCompletion)
+ {
+ return null;
+ }
+
+ _logger.LogDebug($"Invoking completion for {razorDocument.FilePath}");
+
+ if (await _remoteServiceInvoker.TryInvokeAsync(
+ razorDocument.Project.Solution,
+ (service, solutionInfo, cancellationToken)
+ => service.GetPositionInfoAsync(
+ solutionInfo,
+ razorDocument.Id,
+ completionContext,
+ ToVsLSP(request.Position).AssumeNotNull(),
+ cancellationToken),
+ cancellationToken).ConfigureAwait(false) is not { } completionPositionInfo)
+ {
+ // If we can't figure out position info for request position we can't return completions
+ return null;
+ }
+
+ var documentPositionInfo = completionPositionInfo.DocumentPositionInfo;
+ if (documentPositionInfo.LanguageKind != RazorLanguageKind.Razor)
+ {
+ completionContext = DelegatedCompletionHelper.RewriteContext(completionContext, documentPositionInfo.LanguageKind);
+ }
+
+ // First of all, see if we in HTML and get HTML completions before calling OOP to get Razor completions.
+ // Razor completion provider needs a set of existing HTML item labels.
+
+ VSInternalCompletionList? htmlCompletionList = null;
+ var razorCompletionOptions = new RazorCompletionOptions(
+ SnippetsSupported: true, // always true in non-legacy Razor
+ AutoInsertAttributeQuotes: clientSettings.AdvancedSettings.AutoInsertAttributeQuotes,
+ CommitElementsWithSpace: clientSettings.AdvancedSettings.CommitElementsWithSpace);
+ using var _ = HashSetPool.GetPooledObject(out var existingHtmlCompletions);
+
+ if (CompletionTriggerCharacters.IsValidTrigger(CompletionTriggerCharacters.HtmlTriggerCharacters, completionContext))
+ {
+ // We can just blindly call HTML LSP because if we are in C#, generated HTML seen by HTML LSP may return
+ // results we don't want to show. So we want to call HTML LSP only if we know we are in HTML content.
+ if (documentPositionInfo.LanguageKind == RazorLanguageKind.Html)
+ {
+ htmlCompletionList = await GetHtmlCompletionListAsync(request, razorDocument, razorCompletionOptions, cancellationToken);
+ if (htmlCompletionList is not null)
+ {
+ existingHtmlCompletions.UnionWith(htmlCompletionList.Items.Select(i => i.Label));
+ }
+ }
+ }
+
+ _logger.LogDebug($"Calling OOP to get completion items at {request.Position} invoked by typing '{request.Context?.TriggerCharacter}'");
+
+ var data = await _remoteServiceInvoker.TryInvokeAsync(
+ razorDocument.Project.Solution,
+ (service, solutionInfo, cancellationToken)
+ => service.GetCompletionAsync(
+ solutionInfo,
+ razorDocument.Id,
+ completionPositionInfo,
+ completionContext,
+ _clientCapabilities.AssumeNotNull(),
+ razorCompletionOptions,
+ existingHtmlCompletions,
+ cancellationToken),
+ cancellationToken).ConfigureAwait(false);
+
+ if (data.StopHandling)
+ {
+ return null;
+ }
+
+ VSInternalCompletionList? combinedCompletionList = null;
+ if (data.Result is { } oopCompletionList)
+ {
+ combinedCompletionList = htmlCompletionList?.Items is not null && htmlCompletionList.Items.Length > 0
+ // If we have HTML completions, that means OOP completion list is really just Razor completion list
+ ? CompletionListMerger.Merge(oopCompletionList, htmlCompletionList)
+ : oopCompletionList;
+ }
+ else
+ {
+ // Didn't get anything from OOP, so just return HTML completion list or null
+ combinedCompletionList = htmlCompletionList;
+ }
+
+ if (completionPositionInfo.ShouldIncludeDelegationSnippets)
+ {
+ combinedCompletionList = AddSnippets(
+ combinedCompletionList,
+ documentPositionInfo.LanguageKind,
+ completionContext.InvokeKind,
+ completionContext.TriggerCharacter);
+ }
+
+ return combinedCompletionList;
+ }
+
+ private async Task GetHtmlCompletionListAsync(
+ RoslynCompletionParams request,
+ TextDocument razorDocument,
+ RazorCompletionOptions razorCompletionOptions,
+ CancellationToken cancellationToken)
+ {
+ var htmlDocument = await _htmlDocumentSynchronizer.TryGetSynchronizedHtmlDocumentAsync(razorDocument, cancellationToken).ConfigureAwait(false);
+ if (htmlDocument is null)
+ {
+ return null;
+ }
+
+ request.TextDocument = RoslynLspExtensions.WithUri(request.TextDocument, htmlDocument.Uri);
+
+ _logger.LogDebug($"Resolving auto-insertion edit for {htmlDocument.Uri}");
+
+ var result = await _requestInvoker.ReinvokeRequestOnServerAsync(
+ htmlDocument.Buffer,
+ Methods.TextDocumentCompletionName,
+ RazorLSPConstants.HtmlLanguageServerName,
+ request,
+ cancellationToken).ConfigureAwait(false);
+
+ var rewrittenResponse = DelegatedCompletionHelper.RewriteHtmlResponse(result?.Response, razorCompletionOptions);
+
+ return rewrittenResponse;
+ }
+
+ private static T? ToVsLSP(object source) where T : class
+ {
+ // This is, to say the least, not ideal. In future we're going to normalize on to Roslyn LSP types, and this can go.
+ var options = new JsonSerializerOptions();
+ foreach (var converter in RazorServiceDescriptorsWrapper.GetLspConverters())
+ {
+ options.Converters.Add(converter);
+ }
+
+ if (JsonSerializer.Deserialize(JsonSerializer.SerializeToDocument(source), options) is not { } target)
+ {
+ return null;
+ }
+
+ return target;
+ }
+
+ private VSInternalCompletionList? AddSnippets(
+ VSInternalCompletionList? completionList,
+ RazorLanguageKind languageKind,
+ VSInternalCompletionInvokeKind invokeKind,
+ string? triggerCharacter)
+ {
+ using var builder = new PooledArrayBuilder();
+ _snippetCompletionItemProvider.AddSnippetCompletions(
+ languageKind,
+ invokeKind,
+ triggerCharacter,
+ ref builder.AsRef());
+
+ // If there were no snippets, just return the original list
+ if (builder.Count == 0)
+ {
+ return completionList;
+ }
+
+ // If there was a list with items, add them to snippets
+ if (completionList?.Items is { } combinedItems)
+ {
+ builder.AddRange(combinedItems);
+ }
+
+ // Create or update final completion list
+ if (completionList is null)
+ {
+ completionList = new VSInternalCompletionList { IsIncomplete = true, Items = builder.ToArray() };
+ }
+ else
+ {
+ completionList.Items = builder.ToArray();
+ }
+
+ return completionList;
+ }
+
+ internal TestAccessor GetTestAccessor() => new(this);
+
+ internal readonly struct TestAccessor(CohostDocumentCompletionEndpoint instance)
+ {
+ public Task HandleRequestAsync(
+ RoslynCompletionParams request,
+ TextDocument razorDocument,
+ CancellationToken cancellationToken)
+ => instance.HandleRequestAsync(request, razorDocument, cancellationToken);
+ public void SetClientCapabilities(VSInternalClientCapabilities clientCapabilities)
+ {
+ instance._clientCapabilities = clientCapabilities;
+ }
+ }
+}
diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget.cs
index 406fbc3df4e..c5b0175df84 100644
--- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget.cs
+++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget.cs
@@ -34,7 +34,7 @@ internal partial class RazorCustomMessageTarget
private readonly ITelemetryReporter _telemetryReporter;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
private readonly IProjectSnapshotManager _projectManager;
- private readonly SnippetCache _snippetCache;
+ private readonly SnippetCompletionItemProvider _snippetCompletionItemProvider;
private readonly IWorkspaceProvider _workspaceProvider;
private readonly IHtmlDocumentSynchronizer _htmlDocumentSynchronizer;
private readonly FormattingOptionsProvider _formattingOptionsProvider;
@@ -55,7 +55,7 @@ public RazorCustomMessageTarget(
ITelemetryReporter telemetryReporter,
LanguageServerFeatureOptions languageServerFeatureOptions,
IProjectSnapshotManager projectManager,
- SnippetCache snippetCache,
+ SnippetCompletionItemProvider snippetCompletionItemProvider,
IWorkspaceProvider workspaceProvider,
IHtmlDocumentSynchronizer htmlDocumentSynchronizer,
ILoggerFactory loggerFactory)
@@ -86,7 +86,7 @@ public RazorCustomMessageTarget(
_telemetryReporter = telemetryReporter ?? throw new ArgumentNullException(nameof(telemetryReporter));
_languageServerFeatureOptions = languageServerFeatureOptions ?? throw new ArgumentNullException(nameof(languageServerFeatureOptions));
_projectManager = projectManager ?? throw new ArgumentNullException(nameof(projectManager));
- _snippetCache = snippetCache ?? throw new ArgumentNullException(nameof(snippetCache));
+ _snippetCompletionItemProvider = snippetCompletionItemProvider ?? throw new ArgumentNullException(nameof(snippetCompletionItemProvider));
_workspaceProvider = workspaceProvider;
_htmlDocumentSynchronizer = htmlDocumentSynchronizer;
_logger = loggerFactory.GetOrCreateLogger();
diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs
index 8a85d553cdf..6575ce4e232 100644
--- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs
+++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LanguageClient/Endpoints/RazorCustomMessageTarget_Completion.cs
@@ -3,7 +3,6 @@
using System;
using System.Diagnostics;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.PooledObjects;
@@ -150,7 +149,7 @@ internal partial class RazorCustomMessageTarget
};
}
- AddSnippetCompletions(request, ref builder.AsRef());
+ _snippetCompletionItemProvider.AddSnippetCompletions(request.ProjectedKind, request.Context.InvokeKind, request.Context.TriggerCharacter, ref builder.AsRef());
completionList.Items = builder.ToArray();
completionList.Data = JsonHelpers.TryConvertFromJObject(completionList.Data);
@@ -238,7 +237,7 @@ private void UpdateVirtualDocument(
{
// Check if we're completing a snippet item that we provided
if (SnippetCompletionData.TryParse(request.CompletionItem.Data, out var snippetCompletionData) &&
- _snippetCache.TryResolveSnippetString(snippetCompletionData) is { } snippetInsertText)
+ _snippetCompletionItemProvider.SnippetCache.TryResolveSnippetString(snippetCompletionData) is { } snippetInsertText)
{
request.CompletionItem.InsertText = snippetInsertText;
return request.CompletionItem;
@@ -314,60 +313,4 @@ private void UpdateVirtualDocument(
var formattingOptions = _formattingOptionsProvider.GetOptions(document.TextDocumentIdentifier.Uri);
return Task.FromResult(formattingOptions);
}
-
- private void AddSnippetCompletions(DelegatedCompletionParams request, ref PooledArrayBuilder builder)
- {
- if (!request.ShouldIncludeSnippets)
- {
- return;
- }
-
- // Temporary fix: snippets are broken in CSharp. We're investigating
- // but this is very disruptive. This quick fix unblocks things.
- // TODO: Add an option to enable this.
- if (request.ProjectedKind != RazorLanguageKind.Html)
- {
- return;
- }
-
- // Don't add snippets for deletion of a character
- if (request.Context.InvokeKind == VSInternalCompletionInvokeKind.Deletion)
- {
- return;
- }
-
- // Don't add snippets if the trigger characters contain whitespace
- if (request.Context.TriggerCharacter is not null
- && request.Context.TriggerCharacter.Contains(' '))
- {
- return;
- }
-
- var snippets = _snippetCache.GetSnippets(ConvertLanguageKind(request.ProjectedKind));
- if (snippets.IsDefaultOrEmpty)
- {
- return;
- }
-
- builder.AddRange(snippets
- .Select(s => new CompletionItem()
- {
- Label = s.Shortcut,
- Detail = s.Description,
- InsertTextFormat = InsertTextFormat.Snippet,
- InsertText = s.Shortcut,
- Data = s.CompletionData,
- Kind = CompletionItemKind.Snippet,
- CommitCharacters = []
- }));
- }
-
- private static SnippetLanguage ConvertLanguageKind(RazorLanguageKind languageKind)
- => languageKind switch
- {
- RazorLanguageKind.CSharp => SnippetLanguage.CSharp,
- RazorLanguageKind.Html => SnippetLanguage.Html,
- RazorLanguageKind.Razor => SnippetLanguage.Razor,
- _ => throw new InvalidOperationException($"Unexpected value {languageKind}")
- };
}
diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Snippets/SnippetCompletionItemProvider.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Snippets/SnippetCompletionItemProvider.cs
new file mode 100644
index 00000000000..8a99acf51cb
--- /dev/null
+++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Snippets/SnippetCompletionItemProvider.cs
@@ -0,0 +1,77 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System;
+using System.ComponentModel.Composition;
+using System.Linq;
+using Microsoft.AspNetCore.Razor.PooledObjects;
+using Microsoft.CodeAnalysis.Razor.Protocol;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+
+namespace Microsoft.VisualStudio.Razor.Snippets;
+
+[Export(typeof(SnippetCompletionItemProvider))]
+internal sealed class SnippetCompletionItemProvider
+{
+ [ImportingConstructor]
+ public SnippetCompletionItemProvider(SnippetCache snippetCache)
+ {
+ SnippetCache = snippetCache;
+ }
+
+ public SnippetCache SnippetCache { get; }
+
+ public void AddSnippetCompletions(
+ RazorLanguageKind projectedKind,
+ VSInternalCompletionInvokeKind invokeKind,
+ string? triggerCharacter,
+ ref PooledArrayBuilder builder)
+ {
+ // Temporary fix: snippets are broken in CSharp. We're investigating
+ // but this is very disruptive. This quick fix unblocks things.
+ // TODO: Add an option to enable this.
+ if (projectedKind != RazorLanguageKind.Html)
+ {
+ return;
+ }
+
+ // Don't add snippets for deletion of a character
+ if (invokeKind == VSInternalCompletionInvokeKind.Deletion)
+ {
+ return;
+ }
+
+ // Don't add snippets if the trigger characters contain whitespace
+ if (triggerCharacter is not null && triggerCharacter.Contains(' '))
+ {
+ return;
+ }
+
+ var snippets = SnippetCache.GetSnippets(ConvertLanguageKind(projectedKind));
+ if (snippets.IsDefaultOrEmpty)
+ {
+ return;
+ }
+
+ builder.AddRange(snippets
+ .Select(s => new CompletionItem()
+ {
+ Label = s.Shortcut,
+ Detail = s.Description,
+ InsertTextFormat = InsertTextFormat.Snippet,
+ InsertText = s.Shortcut,
+ Data = s.CompletionData,
+ Kind = CompletionItemKind.Snippet,
+ CommitCharacters = []
+ }));
+ }
+
+ private static SnippetLanguage ConvertLanguageKind(RazorLanguageKind languageKind)
+ => languageKind switch
+ {
+ RazorLanguageKind.CSharp => SnippetLanguage.CSharp,
+ RazorLanguageKind.Html => SnippetLanguage.Html,
+ RazorLanguageKind.Razor => SnippetLanguage.Razor,
+ _ => throw new InvalidOperationException($"Unexpected value {languageKind}")
+ };
+}
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListProviderTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListProviderTest.cs
index c44f32afc7a..659a8014854 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListProviderTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListProviderTest.cs
@@ -4,12 +4,13 @@
#nullable disable
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
-using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@@ -29,6 +30,7 @@ public class CompletionListProviderTest : LanguageServerTestBase
private readonly VSInternalCompletionContext _completionContext;
private readonly DocumentContext _documentContext;
private readonly VSInternalClientCapabilities _clientCapabilities;
+ private readonly RazorCompletionOptions _razorCompletionOptions;
public CompletionListProviderTest(ITestOutputHelper testOutput)
: base(testOutput)
@@ -40,6 +42,7 @@ public CompletionListProviderTest(ITestOutputHelper testOutput)
_completionContext = new VSInternalCompletionContext();
_documentContext = TestDocumentContext.Create("C:/path/to/file.cshtml");
_clientCapabilities = new VSInternalClientCapabilities();
+ _razorCompletionOptions = new RazorCompletionOptions(SnippetsSupported: true, AutoInsertAttributeQuotes: true, CommitElementsWithSpace: true);
}
[Fact]
@@ -50,7 +53,7 @@ public async Task MultipleCompletionLists_Merges()
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 0, _completionContext, _documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 0, _completionContext, _documentContext, _clientCapabilities, _razorCompletionOptions, correlationId: Guid.Empty, cancellationToken: DisposalToken);
// Assert
Assert.NotSame(_completionList1, completionList);
@@ -67,7 +70,7 @@ public async Task MultipleCompletionLists_DifferentCommitCharacters_OnlyCallsApp
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 0, _completionContext, _documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 0, _completionContext, _documentContext, _clientCapabilities, _razorCompletionOptions, correlationId: Guid.Empty, cancellationToken: DisposalToken);
// Assert
Assert.Same(_completionList2, completionList);
@@ -78,19 +81,20 @@ private class TestDelegatedCompletionListProvider : DelegatedCompletionListProvi
private readonly VSInternalCompletionList _completionList;
public TestDelegatedCompletionListProvider(VSInternalCompletionList completionList, IEnumerable triggerCharacters)
- : base(Array.Empty(), null, null, null)
+ : base(null, null, null)
{
_completionList = completionList;
- TriggerCharacters = triggerCharacters.ToImmutableHashSet();
+ TriggerCharacters = triggerCharacters.ToFrozenSet();
}
- public override ImmutableHashSet TriggerCharacters { get; }
+ public override FrozenSet TriggerCharacters { get; }
public override Task GetCompletionListAsync(
int absoluteIndex,
VSInternalCompletionContext completionContext,
DocumentContext documentContext,
VSInternalClientCapabilities clientCapabilities,
+ RazorCompletionOptions completionOptions,
Guid correlationId,
CancellationToken cancellationToken)
{
@@ -109,10 +113,10 @@ public TestRazorCompletionListProvider(
: base(completionFactsService: null, completionListCache: null, loggerFactory)
{
_completionList = completionList;
- TriggerCharacters = triggerCharacters.ToImmutableHashSet();
+ TriggerCharacters = triggerCharacters.ToFrozenSet();
}
- public override ImmutableHashSet TriggerCharacters { get; }
+ public override FrozenSet TriggerCharacters { get; }
public override Task GetCompletionListAsync(
int absoluteIndex,
@@ -120,6 +124,7 @@ public override Task GetCompletionListAsync(
DocumentContext documentContext,
VSInternalClientCapabilities clientCapabilities,
HashSet existingCompletions,
+ RazorCompletionOptions razorCompletionOptions,
CancellationToken cancellationToken)
{
return Task.FromResult(_completionList);
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionItemResolverTest.NetFx.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionItemResolverTest.NetFx.cs
index 4bad7b08bcd..37a38438c4b 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionItemResolverTest.NetFx.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionItemResolverTest.NetFx.cs
@@ -12,6 +12,7 @@
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting;
using Microsoft.AspNetCore.Razor.LanguageServer.Test;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
@@ -32,6 +33,7 @@ public class DelegatedCompletionItemResolverTest : LanguageServerTestBase
private readonly DelegatedCompletionParams _htmlCompletionParams;
private readonly IDocumentContextFactory _documentContextFactory;
private readonly AsyncLazy _formattingService;
+ private readonly RazorCompletionOptions _defaultRazorCompletionOptions;
public DelegatedCompletionItemResolverTest(ITestOutputHelper testOutput)
: base(testOutput)
@@ -71,6 +73,10 @@ public DelegatedCompletionItemResolverTest(ITestOutputHelper testOutput)
_documentContextFactory = new TestDocumentContextFactory();
_formattingService = new AsyncLazy(() => TestRazorFormattingService.CreateWithFullSupportAsync(LoggerFactory));
+ _defaultRazorCompletionOptions = new RazorCompletionOptions(
+ SnippetsSupported: true,
+ AutoInsertAttributeQuotes: true,
+ CommitElementsWithSpace: true);
}
[Fact]
@@ -277,7 +283,13 @@ private async Task CreateCSharpServerAsync(RazorCodeDocumen
var provider = TestDelegatedCompletionListProvider.Create(csharpServer, LoggerFactory, DisposalToken);
var completionList = await provider.GetCompletionListAsync(
- cursorPosition, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ cursorPosition,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
return (completionList, provider.DelegatedParams);
}
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionListProviderTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionListProviderTest.cs
index a36c6a557fb..f97b4f2ca53 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionListProviderTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DelegatedCompletionListProviderTest.cs
@@ -4,15 +4,14 @@
#nullable disable
using System;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
-using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
@@ -27,33 +26,17 @@ public class DelegatedCompletionListProviderTest : LanguageServerTestBase
{
private readonly TestDelegatedCompletionListProvider _provider;
private readonly VSInternalClientCapabilities _clientCapabilities;
+ private readonly RazorCompletionOptions _defaultRazorCompletionOptions;
public DelegatedCompletionListProviderTest(ITestOutputHelper testOutput)
: base(testOutput)
{
_provider = TestDelegatedCompletionListProvider.Create(LoggerFactory);
_clientCapabilities = new VSInternalClientCapabilities();
- }
-
- [Fact]
- public async Task ResponseRewritersGetExecutedInOrder()
- {
- // Arrange
- var completionContext = new VSInternalCompletionContext();
- var codeDocument = CreateCodeDocument("<");
- var documentContext = TestDocumentContext.Create("C:/path/to/file.cshtml", codeDocument, hostDocumentVersion: 0);
- var rewriter1 = new TestResponseRewriter(order: 100);
- var rewriter2 = new TestResponseRewriter(order: 20);
- var provider = TestDelegatedCompletionListProvider.Create(LoggerFactory, rewriter1, rewriter2);
-
- // Act
- var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
-
- // Assert
- Assert.Collection(completionList.Items,
- item => Assert.Equal("20", item.Label),
- item => Assert.Equal("100", item.Label));
+ _defaultRazorCompletionOptions = new RazorCompletionOptions(
+ SnippetsSupported: true,
+ AutoInsertAttributeQuotes: true,
+ CommitElementsWithSpace: true);
}
[Fact]
@@ -66,7 +49,13 @@ public async Task HtmlDelegation_Invoked()
// Act
await _provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 1,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
// Assert
var delegatedParameters = _provider.DelegatedParams;
@@ -93,7 +82,13 @@ public async Task HtmlDelegation_TriggerCharacter()
// Act
await _provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 1,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
// Assert
var delegatedParameters = _provider.DelegatedParams;
@@ -121,7 +116,13 @@ public async Task HtmlDelegation_UnsupportedTriggerCharacter_TranslatesToInvoked
// Act
await _provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 1,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
// Assert
var delegatedParameters = _provider.DelegatedParams;
@@ -150,7 +151,13 @@ public async Task Delegation_NullResult_ToIncompleteResult()
// Act
var delegatedCompletionList = await provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 1,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
// Assert
Assert.NotNull(delegatedCompletionList);
@@ -197,7 +204,13 @@ public async Task RazorDelegation_Noop()
// Act
var completionList = await _provider.GetCompletionListAsync(
- absoluteIndex: 11, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 11,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
// Assert
Assert.Null(completionList);
@@ -220,7 +233,13 @@ public async Task ProvisionalCompletion_TranslatesToCSharpWithProvisionalTextEdi
// Act
await _provider.GetCompletionListAsync(
- absoluteIndex: 10, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 10,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
// Assert
var delegatedParameters = _provider.DelegatedParams;
@@ -250,7 +269,13 @@ public async Task DotTriggerInMiddleOfCSharpImplicitExpressionNotTreatedAsProvis
// Act
await _provider.GetCompletionListAsync(
- absoluteIndex: 10, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: 10,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
// Assert
var delegatedParameters = _provider.DelegatedParams;
@@ -295,7 +320,6 @@ public async Task ShouldIncludeSnippets(string input, bool shouldIncludeSnippets
.Returns(true);
var completionProvider = new DelegatedCompletionListProvider(
- responseRewriters: [],
documentMappingServiceMock.Object,
clientConnection,
new CompletionListCache());
@@ -316,34 +340,18 @@ public async Task ShouldIncludeSnippets(string input, bool shouldIncludeSnippets
TriggerCharacter = ".",
};
- await completionProvider.GetCompletionListAsync(cursorPosition, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, DisposalToken);
+ await completionProvider.GetCompletionListAsync(
+ cursorPosition,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ DisposalToken);
Assert.True(requestSent);
}
- private class TestResponseRewriter : DelegatedCompletionResponseRewriter
- {
- private readonly int _order;
-
- public TestResponseRewriter(int order)
- {
- _order = order;
- }
-
- public override int Order => _order;
-
- public override Task RewriteAsync(VSInternalCompletionList completionList, int hostDocumentIndex, DocumentContext hostDocumentContext, DelegatedCompletionParams delegatedParameters, CancellationToken cancellationToken)
- {
- var completionItem = new VSInternalCompletionItem()
- {
- Label = Order.ToString(),
- };
- completionList.Items = completionList.Items.Concat(new[] { completionItem }).ToArray();
-
- return Task.FromResult(completionList);
- }
- }
-
private async Task GetCompletionListAsync(string content, CompletionTriggerKind triggerKind)
{
TestFileMarkupParser.GetPosition(content, out var output, out var cursorPosition);
@@ -376,7 +384,13 @@ private async Task GetCompletionListAsync(string conte
var provider = TestDelegatedCompletionListProvider.Create(csharpServer, LoggerFactory, DisposalToken);
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: cursorPosition, completionContext, documentContext, _clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ absoluteIndex: cursorPosition,
+ completionContext,
+ documentContext,
+ _clientCapabilities,
+ _defaultRazorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
return completionList;
}
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DesignTimeHelperResponseRewriterTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DesignTimeHelperResponseRewriterTest.cs
index 9a1ff7556f7..e22d97af9de 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DesignTimeHelperResponseRewriterTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/DesignTimeHelperResponseRewriterTest.cs
@@ -12,7 +12,7 @@
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
public class DesignTimeHelperResponseRewriterTest(ITestOutputHelper testOutput)
- : ResponseRewriterTestBase(new DesignTimeHelperResponseRewriter(), testOutput)
+ : ResponseRewriterTestBase(testOutput)
{
[Fact]
public async Task RewriteAsync_NotCSharp_Noops()
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/HtmlCommitCharacterResponseRewriterTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/HtmlCommitCharacterResponseRewriterTest.cs
index 098d1a3f090..1d3d246f754 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/HtmlCommitCharacterResponseRewriterTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/HtmlCommitCharacterResponseRewriterTest.cs
@@ -4,8 +4,9 @@
#nullable disable
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Razor.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion.Delegation;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
@@ -14,7 +15,7 @@
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
public class HtmlCommitCharacterResponseRewriterTest(ITestOutputHelper testOutput)
- : ResponseRewriterTestBase(new HtmlCommitCharacterResponseRewriter(TestRazorLSPOptionsMonitor.Create()), testOutput)
+ : ResponseRewriterTestBase(testOutput)
{
[Theory]
[CombinatorialData]
@@ -68,12 +69,14 @@ public async Task RewriteAsync_DefaultCommitCharacters_RemovesSpace(bool useVSTy
TestFileMarkupParser.GetPosition(input, out var documentContent, out var cursorPosition);
var delegatedCompletionList = GenerateCompletionList(useDefaultCommitCharacters: true, useVSTypes, "Element1", "Element2");
- var options = TestRazorLSPOptionsMonitor.Create();
- await options.UpdateAsync(options.CurrentValue with { CommitElementsWithSpace = false }, CancellationToken.None);
- var rewriter = new HtmlCommitCharacterResponseRewriter(options);
+ var razorCompletionOptions = new RazorCompletionOptions(
+ SnippetsSupported: true,
+ AutoInsertAttributeQuotes: true,
+ CommitElementsWithSpace: false);
// Act
- var rewrittenCompletionList = await GetRewrittenCompletionListAsync(cursorPosition, documentContent, delegatedCompletionList, rewriter);
+ var rewrittenCompletionList = await GetRewrittenCompletionListAsync(
+ cursorPosition, documentContent, delegatedCompletionList, razorCompletionOptions);
// Assert
if (useVSTypes)
@@ -111,12 +114,18 @@ public async Task RewriteAsync_ItemCommitCharacters_RemovesSpace(bool useVSTypes
TestFileMarkupParser.GetPosition(input, out var documentContent, out var cursorPosition);
var delegatedCompletionList = GenerateCompletionList(useDefaultCommitCharacters: false, useVSTypes, "Element1", "Element2");
- var options = TestRazorLSPOptionsMonitor.Create();
- await options.UpdateAsync(options.CurrentValue with { CommitElementsWithSpace = false }, CancellationToken.None);
- var rewriter = new HtmlCommitCharacterResponseRewriter(options);
+ var razorCompletionOptions = new RazorCompletionOptions(
+ SnippetsSupported: true,
+ AutoInsertAttributeQuotes: true,
+ CommitElementsWithSpace: false);
+ var rewriter = new HtmlCommitCharacterResponseRewriter();
// Act
- var rewrittenCompletionList = await GetRewrittenCompletionListAsync(cursorPosition, documentContent, delegatedCompletionList, rewriter);
+ var rewrittenCompletionList = await GetRewrittenCompletionListAsync(
+ cursorPosition,
+ documentContent,
+ delegatedCompletionList,
+ razorCompletionOptions);
// Assert
Assert.Null(rewrittenCompletionList.CommitCharacters);
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/ResponseRewriterTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/ResponseRewriterTestBase.cs
index db8ab5ed69a..68a0c64e40d 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/ResponseRewriterTestBase.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/ResponseRewriterTestBase.cs
@@ -6,6 +6,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit.Abstractions;
@@ -13,24 +14,44 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
public abstract class ResponseRewriterTestBase : LanguageServerTestBase
{
- private protected DelegatedCompletionResponseRewriter Rewriter { get; }
-
private protected ResponseRewriterTestBase(
- DelegatedCompletionResponseRewriter rewriter,
ITestOutputHelper testOutput)
: base(testOutput)
{
- Rewriter = rewriter;
}
- private protected async Task GetRewrittenCompletionListAsync(int absoluteIndex, string documentContent, VSInternalCompletionList initialCompletionList, DelegatedCompletionResponseRewriter rewriter = null)
+ private protected Task GetRewrittenCompletionListAsync(
+ int absoluteIndex,
+ string documentContent,
+ VSInternalCompletionList initialCompletionList)
+ {
+ var razorCompletionOptions = new RazorCompletionOptions(
+ SnippetsSupported: true,
+ AutoInsertAttributeQuotes: true,
+ CommitElementsWithSpace: true);
+
+ return GetRewrittenCompletionListAsync(absoluteIndex, documentContent, initialCompletionList, razorCompletionOptions);
+ }
+
+ private protected async Task GetRewrittenCompletionListAsync(
+ int absoluteIndex,
+ string documentContent,
+ VSInternalCompletionList initialCompletionList,
+ RazorCompletionOptions razorCompletionOptions)
{
var completionContext = new VSInternalCompletionContext();
var codeDocument = CreateCodeDocument(documentContent);
var documentContext = TestDocumentContext.Create("C:/path/to/file.cshtml", codeDocument);
- var provider = TestDelegatedCompletionListProvider.Create(initialCompletionList, LoggerFactory, rewriter ?? Rewriter);
+ var provider = TestDelegatedCompletionListProvider.Create(initialCompletionList, LoggerFactory);
var clientCapabilities = new VSInternalClientCapabilities();
- var completionList = await provider.GetCompletionListAsync(absoluteIndex, completionContext, documentContext, clientCapabilities, correlationId: Guid.Empty, cancellationToken: DisposalToken);
+ var completionList = await provider.GetCompletionListAsync(
+ absoluteIndex,
+ completionContext,
+ documentContext,
+ clientCapabilities,
+ razorCompletionOptions,
+ correlationId: Guid.Empty,
+ cancellationToken: DisposalToken);
return completionList;
}
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/SnippetResponseRewriterTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/SnippetResponseRewriterTest.cs
index 78f23228bcc..7de0121b894 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/SnippetResponseRewriterTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/SnippetResponseRewriterTest.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
+using Microsoft.CodeAnalysis.Razor.Completion.Delegation;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
@@ -13,7 +14,7 @@
namespace Microsoft.AspNetCore.Razor.LanguageServer.Test.Completion.Delegation;
public class SnippetResponseRewriterTest(ITestOutputHelper testOutput)
- : ResponseRewriterTestBase(new SnippetResponseRewriter(), testOutput)
+ : ResponseRewriterTestBase(testOutput)
{
[Fact]
public async Task RewriteAsync_ChangesUsingSnippetLabel()
@@ -29,7 +30,7 @@ public async Task RewriteAsync_ChangesUsingSnippetLabel()
// Act
var rewrittenCompletionList = await GetRewrittenCompletionListAsync(
- cursorPosition, documentContent, delegatedCompletionList, rewriter);
+ cursorPosition, documentContent, delegatedCompletionList);
// Assert
Assert.Null(rewrittenCompletionList.CommitCharacters);
@@ -62,7 +63,7 @@ public async Task RewriteAsync_DoesNotChangeUsingKeywordLabel()
// Act
var rewrittenCompletionList = await GetRewrittenCompletionListAsync(
- cursorPosition, documentContent, delegatedCompletionList, rewriter);
+ cursorPosition, documentContent, delegatedCompletionList);
// Assert
Assert.Null(rewrittenCompletionList.CommitCharacters);
@@ -95,7 +96,7 @@ public async Task RewriteAsync_DoesNotChangeIfSnippetLabel()
// Act
var rewrittenCompletionList = await GetRewrittenCompletionListAsync(
- cursorPosition, documentContent, delegatedCompletionList, rewriter);
+ cursorPosition, documentContent, delegatedCompletionList);
// Assert
Assert.Null(rewrittenCompletionList.CommitCharacters);
@@ -114,39 +115,6 @@ public async Task RewriteAsync_DoesNotChangeIfSnippetLabel()
);
}
- [Fact]
- public async Task RewriteAsync_HandlesNullLabels()
- {
- // Arrange
- var documentContent = "@$$";
- TestFileMarkupParser.GetPosition(documentContent, out documentContent, out var cursorPosition);
- var delegatedCompletionList = GenerateCompletionList(
- (null, CompletionItemKind.Keyword),
- ("using", CompletionItemKind.Snippet)
- );
- var rewriter = new SnippetResponseRewriter();
-
- // Act
- var rewrittenCompletionList = await GetRewrittenCompletionListAsync(
- cursorPosition, documentContent, delegatedCompletionList, rewriter);
-
- // Assert
- Assert.Null(rewrittenCompletionList.CommitCharacters);
- Assert.Collection(
- rewrittenCompletionList.Items,
- completion =>
- {
- Assert.Null(completion.Label);
- Assert.Null(completion.SortText);
- },
- completion =>
- {
- Assert.Equal("using statement", completion.Label);
- Assert.Equal("using ", completion.SortText);
- }
- );
- }
-
private static VSInternalCompletionList GenerateCompletionList(params (string? Label, CompletionItemKind Kind)[] itemsData)
{
var items = itemsData.Select(itemData => new VSInternalCompletionItem()
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TestDelegatedCompletionListProvider.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TestDelegatedCompletionListProvider.cs
index 746ba8cc729..9cd45150fc6 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TestDelegatedCompletionListProvider.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TestDelegatedCompletionListProvider.cs
@@ -10,6 +10,7 @@
using Microsoft.AspNetCore.Razor.LanguageServer.Test;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@@ -21,11 +22,9 @@ internal class TestDelegatedCompletionListProvider : DelegatedCompletionListProv
private readonly CompletionRequestResponseFactory _completionFactory;
private TestDelegatedCompletionListProvider(
- DelegatedCompletionResponseRewriter[] responseRewriters,
CompletionRequestResponseFactory completionFactory,
ILoggerFactory loggerFactory)
: base(
- responseRewriters,
new LspDocumentMappingService(new LSPFilePathService(TestLanguageServerFeatureOptions.Instance), new TestDocumentContextFactory(), loggerFactory),
new TestLanguageServer(new Dictionary>>()
{
@@ -37,41 +36,37 @@ private TestDelegatedCompletionListProvider(
}
public static TestDelegatedCompletionListProvider Create(
- ILoggerFactory loggerFactory,
- params DelegatedCompletionResponseRewriter[] responseRewriters) =>
- Create(delegatedCompletionList: null, loggerFactory, responseRewriters: responseRewriters);
+ ILoggerFactory loggerFactory) =>
+ Create(delegatedCompletionList: null, loggerFactory);
public static TestDelegatedCompletionListProvider Create(
CSharpTestLspServer csharpServer,
ILoggerFactory loggerFactory,
- CancellationToken cancellationToken,
- params DelegatedCompletionResponseRewriter[] responseRewriters)
+ CancellationToken cancellationToken)
{
var requestResponseFactory = new DelegatedCSharpCompletionRequestResponseFactory(csharpServer, cancellationToken);
- var provider = new TestDelegatedCompletionListProvider(responseRewriters, requestResponseFactory, loggerFactory);
+ var provider = new TestDelegatedCompletionListProvider(requestResponseFactory, loggerFactory);
return provider;
}
public static TestDelegatedCompletionListProvider Create(
VSInternalCompletionList delegatedCompletionList,
- ILoggerFactory loggerFactory,
- params DelegatedCompletionResponseRewriter[] responseRewriters)
+ ILoggerFactory loggerFactory)
{
delegatedCompletionList ??= new VSInternalCompletionList()
{
Items = Array.Empty(),
};
var requestResponseFactory = new StaticCompletionRequestResponseFactory(delegatedCompletionList);
- var provider = new TestDelegatedCompletionListProvider(responseRewriters, requestResponseFactory, loggerFactory);
+ var provider = new TestDelegatedCompletionListProvider(requestResponseFactory, loggerFactory);
return provider;
}
public static TestDelegatedCompletionListProvider CreateWithNullResponse(
- ILoggerFactory loggerFactory,
- params DelegatedCompletionResponseRewriter[] responseRewriters)
+ ILoggerFactory loggerFactory)
{
var requestResponseFactory = new StaticCompletionRequestResponseFactory(null);
- var provider = new TestDelegatedCompletionListProvider(responseRewriters, requestResponseFactory, loggerFactory);
+ var provider = new TestDelegatedCompletionListProvider(requestResponseFactory, loggerFactory);
return provider;
}
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TextEditResponseRewriterTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TextEditResponseRewriterTest.cs
index 38115319260..61c04e3c98e 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TextEditResponseRewriterTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/Delegation/TextEditResponseRewriterTest.cs
@@ -11,7 +11,7 @@
namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion.Delegation;
public class TextEditResponseRewriterTest(ITestOutputHelper testOutput)
- : ResponseRewriterTestBase(new TextEditResponseRewriter(), testOutput)
+ : ResponseRewriterTestBase(testOutput)
{
[Fact]
public async Task RewriteAsync_NotCSharp_Noops()
@@ -79,6 +79,7 @@ private static VSInternalCompletionList GenerateCompletionList(Range textEditRan
Items = [
new VSInternalCompletionItem()
{
+ Label = string.Empty, // label string is non-nullable
TextEdit = VsLspFactory.CreateTextEdit(textEditRange, "Hello")
}
]
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionResolveEndpointTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionResolveEndpointTest.cs
index 8586d5b9635..f39565e99ad 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionResolveEndpointTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionResolveEndpointTest.cs
@@ -4,12 +4,12 @@
#nullable disable
using System;
-using System.Text.Json;
using System.Linq;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Razor.LanguageServer.Test;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
using Xunit.Abstractions;
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Definition/RazorComponentDefinitionHelpersTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Definition/RazorComponentDefinitionHelpersTest.cs
index d2034729071..a044d20d8ae 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Definition/RazorComponentDefinitionHelpersTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Definition/RazorComponentDefinitionHelpersTest.cs
@@ -3,10 +3,10 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
-using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.ProjectSystem;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.GoToDefinition;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.LanguageServer.Protocol;
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DirectoryHelperTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DirectoryHelperTest.cs
index 705c30e1483..0eb14d8e33c 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DirectoryHelperTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DirectoryHelperTest.cs
@@ -7,7 +7,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Xunit;
using Xunit.Abstractions;
using static Microsoft.AspNetCore.Razor.LanguageServer.DirectoryHelper;
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Hover/HoverServiceTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Hover/HoverServiceTest.cs
index 6dc5f7eda36..759022fb19b 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Hover/HoverServiceTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Hover/HoverServiceTest.cs
@@ -6,7 +6,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
-using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hover;
using Microsoft.AspNetCore.Razor.ProjectSystem;
@@ -14,6 +13,7 @@
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.AspNetCore.Razor.Test.Common.ProjectSystem;
using Microsoft.CodeAnalysis.Classification;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/LinkedEditingRange/LinkedEditingRangeEndpointTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/LinkedEditingRange/LinkedEditingRangeEndpointTest.cs
index 4afda8d32d0..8b5f4f31106 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/LinkedEditingRange/LinkedEditingRangeEndpointTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/LinkedEditingRange/LinkedEditingRangeEndpointTest.cs
@@ -6,7 +6,7 @@
using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.LinkedEditingRange;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Microsoft.AspNetCore.Razor.LanguageServer.Test.csproj b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Microsoft.AspNetCore.Razor.LanguageServer.Test.csproj
index 685173189f4..ddcf41ab63a 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Microsoft.AspNetCore.Razor.LanguageServer.Test.csproj
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Microsoft.AspNetCore.Razor.LanguageServer.Test.csproj
@@ -10,12 +10,12 @@
-
+
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs
index 6ef88b7406d..194d78f6ee5 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs
@@ -14,12 +14,12 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
-using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.SemanticTokens;
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TagHelperFactsServiceTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TagHelperFactsServiceTest.cs
index 4e4bba21832..f4976ce41f7 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TagHelperFactsServiceTest.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TagHelperFactsServiceTest.cs
@@ -7,7 +7,7 @@
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
-using Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.Editor.Razor;
using Xunit;
using Xunit.Abstractions;
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListCacheTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListCacheTest.cs
similarity index 98%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListCacheTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListCacheTest.cs
index e8e4168611b..09d4ddcd119 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListCacheTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListCacheTest.cs
@@ -6,7 +6,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class CompletionListCacheTest : ToolingTestBase
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListMergerTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListMergerTest.cs
similarity index 98%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListMergerTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListMergerTest.cs
index ee70ac5c7b0..2b9eacc282c 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListMergerTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListMergerTest.cs
@@ -9,7 +9,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class CompletionListMergerTest : ToolingTestBase
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListOptimizerTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListOptimizerTest.cs
similarity index 97%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListOptimizerTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListOptimizerTest.cs
index 9d37f1f9655..c4023265695 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/CompletionListOptimizerTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/CompletionListOptimizerTest.cs
@@ -8,7 +8,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class CompletionListOptimizerTest : ToolingTestBase
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/DirectiveAttributeTransitionCompletionItemProviderTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/DirectiveAttributeTransitionCompletionItemProviderTest.cs
similarity index 98%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/DirectiveAttributeTransitionCompletionItemProviderTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/DirectiveAttributeTransitionCompletionItemProviderTest.cs
index 62edca4716a..f8c52ceb2d2 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/DirectiveAttributeTransitionCompletionItemProviderTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/DirectiveAttributeTransitionCompletionItemProviderTest.cs
@@ -7,12 +7,11 @@
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.Test.Common;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Text;
using Xunit;
using Xunit.Abstractions;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class DirectiveAttributeTransitionCompletionItemProviderTest : ToolingTestBase
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/DirectiveVerifier.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/DirectiveVerifier.cs
similarity index 93%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/DirectiveVerifier.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/DirectiveVerifier.cs
index 586a8d25dd7..513bc307235 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/DirectiveVerifier.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/DirectiveVerifier.cs
@@ -4,11 +4,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
internal static class DirectiveVerifier
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/LanguageServerTagHelperCompletionServiceTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/LanguageServerTagHelperCompletionServiceTest.cs
similarity index 99%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/LanguageServerTagHelperCompletionServiceTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/LanguageServerTagHelperCompletionServiceTest.cs
index 8243e3c05a8..7d78b3e3a93 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/LanguageServerTagHelperCompletionServiceTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/LanguageServerTagHelperCompletionServiceTest.cs
@@ -9,12 +9,11 @@
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Test.Common;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Xunit;
using Xunit.Abstractions;
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class LanguageServerTagHelperCompletionServiceTest(ITestOutputHelper testOutput) : ToolingTestBase(testOutput)
{
@@ -1459,7 +1458,7 @@ public void GetElementCompletions_MustSatisfyAttributeRules_NoAttributes_Allowed
AssertCompletionsAreEquivalent(expectedCompletions, completions);
}
- private static LspTagHelperCompletionService CreateTagHelperCompletionFactsService() => new();
+ private static TagHelperCompletionService CreateTagHelperCompletionFactsService() => new();
private static void AssertCompletionsAreEquivalent(ElementCompletionResult expected, ElementCompletionResult actual)
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/MarkupTransitionCompletionItemProviderTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/MarkupTransitionCompletionItemProviderTest.cs
similarity index 98%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/MarkupTransitionCompletionItemProviderTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/MarkupTransitionCompletionItemProviderTest.cs
index 02c27f18395..5a79ab6f76b 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/MarkupTransitionCompletionItemProviderTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/MarkupTransitionCompletionItemProviderTest.cs
@@ -9,12 +9,10 @@
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.Test.Common;
-using Microsoft.CodeAnalysis.Razor;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Xunit;
using Xunit.Abstractions;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class MarkupTransitionCompletionItemProviderTest(ITestOutputHelper testOutput) : ToolingTestBase(testOutput)
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionListProviderTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/RazorCompletionListProviderTest.cs
similarity index 95%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionListProviderTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/RazorCompletionListProviderTest.cs
index e941770fc8f..e7a654019be 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/RazorCompletionListProviderTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/RazorCompletionListProviderTest.cs
@@ -12,7 +12,6 @@
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@@ -20,7 +19,7 @@
using Xunit.Abstractions;
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class RazorCompletionListProviderTest : LanguageServerTestBase
{
@@ -28,6 +27,7 @@ public class RazorCompletionListProviderTest : LanguageServerTestBase
private readonly CompletionListCache _completionListCache;
private readonly VSInternalClientCapabilities _clientCapabilities;
private readonly VSInternalCompletionContext _defaultCompletionContext;
+ private readonly RazorCompletionOptions _razorCompletionOptions;
public RazorCompletionListProviderTest(ITestOutputHelper testOutput)
: base(testOutput)
@@ -54,21 +54,20 @@ public RazorCompletionListProviderTest(ITestOutputHelper testOutput)
};
_defaultCompletionContext = new VSInternalCompletionContext();
+ _razorCompletionOptions = new RazorCompletionOptions(SnippetsSupported: true, AutoInsertAttributeQuotes: true, CommitElementsWithSpace: true);
}
- private static IEnumerable GetCompletionProviders(RazorLSPOptionsMonitor optionsMonitor = null)
+ private static IEnumerable GetCompletionProviders()
{
// Working around strong naming restriction.
- var tagHelperCompletionService = new LspTagHelperCompletionService();
-
- optionsMonitor ??= TestRazorLSPOptionsMonitor.Create();
+ var tagHelperCompletionService = new TagHelperCompletionService();
var completionProviders = new IRazorCompletionItemProvider[]
{
new DirectiveCompletionItemProvider(),
new DirectiveAttributeCompletionItemProvider(),
new DirectiveAttributeParameterCompletionItemProvider(),
- new TagHelperCompletionProvider(tagHelperCompletionService, optionsMonitor)
+ new TagHelperCompletionProvider(tagHelperCompletionService)
};
return completionProviders;
@@ -369,7 +368,7 @@ public async Task GetCompletionListAsync_ProvidesDirectiveCompletionItems(string
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: cursorPosition, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: cursorPosition, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, _razorCompletionOptions, DisposalToken);
// Assert
@@ -395,7 +394,7 @@ public async Task GetCompletionListAsync_ProvidesDirectiveCompletions_Incomplete
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, _razorCompletionOptions, DisposalToken);
// Assert
@@ -427,7 +426,7 @@ public async Task GetCompletionListAsync_ProvidesInjectOnIncomplete_KeywordIn()
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, _razorCompletionOptions, DisposalToken);
// Assert
Assert.Collection(completionList.Items,
@@ -456,7 +455,7 @@ public async Task GetCompletionListAsync_DoesNotProvideInjectOnInvoked()
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, _razorCompletionOptions, DisposalToken);
// Assert
Assert.Empty(completionList.Items);
@@ -484,7 +483,7 @@ public async Task GetCompletionListAsync_ProvidesInjectOnIncomplete()
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: 1, completionContext, documentContext, _clientCapabilities, existingCompletions: null, _razorCompletionOptions, DisposalToken);
// Assert
Assert.Collection(completionList.Items,
@@ -510,7 +509,7 @@ public async Task GetCompletionListAsync_ProvidesTagHelperElementCompletionItems
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 1, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: 1, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, _razorCompletionOptions, DisposalToken);
// Assert
Assert.Contains(completionList.Items, item => item.InsertText == "Test");
@@ -540,7 +539,7 @@ public async Task GetCompletionListAsync_ProvidesTagHelperAttributeItems()
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 6, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: 6, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, _razorCompletionOptions, DisposalToken);
// Assert
Assert.Contains(completionList.Items, item => item.InsertText == "testAttribute=\"$0\"");
@@ -566,16 +565,15 @@ public async Task GetCompletionListAsync_ProvidesTagHelperAttributeItems_Attribu
codeDocument.SetTagHelperContext(tagHelperContext);
var documentContext = TestDocumentContext.Create(documentPath, codeDocument);
- // Set up a custom options monitor with desired options
- var optionsMonitor = TestRazorLSPOptionsMonitor.Create();
- await optionsMonitor.UpdateAsync(optionsMonitor.CurrentValue with { AutoInsertAttributeQuotes = false }, DisposalToken);
+ // Set up desired options
+ var razorCompletionOptions = new RazorCompletionOptions(SnippetsSupported: true, AutoInsertAttributeQuotes: false, CommitElementsWithSpace: true);
- var completionFactsService = new LspRazorCompletionFactsService(GetCompletionProviders(optionsMonitor));
+ var completionFactsService = new LspRazorCompletionFactsService(GetCompletionProviders());
var provider = new RazorCompletionListProvider(completionFactsService, _completionListCache, LoggerFactory);
// Act
var completionList = await provider.GetCompletionListAsync(
- absoluteIndex: 6, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, DisposalToken);
+ absoluteIndex: 6, _defaultCompletionContext, documentContext, _clientCapabilities, existingCompletions: null, razorCompletionOptions, DisposalToken);
// Assert
Assert.Contains(completionList.Items, item => item.InsertText == "testAttribute=$0");
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperCompletionProviderTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/TagHelperCompletionProviderTest.cs
similarity index 95%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperCompletionProviderTest.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/TagHelperCompletionProviderTest.cs
index 174c2a11c51..909a87a8f7b 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperCompletionProviderTest.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/TagHelperCompletionProviderTest.cs
@@ -5,24 +5,21 @@
using System.Collections.Generic;
using System.Collections.Immutable;
-using System.Threading;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.Test.Common;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.Editor.Razor;
using Xunit;
using Xunit.Abstractions;
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public class TagHelperCompletionProviderTest(ITestOutputHelper testOutput) : TagHelperServiceTestBase(testOutput)
{
private TagHelperCompletionProvider CreateTagHelperCompletionProvider()
- => new(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ => new(RazorTagHelperCompletionService);
[Fact]
public void GetNearestAncestorTagInfo_MarkupElement()
@@ -69,7 +66,7 @@ public void GetNearestAncestorTagInfo_TagHelperElement()
public void GetCompletionAt_AtEmptyTagName_ReturnsCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -92,7 +89,7 @@ public void GetCompletionAt_AtEmptyTagName_ReturnsCompletions()
public void GetCompletionAt_InEmptyDocument_ReturnsEmptyCompletionArray()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"$$",
isRazorFile: true,
@@ -109,7 +106,7 @@ public void GetCompletionAt_InEmptyDocument_ReturnsEmptyCompletionArray()
public void GetCompletionAt_OutsideOfTagName_DoesNotReturnCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -129,7 +126,7 @@ public void GetCompletionAt_OutsideOfTagName_DoesNotReturnCompletions()
public void GetCompletionAt_OutsideOfTagName_InsideCSharp_DoesNotReturnCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -153,7 +150,7 @@ public void GetCompletionAt_OutsideOfTagName_InsideCSharp_DoesNotReturnCompletio
public void GetCompletionAt_SelfClosingTag_NotAtEndOfName_DoesNotReturnCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -173,7 +170,7 @@ public void GetCompletionAt_SelfClosingTag_NotAtEndOfName_DoesNotReturnCompletio
public void GetCompletionAt_SelfClosingTag_ReturnsCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -193,7 +190,7 @@ public void GetCompletionAt_SelfClosingTag_ReturnsCompletions()
public void GetCompletionAt_SelfClosingTag_InsideCSharp_ReturnsCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -217,7 +214,7 @@ public void GetCompletionAt_SelfClosingTag_InsideCSharp_ReturnsCompletions()
public void GetCompletionAt_MalformedElement()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -238,7 +235,7 @@ public void GetCompletionAt_MalformedElement()
public void GetCompletionAt_AtHtmlElementNameEdge_ReturnsCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -260,7 +257,7 @@ public void GetCompletionAt_AtHtmlElementNameEdge_ReturnsCompletions()
public void GetCompletionAt_AtTagHelperElementNameEdge_ReturnsCompletions()
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -317,7 +314,7 @@ public void GetCompletionAt_AtTagHelperElementNameEdge_ReturnsCompletions()
public void GetCompletionAt_AtAttributeEdge_BothAttribute_ReturnsCompletions(string documentText)
{
// Arrange
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, TestRazorLSPOptionsMonitor.Create());
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
documentText,
isRazorFile: false,
@@ -348,7 +345,7 @@ public void GetCompletionAt_AtAttributeEdge_IntAttribute_Snippets_ReturnsComplet
{
// Arrange
var service = CreateTagHelperCompletionProvider();
- var options = new RazorCompletionOptions(SnippetsSupported: true);
+ var options = new RazorCompletionOptions(SnippetsSupported: true, AutoInsertAttributeQuotes: true, CommitElementsWithSpace: true);
var context = CreateRazorCompletionContext(
"""
@addTagHelper *, TestAssembly
@@ -439,12 +436,11 @@ public void GetCompletionAt_InBody_ReturnsCompletions()
}
[Fact]
- public async Task GetCompletionAt_InBody_WithoutSpace_ReturnsCompletions()
+ public void GetCompletionAt_InBody_WithoutSpace_ReturnsCompletions()
{
// Arrange
- var options = TestRazorLSPOptionsMonitor.Create();
- await options.UpdateAsync(options.CurrentValue with { CommitElementsWithSpace = false }, CancellationToken.None);
- var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService, options);
+ var razorCompletionOptions = new RazorCompletionOptions(SnippetsSupported: true, AutoInsertAttributeQuotes: true, CommitElementsWithSpace: false);
+ var service = new TagHelperCompletionProvider(RazorTagHelperCompletionService);
var context = CreateRazorCompletionContext(
"""
@@ -454,7 +450,8 @@ public async Task GetCompletionAt_InBody_WithoutSpace_ReturnsCompletions()
""",
isRazorFile: false,
- tagHelpers: DefaultTagHelpers);
+ tagHelpers: DefaultTagHelpers,
+ options: razorCompletionOptions);
// Act
var completions = service.GetCompletionItems(context);
@@ -840,7 +837,7 @@ public void GetCompletionsAt_MiddleOfFullAttribute_ReturnsCompletions_NoSnippetB
""",
isRazorFile: false,
- options: new(SnippetsSupported: true),
+ options: new(SnippetsSupported: true, AutoInsertAttributeQuotes: true, CommitElementsWithSpace: true),
tagHelpers: DefaultTagHelpers);
// Act
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperServiceTestBase.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/TagHelperServiceTestBase.cs
similarity index 94%
rename from src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperServiceTestBase.cs
rename to src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/TagHelperServiceTestBase.cs
index de6cbf14e92..61cc1d0e561 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperServiceTestBase.cs
+++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/Completion/TagHelperServiceTestBase.cs
@@ -8,11 +8,10 @@
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Razor.Completion;
using Xunit.Abstractions;
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
-namespace Microsoft.AspNetCore.Razor.LanguageServer.Completion;
+namespace Microsoft.CodeAnalysis.Razor.Completion;
public abstract class TagHelperServiceTestBase : LanguageServerTestBase
{
@@ -246,23 +245,23 @@ public TagHelperServiceTestBase(ITestOutputHelper testOutput)
directiveAttribute3.Build(),
htmlTagMutator.Build());
- RazorTagHelperCompletionService = new LspTagHelperCompletionService();
+ RazorTagHelperCompletionService = new TagHelperCompletionService();
}
protected static string GetFileName(bool isRazorFile)
=> isRazorFile ? RazorFile : CSHtmlFile;
- internal static RazorCodeDocument CreateCodeDocument(string text, bool isRazorFile, ImmutableArray tagHelpers)
+ protected internal static RazorCodeDocument CreateCodeDocument(string text, bool isRazorFile, ImmutableArray tagHelpers)
{
return CreateCodeDocument(text, GetFileName(isRazorFile), tagHelpers);
}
- internal static RazorCodeDocument CreateCodeDocument(string text, bool isRazorFile, params TagHelperDescriptor[] tagHelpers)
+ protected internal static RazorCodeDocument CreateCodeDocument(string text, bool isRazorFile, params TagHelperDescriptor[] tagHelpers)
{
return CreateCodeDocument(text, GetFileName(isRazorFile), tagHelpers);
}
- internal static RazorCodeDocument CreateCodeDocument(string text, string filePath, ImmutableArray tagHelpers)
+ protected internal static RazorCodeDocument CreateCodeDocument(string text, string filePath, ImmutableArray tagHelpers)
{
tagHelpers = tagHelpers.NullToEmpty();
@@ -274,7 +273,7 @@ internal static RazorCodeDocument CreateCodeDocument(string text, string filePat
return codeDocument;
}
- internal static RazorCodeDocument CreateCodeDocument(string text, string filePath, params TagHelperDescriptor[] tagHelpers)
+ protected internal static RazorCodeDocument CreateCodeDocument(string text, string filePath, params TagHelperDescriptor[] tagHelpers)
{
tagHelpers ??= Array.Empty();
diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentCompletionEndpointTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentCompletionEndpointTest.cs
new file mode 100644
index 00000000000..4dea627af65
--- /dev/null
+++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentCompletionEndpointTest.cs
@@ -0,0 +1,508 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the MIT license. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Razor.PooledObjects;
+using Microsoft.AspNetCore.Razor.Test.Common;
+using Microsoft.CodeAnalysis.ExternalAccess.Razor;
+using Microsoft.CodeAnalysis.Razor.Settings;
+using Microsoft.VisualStudio.LanguageServer.Protocol;
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.Razor.Settings;
+using Microsoft.VisualStudio.Razor.Snippets;
+using Xunit;
+using Xunit.Abstractions;
+using RoslynCompletionParams = Roslyn.LanguageServer.Protocol.CompletionParams;
+using RoslynCompletionTriggerKind = Roslyn.LanguageServer.Protocol.CompletionTriggerKind;
+using RoslynLspExtensions = Roslyn.LanguageServer.Protocol.RoslynLspExtensions;
+using RoslynTextDocumentIdentifier = Roslyn.LanguageServer.Protocol.TextDocumentIdentifier;
+using RoslynVSInternalCompletionContext = Roslyn.LanguageServer.Protocol.VSInternalCompletionContext;
+using RoslynVSInternalCompletionInvokeKind = Roslyn.LanguageServer.Protocol.VSInternalCompletionInvokeKind;
+
+namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
+
+public class CohostDocumentCompletionEndpointTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper)
+{
+ [Fact]
+ public async Task CSharpClassesAtTransition()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+ @$$
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "@",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["char", "DateTime", "Exception"],
+ expectedItemCount: 996);
+ }
+
+ [Fact]
+ public async Task CSharpClassMembersAtProvisionalCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+ @DateTime.$$
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = ".",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["DaysInMonth", "IsLeapYear", "Now"],
+ expectedItemCount: 20);
+ }
+
+ [Fact]
+ public async Task CSharpClassesInCodeBlock()
+ {
+ await VerifyCompletionListAsync(
+ input: $$"""
+ This is a Razor document.
+
+
+
+ @code{ $$ }
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Explicit,
+ TriggerCharacter = null,
+ TriggerKind = RoslynCompletionTriggerKind.Invoked
+ },
+ expectedItemLabels: ["char", "DateTime", "Exception"],
+ expectedItemCount: 1000);
+ }
+
+ [Fact]
+ public async Task CSharpClassMembersInCodeBlock()
+ {
+ await VerifyCompletionListAsync(
+ input: $$"""
+ This is a Razor document.
+
+
+
+ @code{
+ void foo()
+ {
+ DateTime.$$
+ }
+ }
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = ".",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["DaysInMonth", "IsLeapYear", "Now"],
+ expectedItemCount: 20);
+ }
+
+ // Tests MarkupTransitionCompletionItemProvider
+ [Fact]
+ public async Task CSharpMarkupTransitionAndTagHelpersInCodeBlock()
+ {
+ await VerifyCompletionListAsync(
+ input: $$"""
+ This is a Razor document.
+
+
+
+ @code{
+ void foo()
+ {
+ <$$
+ }
+ }
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "<",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["text", "EditForm", "InputDate"],
+ expectedItemCount: 34);
+ }
+
+ [Fact]
+ public async Task RazorDirectives()
+ {
+ await VerifyCompletionListAsync(
+ input: $$"""
+ @$$
+ This is a Razor document.
+
+
+
+ @code{
+ void foo()
+ {
+
+ }
+ }
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "@",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["using", "using directive ...", "page", "page directive ..."],
+ expectedItemCount: 538);
+ }
+
+ [Fact]
+ public async Task ElementNameTagHelpersCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+ <$$
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "<",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["LayoutView", "EditForm", "ValidationMessage"],
+ expectedItemCount: 33);
+ }
+
+ [Fact]
+ public async Task HtmlElementNamesAndTagHelpersCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+ <$$
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "<",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["div", "h1", "LayoutView", "EditForm", "ValidationMessage"],
+ expectedItemCount: 35,
+ delegatedItemLabels: ["div", "h1"]);
+ }
+
+ [Fact]
+ public async Task HtmlElementDoNotCommitWithSpace()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+ <$$
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "<",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["div", "h1", "LayoutView", "EditForm", "ValidationMessage"],
+ expectedItemCount: 35,
+ delegatedItemLabels: ["div", "h1"],
+ delegatedItemCommitCharacters: [" ", ">"],
+ commitElementsWithSpace: false);
+ }
+
+ [Fact]
+ public async Task HtmlSnippetsCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+ $$
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Explicit,
+ TriggerCharacter = null,
+ TriggerKind = RoslynCompletionTriggerKind.Invoked
+ },
+ expectedItemLabels: ["snippet1", "snippet2"],
+ expectedItemCount: 2,
+ snippetLabels: ["snippet1", "snippet2"]);
+ }
+
+ // Tests HTML attributes and DirectiveAttributeTransitionCompletionItemProvider
+ [Fact]
+ public async Task HtmlAndDirectiveAttributeTransitionNamesCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = " ",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["style", "dir", "@..."],
+ expectedItemCount: 3,
+ delegatedItemLabels: ["style", "dir"]);
+ }
+
+ // Tests HTML attributes and DirectiveAttributeCompletionItemProvider
+ [Fact]
+ public async Task HtmlAndDirectiveAttributeNamesCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "@",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["style", "dir", "@rendermode", "@bind-..."],
+ expectedItemCount: 104,
+ delegatedItemLabels: ["style", "dir"]);
+ }
+
+ // Tests HTML attributes and DirectiveAttributeParameterCompletionItemProvider
+ [Fact]
+ public async Task HtmlAndDirectiveAttributeParameterNamesCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = "f",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["style", "dir", "culture", "event", "format", "get", "set", "after"],
+ expectedItemCount: 8,
+ delegatedItemLabels: ["style", "dir"]);
+ }
+
+ [Fact]
+ public async Task HtmlAttributeNamesAndTagHelpersCompletion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = " ",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["style", "dir", "FormName", "OnValidSubmit", "@..."],
+ expectedItemCount: 13,
+ delegatedItemLabels: ["style", "dir"]);
+ }
+
+ [Fact]
+ public async Task TagHelperAttributes_NoAutoInsertQuotes_Completion()
+ {
+ await VerifyCompletionListAsync(
+ input: $"""
+ This is a Razor document.
+
+
+
+ The end.
+ """,
+ completionContext: new RoslynVSInternalCompletionContext()
+ {
+ InvokeKind = RoslynVSInternalCompletionInvokeKind.Typing,
+ TriggerCharacter = " ",
+ TriggerKind = RoslynCompletionTriggerKind.TriggerCharacter
+ },
+ expectedItemLabels: ["FormName", "OnValidSubmit", "@..."],
+ expectedItemCount: 11,
+ autoInsertAttributeQuotes: false);
+ }
+
+ private async Task VerifyCompletionListAsync(
+ TestCode input,
+ RoslynVSInternalCompletionContext completionContext,
+ string[] expectedItemLabels,
+ int expectedItemCount,
+ string[]? delegatedItemLabels = null,
+ string[]? delegatedItemCommitCharacters = null,
+ string[]? snippetLabels = null,
+ bool autoInsertAttributeQuotes = true,
+ bool commitElementsWithSpace = true)
+ {
+ var document = await CreateProjectAndRazorDocumentAsync(input.Text);
+ var sourceText = await document.GetTextAsync(DisposalToken);
+
+ var clientSettingsManager = new ClientSettingsManager([], null, null);
+ clientSettingsManager.Update(ClientAdvancedSettings.Default with { AutoInsertAttributeQuotes = autoInsertAttributeQuotes, CommitElementsWithSpace = commitElementsWithSpace });
+
+ VSInternalCompletionList? response = null;
+ if (delegatedItemLabels is not null)
+ {
+ response = new VSInternalCompletionList()
+ {
+ Items = delegatedItemLabels.Select((label) => new VSInternalCompletionItem()
+ {
+ Label = label,
+ CommitCharacters = delegatedItemCommitCharacters,
+ // If test specifies not to commit with space, set kind to element since we remove space
+ // commit from elements only. Otherwise test doesn't care, so set to None
+ Kind = !commitElementsWithSpace ? CompletionItemKind.Element : CompletionItemKind.None,
+ }).ToArray(),
+ IsIncomplete = true
+ };
+ }
+
+ var requestInvoker = new TestLSPRequestInvoker([(Methods.TextDocumentCompletionName, response)]);
+
+ var snippetCompletionItemProvider = new SnippetCompletionItemProvider(new SnippetCache());
+ if (snippetLabels is not null)
+ {
+ var snippetInfos = snippetLabels.Select(label => new SnippetInfo(label, label, label, string.Empty, SnippetLanguage.Html)).ToImmutableArray();
+ snippetCompletionItemProvider.SnippetCache.Update(SnippetLanguage.Html, snippetInfos);
+ }
+
+ var completionSetting = new CompletionSetting
+ {
+ CompletionItem = new CompletionItemSetting(),
+ CompletionItemKind = new CompletionItemKindSetting()
+ {
+ ValueSet = (CompletionItemKind[])Enum.GetValues(typeof(CompletionItemKind)),
+ },
+ CompletionListSetting = new CompletionListSetting()
+ {
+ ItemDefaults = ["commitCharacters", "editRange", "insertTextFormat"]
+ },
+ ContextSupport = false,
+ InsertTextMode = InsertTextMode.AsIs,
+ };
+ var clientCapabilities = new VSInternalClientCapabilities
+ {
+ TextDocument = new TextDocumentClientCapabilities
+ {
+ Completion = completionSetting
+ }
+ };
+ var endpoint = new CohostDocumentCompletionEndpoint(
+ RemoteServiceInvoker,
+ clientSettingsManager,
+ TestHtmlDocumentSynchronizer.Instance,
+ snippetCompletionItemProvider,
+ requestInvoker,
+ LoggerFactory);
+ endpoint.GetTestAccessor().SetClientCapabilities(clientCapabilities);
+
+ var request = new RoslynCompletionParams()
+ {
+ TextDocument = new RoslynTextDocumentIdentifier()
+ {
+ Uri = document.CreateUri()
+ },
+ Position = RoslynLspExtensions.GetPosition(sourceText, input.Position),
+ Context = completionContext
+ };
+
+ // Roslyn doesn't always return all items right away, so using retry logic
+ VSInternalCompletionList? result = null;
+ var resultCount = 0;
+ const int maxResultCount = 100;
+ do
+ {
+ if (resultCount > 0)
+ {
+ await Task.Delay(100);
+ }
+
+ result = await endpoint.GetTestAccessor().HandleRequestAsync(request, document, DisposalToken);
+ }
+ while (result is not null
+ && result.IsIncomplete
+ && result.Items.Length < expectedItemCount
+ && resultCount++ < maxResultCount);
+
+ Assert.NotNull(result);
+ Assert.Equal(expectedItemCount, result.Items.Length);
+
+ using var _ = HashSetPool.GetPooledObject(out var labelSet);
+ labelSet.AddRange(result.Items.Select((item) => item.Label));
+ foreach (var expectedItemLabel in expectedItemLabels)
+ {
+ Assert.Contains(expectedItemLabel, labelSet);
+ }
+
+ if (!commitElementsWithSpace)
+ {
+ Assert.False(result.Items.Any(item => item.CommitCharacters?.First().Contains(" ") ?? false));
+ }
+
+ if (!autoInsertAttributeQuotes)
+ {
+ // Tag helper attributes create InsertText that looks something like
+ // "OnValidSubmit=\"$0\"" (for OnValidSubmit attribute). Make sure the value
+ // placeholder $0 is not surrounded with quotes if we set AutoInsertAttributeQuotes
+ // to false
+ Assert.False(result.Items.Any(item => item.InsertText?.Contains("\"$0\"") ?? false));
+ }
+ }
+}
diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs
index 99132c4935c..69b3f7ed42a 100644
--- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs
+++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/RazorCustomMessageTargetTest.cs
@@ -64,7 +64,7 @@ public async Task UpdateCSharpBuffer_CannotLookupDocument_NoopsGracefully()
Mock.Of(MockBehavior.Strict),
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -106,7 +106,7 @@ public async Task UpdateCSharpBuffer_UpdatesDocument()
Mock.Of(MockBehavior.Strict),
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -159,7 +159,7 @@ public async Task UpdateCSharpBuffer_UpdatesCorrectDocument()
Mock.Of(MockBehavior.Strict),
new TestLanguageServerFeatureOptions(includeProjectKeyInGeneratedFilePath: true),
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -200,7 +200,7 @@ public async Task ProvideCodeActionsAsync_CannotLookupDocument_ReturnsNullAsync(
Mock.Of(MockBehavior.Strict),
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -280,7 +280,7 @@ async IAsyncEnumerable>
telemetryReporter.Object,
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -367,7 +367,7 @@ async IAsyncEnumerable> GetExpectedRe
telemetryReporter.Object,
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -409,7 +409,7 @@ public async Task ProvideSemanticTokensAsync_CannotLookupDocument_ReturnsNullAsy
Mock.Of(MockBehavior.Strict),
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -458,7 +458,7 @@ public async Task ProvideSemanticTokensAsync_CannotLookupVirtualDocument_Returns
Mock.Of(MockBehavior.Strict),
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -535,7 +535,7 @@ public async Task ProvideSemanticTokensAsync_ContainsRange_ReturnsSemanticTokens
telemetryReporter.Object,
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);
@@ -613,7 +613,7 @@ public async Task ProvideSemanticTokensAsync_EmptyRange_ReturnsNoSemanticTokens(
telemetryReporter.Object,
TestLanguageServerFeatureOptions.Instance,
StrictMock.Of(),
- new SnippetCache(),
+ new SnippetCompletionItemProvider(new SnippetCache()),
StrictMock.Of(),
StrictMock.Of(),
LoggerFactory);