зеркало из https://github.com/dotnet/razor.git
Separate html formatting passes, so we can remove the IsFormatOnType flag
This commit is contained in:
Родитель
6f1c6ef398
Коммит
f74150db38
|
@ -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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче