Skip to content
Open
5 changes: 4 additions & 1 deletion Src/CSharpier.Cli/CommandLineFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 22 additions & 2 deletions Src/CSharpier.Cli/EditorConfig/EditorConfigSections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ internal class EditorConfigSections
printerOptions.EndOfLine = endOfLine;
}

if (resolvedConfiguration.XmlWhitespaceSensitivity is { } xmlWhitespaceSensitivity)
{
printerOptions.XmlWhitespaceSensitivity = xmlWhitespaceSensitivity;
}

return printerOptions;
}

Expand All @@ -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<Section> sections)
Expand Down Expand Up @@ -104,9 +110,23 @@ public ResolvedConfiguration(List<Section> 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;
Expand Down
2 changes: 2 additions & 0 deletions Src/CSharpier.Cli/EditorConfig/Section.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
15 changes: 13 additions & 2 deletions Src/CSharpier.Cli/IgnoreFile.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.IO.Abstractions;
using CSharpier.Cli.DotIgnore;
using CSharpier.Core;
Expand Down Expand Up @@ -35,17 +36,27 @@ public bool IsIgnored(string filePath)
string baseDirectoryPath,
IFileSystem fileSystem,
string? ignorePath,
ConcurrentDictionary<string, IgnoreList>? ignoreCache,
CancellationToken cancellationToken
)
{
Task<IgnoreList> CreateIgnore(string ignoreFilePath, string? overrideBasePath)
async Task<IgnoreList> 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<IgnoreFile?>
Expand Down
10 changes: 10 additions & 0 deletions Src/CSharpier.Cli/Options/ConfigurationFileOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ internal class ConfigurationFileOptions
public int? IndentSize { get; init; }
public bool UseTabs { get; init; }

[JsonConverter(typeof(CaseInsensitiveEnumConverter<XmlWhitespaceSensitivity>))]
public XmlWhitespaceSensitivity XmlWhitespaceSensitivity { get; init; } =
XmlWhitespaceSensitivity.Strict;

[JsonConverter(typeof(CaseInsensitiveEnumConverter<EndOfLine>))]
public EndOfLine EndOfLine { get; init; }

Expand Down Expand Up @@ -38,6 +42,7 @@ out var parsedFormatter
UseTabs = matchingOverride.UseTabs,
Width = matchingOverride.PrintWidth,
EndOfLine = matchingOverride.EndOfLine,
XmlWhitespaceSensitivity = matchingOverride.XmlWhitespaceSensitivity,
};
}

Expand All @@ -50,6 +55,7 @@ out var parsedFormatter
UseTabs = this.UseTabs,
Width = this.PrintWidth,
EndOfLine = this.EndOfLine,
XmlWhitespaceSensitivity = this.XmlWhitespaceSensitivity,
};
}

Expand All @@ -73,6 +79,10 @@ internal class Override
public int IndentSize { get; init; } = 4;
public bool UseTabs { get; init; }

[JsonConverter(typeof(CaseInsensitiveEnumConverter<XmlWhitespaceSensitivity>))]
public XmlWhitespaceSensitivity XmlWhitespaceSensitivity { get; init; } =
XmlWhitespaceSensitivity.Strict;

[JsonConverter(typeof(CaseInsensitiveEnumConverter<EndOfLine>))]
public EndOfLine EndOfLine { get; init; }

Expand Down
11 changes: 10 additions & 1 deletion Src/CSharpier.Cli/Options/OptionsProvider.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,6 +16,7 @@ private readonly ConcurrentDictionary<
string,
CSharpierConfigData?
> csharpierConfigsByDirectory = new();
private readonly ConcurrentDictionary<string, IgnoreList> ignoreWithPathCache = new();
private readonly ConcurrentDictionary<string, IgnoreFile?> ignoreFilesByDirectory = new();
private readonly ConfigurationFileOptions? specifiedConfigFile;
private readonly EditorConfigSections? specifiedEditorConfig;
Expand Down Expand Up @@ -60,6 +62,7 @@ CancellationToken cancellationToken
directoryName,
fileSystem,
ignorePath,
null,
cancellationToken
);

Expand Down Expand Up @@ -204,7 +207,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
Expand Down
92 changes: 77 additions & 15 deletions Src/CSharpier.Core/CSharp/SyntaxNodeComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ CancellationToken cancellationToken
cSharpParseOptions,
cancellationToken: cancellationToken
);
this.CompareFunc = Compare;
}

