Skip to content

Conversation

@RomanGleyzer
Copy link

@RomanGleyzer RomanGleyzer commented Nov 3, 2025

@masssha1308

upd:

Предполагаемый алгоритм обработки текста:

  1. Нам пришел на вход текст: "сер_еди_на тест"
  2. После этого pipeline запускает код, в котором текст превратится в результат
  3. BlockSegmenter - первый этап, он разбивает текст по абзацам. В нашем случае только 1 абзац.
  4. Формируется следующая структура:
Blocks [
  Block {
    RawText: "сер_еди_на тест",
    Inlines: (будут заполнены парсером)
  }
]
  1. Далее парсер обрабатывает сформированные блоки и разбивает текст на вершины (INode) Text, Em, Strong:
Blocks [
  Block {
    RawText: "сер_еди_на тест",
    Inlines: [
      Node("сер", Text),
      Node("еди", Em),
      Node("на тест", Text)
    ]
  }
]
  1. После этого html отрендерит сформированные блоки в:
<p>сер<em>еди</em>на тест</p>

@RomanGleyzer RomanGleyzer changed the title Предпроверка markdown Глейзер Роман Nov 5, 2025
@@ -1,14 +1,16 @@

Choose a reason for hiding this comment

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

Лучше не включать в МР, т.к. изменения были сгененрированы IDE автоматически и не имеют прямого отношения к МР


namespace Markdown;

public class Pipeline(BlockSegmenter blockSegmenter, InlineParser parser, HtmlRenderer htmlRenderer)

Choose a reason for hiding this comment

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

На данный момент нет необходимости выносить эту логику в отдельный класс, давай перенесем ее в Md.Render()

// Предполагается реализация с разделением кода парсинга на отдельные классы, работающие в следующем порядке:
// Класс, посимвольно читающий текст
// Класс, преобразующий текст в единицы разметки (текст, бэк слеш, одинарное подчеркивание итд)
// Класс, отвечающий за обработку экранирования в тексте после преобразования текста в единицы разметки

Choose a reason for hiding this comment

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

Экранирование должно обрабатываться до парсинга тегов

throw new NotImplementedException();
}

// Предполагается реализация с разделением кода парсинга на отдельные классы, работающие в следующем порядке:

Choose a reason for hiding this comment

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

Разбиение парсера на 5 классов избыточно, каждый из предложенных классов будет содержать 1-2 метода что усложнит архитектуру без реальной поьзы, классы не будут являться самостоятельными сущностями, а будут лишь шагами алгоритма
Отдельные классы стоило бы выделить в случае переиспользования логики или разных "профилей" парсера (например, с конфигурируемыми настройками), в этом кейсе такой необходимости нет

Choose a reason for hiding this comment

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

Возможно для упрощения логики парсера стоит вынести отдельный этап предварительной обработки текста которая подготавливает его к основному парсингу, заменяя "мешающие" элементы на временные маркеры


public interface IBlock
{
// Далее планируется добавление двух наследников: HeadingBlock и ParagraphBlock

Choose a reason for hiding this comment

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

Нет необходимости в наследниках исходя из текущих требований, лучше добавить поле BlockType. Разница HeadingBlock и ParagraphBlock только в рендеринге, хранятся и обрабатываются данные одинаково. Наследники понадобились бы, например, в случае разных структур данных


public IReadOnlyList<INode> Inlines { get; }

void SetInlines(IReadOnlyList<INode> inlines);

Choose a reason for hiding this comment

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

Какие преимущества перед сеттером? Точно ли нужен этот метод?

Copy link
Author

Choose a reason for hiding this comment

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

Какие преимущества перед сеттером? Точно ли нужен этот метод?

Посидел, подумал и не нашел ни одной причины, по которой бы использование SetInlines было бы лучше использования дефолтного сеттера
Мне кажется под заданные требования будет достаточно просто сеттера. В итоговой реализации поправлю этот момент

@masssha1308
Copy link

Заметила что для некоторых компонентов выделены отдельные папки, содержащие по одному файлу. Предлагаю убрать, т.к. они избыточны и усложняют навигацию

@RomanGleyzer RomanGleyzer force-pushed the master branch 4 times, most recently from 29a056e to 39c6c27 Compare November 8, 2025 07:37
@RomanGleyzer RomanGleyzer force-pushed the master branch 2 times, most recently from d0d2003 to 0b0c12d Compare November 10, 2025 14:48

public class Md
{
public string Render(string text, BlockSegmenter segmenter, InlineParser parser, HtmlRenderer renderer)

Choose a reason for hiding this comment

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

Давай передавать зависимости в конструктор, а не в метод. Класс должен скрывать детали реализации. Передавать в метод не стоит, т.к. не получится инжектировать зависимости через di + это нарушает инкасуляцию: любой внешний код вызывающий этот метод будет знать какие зависимости передавать, как их конфигурировать и т.д.

return position + length < text.Length ? text[position + length] : Space;
}

public static bool IsWordChar(char ch)

Choose a reason for hiding this comment

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

Избыточный метод

@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00

Choose a reason for hiding this comment

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

в МР не нужно включать

Choose a reason for hiding this comment

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

не нужно тесты выделять в солюшн, проекта достаточно

Copy link
Author

Choose a reason for hiding this comment

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

в МР не нужно включать

Удалил эти файлы

Copy link
Author

Choose a reason for hiding this comment

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

не нужно тесты выделять в солюшн, проекта достаточно

Отказался от .sln. Сейчас в папке проекта нет такого решения

image

}
}
return sb.ToString();
}

Choose a reason for hiding this comment

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

Метод нечитабельный, нужно разбить/сделать компактнее

return text?
.Replace(PlaceholderUnderscore, Underscore)
.Replace(PlaceholderBackslash, Escape)
.Replace(PlaceholderHash, '#');

Choose a reason for hiding this comment

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

магический символ

return sb.ToString();
}

private static string RenderInlines(IReadOnlyList<Node> inlines, BlockType context)

Choose a reason for hiding this comment

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

Вынесем вспомогательные методы для читабельности? Метод получился большой и есть блоки которые напрашиваются в отдельный метод

break;

case NodeType.Em:
sb.Append("<em>")

Choose a reason for hiding this comment

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

магические строки

switch (node.Type)
{
case NodeType.Text:
sb.Append(node.Text ?? string.Empty);

Choose a reason for hiding this comment

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

В случае пусто строки ничего не добавляет, но вызывает метод. Лучше сделать проверку на пустую строку

[TestFixture]
public class MarkupLanguageProcessorTests
{
private static string RenderHtml(string input)

Choose a reason for hiding this comment

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

давай уберем эти вспомогательные методы, они не сокращают кол-во строк в тесте, зато сокращают читабельность: вместо того чтобы открыть тест и посмотреть что там происходит, приходится проваливаться в эти методы и смотреть что там внутри

}

[Test]
public void ParseAndRender_EmPartOfWord_RendersAtStartMiddleEnd()

Choose a reason for hiding this comment

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

Нужно разделить на 3 отдельных теста

[Test]
public void ParseAndRender_EmWholeSentenceFromSpec_ProducesPlainTextHtml()
{
var input = "Текст, <em>окруженный с двух сторон</em> одинарными символами подчерка, должен помещаться в HTML-тег <em>.";

Choose a reason for hiding this comment

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

здесь что проверяется?

Copy link
Author

Choose a reason for hiding this comment

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

здесь что проверяется?

хотел проверить, что строки, содержащие html теги, не меняются и выводятся как обычный текст в абзаце

Copy link
Author

Choose a reason for hiding this comment

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

здесь что проверяется?

Постарался подобрать более читаемое название. Остановился на варианте "Render_TextWithHtmlTags_KeepsHtmlTags"

image

}

[Test]
public void ParseAndRender_OpeningUnderscoreMustBeFollowedByNonSpace_NotOpening()

Choose a reason for hiding this comment

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

очень запутанные названия, давай упростим и если требуется добавим описания к тестам
кстати, почему parseandrender когда тестируется метод render?

}

[Test]
public void ParseAndRender_StrongPartOfWord_RendersAtStartMiddleEnd()

Choose a reason for hiding this comment

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

нужно разделить на 3 теста (возможно тесткейсы)

}

[Test]
public void ParseAndRender_DelimiterFollowedByPunctuation_CommaAfter()
Copy link

@masssha1308 masssha1308 Nov 13, 2025

Choose a reason for hiding this comment

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

эти 3 логичнее объединить в тест с тесткейсами

"Но <em>внутри __одинарного__ не</em> работает. Конец\\\\</p>";

Normalize(RenderHtml(Paragraph)).Should().Be(expected);
}

Choose a reason for hiding this comment

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

Можно добавить тесты на граничные случаи (например, пустые строки/только пробелы между разделителями/максимальная вложенность/unicode символы и др)

[Test]
public void ParseAndRender_ParagraphFromSpec_AllRulesTogether_ProducesExpectedHtml()
{
const string Paragraph =

Choose a reason for hiding this comment

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

почему константа? почему с большой буквы? отличается от других тестов

}

[Test]
public void ParseAndRender_ParagraphFromSpec_AllRulesTogether_ProducesExpectedHtml()

Choose a reason for hiding this comment

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

антипаттерн, если тест упадет то не понятно почему именно упал и что сломалось, нарушен принцип 1 тест = 1 сценарий

[TestFixture]
public class PerformanceTests
{
private const string Paragraph =

Choose a reason for hiding this comment

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

Один фиксированный паттерн, не отражает реальное использование. Для более реальных тестовых данных можно сделать массив паттернов и рандомно генерировать из них текст

Copy link

@masssha1308 masssha1308 Nov 13, 2025

Choose a reason for hiding this comment

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

+ можно добавить несколько итераций для усреднения результатов теста

namespace MarkdownTests;

[TestFixture]
public class PerformanceTests

Choose a reason for hiding this comment

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

Можно добавить тесты на конкретные случаи, например, очень большая вложенность

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants