Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Сазонов Александр #233

Open
wants to merge 51 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
ee8842e
Initial commit
AlexxSaz Nov 21, 2024
f19c836
Html tag classes created
AlexxSaz Nov 24, 2024
cd5dc04
Class token created
AlexxSaz Nov 24, 2024
be46adb
Token parser class created
AlexxSaz Nov 24, 2024
79d1e65
Renderer class created
AlexxSaz Nov 24, 2024
e149587
Md class changed to use new classes
AlexxSaz Nov 24, 2024
f8e12c2
Created test for md class
AlexxSaz Nov 24, 2024
dc503e0
Added tag converter class
AlexxSaz Nov 25, 2024
aee3615
Token parser reworked to SRP
AlexxSaz Nov 25, 2024
c17136a
Added implementation to HtmlRenderer class
AlexxSaz Nov 25, 2024
3bbe844
Updated Md class
AlexxSaz Nov 25, 2024
f1068d7
Added markdown token handlers
AlexxSaz Nov 27, 2024
fd6a1a0
Added markdown token processor
AlexxSaz Nov 27, 2024
4a6be5d
Added markdown token creator
AlexxSaz Nov 27, 2024
f0429ab
Modified token parser
AlexxSaz Nov 27, 2024
65119ec
Rework main class with new classes
AlexxSaz Nov 27, 2024
64c2a3f
Implemented markdown processor interface
AlexxSaz Nov 27, 2024
e9f8459
Few days work
AlexxSaz Dec 3, 2024
1867fad
Evening work
AlexxSaz Dec 3, 2024
0c49358
Job work
AlexxSaz Dec 4, 2024
1e36da5
Emphasis handler reworked
AlexxSaz Dec 4, 2024
7955386
Validation of underscore tags reworked
AlexxSaz Dec 4, 2024
5090645
Added more validation of underscore tags
AlexxSaz Dec 7, 2024
6bf34a4
Refactored EmphasisHandler
AlexxSaz Dec 7, 2024
4ed3d1f
Ephasis handlers implementations added
AlexxSaz Dec 7, 2024
288342e
All logic implemented
AlexxSaz Dec 8, 2024
4a64ed2
Tokenizer refactored
AlexxSaz Dec 8, 2024
e38afe3
Hadlers refactored
AlexxSaz Dec 8, 2024
a176465
More handlers refacting
AlexxSaz Dec 9, 2024
ff1b502
TextInsideEmphasisHandler refactored
AlexxSaz Dec 11, 2024
4a8f2b4
PairEmphasisHandler refactored
AlexxSaz Dec 13, 2024
498c754
Refactored implementation by PR comments
AlexxSaz Dec 13, 2024
5d67ab8
DifferentWordsEmphasisHandler refactored
AlexxSaz Dec 14, 2024
ee04ac4
SkipEmphasisHandler refactored
AlexxSaz Dec 14, 2024
f921258
IntersectEmphasisHandler refactored
AlexxSaz Dec 14, 2024
3fb6a79
NestedEmphasisHandler refactored
AlexxSaz Dec 14, 2024
80fcee3
Added SeriesEmphasisHandler
AlexxSaz Dec 14, 2024
f8f5d84
Added image tag tests
AlexxSaz Dec 15, 2024
93a160f
Added attributes
AlexxSaz Dec 15, 2024
954e5c0
HtmlConverter reworked to convert image tag
AlexxSaz Dec 15, 2024
b66c900
HtmlConverter reworked to convert image tag
AlexxSaz Dec 15, 2024
d13d24a
HeaderHandler fix
AlexxSaz Dec 15, 2024
751f4ff
Created new folder for underscore tag validators
AlexxSaz Dec 15, 2024
e455978
Added validators for image tag
AlexxSaz Dec 15, 2024
c59dd5e
EmphasisHandlers fix
AlexxSaz Dec 15, 2024
1aadbc9
Created handlers for image tag
AlexxSaz Dec 15, 2024
91dd8ad
Updated main test
AlexxSaz Dec 15, 2024
04644f3
Merge branch 'imageTag'
AlexxSaz Dec 15, 2024
4de72d8
HeaderHandler refactored
AlexxSaz Dec 16, 2024
dacaa2c
NonPairEmphasisTagHandler refactored
AlexxSaz Dec 16, 2024
4f087ad
UnionImageHandler refactored
AlexxSaz Dec 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions cs/Markdown/Html/HtmlTagConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Markdown.Html.Tags;

namespace Markdown.Html;

public class HtmlTagConverter
{
private readonly IHtmlTag[] _htmlTags =
[
new HeaderTag(),
new BoldTag(),
new ItalicTag()
];

public string Convert(string tagContent, bool isClosedTag)
{
foreach (var htmlTag in _htmlTags)
{
var isConvertedToHtml = htmlTag.TryConvert(tagContent, isClosedTag, out var resultHtmlTag);

if (isConvertedToHtml)
return resultHtmlTag;
}

throw new ArgumentException($"{nameof(tagContent)} doesn't match to any html tag.");
}
}
9 changes: 9 additions & 0 deletions cs/Markdown/Html/Tags/BoldTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Markdown.Html.Tags;

