From 9a307d7c52ef6dc1ce66f5f8cd63e12537484c60 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 29 Apr 2022 15:22:30 -0700 Subject: [PATCH 1/5] Explicitly pass formatting options to Roslyn APIs (via External Access) --- .../Formatting/CSharpFormatter.cs | 11 ++++---- .../Formatting/CSharpOnTypeFormattingPass.cs | 15 +++++------ .../Formatting/FormattingOptionsExtensions.cs | 17 +++++++++++++ .../FormattingLanguageServerClient.cs | 25 ++++++++----------- .../Formatting/FormattingTestBase.cs | 8 ++++-- 5 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs index 33d7cbe4521..8a6140a8815 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions; using Microsoft.CodeAnalysis.Text; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -135,15 +136,14 @@ private static async Task FormatOnServerAsync( Range projectedRange, CancellationToken cancellationToken) { + var csharpDocument = context.CSharpWorkspaceDocument; var csharpSourceText = context.CodeDocument.GetCSharpSourceText(); var spanToFormat = projectedRange.AsTextSpan(csharpSourceText); var root = await context.CSharpWorkspaceDocument.GetSyntaxRootAsync(cancellationToken); Assumes.NotNull(root); - var workspace = context.CSharpWorkspace; - - // Formatting options will already be set in the workspace. - var changes = CodeAnalysis.Formatting.Formatter.GetFormattedTextChanges(root, spanToFormat, workspace, cancellationToken: cancellationToken); + var services = csharpDocument.Project.Solution.Workspace.Services; + var changes = RazorCSharpFormattingInteractionService.GetFormattedTextChanges(services, root, spanToFormat, context.Options.GetIndentationOptions(), cancellationToken); var edits = changes.Select(c => c.AsTextEdit(csharpSourceText)).ToArray(); return edits; @@ -165,7 +165,8 @@ private static async Task> GetCSharpIndentationCoreAsync(Fo // At this point, we have added all the necessary markers and attached annotations. // Let's invoke the C# formatter and hope for the best. - var formattedRoot = CodeAnalysis.Formatting.Formatter.Format(root, context.CSharpWorkspace, cancellationToken: cancellationToken); + var services = context.CSharpWorkspaceDocument.Project.Solution.Workspace.Services; + var formattedRoot = RazorCSharpFormattingInteractionService.Format(services, root, context.Options.GetIndentationOptions(), cancellationToken); var formattedText = formattedRoot.GetText(); var desiredIndentationMap = new Dictionary(); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs index a194a00b571..30e8f4c14b8 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs @@ -27,11 +27,13 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting internal class CSharpOnTypeFormattingPass : CSharpFormattingPassBase { private readonly ILogger _logger; + private readonly RazorGlobalOptions _globalOptions; public CSharpOnTypeFormattingPass( RazorDocumentMappingService documentMappingService, FilePathNormalizer filePathNormalizer, ClientNotifierServiceBase server, + RazorGlobalOptions globalOptions, ILoggerFactory loggerFactory) : base(documentMappingService, filePathNormalizer, server) { @@ -41,6 +43,7 @@ public CSharpOnTypeFormattingPass( } _logger = loggerFactory.CreateLogger(); + _globalOptions = globalOptions; } public async override Task ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken) @@ -65,19 +68,13 @@ public async override Task ExecuteAsync(FormattingContext cont } // Ask C# for formatting changes. - var indentationOptions = new RazorIndentationOptions( - UseTabs: !context.Options.InsertSpaces, - TabSize: context.Options.TabSize, - IndentationSize: context.Options.TabSize); - var autoFormattingOptions = new RazorAutoFormattingOptions( - formatOnReturn: true, formatOnTyping: true, formatOnSemicolon: true, formatOnCloseBrace: true); - + var formattingChanges = await RazorCSharpFormattingInteractionService.GetFormattingChangesAsync( context.CSharpWorkspaceDocument, typedChar: context.TriggerCharacter, projectedIndex, - indentationOptions, - autoFormattingOptions, + context.Options.GetIndentationOptions(), + _globalOptions.GetAutoFormattingOptions(), indentStyle: CodeAnalysis.Formatting.FormattingOptions.IndentStyle.Smart, cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs new file mode 100644 index 00000000000..4c05cebc667 --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting +{ + internal static class FormattingOptionsExtensions + { + public static RazorIndentationOptions GetIndentationOptions(this FormattingOptions options) + => new( + UseTabs: !options.InsertSpaces, + TabSize: options.TabSize, + IndentationSize: options.TabSize); + } +} diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingLanguageServerClient.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingLanguageServerClient.cs index 66149ac3404..3a8fba26d17 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingLanguageServerClient.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingLanguageServerClient.cs @@ -17,7 +17,7 @@ using Microsoft.AspNetCore.Razor.LanguageServer.Protocol; using Microsoft.AspNetCore.Razor.LanguageServer.Test.Common; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; @@ -36,7 +36,6 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Extensions.LanguageServer.Protocol.Server.WorkDone; using Xunit; -using FormattingOptions = OmniSharp.Extensions.LanguageServer.Protocol.Models.FormattingOptions; namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting { @@ -71,7 +70,7 @@ public void AddCodeDocument(RazorCodeDocument codeDocument) _documents.TryAdd("/" + path, codeDocument); } - private RazorDocumentFormattingResponse Format(DocumentOnTypeFormattingParams @params) + private RazorDocumentFormattingResponse Format() { var response = new RazorDocumentFormattingResponse(); @@ -150,14 +149,13 @@ private RazorDocumentFormattingResponse Format(RazorDocumentRangeFormattingParam throw new InvalidOperationException("We shouldn't be asked to format Razor language kind."); } - var options = @params.Options; var response = new RazorDocumentFormattingResponse(); if (@params.Kind == RazorLanguageKind.CSharp) { var codeDocument = _documents[@params.HostDocumentFilePath]; var csharpSourceText = codeDocument.GetCSharpSourceText(); - var csharpDocument = GetCSharpDocument(codeDocument, @params.Options); + var csharpDocument = GetCSharpDocument(codeDocument); if (!csharpDocument.TryGetSyntaxRoot(out var root)) { throw new InvalidOperationException("Couldn't get syntax root."); @@ -165,7 +163,10 @@ private RazorDocumentFormattingResponse Format(RazorDocumentRangeFormattingParam var spanToFormat = @params.ProjectedRange.AsTextSpan(csharpSourceText); - var changes = Formatter.GetFormattedTextChanges(root, spanToFormat, csharpDocument.Project.Solution.Workspace); + var services = csharpDocument.Project.Solution.Workspace.Services; + var options = @params.Options.GetIndentationOptions(); + var changes = RazorCSharpFormattingInteractionService.GetFormattedTextChanges( + services, root, spanToFormat, options, CancellationToken.None); response.Edits = changes.Select(c => c.AsTextEdit(csharpSourceText)).ToArray(); } @@ -204,15 +205,9 @@ public TextEdit AsTextEdit(SourceText sourceText) } } - private static Document GetCSharpDocument(RazorCodeDocument codeDocument, FormattingOptions options) + private static Document GetCSharpDocument(RazorCodeDocument codeDocument) { var adhocWorkspace = new AdhocWorkspace(); - var csharpOptions = adhocWorkspace.Options - .WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.TabSize, LanguageNames.CSharp, (int)options.TabSize) - .WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.IndentationSize, LanguageNames.CSharp, (int)options.TabSize) - .WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.UseTabs, LanguageNames.CSharp, !options.InsertSpaces); - adhocWorkspace.TryApplyChanges(adhocWorkspace.CurrentSolution.WithOptions(csharpOptions)); - var project = adhocWorkspace.AddProject("TestProject", LanguageNames.CSharp); var csharpSourceText = codeDocument.GetCSharpSourceText(); var csharpDocument = adhocWorkspace.AddDocument(project.Id, "TestDocument", csharpSourceText); @@ -265,10 +260,10 @@ public override Task SendRequestAsync(string method, return Task.FromResult(Convert(response)); } - else if (@params is DocumentOnTypeFormattingParams onTypeFormattingParams && + else if (@params is DocumentOnTypeFormattingParams && string.Equals(method, "textDocument/onTypeFormatting", StringComparison.Ordinal)) { - var response = Format(onTypeFormattingParams); + var response = Format(); return Task.FromResult(Convert(response)); } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingTestBase.cs index 0df397d1dcf..55a534cf84a 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/FormattingTestBase.cs @@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Razor.LanguageServer.Test; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Razor.Serialization; using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions; @@ -221,19 +222,22 @@ private RazorFormattingService CreateFormattingService(RazorCodeDocument codeDoc var dispatcher = new LSPProjectSnapshotManagerDispatcher(LoggerFactory); var versionCache = new DefaultDocumentVersionCache(dispatcher); + var workspaceFactory = TestAdhocWorkspaceFactory.Instance; + var globalOptions = RazorGlobalOptions.GetGlobalOptions(workspaceFactory.Create()); + var client = new FormattingLanguageServerClient(); client.AddCodeDocument(codeDocument); var passes = new List() { new HtmlFormattingPass(mappingService, FilePathNormalizer, client, versionCache, LoggerFactory), new CSharpFormattingPass(mappingService, FilePathNormalizer, client, LoggerFactory), - new CSharpOnTypeFormattingPass(mappingService, FilePathNormalizer, client, LoggerFactory), + new CSharpOnTypeFormattingPass(mappingService, FilePathNormalizer, client, globalOptions, LoggerFactory), new RazorFormattingPass(mappingService, FilePathNormalizer, client, LoggerFactory), new FormattingDiagnosticValidationPass(mappingService, FilePathNormalizer, client, LoggerFactory), new FormattingContentValidationPass(mappingService, FilePathNormalizer, client, LoggerFactory), }; - return new DefaultRazorFormattingService(passes, LoggerFactory, TestAdhocWorkspaceFactory.Instance); + return new DefaultRazorFormattingService(passes, LoggerFactory, workspaceFactory); } private static SourceText ApplyEdits(SourceText source, TextEdit[] edits) From 50a650ad5b95a076bc39cd3397bdc380b373a0a0 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Sun, 7 Jul 2024 22:24:19 +1000 Subject: [PATCH 2/5] Updates after merge --- .../Formatting/CSharpFormatter.cs | 45 +++++++++---------- .../Formatting/CSharpOnTypeFormattingPass.cs | 11 +++-- .../Formatting/FormattingOptionsExtensions.cs | 17 ++++--- .../Formatting/TestRazorFormattingService.cs | 42 ----------------- 4 files changed, 37 insertions(+), 78 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs index f9761105bc2..0b50a43683e 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs @@ -7,15 +7,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.LanguageServer.Hosting; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Razor.DocumentMapping; -using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.CodeAnalysis.Razor; -using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions; +using Microsoft.CodeAnalysis.Razor.DocumentMapping; +using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -79,22 +77,23 @@ private TextEdit[] MapEditsToHostDocument(RazorCodeDocument codeDocument, TextEd return actualEdits; } - private static async Task FormatOnServerAsync( - FormattingContext context, - Range projectedRange, - CancellationToken cancellationToken) - { - var csharpSourceText = context.CodeDocument.GetCSharpSourceText(); - var spanToFormat = projectedRange.AsTextSpan(csharpSourceText); - var root = await context.CSharpWorkspaceDocument.GetSyntaxRootAsync(cancellationToken); - Assumes.NotNull(root); + private static async Task GetFormattingEditsAsync( + FormattingContext context, + Range projectedRange, + CancellationToken cancellationToken) + { + var csharpSourceText = context.CodeDocument.GetCSharpSourceText(); + var spanToFormat = projectedRange.AsTextSpan(csharpSourceText); + var csharpDocument = context.CSharpWorkspaceDocument; + var root = await csharpDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + Assumes.NotNull(root); - var services = csharpDocument.Project.Solution.Workspace.Services; - var changes = RazorCSharpFormattingInteractionService.GetFormattedTextChanges(services, root, spanToFormat, context.Options.GetIndentationOptions(), cancellationToken); + var services = csharpDocument.Project.Solution.Workspace.Services; + var changes = RazorCSharpFormattingInteractionService.GetFormattedTextChanges(services, root, spanToFormat, context.Options.GetIndentationOptions(), cancellationToken); - var edits = changes.Select(c => c.AsTextEdit(csharpSourceText)).ToArray(); - return edits; - } + var edits = changes.Select(c => c.ToTextEdit(csharpSourceText)).ToArray(); + return edits; + } private static async Task> GetCSharpIndentationCoreAsync(FormattingContext context, List projectedDocumentLocations, CancellationToken cancellationToken) { @@ -110,11 +109,11 @@ private static async Task> GetCSharpIndentationCoreAsync(Fo root = AttachAnnotations(indentationMap, projectedDocumentLocations, root); - // At this point, we have added all the necessary markers and attached annotations. - // Let's invoke the C# formatter and hope for the best. - var services = context.CSharpWorkspaceDocument.Project.Solution.Workspace.Services; - var formattedRoot = RazorCSharpFormattingInteractionService.Format(services, root, context.Options.GetIndentationOptions(), cancellationToken); - var formattedText = formattedRoot.GetText(); + // At this point, we have added all the necessary markers and attached annotations. + // Let's invoke the C# formatter and hope for the best. + var services = context.CSharpWorkspaceDocument.Project.Solution.Workspace.Services; + var formattedRoot = RazorCSharpFormattingInteractionService.Format(services, root, context.Options.GetIndentationOptions(), cancellationToken); + var formattedText = formattedRoot.GetText(); var desiredIndentationMap = new Dictionary(); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs index e37b594559a..b6f250950d7 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs @@ -31,6 +31,7 @@ internal sealed class CSharpOnTypeFormattingPass( : CSharpFormattingPassBase(documentMappingService) { private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); + public async override Task ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken) { if (!context.IsFormatOnType || result.Kind != RazorLanguageKind.CSharp) @@ -52,14 +53,16 @@ public async override Task ExecuteAsync(FormattingContext cont return result; } - // Ask C# for formatting changes. - - var formattingChanges = await RazorCSharpFormattingInteractionService.GetFormattingChangesAsync( + // Ask C# for formatting changes. + var autoFormattingOptions = new RazorAutoFormattingOptions( + formatOnReturn: true, formatOnTyping: true, formatOnSemicolon: true, formatOnCloseBrace: true); + + var formattingChanges = await RazorCSharpFormattingInteractionService.GetFormattingChangesAsync( context.CSharpWorkspaceDocument, typedChar: context.TriggerCharacter, projectedIndex, context.Options.GetIndentationOptions(), - _globalOptions.GetAutoFormattingOptions(), + autoFormattingOptions, indentStyle: CodeAnalysis.Formatting.FormattingOptions.IndentStyle.Smart, cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs index 3bd1a7df6cc..5271e0f9ec1 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingOptionsExtensions.cs @@ -4,14 +4,13 @@ using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.VisualStudio.LanguageServer.Protocol; -namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting +namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting; + +internal static class FormattingOptionsExtensions { - internal static class FormattingOptionsExtensions - { - public static RazorIndentationOptions GetIndentationOptions(this FormattingOptions options) - => new( - UseTabs: !options.InsertSpaces, - TabSize: options.TabSize, - IndentationSize: options.TabSize); - } + public static RazorIndentationOptions GetIndentationOptions(this FormattingOptions options) + => new( + UseTabs: !options.InsertSpaces, + TabSize: options.TabSize, + IndentationSize: options.TabSize); } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/TestRazorFormattingService.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/TestRazorFormattingService.cs index ec492dfd36e..648e368f8a8 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/TestRazorFormattingService.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting/TestRazorFormattingService.cs @@ -5,9 +5,6 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.LanguageServer.Test; -using Microsoft.AspNetCore.Razor.LanguageServer.Test.Common; -using Microsoft.CodeAnalysis.ExternalAccess.Razor; using Microsoft.AspNetCore.Razor.LanguageServer.Hosting; using Microsoft.CodeAnalysis.Razor.Logging; using Microsoft.CodeAnalysis.Razor.ProjectSystem; @@ -19,45 +16,6 @@ internal static class TestRazorFormattingService { #pragma warning disable IDE0060 // Remove unused parameter - public static async Task CreateWithFullSupportAsync( - RazorCodeDocument? codeDocument = null, - DocumentSnapshot? documentSnapshot = null, - ILoggerFactory? loggerFactory = null) - { - codeDocument ??= TestRazorCodeDocument.CreateEmpty(); - loggerFactory ??= NullLoggerFactory.Instance; - - var mappingService = new DefaultRazorDocumentMappingService(TestLanguageServerFeatureOptions.Instance, new TestDocumentContextFactory(), loggerFactory); - - var dispatcher = new LSPProjectSnapshotManagerDispatcher(loggerFactory); - var versionCache = new DefaultDocumentVersionCache(dispatcher); - - var workspaceFactory = TestAdhocWorkspaceFactory.Instance; - var globalOptions = RazorGlobalOptions.GetGlobalOptions(workspaceFactory.Create()); - - if (documentSnapshot is not null) - { - await dispatcher.RunOnDispatcherThreadAsync(() => - { - versionCache.TrackDocumentVersion(documentSnapshot, version: 1); - }, CancellationToken.None); - } - - var client = new FormattingLanguageServerClient(); - client.AddCodeDocument(codeDocument); - - var passes = new List() - { - new HtmlFormattingPass(mappingService, client, versionCache, loggerFactory), - new CSharpFormattingPass(mappingService, client, loggerFactory), - new CSharpOnTypeFormattingPass(mappingService, client, globalOptions, loggerFactory), - new RazorFormattingPass(mappingService, client, loggerFactory), - new FormattingDiagnosticValidationPass(mappingService, client, loggerFactory), - new FormattingContentValidationPass(mappingService, client, loggerFactory), - }; - - return new DefaultRazorFormattingService(passes, loggerFactory, TestAdhocWorkspaceFactory.Instance); - } public static Task CreateWithFullSupportAsync( ILoggerFactory loggerFactory, RazorCodeDocument? codeDocument = null, From e0e78bb712af3d87fa3e5b67d97b6856d43de4f4 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 8 Jul 2024 08:20:23 +1000 Subject: [PATCH 3/5] Actually fix the bug by not calling TryApplyChanges on the workspace --- .../Formatting/CSharpFormatter.cs | 2 +- .../Formatting/FormattingContext.cs | 23 +------------------ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs index 0b50a43683e..7f3f4d8680b 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs @@ -111,7 +111,7 @@ private static async Task> GetCSharpIndentationCoreAsync(Fo // At this point, we have added all the necessary markers and attached annotations. // Let's invoke the C# formatter and hope for the best. - var services = context.CSharpWorkspaceDocument.Project.Solution.Workspace.Services; + var services = context.CSharpWorkspace.Services; var formattedRoot = RazorCSharpFormattingInteractionService.Format(services, root, context.Options.GetIndentationOptions(), cancellationToken); var formattedText = formattedRoot.GetText(); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs index de4352ddd17..a87dab72d9e 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs @@ -73,28 +73,7 @@ public Document CSharpWorkspaceDocument } } - public AdhocWorkspace CSharpWorkspace - { - get - { - if (_csharpWorkspace is null) - { - var adhocWorkspace = _workspaceFactory.Create(); - var csharpOptions = GetChangedOptionSet(adhocWorkspace.Options); - adhocWorkspace.TryApplyChanges(adhocWorkspace.CurrentSolution.WithOptions(csharpOptions)); - _csharpWorkspace = adhocWorkspace; - } - - return _csharpWorkspace; - } - } - - public CodeAnalysis.Options.OptionSet GetChangedOptionSet(CodeAnalysis.Options.OptionSet optionsSet) - { - return optionsSet.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.TabSize, LanguageNames.CSharp, Options.TabSize) - .WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.IndentationSize, LanguageNames.CSharp, Options.TabSize) - .WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.UseTabs, LanguageNames.CSharp, !Options.InsertSpaces); - } + public AdhocWorkspace CSharpWorkspace => _csharpWorkspace ??= _workspaceFactory.Create(); /// A Dictionary of int (line number) to IndentationContext. /// From dbacc56edaf74070b574b35e87198ec64d9bfc2e Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 8 Jul 2024 08:30:55 +1000 Subject: [PATCH 4/5] Whitespace --- .../Formatting/CSharpFormatter.cs | 14 ++++---------- .../Formatting/CSharpOnTypeFormattingPass.cs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs index 7f3f4d8680b..edb09ef5e9d 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs @@ -77,19 +77,14 @@ private TextEdit[] MapEditsToHostDocument(RazorCodeDocument codeDocument, TextEd return actualEdits; } - private static async Task GetFormattingEditsAsync( - FormattingContext context, - Range projectedRange, - CancellationToken cancellationToken) + private static async Task GetFormattingEditsAsync(FormattingContext context, Range projectedRange, CancellationToken cancellationToken) { var csharpSourceText = context.CodeDocument.GetCSharpSourceText(); var spanToFormat = projectedRange.AsTextSpan(csharpSourceText); - var csharpDocument = context.CSharpWorkspaceDocument; - var root = await csharpDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var root = await context.CSharpWorkspaceDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); Assumes.NotNull(root); - var services = csharpDocument.Project.Solution.Workspace.Services; - var changes = RazorCSharpFormattingInteractionService.GetFormattedTextChanges(services, root, spanToFormat, context.Options.GetIndentationOptions(), cancellationToken); + var changes = RazorCSharpFormattingInteractionService.GetFormattedTextChanges(context.CSharpWorkspace.Services, root, spanToFormat, context.Options.GetIndentationOptions(), cancellationToken); var edits = changes.Select(c => c.ToTextEdit(csharpSourceText)).ToArray(); return edits; @@ -111,8 +106,7 @@ private static async Task> GetCSharpIndentationCoreAsync(Fo // At this point, we have added all the necessary markers and attached annotations. // Let's invoke the C# formatter and hope for the best. - var services = context.CSharpWorkspace.Services; - var formattedRoot = RazorCSharpFormattingInteractionService.Format(services, root, context.Options.GetIndentationOptions(), cancellationToken); + var formattedRoot = RazorCSharpFormattingInteractionService.Format(context.CSharpWorkspace.Services, root, context.Options.GetIndentationOptions(), cancellationToken); var formattedText = formattedRoot.GetText(); var desiredIndentationMap = new Dictionary(); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs index b6f250950d7..f900e4ae41b 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpOnTypeFormattingPass.cs @@ -55,16 +55,16 @@ public async override Task ExecuteAsync(FormattingContext cont // Ask C# for formatting changes. var autoFormattingOptions = new RazorAutoFormattingOptions( - formatOnReturn: true, formatOnTyping: true, formatOnSemicolon: true, formatOnCloseBrace: true); + formatOnReturn: true, formatOnTyping: true, formatOnSemicolon: true, formatOnCloseBrace: true); var formattingChanges = await RazorCSharpFormattingInteractionService.GetFormattingChangesAsync( - context.CSharpWorkspaceDocument, - typedChar: context.TriggerCharacter, - projectedIndex, - context.Options.GetIndentationOptions(), - autoFormattingOptions, - indentStyle: CodeAnalysis.Formatting.FormattingOptions.IndentStyle.Smart, - cancellationToken).ConfigureAwait(false); + context.CSharpWorkspaceDocument, + typedChar: context.TriggerCharacter, + projectedIndex, + context.Options.GetIndentationOptions(), + autoFormattingOptions, + indentStyle: CodeAnalysis.Formatting.FormattingOptions.IndentStyle.Smart, + cancellationToken).ConfigureAwait(false); if (formattingChanges.IsEmpty) { From c5de6625c4ae345759ffd6695efc329b60c15579 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 8 Jul 2024 08:33:01 +1000 Subject: [PATCH 5/5] Make minimal --- .../Formatting/CSharpFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs index edb09ef5e9d..924e0c5df57 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/CSharpFormatter.cs @@ -80,7 +80,7 @@ private TextEdit[] MapEditsToHostDocument(RazorCodeDocument codeDocument, TextEd private static async Task GetFormattingEditsAsync(FormattingContext context, Range projectedRange, CancellationToken cancellationToken) { var csharpSourceText = context.CodeDocument.GetCSharpSourceText(); - var spanToFormat = projectedRange.AsTextSpan(csharpSourceText); + var spanToFormat = projectedRange.ToTextSpan(csharpSourceText); var root = await context.CSharpWorkspaceDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); Assumes.NotNull(root);