Skip to content

Commit

Permalink
Implement .editorconfig model serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
FrediKats committed May 15, 2024
1 parent ffec775 commit 5b4e6e1
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Kysect.CommonLib.BaseTypes.Extensions;
using Kysect.Configuin.EditorConfig.DocumentModel.Nodes;

namespace Kysect.Configuin.EditorConfig.DocumentModel;

public class EditorConfigDocumentSerializer
{
public string Build(EditorConfigDocument document)
{
document.ThrowIfNull();

return document.ToFullString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ public EditorConfigDocument Parse(string content)
bool isSection = trimmedLine.StartsWith(EditorConfigDocumentSectionNode.NodeIndicator);
if (isSection)
{
int indicatorLength = EditorConfigDocumentSectionNode.NodeIndicator.Length;
string sectionTitle = line.Substring(indicatorLength, line.Length - (indicatorLength * 2)).Trim();
context.AddSection(sectionTitle);
context.AddSection(trimmedLine);
continue;
}

Expand All @@ -49,9 +47,7 @@ public EditorConfigDocument Parse(string content)
if (parts.Length != 2)
throw new ArgumentException($"Line {line} contains unexpected count of '='");

string key = parts[0].Trim();
string value = parts[1].Trim();
EditorConfigPropertyNode propertyNode = new EditorConfigPropertyNode(key, value);
var propertyNode = new EditorConfigPropertyNode(EditorConfigStringNode.Create(parts[0]), EditorConfigStringNode.Create(parts[1]));
context.AddProperty(propertyNode);
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public EditorConfigDocument Build()
{
DumpSection();
DumpCategory();
return _currentDocument;
return _currentDocument with { TrailingTrivia = _currentTrivia.ToImmutableList() };
}

public void AddTrivia(string line)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

public interface IEditorConfigNode
{
string ToFullString();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
using System.Text;

namespace Kysect.Configuin.EditorConfig.DocumentModel.Nodes;

Expand All @@ -17,4 +18,18 @@ public EditorConfigCategoryNode AddChild(IEditorConfigNode child)
{
return this with { Children = Children.Add(child) };
}

public string ToFullString()
{
var stringBuilder = new StringBuilder();
LeadingTrivia.ForEach(s => stringBuilder.AppendLine(s));

string line = $"[{Value}]";
if (TrailingTrivia is not null)
line += $"{line} {TrailingTrivia}";
stringBuilder.Append(line);

Children.ForEach(c => stringBuilder.Append($"{Environment.NewLine}{c.ToFullString()}"));
return stringBuilder.ToString();
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
using System.Collections.Immutable;
using System.Text;

namespace Kysect.Configuin.EditorConfig.DocumentModel.Nodes;

public record EditorConfigDocument(ImmutableList<IEditorConfigNode> Children) : IEditorConfigContainerNode
public record EditorConfigDocument(ImmutableList<IEditorConfigNode> Children, ImmutableList<string> TrailingTrivia) : IEditorConfigContainerNode
{
public EditorConfigDocument(ImmutableList<IEditorConfigNode> children) : this(children, ImmutableList<string>.Empty)
{
}

IEditorConfigContainerNode IEditorConfigContainerNode.AddChild(IEditorConfigNode child)
{
return new EditorConfigDocument(Children: Children.Add(child));
return AddChild(child);
}

public EditorConfigDocument AddChild(IEditorConfigNode child)
{
return new EditorConfigDocument(Children: Children.Add(child));
return this with { Children = Children.Add(child) };
}

public string ToFullString()
{
var stringBuilder = new StringBuilder();
List<string> lines = new();
lines.AddRange(Children.Select(c => c.ToFullString()));
lines.AddRange(TrailingTrivia);

stringBuilder.AppendJoin(Environment.NewLine, lines);
return stringBuilder.ToString();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
using System.Text;

namespace Kysect.Configuin.EditorConfig.DocumentModel.Nodes;

Expand All @@ -19,4 +20,18 @@ public EditorConfigDocumentSectionNode AddChild(IEditorConfigNode child)
{
return this with { Children = Children.Add(child) };
}

public string ToFullString()
{
var stringBuilder = new StringBuilder();
LeadingTrivia.ForEach(s => stringBuilder.AppendLine(s));

string line = $"{Value}";
if (TrailingTrivia is not null)
line += $"{line} {TrailingTrivia}";
stringBuilder.Append(line);

Children.ForEach(c => stringBuilder.Append($"{Environment.NewLine}{c.ToFullString()}"));
return stringBuilder.ToString();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
using System.Collections.Immutable;
using System.Text;

namespace Kysect.Configuin.EditorConfig.DocumentModel.Nodes;

public record EditorConfigPropertyNode(string Key, string Value, ImmutableList<string> LeadingTrivia, string? TrailingTrivia) : IEditorConfigNode
public record EditorConfigPropertyNode(EditorConfigStringNode Key, EditorConfigStringNode Value, ImmutableList<string> LeadingTrivia, string? TrailingTrivia) : IEditorConfigNode
{
public EditorConfigPropertyNode(string key, string value) : this(key, value, ImmutableList<string>.Empty, null)
public EditorConfigPropertyNode(EditorConfigStringNode key, EditorConfigStringNode value) : this(key, value, ImmutableList<string>.Empty, null)
{
}

public EditorConfigPropertyNode(string key, string value) : this(EditorConfigStringNode.Create(key), EditorConfigStringNode.Create(value))
{
}

public string ToFullString()
{
var stringBuilder = new StringBuilder();
LeadingTrivia.ForEach(s => stringBuilder.AppendLine(s));

string line = $"{Key.ToFullString()}={Value.ToFullString()}";
if (TrailingTrivia is not null)
line += $"{line} {TrailingTrivia}";

stringBuilder.Append(line);
return stringBuilder.ToString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Kysect.CommonLib.BaseTypes.Extensions;

namespace Kysect.Configuin.EditorConfig.DocumentModel.Nodes;

public record EditorConfigStringNode(string Value, string LeadingTrivia, string TrailingTrivia) : IEditorConfigNode
{
public static EditorConfigStringNode Create(string value)
{
value.ThrowIfNull();

string leadingTriviaLength = value.Substring(0, value.Length - value.TrimStart().Length);
string trailingTriviaLength = value.Substring(value.TrimEnd().Length, value.Length - value.TrimEnd().Length);

return new EditorConfigStringNode(
value.Trim(),
leadingTriviaLength,
trailingTriviaLength);
}

public string ToFullString()
{
return $"{LeadingTrivia}{Value}{TrailingTrivia}";
}
}
22 changes: 11 additions & 11 deletions Sources/Kysect.Configuin.EditorConfig/EditorConfigSettingsParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ public EditorConfigSettings Parse(string content)

private IEditorConfigSetting ParseSetting(EditorConfigPropertyNode line)
{
if (_generalRuleKeys.Contains(line.Key))
return new GeneralEditorConfigSetting(line.Key, line.Value);
if (_generalRuleKeys.Contains(line.Key.Value))
return new GeneralEditorConfigSetting(line.Key.Value, line.Value.Value);

bool isSeveritySetting = line.Key.StartsWith("dotnet_diagnostic.");
bool isSeveritySetting = line.Key.Value.StartsWith("dotnet_diagnostic.");
if (isSeveritySetting)
return ParseSeveritySetting(line);

bool isCompositeKeyRule = line.Key.Contains('.');
bool isCompositeKeyRule = line.Key.Value.Contains('.');
if (isCompositeKeyRule)
return ParseCompositeKeySetting(line);

Expand All @@ -61,29 +61,29 @@ private IEditorConfigSetting ParseSetting(EditorConfigPropertyNode line)

private static RoslynSeverityEditorConfigSetting ParseSeveritySetting(EditorConfigPropertyNode line)
{
string[] keyParts = line.Key.Split('.');
string[] keyParts = line.Key.Value.Split('.');

if (keyParts.Length != 3)
throw new ArgumentException($"Incorrect rule key: {line.Key}");
throw new ArgumentException($"Incorrect rule key: {line.Key.Value}");

if (!string.Equals(keyParts[2], "severity", StringComparison.InvariantCultureIgnoreCase))
throw new ArgumentException($"Expect postfix .severity for diagnostic rule but was {keyParts[2]}");

if (!Enum.TryParse(line.Value, true, out RoslynRuleSeverity severity))
throw new ArgumentException($"Cannot parse severity from {line.Value}");
if (!Enum.TryParse(line.Value.Value, true, out RoslynRuleSeverity severity))
throw new ArgumentException($"Cannot parse severity from {line.Value.Value}");

var ruleId = RoslynRuleId.Parse(keyParts[1]);
return new RoslynSeverityEditorConfigSetting(ruleId, severity);
}

private static CompositeRoslynOptionEditorConfigSetting ParseCompositeKeySetting(EditorConfigPropertyNode line)
{
string[] keyParts = line.Key.Split('.');
return new CompositeRoslynOptionEditorConfigSetting(keyParts, line.Value, Severity: null);
string[] keyParts = line.Key.Value.Split('.');
return new CompositeRoslynOptionEditorConfigSetting(keyParts, line.Value.Value, Severity: null);
}

private static RoslynOptionEditorConfigSetting ParseOptionSetting(EditorConfigPropertyNode line)
{
return new RoslynOptionEditorConfigSetting(line.Key, line.Value);
return new RoslynOptionEditorConfigSetting(line.Key.Value, line.Value.Value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ public class EditorConfigDocumentParserTests()
public void Parse_EmptyFile_ReturnEmptyDocumentNode()
{
const string content = "";
EditorConfigDocument expected = new EditorConfigDocument(ImmutableList<IEditorConfigNode>.Empty);
EditorConfigDocument expected = new EditorConfigDocument(ImmutableList<IEditorConfigNode>.Empty, [""]);

ParseAndCompare(content, expected);
}

[Fact]
public void Parse_TriviaOnly_ReturnEmptyDocumentNode()
{
const string content = "# comment";
EditorConfigDocument expected = new EditorConfigDocument(ImmutableList<IEditorConfigNode>.Empty, ["# comment"]);

ParseAndCompare(content, expected);
}
Expand All @@ -36,7 +45,7 @@ public void Parse_Group_ReturnDocumentWithGroupNode()
const string content = """
### Custom section ###
""";
EditorConfigDocument expected = new EditorConfigDocument([new EditorConfigDocumentSectionNode("Custom section")]);
EditorConfigDocument expected = new EditorConfigDocument([new EditorConfigDocumentSectionNode("### Custom section ###")]);

EditorConfigDocument actual = _parser.Parse(content);

Expand All @@ -47,7 +56,7 @@ public void Parse_Group_ReturnDocumentWithGroupNode()
public void Parse_Property_ReturnDocumentWithPropertyNode()
{
const string content = """
key = value
key=value
""";
EditorConfigDocument expected = new EditorConfigDocument([new EditorConfigPropertyNode("key", "value")]);

Expand All @@ -59,9 +68,9 @@ public void Parse_CategoryWithProperty_ReturnCorrectDocument()
{
const string content = """
[*.cs]
tab_width = 4
indent_size = 4
end_of_line = crlf
tab_width=4
indent_size=4
end_of_line=crlf
""";

EditorConfigDocument expected = new EditorConfigDocument([
Expand All @@ -79,14 +88,14 @@ public void Parse_CategoryWithSectionWithProperty_ReturnCorrectDocument()
const string content = """
[*.cs]
### Custom section ###
tab_width = 4
indent_size = 4
end_of_line = crlf
tab_width=4
indent_size=4
end_of_line=crlf
""";

EditorConfigDocument expected = new EditorConfigDocument([
new EditorConfigCategoryNode("*.cs")
.AddChild(new EditorConfigDocumentSectionNode("Custom section")
.AddChild(new EditorConfigDocumentSectionNode("### Custom section ###")
.AddChild(new EditorConfigPropertyNode("tab_width", "4"))
.AddChild(new EditorConfigPropertyNode("indent_size", "4"))
.AddChild(new EditorConfigPropertyNode("end_of_line", "crlf")))
Expand All @@ -102,7 +111,7 @@ public void Parse_CategoryWithPropertyWithLeadingTrivia_ReturnCorrectDocument()
[*.cs]
tab_width = 4
tab_width=4
""";

EditorConfigDocument expected = new EditorConfigDocument([
Expand All @@ -113,6 +122,32 @@ public void Parse_CategoryWithPropertyWithLeadingTrivia_ReturnCorrectDocument()
ParseAndCompare(content, expected);
}

[Fact]
public void Parse_SampleDocumentAndSerialize_ReturnSameValue()
{
string expected = File.ReadAllText(Path.Combine("Resources", "Editor-config-sample.ini"));
EditorConfigDocument document = _parser.Parse(expected);
string actual = document.ToFullString();

actual.Should().Be(expected);
}

[Fact]
public void Parse_DocumentWithAllElements_ReturnSameValue()
{
string expected = """
# comment
[*.cs]
### Category ###
key = value
""";

EditorConfigDocument document = _parser.Parse(expected);
string actual = document.ToFullString();

actual.Should().Be(expected);
}

private void ParseAndCompare(string content, EditorConfigDocument expected)
{
EditorConfigDocument actual = _parser.Parse(content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public void Compare(EditorConfigDocument actual, EditorConfigDocument expected)
expected.ThrowIfNull();

CompareChildren(actual.Children, expected.Children);
actual.TrailingTrivia.Should().BeEquivalentTo(expected.TrailingTrivia);
}

private void CompareChildren(ImmutableList<IEditorConfigNode> actual, ImmutableList<IEditorConfigNode> expected)
Expand Down

0 comments on commit 5b4e6e1

Please sign in to comment.