Skip to content

Commit

Permalink
Еще рефакторинг
Browse files Browse the repository at this point in the history
  • Loading branch information
kashin.aleksandr committed Dec 11, 2024
1 parent 6f02ada commit abf4e24
Show file tree
Hide file tree
Showing 37 changed files with 242 additions and 270 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Markdown;

public interface IMarkdown
public interface IMarkdownRenderer
{
string Render(string markdown);
}
5 changes: 0 additions & 5 deletions cs/Markdown/Markdown.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@
<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="Verify.NUnit" Version="28.3.2" />
</ItemGroup>

<ItemGroup>
<Folder Include="Tests\Markdown\snapshots\" />
</ItemGroup>

</Project>
92 changes: 11 additions & 81 deletions cs/Markdown/MarkdownRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,101 +1,31 @@
using Markdown.Render;
using Markdown.Tokenizer;
using Markdown.Tokenizer.Handlers;
using Markdown.Tokenizer.Nodes;
using Markdown.Tokenizer.Tags;
using Markdown.TreeBuilder;

namespace Markdown;

public class MarkdownRenderer : IMarkdown
public class MarkdownRenderer : IMarkdownRenderer
{
private readonly ITreeRenderer treeRenderer = new TreeRenderer();
private readonly ITokenizer tokenizer;
private readonly List<IHandler> handlers = new()
{
new HeaderHandler(),
new ItalicHandler(),
new BoldHandler(),
};
public string Render(string markdown)

public MarkdownRenderer()
{
var tokenizer = new MarkdownTokenizer(new HandlerManager(handlers), new TagProcessor());
var renderer = new HtmlRenderer();
var tokens = tokenizer.Tokenize(markdown);
var tree = ToTree(tokens);
return renderer.Render(tree);
tokenizer = new MarkdownTokenizer(new HandlerManager(handlers), new TagProcessor());
}

private Node ToTree(List<Token> tokens)
public string Render(string markdown)
{
Node mainNode = new MainNode();
Node currentNode = mainNode;
for (int i = 0; i < tokens.Count; i++)
{
if (tokens[i].TagStatus == TagStatus.Broken)
{
currentNode.Children.Add(new TextNode{Value = tokens[i].Value});
continue;
}

if (tokens[i] is ItalicTag tag)
{
if(tag.TagStatus == TagStatus.Open)
{
var node = new ItalicNode();
currentNode.Children.Add(node);
node.Parent = currentNode;
currentNode = node;
continue;
}

if (tag.TagStatus == TagStatus.Closed)
{
currentNode = currentNode.Parent;
continue;
}
}

if (tokens[i] is BoldTag boldTag)
{
if(boldTag.TagStatus == TagStatus.Open)
{
var node = new BoldNode();
currentNode.Children.Add(node);
node.Parent = currentNode;
currentNode = node;
continue;
}

if (boldTag.TagStatus == TagStatus.Closed)
{
currentNode = currentNode.Parent;
continue;
}
}

if (tokens[i] is HeaderTag)
{
var node = new HeaderNode();
currentNode.Children.Add(node);
node.Parent = currentNode;
currentNode = node;
continue;
}

if (tokens[i] is NewLineToken)
{
if (currentNode is HeaderNode)
{
currentNode = currentNode.Parent;
}
continue;
}

if (tokens[i] is TextToken textToken)
{
currentNode.Children.Add(new TextNode { Value = textToken.Value });
continue;
}
}
var tokens = tokenizer.Tokenize(markdown);
var tree = new TreeBuilder.TreeBuilder(new NodeFactory()).Build(tokens);

return currentNode.Parent ?? currentNode;
return treeRenderer.Render(tree);
}
}
8 changes: 0 additions & 8 deletions cs/Markdown/Render/ITokenRenderer.cs

This file was deleted.

8 changes: 8 additions & 0 deletions cs/Markdown/Render/ITreeRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Markdown.TreeBuilder.Nodes;

namespace Markdown.Render;