public class BoldTag : IHtmlTag
{
public bool TryConvert(string content, bool isClosedTag, out string resultContent)
{
throw new NotImplementedException();
}
}
9 changes: 9 additions & 0 deletions cs/Markdown/Html/Tags/HeaderTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Markdown.Html.Tags;

public class HeaderTag : IHtmlTag
{
public bool TryConvert(string content, bool isClosedTag, out string resultContent)
{
throw new NotImplementedException();
}
}
6 changes: 6 additions & 0 deletions cs/Markdown/Html/Tags/IHtmlTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Markdown.Html.Tags;

public interface IHtmlTag
{
bool TryConvert(string content, bool isClosedTag, out string resultContent);
}
9 changes: 9 additions & 0 deletions cs/Markdown/Html/Tags/ItalicTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Markdown.Html.Tags;

public class ItalicTag : IHtmlTag
{
public bool TryConvert(string content, bool isClosedTag, out string resultContent)
{
throw new NotImplementedException();
}
}
10 changes: 10 additions & 0 deletions cs/Markdown/Markdown.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
16 changes: 16 additions & 0 deletions cs/Markdown/Md.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Markdown.Renderers;
using Markdown.Tokens;

namespace Markdown;

public class Md
{
private readonly IRenderer _renderer = new HtmlRenderer();
private readonly MarkdownTokenParser _tokenParser = new();

public string Render(string text)
{
var tokens = _tokenParser.Parse(text);
return _renderer.Render(tokens.ToArray());
}
}
25 changes: 25 additions & 0 deletions cs/Markdown/Renderers/HtmlRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Text;
using Markdown.Html;
using Markdown.Tokens;

namespace Markdown.Renderers;

public class HtmlRenderer : IRenderer
{
private readonly HtmlTagConverter _tagConverter;

public string Render(IList<MarkdownToken> tokens)
{
var tokenBuilder = new StringBuilder();

foreach (var token in tokens)
{
if (token.Type is TokenType.Tag)
tokenBuilder.Append(_tagConverter.Convert(token.Content, token.IsClosedTag));
else
tokenBuilder.Append(token.Content);
}

return tokenBuilder.ToString();
}
}
7 changes: 7 additions & 0 deletions cs/Markdown/Renderers/IRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Markdown.Tokens;

namespace Markdown.Renderers;
public interface IRenderer
{
string Render(IList<MarkdownToken> document);
}
18 changes: 18 additions & 0 deletions cs/Markdown/Tokens/MarkdownToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Markdown.Tokens;

public class MarkdownToken(string content, TokenType type, bool isClosedTag = false)
{
public string Content { get; } = content;
public TokenType Type { get; } = type;
public bool IsClosedTag { get; } = isClosedTag;

public static bool TryGetTagToken(string str, out MarkdownToken resultToken)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Кстати) давай попробуем анемичные модели данных. Т.е. оставлять объекты описательными, а все методы над ними выносить в самостоятельные классы. Зачем? Объект и бизнес-требования могут быть связаны сегодня, но нет гарантий, что завтра не появится бизнес-требование v2 или v3, а объект почти наверняка останется тот же. Также можно рассмотреть через SRP-призму: в текущем классе две ответственности

  • описание объекта токена разметки
  • методы формирования токена на основе строки

И следует стараться оставлять в классе только одну причину для изменений

{
throw new NotImplementedException();
}

public static MarkdownToken GetToken(string str)
{
throw new NotImplementedException();
}
}
42 changes: 42 additions & 0 deletions cs/Markdown/Tokens/MarkdownTokenParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace Markdown.Tokens;

public class MarkdownTokenParser
{
public IEnumerable<MarkdownToken> Parse(string text)
{
var lines = SplitIntoLines(text);
return ParseLines(lines);
}

private static string[] SplitIntoLines(string text)
{
return text.Split('\n', '\r');
}

private IEnumerable<MarkdownToken> ParseLines(string[] lines)
{
foreach (var line in lines)
{
foreach (var token in ParseLine(line))
{
yield return token;
}
yield return MarkdownToken.GetToken("\n");
}
}

private static IEnumerable<MarkdownToken> ParseLine(string line)
{
for (var i = 0; i < line.Length; i++)
{
//TODO: ����� ����� ���������� ���������� ������ �� ������

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А что здесь написано? Какой кодировкой? Давай все оформлять в стандартной - UTF-8

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь будет реализация парсинга строк на токены=)

var currStr = line.Substring(i, 1);

var isTagToken = MarkdownToken.TryGetTagToken(currStr, out var tagToken);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Раньше идея была работать с токенами абстрактно, сейчас, предполагаю, идея модернизировалась и теперь с текстом будет более плотное взаимодействие. К сожалению, я не нашел никаких сущностей, ответственных за md-теги, их обработку, логику и тд. Оттого есть подозрение, что логика их обработки будет или размазана, или храниться в одном классе потоком кода. Так делать точно не стоит) У нас ООП и DDD =) а еще всегда держим в голове SRP

