Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,11 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal class CSharpFormatter
internal sealed class CSharpFormatter(IRazorDocumentMappingService documentMappingService)
{
private const string MarkerId = "RazorMarker";

private readonly IRazorDocumentMappingService _documentMappingService;
private readonly IClientConnection _clientConnection;

public CSharpFormatter(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection)
{
if (documentMappingService is null)
{
throw new ArgumentNullException(nameof(documentMappingService));
}

if (clientConnection is null)
{
throw new ArgumentNullException(nameof(clientConnection));
}

_documentMappingService = documentMappingService;
_clientConnection = clientConnection;
}
private readonly IRazorDocumentMappingService _documentMappingService = documentMappingService;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're removing the ArgumentNullException, could IRazorDocumentMappingService ever be null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is directly constructed in code, so nullable reference types protects us from ever passing in null. If it was created by DI, then both of the DI technologies we use would throw errors during composition/container creation and never get this far anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that both MEF and MS.Ext.DependencyInjection already protect against null is a good one to remember. We have lots of services and endpoints that validate against null arguments that are wholly unnecessary. As more and more code is nullable enabled (most of tooling is and most of the compiler is not), removing unnecessary ArgumentNullExceptions gets a thumbs up from me.


public async Task<TextEdit[]> FormatAsync(FormattingContext context, Range rangeToFormat, CancellationToken cancellationToken)
{
Expand All @@ -57,7 +38,7 @@ public async Task<TextEdit[]> FormatAsync(FormattingContext context, Range range

if (!_documentMappingService.TryMapToGeneratedDocumentRange(context.CodeDocument.GetCSharpDocument(), rangeToFormat, out var projectedRange))
{
return Array.Empty<TextEdit>();
return [];
}

var edits = await GetFormattingEditsAsync(context, projectedRange, cancellationToken).ConfigureAwait(false);
Expand Down Expand Up @@ -116,7 +97,7 @@ private static async Task<Dictionary<int, int>> GetCSharpIndentationCoreAsync(Fo
// No point calling the C# formatting if we won't be interested in any of its work anyway
if (projectedDocumentLocations.Count == 0)
{
return new Dictionary<int, int>();
return [];
}

var (indentationMap, syntaxTree) = InitializeIndentationData(context, projectedDocumentLocations, cancellationToken);
Expand Down Expand Up @@ -195,7 +176,7 @@ static void ExtractTokenAnnotations(
// will not indent them at all. When they happen to be indented more than 2 levels this causes a problem
// because we essentially assume that we should always move them left by at least 2 levels. This means that these
// nodes end up moving left with every format operation, until they hit the minimum of 2 indent levels.
// We can't fix this, so we just work around it by ignoring those lines compeletely, and leaving them where the
// We can't fix this, so we just work around it by ignoring those lines completely, and leaving them where the
// user put them.

if (ShouldIgnoreLineCompletely(token, formattedText))
Expand All @@ -220,7 +201,7 @@ static bool ShouldIgnoreLineCompletelyBecauseOfNode(SyntaxNode? node, SourceText
{
// We don't want to format lines that are part of multi-line string literals
LiteralExpressionSyntax { RawKind: (int)CodeAnalysis.CSharp.SyntaxKind.StringLiteralExpression } => SpansMultipleLines(node, text),
// As above, but for mutli-line interpolated strings
// As above, but for multi-line interpolated strings
InterpolatedStringExpressionSyntax => SpansMultipleLines(node, text),
InterpolatedStringTextSyntax => SpansMultipleLines(node, text),
_ => false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,12 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal class CSharpFormattingPass : CSharpFormattingPassBase
internal sealed class CSharpFormattingPass(
IRazorDocumentMappingService documentMappingService,
ILoggerFactory loggerFactory)
: CSharpFormattingPassBase(documentMappingService)
{
private readonly ILogger _logger;

public CSharpFormattingPass(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection,
ILoggerFactory loggerFactory)
: base(documentMappingService, clientConnection)
{
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_logger = loggerFactory.GetOrCreateLogger<CSharpFormattingPass>();
}
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CSharpFormattingPass>();

// Run after the HTML and Razor formatter pass.
public override int Order => DefaultOrder - 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
Expand All @@ -21,10 +20,10 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal abstract class CSharpFormattingPassBase : FormattingPassBase
{
protected CSharpFormattingPassBase(IRazorDocumentMappingService documentMappingService, IClientConnection clientConnection)
: base(documentMappingService, clientConnection)
protected CSharpFormattingPassBase(IRazorDocumentMappingService documentMappingService)
: base(documentMappingService)
{
CSharpFormatter = new CSharpFormatter(documentMappingService, clientConnection);
CSharpFormatter = new CSharpFormatter(documentMappingService);
}

protected CSharpFormatter CSharpFormatter { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.LanguageServer.CodeActions;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.AspNetCore.Razor.TextDifferencing;
using Microsoft.CodeAnalysis;
Expand All @@ -23,30 +22,15 @@
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using SyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
using TextSpan = Microsoft.CodeAnalysis.Text.TextSpan;

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal class CSharpOnTypeFormattingPass : CSharpFormattingPassBase
internal sealed class CSharpOnTypeFormattingPass(
IRazorDocumentMappingService documentMappingService,
ILoggerFactory loggerFactory)
: CSharpFormattingPassBase(documentMappingService)
{
private readonly ILogger _logger;
private readonly RazorLSPOptionsMonitor _optionsMonitor;

public CSharpOnTypeFormattingPass(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection,
RazorLSPOptionsMonitor optionsMonitor,
ILoggerFactory loggerFactory)
: base(documentMappingService, clientConnection)
{
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_logger = loggerFactory.GetOrCreateLogger<CSharpOnTypeFormattingPass>();
_optionsMonitor = optionsMonitor;
}
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<CSharpOnTypeFormattingPass>();

public async override Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
{
Expand Down Expand Up @@ -235,7 +219,7 @@ private static async Task<TextEdit[]> AddUsingStatementEditsIfNecessaryAsync(For
if (textEdits.Any(e => e.NewText.IndexOf("using") != -1))
{
var usingStatementEdits = await AddUsingsCodeActionProviderHelper.GetUsingStatementEditsAsync(codeDocument, csharpText, originalTextWithChanges, cancellationToken).ConfigureAwait(false);
finalEdits = usingStatementEdits.Concat(finalEdits).ToArray();
finalEdits = [.. usingStatementEdits, .. finalEdits];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,12 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal class FormattingContentValidationPass : FormattingPassBase
internal sealed class FormattingContentValidationPass(
IRazorDocumentMappingService documentMappingService,
ILoggerFactory loggerFactory)
: FormattingPassBase(documentMappingService)
{
private readonly ILogger _logger;

public FormattingContentValidationPass(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection,
ILoggerFactory loggerFactory)
: base(documentMappingService, clientConnection)
{
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_logger = loggerFactory.GetOrCreateLogger<FormattingContentValidationPass>();
}
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<FormattingContentValidationPass>();

// We want this to run at the very end.
public override int Order => DefaultOrder + 1000;
Expand Down Expand Up @@ -79,7 +68,7 @@ public override Task<FormattingResult> ExecuteAsync(FormattingContext context, F
Debug.Fail("A formatting result was rejected because it was going to change non-whitespace content in the document.");
}

return Task.FromResult(new FormattingResult(Array.Empty<TextEdit>()));
return Task.FromResult(new FormattingResult([]));
}

return Task.FromResult(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,12 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal class FormattingDiagnosticValidationPass : FormattingPassBase
internal sealed class FormattingDiagnosticValidationPass(
IRazorDocumentMappingService documentMappingService,
ILoggerFactory loggerFactory)
: FormattingPassBase(documentMappingService)
{
private readonly ILogger _logger;

public FormattingDiagnosticValidationPass(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection,
ILoggerFactory loggerFactory)
: base(documentMappingService, clientConnection)
{
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_logger = loggerFactory.GetOrCreateLogger<FormattingDiagnosticValidationPass>();
}
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<FormattingDiagnosticValidationPass>();

// We want this to run at the very end.
public override int Order => DefaultOrder + 1000;
Expand Down Expand Up @@ -85,7 +74,7 @@ public async override Task<FormattingResult> ExecuteAsync(FormattingContext cont
Debug.Fail("A formatting result was rejected because the formatted text produced different diagnostics compared to the original text.");
}

return new FormattingResult(Array.Empty<TextEdit>());
return new FormattingResult([]);
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,15 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal abstract class FormattingPassBase : IFormattingPass
internal abstract class FormattingPassBase(IRazorDocumentMappingService documentMappingService) : IFormattingPass
{
protected static readonly int DefaultOrder = 1000;

public FormattingPassBase(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection)
{
if (documentMappingService is null)
{
throw new ArgumentNullException(nameof(documentMappingService));
}

if (clientConnection is null)
{
throw new ArgumentNullException(nameof(clientConnection));
}

DocumentMappingService = documentMappingService;
}

public abstract bool IsValidationPass { get; }

public virtual int Order => DefaultOrder;

protected IRazorDocumentMappingService DocumentMappingService { get; }
protected IRazorDocumentMappingService DocumentMappingService { get; } = documentMappingService;

public abstract Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken);

Expand All @@ -61,7 +44,7 @@ protected TextEdit[] RemapTextEdits(RazorCodeDocument codeDocument, TextEdit[] p

if (codeDocument.IsUnsupported())
{
return Array.Empty<TextEdit>();
return [];
}

var edits = DocumentMappingService.GetHostDocumentEdits(codeDocument.GetCSharpDocument(), projectedTextEdits);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
Expand All @@ -15,41 +14,24 @@
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using TextSpan = Microsoft.CodeAnalysis.Text.TextSpan;

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal class HtmlFormattingPass : FormattingPassBase
internal sealed class HtmlFormattingPass(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection,
IDocumentVersionCache documentVersionCache,
ILoggerFactory loggerFactory)
: FormattingPassBase(documentMappingService)
{
private readonly ILogger _logger;
private readonly RazorLSPOptionsMonitor _optionsMonitor;

public HtmlFormattingPass(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection,
IDocumentVersionCache documentVersionCache,
RazorLSPOptionsMonitor optionsMonitor,
ILoggerFactory loggerFactory)
: base(documentMappingService, clientConnection)
{
if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_logger = loggerFactory.GetOrCreateLogger<HtmlFormattingPass>();

HtmlFormatter = new HtmlFormatter(clientConnection, documentVersionCache);
_optionsMonitor = optionsMonitor;
}
private readonly HtmlFormatter _htmlFormatter = new HtmlFormatter(clientConnection, documentVersionCache);
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<HtmlFormattingPass>();

// We want this to run first because it uses the client HTML formatter.
public override int Order => DefaultOrder - 5;

public override bool IsValidationPass => false;

protected HtmlFormatter HtmlFormatter { get; }

public async override Task<FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
{
var originalText = context.SourceText;
Expand All @@ -58,11 +40,11 @@ public async override Task<FormattingResult> ExecuteAsync(FormattingContext cont

if (context.IsFormatOnType && result.Kind == RazorLanguageKind.Html)
{
htmlEdits = await HtmlFormatter.FormatOnTypeAsync(context, cancellationToken).ConfigureAwait(false);
htmlEdits = await _htmlFormatter.FormatOnTypeAsync(context, cancellationToken).ConfigureAwait(false);
}
else if (!context.IsFormatOnType)
{
htmlEdits = await HtmlFormatter.FormatAsync(context, cancellationToken).ConfigureAwait(false);
htmlEdits = await _htmlFormatter.FormatAsync(context, cancellationToken).ConfigureAwait(false);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting;

internal class RazorFormattingPass(
internal sealed class RazorFormattingPass(
IRazorDocumentMappingService documentMappingService,
IClientConnection clientConnection,
RazorLSPOptionsMonitor optionsMonitor,
ILoggerFactory loggerFactory)
: FormattingPassBase(documentMappingService, clientConnection)
RazorLSPOptionsMonitor optionsMonitor)
: FormattingPassBase(documentMappingService)
{
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<RazorFormattingPass>();
private readonly RazorLSPOptionsMonitor _optionsMonitor = optionsMonitor;

// Run after the C# formatter pass.
Expand Down Expand Up @@ -85,7 +82,7 @@ private IEnumerable<TextEdit> FormatRazor(FormattingContext context, RazorSyntax
return edits;
}

private void TryFormatBlocks(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, SyntaxNode node)
private static void TryFormatBlocks(FormattingContext context, List<TextEdit> edits, RazorSourceDocument source, SyntaxNode node)
{
// We only want to run one of these
_ = TryFormatFunctionsBlock(context, edits, source, node) ||
Expand Down
Loading