public interface ITreeRenderer
{
string Render(Node tokens);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Text;
using Markdown.Tokenizer.Nodes;
using Markdown.TreeBuilder.Nodes;

namespace Markdown.Render;

public class HtmlRenderer : ITokenRenderer
public class TreeRenderer : ITreeRenderer
{
public string Render(Node tokens)
{
Expand All @@ -19,10 +19,7 @@ public string Render(Node tokens)
return node switch
{
TextNode textNode => textNode.Value,
HeaderNode => $"<h1>{Render(node)}</h1>",
ItalicNode => $"<em>{Render(node)}</em>",
BoldNode => $"<strong>{Render(node)}</strong>",
_ => throw new Exception($"Unknown token type: {node.GetType()}")
_ => $"{node.OpenTag}{Render(node)}{node.CloseTag}"
};
}
}
112 changes: 31 additions & 81 deletions cs/Markdown/Tests/Markdown/MarkdownTests.cs
Original file line number Diff line number Diff line change
@@ -1,93 +1,43 @@
namespace Markdown.Tests.Markdown;
using NUnit.Framework;

namespace Markdown.Tests.Markdown;

[TestFixture]
public class MarkdownTests
{
private static readonly VerifySettings Settings = new();
private static readonly MarkdownRenderer Renderer = new();
private IMarkdownRenderer renderer;

[OneTimeSetUp]
public void OneTimeSetUp()
[SetUp]
public void SetUp()
{
Settings.UseDirectory("snapshots");
renderer = new MarkdownRenderer();
}

[TestCaseSource(nameof(ItalicTestCases))]
public string Test_1(string input) => Renderer.Render(input);
[TestCaseSource(nameof(MarkdownRendererTestCases))]
public string MarkdownRenderer_Verify(string input) => renderer.Render(input);

private static TestCaseData[] ItalicTestCases =
private static TestCaseData[] MarkdownRendererTestCases =
[
new TestCaseData("# Header").Returns("<h1>Header</h1>"),
new TestCaseData("\\# Header").Returns("# Header"),
new TestCaseData("\\\\# Header").Returns("\\<h1>Header</h1>"),
new TestCaseData("_Italic text_").Returns("<em>Italic text</em>"),
new TestCaseData("\\_Text_").Returns("_Text_"),
new TestCaseData("\\\\_Italic text_").Returns("\\<em>Italic text</em>"),
new TestCaseData("_Italic text").Returns("_Italic text"),
new TestCaseData("Italic text_").Returns("Italic text_"),
new TestCaseData("Italic_ text_").Returns("Italic_ text_"),
new TestCaseData("_Italic _text").Returns("_Italic _text"),
new TestCaseData("_нач_але").Returns("<em>нач</em>але"),
new TestCaseData("сер_еди_не").Returns("сер<em>еди</em>не"),
new TestCaseData("цифры_1_12_3").Returns("цифры_1_12_3"),
new TestCaseData("кон_це._").Returns("кон<em>це.</em>"),
new TestCaseData("в ра_зных сл_овах не").Returns("в ра_зных сл_овах не"),
new TestCaseData("__bold__").Returns("<strong>bold</strong>"),
new TestCaseData("_Text__").Returns("_Text__"),
new TestCaseData("__Text_").Returns("__Text_"),
new TestCaseData("__Italic __text").Returns("__Italic __text"),
new TestCaseData("__два _один_ может__").Returns("<strong>два <em>один</em> может</strong>"),
new TestCaseData("_одинарного __двойное__ не_").Returns( "<em>одинарного __двойное__ не</em>")
new TestCaseData("# Header").Returns("<h1>Header</h1>").SetDescription("Простой заголовок."),
new TestCaseData("\\# Header").Returns("# Header").SetDescription("Экранированный заголовок."),
new TestCaseData("\\\\# Header").Returns("\\<h1>Header</h1>").SetDescription("Экранирован экранирования."),
new TestCaseData("_Italic text_").Returns("<em>Italic text</em>").SetDescription("Курсив"),
new TestCaseData("\\_Text_").Returns("_Text_").SetDescription("Экранирование курсива."),
new TestCaseData("\\\\_Italic text_").Returns("\\<em>Italic text</em>").SetDescription("Экранирование экранирования курсива."),
new TestCaseData("_Italic text").Returns("_Italic text").SetDescription("Одинокий открывающий тэг."),
new TestCaseData("Italic text_").Returns("Italic text_").SetDescription("Одинокий закрывающий тэг."),
new TestCaseData("Italic_ text_").Returns("Italic_ text_").SetDescription("Два закрывающих тэга."),
new TestCaseData("_Italic _text").Returns("_Italic _text").SetDescription("Два открывающих тэга."),
new TestCaseData("_нач_але").Returns("<em>нач</em>але").SetDescription("Курсив в начале слова."),
new TestCaseData("сер_еди_не").Returns("сер<em>еди</em>не").SetDescription("Курсив в середине слова."),
new TestCaseData("кон_це._").Returns("кон<em>це.</em>").SetDescription("Курсив в конце слова."),
new TestCaseData("цифры_1_12_3").Returns("цифры_1_12_3").SetDescription("Между цифр - подчерки."),
new TestCaseData("в ра_зных сл_овах не").Returns("в ра_зных сл_овах не").SetDescription("В разных словах - не работает."),
new TestCaseData("__bold__").Returns("<strong>bold</strong>").SetDescription("Полужирный"),
new TestCaseData("_Text__").Returns("_Text__").SetDescription("Разные тэги 1"),
new TestCaseData("__Text_").Returns("__Text_").SetDescription("Разные тэги 2"),
new TestCaseData("__Italic __text").Returns("__Italic __text").SetDescription("Два открывающих тэга."),
new TestCaseData("__два _один_ может__").Returns("<strong>два <em>один</em> может</strong>").SetDescription("Курсив в полужирном."),
new TestCaseData("_одинарного __двойное__ не_").Returns( "<em>одинарного __двойное__ не</em>").SetDescription("Полужирный в курсиве - не работает."),
];

private static Task Verify(string target) =>
Verifier.Verify(target, Settings);

[Test]
public void SimpleText_Render_Verify() =>
Verify(Renderer.Render("Text"));

[Test]
public void EscapedCharacter_Render_Verify() =>
Verify(Renderer.Render(@"\_Text_"));

[Test]
public void ItalicText_Render_Verify() =>
Verify(Renderer.Render("_Italic text_"));

[Test]
public void BoldText_Render_Verify() =>
Verify(Renderer.Render("__Bold text__"));

[Test]
public void BoldWithItalicText_Render_Verify() =>
Verify(Renderer.Render("__Bold _with italic_ text__"));

[Test]
public void SimpleHeader_Render_Verify() =>
Verify(Renderer.Render("# Header"));

[Test]
public void TwoHeaders_Render_Verify() =>
Verify(Renderer.Render("# Header one \n# Header two"));
//
// [Test]
// public void HeaderWithItalic_Render_Verify() =>
// Verify(Renderer.Render("# Header with _italic text_"));
//
// [Test]
// public void HeaderWithBoldAndItalic_Render_Verify() =>
// Verify(Renderer.Render("# Header with _italic_ and __bold__ text"));
//
// [Test]
// public void HeaderWithItalicInBold_Render_Verify() =>
// Verify(Renderer.Render("# Header ___italic_ in bold__ text"));
//
// [Test]
// public void SimpleList_Render_Verify() =>
// Verify(Renderer.Render("- item1\n- item2"));
//
// [Test]
// public void ListWithItalicAndBold_Render_Verify() =>
// Verify(Renderer.Render("- _item1_\n- __item2__"));
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

19 changes: 14 additions & 5 deletions cs/Markdown/Tests/Tokenizer/BoldHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@
using Markdown.Tokenizer;
using Markdown.Tokenizer.Handlers;
using Markdown.Tokenizer.Tags;
using Markdown.TreeBuilder;
using NUnit.Framework;

namespace Markdown.Tests.Tokenizer;

[TestFixture]
public class BoldHandlerTests
{
private ITokenizer tokenizer;

[SetUp]
public void SetUp()
{
var handlers = new List<IHandler> { new HeaderHandler(), new ItalicHandler(), new BoldHandler() };
tokenizer = new MarkdownTokenizer(new HandlerManager(handlers), new TagProcessor());
}

[TestCaseSource(nameof(BoldTokenSource))]
public void BoldTokenizerTests((string input, Token[] tags) testCase)
{
var handlers = new List<IHandler>() { new HeaderHandler(), new ItalicHandler(), new BoldHandler() };
var tokenizer = new MarkdownTokenizer(new HandlerManager(handlers), new TagProcessor());
var res = tokenizer.Tokenize(testCase.input).ToArray();
var tokens = tokenizer.Tokenize(testCase.input).ToArray();

for (var i = 0; i < testCase.tags.Length; i++)
{
res[i].Value.Should().Be(testCase.tags[i].Value);
res[i].TokenType.Should().Be(testCase.tags[i].TokenType);
tokens[i].Value.Should().Be(testCase.tags[i].Value);
tokens[i].TokenType.Should().Be(testCase.tags[i].TokenType);
}
}

Expand Down
19 changes: 14 additions & 5 deletions cs/Markdown/Tests/Tokenizer/HeaderHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@
using Markdown.Tokenizer;
using Markdown.Tokenizer.Handlers;
using Markdown.Tokenizer.Tags;
using Markdown.TreeBuilder;
using NUnit.Framework;

namespace Markdown.Tests.Tokenizer;

[TestFixture]
public class HeaderHandlerTests
{
private ITokenizer tokenizer;

[SetUp]
public void SetUp()
{
var handlers = new List<IHandler> { new HeaderHandler(), new ItalicHandler(), new BoldHandler() };
tokenizer = new MarkdownTokenizer(new HandlerManager(handlers), new TagProcessor());
}

[TestCaseSource(nameof(HeaderTokenSource))]
public void HeaderTokenizerTests((string input, Token[] tags) testCase)
{
var handlers = new List<IHandler>() { new HeaderHandler(), new ItalicHandler(), new BoldHandler() };
var tokenizer = new MarkdownTokenizer(new HandlerManager(handlers), new TagProcessor());
var res = tokenizer.Tokenize(testCase.input).ToArray();
var tokens = tokenizer.Tokenize(testCase.input).ToArray();

for (var i = 0; i < testCase.tags.Length; i++)
{
res[i].Value.Should().Be(testCase.tags[i].Value);
res[i].TokenType.Should().Be(testCase.tags[i].TokenType);
tokens[i].Value.Should().Be(testCase.tags[i].Value);
tokens[i].TokenType.Should().Be(testCase.tags[i].TokenType);
}
}

Expand Down
Loading

0 comments on commit abf4e24

Please sign in to comment.