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 @@ -171,7 +171,7 @@ public void ApplyCapabilities(VSInternalServerCapabilities serverCapabilities, V
// For C# we run the edit through our formatting engine
Debug.Assert(positionInfo.LanguageKind == RazorLanguageKind.CSharp);

var options = RazorFormattingOptions.From(originalRequest.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine);
var options = RazorFormattingOptions.From(originalRequest.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine, _optionsMonitor.CurrentValue.AttributeIndentStyle);

var csharpSourceText = await documentContext.GetCSharpSourceTextAsync(cancellationToken).ConfigureAwait(false);
var textChange = csharpSourceText.GetTextChange(delegatedResponse.TextEdit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private async Task<VSInternalCompletionItem> PostProcessCompletionItemAsync(
return resolvedCompletionItem;
}

var options = RazorFormattingOptions.From(formattingOptions, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine);
var options = RazorFormattingOptions.From(formattingOptions, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine, _optionsMonitor.CurrentValue.AttributeIndentStyle);

return await DelegatedCompletionHelper.FormatCSharpCompletionItemAsync(
resolvedCompletionItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Settings;

Expand Down Expand Up @@ -95,13 +96,14 @@ internal RazorLSPOptions BuildOptions(JsonObject[] result)
}
else
{
ExtractVSCodeOptions(result, out var formatting, out var autoClosingTags, out var commitElementsWithSpace, out var codeBlockBraceOnNextLine);
ExtractVSCodeOptions(result, out var formatting, out var autoClosingTags, out var commitElementsWithSpace, out var codeBlockBraceOnNextLine, out var attributeIndentStyle);
return RazorLSPOptions.Default with
{
Formatting = formatting,
AutoClosingTags = autoClosingTags,
CommitElementsWithSpace = commitElementsWithSpace,
CodeBlockBraceOnNextLine = codeBlockBraceOnNextLine
CodeBlockBraceOnNextLine = codeBlockBraceOnNextLine,
AttributeIndentStyle = attributeIndentStyle,
};
}
}
Expand All @@ -111,7 +113,8 @@ private void ExtractVSCodeOptions(
out FormattingFlags formatting,
out bool autoClosingTags,
out bool commitElementsWithSpace,
out bool codeBlockBraceOnNextLine)
out bool codeBlockBraceOnNextLine,
out AttributeIndentStyle attributeIndentStyle)
{
var razor = result[0];
var html = result[1];
Expand All @@ -122,6 +125,7 @@ private void ExtractVSCodeOptions(
// Deliberately not using the "default" here because we want a different default for VS Code, as
// this matches VS Code's html servers commit behaviour
commitElementsWithSpace = false;
attributeIndentStyle = RazorLSPOptions.Default.AttributeIndentStyle;

if (razor.TryGetPropertyValue("format", out var parsedFormatNode) &&
parsedFormatNode?.AsObject() is { } parsedFormat)
Expand All @@ -145,6 +149,12 @@ private void ExtractVSCodeOptions(
{
codeBlockBraceOnNextLine = GetObjectOrDefault(parsedCodeBlockBraceOnNextLine, codeBlockBraceOnNextLine);
}

if (parsedFormat.TryGetPropertyValue("attributeIndentStyle", out var parsedAttributeIndentStyle) &&
parsedAttributeIndentStyle is not null)
{
attributeIndentStyle = GetEnumValue(parsedAttributeIndentStyle, AttributeIndentStyle.AlignWithFirst);
}
}

if (razor.TryGetPropertyValue("completion", out var parsedCompletionNode) &&
Expand Down Expand Up @@ -212,4 +222,26 @@ private T GetObjectOrDefault<T>(JsonNode token, T defaultValue, [CallerArgumentE
return defaultValue;
}
}

private T GetEnumValue<T>(JsonNode token, T defaultValue, [CallerArgumentExpression(nameof(defaultValue))] string? expression = null)
where T : struct
{
try
{
// GetValue could potentially throw here if the user provides malformed options.
// If this occurs, catch the exception and return the default value.
if (token.GetValue<string>() is { } stringValue &&
Enum.TryParse<T>(stringValue, ignoreCase: true, out var parsed))
{
return parsed;
}

return defaultValue;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Malformed option: Token {token} cannot be converted to type {typeof(T)} for {expression}.");
return defaultValue;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(DocumentFormattingParams

var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);

var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine);
var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine, _optionsMonitor.CurrentValue.AttributeIndentStyle);

if (await _htmlFormatter.GetDocumentFormattingEditsAsync(
documentContext.Snapshot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(DocumentOnTypeFormatting

cancellationToken.ThrowIfCancellationRequested();

var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine);
var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine, _optionsMonitor.CurrentValue.AttributeIndentStyle);

ImmutableArray<TextChange> formattedChanges;
if (triggerCharacterKind == RazorLanguageKind.CSharp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(DocumentRangeFormattingP
}
}

var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine);
var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine, _optionsMonitor.CurrentValue.AttributeIndentStyle);

if (await _htmlFormatter.GetDocumentFormattingEditsAsync(
documentContext.Snapshot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal sealed record RazorLSPOptions(
bool AutoInsertAttributeQuotes,
bool ColorBackground,
bool CodeBlockBraceOnNextLine,
AttributeIndentStyle AttributeIndentStyle,
bool CommitElementsWithSpace,
ImmutableArray<string> TaskListDescriptors)
{
Expand All @@ -31,6 +32,7 @@ internal sealed record RazorLSPOptions(
AutoInsertAttributeQuotes: true,
ColorBackground: false,
CodeBlockBraceOnNextLine: false,
AttributeIndentStyle: AttributeIndentStyle.AlignWithFirst,
CommitElementsWithSpace: true,
TaskListDescriptors: []);

Expand All @@ -47,16 +49,17 @@ public ImmutableArray<string> TaskListDescriptors
/// </summary>
internal static RazorLSPOptions From(ClientSettings settings)
=> new(GetFormattingFlags(settings),
settings.AdvancedSettings.AutoClosingTags,
!settings.ClientSpaceSettings.IndentWithTabs,
settings.ClientSpaceSettings.IndentSize,
settings.ClientCompletionSettings.AutoShowCompletion,
settings.ClientCompletionSettings.AutoListParams,
settings.AdvancedSettings.AutoInsertAttributeQuotes,
settings.AdvancedSettings.ColorBackground,
settings.AdvancedSettings.CodeBlockBraceOnNextLine,
settings.AdvancedSettings.CommitElementsWithSpace,
settings.AdvancedSettings.TaskListDescriptors);
settings.AdvancedSettings.AutoClosingTags,
!settings.ClientSpaceSettings.IndentWithTabs,
settings.ClientSpaceSettings.IndentSize,
settings.ClientCompletionSettings.AutoShowCompletion,
settings.ClientCompletionSettings.AutoListParams,
settings.AdvancedSettings.AutoInsertAttributeQuotes,
settings.AdvancedSettings.ColorBackground,
settings.AdvancedSettings.CodeBlockBraceOnNextLine,
settings.AdvancedSettings.AttributeIndentStyle,
settings.AdvancedSettings.CommitElementsWithSpace,
settings.AdvancedSettings.TaskListDescriptors);

private static FormattingFlags GetFormattingFlags(ClientSettings settings)
{
Expand Down Expand Up @@ -86,6 +89,7 @@ public bool Equals(RazorLSPOptions? other)
AutoInsertAttributeQuotes == other.AutoInsertAttributeQuotes &&
ColorBackground == other.ColorBackground &&
CodeBlockBraceOnNextLine == other.CodeBlockBraceOnNextLine &&
AttributeIndentStyle == other.AttributeIndentStyle &&
CommitElementsWithSpace == other.CommitElementsWithSpace &&
TaskListDescriptors.SequenceEqual(other.TaskListDescriptors);
}
Expand All @@ -102,6 +106,7 @@ public override int GetHashCode()
hash.Add(AutoInsertAttributeQuotes);
hash.Add(ColorBackground);
hash.Add(CodeBlockBraceOnNextLine);
hash.Add(AttributeIndentStyle);
hash.Add(CommitElementsWithSpace);
hash.Add(TaskListDescriptors);
return hash;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(VSInternalInlineCompleti
continue;
}

var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine);
var options = RazorFormattingOptions.From(request.Options, _optionsMonitor.CurrentValue.CodeBlockBraceOnNextLine, _optionsMonitor.CurrentValue.AttributeIndentStyle);
var formattingContext = FormattingContext.Create(
documentContext.Snapshot,
codeDocument,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
{
InsertSpaces = !clientSettings.ClientSpaceSettings.IndentWithTabs,
TabSize = clientSettings.ClientSpaceSettings.IndentSize,
CodeBlockBraceOnNextLine = clientSettings.AdvancedSettings.CodeBlockBraceOnNextLine
CodeBlockBraceOnNextLine = clientSettings.AdvancedSettings.CodeBlockBraceOnNextLine,
AttributeIndentStyle = clientSettings.AdvancedSettings.AttributeIndentStyle,
};

return await _remoteServiceInvoker.TryInvokeAsync<IRemoteCodeActionsService, CodeAction>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
{
InsertSpaces = !clientSettings.ClientSpaceSettings.IndentWithTabs,
TabSize = clientSettings.ClientSpaceSettings.IndentSize,
CodeBlockBraceOnNextLine = clientSettings.AdvancedSettings.CodeBlockBraceOnNextLine
CodeBlockBraceOnNextLine = clientSettings.AdvancedSettings.CodeBlockBraceOnNextLine,
AttributeIndentStyle = clientSettings.AdvancedSettings.AttributeIndentStyle,
};

// Couldn't find an associated completion list, so its either Razor or C#. Either way, over to OOP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
var options = RazorFormattingOptions.From(
request.Options,
_clientSettingsManager.GetClientSettings().AdvancedSettings.CodeBlockBraceOnNextLine,
_clientSettingsManager.GetClientSettings().AdvancedSettings.AttributeIndentStyle,
csharpSyntaxFormattingOptions);

_logger.LogDebug($"Calling OOP with the {htmlChanges.Length} html edits, so it can fill in the rest");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
}

var csharpSyntaxFormattingOptions = RazorCSharpFormattingInteractionService.GetRazorCSharpSyntaxFormattingOptions(razorDocument.Project.Solution.Services);
var options = RazorFormattingOptions.From(request.Options, clientSettings.AdvancedSettings.CodeBlockBraceOnNextLine, csharpSyntaxFormattingOptions);
var options = RazorFormattingOptions.From(request.Options, clientSettings.AdvancedSettings.CodeBlockBraceOnNextLine, clientSettings.AdvancedSettings.AttributeIndentStyle, csharpSyntaxFormattingOptions);

_logger.LogDebug($"Calling OOP with the {htmlChanges.Length} html edits, so it can fill in the rest");
var remoteResult = await _remoteServiceInvoker.TryInvokeAsync<IRemoteFormattingService, ImmutableArray<TextChange>>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
var options = RazorFormattingOptions.From(
request.Options,
_clientSettingsManager.GetClientSettings().AdvancedSettings.CodeBlockBraceOnNextLine,
_clientSettingsManager.GetClientSettings().AdvancedSettings.AttributeIndentStyle,
csharpSyntaxFormattingOptions);

_logger.LogDebug($"Calling OOP with the {htmlChanges.Length} html edits, so it can fill in the rest");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Protocol.AutoInsert;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.CodeAnalysis.Razor.Settings;
using Microsoft.CodeAnalysis.Razor.Workspaces.Settings;
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
using Response = Microsoft.CodeAnalysis.Razor.Remote.RemoteResponse<Microsoft.CodeAnalysis.Razor.Protocol.AutoInsert.RemoteAutoInsertTextEdit?>;
Expand Down Expand Up @@ -90,7 +91,7 @@ public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilitie
_logger.LogDebug($"Resolving auto-insertion for {razorDocument.FilePath}");

var clientSettings = _clientSettingsManager.GetClientSettings();
var razorFormattingOptions = RazorFormattingOptions.From(request.Options, codeBlockBraceOnNextLine: false);
var razorFormattingOptions = RazorFormattingOptions.From(request.Options, codeBlockBraceOnNextLine: false, attributeIndentStyle: AttributeIndentStyle.AlignWithFirst);
var autoInsertOptions = RemoteAutoInsertOptions.From(clientSettings, razorFormattingOptions);

_logger.LogDebug($"Calling OOP to resolve insertion at {request.Position} invoked by typing '{request.Character}'");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ private async Task<WorkspaceEdit> GenerateMethodInCodeBlockAsync(
{
TabSize = options.TabSize,
InsertSpaces = options.InsertSpaces,
CodeBlockBraceOnNextLine = options.CodeBlockBraceOnNextLine
CodeBlockBraceOnNextLine = options.CodeBlockBraceOnNextLine,
AttributeIndentStyle = options.AttributeIndentStyle,
};

var formattedChange = await _razorFormattingService.TryGetCSharpCodeActionEditAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Settings;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Text;
using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
Expand Down Expand Up @@ -156,6 +157,7 @@ private sealed class Generator(
private readonly SyntaxNode _csharpSyntaxRoot = csharpSyntaxRoot;
private readonly bool _insertSpaces = options.InsertSpaces;
private readonly int _tabSize = options.TabSize;
private readonly AttributeIndentStyle _attributeIndentStyle = options.AttributeIndentStyle;
private readonly RazorCSharpSyntaxFormattingOptions? _csharpSyntaxFormattingOptions = options.CSharpSyntaxFormattingOptions;
private readonly StringBuilder _builder = builder;
private readonly ImmutableArray<LineInfo>.Builder _lineInfoBuilder = lineInfoBuilder;
Expand Down Expand Up @@ -717,16 +719,23 @@ public override LineInfo VisitMarkupCommentBlock(MarkupCommentBlockSyntax node)
{
if (RazorSyntaxFacts.IsAttributeName(node, out var startTag))
{
// Attributes are indented to align with the first attribute in their tag. In future, now that we're handling
// this ourselves, we can make this an option: https://github.com/dotnet/razor/issues/6551
var firstAttribute = startTag.Attributes[0];
var nameSpan = RazorSyntaxFacts.GetFullAttributeNameSpan(firstAttribute);

// We need to line up with the first attribute, but the start tag might not be the first thing on the line,
// so it's really relative to the first non-whitespace character on the line. We use the line that the attribute
// is on, just in case its not on the same line as the start tag.
var lineStart = _sourceText.Lines[GetLineNumber(nameSpan)].GetFirstNonWhitespacePosition().GetValueOrDefault();
var htmlIndentLevel = FormattingUtilities.GetIndentationLevel(nameSpan.Start - lineStart, _tabSize, out var additionalIndentation);
// If we're just indenting attributes by one level, then we don't need to do anything special here.
var htmlIndentLevel = 1;
var additionalIndentation = "";

// Otherwise, attributes can be configured to align with the first attribute in their tag.
if (_attributeIndentStyle == AttributeIndentStyle.AlignWithFirst)
{
var firstAttribute = startTag.Attributes[0];
var nameSpan = RazorSyntaxFacts.GetFullAttributeNameSpan(firstAttribute);

// We need to line up with the first attribute, but the start tag might not be the first thing on the line,
// so it's really relative to the first non-whitespace character on the line. We use the line that the attribute
// is on, just in case its not on the same line as the start tag.
var lineStart = _sourceText.Lines[GetLineNumber(nameSpan)].GetFirstNonWhitespacePosition().GetValueOrDefault();
htmlIndentLevel = FormattingUtilities.GetIndentationLevel(nameSpan.Start - lineStart, _tabSize, out additionalIndentation);
Copy link
Contributor

Choose a reason for hiding this comment

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

nameSpan.Start - lineStart

Same concern from the other PR about whether this could be negative

Copy link
Member Author

Choose a reason for hiding this comment

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

The Html formatter always puts the first attribute on the same line as the start tag. Will add a test.

Copy link
Contributor

Choose a reason for hiding this comment

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

Are you certain? In vscode, if I create a tag that starts at column 100 or so, it's first attribute is placed on the subsequent line. I thought the html formatter in VS did something similar at some point.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh interesting, I was only testing at column 0. Thank you! Will add a test and make sure nothing bad happens.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated the test I added in the other PR, to cover both indent styles

}

// If the element has caused indentation, then we'll want to take one level off our attribute indentation to
// compensate. Need to be careful here because things like `<a` are likely less than a single indent level.
if (ElementCausesIndentation(startTag) && htmlIndentLevel > 0)
Expand Down
Loading