if (isTagToken)
yield return tagToken;
else
yield return MarkdownToken.GetToken(currStr);
}
}
}
10 changes: 10 additions & 0 deletions cs/Markdown/Tokens/TokenType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Markdown.Tokens;

public enum TokenType
{
Tag,
Text,
NewLine,
Space,
Digit
}
37 changes: 37 additions & 0 deletions cs/MarkdownTests/Markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Markdown

Напиши процессор упрощённого markdown-подобного языка разметки.

[Спецификация языка разметки](MarkdownSpec.md).

## Формат

В fork-е этого репозитория создай проект Markdown и реализуй метод Render класса Md. Он принимает в качестве аргумента текст в markdown-подобной разметке, и возвращает строку с html-кодом этого текста согласно спецификации.

Обрати внимание, что в этой задаче <ins>запрещено</ins> использовать регулярные выражения.

## Важные моменты
1. Проведи начальное проектирование: зафиксируй классы и их методы в коде (а также связи между классами), но не пиши внутренности методов
2. Покажи декомпозицию наставнику, получи обратную связь
3. После этого приступай к реализации методов, используя TDD
4. Помни, твой алгоритм должен работать быстро — линейно или почти линейно от размера входа. Не забудь написать такой тест!

## Оценка

#### Минимальные требования (на 1 балл)
1. Поддерживаются тэги `_`, `__` и `#` согласно спецификации
2. Тесты

#### Полное решение (на 2 балла)
1. Выполнены минимальные требования
2. Решение разбито на составные части, каждая из которых легко читается

#### Максимальное решение (на 3 балла)
1. Выполнено полное решение
2. Умеет рендериться <ins>один</ins> из дополнительных тегов:
- Маркерованный список
- Нумерованный список
- Ссылка
- Картинка
3. В файле [спецификации](MarkdownSpec.md) подробно описано, как работает новый тег
4. Обрати внимание: если упадет читаемость кода, то дополнительный балл засчитан не будет!
73 changes: 73 additions & 0 deletions cs/MarkdownTests/MarkdownSpec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Спецификация языка разметки

Посмотрите этот файл в сыром виде. Сравните с тем, что показывает github.
Все совпадения случайны ;)



# Курсив

Текст, _окруженный с двух сторон_ одинарными символами подчерка,
должен помещаться в HTML-тег \<еm> вот так:

Текст, \<еm>окруженный с двух сторон\</еm> одинарными символами подчерка,
должен помещаться в HTML-тег \<еm>.



# Полужирный

__Выделенный двумя символами текст__ должен становиться полужирным с помощью тега \<strоng>.



# Экранирование

Любой символ можно экранировать, чтобы он не считался частью разметки.
\_Вот это\_, не должно выделиться тегом \<еm>.

Символ экранирования исчезает из результата, только если экранирует что-то.
Здесь сим\волы экранирования\ \должны остаться.\

Символ экранирования тоже можно экранировать: \\_вот это будет выделено тегом_ \<еm>



# Взаимодействие тегов

Внутри __двойного выделения _одинарное_ тоже__ работает.

Но не наоборот — внутри _одинарного __двойное__ не_ работает.

Подчерки внутри текста c цифрами_12_3 не считаются выделением и должны оставаться символами подчерка.

Однако выделять часть слова они могут: и в _нач_але, и в сер_еди_не, и в кон_це._

В то же время выделение в ра_зных сл_овах не работает.

__Непарные_ символы в рамках одного абзаца не считаются выделением.

За подчерками, начинающими выделение, должен следовать непробельный символ. Иначе эти_ подчерки_ не считаются выделением
и остаются просто символами подчерка.

Подчерки, заканчивающие выделение, должны следовать за непробельным символом. Иначе эти _подчерки _не считаются_ окончанием выделения
и остаются просто символами подчерка.

В случае __пересечения _двойных__ и одинарных_ подчерков ни один из них не считается выделением.

Если внутри подчерков пустая строка ____, то они остаются символами подчерка.



# Заголовки

Абзац, начинающийся с "# ", выделяется тегом \<h1> в заголовок.
В тексте заголовка могут присутствовать все прочие символы разметки с указанными правилами.

Таким образом

# Заголовок __с _разными_ символами__

превратится в:

\<h1>Заголовок \<strоng>с \<еm>разными\</еm> символами\</strоng>\</h1>
29 changes: 29 additions & 0 deletions cs/MarkdownTests/MarkdownTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Все еще есть лишние подключения)

<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Markdown\Markdown.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

</Project>
Loading