public string CompareSource()
Expand Down Expand Up @@ -148,14 +149,16 @@ SyntaxNode formattedStart
return Equal;
}

#pragma warning disable CA1822
private CompareResult CompareLists<T>(
IReadOnlyList<T> originalList,
IReadOnlyList<T> formattedList,
Func<T, T, CompareResult> comparer,
Func<T, TextSpan> getSpan,
T originalList,
T formattedList,
Func<SyntaxToken, SyntaxToken, CompareResult> comparer,
Func<SyntaxToken, TextSpan> getSpan,
TextSpan originalParentSpan,
TextSpan newParentSpan
)
where T : IReadOnlyList<SyntaxToken>
{
for (var x = 0; x < originalList.Count || x < formattedList.Count; x++)
{
Expand All @@ -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>(
T originalList,
T formattedList,
Func<SyntaxNode, SyntaxNode, CompareResult> comparer,
Func<SyntaxNode, TextSpan> getSpan,
TextSpan originalParentSpan,
TextSpan newParentSpan
)
where T : IReadOnlyList<SyntaxNode>
{
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<SyntaxNode> 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)
Expand All @@ -210,6 +259,8 @@ private static CompareResult NotEqual(TextSpan? originalSpan, TextSpan? formatte
};
}

private Func<SyntaxToken, SyntaxToken, CompareResult> CompareFunc { get; }

private CompareResult Compare(SyntaxToken originalToken, SyntaxToken formattedToken)
{
return this.Compare(originalToken, formattedToken, null, null);
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static List<Doc> Print<T>(
)
where T : MemberDeclarationSyntax
{
var result = new List<Doc>();
var result = new List<Doc>(members.Count * 3);
if (!skipFirstHardLine)
{
result.Add(Doc.HardLine);
Expand Down
1 change: 1 addition & 0 deletions Src/CSharpier.Core/CSharp/SyntaxPrinter/Modifiers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CSharpier.Core.DocTypes;
using CSharpier.Core.Utilities;
using Microsoft.CodeAnalysis;

namespace CSharpier.Core.CSharp.SyntaxPrinter;
Expand Down
1 change: 1 addition & 0 deletions Src/CSharpier.Core/CSharp/SyntaxPrinter/RightHandSide.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CSharpier.Core.DocTypes;
using CSharpier.Core.Utilities;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ node.Parent is CompilationUnitSyntax compilationUnitSyntax

var printSeparatedSyntaxList = SeparatedSyntaxList.Print(
node.Attributes,
(attributeNode, _) =>
static (attributeNode, context) =>
{
var name = Node.Print(attributeNode.Name, context);
if (attributeNode.ArgumentList == null)
Expand All @@ -60,7 +60,7 @@ is [
singleCollectionExpression ? Doc.Null : Doc.SoftLine,
SeparatedSyntaxList.Print(
attributeNode.ArgumentList.Arguments,
(attributeArgumentNode, _) =>
static (attributeArgumentNode, context) =>
Doc.Concat(
attributeArgumentNode.NameEquals != null
? NameEquals.Print(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static Doc Print(FunctionPointerTypeSyntax node, PrintingContext context)
Doc.SoftLine,
SeparatedSyntaxList.Print(
node.ParameterList.Parameters,
(o, _) =>
static (o, context) =>
Doc.Concat(
AttributeLists.Print(o, o.AttributeLists, context),
Modifiers.Print(o.Modifiers, context),
Expand All @@ -46,7 +46,7 @@ PrintingContext context
Token.Print(node.UnmanagedCallingConventionList.OpenBracketToken, context),
SeparatedSyntaxList.Print(
node.UnmanagedCallingConventionList.CallingConventions,
(o, _) => Token.Print(o.Name, context),
static (o, context) => Token.Print(o.Name, context),
" ",
context
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static Doc Print(OrderByClauseSyntax node, PrintingContext context)
Token.Print(node.OrderByKeyword, context),
SeparatedSyntaxList.Print(
node.Orderings,
(orderingNode, _) =>
static (orderingNode, context) =>
Doc.Concat(
" ",
Node.Print(orderingNode.Expression, context),
Expand Down
Loading