Separate html formatting passes, so we can remove the IsFormatOnType flag

This commit is contained in:
David Wengier 2024-08-22 15:12:12 +10:00
Родитель 6f1c6ef398
Коммит f74150db38
8 изменённых файлов: 89 добавлений и 121 удалений

Просмотреть файл

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information. // Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -27,16 +26,6 @@ internal sealed class CSharpFormatter(IDocumentMappingService documentMappingSer
public async Task<TextEdit[]> FormatAsync(FormattingContext context, Range rangeToFormat, CancellationToken cancellationToken) public async Task<TextEdit[]> FormatAsync(FormattingContext context, Range rangeToFormat, CancellationToken cancellationToken)
{ {
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (rangeToFormat is null)
{
throw new ArgumentNullException(nameof(rangeToFormat));
}
if (!_documentMappingService.TryMapToGeneratedDocumentRange(context.CodeDocument.GetCSharpDocument(), rangeToFormat, out var projectedRange)) if (!_documentMappingService.TryMapToGeneratedDocumentRange(context.CodeDocument.GetCSharpDocument(), rangeToFormat, out var projectedRange))
{ {
return []; return [];
@ -52,16 +41,6 @@ internal sealed class CSharpFormatter(IDocumentMappingService documentMappingSer
IReadOnlyCollection<int> projectedDocumentLocations, IReadOnlyCollection<int> projectedDocumentLocations,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (projectedDocumentLocations is null)
{
throw new ArgumentNullException(nameof(projectedDocumentLocations));
}
// Sorting ensures we count the marker offsets correctly. // Sorting ensures we count the marker offsets correctly.
// We also want to ensure there are no duplicates to avoid duplicate markers. // We also want to ensure there are no duplicates to avoid duplicate markers.
var filteredLocations = projectedDocumentLocations.Distinct().OrderBy(l => l).ToList(); var filteredLocations = projectedDocumentLocations.Distinct().OrderBy(l => l).ToList();

Просмотреть файл

@ -29,14 +29,13 @@ internal sealed class FormattingContext : IDisposable
private IReadOnlyDictionary<int, IndentationContext>? _indentations; private IReadOnlyDictionary<int, IndentationContext>? _indentations;
private FormattingContext(IAdhocWorkspaceFactory workspaceFactory, Uri uri, IDocumentSnapshot originalSnapshot, RazorCodeDocument codeDocument, RazorFormattingOptions options, private FormattingContext(IAdhocWorkspaceFactory workspaceFactory, Uri uri, IDocumentSnapshot originalSnapshot, RazorCodeDocument codeDocument, RazorFormattingOptions options,
bool isFormatOnType, bool automaticallyAddUsings, int hostDocumentIndex, char triggerCharacter) bool automaticallyAddUsings, int hostDocumentIndex, char triggerCharacter)
{ {
_workspaceFactory = workspaceFactory; _workspaceFactory = workspaceFactory;
Uri = uri; Uri = uri;
OriginalSnapshot = originalSnapshot; OriginalSnapshot = originalSnapshot;
CodeDocument = codeDocument; CodeDocument = codeDocument;
Options = options; Options = options;
IsFormatOnType = isFormatOnType;
AutomaticallyAddUsings = automaticallyAddUsings; AutomaticallyAddUsings = automaticallyAddUsings;
HostDocumentIndex = hostDocumentIndex; HostDocumentIndex = hostDocumentIndex;
TriggerCharacter = triggerCharacter; TriggerCharacter = triggerCharacter;
@ -48,7 +47,6 @@ internal sealed class FormattingContext : IDisposable
public IDocumentSnapshot OriginalSnapshot { get; } public IDocumentSnapshot OriginalSnapshot { get; }
public RazorCodeDocument CodeDocument { get; } public RazorCodeDocument CodeDocument { get; }
public RazorFormattingOptions Options { get; } public RazorFormattingOptions Options { get; }
public bool IsFormatOnType { get; }
public bool AutomaticallyAddUsings { get; } public bool AutomaticallyAddUsings { get; }
public int HostDocumentIndex { get; } public int HostDocumentIndex { get; }
public char TriggerCharacter { get; } public char TriggerCharacter { get; }
@ -267,7 +265,6 @@ internal sealed class FormattingContext : IDisposable
OriginalSnapshot, OriginalSnapshot,
codeDocument, codeDocument,
Options, Options,
IsFormatOnType,
AutomaticallyAddUsings, AutomaticallyAddUsings,
HostDocumentIndex, HostDocumentIndex,
TriggerCharacter); TriggerCharacter);
@ -303,7 +300,16 @@ internal sealed class FormattingContext : IDisposable
int hostDocumentIndex, int hostDocumentIndex,
char triggerCharacter) char triggerCharacter)
{ {
return CreateCore(uri, originalSnapshot, codeDocument, options, workspaceFactory, isFormatOnType: true, automaticallyAddUsings, hostDocumentIndex, triggerCharacter); return new FormattingContext(
workspaceFactory,
uri,
originalSnapshot,
codeDocument,
options,
automaticallyAddUsings,
hostDocumentIndex,
triggerCharacter
);
} }
public static FormattingContext Create( public static FormattingContext Create(
@ -313,33 +319,15 @@ internal sealed class FormattingContext : IDisposable
RazorFormattingOptions options, RazorFormattingOptions options,
IAdhocWorkspaceFactory workspaceFactory) IAdhocWorkspaceFactory workspaceFactory)
{ {
return CreateCore(uri, originalSnapshot, codeDocument, options, workspaceFactory, isFormatOnType: false, automaticallyAddUsings: false, hostDocumentIndex: 0, triggerCharacter: '\0');
}
private static FormattingContext CreateCore(
Uri uri,
IDocumentSnapshot originalSnapshot,
RazorCodeDocument codeDocument,
RazorFormattingOptions options,
IAdhocWorkspaceFactory workspaceFactory,
bool isFormatOnType,
bool automaticallyAddUsings,
int hostDocumentIndex,
char triggerCharacter)
{
// hostDocumentIndex, triggerCharacter and automaticallyAddUsings are only supported in on type formatting
Debug.Assert(isFormatOnType || (hostDocumentIndex == 0 && triggerCharacter == '\0' && automaticallyAddUsings == false));
return new FormattingContext( return new FormattingContext(
workspaceFactory, workspaceFactory,
uri, uri,
originalSnapshot, originalSnapshot,
codeDocument, codeDocument,
options, options,
isFormatOnType, automaticallyAddUsings: false,
automaticallyAddUsings, hostDocumentIndex: 0,
hostDocumentIndex, triggerCharacter: '\0'
triggerCharacter );
);
} }
} }

Просмотреть файл

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -20,17 +21,14 @@ namespace Microsoft.CodeAnalysis.Razor.Formatting;
internal sealed class CSharpFormattingPass( internal sealed class CSharpFormattingPass(
IDocumentMappingService documentMappingService, IDocumentMappingService documentMappingService,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
: CSharpFormattingPassBase(documentMappingService) : CSharpFormattingPassBase(documentMappingService, isFormatOnType: false)
{ {
private readonly CSharpFormatter _csharpFormatter = new CSharpFormatter(documentMappingService);
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CSharpFormattingPass>(); private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CSharpFormattingPass>();
public async override Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken) public async override Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
{ {
if (context.IsFormatOnType || result.Kind != RazorLanguageKind.Razor) Debug.Assert(result.Kind == RazorLanguageKind.Razor);
{
// We don't want to handle OnTypeFormatting here.
return result;
}
// Apply previous edits if any. // Apply previous edits if any.
var originalText = context.SourceText; var originalText = context.SourceText;
@ -91,7 +89,7 @@ internal sealed class CSharpFormattingPass(
// These should already be remapped. // These should already be remapped.
var range = sourceText.GetRange(span); var range = sourceText.GetRange(span);
var edits = await CSharpFormatter.FormatAsync(context, range, cancellationToken).ConfigureAwait(false); var edits = await _csharpFormatter.FormatAsync(context, range, cancellationToken).ConfigureAwait(false);
csharpEdits.AddRange(edits.Where(e => range.Contains(e.Range))); csharpEdits.AddRange(edits.Where(e => range.Contains(e.Range)));
} }

Просмотреть файл

@ -15,15 +15,15 @@ using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServer.Protocol;
using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range; using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
namespace Microsoft.CodeAnalysis.Razor.Formatting; namespace Microsoft.CodeAnalysis.Razor.Formatting;
using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode; internal abstract class CSharpFormattingPassBase(IDocumentMappingService documentMappingService, bool isFormatOnType) : IFormattingPass
internal abstract class CSharpFormattingPassBase(IDocumentMappingService documentMappingService) : IFormattingPass
{ {
private readonly bool _isFormatOnType = isFormatOnType;
protected IDocumentMappingService DocumentMappingService { get; } = documentMappingService; protected IDocumentMappingService DocumentMappingService { get; } = documentMappingService;
protected CSharpFormatter CSharpFormatter { get; } = new CSharpFormatter(documentMappingService);
public abstract Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken); public abstract Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken);
@ -250,7 +250,7 @@ internal abstract class CSharpFormattingPassBase(IDocumentMappingService documen
if (indentations[i].StartsInHtmlContext) if (indentations[i].StartsInHtmlContext)
{ {
// This is a non-C# line. // This is a non-C# line.
if (context.IsFormatOnType) if (_isFormatOnType)
{ {
// HTML formatter doesn't run in the case of format on type. // HTML formatter doesn't run in the case of format on type.
// Let's stick with our syntax understanding of HTML to figure out the desired indentation. // Let's stick with our syntax understanding of HTML to figure out the desired indentation.
@ -291,13 +291,13 @@ internal abstract class CSharpFormattingPassBase(IDocumentMappingService documen
protected static bool ShouldFormat(FormattingContext context, TextSpan mappingSpan, bool allowImplicitStatements) protected static bool ShouldFormat(FormattingContext context, TextSpan mappingSpan, bool allowImplicitStatements)
=> ShouldFormat(context, mappingSpan, allowImplicitStatements, out _); => ShouldFormat(context, mappingSpan, allowImplicitStatements, out _);
protected static bool ShouldFormat(FormattingContext context, TextSpan mappingSpan, bool allowImplicitStatements, out SyntaxNode? foundOwner) protected static bool ShouldFormat(FormattingContext context, TextSpan mappingSpan, bool allowImplicitStatements, out RazorSyntaxNode? foundOwner)
=> ShouldFormat(context, mappingSpan, new ShouldFormatOptions(allowImplicitStatements, isLineRequest: false), out foundOwner); => ShouldFormat(context, mappingSpan, new ShouldFormatOptions(allowImplicitStatements, isLineRequest: false), out foundOwner);
private static bool ShouldFormatLine(FormattingContext context, TextSpan mappingSpan, bool allowImplicitStatements) private static bool ShouldFormatLine(FormattingContext context, TextSpan mappingSpan, bool allowImplicitStatements)
=> ShouldFormat(context, mappingSpan, new ShouldFormatOptions(allowImplicitStatements, isLineRequest: true), out _); => ShouldFormat(context, mappingSpan, new ShouldFormatOptions(allowImplicitStatements, isLineRequest: true), out _);
private static bool ShouldFormat(FormattingContext context, TextSpan mappingSpan, ShouldFormatOptions options, out SyntaxNode? foundOwner) private static bool ShouldFormat(FormattingContext context, TextSpan mappingSpan, ShouldFormatOptions options, out RazorSyntaxNode? foundOwner)
{ {
// We should be called with the range of various C# SourceMappings. // We should be called with the range of various C# SourceMappings.
@ -437,10 +437,10 @@ internal abstract class CSharpFormattingPassBase(IDocumentMappingService documen
return owner is MarkupTextLiteralSyntax return owner is MarkupTextLiteralSyntax
{ {
Parent: MarkupTagHelperAttributeSyntax { TagHelperAttributeInfo: { Bound: true } } or Parent: MarkupTagHelperAttributeSyntax { TagHelperAttributeInfo.Bound: true } or
MarkupTagHelperDirectiveAttributeSyntax { TagHelperAttributeInfo: { Bound: true } } or MarkupTagHelperDirectiveAttributeSyntax { TagHelperAttributeInfo.Bound: true } or
MarkupMinimizedTagHelperAttributeSyntax { TagHelperAttributeInfo: { Bound: true } } or MarkupMinimizedTagHelperAttributeSyntax { TagHelperAttributeInfo.Bound: true } or
MarkupMinimizedTagHelperDirectiveAttributeSyntax { TagHelperAttributeInfo: { Bound: true } } MarkupMinimizedTagHelperDirectiveAttributeSyntax { TagHelperAttributeInfo.Bound: true }
} && !options.IsLineRequest; } && !options.IsLineRequest;
} }

Просмотреть файл

@ -26,17 +26,13 @@ namespace Microsoft.CodeAnalysis.Razor.Formatting;
internal sealed class CSharpOnTypeFormattingPass( internal sealed class CSharpOnTypeFormattingPass(
IDocumentMappingService documentMappingService, IDocumentMappingService documentMappingService,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
: CSharpFormattingPassBase(documentMappingService) : CSharpFormattingPassBase(documentMappingService, isFormatOnType: true)
{ {
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CSharpOnTypeFormattingPass>(); private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CSharpOnTypeFormattingPass>();
public async override Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken) public async override Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
{ {
if (!context.IsFormatOnType || result.Kind != RazorLanguageKind.CSharp) Debug.Assert(result.Kind == RazorLanguageKind.CSharp);
{
// We don't want to handle regular formatting or non-C# on type formatting here.
return result;
}
// Normalize and re-map the C# edits. // Normalize and re-map the C# edits.
var codeDocument = context.CodeDocument; var codeDocument = context.CodeDocument;

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT license. See License.txt in the project root for license information. // Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,20 +15,34 @@ using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.Formatting; namespace Microsoft.CodeAnalysis.Razor.Formatting;
internal sealed class HtmlFormattingPass(ILoggerFactory loggerFactory) : IFormattingPass internal sealed class HtmlFormattingPass(ILoggerFactory loggerFactory) : HtmlFormattingPassBase(loggerFactory.GetOrCreateLogger<HtmlFormattingPass>())
{ {
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<HtmlFormattingPass>(); }
public async Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken) internal sealed class HtmlOnTypeFormattingPass(ILoggerFactory loggerFactory) : HtmlFormattingPassBase(loggerFactory.GetOrCreateLogger<HtmlOnTypeFormattingPass>())
{
public override Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
{
Debug.Assert(result.Kind == RazorLanguageKind.Html);
if (result.Edits.Length == 0)
{
// There are no HTML edits for us to apply. No op.
return Task.FromResult(new FormattingResult([]));
}
return base.ExecuteAsync(context, result, cancellationToken);
}
}
internal abstract class HtmlFormattingPassBase(ILogger logger) : IFormattingPass
{
private readonly ILogger _logger = logger;
public virtual async Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
{ {
var originalText = context.SourceText; var originalText = context.SourceText;
if (context.IsFormatOnType && result.Kind != RazorLanguageKind.Html)
{
// We don't want to handle on type formatting requests for other languages
return result;
}
var htmlEdits = result.Edits; var htmlEdits = result.Edits;
var changedText = originalText; var changedText = originalText;
@ -44,11 +59,6 @@ internal sealed class HtmlFormattingPass(ILoggerFactory loggerFactory) : IFormat
_logger.LogTestOnly($"After normalizedEdits:\r\n{changedText}"); _logger.LogTestOnly($"After normalizedEdits:\r\n{changedText}");
} }
else if (context.IsFormatOnType)
{
// There are no HTML edits for us to apply. No op.
return new FormattingResult(htmlEdits);
}
var indentationChanges = AdjustRazorIndentation(changedContext); var indentationChanges = AdjustRazorIndentation(changedContext);
if (indentationChanges.Count > 0) if (indentationChanges.Count > 0)

Просмотреть файл

@ -13,24 +13,16 @@ using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Syntax; using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServer.Protocol;
using RazorRazorSyntaxNodeList = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxList<Microsoft.AspNetCore.Razor.Language.Syntax.RazorSyntaxNode>; using RazorRazorSyntaxNodeList = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxList<Microsoft.AspNetCore.Razor.Language.Syntax.RazorSyntaxNode>;
using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
using RazorSyntaxNodeList = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxList<Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode>; using RazorSyntaxNodeList = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxList<Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode>;
namespace Microsoft.CodeAnalysis.Razor.Formatting; namespace Microsoft.CodeAnalysis.Razor.Formatting;
using SyntaxNode = AspNetCore.Razor.Language.Syntax.SyntaxNode;
internal sealed class RazorFormattingPass : IFormattingPass internal sealed class RazorFormattingPass : IFormattingPass
{ {
public async Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken) public async Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
{ {
if (context.IsFormatOnType)
{
// We don't want to handle OnTypeFormatting here.
return result;
}
// Apply previous edits if any. // Apply previous edits if any.
var originalText = context.SourceText; var originalText = context.SourceText;
var changedText = originalText; var changedText = originalText;
@ -58,7 +50,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return new FormattingResult(finalEdits); return new FormattingResult(finalEdits);
} }
private IEnumerable<TextEdit> FormatRazor(FormattingContext context, RazorSyntaxTree syntaxTree) private static IEnumerable<TextEdit> FormatRazor(FormattingContext context, RazorSyntaxTree syntaxTree)
{ {
var edits = new List<TextEdit>(); var edits = new List<TextEdit>();
var source = syntaxTree.Source; var source = syntaxTree.Source;
@ -75,7 +67,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return edits; return edits;
} }
private static void TryFormatBlocks(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static void TryFormatBlocks(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// We only want to run one of these // We only want to run one of these
_ = TryFormatFunctionsBlock(context, edits, source, node) || _ = TryFormatFunctionsBlock(context, edits, source, node) ||
@ -85,7 +77,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
TryFormatSectionBlock(context, edits, source, node); TryFormatSectionBlock(context, edits, source, node);
} }
private static bool TryFormatSectionBlock(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static bool TryFormatSectionBlock(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// @section Goo { // @section Goo {
// } // }
@ -142,7 +134,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
} }
} }
private static bool TryFormatFunctionsBlock(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static bool TryFormatFunctionsBlock(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// @functions // @functions
// { // {
@ -180,7 +172,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return false; return false;
} }
private static bool TryFormatCSharpExplicitTransition(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static bool TryFormatCSharpExplicitTransition(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// We're looking for a code block like this: // We're looking for a code block like this:
// //
@ -201,7 +193,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return false; return false;
} }
private static bool TryFormatComplexCSharpBlock(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static bool TryFormatComplexCSharpBlock(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// complex situations like // complex situations like
// @{ // @{
@ -223,7 +215,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return false; return false;
} }
private static bool TryFormatHtmlInCSharp(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static bool TryFormatHtmlInCSharp(FormattingContext context, IList<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// void Method() // void Method()
// { // {
@ -241,7 +233,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return false; return false;
} }
private static void TryFormatCSharpBlockStructure(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static void TryFormatCSharpBlockStructure(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// We're looking for a code block like this: // We're looking for a code block like this:
// //
@ -301,7 +293,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
} }
} }
private static void TryFormatSingleLineDirective(List<TextEdit> edits, RazorSourceDocument source, SyntaxNode node) private static void TryFormatSingleLineDirective(List<TextEdit> edits, RazorSourceDocument source, RazorSyntaxNode node)
{ {
// Looking for single line directives like // Looking for single line directives like
// //
@ -322,7 +314,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
} }
} }
static bool IsSingleLineDirective(SyntaxNode node, out RazorSyntaxNodeList children) static bool IsSingleLineDirective(RazorSyntaxNode node, out RazorSyntaxNodeList children)
{ {
if (node is CSharpCodeBlockSyntax content && if (node is CSharpCodeBlockSyntax content &&
node.Parent?.Parent is RazorDirectiveSyntax directive && node.Parent?.Parent is RazorDirectiveSyntax directive &&
@ -337,7 +329,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
} }
} }
private static void FormatWhitespaceBetweenDirectiveAndBrace(SyntaxNode node, RazorDirectiveSyntax directive, List<TextEdit> edits, RazorSourceDocument source, FormattingContext context, bool forceNewLine) private static void FormatWhitespaceBetweenDirectiveAndBrace(RazorSyntaxNode node, RazorDirectiveSyntax directive, List<TextEdit> edits, RazorSourceDocument source, FormattingContext context, bool forceNewLine)
{ {
if (node.ContainsOnlyWhitespace(includingNewLines: false) && !forceNewLine) if (node.ContainsOnlyWhitespace(includingNewLines: false) && !forceNewLine)
{ {
@ -356,7 +348,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
} }
} }
private static void ShrinkToSingleSpace(SyntaxNode node, List<TextEdit> edits, RazorSourceDocument source) private static void ShrinkToSingleSpace(RazorSyntaxNode node, List<TextEdit> edits, RazorSourceDocument source)
{ {
// If there is anything other than one single space then we replace with one space between directive and brace. // If there is anything other than one single space then we replace with one space between directive and brace.
// //
@ -365,7 +357,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
edits.Add(edit); edits.Add(edit);
} }
private static bool FormatBlock(FormattingContext context, RazorSourceDocument source, SyntaxNode? directiveNode, SyntaxNode openBraceNode, SyntaxNode codeNode, SyntaxNode closeBraceNode, IList<TextEdit> edits) private static bool FormatBlock(FormattingContext context, RazorSourceDocument source, RazorSyntaxNode? directiveNode, RazorSyntaxNode openBraceNode, RazorSyntaxNode codeNode, RazorSyntaxNode closeBraceNode, IList<TextEdit> edits)
{ {
var didFormat = false; var didFormat = false;
@ -426,7 +418,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return hasBeenModified; return hasBeenModified;
} }
static int GetAdditionalIndentationLevel(FormattingContext context, Range range, SyntaxNode openBraceNode, SyntaxNode codeNode) static int GetAdditionalIndentationLevel(FormattingContext context, Range range, RazorSyntaxNode openBraceNode, RazorSyntaxNode codeNode)
{ {
if (!context.TryGetIndentationLevel(codeNode.Position, out var desiredIndentationLevel)) if (!context.TryGetIndentationLevel(codeNode.Position, out var desiredIndentationLevel))
{ {
@ -441,7 +433,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return desiredIndentationOffset - currentIndentationOffset; return desiredIndentationOffset - currentIndentationOffset;
static int GetLeadingWhitespaceLength(SyntaxNode node, FormattingContext context) static int GetLeadingWhitespaceLength(RazorSyntaxNode node, FormattingContext context)
{ {
var tokens = node.GetTokens(); var tokens = node.GetTokens();
var whitespaceLength = 0; var whitespaceLength = 0;
@ -473,7 +465,7 @@ internal sealed class RazorFormattingPass : IFormattingPass
return whitespaceLength; return whitespaceLength;
} }
static int GetTrailingWhitespaceLength(SyntaxNode node, FormattingContext context) static int GetTrailingWhitespaceLength(RazorSyntaxNode node, FormattingContext context)
{ {
var tokens = node.GetTokens(); var tokens = node.GetTokens();
var whitespaceLength = 0; var whitespaceLength = 0;

Просмотреть файл

@ -26,7 +26,7 @@ internal class RazorFormattingService : IRazorFormattingService
private readonly ImmutableArray<IFormattingPass> _documentFormattingPasses; private readonly ImmutableArray<IFormattingPass> _documentFormattingPasses;
private readonly ImmutableArray<IFormattingPass> _validationPasses; private readonly ImmutableArray<IFormattingPass> _validationPasses;
private readonly CSharpOnTypeFormattingPass _csharpOnTypeFormattingPass; private readonly CSharpOnTypeFormattingPass _csharpOnTypeFormattingPass;
private readonly HtmlFormattingPass _htmlFormattingPass; private readonly HtmlOnTypeFormattingPass _htmlOnTypeFormattingPass;
public RazorFormattingService( public RazorFormattingService(
IDocumentMappingService documentMappingService, IDocumentMappingService documentMappingService,
@ -35,15 +35,20 @@ internal class RazorFormattingService : IRazorFormattingService
{ {
_workspaceFactory = workspaceFactory; _workspaceFactory = workspaceFactory;
var cSharpFormattingPass = new CSharpFormattingPass(documentMappingService, loggerFactory); _htmlOnTypeFormattingPass = new HtmlOnTypeFormattingPass(loggerFactory);
var razorFormattingPass = new RazorFormattingPass();
var diagnosticValidationPass = new FormattingDiagnosticValidationPass(loggerFactory);
var contentValidationPass = new FormattingContentValidationPass(loggerFactory);
_htmlFormattingPass = new HtmlFormattingPass(loggerFactory);
_csharpOnTypeFormattingPass = new CSharpOnTypeFormattingPass(documentMappingService, loggerFactory); _csharpOnTypeFormattingPass = new CSharpOnTypeFormattingPass(documentMappingService, loggerFactory);
_validationPasses = [diagnosticValidationPass, contentValidationPass]; _validationPasses =
_documentFormattingPasses = [_htmlFormattingPass, razorFormattingPass, cSharpFormattingPass, .. _validationPasses]; [
new FormattingDiagnosticValidationPass(loggerFactory),
new FormattingContentValidationPass(loggerFactory)
];
_documentFormattingPasses =
[
new HtmlFormattingPass(loggerFactory),
new RazorFormattingPass(),
new CSharpFormattingPass(documentMappingService, loggerFactory),
.. _validationPasses
];
} }
public async Task<TextEdit[]> GetDocumentFormattingEditsAsync( public async Task<TextEdit[]> GetDocumentFormattingEditsAsync(
@ -120,7 +125,7 @@ internal class RazorFormattingService : IRazorFormattingService
options, options,
hostDocumentIndex, hostDocumentIndex,
triggerCharacter, triggerCharacter,
[_htmlFormattingPass, .. _validationPasses], [_htmlOnTypeFormattingPass, .. _validationPasses],
collapseEdits: false, collapseEdits: false,
automaticallyAddUsings: false, automaticallyAddUsings: false,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);