({} as any);
+
+export const useAppContext = () => useContext(AppStateContext);
diff --git a/Src/CSharpier.Playground/ClientApp/src/CodeEditor.tsx b/Src/CSharpier.Playground/ClientApp/src/CodeEditor.tsx
index db8b2cec1..3c4d1a6d3 100644
--- a/Src/CSharpier.Playground/ClientApp/src/CodeEditor.tsx
+++ b/Src/CSharpier.Playground/ClientApp/src/CodeEditor.tsx
@@ -2,10 +2,11 @@ import React, { useEffect } from "react";
import { Controlled as CodeMirror } from "react-codemirror2";
import "codemirror/lib/codemirror.css";
import "codemirror/mode/clike/clike";
-import { useAppContext } from "./AppContext";
import { useOptions } from "./Hooks";
+import { useAppContext } from "./AppContext";
+import { observer } from "mobx-react-lite";
-export const CodeEditor = () => {
+export const CodeEditor = observer(() => {
const { formatCode, enteredCode, setEnteredCode } = useAppContext();
useEffect(() => {
formatCode();
@@ -23,4 +24,4 @@ export const CodeEditor = () => {
onChange={() => {}}
/>
);
-};
+});
diff --git a/Src/CSharpier.Playground/ClientApp/src/Controls.css b/Src/CSharpier.Playground/ClientApp/src/Controls.css
index b44b77c03..2a11be3ad 100644
--- a/Src/CSharpier.Playground/ClientApp/src/Controls.css
+++ b/Src/CSharpier.Playground/ClientApp/src/Controls.css
@@ -31,6 +31,12 @@
margin-top: 10px;
}
+.controlsWrapper select {
+ width: 100%;
+ margin-top: 10px;
+ padding: 3px 4px;
+}
+
.smallButton {
background-color: #ccc;
border-radius: 4px;
diff --git a/Src/CSharpier.Playground/ClientApp/src/Controls.tsx b/Src/CSharpier.Playground/ClientApp/src/Controls.tsx
index fef3d50ce..3eec52dda 100644
--- a/Src/CSharpier.Playground/ClientApp/src/Controls.tsx
+++ b/Src/CSharpier.Playground/ClientApp/src/Controls.tsx
@@ -1,9 +1,18 @@
import React from "react";
-import { useAppContext } from "./AppContext";
import "./Controls.css";
+import { observer } from "mobx-react-lite";
+import { useAppContext } from "./AppContext";
-export const Controls = () => {
+export const Controls = observer(() => {
const {
+ printWidth,
+ setPrintWidth,
+ indentSize,
+ setIndentSize,
+ useTabs,
+ setUseTabs,
+ formatter,
+ setFormatter,
showDoc,
setShowDoc,
hideNull,
@@ -13,14 +22,6 @@ export const Controls = () => {
setEmptyMethod,
setEmptyClass,
copyLeft,
- printWidth,
- setPrintWidth,
- indentSize,
- setIndentSize,
- useTabs,
- setUseTabs,
- parser,
- setParser,
} = useAppContext();
return (
@@ -41,9 +42,10 @@ export const Controls = () => {
Use Tabs
-
@@ -92,4 +94,4 @@ export const Controls = () => {
);
-};
+});
diff --git a/Src/CSharpier.Playground/ClientApp/src/DocTree.tsx b/Src/CSharpier.Playground/ClientApp/src/DocTree.tsx
index 3b4466d78..c41b058ea 100644
--- a/Src/CSharpier.Playground/ClientApp/src/DocTree.tsx
+++ b/Src/CSharpier.Playground/ClientApp/src/DocTree.tsx
@@ -1,6 +1,7 @@
-import React, { useEffect } from "react";
+import React from "react";
import { UnControlled as CodeMirror } from "react-codemirror2";
import { useAppContext } from "./AppContext";
+import { observer } from "mobx-react-lite";
const options = {
mode: {
@@ -14,9 +15,9 @@ const options = {
const regex = /\s+Doc\.Null,/g;
-export const DocTree = () => {
+export const DocTree = observer(() => {
const { doc, hideNull } = useAppContext();
const docToDisplay = hideNull ? doc.replaceAll(regex, "") : doc;
return {}} onChange={() => {}} />;
-};
+});
diff --git a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts
index 9ec4f25af..ac1e23e30 100644
--- a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts
+++ b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts
@@ -2,11 +2,17 @@ let gutters: any[] = [];
let marks: any[] = [];
let editor: any = undefined;
-export const formatCode = async (code: string, printWidth: number, indentSize: number, useTabs: boolean, parser: string) => {
+export const formatCode = async (
+ code: string,
+ printWidth: number,
+ indentSize: number,
+ useTabs: boolean,
+ formatter: string,
+) => {
const makeRequest = async () => {
const response = await fetch("/Format", {
method: "POST",
- body: JSON.stringify({ code, printWidth, indentSize, useTabs, parser }),
+ body: JSON.stringify({ code, printWidth, indentSize, useTabs, formatter }),
headers: {
"Content-Type": "application/json",
"cache-control": "no-cache",
diff --git a/Src/CSharpier.Playground/ClientApp/src/FormattedCode.tsx b/Src/CSharpier.Playground/ClientApp/src/FormattedCode.tsx
index 67ca93084..ccb28b501 100644
--- a/Src/CSharpier.Playground/ClientApp/src/FormattedCode.tsx
+++ b/Src/CSharpier.Playground/ClientApp/src/FormattedCode.tsx
@@ -1,9 +1,10 @@
import { Controlled as CodeMirror } from "react-codemirror2";
import React from "react";
-import { useAppContext } from "./AppContext";
import { useOptions } from "./Hooks";
+import { useAppContext } from "./AppContext";
+import { observer } from "mobx-react-lite";
-export const FormattedCode = () => {
+export const FormattedCode = observer(() => {
const { formattedCode, setFormattedCodeEditor } = useAppContext();
const options = useOptions();
@@ -20,4 +21,4 @@ export const FormattedCode = () => {
onChange={() => {}}
/>
);
-};
+});
diff --git a/Src/CSharpier.Playground/ClientApp/src/Header.tsx b/Src/CSharpier.Playground/ClientApp/src/Header.tsx
index feca55a3e..e5383ae58 100644
--- a/Src/CSharpier.Playground/ClientApp/src/Header.tsx
+++ b/Src/CSharpier.Playground/ClientApp/src/Header.tsx
@@ -1,9 +1,10 @@
import React, { useEffect, useState } from "react";
import { Loading } from "./Icons/Loading";
-import { useAppContext } from "./AppContext";
import "./Header.css";
+import { useAppContext } from "./AppContext";
+import { observer } from "mobx-react-lite";
-export const Header = () => {
+export const Header = observer(() => {
const { isLoading, formatCode, showDoc, showAst } = useAppContext();
const width = showDoc && showAst ? 25 : showDoc || showAst ? 33 : 50;
const [version, setVersion] = useState();
@@ -39,4 +40,4 @@ export const Header = () => {
);
-};
+});
diff --git a/Src/CSharpier.Playground/ClientApp/src/Layout.tsx b/Src/CSharpier.Playground/ClientApp/src/Layout.tsx
index 5bdc6ccb6..21e0ebf93 100644
--- a/Src/CSharpier.Playground/ClientApp/src/Layout.tsx
+++ b/Src/CSharpier.Playground/ClientApp/src/Layout.tsx
@@ -4,13 +4,14 @@ import "codemirror/mode/clike/clike";
import { SyntaxTree } from "./SyntaxTree";
import { DocTree } from "./DocTree";
import { Header } from "./Header";
-import { useAppContext } from "./AppContext";
import { CodeEditor } from "./CodeEditor";
import { FormattedCode } from "./FormattedCode";
import { Controls } from "./Controls";
import "./Layout.scss";
+import { useAppContext } from "./AppContext";
+import { observer } from "mobx-react-lite";
-export const Layout = () => {
+export const Layout = observer(() => {
const { showDoc, showAst } = useAppContext();
const width = showDoc && showAst ? 25 : showDoc || showAst ? 33 : 50;
return (
@@ -32,4 +33,4 @@ export const Layout = () => {
);
-};
+});
diff --git a/Src/CSharpier.Playground/ClientApp/src/SyntaxTree.tsx b/Src/CSharpier.Playground/ClientApp/src/SyntaxTree.tsx
index 997d165fa..e53a682d6 100644
--- a/Src/CSharpier.Playground/ClientApp/src/SyntaxTree.tsx
+++ b/Src/CSharpier.Playground/ClientApp/src/SyntaxTree.tsx
@@ -1,7 +1,8 @@
import React from "react";
import ReactJson, { CollapsedFieldProps } from "react-json-view";
-import { useAppContext } from "./AppContext";
import "./SyntaxTree.css";
+import { useAppContext } from "./AppContext";
+import { observer } from "mobx-react-lite";
const shouldCollapse = (field: CollapsedFieldProps) => {
if (
@@ -15,7 +16,7 @@ const shouldCollapse = (field: CollapsedFieldProps) => {
return false;
};
-export const SyntaxTree = () => {
+export const SyntaxTree = observer(() => {
const { syntaxTree } = useAppContext();
if (!syntaxTree) {
return null;
@@ -34,4 +35,4 @@ export const SyntaxTree = () => {
/>
);
-};
+});
diff --git a/Src/CSharpier.Playground/Controllers/FormatController.cs b/Src/CSharpier.Playground/Controllers/FormatController.cs
index 07a74a617..57d00decc 100644
--- a/Src/CSharpier.Playground/Controllers/FormatController.cs
+++ b/Src/CSharpier.Playground/Controllers/FormatController.cs
@@ -1,16 +1,9 @@
-using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Linq;
-using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
-using Microsoft.Extensions.Logging;
namespace CSharpier.Playground.Controllers;
-using CSharpier.Utilities;
-
public class FormatResult
{
public required string Code { get; set; }
@@ -30,16 +23,10 @@ public class FormatError
[Route("[controller]")]
public class FormatController : ControllerBase
{
- private readonly IWebHostEnvironment webHostEnvironment;
private readonly ILogger logger;
- // ReSharper disable once SuggestBaseTypeForParameter
- public FormatController(
- IWebHostEnvironment webHostEnvironment,
- ILogger logger
- )
+ public FormatController(ILogger logger)
{
- this.webHostEnvironment = webHostEnvironment;
this.logger = logger;
}
@@ -49,7 +36,7 @@ public class PostModel
public int PrintWidth { get; set; }
public int IndentSize { get; set; }
public bool UseTabs { get; set; }
- public string Parser { get; set; } = string.Empty;
+ public string Formatter { get; set; } = string.Empty;
}
[HttpPost]
@@ -58,10 +45,12 @@ public async Task Post(
CancellationToken cancellationToken
)
{
- var sourceCodeKind = model.Parser.EqualsIgnoreCase("CSharp")
- ? SourceCodeKind.Regular
- : SourceCodeKind.Script;
- var result = await CSharpFormatter.FormatAsync(
+ if (!Enum.TryParse(model.Formatter, ignoreCase: true, out var parsedFormatter))
+ {
+ throw new Exception("Invalid formatter " + model.Formatter);
+ }
+
+ var result = await CodeFormatter.FormatAsync(
model.Code,
new PrinterOptions
{
@@ -70,17 +59,18 @@ CancellationToken cancellationToken
Width = model.PrintWidth,
IndentSize = model.IndentSize,
UseTabs = model.UseTabs,
+ Formatter = parsedFormatter,
},
- sourceCodeKind,
cancellationToken
);
+ // TODO xml what about this?
var comparer = new SyntaxNodeComparer(
model.Code,
result.Code,
result.ReorderedModifiers,
result.ReorderedUsingsWithDisabledText,
- sourceCodeKind,
+ parsedFormatter is Formatter.CSharp ? SourceCodeKind.Regular : SourceCodeKind.Script,
cancellationToken
);
diff --git a/Src/CSharpier.Tests.Generators/CSharpier.Tests.Generators.csproj b/Src/CSharpier.Tests.Generators/CSharpier.Tests.Generators.csproj
index 5dfcd5111..196a8fca1 100644
--- a/Src/CSharpier.Tests.Generators/CSharpier.Tests.Generators.csproj
+++ b/Src/CSharpier.Tests.Generators/CSharpier.Tests.Generators.csproj
@@ -14,9 +14,4 @@
-
-
- TemplatedGenerator.cs
-
-
diff --git a/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.cs b/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.cs
index 2320850c9..b0aa77f51 100644
--- a/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.cs
+++ b/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.cs
@@ -1,20 +1,57 @@
-using Generators;
+using System.IO;
+using System.Linq;
+using System.Text;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Scriban;
namespace CSharpier.Tests.Generators;
+// the magic command to get source generators to actually regenerate when they get stuck with old code
+// dotnet build-server shutdown
[Generator]
-public class FormattingTestsGenerator : TemplatedGenerator
+public class FormattingTestsGenerator : ISourceGenerator
{
- protected override string SourceName => "FormattingTests";
+ public void Initialize(GeneratorInitializationContext context) { }
- protected override object GetModel(GeneratorExecutionContext context)
+ public void Execute(GeneratorExecutionContext context)
+ {
+ var template = Template.Parse(this.GetContent(this.GetType().Name + ".sbntxt"));
+
+ var extensions = this.GetExtensions(context).ToList();
+ foreach (var extension in extensions)
+ {
+ var renderedSource = template.Render(
+ this.GetModel(context, extension),
+ member => member.Name
+ );
+
+ var sourceText = SourceText.From(renderedSource, Encoding.UTF8);
+
+ context.AddSource("FormattingTests_" + extension, sourceText);
+ }
+ }
+
+ private IEnumerable GetExtensions(GeneratorExecutionContext context)
+ {
+ return context
+ .AdditionalFiles.Where(o =>
+ o.Path.EndsWith(".test")
+ && !o.Path.EndsWith(".actual.test")
+ && !o.Path.EndsWith(".expected.test")
+ )
+ .Select(o => new FileInfo(o.Path).Directory!.Name)
+ .Distinct();
+ }
+
+ protected object GetModel(GeneratorExecutionContext context, string extension)
{
var tests = context
.AdditionalFiles.Where(o =>
o.Path.EndsWith(".test")
&& !o.Path.EndsWith(".actual.test")
&& !o.Path.EndsWith(".expected.test")
+ && new FileInfo(o.Path).Directory!.Name == extension
)
.Select(o => new
{
@@ -23,6 +60,32 @@ protected override object GetModel(GeneratorExecutionContext context)
UseTabs = Path.GetFileNameWithoutExtension(o.Path).EndsWith("_Tabs"),
});
- return new { Tests = tests };
+ return new { Tests = tests, FileExtension = extension };
+ }
+
+ public string GetContent(string relativePath)
+ {
+ var assembly = this.GetType().Assembly;
+ var baseName = assembly.GetName().Name;
+ var resourceName = relativePath
+ .TrimStart('.')
+ .Replace(Path.DirectorySeparatorChar, '.')
+ .Replace(Path.AltDirectorySeparatorChar, '.');
+
+ var name = baseName + "." + resourceName;
+ using var stream = assembly.GetManifestResourceStream(name);
+
+ if (stream == null)
+ {
+ var list = assembly.GetManifestResourceNames();
+
+ throw new Exception(
+ $"No embedded resource found with the name {name}. Resources found are "
+ + string.Join(", ", list)
+ );
+ }
+
+ using var reader = new StreamReader(stream);
+ return reader.ReadToEnd();
}
}
diff --git a/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.sbntxt b/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.sbntxt
index 78908ab97..80f39280b 100644
--- a/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.sbntxt
+++ b/Src/CSharpier.Tests.Generators/FormattingTestsGenerator.sbntxt
@@ -4,7 +4,7 @@ namespace CSharpier.Tests.FormattingTests
{
[TestFixture]
[Parallelizable(ParallelScope.All)]
- public class FormattingTests : BaseTest
+ public class FormattingTests_{{ FileExtension }} : BaseTest
{
{{- for test in Tests }}
[Test]
diff --git a/Src/CSharpier.Tests/CSharpier.Tests.csproj b/Src/CSharpier.Tests/CSharpier.Tests.csproj
index c646b2636..4a5caa83f 100644
--- a/Src/CSharpier.Tests/CSharpier.Tests.csproj
+++ b/Src/CSharpier.Tests/CSharpier.Tests.csproj
@@ -30,7 +30,4 @@
$([System.String]::Copy(%(Filename)).Replace('.expected', '.test'))
-
-
-
diff --git a/Src/CSharpier.Tests/CSharpierIgnoreTests.cs b/Src/CSharpier.Tests/CSharpierIgnoreTests.cs
index 264d3e1d2..6f2a12399 100644
--- a/Src/CSharpier.Tests/CSharpierIgnoreTests.cs
+++ b/Src/CSharpier.Tests/CSharpierIgnoreTests.cs
@@ -1,10 +1,9 @@
+using CSharpier.SyntaxPrinter;
+using FluentAssertions;
using NUnit.Framework;
namespace CSharpier.Tests;
-using CSharpier.SyntaxPrinter;
-using FluentAssertions;
-
[TestFixture]
public class CSharpierIgnoreTests
{
diff --git a/Src/CSharpier.Tests/Cli/Options/ConfigFileParserTests.cs b/Src/CSharpier.Tests/Cli/Options/ConfigFileParserTests.cs
index 8dbf3cd1f..d206bb708 100644
--- a/Src/CSharpier.Tests/Cli/Options/ConfigFileParserTests.cs
+++ b/Src/CSharpier.Tests/Cli/Options/ConfigFileParserTests.cs
@@ -1,9 +1,9 @@
-namespace CSharpier.Tests.Cli.Options;
-
using CSharpier.Cli.Options;
using FluentAssertions;
using NUnit.Framework;
+namespace CSharpier.Tests.Cli.Options;
+
[TestFixture]
public class ConfigFileParserTests
{
diff --git a/Src/CSharpier.Tests/CodeFormatterTests.cs b/Src/CSharpier.Tests/CodeFormatterTests.cs
index da686609c..3c1b6cb5d 100644
--- a/Src/CSharpier.Tests/CodeFormatterTests.cs
+++ b/Src/CSharpier.Tests/CodeFormatterTests.cs
@@ -4,37 +4,10 @@
namespace CSharpier.Tests;
-using CSharpier.Utilities;
-
-// TODO xml move these around
[TestFixture]
[Parallelizable(ParallelScope.All)]
internal class CodeFormatterTests
{
- [TestCase(EndOfLine.LF, "\n")]
- [TestCase(EndOfLine.CRLF, "\r\n")]
- public void GetLineEndings_Should_Return_Easy_Cases(EndOfLine endOfLine, string expected)
- {
- var code = "tester\n";
- var result = PrinterOptions.GetLineEnding(
- code,
- new PrinterOptions { EndOfLine = endOfLine }
- );
-
- result.Should().Be(expected);
- }
-
- [TestCase("tester\n", "\n")]
- [TestCase("tester\r\n", "\r\n")]
- [TestCase("\ntester\n", "\n")]
- [TestCase("tester", "\n")]
- public void GetLineEndings_With_Auto_Should_Detect(string code, string expected)
- {
- var result = PrinterOptions.GetLineEnding(code, new PrinterOptions());
-
- result.Should().Be(expected);
- }
-
[Test]
public void Format_Should_Use_Default_Width()
{
diff --git a/Src/CSharpier.Tests/CommandLineFormatterTests.cs b/Src/CSharpier.Tests/CommandLineFormatterTests.cs
index 94c5748dc..160867e8f 100644
--- a/Src/CSharpier.Tests/CommandLineFormatterTests.cs
+++ b/Src/CSharpier.Tests/CommandLineFormatterTests.cs
@@ -2,12 +2,11 @@
using System.Text;
using CSharpier.Cli;
using FluentAssertions;
+using Microsoft.Extensions.Logging;
using NUnit.Framework;
namespace CSharpier.Tests;
-using Microsoft.Extensions.Logging;
-
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class CommandLineFormatterTests
diff --git a/Src/CSharpier.Tests/Formatters/CSharp/PreprocessorSymbolsTests.cs b/Src/CSharpier.Tests/Formatters/CSharp/PreprocessorSymbolsTests.cs
index ffff8d9ad..65ad6acee 100644
--- a/Src/CSharpier.Tests/Formatters/CSharp/PreprocessorSymbolsTests.cs
+++ b/Src/CSharpier.Tests/Formatters/CSharp/PreprocessorSymbolsTests.cs
@@ -1,9 +1,9 @@
-namespace CSharpier.Tests.Formatters.CSharp;
-
using CSharpier.Formatters.CSharp;
using FluentAssertions;
using NUnit.Framework;
+namespace CSharpier.Tests.Formatters.CSharp;
+
[TestFixture]
public class PreprocessorSymbolsTests
{
@@ -199,8 +199,6 @@ public void GetSets_Should_Handle_Nested_If()
#endif
#endif
",
- // TODO because the second is a subset of the first, we don't actually need it
- // but it's more work to figure that out for now
"ONE,TWO",
"ONE"
);
diff --git a/Src/CSharpier.Tests/FormattingTests/BaseTest.cs b/Src/CSharpier.Tests/FormattingTests/BaseTest.cs
index 5c6ad7da6..72a8c780e 100644
--- a/Src/CSharpier.Tests/FormattingTests/BaseTest.cs
+++ b/Src/CSharpier.Tests/FormattingTests/BaseTest.cs
@@ -2,25 +2,28 @@
using System.Text;
using CSharpier.Cli;
using CSharpier.SyntaxPrinter;
+using CSharpier.Utilities;
using DiffEngine;
using FluentAssertions;
+using Microsoft.CodeAnalysis;
namespace CSharpier.Tests.FormattingTests;
-using CSharpier.Utilities;
-using Microsoft.CodeAnalysis;
-
public class BaseTest
{
private readonly DirectoryInfo rootDirectory = DirectoryFinder.FindParent("CSharpier.Tests");
- protected async Task RunTest(string fileName, string fileExtension, bool useTabs = false)
+ protected async Task RunTest(
+ string fileName,
+ string fileExtensionWithoutDot,
+ bool useTabs = false
+ )
{
var filePath = Path.Combine(
this.rootDirectory.FullName,
"FormattingTests",
"TestFiles",
- fileExtension,
+ fileExtensionWithoutDot,
fileName + ".test"
);
var fileReaderResult = await FileReader.ReadFileAsync(
@@ -29,10 +32,22 @@ protected async Task RunTest(string fileName, string fileExtension, bool useTabs
CancellationToken.None
);
- var result = await CSharpFormatter.FormatAsync(
+ var formatter = fileExtensionWithoutDot switch
+ {
+ "cs" => Formatter.CSharp,
+ "csx" => Formatter.CSharpScript,
+ "xml" => Formatter.XML,
+ _ => Formatter.Unknown,
+ };
+
+ var result = await CodeFormatter.FormatAsync(
fileReaderResult.FileContents,
- new PrinterOptions { Width = PrinterOptions.WidthUsedByTests, UseTabs = useTabs },
- fileExtension.EqualsIgnoreCase("csx") ? SourceCodeKind.Script : SourceCodeKind.Regular,
+ new PrinterOptions
+ {
+ Width = PrinterOptions.WidthUsedByTests,
+ UseTabs = useTabs,
+ Formatter = formatter,
+ },
CancellationToken.None
);
@@ -58,6 +73,7 @@ protected async Task RunTest(string fileName, string fileExtension, bool useTabs
normalizedCode = normalizedCode.Replace("\r\n", "\n");
}
+ // TODO xml what about this?
var comparer = new SyntaxNodeComparer(
expectedCode,
normalizedCode,
diff --git a/Src/CSharpier.Tests/FormattingTests/LineEndingEdgeCase.cs b/Src/CSharpier.Tests/FormattingTests/LineEndingEdgeCase.cs
index 74596cb11..02cd37407 100644
--- a/Src/CSharpier.Tests/FormattingTests/LineEndingEdgeCase.cs
+++ b/Src/CSharpier.Tests/FormattingTests/LineEndingEdgeCase.cs
@@ -1,9 +1,9 @@
-namespace CSharpier.Tests.FormattingTests;
-
using System.Text;
using FluentAssertions;
using NUnit.Framework;
+namespace CSharpier.Tests.FormattingTests;
+
[TestFixture]
internal class LineEndingEdgeCase
{
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Attributes.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Attributes.test
new file mode 100644
index 000000000..f5c97bca6
--- /dev/null
+++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Attributes.test
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/BasicProject.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/BasicProject.test
new file mode 100644
index 000000000..8c1a5dc4b
--- /dev/null
+++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/BasicProject.test
@@ -0,0 +1,5 @@
+
+
+ 4
+
+
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Comments.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Comments.test
new file mode 100644
index 000000000..7ad24f919
--- /dev/null
+++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Comments.test
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Conditions.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Conditions.test
new file mode 100644
index 000000000..f0a27341a
--- /dev/null
+++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Conditions.test
@@ -0,0 +1,9 @@
+
+
+ v4.5.2
+
+
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Elements.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Elements.test
new file mode 100644
index 000000000..8d38d2d14
--- /dev/null
+++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Elements.test
@@ -0,0 +1,14 @@
+
+
+
+ Text
+ TextValue
+ TextValue
+ TextValue
+ TextValue
+
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/LongAttributes.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/LongAttributes.test
new file mode 100644
index 000000000..036644c51
--- /dev/null
+++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/LongAttributes.test
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/RetainXmlElement.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/RetainXmlElement.test
new file mode 100644
index 000000000..820a64750
--- /dev/null
+++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/RetainXmlElement.test
@@ -0,0 +1,2 @@
+
+
diff --git a/Src/CSharpier.Tests/OptionsProviderTests.cs b/Src/CSharpier.Tests/OptionsProviderTests.cs
index be40d21ba..acff098aa 100644
--- a/Src/CSharpier.Tests/OptionsProviderTests.cs
+++ b/Src/CSharpier.Tests/OptionsProviderTests.cs
@@ -1,11 +1,11 @@
-namespace CSharpier.Tests;
-
using System.IO.Abstractions.TestingHelpers;
using CSharpier.Cli.Options;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using NUnit.Framework;
+namespace CSharpier.Tests;
+
[TestFixture]
[Parallelizable(ParallelScope.Fixtures)]
public class OptionsProviderTests
diff --git a/Src/CSharpier.Tests/PrinterOptionsTests.cs b/Src/CSharpier.Tests/PrinterOptionsTests.cs
new file mode 100644
index 000000000..080e071e9
--- /dev/null
+++ b/Src/CSharpier.Tests/PrinterOptionsTests.cs
@@ -0,0 +1,33 @@
+using FluentAssertions;
+using NUnit.Framework;
+
+namespace CSharpier.Tests;
+
+[TestFixture]
+[Parallelizable(ParallelScope.All)]
+internal class PrinterOptionsTests
+{
+ [TestCase(EndOfLine.LF, "\n")]
+ [TestCase(EndOfLine.CRLF, "\r\n")]
+ public void GetLineEndings_Should_Return_Easy_Cases(EndOfLine endOfLine, string expected)
+ {
+ var code = "tester\n";
+ var result = PrinterOptions.GetLineEnding(
+ code,
+ new PrinterOptions { EndOfLine = endOfLine }
+ );
+
+ result.Should().Be(expected);
+ }
+
+ [TestCase("tester\n", "\n")]
+ [TestCase("tester\r\n", "\r\n")]
+ [TestCase("\ntester\n", "\n")]
+ [TestCase("tester", "\n")]
+ public void GetLineEndings_With_Auto_Should_Detect(string code, string expected)
+ {
+ var result = PrinterOptions.GetLineEnding(code, new PrinterOptions());
+
+ result.Should().Be(expected);
+ }
+}
diff --git a/Src/CSharpier.Tests/Samples/AllInOne.test b/Src/CSharpier.Tests/Samples/AllInOne.test
index 5f282702b..1d8ef2950 100644
--- a/Src/CSharpier.Tests/Samples/AllInOne.test
+++ b/Src/CSharpier.Tests/Samples/AllInOne.test
@@ -1 +1 @@
-
\ No newline at end of file
+Doc.HardLineIfNoPreviousLine
\ No newline at end of file
diff --git a/Src/CSharpier.Tests/Samples/Samples.cs b/Src/CSharpier.Tests/Samples/Samples.cs
index 8cc6e5f76..5392747f3 100644
--- a/Src/CSharpier.Tests/Samples/Samples.cs
+++ b/Src/CSharpier.Tests/Samples/Samples.cs
@@ -1,11 +1,10 @@
using System.Text;
using FluentAssertions;
+using Microsoft.CodeAnalysis;
using NUnit.Framework;
namespace CSharpier.Tests.Samples;
-using Microsoft.CodeAnalysis;
-
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class Samples
@@ -51,12 +50,12 @@ public static async Task RunTest(string fileName)
compareResult.Should().BeEmpty();
await File.WriteAllTextAsync(
- file.Replace(".test", ".actual.test"),
+ file.Replace(".cst", ".actual.test"),
result.Code,
Encoding.UTF8
);
await File.WriteAllTextAsync(
- file.Replace(".test", ".doctree.txt"),
+ file.Replace(".cst", ".doctree.txt"),
result.DocTree,
Encoding.UTF8
);
diff --git a/Src/CSharpier.Tests/Samples/Scratch.test b/Src/CSharpier.Tests/Samples/Scratch.test
index 5f282702b..1d8ef2950 100644
--- a/Src/CSharpier.Tests/Samples/Scratch.test
+++ b/Src/CSharpier.Tests/Samples/Scratch.test
@@ -1 +1 @@
-
\ No newline at end of file
+Doc.HardLineIfNoPreviousLine
\ No newline at end of file
diff --git a/Src/CSharpier.Tests/SyntaxNodeComparerTests.cs b/Src/CSharpier.Tests/SyntaxNodeComparerTests.cs
index b4ae576e2..aa9f457f0 100644
--- a/Src/CSharpier.Tests/SyntaxNodeComparerTests.cs
+++ b/Src/CSharpier.Tests/SyntaxNodeComparerTests.cs
@@ -1,10 +1,9 @@
using FluentAssertions;
+using Microsoft.CodeAnalysis;
using NUnit.Framework;
namespace CSharpier.Tests;
-using Microsoft.CodeAnalysis;
-
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class SyntaxNodeComparerTests
diff --git a/Src/CSharpier/CSharpFormatter.cs b/Src/CSharpier/CSharpFormatter.cs
index 46ad87820..45630d66e 100644
--- a/Src/CSharpier/CSharpFormatter.cs
+++ b/Src/CSharpier/CSharpFormatter.cs
@@ -1,25 +1,14 @@
-namespace CSharpier;
-
using System.Text;
using System.Text.Json;
using CSharpier.Formatters.CSharp;
using CSharpier.SyntaxPrinter;
-internal class CSharpScriptFormatter : CSharpFormatter { }
+namespace CSharpier;
-internal class CSharpFormatter : IFormatter
+internal static class CSharpFormatter
{
internal static readonly LanguageVersion LanguageVersion = LanguageVersion.Preview;
- Task IFormatter.FormatAsync(
- string code,
- PrinterOptions printerOptions,
- CancellationToken cancellationToken
- )
- {
- return FormatAsync(code, printerOptions, cancellationToken);
- }
-
internal static Task FormatAsync(
string code,
PrinterOptions printerOptions
diff --git a/Src/CSharpier/CodeFormatter.cs b/Src/CSharpier/CodeFormatter.cs
index 47a4466ef..ef0645c0c 100644
--- a/Src/CSharpier/CodeFormatter.cs
+++ b/Src/CSharpier/CodeFormatter.cs
@@ -1,5 +1,6 @@
using System.Text;
using System.Text.Json;
+using CSharpier.Formatters.Xml;
using CSharpier.SyntaxPrinter;
namespace CSharpier;
@@ -28,7 +29,7 @@ public static Task FormatAsync(
IndentSize = options.IndentSize,
EndOfLine = options.EndOfLine,
IncludeGenerated = options.IncludeGenerated,
- Formatter = "csharp",
+ Formatter = Formatter.CSharp,
},
cancellationToken
);
@@ -58,10 +59,37 @@ public static Task FormatAsync(
UseTabs = options.IndentStyle == IndentStyle.Tabs,
IndentSize = options.IndentSize,
EndOfLine = options.EndOfLine,
- Formatter = "csharp",
+ Formatter = Formatter.CSharp,
},
SourceCodeKind.Regular,
cancellationToken
);
}
+
+ internal static async Task FormatAsync(
+ string fileContents,
+ PrinterOptions options,
+ CancellationToken cancellationToken
+ )
+ {
+ return options.Formatter switch
+ {
+ Formatter.CSharp
+ => await CSharpFormatter.FormatAsync(
+ fileContents,
+ options,
+ SourceCodeKind.Regular,
+ cancellationToken
+ ),
+ Formatter.CSharpScript
+ => await CSharpFormatter.FormatAsync(
+ fileContents,
+ options,
+ SourceCodeKind.Script,
+ cancellationToken
+ ),
+ Formatter.XML => XmlFormatter.Format(fileContents, options),
+ _ => new CodeFormatterResult { FailureMessage = "Is an unsupported file type." }
+ };
+ }
}
diff --git a/Src/CSharpier/DocPrinter/DocFitter.cs b/Src/CSharpier/DocPrinter/DocFitter.cs
index fa4b7f677..838d376d7 100644
--- a/Src/CSharpier/DocPrinter/DocFitter.cs
+++ b/Src/CSharpier/DocPrinter/DocFitter.cs
@@ -90,8 +90,9 @@ void Push(Doc doc, PrintMode printMode, Indent indent)
case IfBreak ifBreak:
{
var ifBreakMode =
- ifBreak.GroupId != null && groupModeMap!.ContainsKey(ifBreak.GroupId)
- ? groupModeMap[ifBreak.GroupId]
+ ifBreak.GroupId != null
+ && groupModeMap.TryGetValue(ifBreak.GroupId, out var value)
+ ? value
: currentMode;
var contents =
diff --git a/Src/CSharpier/DocTypes/Doc.cs b/Src/CSharpier/DocTypes/Doc.cs
index 10edb115b..e6f02b13e 100644
--- a/Src/CSharpier/DocTypes/Doc.cs
+++ b/Src/CSharpier/DocTypes/Doc.cs
@@ -102,6 +102,17 @@ public static Group GroupWithId(string groupId, Doc contents)
return group;
}
+ private static int groupNumber;
+
+ public static Group GroupWithNewId(out string groupId, Doc contents)
+ {
+ groupId = "Group_" + groupNumber;
+ Interlocked.Increment(ref groupNumber);
+ var group = Group(contents);
+ group.GroupId = groupId;
+ return group;
+ }
+
public static Group GroupWithId(string groupId, params Doc[] contents)
{
var group = Group(contents);
diff --git a/Src/CSharpier/Formatters/CSharp/BooleanExpressionParser.cs b/Src/CSharpier/Formatters/CSharp/BooleanExpressionParser.cs
index 8c8f62f28..ff7f112ea 100644
--- a/Src/CSharpier/Formatters/CSharp/BooleanExpressionParser.cs
+++ b/Src/CSharpier/Formatters/CSharp/BooleanExpressionParser.cs
@@ -1,8 +1,8 @@
-namespace CSharpier.Formatters.CSharp;
-
using System.Collections.Concurrent;
using System.Linq.Expressions;
+namespace CSharpier.Formatters.CSharp;
+
internal abstract class BooleanExpressionParser
{
private static readonly ConcurrentDictionary parsedExpressions =
diff --git a/Src/CSharpier/Formatters/Xml/XmlFormatter.cs b/Src/CSharpier/Formatters/Xml/XmlFormatter.cs
new file mode 100644
index 000000000..9567c72cd
--- /dev/null
+++ b/Src/CSharpier/Formatters/Xml/XmlFormatter.cs
@@ -0,0 +1,25 @@
+using System.Text.Json;
+using System.Xml;
+using CSharpier.Formatters.Xml.XmlNodePrinters;
+
+namespace CSharpier.Formatters.Xml;
+
+internal static class XmlFormatter
+{
+ internal static CodeFormatterResult Format(string xml, PrinterOptions printerOptions)
+ {
+ var xmlDocument = new XmlDocument();
+ xmlDocument.LoadXml(xml);
+
+ var lineEnding = PrinterOptions.GetLineEnding(xml, printerOptions);
+ var doc = Node.Print(xmlDocument);
+ var formattedXml = DocPrinter.DocPrinter.Print(doc, printerOptions, lineEnding);
+
+ return new CodeFormatterResult
+ {
+ Code = formattedXml,
+ DocTree = printerOptions.IncludeDocTree ? DocSerializer.Serialize(doc) : string.Empty,
+ AST = printerOptions.IncludeAST ? JsonSerializer.Serialize(xmlDocument) : string.Empty,
+ };
+ }
+}
diff --git a/Src/CSharpier/Formatters/Xml/XmlNodePrinters/Element.cs b/Src/CSharpier/Formatters/Xml/XmlNodePrinters/Element.cs
new file mode 100644
index 000000000..60f201d6d
--- /dev/null
+++ b/Src/CSharpier/Formatters/Xml/XmlNodePrinters/Element.cs
@@ -0,0 +1,372 @@
+using System.Xml;
+
+namespace CSharpier.Formatters.Xml.XmlNodePrinters;
+
+// TODO it may be worth looking
+// at how this works instead of the html one
+// https://github.com/prettier/plugin-xml/blob/main/src/printer.js
+internal static class Element
+{
+ internal static Doc Print(XmlElement node)
+ {
+ var shouldHugContent = false;
+ // TODO need any of this?
+ // node.ChildNodes.Count == 1 &&
+ // (node.firstChild.type == "interpolation" ||
+ // node.firstChild.type == "angularIcuExpression") &&
+ // node.firstChild.isLeadingSpaceSensitive &&
+ // !node.firstChild.hasLeadingSpaces &&
+ // node.lastChild.isTrailingSpaceSensitive &&
+ // !node.lastChild.hasTrailingSpaces;
+
+ // TODO good idea for group names
+ var attrGroupId = Guid.NewGuid().ToString(); // Symbol("element-attr-group-id");
+
+ Group PrintTag(Doc doc) =>
+ Doc.Group(
+ Doc.GroupWithId(attrGroupId, PrintOpeningTag(node)),
+ doc,
+ PrintClosingTag(node)
+ );
+
+ Doc PrintChildrenDoc(params Doc[] childrenDoc)
+ {
+ if (shouldHugContent)
+ {
+ return Doc.IndentIfBreak(Doc.Concat(childrenDoc), attrGroupId);
+ }
+
+ return Doc.Indent(childrenDoc);
+ }
+
+ Doc PrintLineBeforeChildren()
+ {
+ if (shouldHugContent)
+ {
+ return Doc.IfBreak(Doc.SoftLine, "", attrGroupId);
+ }
+
+ // if (
+ // node.firstChild.hasLeadingSpaces &&
+ // node.firstChild.isLeadingSpaceSensitive
+ // ) {
+ // return Doc.Line;
+ // }
+
+ // if (
+ // node.firstChild.type == "text" &&
+ // node.isWhitespaceSensitive &&
+ // node.isIndentationSensitive
+ // ) {
+ // return dedentToRoot(Doc.SoftLine);
+ // }
+
+ return Doc.SoftLine;
+ }
+ ;
+
+ Doc PrintLineAfterChildren()
+ {
+ // var needsToBorrow = node.next
+ // ? needsToBorrowPrevClosingTagEndMarker(node.next)
+ // : needsToBorrowLastChildClosingTagEndMarker(node.parent);
+ // if (needsToBorrow) {
+ // if (
+ // node.lastChild.hasTrailingSpaces &&
+ // node.lastChild.isTrailingSpaceSensitive
+ // ) {
+ // return " ";
+ // }
+ // return "";
+ // }
+ if (shouldHugContent)
+ {
+ return Doc.IfBreak(Doc.SoftLine, "", attrGroupId);
+ }
+
+ // if (
+ // node.lastChild.hasTrailingSpaces &&
+ // node.lastChild.isTrailingSpaceSensitive
+ // ) {
+ // return line;
+ // }
+ // if (
+ // (node.lastChild.type == "comment" ||
+ // (node.lastChild.type == "text" &&
+ // node.isWhitespaceSensitive &&
+ // node.isIndentationSensitive)) &&
+ // new RegExp(
+ // `\\n[\\t ]{${options.tabWidth * (path.ancestors.length - 1)}}$`,
+ // ).test(node.lastChild.value)
+ // ) {
+ // return "";
+ // }
+
+ return Doc.SoftLine;
+ }
+ ;
+
+ if (node.ChildNodes.Count == 0)
+ {
+ return PrintTag(
+ ""
+ // node.hasDanglingSpaces && node.isDanglingSpaceSensitive ? Doc.Line : ""
+ );
+ }
+
+ return PrintTag(
+ Doc.Concat(
+ ForceBreakContent(node) ? Doc.BreakParent : "",
+ PrintChildrenDoc(PrintLineBeforeChildren(), ElementChildren.Print(node)),
+ PrintLineAfterChildren()
+ )
+ );
+
+ // if (element.IsEmpty)
+ // {
+ // return Doc.Doc.Group("<" + element.Name, PrintAttributes(element), Doc.Line, "/>");
+ // }
+ //
+ // return Doc.Doc.Group(
+ // Doc.Doc.Group("<" + element.Name, PrintAttributes(element)),
+ // Doc.Indent(Doc.SoftLine, ">", PrintChildren(element), "" + element.Name),
+ // Doc.SoftLine,
+ // ">"
+ // );
+
+ // return Doc.Doc.Group(
+ // Doc.GroupWithNewId(
+ // out var groupId,
+ // Doc.Indent(
+ // Doc.Doc.Group("<" + element.Name, PrintAttributes(element), Doc.SoftLine, ">"),
+ // PrintChildren(element)
+ // )
+ // ),
+ // "" + element.Name,
+ // Doc.IfBreak(Doc.SoftLine, Doc.Null, groupId),
+ // ">"
+ // );
+ }
+
+ private static Doc PrintOpeningTag(XmlElement node)
+ {
+ return Doc.Concat("<" + node.Name, PrintAttributes(node), node.IsEmpty ? Doc.Null : ">");
+
+ // return [
+ // printOpeningTagStart(node, options),
+ // printAttributes(path, options, print),
+ // node.isSelfClosing ? "" : printOpeningTagEnd(node),
+ // ];
+ }
+
+ private static Doc PrintClosingTag(XmlElement node)
+ {
+ return Doc.Concat(
+ node.IsEmpty ? Doc.Null : PrintClosingTagStart(node),
+ PrintClosingTagEnd(node)
+ );
+ // return [
+ // node.isSelfClosing ? "" : printClosingTagStart(node, options),
+ // printClosingTagEnd(node, options),
+ // ];
+ }
+
+ private static Doc PrintClosingTagStart(XmlElement node)
+ {
+ // return node.lastChild is not null &&
+ // needsToBorrowParentClosingTagStartMarker(node.lastChild)
+ // ? Doc.Null
+ // :
+
+ return Doc.Concat(PrintClosingTagPrefix(node), PrintClosingTagStartMarker(node));
+ }
+
+ private static Doc PrintClosingTagStartMarker(XmlElement node)
+ {
+ // if (shouldNotPrintClosingTag(node)) {
+ // return "";
+ // }
+ // switch (node.type) {
+ // case "ieConditionalComment":
+ // return "";
+ // case "ieConditionalStartComment":
+ // return "]>";
+ // case "interpolation":
+ // return "}}";
+ // case "angularIcuExpression":
+ // return "}";
+ // case "element":
+ // if (node.isSelfClosing) {
+ // return "/>";
+ // }
+ // // fall through
+ // default:
+
+ return node.IsEmpty ? "/>" : ">";
+ }
+
+ private static Doc PrintClosingTagSuffix(XmlElement node)
+ {
+ return Doc.Null;
+ // return needsToBorrowParentClosingTagStartMarker(node)
+ // ? printClosingTagStartMarker(node.parent, options)
+ // : needsToBorrowNextOpeningTagStartMarker(node)
+ // ? printOpeningTagStartMarker(node.next)
+ // : "";
+ }
+
+ private static Doc PrintClosingTagPrefix(XmlElement node)
+ {
+ return Doc.Null;
+ // return needsToBorrowLastChildClosingTagEndMarker(node)
+ // ? printClosingTagEndMarker(node.lastChild, options)
+ // : "";
+ }
+
+ private static bool ForceBreakContent(XmlElement node)
+ {
+ return false;
+ // return (
+ // forceBreakChildren(node) ||
+ // (node.type === "element" &&
+ // node.children.length > 0 &&
+ // (["body", "script", "style"].includes(node.name) ||
+ // node.children.some((child) => hasNonTextChild(child)))) ||
+ // (node.firstChild &&
+ // node.firstChild === node.lastChild &&
+ // node.firstChild.type !== "text" &&
+ // hasLeadingLineBreak(node.firstChild) &&
+ // (!node.lastChild.isTrailingSpaceSensitive ||
+ // hasTrailingLineBreak(node.lastChild)))
+ // );
+ }
+
+ private static Doc PrintAttributes(XmlElement node)
+ {
+ if (node.Attributes.Count == 0)
+ {
+ return node.IsEmpty ? " " : Doc.Null;
+ }
+
+ // this is just shoved in here for now
+ var result = new List();
+ foreach (XmlAttribute attribute in node.Attributes)
+ {
+ result.Add(Doc.Line, attribute.Name, "=\"", attribute.Value, "\"");
+ }
+
+ return Doc.Indent(result);
+ // const ignoreAttributeData =
+ // node.prev?.type === "comment" &&
+ // getPrettierIgnoreAttributeCommentData(node.prev.value);
+ //
+ // const hasPrettierIgnoreAttribute =
+ // typeof ignoreAttributeData === "boolean"
+ // ? () => ignoreAttributeData
+ // : Array.isArray(ignoreAttributeData)
+ // ? (attribute) => ignoreAttributeData.includes(attribute.rawName)
+ // : () => false;
+ //
+ // const printedAttributes = path.map(
+ // ({ node: attribute }) =>
+ // hasPrettierIgnoreAttribute(attribute)
+ // ? replaceEndOfLine(
+ // options.originalText.slice(
+ // locStart(attribute),
+ // locEnd(attribute),
+ // ),
+ // )
+ // : print(),
+ // "attrs",
+ // );
+ //
+ // const forceNotToBreakAttrContent =
+ // node.type === "element" &&
+ // node.fullName === "script" &&
+ // node.attrs.length === 1 &&
+ // node.attrs[0].fullName === "src" &&
+ // node.children.length === 0;
+ //
+ // const shouldPrintAttributePerLine =
+ // options.singleAttributePerLine &&
+ // node.attrs.length > 1 &&
+ // !isVueSfcBlock(node, options);
+ // const attributeLine = shouldPrintAttributePerLine ? hardline : line;
+ //
+ // /** @type {Doc[]} */
+ // const parts = [
+ // indent([
+ // forceNotToBreakAttrContent ? " " : line,
+ // join(attributeLine, printedAttributes),
+ // ]),
+ // ];
+ //
+ // if (
+ // /**
+ // * 123456
+ // */
+ // (node.firstChild &&
+ // needsToBorrowParentOpeningTagEndMarker(node.firstChild)) ||
+ // /**
+ // * 123
+ // */
+ // (node.isSelfClosing &&
+ // needsToBorrowLastChildClosingTagEndMarker(node.parent)) ||
+ // forceNotToBreakAttrContent
+ // ) {
+ // parts.push(node.isSelfClosing ? " " : "");
+ // } else {
+ // parts.push(
+ // options.bracketSameLine
+ // ? node.isSelfClosing
+ // ? " "
+ // : ""
+ // : node.isSelfClosing
+ // ? line
+ // : softline,
+ // );
+ // }
+ //
+ // return parts;
+ }
+}
diff --git a/Src/CSharpier/Formatters/Xml/XmlNodePrinters/ElementChildren.cs b/Src/CSharpier/Formatters/Xml/XmlNodePrinters/ElementChildren.cs
new file mode 100644
index 000000000..5994f3132
--- /dev/null
+++ b/Src/CSharpier/Formatters/Xml/XmlNodePrinters/ElementChildren.cs
@@ -0,0 +1,152 @@
+using System.Xml;
+
+namespace CSharpier.Formatters.Xml.XmlNodePrinters;
+
+internal static class ElementChildren
+{
+ public static Doc Print(XmlElement node)
+ {
+ return PrintChildren(node, Node.Print);
+
+ // if (node.ChildNodes.Count == 0)
+ // {
+ // return Doc.Null;
+ // }
+ //
+ // if (node.FirstChild is XmlText)
+ // {
+ // return Node.Print(node.FirstChild);
+ // }
+ //
+ // var result = new List();
+ //
+ // foreach (XmlNode xmlNode in node.ChildNodes)
+ // {
+ // result.Add(Doc.Line);
+ // result.Add(Node.Print(xmlNode));
+ // }
+ //
+ // return Doc.Concat(Doc.BreakParent, Doc.Concat(result), Doc.Line);
+ }
+
+ public static Doc PrintChildren(XmlElement node, Func print)
+ {
+ // this force breaks html, head, ul, ol, etc
+ // if (ForceBreakChildren(node))
+ // {
+ // var result = new List { Doc.BreakParent };
+ //
+ // foreach (XmlNode child in node.ChildNodes)
+ // {
+ // var prevBetweenLine =
+ // child.PreviousSibling == null
+ // ? Doc.Null
+ // : PrintBetweenLine(child.PreviousSibling, child);
+ // result.AddRange(
+ // prevBetweenLine == Doc.Null
+ // ? new List()
+ // : new List
+ // {
+ // prevBetweenLine,
+ // ForceNextEmptyLine(child.Prev) ? Doc.HardLine : Doc.Null,
+ // }
+ // );
+ // result.AddRange(PrintChild(child, print));
+ // }
+ //
+ // return result;
+ // }
+
+ var groupIds = new List(); // Assuming CSharpier has grouping feature
+ foreach (var child in node.ChildNodes)
+ {
+ groupIds.Add(Guid.NewGuid().ToString());
+ }
+
+ var docs = new List();
+ foreach (XmlNode child in node.ChildNodes)
+ {
+ var prevBetweenLine =
+ child.PreviousSibling == null
+ ? Doc.Null
+ : PrintBetweenLine(child.PreviousSibling, child);
+ var nextBetweenLine =
+ child.NextSibling == null ? Doc.Null : PrintBetweenLine(child, child.NextSibling);
+
+ var parts = new List();
+ if (prevBetweenLine is not NullDoc)
+ {
+ parts.Add(prevBetweenLine == Doc.HardLine ? Doc.HardLine : Doc.SoftLine);
+ }
+
+ parts.Add(PrintChild(child, print));
+
+ if (nextBetweenLine is not NullDoc)
+ {
+ parts.Add(nextBetweenLine == Doc.HardLine ? Doc.HardLine : Doc.SoftLine);
+ }
+
+ docs.Add(Doc.Group(parts));
+ }
+
+ return Doc.Concat(docs);
+ }
+
+ public static Doc PrintChild(XmlNode child, Func print)
+ {
+ // should we try to support csharpier-ignore some day?
+ // if (HasPrettierIgnore(child))
+ // {
+ // int endLocation = GetEndLocation(child);
+ //
+ // return new List
+ // {
+ // PrintOpeningTagPrefix(child, options),
+ // ReplaceEndOfLine(TrimEnd(options.OriginalText.Substring(
+ // LocStart(child) + (child.Prev != null && NeedsToBorrowNextOpeningTagStartMarker(child.Prev)
+ // ? PrintOpeningTagStartMarker(child).Length : 0),
+ // endLocation - (child.Next != null && NeedsToBorrowPrevClosingTagEndMarker(child.Next)
+ // ? PrintClosingTagEndMarker(child, options).Length : 0)
+ // ))),
+ // PrintClosingTagSuffix(child, options)
+ // };
+ // }
+
+ return print(child);
+ }
+
+ // public static int GetEndLocation(XmlElement node)
+ // {
+ // int endLocation = LocEnd(node);
+ //
+ // // Element can be unclosed
+ // if (node.Type == "element" && node.Children != null && node.Children.Count > 0)
+ // {
+ // return Math.Max(endLocation, GetEndLocation(node.Children[^1])); // Using C# ^ for last element
+ // }
+ //
+ // return endLocation;
+ // }
+
+ public static Doc PrintBetweenLine(XmlNode prevNode, XmlNode nextNode)
+ {
+ return Doc.Line;
+
+ // if (prevNode is XmlText && nextNode is XmlText)
+ // {
+ // if (prevNode.IsTrailingSpaceSensitive)
+ // {
+ // return prevNode.HasTrailingSpaces
+ // ? PreferHardlineAsLeadingSpaces(nextNode)
+ // ? Doc.HardLine
+ // : Line()
+ // : Doc.Empty;
+ // }
+ // return PreferHardlineAsLeadingSpaces(nextNode) ? Doc.HardLine : Doc.SoftLine;
+ // }
+ //
+ // return ShouldBreakBetweenNodes(prevNode, nextNode) ? Doc.HardLine
+ // : nextNode.HasLeadingSpaces ? Line()
+ // : Doc.SoftLine;
+ }
+}
diff --git a/Src/CSharpier/Formatters/Xml/XmlNodePrinters/Node.cs b/Src/CSharpier/Formatters/Xml/XmlNodePrinters/Node.cs
new file mode 100644
index 000000000..1b96dc964
--- /dev/null
+++ b/Src/CSharpier/Formatters/Xml/XmlNodePrinters/Node.cs
@@ -0,0 +1,44 @@
+using System.Xml;
+
+namespace CSharpier.Formatters.Xml.XmlNodePrinters;
+
+internal static class Node
+{
+ internal static Doc Print(XmlNode xmlNode)
+ {
+ if (xmlNode is XmlDocument xmlDocument)
+ {
+ var result = new List();
+ foreach (XmlNode node in xmlDocument.ChildNodes)
+ {
+ result.Add(Print(node), Doc.HardLine);
+ }
+
+ result.Add(Doc.HardLine);
+
+ return Doc.Concat(result);
+ }
+
+ if (xmlNode is XmlDeclaration xmlDeclaration)
+ {
+ return xmlDeclaration.OuterXml;
+ }
+
+ if (xmlNode is XmlElement xmlElement)
+ {
+ return Element.Print(xmlElement);
+ }
+
+ if (xmlNode is XmlText)
+ {
+ return xmlNode.OuterXml;
+ }
+
+ if (xmlNode is XmlComment)
+ {
+ return xmlNode.OuterXml;
+ }
+
+ throw new Exception("Need to handle + " + xmlNode);
+ }
+}
diff --git a/Src/CSharpier/PrinterOptions.cs b/Src/CSharpier/PrinterOptions.cs
index 895176012..7cb4b4ae8 100644
--- a/Src/CSharpier/PrinterOptions.cs
+++ b/Src/CSharpier/PrinterOptions.cs
@@ -10,7 +10,7 @@ internal class PrinterOptions
public EndOfLine EndOfLine { get; set; } = EndOfLine.Auto;
public bool TrimInitialLines { get; init; } = true;
public bool IncludeGenerated { get; set; }
- public string Formatter { get; init; } = string.Empty;
+ public Formatter Formatter { get; init; } = Formatter.Unknown;
public const int WidthUsedByTests = 100;
@@ -34,3 +34,11 @@ internal static string GetLineEnding(string code, PrinterOptions printerOptions)
return "\n";
}
}
+
+internal enum Formatter
+{
+ Unknown,
+ CSharp,
+ CSharpScript,
+ XML,
+}
diff --git a/Src/CSharpier/SyntaxNodeJsonWriter.cs b/Src/CSharpier/SyntaxNodeJsonWriter.cs
index 38874ea68..ec0b15bd8 100644
--- a/Src/CSharpier/SyntaxNodeJsonWriter.cs
+++ b/Src/CSharpier/SyntaxNodeJsonWriter.cs
@@ -1,7 +1,7 @@
-namespace CSharpier;
-
using System.Text.Json;
+namespace CSharpier;
+
internal static partial class SyntaxNodeJsonWriter
{
private static string? WriteBoolean(string name, bool value)
diff --git a/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs b/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs
index fb4e750bd..523c54e98 100644
--- a/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs
+++ b/Src/CSharpier/SyntaxPrinter/CSharpierIgnore.cs
@@ -1,8 +1,8 @@
-namespace CSharpier.SyntaxPrinter;
-
using System.Text;
using System.Text.RegularExpressions;
+namespace CSharpier.SyntaxPrinter;
+
internal static class CSharpierIgnore
{
private static readonly Regex IgnoreRegex = new("^// csharpier-ignore($| -)");
diff --git a/Src/CSharpier/SyntaxPrinter/FormattingContext.cs b/Src/CSharpier/SyntaxPrinter/FormattingContext.cs
index 810ce602c..cee102fc1 100644
--- a/Src/CSharpier/SyntaxPrinter/FormattingContext.cs
+++ b/Src/CSharpier/SyntaxPrinter/FormattingContext.cs
@@ -10,6 +10,7 @@ internal class FormattingContext
public required bool UseTabs { get; init; }
// TODO the rest of these go into State
+
// context.State.PrintingDepth
public int PrintingDepth { get; set; }
public bool NextTriviaNeedsLine { get; set; }
diff --git a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs
index 6e858fb89..885d4f72b 100644
--- a/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs
+++ b/Src/CSharpier/SyntaxPrinter/MembersWithForcedLines.cs
@@ -1,9 +1,8 @@
using System.Collections.Immutable;
+using System.Text;
namespace CSharpier.SyntaxPrinter;
-using System.Text;
-
internal static class MembersWithForcedLines
{
public static List Print(
diff --git a/Src/CSharpier/SyntaxPrinter/SeparatedSyntaxList.cs b/Src/CSharpier/SyntaxPrinter/SeparatedSyntaxList.cs
index 45e730f55..270a66e60 100644
--- a/Src/CSharpier/SyntaxPrinter/SeparatedSyntaxList.cs
+++ b/Src/CSharpier/SyntaxPrinter/SeparatedSyntaxList.cs
@@ -1,7 +1,7 @@
-namespace CSharpier.SyntaxPrinter;
-
using System.Text;
+namespace CSharpier.SyntaxPrinter;
+
internal static class SeparatedSyntaxList
{
public static Doc Print(
diff --git a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs
index 69d36fa04..bf7fe4746 100644
--- a/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs
+++ b/Src/CSharpier/SyntaxPrinter/SyntaxNodePrinters/BaseMethodDeclaration.cs
@@ -1,7 +1,7 @@
-namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters;
-
using System.Text.RegularExpressions;
+namespace CSharpier.SyntaxPrinter.SyntaxNodePrinters;
+
internal static class BaseMethodDeclaration
{
public static Doc Print(CSharpSyntaxNode node, FormattingContext context)
diff --git a/Src/CSharpier/SyntaxPrinter/Token.cs b/Src/CSharpier/SyntaxPrinter/Token.cs
index f7291e2c7..26d646772 100644
--- a/Src/CSharpier/SyntaxPrinter/Token.cs
+++ b/Src/CSharpier/SyntaxPrinter/Token.cs
@@ -1,7 +1,7 @@
-namespace CSharpier.SyntaxPrinter;
-
using System.Text.RegularExpressions;
+namespace CSharpier.SyntaxPrinter;
+
internal static class Token
{
public static Doc PrintWithoutLeadingTrivia(SyntaxToken syntaxToken, FormattingContext context)
diff --git a/Src/CSharpier/Utilities/StackExtensions.cs b/Src/CSharpier/Utilities/StackExtensions.cs
index 78ff09c64..a465419cd 100644
--- a/Src/CSharpier/Utilities/StackExtensions.cs
+++ b/Src/CSharpier/Utilities/StackExtensions.cs
@@ -1,7 +1,7 @@
-namespace CSharpier.Utilities;
-
using System.Diagnostics.CodeAnalysis;
+namespace CSharpier.Utilities;
+
internal static class StackExtensions
{
#if NETSTANDARD2_0
diff --git a/Src/CSharpier/XmlFormatter.cs b/Src/CSharpier/XmlFormatter.cs
deleted file mode 100644
index f40076234..000000000
--- a/Src/CSharpier/XmlFormatter.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace CSharpier;
-
-internal class XmlFormatter : IFormatter
-{
- public Task FormatAsync(
- string code,
- PrinterOptions printerOptions,
- CancellationToken cancellationToken
- )
- {
- throw new NotImplementedException();
- }
-}
diff --git a/Src/SyntaxFinder/SyntaxFinderWalker.cs b/Src/SyntaxFinder/SyntaxFinderWalker.cs
index 79bd461a1..849b375e0 100644
--- a/Src/SyntaxFinder/SyntaxFinderWalker.cs
+++ b/Src/SyntaxFinder/SyntaxFinderWalker.cs
@@ -1,8 +1,8 @@
-namespace SyntaxFinder;
-
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
+namespace SyntaxFinder;
+
public abstract class SyntaxFinderWalker(string file) : CSharpSyntaxWalker
{
private bool wroteFile;
diff --git a/Src/SyntaxFinder/SyntaxTriviaExtensions.cs b/Src/SyntaxFinder/SyntaxTriviaExtensions.cs
index 3d67990b0..79d545180 100644
--- a/Src/SyntaxFinder/SyntaxTriviaExtensions.cs
+++ b/Src/SyntaxFinder/SyntaxTriviaExtensions.cs
@@ -1,8 +1,8 @@
-namespace SyntaxFinder;
-
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
+namespace SyntaxFinder;
+
public static class SyntaxTriviaExtensions
{
public static bool IsComment(this SyntaxTrivia syntaxTrivia)
diff --git a/Src/SyntaxFinder/Walkers/ExampleWalker.cs b/Src/SyntaxFinder/Walkers/ExampleWalker.cs
index 43424381f..451c45aa9 100644
--- a/Src/SyntaxFinder/Walkers/ExampleWalker.cs
+++ b/Src/SyntaxFinder/Walkers/ExampleWalker.cs
@@ -1,10 +1,10 @@
-namespace SyntaxFinder.Walkers;
-
using System.Collections.Concurrent;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+namespace SyntaxFinder.Walkers;
+
public class ExampleWalker(string file) : SyntaxFinderWalker(file)
{
public static readonly ConcurrentDictionary> MembersInType = new();
diff --git a/Src/SyntaxFinder/Walkers/ModifiersWalker.cs b/Src/SyntaxFinder/Walkers/ModifiersWalker.cs
index 868184db4..a7ab5f0ba 100644
--- a/Src/SyntaxFinder/Walkers/ModifiersWalker.cs
+++ b/Src/SyntaxFinder/Walkers/ModifiersWalker.cs
@@ -1,7 +1,7 @@
-namespace SyntaxFinder.Walkers;
-
using Microsoft.CodeAnalysis.CSharp.Syntax;
+namespace SyntaxFinder.Walkers;
+
public class ModifiersWalker : SyntaxFinderWalker
{
public ModifiersWalker(string file)
diff --git a/Src/SyntaxFinder/Walkers/NestedConditionalExpressionsWalker.cs b/Src/SyntaxFinder/Walkers/NestedConditionalExpressionsWalker.cs
index 13ccd77cc..6ed98bf15 100644
--- a/Src/SyntaxFinder/Walkers/NestedConditionalExpressionsWalker.cs
+++ b/Src/SyntaxFinder/Walkers/NestedConditionalExpressionsWalker.cs
@@ -1,10 +1,10 @@
-namespace SyntaxFinder.Walkers;
-
using System.Collections.Concurrent;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+namespace SyntaxFinder.Walkers;
+
public class NestedConditionalExpressionsWalker(string file) : SyntaxFinderWalker(file)
{
public static readonly ConcurrentDictionary> MembersInType = new();
diff --git a/Src/SyntaxFinder/Walkers/ObjectInitializerWalker.cs b/Src/SyntaxFinder/Walkers/ObjectInitializerWalker.cs
index 903540254..81194aac3 100644
--- a/Src/SyntaxFinder/Walkers/ObjectInitializerWalker.cs
+++ b/Src/SyntaxFinder/Walkers/ObjectInitializerWalker.cs
@@ -1,8 +1,8 @@
-namespace SyntaxFinder.Walkers;
-
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+namespace SyntaxFinder.Walkers;
+
public class ObjectInitializerWalker : CSharpSyntaxWalker
{
private static int total;
diff --git a/Src/SyntaxFinder/Walkers/SpreadWalker.cs b/Src/SyntaxFinder/Walkers/SpreadWalker.cs
index ba5ae061f..6f2c098b3 100644
--- a/Src/SyntaxFinder/Walkers/SpreadWalker.cs
+++ b/Src/SyntaxFinder/Walkers/SpreadWalker.cs
@@ -1,8 +1,8 @@
-namespace SyntaxFinder.Walkers;
-
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+namespace SyntaxFinder.Walkers;
+
public class SpreadWalker : CSharpSyntaxWalker
{
private static int total;