diff --git a/CSharpier.sln.DotSettings b/CSharpier.sln.DotSettings index 176f4143d..0c7f8f6fb 100644 --- a/CSharpier.sln.DotSettings +++ b/CSharpier.sln.DotSettings @@ -1,5 +1,8 @@  False + True + False + True True True diff --git a/Src/CSharpier.Cli/CommandLineFormatter.cs b/Src/CSharpier.Cli/CommandLineFormatter.cs index fadb4ccb6..d45eb33ec 100644 --- a/Src/CSharpier.Cli/CommandLineFormatter.cs +++ b/Src/CSharpier.Cli/CommandLineFormatter.cs @@ -501,7 +501,10 @@ CancellationToken cancellationToken { IFormattingValidator? formattingValidator = null; - if (printerOptions.Formatter is Formatter.CSharp or Formatter.CSharpScript) + if ( + printerOptions.Formatter is Formatter.CSharp or Formatter.CSharpScript + && fileToFormatInfo.FileContents != codeFormattingResult.Code + ) { var sourceCodeKind = printerOptions.Formatter is Formatter.CSharpScript diff --git a/Src/CSharpier.Cli/EditorConfig/EditorConfigFileParser.cs b/Src/CSharpier.Cli/EditorConfig/CSharpierConfigParser.cs similarity index 100% rename from Src/CSharpier.Cli/EditorConfig/EditorConfigFileParser.cs rename to Src/CSharpier.Cli/EditorConfig/CSharpierConfigParser.cs diff --git a/Src/CSharpier.Cli/EditorConfig/EditorConfigSections.cs b/Src/CSharpier.Cli/EditorConfig/EditorConfigSections.cs index c6476769a..bd7e33ec4 100644 --- a/Src/CSharpier.Cli/EditorConfig/EditorConfigSections.cs +++ b/Src/CSharpier.Cli/EditorConfig/EditorConfigSections.cs @@ -53,6 +53,11 @@ internal class EditorConfigSections printerOptions.EndOfLine = endOfLine; } + if (resolvedConfiguration.XmlWhitespaceSensitivity is { } xmlWhitespaceSensitivity) + { + printerOptions.XmlWhitespaceSensitivity = xmlWhitespaceSensitivity; + } + return printerOptions; } @@ -63,6 +68,7 @@ private class ResolvedConfiguration public int? TabWidth { get; } public int? MaxLineLength { get; } public EndOfLine? EndOfLine { get; } + public XmlWhitespaceSensitivity? XmlWhitespaceSensitivity { get; set; } public string? Formatter { get; } public ResolvedConfiguration(List
sections) @@ -104,9 +110,23 @@ public ResolvedConfiguration(List
sections) } var endOfLine = sections.LastOrDefault(o => o.EndOfLine != null)?.EndOfLine; - if (Enum.TryParse(endOfLine, true, out EndOfLine result)) + if (Enum.TryParse(endOfLine, true, out EndOfLine parsedEndOfLine)) + { + this.EndOfLine = parsedEndOfLine; + } + + var xmlWhitespaceSensitivity = sections + .LastOrDefault(o => o.XmlWhitespaceSensitivity != null) + ?.XmlWhitespaceSensitivity; + if ( + Enum.TryParse( + xmlWhitespaceSensitivity, + true, + out XmlWhitespaceSensitivity parsedXmlWhitespaceSensitivity + ) + ) { - this.EndOfLine = result; + this.XmlWhitespaceSensitivity = parsedXmlWhitespaceSensitivity; } this.Formatter = sections.LastOrDefault(o => o.Formatter is not null)?.Formatter; diff --git a/Src/CSharpier.Cli/EditorConfig/Section.cs b/Src/CSharpier.Cli/EditorConfig/Section.cs index e8f708831..156ccebbf 100644 --- a/Src/CSharpier.Cli/EditorConfig/Section.cs +++ b/Src/CSharpier.Cli/EditorConfig/Section.cs @@ -13,6 +13,8 @@ internal class Section(SectionData section, string directory) public string? MaxLineLength { get; } = section.Keys["max_line_length"]; public string? EndOfLine { get; } = section.Keys["end_of_line"]; public string? Formatter { get; } = section.Keys["csharpier_formatter"]; + public string? XmlWhitespaceSensitivity { get; } = + section.Keys["csharpier_xml_whitespace_sensitivity"]; public bool IsMatch(string fileName, bool ignoreDirectory) { diff --git a/Src/CSharpier.Cli/IgnoreFile.cs b/Src/CSharpier.Cli/IgnoreFile.cs index 89d2320bf..a64738e0c 100644 --- a/Src/CSharpier.Cli/IgnoreFile.cs +++ b/Src/CSharpier.Cli/IgnoreFile.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.IO.Abstractions; using CSharpier.Cli.DotIgnore; using CSharpier.Core; @@ -35,17 +36,27 @@ public bool IsIgnored(string filePath) string baseDirectoryPath, IFileSystem fileSystem, string? ignorePath, + ConcurrentDictionary? ignoreCache, CancellationToken cancellationToken ) { - Task CreateIgnore(string ignoreFilePath, string? overrideBasePath) + async Task CreateIgnore(string ignoreFilePath, string? overrideBasePath) { - return IgnoreList.CreateAsync( + if (ignoreCache is not null && ignoreCache.TryGetValue(ignoreFilePath, out var ignore)) + { + return ignore; + } + + ignore = await IgnoreList.CreateAsync( fileSystem, overrideBasePath ?? Path.GetDirectoryName(ignoreFilePath)!, ignoreFilePath, cancellationToken ); + + ignoreCache?[ignoreFilePath] = ignore; + + return ignore; } return await SharedFunc diff --git a/Src/CSharpier.Cli/Options/ConfigurationFileOptions.cs b/Src/CSharpier.Cli/Options/ConfigurationFileOptions.cs index ee85813cb..a83508c02 100644 --- a/Src/CSharpier.Cli/Options/ConfigurationFileOptions.cs +++ b/Src/CSharpier.Cli/Options/ConfigurationFileOptions.cs @@ -10,6 +10,10 @@ internal class ConfigurationFileOptions public int? IndentSize { get; init; } public bool UseTabs { get; init; } + [JsonConverter(typeof(CaseInsensitiveEnumConverter))] + public XmlWhitespaceSensitivity XmlWhitespaceSensitivity { get; init; } = + XmlWhitespaceSensitivity.Strict; + [JsonConverter(typeof(CaseInsensitiveEnumConverter))] public EndOfLine EndOfLine { get; init; } @@ -38,6 +42,7 @@ out var parsedFormatter UseTabs = matchingOverride.UseTabs, Width = matchingOverride.PrintWidth, EndOfLine = matchingOverride.EndOfLine, + XmlWhitespaceSensitivity = matchingOverride.XmlWhitespaceSensitivity, }; } @@ -50,6 +55,7 @@ out var parsedFormatter UseTabs = this.UseTabs, Width = this.PrintWidth, EndOfLine = this.EndOfLine, + XmlWhitespaceSensitivity = this.XmlWhitespaceSensitivity, }; } @@ -73,6 +79,10 @@ internal class Override public int IndentSize { get; init; } = 4; public bool UseTabs { get; init; } + [JsonConverter(typeof(CaseInsensitiveEnumConverter))] + public XmlWhitespaceSensitivity XmlWhitespaceSensitivity { get; init; } = + XmlWhitespaceSensitivity.Strict; + [JsonConverter(typeof(CaseInsensitiveEnumConverter))] public EndOfLine EndOfLine { get; init; } diff --git a/Src/CSharpier.Cli/Options/OptionsProvider.cs b/Src/CSharpier.Cli/Options/OptionsProvider.cs index 0eb094614..6ce1c2da4 100644 --- a/Src/CSharpier.Cli/Options/OptionsProvider.cs +++ b/Src/CSharpier.Cli/Options/OptionsProvider.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using System.IO.Abstractions; using System.Text.Json; +using CSharpier.Cli.DotIgnore; using CSharpier.Cli.EditorConfig; using CSharpier.Core; using Microsoft.Extensions.Logging; @@ -15,6 +16,7 @@ private readonly ConcurrentDictionary< string, CSharpierConfigData? > csharpierConfigsByDirectory = new(); + private readonly ConcurrentDictionary ignoreWithPathCache = new(); private readonly ConcurrentDictionary ignoreFilesByDirectory = new(); private readonly ConfigurationFileOptions? specifiedConfigFile; private readonly EditorConfigSections? specifiedEditorConfig; @@ -60,6 +62,7 @@ CancellationToken cancellationToken directoryName, fileSystem, ignorePath, + null, cancellationToken ); @@ -114,6 +117,11 @@ await EditorConfigLocator.FindForDirectoryNameAsync( CancellationToken cancellationToken ) { + // TODO #1794 the xml whitespace should default based on the file + // possible defaults + // xaml files = xaml + // msbuild ones = ignore + // everything else = strict if (this.specifiedConfigFile is not null) { return this.specifiedConfigFile.ConvertToPrinterOptions(filePath); @@ -204,7 +212,13 @@ CancellationToken cancellationToken Path.Combine(searchingDirectory, ".csharpierignore") ), (searchingDirectory) => - IgnoreFile.CreateAsync(searchingDirectory, this.fileSystem, null, cancellationToken) + IgnoreFile.CreateAsync( + searchingDirectory, + this.fileSystem, + null, + ignoreWithPathCache, + cancellationToken + ) ); #pragma warning disable IDE0270 diff --git a/Src/CSharpier.Core/CSharp/CSharpFormatter.cs b/Src/CSharpier.Core/CSharp/CSharpFormatter.cs index 984a53912..162d17550 100644 --- a/Src/CSharpier.Core/CSharp/CSharpFormatter.cs +++ b/Src/CSharpier.Core/CSharp/CSharpFormatter.cs @@ -155,6 +155,7 @@ bool TryGetCompilationFailure(out CodeFormatterResult compilationResult) LineEnding = lineEnding, IndentSize = printerOptions.IndentSize, UseTabs = printerOptions.UseTabs, + XmlWhitespaceSensitivity = XmlWhitespaceSensitivity.Strict, }, }; var document = Node.Print(rootNode, printingContext); @@ -181,6 +182,7 @@ bool TryGetCompilationFailure(out CodeFormatterResult compilationResult) LineEnding = lineEnding, IndentSize = printerOptions.IndentSize, UseTabs = printerOptions.UseTabs, + XmlWhitespaceSensitivity = XmlWhitespaceSensitivity.Strict, }, }; document = Node.Print( diff --git a/Src/CSharpier.Core/CSharp/SyntaxNodeComparer.cs b/Src/CSharpier.Core/CSharp/SyntaxNodeComparer.cs index 7d5bed4a1..8675bfbf3 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxNodeComparer.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxNodeComparer.cs @@ -50,6 +50,7 @@ CancellationToken cancellationToken cSharpParseOptions, cancellationToken: cancellationToken ); + this.CompareFunc = Compare; } public string CompareSource() @@ -148,14 +149,16 @@ SyntaxNode formattedStart return Equal; } +#pragma warning disable CA1822 private CompareResult CompareLists( - IReadOnlyList originalList, - IReadOnlyList formattedList, - Func comparer, - Func getSpan, + T originalList, + T formattedList, + Func comparer, + Func getSpan, TextSpan originalParentSpan, TextSpan newParentSpan ) + where T : IReadOnlyList { for (var x = 0; x < originalList.Count || x < formattedList.Count; x++) { @@ -169,25 +172,71 @@ TextSpan newParentSpan return NotEqual(getSpan(originalList[x]), newParentSpan); } - if ( - originalList[x] is SyntaxNode originalNode - && formattedList[x] is SyntaxNode formattedNode - ) + var result = comparer(originalList[x], formattedList[x]); + if (result.IsInvalid) + { + return result; + } + } + + return Equal; + } +#pragma warning restore CA1822 + + private CompareResult CompareLists( + T originalList, + T formattedList, + Func comparer, + Func getSpan, + TextSpan originalParentSpan, + TextSpan newParentSpan + ) + where T : IReadOnlyList + { + for (var x = 0; x < originalList.Count || x < formattedList.Count; x++) + { + if (x == originalList.Count) { - this.originalStack.Push((originalNode, originalNode.Parent)); - this.formattedStack.Push((formattedNode, formattedNode.Parent)); + return NotEqual(originalParentSpan, getSpan(formattedList[x])); } - else + + if (x == formattedList.Count) { - var result = comparer(originalList[x], formattedList[x]); - if (result.IsInvalid) + return NotEqual(getSpan(originalList[x]), newParentSpan); + } + + var originalNode = originalList[x]; + var formattedNode = formattedList[x]; + this.originalStack.Push((originalNode, originalNode.Parent)); + this.formattedStack.Push((formattedNode, formattedNode.Parent)); + } + + return Equal; + } + + private static SyntaxToken[] AllSeparatorsButLast(in SeparatedSyntaxList list) + { + if (list.Count <= 1) + { + return []; + } + + var tokens = new SyntaxToken[list.Count - 1]; + var tokenIndex = 0; + + foreach (var element in list.GetWithSeparators()) + { + if (element.IsToken) + { + tokens[tokenIndex++] = element.AsToken(); + if (tokenIndex == tokens.Length) { - return result; + break; } } } - return Equal; + return tokens; } private static CompareResult NotEqual(SyntaxNode? originalNode, SyntaxNode? formattedNode) @@ -210,6 +259,8 @@ private static CompareResult NotEqual(TextSpan? originalSpan, TextSpan? formatte }; } + private Func CompareFunc { get; } + private CompareResult Compare(SyntaxToken originalToken, SyntaxToken formattedToken) { return this.Compare(originalToken, formattedToken, null, null); @@ -322,6 +373,17 @@ private CompareResult Compare(SyntaxTrivia originalTrivia, SyntaxTrivia formatte : NotEqual(originalTrivia.Span, formattedTrivia.Span); } + private bool CompareFullSpan(SyntaxNode originalStart, SyntaxNode formattedStart) + { + var originalSpan = OriginalSourceCode + .AsSpan() + .Slice(originalStart.FullSpan.Start, originalStart.FullSpan.Length); + var formattedSpan = NewSourceCode + .AsSpan() + .Slice(formattedStart.FullSpan.Start, formattedStart.FullSpan.Length); + return originalSpan == formattedSpan; + } + private static CompareResult CompareComment( string originalComment, string formattedComment, diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs index 9e2b0d0dc..33a5b352f 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/MembersWithForcedLines.cs @@ -20,7 +20,7 @@ public static List Print( ) where T : MemberDeclarationSyntax { - var result = new List(); + var result = new List(members.Count * 3); if (!skipFirstHardLine) { result.Add(Doc.HardLine); diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/Modifiers.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/Modifiers.cs index 40ffc5d52..f966d6084 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/Modifiers.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/Modifiers.cs @@ -1,4 +1,5 @@ using CSharpier.Core.DocTypes; +using CSharpier.Core.Utilities; using Microsoft.CodeAnalysis; namespace CSharpier.Core.CSharp.SyntaxPrinter; diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs index e0f5176d4..dcac53eae 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/PrintingContext.cs @@ -36,6 +36,7 @@ public class PrintingContextOptions public required string LineEnding { get; init; } public required int IndentSize { get; init; } public required bool UseTabs { get; init; } + public required XmlWhitespaceSensitivity XmlWhitespaceSensitivity { get; init; } } public class PrintingContextState diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/RightHandSide.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/RightHandSide.cs index 272082060..734033ead 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/RightHandSide.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/RightHandSide.cs @@ -1,4 +1,5 @@ using CSharpier.Core.DocTypes; +using CSharpier.Core.Utilities; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Argument.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Argument.cs index f1d3aa223..c001e5298 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Argument.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Argument.cs @@ -9,7 +9,11 @@ internal static class Argument { public static Doc Print(ArgumentSyntax node, PrintingContext context) { - return Doc.Concat(PrintModifiers(node, context), Node.Print(node.Expression, context)); + var modifiers = PrintModifiers(node, context); + + return modifiers == Doc.Null + ? Node.Print(node.Expression, context) + : Doc.Concat(modifiers, Node.Print(node.Expression, context)); } public static Doc PrintModifiers(ArgumentSyntax node, PrintingContext context) diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Parameter.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Parameter.cs index 4aba50361..a802a41e3 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Parameter.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/Parameter.cs @@ -19,7 +19,7 @@ public static Doc Print(ParameterSyntax node, PrintingContext context) if ( node.AttributeLists.Count < 2 && ( - Enumerable.Any(node.GetLeadingTrivia(), o => o.IsComment()) + node.GetLeadingTrivia().Any(o => o.IsComment()) || node.Parent is ParameterListSyntax { Parameters.Count: 0 } ) ) diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/RecursivePattern.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/RecursivePattern.cs index 2553ca007..6c8b20648 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/RecursivePattern.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/SyntaxNodePrinters/RecursivePattern.cs @@ -78,9 +78,8 @@ or BinaryPatternSyntax result.Add( Doc.Group( node.Type != null - && !Enumerable.Any( - node.PropertyPatternClause.OpenBraceToken.LeadingTrivia, - o => o.IsDirective || o.IsComment() + && !node.PropertyPatternClause.OpenBraceToken.LeadingTrivia.Any(o => + o.IsDirective || o.IsComment() ) ? Doc.Line : Doc.Null, diff --git a/Src/CSharpier.Core/CSharp/SyntaxPrinter/Token.cs b/Src/CSharpier.Core/CSharp/SyntaxPrinter/Token.cs index 579966134..18e11218b 100644 --- a/Src/CSharpier.Core/CSharp/SyntaxPrinter/Token.cs +++ b/Src/CSharpier.Core/CSharp/SyntaxPrinter/Token.cs @@ -131,7 +131,7 @@ or SyntaxKind.InterpolatedRawStringEndToken { if ( context.State.TrailingComma is not null - && Enumerable.FirstOrDefault(syntaxToken.TrailingTrivia, o => o.IsComment()) + && syntaxToken.TrailingTrivia.FirstOrDefault(o => o.IsComment()) == context.State.TrailingComma.TrailingComment ) { @@ -174,7 +174,8 @@ public static Doc PrintLeadingTrivia(SyntaxToken syntaxToken, PrintingContext co skipLastHardline: isClosingBrace ); - var hasDirective = Enumerable.Any(syntaxToken.LeadingTrivia, o => o.IsDirective); + var leadingTrivia = syntaxToken.LeadingTrivia; + var hasDirective = leadingTrivia.Any(o => o.IsDirective); if (hasDirective) { @@ -198,7 +199,7 @@ public static Doc PrintLeadingTrivia(SyntaxToken syntaxToken, PrintingContext co Doc extraNewLines = Doc.Null; - if (hasDirective || Enumerable.Any(syntaxToken.LeadingTrivia, o => o.IsComment())) + if (hasDirective || leadingTrivia.Any(o => o.IsComment())) { extraNewLines = ExtraNewLines.Print(syntaxToken.LeadingTrivia); } @@ -351,12 +352,7 @@ void AddLeadingComment(CommentType commentType) if (context.State.NextTriviaNeedsLine) { - if ( - Enumerable.Any( - leadingTrivia, - o => o.RawSyntaxKind() is SyntaxKind.IfDirectiveTrivia - ) - ) + if (leadingTrivia.Any(o => o.RawSyntaxKind() is SyntaxKind.IfDirectiveTrivia)) { docs.Insert(0, Doc.HardLineSkipBreakIfFirstInGroup); } @@ -430,17 +426,11 @@ private static Doc PrintTrailingTrivia(in SyntaxTriviaList trailingTrivia) public static bool HasComments(SyntaxToken syntaxToken) { - return Enumerable.Any( - syntaxToken.LeadingTrivia, - o => - o.RawSyntaxKind() - is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.EndOfLineTrivia) + return syntaxToken.LeadingTrivia.Any(o => + o.RawSyntaxKind() is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.EndOfLineTrivia) ) - || Enumerable.Any( - syntaxToken.TrailingTrivia, - o => - o.RawSyntaxKind() - is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.EndOfLineTrivia) + || syntaxToken.TrailingTrivia.Any(o => + o.RawSyntaxKind() is not (SyntaxKind.WhitespaceTrivia or SyntaxKind.EndOfLineTrivia) ); } @@ -463,11 +453,8 @@ public static bool HasLeadingCommentMatching(SyntaxNode node, Regex regex) public static bool HasLeadingCommentMatching(SyntaxToken token, Regex regex) { - return Enumerable.Any( - token.LeadingTrivia, - o => - o.RawSyntaxKind() is SyntaxKind.SingleLineCommentTrivia - && regex.IsMatch(o.ToString()) + return token.LeadingTrivia.Any(o => + o.RawSyntaxKind() is SyntaxKind.SingleLineCommentTrivia && regex.IsMatch(o.ToString()) ); } } diff --git a/Src/CSharpier.Core/DocPrinter/PropagateBreaks.cs b/Src/CSharpier.Core/DocPrinter/PropagateBreaks.cs index e036ed204..24d2a0189 100644 --- a/Src/CSharpier.Core/DocPrinter/PropagateBreaks.cs +++ b/Src/CSharpier.Core/DocPrinter/PropagateBreaks.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using CSharpier.Core.DocTypes; namespace CSharpier.Core.DocPrinter; @@ -27,6 +28,7 @@ void BreakParentGroup() } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] bool OnEnter(Doc doc) { if (doc is ForceFlat) @@ -87,22 +89,23 @@ void OnExit(Doc doc) docsStack.Push(document); while (docsStack.Count > 0) { - var doc = docsStack.Pop(); + var doc = docsStack.Peek(); if (doc == TraverseDocOnExitStackMarker) { + docsStack.Pop(); OnExit(docsStack.Pop()); continue; } - docsStack.Push(doc); - docsStack.Push(TraverseDocOnExitStackMarker); - if (!OnEnter(doc)) { + OnExit(docsStack.Pop()); continue; } + docsStack.Push(TraverseDocOnExitStackMarker); + if (doc is Concat concat) { // push onto stack in reverse order so they are processed in the original order diff --git a/Src/CSharpier.Core/PrinterOptions.cs b/Src/CSharpier.Core/PrinterOptions.cs index 6e0b16da6..48a78d678 100644 --- a/Src/CSharpier.Core/PrinterOptions.cs +++ b/Src/CSharpier.Core/PrinterOptions.cs @@ -26,7 +26,9 @@ public int IndentSize public EndOfLine EndOfLine { get; set; } = EndOfLine.Auto; public bool TrimInitialLines { get; init; } = true; public bool IncludeGenerated { get; set; } - public Formatter Formatter { get; set; } = formatter; + public Formatter Formatter { get; } = formatter; + public XmlWhitespaceSensitivity XmlWhitespaceSensitivity { get; set; } = + XmlWhitespaceSensitivity.Strict; public const int WidthUsedByTests = 100; diff --git a/Src/CSharpier.Core/Xml/XNodePrinters/Attributes.cs b/Src/CSharpier.Core/Xml/XNodePrinters/Attributes.cs index 2f1a8d5ae..4120605e6 100644 --- a/Src/CSharpier.Core/Xml/XNodePrinters/Attributes.cs +++ b/Src/CSharpier.Core/Xml/XNodePrinters/Attributes.cs @@ -42,7 +42,7 @@ public static Doc Print(RawNode rawNode, PrintingContext context) */ ( rawNode.Nodes.Count != 0 - && Tag.NeedsToBorrowParentOpeningTagEndMarker(rawNode.Nodes.First()) + && Tag.NeedsToBorrowParentOpeningTagEndMarker(rawNode.Nodes.First(), context) ) || doNotBreakAttributes ) { diff --git a/Src/CSharpier.Core/Xml/XNodePrinters/Element.cs b/Src/CSharpier.Core/Xml/XNodePrinters/Element.cs index 8941a1811..59cd81310 100644 --- a/Src/CSharpier.Core/Xml/XNodePrinters/Element.cs +++ b/Src/CSharpier.Core/Xml/XNodePrinters/Element.cs @@ -34,19 +34,27 @@ Doc PrintLineBeforeChildren() } if ( - rawNode.Nodes.FirstOrDefault() is - { NodeType: XmlNodeType.Text, Value: ['\n', ..] or ['\r', ..] } + context.Options.XmlWhitespaceSensitivity is XmlWhitespaceSensitivity.Strict + && rawNode.Nodes.FirstOrDefault() + is { NodeType: XmlNodeType.Text, Value: ['\n', ..] or ['\r', ..] } ) { return Doc.LiteralLine; } - if (rawNode.Attributes.Length == 0 && rawNode.Nodes is [{ NodeType: XmlNodeType.Text }]) + if ( + rawNode.Attributes.Length == 0 + && rawNode.Nodes is [{ NodeType: XmlNodeType.Text }] + && context.Options.XmlWhitespaceSensitivity is XmlWhitespaceSensitivity.Strict + ) { return Doc.Null; } - if (rawNode.Nodes.Any(o => o.NodeType is XmlNodeType.Text && o.Value.Contains('\n'))) + if ( + rawNode.Nodes.Count > 1 + && rawNode.Nodes.Any(o => o.NodeType is XmlNodeType.Text && o.Value.Contains('\n')) + ) { return Doc.HardLine; } @@ -62,14 +70,19 @@ Doc PrintLineAfterChildren() return Doc.IfBreak(Doc.SoftLine, "", attrGroupId); } - if (rawNode.Attributes.Length == 0 && rawNode.Nodes is [{ NodeType: XmlNodeType.Text }]) + if ( + rawNode.Attributes.Length == 0 + && rawNode.Nodes is [{ NodeType: XmlNodeType.Text }] + && context.Options.XmlWhitespaceSensitivity is XmlWhitespaceSensitivity.Strict + ) { return Doc.Null; } if ( - rawNode.Nodes is [{ NodeType: XmlNodeType.Text }] - && rawNode.Nodes[0].Value.TrimEnd(' ')[^1] is 'r' or '\n' + context.Options.XmlWhitespaceSensitivity is XmlWhitespaceSensitivity.Strict + && rawNode.Nodes is [{ NodeType: XmlNodeType.Text }] + && rawNode.Nodes[0].Value.TrimEnd(' ')[^1] is '\r' or '\n' ) { return Doc.Null; diff --git a/Src/CSharpier.Core/Xml/XNodePrinters/ElementChildren.cs b/Src/CSharpier.Core/Xml/XNodePrinters/ElementChildren.cs index 0db54e9d4..913090511 100644 --- a/Src/CSharpier.Core/Xml/XNodePrinters/ElementChildren.cs +++ b/Src/CSharpier.Core/Xml/XNodePrinters/ElementChildren.cs @@ -12,10 +12,10 @@ public static Doc Print(RawNode node, PrintingContext context) var groupIds = new List(); foreach (var _ in node.Nodes) { - groupIds.Add(context.GroupFor("symbol")); + groupIds.Add(context.GroupFor("children group")); } - var result = new DocListBuilder(node.Nodes.Count * 5); + var result = new List(); var x = 0; foreach (var childNode in node.Nodes) { @@ -25,10 +25,10 @@ public static Doc Print(RawNode node, PrintingContext context) continue; } - var prevParts = new DocListBuilder(2); - var leadingParts = new DocListBuilder(2); - var trailingParts = new DocListBuilder(2); - var nextParts = new DocListBuilder(2); + var prevParts = new List(); + var leadingParts = new List(); + var trailingParts = new List(); + var nextParts = new List(); var prevBetweenLine = childNode.PreviousNode is not null ? PrintBetweenLine(childNode.PreviousNode, childNode) @@ -50,7 +50,14 @@ public static Doc Print(RawNode node, PrintingContext context) } else { - leadingParts.Add(Doc.IfBreak(Doc.Null, Doc.SoftLine, groupIds[x - 1])); + if (groupIds.Count > 1) + { + leadingParts.Add(Doc.IfBreak(Doc.Null, Doc.SoftLine, groupIds[x - 1])); + } + else + { + leadingParts.Add(prevBetweenLine); + } } } @@ -69,22 +76,22 @@ public static Doc Print(RawNode node, PrintingContext context) } } - result.Add(prevParts.AsSpan()); + result.AddRange(prevParts); result.Add( Doc.Group( - Doc.Concat(ref leadingParts), + Doc.Concat(leadingParts), Doc.GroupWithId( groupIds[x], PrintChild(childNode, context), - Doc.Concat(ref trailingParts) + Doc.Concat(trailingParts) ) ) ); - result.Add(nextParts.AsSpan()); + result.AddRange(nextParts); x++; } - return Doc.Concat(ref result); + return Doc.Concat(result); } public static Doc PrintChild(RawNode child, PrintingContext context) diff --git a/Src/CSharpier.Core/Xml/XNodePrinters/Node.cs b/Src/CSharpier.Core/Xml/XNodePrinters/Node.cs index 79436b939..2121b075a 100644 --- a/Src/CSharpier.Core/Xml/XNodePrinters/Node.cs +++ b/Src/CSharpier.Core/Xml/XNodePrinters/Node.cs @@ -40,8 +40,8 @@ internal static Doc Print(RawNode node, PrintingContext context) { List doc = [ - Tag.PrintOpeningTagPrefix(node), - GetTextValue(node), + Tag.PrintOpeningTagPrefix(node, context), + GetTextValue(node, context), Tag.PrintClosingTagSuffix(node, context), ]; @@ -77,7 +77,7 @@ or XmlNodeType.CDATA throw new Exception("Need to handle + " + node.NodeType); } - private static Doc GetTextValue(RawNode rawNode) + private static Doc GetTextValue(RawNode rawNode, PrintingContext context) { var textValue = rawNode.Value; @@ -86,6 +86,19 @@ private static Doc GetTextValue(RawNode rawNode) return Doc.Null; } + if (context.Options.XmlWhitespaceSensitivity is XmlWhitespaceSensitivity.Ignore) + { + if (rawNode.PreviousNode is null) + { + textValue = textValue.TrimStart(); + } + + if (rawNode.NextNode is null) + { + textValue = textValue.TrimEnd(); + } + } + if (rawNode.Parent?.Nodes.First() == rawNode) { if (textValue[0] is '\r') diff --git a/Src/CSharpier.Core/Xml/XNodePrinters/Tag.cs b/Src/CSharpier.Core/Xml/XNodePrinters/Tag.cs index 1a0afb172..07d40b372 100644 --- a/Src/CSharpier.Core/Xml/XNodePrinters/Tag.cs +++ b/Src/CSharpier.Core/Xml/XNodePrinters/Tag.cs @@ -11,7 +11,7 @@ public static Doc PrintOpeningTag(RawNode rawNode, PrintingContext context) return Doc.Concat( PrintOpeningTagStart(rawNode, context), Attributes.Print(rawNode, context), - rawNode.IsEmpty ? Doc.Null : PrintOpeningTagEnd(rawNode) + rawNode.IsEmpty ? Doc.Null : PrintOpeningTagEnd(rawNode, context) ); } @@ -22,23 +22,23 @@ rawNode.PreviousNode is not null && NeedsToBorrowNextOpeningTagStartMarker(rawNode.PreviousNode) ? Doc.Null : Doc.Concat( - PrintOpeningTagPrefix(rawNode), + PrintOpeningTagPrefix(rawNode, context), PrintOpeningTagStartMarker(rawNode, context) ); } - private static Doc PrintOpeningTagEnd(RawNode rawNode) + private static Doc PrintOpeningTagEnd(RawNode rawNode, PrintingContext context) { return rawNode.Nodes.FirstOrDefault() is { } firstNode - && NeedsToBorrowParentOpeningTagEndMarker(firstNode) + && NeedsToBorrowParentOpeningTagEndMarker(firstNode, context) ? Doc.Null : ">"; } - public static Doc PrintOpeningTagPrefix(RawNode rawNode) + public static Doc PrintOpeningTagPrefix(RawNode rawNode, PrintingContext context) { - return NeedsToBorrowParentOpeningTagEndMarker(rawNode) ? ">" : ""; + return NeedsToBorrowParentOpeningTagEndMarker(rawNode, context) ? ">" : ""; } public static Doc PrintClosingTag(RawNode rawNode, PrintingContext context) @@ -53,7 +53,7 @@ public static Doc PrintClosingTagStart(RawNode rawNode, PrintingContext context) { var lastChild = rawNode.Nodes.LastOrDefault(); - return lastChild is not null && PrintParentClosingTagStartWithContent(lastChild) + return lastChild is not null && PrintParentClosingTagStartWithContent(lastChild, context) ? Doc.Null : PrintClosingTagStartMarker(rawNode, context); } @@ -78,7 +78,7 @@ public static Doc PrintClosingTagEndMarker(RawNode rawNode) public static Doc PrintClosingTagSuffix(RawNode rawNode, PrintingContext context) { - return PrintParentClosingTagStartWithContent(rawNode) + return PrintParentClosingTagStartWithContent(rawNode, context) ? PrintClosingTagStartMarker(rawNode.Parent!, context) : NeedsToBorrowNextOpeningTagStartMarker(rawNode) ? PrintOpeningTagStartMarker(rawNode.NextNode!, context) @@ -113,7 +113,10 @@ private static bool NeedsToBorrowNextOpeningTagStartMarker(RawNode rawNode) ; } - private static bool PrintParentClosingTagStartWithContent(RawNode rawNode) + private static bool PrintParentClosingTagStartWithContent( + RawNode rawNode, + PrintingContext context + ) { /* *

@@ -147,7 +150,8 @@ Life is demanding. */ - return rawNode.NextNode is null + return context.Options.XmlWhitespaceSensitivity is XmlWhitespaceSensitivity.Strict + && rawNode.NextNode is null && rawNode.IsTextLike() && rawNode.GetLastDescendant() is { NodeType: XmlNodeType.Text } textNode && ( @@ -157,7 +161,10 @@ Life is demanding. ); } - public static bool NeedsToBorrowParentOpeningTagEndMarker(RawNode rawNode) + public static bool NeedsToBorrowParentOpeningTagEndMarker( + RawNode rawNode, + PrintingContext context + ) { /* *

o.Name)) { var propertyName = propertySymbol.Name; @@ -228,10 +240,13 @@ private static void GenerateMethod(StringBuilder sourceBuilder, INamedTypeSymbol } else { - var compare = propertyType.Name == nameof(SyntaxTokenList) ? "Compare" : "null"; + var compare = + propertyType.Name == nameof(SyntaxTokenList) + ? "CompareFunc" + : "static (_, _) => default"; if (propertyName == "Modifiers") { - propertyName += ".OrderBy(o => o.Text).ToList()"; + propertyName += ".OrderBy(o => o.Text).ToArray()"; } sourceBuilder.AppendLine( @@ -249,13 +264,13 @@ private static void GenerateMethod(StringBuilder sourceBuilder, INamedTypeSymbol ) { sourceBuilder.AppendLine( - $" result = this.CompareLists(originalNode.{propertyName}, formattedNode.{propertyName}, null, o => o.Span, originalNode.Span, formattedNode.Span);" + $" result = this.CompareLists(originalNode.{propertyName}, formattedNode.{propertyName}, static (_, _) => default, o => o.Span, originalNode.Span, formattedNode.Span);" ); sourceBuilder.AppendLine(" if (result.IsInvalid) return result;"); // Omit the last separator when comparing the original node with the formatted node, as it legitimately may be added or removed sourceBuilder.AppendLine( - $" result = this.CompareLists(originalNode.{propertyName}.GetSeparators().Take(originalNode.{propertyName}.Count() - 1).ToList(), formattedNode.{propertyName}.GetSeparators().Take(formattedNode.{propertyName}.Count() - 1).ToList(), Compare, o => o.Span, originalNode.Span, formattedNode.Span);" + $" result = this.CompareLists(AllSeparatorsButLast(originalNode.{propertyName}), AllSeparatorsButLast(formattedNode.{propertyName}), CompareFunc, o => o.Span, originalNode.Span, formattedNode.Span);" ); sourceBuilder.AppendLine(" if (result.IsInvalid) return result;"); } diff --git a/Src/CSharpier.Playground/ClientApp/src/AppContext.ts b/Src/CSharpier.Playground/ClientApp/src/AppContext.ts index 246ca561c..ef0c1ba03 100644 --- a/Src/CSharpier.Playground/ClientApp/src/AppContext.ts +++ b/Src/CSharpier.Playground/ClientApp/src/AppContext.ts @@ -37,6 +37,7 @@ class AppState { showAst = getBoolean("showAst", false); showDoc = getBoolean("showDoc", false); hideNull = getBoolean("hideNull", false); + xmlWhitespaceSensitivity = getString("xmlWhitespaceSensitivity", "Strict"); doc = ""; isLoading = false; hasErrors = false; @@ -53,6 +54,11 @@ class AppState { this.formatter = value; }; + setXmlWhitespaceSensitivity = (value: string) => { + window.sessionStorage.setItem("xmlWhitespaceSensitivity", value); + this.xmlWhitespaceSensitivity = value; + }; + setIndentSize = (value: number) => { window.sessionStorage.setItem("indentSize", value.toString(10)); this.indentSize = value; @@ -119,6 +125,7 @@ class AppState { this.indentSize, this.useTabs, this.formatter, + this.xmlWhitespaceSensitivity, ); runInAction(() => { diff --git a/Src/CSharpier.Playground/ClientApp/src/Controls.tsx b/Src/CSharpier.Playground/ClientApp/src/Controls.tsx index 3eec52dda..99c727255 100644 --- a/Src/CSharpier.Playground/ClientApp/src/Controls.tsx +++ b/Src/CSharpier.Playground/ClientApp/src/Controls.tsx @@ -13,6 +13,8 @@ export const Controls = observer(() => { setUseTabs, formatter, setFormatter, + xmlWhitespaceSensitivity, + setXmlWhitespaceSensitivity, showDoc, setShowDoc, hideNull, @@ -42,11 +44,51 @@ export const Controls = observer(() => { Use Tabs - + + + + {formatter === "XML" && ( + <> + + + + + + )}

Debug

diff --git a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts index 88b3c0af4..c74dfa8be 100644 --- a/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts +++ b/Src/CSharpier.Playground/ClientApp/src/FormatCode.ts @@ -8,11 +8,12 @@ export const formatCode = async ( indentSize: number, useTabs: boolean, formatter: string, + xmlWhitespaceSensitivity: string, ) => { const makeRequest = async () => { const response = await fetch("/Format", { method: "POST", - body: JSON.stringify({ code, printWidth, indentSize, useTabs, formatter }), + body: JSON.stringify({ code, printWidth, indentSize, useTabs, formatter, xmlWhitespaceSensitivity }), headers: { "Content-Type": "application/json", "cache-control": "no-cache", diff --git a/Src/CSharpier.Playground/Controllers/FormatController.cs b/Src/CSharpier.Playground/Controllers/FormatController.cs index 8bf0bf7f1..c92be1a18 100644 --- a/Src/CSharpier.Playground/Controllers/FormatController.cs +++ b/Src/CSharpier.Playground/Controllers/FormatController.cs @@ -32,6 +32,7 @@ public class PostModel public int IndentSize { get; set; } public bool UseTabs { get; set; } public string Formatter { get; set; } = string.Empty; + public string XmlWhitespaceSensitivity { get; set; } = string.Empty; } [HttpPost] @@ -54,6 +55,12 @@ CancellationToken cancellationToken Width = model.PrintWidth, IndentSize = model.IndentSize, UseTabs = model.UseTabs, + XmlWhitespaceSensitivity = model.XmlWhitespaceSensitivity switch + { + "Ignore" => XmlWhitespaceSensitivity.Ignore, + "Xaml" => XmlWhitespaceSensitivity.Xaml, + _ => XmlWhitespaceSensitivity.Strict, + }, }, cancellationToken ); diff --git a/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs b/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs index 259a73b38..ffd281939 100644 --- a/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs +++ b/Src/CSharpier.Tests/CSharp/SyntaxPrinter/CSharpierIgnoreTests.cs @@ -1,4 +1,5 @@ using AwesomeAssertions; +using CSharpier.Core; using CSharpier.Core.CSharp.SyntaxPrinter; namespace CSharpier.Tests.CSharp.SyntaxPrinter; @@ -71,6 +72,7 @@ private static string PrintWithoutFormatting(string code) LineEnding = Environment.NewLine, IndentSize = 4, UseTabs = false, + XmlWhitespaceSensitivity = XmlWhitespaceSensitivity.Strict, }, } ) diff --git a/Src/CSharpier.Tests/Cli/IgnoreFileTests.cs b/Src/CSharpier.Tests/Cli/IgnoreFileTests.cs index 75e3de8f3..7398711fe 100644 --- a/Src/CSharpier.Tests/Cli/IgnoreFileTests.cs +++ b/Src/CSharpier.Tests/Cli/IgnoreFileTests.cs @@ -621,7 +621,13 @@ private void GitBasedTest(string gitignore, string[] files) var gitIgnoredFiles = files.Where(o => !gitUntrackedFiles.Contains(o)); var ignoreFile = IgnoreFile - .CreateAsync(this.gitRepository.RepoPath, this.fileSystem, null, CancellationToken.None) + .CreateAsync( + this.gitRepository.RepoPath, + this.fileSystem, + null, + null, + CancellationToken.None + ) .GetAwaiter() .GetResult(); diff --git a/Src/CSharpier.Tests/Cli/Options/EditorConfigFileParserTests.cs b/Src/CSharpier.Tests/Cli/Options/CSharpierConfigParserTests.cs similarity index 97% rename from Src/CSharpier.Tests/Cli/Options/EditorConfigFileParserTests.cs rename to Src/CSharpier.Tests/Cli/Options/CSharpierConfigParserTests.cs index a143191ed..6315eda64 100644 --- a/Src/CSharpier.Tests/Cli/Options/EditorConfigFileParserTests.cs +++ b/Src/CSharpier.Tests/Cli/Options/CSharpierConfigParserTests.cs @@ -4,7 +4,7 @@ namespace CSharpier.Tests.Cli.Options; -public class EditorConfigFileParserTests +public class CSharpierConfigParserTests { [Test] public void Should_Parse_Yaml_With_Overrides() diff --git a/Src/CSharpier.Tests/FormattingTests/BaseTest.cs b/Src/CSharpier.Tests/FormattingTests/BaseTest.cs index 9aa1162c4..b6512fc00 100644 --- a/Src/CSharpier.Tests/FormattingTests/BaseTest.cs +++ b/Src/CSharpier.Tests/FormattingTests/BaseTest.cs @@ -29,22 +29,47 @@ public void BuildTests(DynamicTestBuilderContext context, string folder) foreach (var group in files.GroupBy(o => o.Directory!.Name)) { - var formattingType = group.Key; - foreach (var file in group) { var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file.Name); var useTabs = fileNameWithoutExtension.EndsWith("_Tabs", StringComparison.Ordinal); + var directoryName = file.Directory!.Name; + var xmlWhitespaceSensitivity = XmlWhitespaceSensitivity.Strict; + if (directoryName.Contains('_')) + { + var parts = directoryName.Split('_'); + directoryName = parts[0]; + xmlWhitespaceSensitivity = + parts[1] is "ignore" ? XmlWhitespaceSensitivity.Ignore + : parts[1] is "xaml" ? XmlWhitespaceSensitivity.Xaml + : XmlWhitespaceSensitivity.Strict; + } + + var formatter = directoryName switch + { + "cs" => Formatter.CSharp, + "csx" => Formatter.CSharpScript, + "xml" => Formatter.XML, + _ => Formatter.Unknown, + }; + + var printerOptions = new PrinterOptions(formatter) + { + UseTabs = useTabs, + Width = PrinterOptions.WidthUsedByTests, + IndentSize = formatter == Formatter.XML ? 2 : 4, + XmlWhitespaceSensitivity = xmlWhitespaceSensitivity, + }; context.AddTest( new DynamicTest { - TestMethod = @class => @class.RunTest(string.Empty, string.Empty, false), + TestMethod = @class => @class.RunTest(string.Empty, string.Empty, null!), TestMethodArguments = [ fileNameWithoutExtension, file.Directory!.Name, - useTabs, + printerOptions, ], DisplayName = fileNameWithoutExtension, } @@ -53,13 +78,17 @@ public void BuildTests(DynamicTestBuilderContext context, string folder) } } - public async Task RunTest(string fileName, string fileExtensionWithoutDot, bool useTabs = false) + internal async Task RunTest( + string fileName, + string directoryName, + PrinterOptions printerOptions + ) { var filePath = Path.Combine( this.rootDirectory.FullName, "FormattingTests", "TestFiles", - fileExtensionWithoutDot, + directoryName, fileName + ".test" ); var fileReaderResult = await FileReader.ReadFileAsync( @@ -68,22 +97,9 @@ public async Task RunTest(string fileName, string fileExtensionWithoutDot, bool CancellationToken.None ); - var formatter = fileExtensionWithoutDot switch - { - "cs" => Formatter.CSharp, - "csx" => Formatter.CSharpScript, - "xml" => Formatter.XML, - _ => Formatter.Unknown, - }; - var result = await CodeFormatter.FormatAsync( fileReaderResult.FileContents, - new PrinterOptions(formatter) - { - Width = PrinterOptions.WidthUsedByTests, - UseTabs = useTabs, - IndentSize = formatter == Formatter.XML ? 2 : 4, - }, + printerOptions, CancellationToken.None ); diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/CData.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/CData.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/CData.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/CData.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/CData_EdgeCase.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/CData_EdgeCase.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/CData_EdgeCase.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/CData_EdgeCase.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/CData_InText.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/CData_InText.test new file mode 100644 index 000000000..285e169af --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/CData_InText.test @@ -0,0 +1,4 @@ + + SomeText + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Comments.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Comments.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Comments.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Comments.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Conditions.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Conditions.test new file mode 100644 index 000000000..fea994830 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Conditions.test @@ -0,0 +1,11 @@ + + + + v4.5.2 + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/DoNotBreak.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/DoNotBreak.test new file mode 100644 index 000000000..56bfda863 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/DoNotBreak.test @@ -0,0 +1,8 @@ + + + + ..\..\packages\Newtonsoft.Json.Bson.1.0.2\lib\net45\Newtonsoft.Json.Bson.dll + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements.expected.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements.expected.test new file mode 100644 index 000000000..ccedaed25 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements.expected.test @@ -0,0 +1,10 @@ + + + TextValue + + + TextValue + + TextValue + TextValue + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements.test new file mode 100644 index 000000000..89f1ec8d6 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements.test @@ -0,0 +1,10 @@ + + TextValue + TextValue + + TextValue + + + TextValue + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements2.expected.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements2.expected.test new file mode 100644 index 000000000..ccedaed25 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements2.expected.test @@ -0,0 +1,10 @@ + + + TextValue + + + TextValue + + TextValue + TextValue + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements2.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements2.test new file mode 100644 index 000000000..ccedaed25 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements2.test @@ -0,0 +1,10 @@ + + + TextValue + + + TextValue + + TextValue + TextValue + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements_HtmlValues.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements_HtmlValues.test new file mode 100644 index 000000000..5e89039bd --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/Elements_HtmlValues.test @@ -0,0 +1,12 @@ + + + Some text +
url.com + more text. + + + Some long text to make things break + url.com + more text. + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EmptyLines.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EmptyLines.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EmptyLines.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EmptyLines.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EmptyLines_RemoveSome.expected.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EmptyLines_RemoveSome.expected.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EmptyLines_RemoveSome.expected.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EmptyLines_RemoveSome.expected.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EmptyLines_RemoveSome.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EmptyLines_RemoveSome.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EmptyLines_RemoveSome.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EmptyLines_RemoveSome.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EncodedValues.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EncodedValues.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/EncodedValues.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/EncodedValues.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/LongAttributes.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/LongAttributes.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/LongAttributes.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/LongAttributes.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/PrefixedElementNames.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/PrefixedElementNames.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/PrefixedElementNames.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/PrefixedElementNames.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/RetainXmlElement.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/RetainXmlElement.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/RetainXmlElement.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/RetainXmlElement.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/StrictWhitespace.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/StrictWhitespace.test new file mode 100644 index 000000000..d8d9226df --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/StrictWhitespace.test @@ -0,0 +1,37 @@ + + + Because whitespace is strict The indentation of the closing element can't + change + + Shorter Text with indentation that can't change + + + A + + containing a value that reflects the sort order of + + as compared to + + . The following table defines the conditions under which the returned + value is a negative number, zero, or a positive number. + + + Some text that ends with a space. + + Some loooooooooooooooooooooooooooooooooong text with this +
+ and a space after this. +
+ + The current instance is read-only and a set operation was attempted. + + + public void MethodName() { } + + + + + +
diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/XProcessingInstruction.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/XProcessingInstruction.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/XProcessingInstruction.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/XProcessingInstruction.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/XmlDeclarationWorksWithMapping.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/XmlDeclarationWorksWithMapping.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/XmlDeclarationWorksWithMapping.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/XmlDeclarationWorksWithMapping.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/XmlDocumentType.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/XmlDocumentType.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/XmlDocumentType.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_ignore/XmlDocumentType.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Attributes.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Attributes.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Attributes.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Attributes.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/BasicProject.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/BasicProject.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/BasicProject.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/BasicProject.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData.test new file mode 100644 index 000000000..687730eda --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData.test @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData_EdgeCase.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData_EdgeCase.test new file mode 100644 index 000000000..1878d288c --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData_EdgeCase.test @@ -0,0 +1,5 @@ + + + entryPointFullTypeName + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/CData_InText.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData_InText.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/CData_InText.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/CData_InText.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Comments.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Comments.test new file mode 100644 index 000000000..b6a2db72e --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Comments.test @@ -0,0 +1,12 @@ + + + + + CA1031; + IDE0005; + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Conditions.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Conditions.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Conditions.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Conditions.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/DoNotBreak.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/DoNotBreak.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/DoNotBreak.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/DoNotBreak.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/DoubleQuotesForced.expected.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/DoubleQuotesForced.expected.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/DoubleQuotesForced.expected.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/DoubleQuotesForced.expected.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/DoubleQuotesForced.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/DoubleQuotesForced.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/DoubleQuotesForced.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/DoubleQuotesForced.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Element_DoesNotAddPrefix.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Element_DoesNotAddPrefix.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Element_DoesNotAddPrefix.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Element_DoesNotAddPrefix.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Elements.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Elements.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Elements.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Elements.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Elements_HtmlValues.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Elements_HtmlValues.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/Elements_HtmlValues.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/Elements_HtmlValues.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines.test new file mode 100644 index 000000000..48a72189a --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines.test @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines_RemoveSome.expected.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines_RemoveSome.expected.test new file mode 100644 index 000000000..f7c26ecb7 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines_RemoveSome.expected.test @@ -0,0 +1,5 @@ + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines_RemoveSome.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines_RemoveSome.test new file mode 100644 index 000000000..7ac67cf30 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EmptyLines_RemoveSome.test @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EncodedValues.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EncodedValues.test new file mode 100644 index 000000000..5ad7a455f --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/EncodedValues.test @@ -0,0 +1,6 @@ + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/LongAttributes.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/LongAttributes.test new file mode 100644 index 000000000..6ef7a04ba --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/LongAttributes.test @@ -0,0 +1,98 @@ + + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/PrefixedElementNames.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/PrefixedElementNames.test new file mode 100644 index 000000000..6eed22404 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/PrefixedElementNames.test @@ -0,0 +1,6 @@ + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/RetainXmlElement.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/RetainXmlElement.test new file mode 100644 index 000000000..820a64750 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/RetainXmlElement.test @@ -0,0 +1,2 @@ + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml/StrictWhitespace.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/StrictWhitespace.test similarity index 100% rename from Src/CSharpier.Tests/FormattingTests/TestFiles/xml/StrictWhitespace.test rename to Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/StrictWhitespace.test diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XProcessingInstruction.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XProcessingInstruction.test new file mode 100644 index 000000000..692c52632 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XProcessingInstruction.test @@ -0,0 +1,3 @@ + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XmlDeclarationWorksWithMapping.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XmlDeclarationWorksWithMapping.test new file mode 100644 index 000000000..5e9f4ffe7 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XmlDeclarationWorksWithMapping.test @@ -0,0 +1,6 @@ + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XmlDocumentType.test b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XmlDocumentType.test new file mode 100644 index 000000000..0f8095c88 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/TestFiles/xml_strict/XmlDocumentType.test @@ -0,0 +1,6 @@ + + + + + + diff --git a/Src/CSharpier.Tests/FormattingTests/XmlFormatting.cs b/Src/CSharpier.Tests/FormattingTests/XmlIgnoreFormatting.cs similarity index 56% rename from Src/CSharpier.Tests/FormattingTests/XmlFormatting.cs rename to Src/CSharpier.Tests/FormattingTests/XmlIgnoreFormatting.cs index 390d7f837..d45bc229d 100644 --- a/Src/CSharpier.Tests/FormattingTests/XmlFormatting.cs +++ b/Src/CSharpier.Tests/FormattingTests/XmlIgnoreFormatting.cs @@ -1,10 +1,10 @@ namespace CSharpier.Tests.FormattingTests; -public class XmlFormatting : BaseTest +public class XmlIgnoreFormatting : BaseTest { [DynamicTestBuilder] public void BuildTests(DynamicTestBuilderContext context) { - this.BuildTests(context, "xml"); + this.BuildTests(context, "xml_ignore"); } } diff --git a/Src/CSharpier.Tests/FormattingTests/XmlStrictFormatting.cs b/Src/CSharpier.Tests/FormattingTests/XmlStrictFormatting.cs new file mode 100644 index 000000000..d71d5a429 --- /dev/null +++ b/Src/CSharpier.Tests/FormattingTests/XmlStrictFormatting.cs @@ -0,0 +1,10 @@ +namespace CSharpier.Tests.FormattingTests; + +public class XmlStrictFormatting : BaseTest +{ + [DynamicTestBuilder] + public void BuildTests(DynamicTestBuilderContext context) + { + this.BuildTests(context, "xml_strict"); + } +} diff --git a/Src/CSharpier.Tests/OptionsProviderTests.cs b/Src/CSharpier.Tests/OptionsProviderTests.cs index 23ca9259e..b7e946150 100644 --- a/Src/CSharpier.Tests/OptionsProviderTests.cs +++ b/Src/CSharpier.Tests/OptionsProviderTests.cs @@ -97,16 +97,20 @@ public async Task Should_Return_Json_Extension_Options() var context = new TestContext(); context.WhenAFileExists( "c:/test/.csharpierrc.json", - @"{ - ""printWidth"": 10, - ""endOfLine"": ""crlf"" -}" + """ +{ + "printWidth": 10, + "endOfLine": "crlf", + "xmlWhitespaceSensitivity": "xaml" +} +""" ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); result.Width.Should().Be(10); result.EndOfLine.Should().Be(EndOfLine.CRLF); + result.XmlWhitespaceSensitivity.Should().Be(XmlWhitespaceSensitivity.Xaml); } [Test] @@ -117,16 +121,18 @@ public async Task Should_Return_Yaml_Extension_Options(string extension) var context = new TestContext(); context.WhenAFileExists( $"c:/test/.csharpierrc.{extension}", - @" + """ printWidth: 10 endOfLine: crlf -" +xmlWhitespaceSensitivity: xaml +""" ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); result.Width.Should().Be(10); result.EndOfLine.Should().Be(EndOfLine.CRLF); + result.XmlWhitespaceSensitivity.Should().Be(XmlWhitespaceSensitivity.Xaml); } [Test] @@ -251,11 +257,12 @@ public async Task Should_Return_IndentSize_For_Override_With_Yaml() context.WhenAFileExists( "c:/test/.csharpierrc", """ - overrides: - - files: "*.{override,another}" - formatter: "csharp" - indentSize: 2 - """ +overrides: + - files: "*.{override,another}" + formatter: "csharp" + indentSize: 2 + xmlWhitespaceSensitivity: ignore +""" ); var result = await context.CreateProviderAndGetOptionsFor( @@ -264,6 +271,7 @@ public async Task Should_Return_IndentSize_For_Override_With_Yaml() ); result.IndentSize.Should().Be(2); + result.XmlWhitespaceSensitivity.Should().Be(XmlWhitespaceSensitivity.Ignore); } [Test] @@ -273,16 +281,17 @@ public async Task Should_Return_IndentSize_For_Override_With_Json() context.WhenAFileExists( "c:/test/.csharpierrc", """ - { - "overrides": [ - { - "files": "*.{override,another}", - "formatter": "csharp", - "indentSize": 2 - } - ] - } - """ +{ + "overrides": [ + { + "files": "*.{override,another}", + "formatter": "csharp", + "indentSize": 2, + "xmlWhitespaceSensitivity": "ignore" + } + ] +} +""" ); var result = await context.CreateProviderAndGetOptionsFor( @@ -291,6 +300,7 @@ public async Task Should_Return_IndentSize_For_Override_With_Json() ); result.IndentSize.Should().Be(2); + result.XmlWhitespaceSensitivity.Should().Be(XmlWhitespaceSensitivity.Ignore); } [Test] @@ -332,13 +342,15 @@ public async Task Should_Support_EditorConfig_Basic() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*] -indent_style = space -indent_size = 2 -max_line_length = 10 -end_of_line = crlf -" + """ + + [*] + indent_style = space + indent_size = 2 + max_line_length = 10 + end_of_line = crlf + csharpier_xml_whitespace_sensitivity = xaml + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -347,6 +359,7 @@ public async Task Should_Support_EditorConfig_Basic() result.IndentSize.Should().Be(2); result.Width.Should().Be(10); result.EndOfLine.Should().Be(EndOfLine.CRLF); + result.XmlWhitespaceSensitivity.Should().Be(XmlWhitespaceSensitivity.Xaml); } [Test] @@ -355,19 +368,21 @@ public async Task Should_Support_EditorConfig_With_Comments() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -# EditorConfig is awesome: https://EditorConfig.org + """ -# top-most EditorConfig file -root = true + # EditorConfig is awesome: https://EditorConfig.org -[*] -indent_style = space -indent_size = 2 -max_line_length = 10 -; Windows-style line endings -end_of_line = crlf -" + # top-most EditorConfig file + root = true + + [*] + indent_style = space + indent_size = 2 + max_line_length = 10 + ; Windows-style line endings + end_of_line = crlf + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -384,13 +399,15 @@ public async Task Should_Support_EditorConfig_With_Duplicated_Sections() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*] -indent_size = 2 + """ -[*] -indent_size = 4 -" + [*] + indent_size = 2 + + [*] + indent_size = 4 + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -404,11 +421,13 @@ public async Task Should_Support_EditorConfig_With_Duplicated_Rules() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*] -indent_size = 2 -indent_size = 4 -" + """ + + [*] + indent_size = 2 + indent_size = 4 + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -422,10 +441,12 @@ public async Task Should_Not_Fail_With_Bad_EditorConfig() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[* -indent_size== -" + """ + + [* + indent_size== + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -441,11 +462,13 @@ public async Task Should_Support_EditorConfig_Tabs(string propertyName) var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - $@" - [*] - indent_style = tab - {propertyName} = 2 - " + $""" + + [*] + indent_style = tab + {propertyName} = 2 + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -460,12 +483,14 @@ public async Task Should_Support_EditorConfig_Tabs_With_Tab_Width() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" - [*] - indent_style = tab - indent_size = 1 - tab_width = 3 - " + """ + + [*] + indent_style = tab + indent_size = 1 + tab_width = 3 + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -480,11 +505,13 @@ public async Task Should_Support_EditorConfig_Tabs_With_Indent_Size_Tab() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" - [*] - indent_size = tab - tab_width = 3 - " + """ + + [*] + indent_size = tab + tab_width = 3 + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -498,19 +525,23 @@ public async Task Should_Support_EditorConfig_Tabs_With_Multiple_Files() var context = new TestContext(); context.WhenAFileExists( "c:/test/subfolder/.editorconfig", - @" - [*] - indent_size = 1 - " + """ + + [*] + indent_size = 1 + + """ ); context.WhenAFileExists( "c:/test/.editorconfig", - @" - [*] - indent_size = 2 - max_line_length = 10 - " + """ + + [*] + indent_size = 2 + max_line_length = 10 + + """ ); var result = await context.CreateProviderAndGetOptionsFor( @@ -527,18 +558,22 @@ public async Task Should_Support_EditorConfig_Tabs_With_Multiple_Files_And_Unset var context = new TestContext(); context.WhenAFileExists( "c:/test/subfolder/.editorconfig", - @" - [*] - indent_size = unset - " + """ + + [*] + indent_size = unset + + """ ); context.WhenAFileExists( "c:/test/.editorconfig", - @" - [*] - indent_size = 2 - " + """ + + [*] + indent_size = 2 + + """ ); var result = await context.CreateProviderAndGetOptionsFor( @@ -554,20 +589,24 @@ public async Task Should_Support_EditorConfig_Root() var context = new TestContext(); context.WhenAFileExists( "c:/test/subfolder/.editorconfig", - @" - root = true + """ - [*] - indent_size = 2 - " + root = true + + [*] + indent_size = 2 + + """ ); context.WhenAFileExists( "c:/test/.editorconfig", - @" - [*] - max_line_length = 2 - " + """ + + [*] + max_line_length = 2 + + """ ); var result = await context.CreateProviderAndGetOptionsFor( @@ -583,13 +622,15 @@ public async Task Should_Support_EditorConfig_Tabs_With_Globs() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*] -indent_size = 1 + """ + + [*] + indent_size = 1 + + [*.cs] + indent_size = 2 -[*.cs] -indent_size = 2 -" + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -602,13 +643,15 @@ public async Task Should_Support_EditorConfig_Tabs_With_Glob_Braces() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*] -indent_size = 1 + """ + + [*] + indent_size = 1 -[*.{cs}] -indent_size = 2 -" + [*.{cs}] + indent_size = 2 + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -621,13 +664,15 @@ public async Task Should_Support_EditorConfig_Tabs_With_Glob_Braces_Multiples() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*] -indent_size = 1 + """ + + [*] + indent_size = 1 + + [*.{csx,cs}] + indent_size = 2 -[*.{csx,cs}] -indent_size = 2 -" + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -635,7 +680,7 @@ public async Task Should_Support_EditorConfig_Tabs_With_Glob_Braces_Multiples() } [Test] - public async Task Should_Return_IndentSize_For_Formatter_In_Editorconfig() + public async Task Should_Return_Overrides_In_Editorconfig() { var context = new TestContext(); context.WhenAFileExists( @@ -644,12 +689,14 @@ public async Task Should_Return_IndentSize_For_Formatter_In_Editorconfig() [*.cst] indent_size = 2 csharpier_formatter = csharp + csharpier_xml_whitespace_sensitivity = ignore """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cst"); result.IndentSize.Should().Be(2); + result.XmlWhitespaceSensitivity.Should().Be(XmlWhitespaceSensitivity.Ignore); } [Test] @@ -658,10 +705,12 @@ public async Task Should_Find_EditorConfig_In_Parent_Directory() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*.cs] -indent_size = 2 -" + """ + + [*.cs] + indent_size = 2 + + """ ); var result = await context.CreateProviderAndGetOptionsFor( @@ -677,10 +726,12 @@ public async Task Should_Prefer_CSharpierrc_In_SameFolder() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*.cs] -indent_size = 2 -" + """ + + [*.cs] + indent_size = 2 + + """ ); context.WhenAFileExists("c:/test/.csharpierrc", "indentSize: 1"); @@ -694,10 +745,12 @@ public async Task Should_Not_Prefer_Closer_EditorConfig() var context = new TestContext(); context.WhenAFileExists( "c:/test/subfolder/.editorconfig", - @" -[*.cs] -indent_size = 2 -" + """ + + [*.cs] + indent_size = 2 + + """ ); context.WhenAFileExists("c:/test/.csharpierrc", "indentSize: 1"); @@ -714,11 +767,13 @@ public async Task Should_Ignore_Invalid_EditorConfig_Lines() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*] -indent_size = 2 -INVALID -" + """ + + [*] + indent_size = 2 + INVALID + + """ ); var result = await context.CreateProviderAndGetOptionsFor("c:/test", "c:/test/test.cs"); @@ -732,18 +787,22 @@ public async Task Should_Not_Ignore_Ignored_EditorConfig() var context = new TestContext(); context.WhenAFileExists( "c:/test/subfolder/.editorconfig", - @" - [*] - indent_size = 2 - " + """ + + [*] + indent_size = 2 + + """ ); context.WhenAFileExists( "c:/test/.editorconfig", - @" - [*] - indent_size = 1 - " + """ + + [*] + indent_size = 1 + + """ ); context.WhenAFileExists("c:/test/.csharpierignore", "/subfolder/.editorconfig"); @@ -761,10 +820,12 @@ public async Task Should_Prefer_Closer_CSharpierrc() var context = new TestContext(); context.WhenAFileExists( "c:/test/.editorconfig", - @" -[*.cs] -indent_size = 2 -" + """ + + [*.cs] + indent_size = 2 + + """ ); context.WhenAFileExists("c:/test/subfolder/.csharpierrc", "indentSize: 1"); @@ -789,6 +850,7 @@ private static void ShouldHaveDefaultXmlOptions(PrinterOptions printerOptions) printerOptions.IndentSize.Should().Be(2); printerOptions.UseTabs.Should().BeFalse(); printerOptions.EndOfLine.Should().Be(EndOfLine.Auto); + printerOptions.XmlWhitespaceSensitivity.Should().Be(XmlWhitespaceSensitivity.Strict); } private sealed class TestContext diff --git a/Src/CSharpier.VSCode/src/FormattingService.ts b/Src/CSharpier.VSCode/src/FormattingService.ts index a1cd8011b..73091f889 100644 --- a/Src/CSharpier.VSCode/src/FormattingService.ts +++ b/Src/CSharpier.VSCode/src/FormattingService.ts @@ -89,7 +89,7 @@ export class FormattingService { private minimalEdit(document: TextDocument, newText: string) { let existingText = document.getText(); - + let i = 0; while (i < existingText.length && i < newText.length && existingText[i] === newText[i]) { ++i;