diff --git a/Sources/Kysect.Configuin.EditorConfig/Formatter/EditorConfigFormatter.cs b/Sources/Kysect.Configuin.EditorConfig/Formatter/EditorConfigFormatter.cs new file mode 100644 index 0000000..74d12bb --- /dev/null +++ b/Sources/Kysect.Configuin.EditorConfig/Formatter/EditorConfigFormatter.cs @@ -0,0 +1,79 @@ +using Kysect.CommonLib.Collections.Extensions; +using Kysect.Configuin.EditorConfig.DocumentModel; +using Kysect.Configuin.EditorConfig.DocumentModel.Nodes; +using Kysect.Configuin.EditorConfig.Settings; +using Kysect.Configuin.RoslynModels; + +namespace Kysect.Configuin.EditorConfig.Formatter; + +public class EditorConfigFormatter +{ + private readonly DotnetConfigSettingsParser _settingsParser; + + public EditorConfigFormatter(DotnetConfigSettingsParser settingsParser) + { + _settingsParser = settingsParser; + } + + public EditorConfigDocument Format(EditorConfigDocument value) + { + List nodesForRemoving = new List(); + IReadOnlyCollection styleRuleNodesForMoving = SelectIdeNodes(value, RoslynRuleType.StyleRule); + IReadOnlyCollection qualityRuleNodesForMoving = SelectIdeNodes(value, RoslynRuleType.QualityRule); + nodesForRemoving.AddRange(styleRuleNodesForMoving); + nodesForRemoving.AddRange(qualityRuleNodesForMoving); + + if (nodesForRemoving.IsEmpty()) + return value; + + value = value.RemoveNodes(nodesForRemoving); + var autoGeneratedSection = new EditorConfigCategoryNode("*.cs", [], ["# Autoformatted values"], null); + + if (styleRuleNodesForMoving.Any()) + { + var styleRuleSection = new EditorConfigDocumentSectionNode("### IDE ###"); + + styleRuleNodesForMoving = styleRuleNodesForMoving.OrderBy(r => r.Key.Value).ToList(); + foreach (EditorConfigPropertyNode styleRule in styleRuleNodesForMoving) + styleRuleSection = styleRuleSection.AddChild(styleRule); + + autoGeneratedSection = autoGeneratedSection.AddChild(styleRuleSection); + } + + if (qualityRuleNodesForMoving.Any()) + { + var qualitySection = new EditorConfigDocumentSectionNode("### CA ###"); + + qualityRuleNodesForMoving = qualityRuleNodesForMoving.OrderBy(r => r.Key.Value).ToList(); + foreach (EditorConfigPropertyNode qualityRule in qualityRuleNodesForMoving) + qualitySection = qualitySection.AddChild(qualityRule); + + autoGeneratedSection = autoGeneratedSection.AddChild(qualitySection); + } + + value = value.AddChild(autoGeneratedSection); + + return value; + } + + public IReadOnlyCollection SelectIdeNodes(EditorConfigDocument document, RoslynRuleType roslynRuleType) + { + List propertyNodes = document + .DescendantNodes() + .OfType() + .ToList(); + + List styleRuleNodes = new List(); + foreach (EditorConfigPropertyNode editorConfigPropertyNode in propertyNodes) + { + IEditorConfigSetting editorConfigSetting = _settingsParser.ParseSetting(editorConfigPropertyNode); + if (editorConfigSetting is not RoslynSeverityEditorConfigSetting severityConfigSetting) + continue; + + if (severityConfigSetting.RuleId.Type == roslynRuleType) + styleRuleNodes.Add(editorConfigPropertyNode); + } + + return styleRuleNodes; + } +} \ No newline at end of file diff --git a/Sources/Kysect.Configuin.Tests/EditorConfig/EditorConfigFormatterTests.cs b/Sources/Kysect.Configuin.Tests/EditorConfig/EditorConfigFormatterTests.cs new file mode 100644 index 0000000..291f631 --- /dev/null +++ b/Sources/Kysect.Configuin.Tests/EditorConfig/EditorConfigFormatterTests.cs @@ -0,0 +1,79 @@ +using Kysect.Configuin.EditorConfig; +using Kysect.Configuin.EditorConfig.DocumentModel; +using Kysect.Configuin.EditorConfig.DocumentModel.Nodes; +using Kysect.Configuin.EditorConfig.Formatter; +using Kysect.Configuin.Tests.Tools; + +namespace Kysect.Configuin.Tests.EditorConfig; + +public class EditorConfigFormatterTests +{ + private readonly EditorConfigFormatter _formatter; + private readonly EditorConfigDocumentParser _parser; + + public EditorConfigFormatterTests() + { + _formatter = new EditorConfigFormatter(new DotnetConfigSettingsParser(TestLogger.ProviderForTests())); + _parser = new EditorConfigDocumentParser(); + } + + [Fact] + public void Format_OrderedIdeRulesWithoutHeader_HeaderAdded() + { + var input = """ + first = value + dotnet_diagnostic.IDE0081.severity = none + dotnet_diagnostic.IDE0080.severity = none + dotnet_diagnostic.IDE0082.severity = warning + second = value + """; + + var expected = """ + first = value + second = value + # Autoformatted values + [*.cs] + ### IDE ### + dotnet_diagnostic.IDE0080.severity = none + dotnet_diagnostic.IDE0081.severity = none + dotnet_diagnostic.IDE0082.severity = warning + """; + + FormatAndCompare(input, expected); + } + + [Fact] + public void Format_QualityAndStyleRulesMashed_ReturnOrderedLinesWithHeader() + { + var input = """ + first = value + dotnet_diagnostic.IDE0081.severity = none + dotnet_diagnostic.CA2001.severity = none + second = value + dotnet_diagnostic.IDE0080.severity = none + dotnet_diagnostic.CA2000.severity = warning + """; + + var expected = """ + first = value + second = value + # Autoformatted values + [*.cs] + ### IDE ### + dotnet_diagnostic.IDE0080.severity = none + dotnet_diagnostic.IDE0081.severity = none + ### CA ### + dotnet_diagnostic.CA2000.severity = warning + dotnet_diagnostic.CA2001.severity = none + """; + + FormatAndCompare(input, expected); + } + + private void FormatAndCompare(string input, string expected) + { + EditorConfigDocument editorConfigDocument = _parser.Parse(input); + EditorConfigDocument formattedDocument = _formatter.Format(editorConfigDocument); + formattedDocument.ToFullString().Should().Be(expected); + } +} \ No newline at end of file