diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriter.cs index 5b44fbc7fc3a..59f46fea939e 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriter.cs @@ -18,9 +18,15 @@ public sealed class CodeWriter private int _currentLineIndex; private int _currentLineCharacterIndex; - public CodeWriter() + public CodeWriter() : this(Environment.NewLine, RazorCodeGenerationOptions.CreateDefault()) { - NewLine = Environment.NewLine; + } + + public CodeWriter(string newLine, RazorCodeGenerationOptions options) + { + NewLine = newLine; + IndentWithTabs = options.IndentWithTabs; + TabSize = options.IndentSize; _builder = new StringBuilder(); } @@ -47,6 +53,10 @@ public string NewLine } } + public bool IndentWithTabs { get; } + + public int TabSize { get; } + public SourceLocation Location => new SourceLocation(_absoluteIndex, _currentLineIndex, _currentLineCharacterIndex); public char this[int index] @@ -62,16 +72,34 @@ public char this[int index] } } - // Internal for testing. - internal CodeWriter Indent(int size) + public CodeWriter Indent(int size) { - if (Length == 0 || this[Length - 1] == '\n') + if (size == 0 || (Length != 0 && this[Length - 1] != '\n')) { - _builder.Append(' ', size); + return this; + } - _currentLineCharacterIndex += size; - _absoluteIndex += size; + var actualSize = 0; + if (IndentWithTabs) + { + // Avoid writing directly to the StringBuilder here, that will throw off the manual indexing + // done by the base class. + var tabs = size / TabSize; + actualSize += tabs; + _builder.Append('\t', tabs); + + var spaces = size % TabSize; + actualSize += spaces; + _builder.Append(' ', spaces); } + else + { + actualSize = size; + _builder.Append(' ', size); + } + + _currentLineCharacterIndex += actualSize; + _absoluteIndex += actualSize; return this; } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriterExtensions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriterExtensions.cs index eee9f37927cb..227f8d2ae3a4 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriterExtensions.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/CodeWriterExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -48,29 +48,7 @@ public static CodeWriter WritePadding(this CodeWriter writer, int offset, Source var basePadding = CalculatePadding(); var resolvedPadding = Math.Max(basePadding - offset, 0); - if (context.Options.IndentWithTabs) - { - // Avoid writing directly to the StringBuilder here, that will throw off the manual indexing - // done by the base class. - var tabs = resolvedPadding / context.Options.IndentSize; - for (var i = 0; i < tabs; i++) - { - writer.Write("\t"); - } - - var spaces = resolvedPadding % context.Options.IndentSize; - for (var i = 0; i < spaces; i++) - { - writer.Write(" "); - } - } - else - { - for (var i = 0; i < resolvedPadding; i++) - { - writer.Write(" "); - } - } + writer.Indent(resolvedPadding); return writer; @@ -86,7 +64,7 @@ int CalculatePadding() } else if (@char == '\t') { - spaceCount += context.Options.IndentSize; + spaceCount += writer.TabSize; } else { @@ -569,11 +547,11 @@ public struct CSharpCodeWritingScope : IDisposable private int _tabSize; private int _startIndent; - public CSharpCodeWritingScope(CodeWriter writer, int tabSize = 4, bool autoSpace = true) + public CSharpCodeWritingScope(CodeWriter writer, bool autoSpace = true) { _writer = writer; _autoSpace = autoSpace; - _tabSize = tabSize; + _tabSize = writer.TabSize; _startIndent = -1; // Set in WriteStartScope WriteStartScope(); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs index c5fb4de90e25..e23a3030e940 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -32,7 +32,7 @@ public override RazorCSharpDocument WriteDocument(RazorCodeDocument codeDocument } var context = new DefaultCodeRenderingContext( - new CodeWriter(), + new CodeWriter(Environment.NewLine, _options), _codeTarget.CreateNodeWriter(), codeDocument, documentNode, diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptions.cs index 0b1441b945ac..03b56f309168 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptions.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptions.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Razor.Language internal class DefaultRazorCodeGenerationOptions : RazorCodeGenerationOptions { public DefaultRazorCodeGenerationOptions( - bool indentWithTabs, + bool indentWithTabs, int indentSize, bool designTime, string rootNamespace, diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt index 8a9e35f076b7..8f38fcd0813c 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt @@ -1,6 +1,10 @@ #nullable enable +Microsoft.AspNetCore.Razor.Language.CodeGeneration.CodeWriter.IndentWithTabs.get -> bool +Microsoft.AspNetCore.Razor.Language.CodeGeneration.CodeWriter.TabSize.get -> int Microsoft.AspNetCore.Razor.Language.Intermediate.CascadingGenericTypeParameter Microsoft.AspNetCore.Razor.Language.Intermediate.CascadingGenericTypeParameter.CascadingGenericTypeParameter() -> void +~Microsoft.AspNetCore.Razor.Language.CodeGeneration.CodeWriter.CodeWriter(string newLine, Microsoft.AspNetCore.Razor.Language.RazorCodeGenerationOptions options) -> void +~Microsoft.AspNetCore.Razor.Language.CodeGeneration.CodeWriter.Indent(int size) -> Microsoft.AspNetCore.Razor.Language.CodeGeneration.CodeWriter ~Microsoft.AspNetCore.Razor.Language.Intermediate.CascadingGenericTypeParameter.GenericTypeNames.get -> System.Collections.Generic.IReadOnlyCollection ~Microsoft.AspNetCore.Razor.Language.Intermediate.CascadingGenericTypeParameter.GenericTypeNames.set -> void ~Microsoft.AspNetCore.Razor.Language.Intermediate.ComponentIntermediateNode.ProvidesCascadingGenericTypes.get -> System.Collections.Generic.Dictionary diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs index cb7973df37f0..88f4447eda61 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/CSharpCodeWriterTest.cs @@ -359,5 +359,47 @@ public void WriteAutoPropertyDeclaration_WithModifiers_WritesPropertyDeclaration var output = writer.GenerateCode(); Assert.Equal("public static global::System.String MyString { get; set; }" + Environment.NewLine, output); } + + [Fact] + public void CSharpCodeWriter_RespectTabSetting() + { + // Arrange + var options = RazorCodeGenerationOptions.Create(o => + { + o.IndentWithTabs = true; + o.IndentSize = 4; + }); + + var writer = new CodeWriter(Environment.NewLine, options); + + // Act + writer.BuildClassDeclaration(Array.Empty(), "C", "", Array.Empty(), Array.Empty()); + writer.WriteField(Array.Empty(), Array.Empty(), "int", "f"); + + // Assert + var output = writer.GenerateCode(); + Assert.Equal("class C" + Environment.NewLine + "{" + Environment.NewLine + "\tint f;" + Environment.NewLine, output); + } + + [Fact] + public void CSharpCodeWriter_RespectSpaceSetting() + { + // Arrange + var options = RazorCodeGenerationOptions.Create(o => + { + o.IndentWithTabs = false; + o.IndentSize = 4; + }); + + var writer = new CodeWriter(Environment.NewLine, options); + + // Act + writer.BuildClassDeclaration(Array.Empty(), "C", "", Array.Empty(), Array.Empty()); + writer.WriteField(Array.Empty(), Array.Empty(), "int", "f"); + + // Assert + var output = writer.GenerateCode(); + Assert.Equal("class C" + Environment.NewLine + "{" + Environment.NewLine + " int f;" + Environment.NewLine, output); + } } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.codegen.cs index 95b48f43d907..a38bd26fe387 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.codegen.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.codegen.cs @@ -76,7 +76,7 @@ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Renderin #line default #line hidden #nullable disable - + __o = Microsoft.AspNetCore.Components.BindConverter.FormatValue( #nullable restore diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.mappings.txt index f3d9de1293b1..0fa1e7a5b834 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.mappings.txt +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/Component_WithCssScope/TestComponent.mappings.txt @@ -35,19 +35,19 @@ Generated Location: (2126:73,38 [18] ) Source Location: (557:12,84 [6] x:\dir\subdir\Test\TestComponent.cshtml) | | -Generated Location: (2353:78,96 [6] ) +Generated Location: (2341:78,84 [6] ) | | Source Location: (589:13,30 [10] x:\dir\subdir\Test\TestComponent.cshtml) |myVariable| -Generated Location: (2540:83,30 [10] ) +Generated Location: (2528:83,30 [10] ) |myVariable| Source Location: (637:13,78 [3] x:\dir\subdir\Test\TestComponent.cshtml) | }| -Generated Location: (2905:92,78 [3] ) +Generated Location: (2893:92,78 [3] ) | }| @@ -62,7 +62,7 @@ Source Location: (651:16,7 [245] x:\dir\subdir\Test\TestComponent.cshtml) for (var i = 0; i < 10; i++) { | -Generated Location: (3087:102,7 [245] ) +Generated Location: (3075:102,7 [245] ) | ElementReference myElementReference; TemplatedComponent myComponentReference; @@ -76,12 +76,12 @@ Generated Location: (3087:102,7 [245] ) Source Location: (912:25,28 [1] x:\dir\subdir\Test\TestComponent.cshtml) |i| -Generated Location: (3499:119,28 [1] ) +Generated Location: (3487:119,28 [1] ) |i| Source Location: (925:25,41 [1] x:\dir\subdir\Test\TestComponent.cshtml) |i| -Generated Location: (3675:127,41 [1] ) +Generated Location: (3663:127,41 [1] ) |i| Source Location: (931:25,47 [166] x:\dir\subdir\Test\TestComponent.cshtml) @@ -93,7 +93,7 @@ Source Location: (931:25,47 [166] x:\dir\subdir\Test\TestComponent.cshtml) System.GC.KeepAlive(myVariable); } | -Generated Location: (3847:134,47 [166] ) +Generated Location: (3835:134,47 [166] ) | } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.codegen.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.codegen.cs index 4b5f6a8517b4..c72365f1eb5f 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.codegen.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.codegen.cs @@ -45,7 +45,7 @@ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Renderin #line default #line hidden #nullable disable - + } #pragma warning restore 1998 diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.mappings.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.mappings.txt index 34c80001e6a7..fe1a4a0917e6 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.mappings.txt +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/SingleLineControlFlowStatements_InCodeBlock/TestComponent.mappings.txt @@ -24,7 +24,7 @@ Generated Location: (1301:42,16 [6] ) Source Location: (216:6,26 [2] x:\dir\subdir\Test\TestComponent.cshtml) | | -Generated Location: (1398:47,38 [2] ) +Generated Location: (1386:47,26 [2] ) | |