From 745df99a284c7cb5169250848619f0c0819dca2d Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 3 Mar 2023 07:55:51 +1100 Subject: [PATCH] Revert back to 0591c7026e0972ff2f2da0be7caf26f1590e73f6 --- .../AutoClosingTagOnAutoInsertProvider.cs | 123 +++++++++--------- .../CloseTextTagOnAutoInsertProvider.cs | 44 ++++--- .../AutoInsert/IOnAutoInsertProvider.cs | 15 --- .../AutoInsert/OnAutoInsertEndpoint.cs | 18 ++- .../AutoInsert/RazorOnAutoInsertProvider.cs | 29 +++++ .../IVSOnAutoInsertEndpoint.cs | 13 ++ .../OnAutoInsertParamsBridge.cs | 10 ++ .../RazorLanguageServer.cs | 4 +- .../AutoClosingTagOnAutoInsertProviderTest.cs | 2 +- .../CloseTextTagOnAutoInsertProviderTest.cs | 2 +- .../AutoInsert/OnAutoInsertEndpointTest.cs | 55 ++++---- .../RazorOnAutoInsertProviderTestBase.cs | 2 +- src/Shared/Directory.Build.props | 15 --- ...t.AspNetCore.Razor.Utilities.Shared.csproj | 1 - 14 files changed, 182 insertions(+), 151 deletions(-) delete mode 100644 src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/IOnAutoInsertProvider.cs create mode 100644 src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/RazorOnAutoInsertProvider.cs create mode 100644 src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/IVSOnAutoInsertEndpoint.cs create mode 100644 src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/OnAutoInsertParamsBridge.cs delete mode 100644 src/Shared/Directory.Build.props diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/AutoClosingTagOnAutoInsertProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/AutoClosingTagOnAutoInsertProvider.cs index b33a447afa0..194cc91e262 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/AutoClosingTagOnAutoInsertProvider.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/AutoClosingTagOnAutoInsertProvider.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -18,10 +17,11 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; -internal sealed class AutoClosingTagOnAutoInsertProvider : IOnAutoInsertProvider +internal class AutoClosingTagOnAutoInsertProvider : RazorOnAutoInsertProvider { // From http://dev.w3.org/html5/spec/Overview.html#elements-0 - private static readonly ImmutableHashSet s_voidElements = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, + private static readonly IReadOnlyList s_voidElements = new[] + { "area", "base", "br", @@ -39,32 +39,35 @@ internal sealed class AutoClosingTagOnAutoInsertProvider : IOnAutoInsertProvider "source", "track", "wbr" - ); - private static readonly ImmutableHashSet s_voidElementsCaseSensitive = s_voidElements.WithComparer(StringComparer.Ordinal); + }; private readonly IOptionsMonitor _optionsMonitor; - private readonly ILogger _logger; public AutoClosingTagOnAutoInsertProvider(IOptionsMonitor optionsMonitor, ILoggerFactory loggerFactory) + : base(loggerFactory) { if (optionsMonitor is null) { throw new ArgumentNullException(nameof(optionsMonitor)); } - if (loggerFactory is null) - { - throw new ArgumentNullException(nameof(loggerFactory)); - } - _optionsMonitor = optionsMonitor; - _logger = loggerFactory.CreateLogger(); } - public string TriggerCharacter => ">"; + public override string TriggerCharacter => ">"; - public bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit? edit, out InsertTextFormat format) + public override bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit? edit, out InsertTextFormat format) { + if (position is null) + { + throw new ArgumentNullException(nameof(position)); + } + + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + if (!_optionsMonitor.CurrentValue.AutoClosingTags) { format = default; @@ -72,7 +75,7 @@ public bool TryResolveInsertion(Position position, FormattingContext context, [N return false; } - if (!position.TryGetAbsoluteIndex(context.SourceText, _logger, out var afterCloseAngleIndex)) + if (!position.TryGetAbsoluteIndex(context.SourceText, Logger, out var afterCloseAngleIndex)) { format = default; edit = default; @@ -94,26 +97,28 @@ public bool TryResolveInsertion(Position position, FormattingContext context, [N NewText = $"$0", Range = new Range { Start = position, End = position }, }; - - return true; } + else + { + Debug.Assert(autoClosingBehavior == AutoClosingBehavior.SelfClosing); - Debug.Assert(autoClosingBehavior == AutoClosingBehavior.SelfClosing); - - format = InsertTextFormat.Plaintext; + format = InsertTextFormat.Plaintext; - // Need to replace the `>` with ' />$0' or '/>$0' depending on if there's prefixed whitespace. - var insertionText = char.IsWhiteSpace(context.SourceText[afterCloseAngleIndex - 2]) ? "/" : " /"; - var insertionPosition = new Position(position.Line, position.Character - 1); - edit = new TextEdit() - { - NewText = insertionText, - Range = new Range + // Need to replace the `>` with ' />$0' or '/>$0' depending on if there's prefixed whitespace. + var insertionText = char.IsWhiteSpace(context.SourceText[afterCloseAngleIndex - 2]) ? "/" : " /"; + var insertionPosition = new Position(position.Line, position.Character - 1); + var insertionRange = new Range { Start = insertionPosition, End = insertionPosition - } - }; + }; + edit = new TextEdit() + { + NewText = insertionText, + Range = insertionRange + }; + + } return true; } @@ -131,14 +136,12 @@ private static bool TryResolveAutoClosingBehavior(FormattingContext context, int return false; } - if (owner.Parent is MarkupStartTagSyntax - { - ForwardSlash: null, - Parent: MarkupElementSyntax htmlElement - } startTag) + if (owner.Parent is MarkupStartTagSyntax startTag && + startTag.ForwardSlash is null && + startTag.Parent is MarkupElementSyntax htmlElement) { var unescapedTagName = startTag.Name.Content; - autoClosingBehavior = InferAutoClosingBehavior(unescapedTagName, caseSensitive: false); + autoClosingBehavior = InferAutoClosingBehavior(unescapedTagName); if (autoClosingBehavior == AutoClosingBehavior.EndTag && !CouldAutoCloseParentOrSelf(unescapedTagName, htmlElement)) { @@ -153,17 +156,15 @@ private static bool TryResolveAutoClosingBehavior(FormattingContext context, int return true; } - if (owner.Parent is MarkupTagHelperStartTagSyntax - { - ForwardSlash: null, - Parent: MarkupTagHelperElementSyntax tagHelperElement - } startTagHelper) + if (owner.Parent is MarkupTagHelperStartTagSyntax startTagHelper && + startTagHelper.ForwardSlash is null && + startTagHelper.Parent is MarkupTagHelperElementSyntax tagHelperElement) { name = startTagHelper.Name.Content; if (!TryGetTagHelperAutoClosingBehavior(tagHelperElement.TagHelperInfo.BindingResult, out autoClosingBehavior)) { - autoClosingBehavior = InferAutoClosingBehavior(name, caseSensitive: true); + autoClosingBehavior = InferAutoClosingBehavior(name, tagNameComparer: StringComparer.Ordinal); } if (autoClosingBehavior == AutoClosingBehavior.EndTag && !CouldAutoCloseParentOrSelf(name, tagHelperElement)) @@ -192,10 +193,8 @@ private static bool TryEnsureOwner_WorkaroundCompilerQuirks(int afterCloseAngleI return false; } - if (currentOwner.Parent is MarkupElementSyntax - { - StartTag: not null - } parentElement) + if (currentOwner.Parent is MarkupElementSyntax parentElement && + parentElement.StartTag != null) { // In cases where a user types ">" in a C# code block there can be uncertainty as to "who owns" the edge of the element. Reason being that the tag // could be malformed and you could be in a situation like this: @@ -212,10 +211,8 @@ private static bool TryEnsureOwner_WorkaroundCompilerQuirks(int afterCloseAngleI currentOwner = parentElement.StartTag.CloseAngle; } } - else if (currentOwner.Parent is MarkupTagHelperElementSyntax - { - StartTag: not null - } parentTagHelperElement) + else if (currentOwner.Parent is MarkupTagHelperElementSyntax parentTagHelperElement && + parentTagHelperElement.StartTag != null) { // Same reasoning as the above block here. @@ -240,7 +237,8 @@ private static bool TryEnsureOwner_WorkaroundCompilerQuirks(int afterCloseAngleI var closeAngleSourceChange = new SourceChange(closeAngleIndex, length: 0, newText: string.Empty); currentOwner = syntaxTree.Root.LocateOwner(closeAngleSourceChange); } - else if (currentOwner.Parent is MarkupEndTagSyntax or MarkupTagHelperEndTagSyntax) + else if (currentOwner.Parent is MarkupEndTagSyntax || + currentOwner.Parent is MarkupTagHelperEndTagSyntax) { // Quirk: https://github.com/dotnet/aspnetcore/issues/33919#issuecomment-870233627 // When tags are nested within each other within a C# block like: @@ -253,13 +251,18 @@ private static bool TryEnsureOwner_WorkaroundCompilerQuirks(int afterCloseAngleI // The owner will be the . Note this does not happen outside of C# blocks. var closeAngleSourceChange = new SourceChange(afterCloseAngleIndex - 1, length: 0, newText: string.Empty); - currentOwner = syntaxTree.Root.LocateOwner(closeAngleSourceChange) switch + currentOwner = syntaxTree.Root.LocateOwner(closeAngleSourceChange); + + // Get the real closing angle if we get the quote from an attribute syntax. See https://github.com/dotnet/razor-tooling/issues/5694 + switch (currentOwner) { - // Get the real closing angle if we get the quote from an attribute syntax. See https://github.com/dotnet/razor-tooling/issues/5694 - MarkupTextLiteralSyntax { Parent.Parent: MarkupStartTagSyntax startTag } => startTag.CloseAngle, - MarkupTextLiteralSyntax { Parent.Parent: MarkupTagHelperStartTagSyntax startTagHelper } => startTagHelper.CloseAngle, - var owner => owner - }; + case MarkupTextLiteralSyntax { Parent.Parent: MarkupStartTagSyntax startTag }: + currentOwner = startTag.CloseAngle; + break; + case MarkupTextLiteralSyntax { Parent.Parent: MarkupTagHelperStartTagSyntax startTagHelper }: + currentOwner = startTagHelper.CloseAngle; + break; + } } else if (currentOwner.Parent is MarkupStartTagSyntax startTag && startTag.OpenAngle.Position == afterCloseAngleIndex) @@ -320,11 +323,11 @@ private static bool TryEnsureOwner_WorkaroundCompilerQuirks(int afterCloseAngleI return true; } - private static AutoClosingBehavior InferAutoClosingBehavior(string name, bool caseSensitive) + private static AutoClosingBehavior InferAutoClosingBehavior(string name, StringComparer? tagNameComparer = null) { - var voidElements = caseSensitive ? s_voidElementsCaseSensitive : s_voidElements; + tagNameComparer ??= StringComparer.OrdinalIgnoreCase; - if (voidElements.Contains(name)) + if (s_voidElements.Contains(name, tagNameComparer)) { return AutoClosingBehavior.SelfClosing; } @@ -427,7 +430,7 @@ private static bool CouldAutoCloseParentOrSelf(string currentTagName, SyntaxNode } node = node.Parent; - } while (node is not null); + } while (node != null); return false; } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/CloseTextTagOnAutoInsertProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/CloseTextTagOnAutoInsertProvider.cs index 1306ebf859b..75f1fd71616 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/CloseTextTagOnAutoInsertProvider.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/CloseTextTagOnAutoInsertProvider.cs @@ -15,31 +15,37 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; -internal sealed class CloseTextTagOnAutoInsertProvider : IOnAutoInsertProvider +internal class CloseTextTagOnAutoInsertProvider : RazorOnAutoInsertProvider { private readonly IOptionsMonitor _optionsMonitor; - private readonly ILogger _logger; - public CloseTextTagOnAutoInsertProvider(IOptionsMonitor optionsMonitor, ILoggerFactory loggerFactory) + public CloseTextTagOnAutoInsertProvider( + IOptionsMonitor optionsMonitor, + ILoggerFactory loggerFactory) + : base(loggerFactory) { if (optionsMonitor is null) { throw new ArgumentNullException(nameof(optionsMonitor)); } - if (loggerFactory is null) - { - throw new ArgumentNullException(nameof(loggerFactory)); - } - _optionsMonitor = optionsMonitor; - _logger = loggerFactory.CreateLogger(); } - public string TriggerCharacter => ">"; + public override string TriggerCharacter => ">"; - public bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit? edit, out InsertTextFormat format) + public override bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit? edit, out InsertTextFormat format) { + if (position is null) + { + throw new ArgumentNullException(nameof(position)); + } + + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + if (!_optionsMonitor.CurrentValue.AutoClosingTags) { // We currently only support auto-closing tags our onType formatter. @@ -48,7 +54,7 @@ public bool TryResolveInsertion(Position position, FormattingContext context, [N return false; } - if (!IsAtTextTag(context, position, _logger)) + if (!IsAtTextTag(context, position, Logger)) { format = default; edit = default; @@ -75,14 +81,14 @@ private static bool IsAtTextTag(FormattingContext context, Position position, IL return false; } - var change = new SourceChange(absoluteIndex - 1, 0, string.Empty); + absoluteIndex -= 1; + var change = new SourceChange(absoluteIndex, 0, string.Empty); var owner = syntaxTree.Root.LocateOwner(change); - // Make sure the end tag doesn't already exist - if (owner?.Parent is MarkupStartTagSyntax - { - IsMarkupTransition: true, - Parent: MarkupElementSyntax { EndTag: null } - } startTag) + if (owner?.Parent != null && + owner.Parent is MarkupStartTagSyntax startTag && + startTag.IsMarkupTransition && + startTag.Parent is MarkupElementSyntax element && + element.EndTag is null) // Make sure the end tag doesn't already exist { Debug.Assert(string.Equals(startTag.Name.Content, SyntaxConstants.TextTagName, StringComparison.Ordinal), "MarkupTransition that is not a tag."); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/IOnAutoInsertProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/IOnAutoInsertProvider.cs deleted file mode 100644 index 5e83a82a646..00000000000 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/IOnAutoInsertProvider.cs +++ /dev/null @@ -1,15 +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.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Razor.LanguageServer.Formatting; -using Microsoft.VisualStudio.LanguageServer.Protocol; - -namespace Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; - -internal interface IOnAutoInsertProvider -{ - string TriggerCharacter { get; } - - bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit? edit, out InsertTextFormat format); -} diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/OnAutoInsertEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/OnAutoInsertEndpoint.cs index 30f55cc51fc..e83cebcace2 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/OnAutoInsertEndpoint.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/OnAutoInsertEndpoint.cs @@ -13,26 +13,24 @@ using Microsoft.AspNetCore.Razor.LanguageServer.Formatting; using Microsoft.AspNetCore.Razor.LanguageServer.Protocol; using Microsoft.CodeAnalysis.Razor.Workspaces; -using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; -[LanguageServerEndpoint(VSInternalMethods.OnAutoInsertName)] -internal class OnAutoInsertEndpoint : AbstractRazorDelegatingEndpoint, IRegistrationExtension +internal class OnAutoInsertEndpoint : AbstractRazorDelegatingEndpoint, IVSOnAutoInsertEndpoint { private static readonly HashSet s_htmlAllowedTriggerCharacters = new(StringComparer.Ordinal) { "=", }; private static readonly HashSet s_cSharpAllowedTriggerCharacters = new(StringComparer.Ordinal) { "'", "/", "\n" }; private readonly LanguageServerFeatureOptions _languageServerFeatureOptions; - private readonly IReadOnlyList _onAutoInsertProviders; + private readonly IReadOnlyList _onAutoInsertProviders; public OnAutoInsertEndpoint( LanguageServerFeatureOptions languageServerFeatureOptions, RazorDocumentMappingService documentMappingService, ClientNotifierServiceBase languageServer, - IEnumerable onAutoInsertProvider, + IEnumerable onAutoInsertProvider, ILoggerFactory loggerFactory) : base(languageServerFeatureOptions, documentMappingService, languageServer, loggerFactory.CreateLogger()) { @@ -61,7 +59,7 @@ public RegistrationExtensionResult GetRegistration(VSInternalClientCapabilities return new RegistrationExtensionResult(AssociatedServerCapability, registrationOptions); } - protected override async Task TryHandleAsync(VSInternalDocumentOnAutoInsertParams request, RazorRequestContext requestContext, Projection projection, CancellationToken cancellationToken) + protected override async Task TryHandleAsync(OnAutoInsertParamsBridge request, RazorRequestContext requestContext, Projection projection, CancellationToken cancellationToken) { var documentContext = requestContext.GetRequiredDocumentContext(); var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false); @@ -74,7 +72,7 @@ public RegistrationExtensionResult GetRegistration(VSInternalClientCapabilities var character = request.Character; - var applicableProviders = new List(); + var applicableProviders = new List(); for (var i = 0; i < _onAutoInsertProviders.Count; i++) { var formatOnTypeProvider = _onAutoInsertProviders[i]; @@ -87,7 +85,7 @@ public RegistrationExtensionResult GetRegistration(VSInternalClientCapabilities if (applicableProviders.Count == 0) { // There's currently a bug in the LSP platform where other language clients OnAutoInsert trigger characters influence every language clients trigger characters. - // To combat this we need to preemptively return so we don't try having our providers handle characters that they can't. + // To combat this we need to pre-emptively return so we don't try having our providers handle characters that they can't. return null; } @@ -114,7 +112,7 @@ public RegistrationExtensionResult GetRegistration(VSInternalClientCapabilities return null; } - protected override Task CreateDelegatedParamsAsync(VSInternalDocumentOnAutoInsertParams request, RazorRequestContext requestContext, Projection projection, CancellationToken cancellationToken) + protected override Task CreateDelegatedParamsAsync(OnAutoInsertParamsBridge request, RazorRequestContext requestContext, Projection projection, CancellationToken cancellationToken) { var documentContext = requestContext.GetRequiredDocumentContext(); if (projection.LanguageKind == RazorLanguageKind.Html && @@ -140,7 +138,7 @@ public RegistrationExtensionResult GetRegistration(VSInternalClientCapabilities protected override async Task HandleDelegatedResponseAsync( VSInternalDocumentOnAutoInsertResponseItem? delegatedResponse, - VSInternalDocumentOnAutoInsertParams originalRequest, + OnAutoInsertParamsBridge originalRequest, RazorRequestContext requestContext, Projection projection, CancellationToken cancellationToken) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/RazorOnAutoInsertProvider.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/RazorOnAutoInsertProvider.cs new file mode 100644 index 00000000000..45e6f28c27f --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/AutoInsert/RazorOnAutoInsertProvider.cs @@ -0,0 +1,29 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Razor.LanguageServer.Formatting; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; + +internal abstract class RazorOnAutoInsertProvider +{ + internal readonly ILogger Logger; + + public RazorOnAutoInsertProvider(ILoggerFactory loggerFactory) + { + if (loggerFactory is null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + Logger = loggerFactory.CreateLogger(); + } + + public abstract string TriggerCharacter { get; } + + public abstract bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit? edit, out InsertTextFormat format); +} diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/IVSOnAutoInsertEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/IVSOnAutoInsertEndpoint.cs new file mode 100644 index 00000000000..9e8126ba0be --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/IVSOnAutoInsertEndpoint.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts; +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; + +[LanguageServerEndpoint(VSInternalMethods.OnAutoInsertName)] +internal interface IVSOnAutoInsertEndpoint : IRazorRequestHandler, IRegistrationExtension +{ +} diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/OnAutoInsertParamsBridge.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/OnAutoInsertParamsBridge.cs new file mode 100644 index 00000000000..6dccc6b43e9 --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/EndpointContracts/OnAutoInsertParamsBridge.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts; + +internal class OnAutoInsertParamsBridge : VSInternalDocumentOnAutoInsertParams, ITextDocumentPositionParams +{ +} diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs index e5b5ec9cfd3..f100ea2d5df 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorLanguageServer.cs @@ -133,8 +133,8 @@ protected override ILspServices ConstructLspServices() services.AddTextDocumentServices(); // Auto insert - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); // Folding Range Providers services.AddSingleton(); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/AutoClosingTagOnAutoInsertProviderTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/AutoClosingTagOnAutoInsertProviderTest.cs index 33f4a492cdd..c1387916377 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/AutoClosingTagOnAutoInsertProviderTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/AutoClosingTagOnAutoInsertProviderTest.cs @@ -951,7 +951,7 @@ public void OnTypeCloseAngle_AutoInsertDisabled_Noops() "); } - internal override IOnAutoInsertProvider CreateProvider() + internal override RazorOnAutoInsertProvider CreateProvider() { var optionsMonitor = new Mock>(MockBehavior.Strict); optionsMonitor.SetupGet(o => o.CurrentValue).Returns(Options); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/CloseTextTagOnAutoInsertProviderTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/CloseTextTagOnAutoInsertProviderTest.cs index cf06002f135..19ab32cc446 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/CloseTextTagOnAutoInsertProviderTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/CloseTextTagOnAutoInsertProviderTest.cs @@ -43,7 +43,7 @@ public void OnTypeCloseAngle_OutsideRazorBlock_DoesNotCloseTextTag() "); } - internal override IOnAutoInsertProvider CreateProvider() + internal override RazorOnAutoInsertProvider CreateProvider() { var optionsMonitor = new Mock>(MockBehavior.Strict); optionsMonitor.SetupGet(o => o.CurrentValue).Returns(RazorLSPOptions.Default); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/OnAutoInsertEndpointTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/OnAutoInsertEndpointTest.cs index 055d05601a3..829c7ca8033 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/OnAutoInsertEndpointTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/OnAutoInsertEndpointTest.cs @@ -2,23 +2,23 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; +using Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; using Microsoft.AspNetCore.Razor.LanguageServer.Common.Extensions; using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts; using Microsoft.AspNetCore.Razor.LanguageServer.Extensions; -using Microsoft.AspNetCore.Razor.LanguageServer.Formatting; using Microsoft.AspNetCore.Razor.LanguageServer.Test; using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions; using Microsoft.CodeAnalysis.Testing; using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.LanguageServer.Protocol; using Moq; using Xunit; using Xunit.Abstractions; -namespace Microsoft.AspNetCore.Razor.LanguageServer.AutoInsert; +namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting; public class OnAutoInsertEndpointTest : SingleServerDelegatingEndpointTestBase { @@ -36,9 +36,9 @@ public async Task Handle_SingleProvider_InvokesProvider() var uri = new Uri(razorFilePath); await CreateLanguageServerAsync(codeDocument, razorFilePath); var documentContext = CreateDocumentContext(uri, codeDocument); - var insertProvider = new TestOnAutoInsertProvider(">", canResolve: true); + var insertProvider = new TestOnAutoInsertProvider(">", canResolve: true, LoggerFactory); var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, new[] { insertProvider }, LoggerFactory); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = uri, }, Position = new Position(0, 0), @@ -69,16 +69,16 @@ public async Task Handle_MultipleProviderSameTrigger_UsesSuccessful() var uri = new Uri(razorFilePath); await CreateLanguageServerAsync(codeDocument, razorFilePath); var documentContext = CreateDocumentContext(uri, codeDocument); - var insertProvider1 = new TestOnAutoInsertProvider(">", canResolve: false) + var insertProvider1 = new TestOnAutoInsertProvider(">", canResolve: false, LoggerFactory) { ResolvedTextEdit = new TextEdit() }; - var insertProvider2 = new TestOnAutoInsertProvider(">", canResolve: true) + var insertProvider2 = new TestOnAutoInsertProvider(">", canResolve: true, LoggerFactory) { ResolvedTextEdit = new TextEdit() }; var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, new[] { insertProvider1, insertProvider2 }, LoggerFactory); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = uri, }, Position = new Position(0, 0), @@ -112,16 +112,16 @@ public async Task Handle_MultipleProviderSameTrigger_UsesFirstSuccessful() var uri = new Uri(razorFilePath); await CreateLanguageServerAsync(codeDocument, razorFilePath); var documentContext = CreateDocumentContext(uri, codeDocument); - var insertProvider1 = new TestOnAutoInsertProvider(">", canResolve: true) + var insertProvider1 = new TestOnAutoInsertProvider(">", canResolve: true, LoggerFactory) { ResolvedTextEdit = new TextEdit() }; - var insertProvider2 = new TestOnAutoInsertProvider(">", canResolve: true) + var insertProvider2 = new TestOnAutoInsertProvider(">", canResolve: true, LoggerFactory) { ResolvedTextEdit = new TextEdit() }; var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, new[] { insertProvider1, insertProvider2 }, LoggerFactory); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = uri, }, Position = new Position(0, 0), @@ -154,10 +154,10 @@ public async Task Handle_MultipleProviderUnmatchingTrigger_ReturnsNull() var uri = new Uri(razorFilePath); await CreateLanguageServerAsync(codeDocument, razorFilePath); var documentContext = CreateDocumentContext(uri, codeDocument); - var insertProvider1 = new TestOnAutoInsertProvider(">", canResolve: true); - var insertProvider2 = new TestOnAutoInsertProvider("<", canResolve: true); + var insertProvider1 = new TestOnAutoInsertProvider(">", canResolve: true, LoggerFactory); + var insertProvider2 = new TestOnAutoInsertProvider("<", canResolve: true, LoggerFactory); var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, new[] { insertProvider1, insertProvider2 }, LoggerFactory); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = uri, }, Position = new Position(0, 0), @@ -188,10 +188,10 @@ public async Task Handle_DocumentNotFound_ReturnsNull() var razorFilePath = "file://path/test.razor"; await CreateLanguageServerAsync(codeDocument, razorFilePath); - var insertProvider = new TestOnAutoInsertProvider(">", canResolve: true); + var insertProvider = new TestOnAutoInsertProvider(">", canResolve: true, LoggerFactory); var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, new[] { insertProvider }, LoggerFactory); var uri = new Uri("file://path/test.razor"); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = uri, }, Position = new Position(0, 0), @@ -223,9 +223,9 @@ public async Task Handle_UnsupportedCodeDocument_ReturnsNull() var uri = new Uri(razorFilePath); await CreateLanguageServerAsync(codeDocument, razorFilePath); var documentContext = CreateDocumentContext(uri, codeDocument); - var insertProvider = new TestOnAutoInsertProvider(">", canResolve: true); + var insertProvider = new TestOnAutoInsertProvider(">", canResolve: true, LoggerFactory); var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, new[] { insertProvider }, LoggerFactory); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = uri, }, Position = new Position(0, 0), @@ -256,9 +256,9 @@ public async Task Handle_NoApplicableProvider_CallsProviderAndReturnsNull() var uri = new Uri(razorFilePath); await CreateLanguageServerAsync(codeDocument, razorFilePath); var documentContext = CreateDocumentContext(uri, codeDocument); - var insertProvider = new TestOnAutoInsertProvider(">", canResolve: false); + var insertProvider = new TestOnAutoInsertProvider(">", canResolve: false, LoggerFactory); var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, new[] { insertProvider }, LoggerFactory); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = uri, }, Position = new Position(0, 0), @@ -409,12 +409,12 @@ private async Task VerifyCSharpOnAutoInsertAsync(string input, string expected, var razorFilePath = "file://path/test.razor"; await CreateLanguageServerAsync(codeDocument, razorFilePath); - var insertProvider = new TestOnAutoInsertProvider("!!!", canResolve: false); + var insertProvider = new TestOnAutoInsertProvider("!!!", canResolve: false, LoggerFactory); var providers = new[] { insertProvider }; var endpoint = new OnAutoInsertEndpoint(LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, providers, LoggerFactory); codeDocument.GetSourceText().GetLineAndOffset(cursorPosition, out var line, out var offset); - var @params = new VSInternalDocumentOnAutoInsertParams() + var @params = new OnAutoInsertParamsBridge() { TextDocument = new TextDocumentIdentifier { Uri = new Uri(razorFilePath), }, Position = new Position(line, offset), @@ -442,11 +442,12 @@ private async Task VerifyCSharpOnAutoInsertAsync(string input, string expected, Assert.Equal(expected, newText); } - private class TestOnAutoInsertProvider : IOnAutoInsertProvider + private class TestOnAutoInsertProvider : RazorOnAutoInsertProvider { private readonly bool _canResolve; - public TestOnAutoInsertProvider(string triggerCharacter, bool canResolve) + public TestOnAutoInsertProvider(string triggerCharacter, bool canResolve, ILoggerFactory loggerFactory) + : base(loggerFactory) { TriggerCharacter = triggerCharacter; _canResolve = canResolve; @@ -456,10 +457,12 @@ public TestOnAutoInsertProvider(string triggerCharacter, bool canResolve) public TextEdit? ResolvedTextEdit { get; set; } - public string TriggerCharacter { get; } + public override string TriggerCharacter { get; } // Disabling because [NotNullWhen] is available in two Assemblies and causes warnings - public bool TryResolveInsertion(Position position, FormattingContext context, [NotNullWhen(true)] out TextEdit? edit, out InsertTextFormat format) +#pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes). + public override bool TryResolveInsertion(Position position, FormattingContext context, out TextEdit? edit, out InsertTextFormat format) +#pragma warning restore CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes). { Called = true; edit = ResolvedTextEdit!; diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/RazorOnAutoInsertProviderTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/RazorOnAutoInsertProviderTestBase.cs index 06f5082a399..e9b17a8a114 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/RazorOnAutoInsertProviderTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/AutoInsert/RazorOnAutoInsertProviderTestBase.cs @@ -28,7 +28,7 @@ protected RazorOnAutoInsertProviderTestBase(ITestOutputHelper testOutput) { } - internal abstract IOnAutoInsertProvider CreateProvider(); + internal abstract RazorOnAutoInsertProvider CreateProvider(); protected void RunAutoInsertTest(string input, string expected, int tabSize = 4, bool insertSpaces = true, string fileKind = default, IReadOnlyList tagHelpers = default) { diff --git a/src/Shared/Directory.Build.props b/src/Shared/Directory.Build.props deleted file mode 100644 index 4f2abfaac0c..00000000000 --- a/src/Shared/Directory.Build.props +++ /dev/null @@ -1,15 +0,0 @@ - - - - - aspnetcore;cshtml;razor - true - true - true - true - - - true - - - diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Microsoft.AspNetCore.Razor.Utilities.Shared.csproj b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Microsoft.AspNetCore.Razor.Utilities.Shared.csproj index ef169625356..c844f57a1f2 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Microsoft.AspNetCore.Razor.Utilities.Shared.csproj +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/Microsoft.AspNetCore.Razor.Utilities.Shared.csproj @@ -4,7 +4,6 @@ netstandard2.0;$(DefaultNetCoreTargetFramework);$(DefaultNetFxTargetFramework) Razor is a markup syntax for adding server-side logic to web pages. This package contains the language server library assets. false - false false Microsoft.AspNetCore.Razor