| name | rsdn-dev |
| description | Правила разработки RsdnFormatter |
Правила разработки
Форматирование
- Для отступов спользуется табуляция. Размер табуляции - 2 символа.
Организация файлов
- Один тип верхнего уровня — один файл — каждый публичный класс, структура, интерфейс, enum в отдельном файле
- File-scoped namespaces для всех файлов
- ** Namespace файла должен соответствовать структуре каталогов проекта
- Для нескольких методов-расширений в одном классе используем синтаксис extension
namespace Rsdn.Framework.Formatting.BBCode.Nodes;
public class TextNode : Node { }
namespace Rsdn.Framework.Formatting.BBCode.Nodes
{
public class TextNode : Node { }
}
C# 14 Features
Primary Constructors
Использовать для классов с обязательной инициализацией:
public class TagNode(string tagName, string? attribute = null) : Node
{
public string TagName { get; } = tagName.ToLowerInvariant();
public string? Attribute { get; } = attribute;
public List<Node> Children { get; } = [];
}
Collection Expressions
public List<Node> Children { get; } = [];
public List<Node> Children { get; } = new List<Node>();
Target-typed New
var document = new DocumentNode();
Dictionary<string, string> map = new() { ["key"] = "value" };
Pattern Matching
public override string ToString() => Type switch
{
TokenType.Text => $"Text: [{TextRange.Start}..{TextRange.End}]",
TokenType.OpenTag => $"OpenTag: [{TagNameRange.Start}..{TagNameRange.End}]",
_ => $"Unknown: {Type}"
};
if (child is TextNode textNode)
{
var text = textNode.Text;
}
File scoped namespace
namespace Rsdn.Framework.Formatting.CodeFormat;
public class CodeFormatter
{
}
Типы данных
ref struct
Использовать для работы с ReadOnlySpan<T> без аллокаций:
public ref struct Tokenizer(ReadOnlySpan<char> text)
{
private readonly ReadOnlySpan<char> _text = text;
public int Position { get; private set; } = 0;
}
readonly struct
Использовать для immutable структур данных:
public readonly struct Token
{
public TokenType Type { get; }
public int Position { get; }
}
Nullable Reference Types
Проект использует NRT — явно указывать ? для nullable типов:
public string? Attribute { get; }
public string TagName { get; }
Паттерны проектирования
Visitor Pattern
Использовать для обработки AST:
public interface INodeVisitor<TContext>
{
void Visit(DocumentNode node, TContext ctx);
void Visit(TagNode node, TContext ctx);
void Visit(TextNode node, TContext ctx);
}
public abstract class Node
{
public abstract void Accept<TContext>(INodeVisitor<TContext> visitor, TContext ctx);
}
Token-based Parsing
Разделять лексический и синтаксический анализ:
Текст → Tokenizer → Токены → Parser → AST → Renderer → HTML
TextRange вместо строк
Использовать диапазоны в токенах для избежания аллокаций:
public readonly struct TextRange
{
public int Start { get; }
public int End { get; }
public int Length => End - Start;
}
public string? GetTextString(ReadOnlySpan<char> source)
{
var span = source.Slice(TextRange.Start, TextRange.Length);
return span.IsEmpty ? null : span.ToString();
}
Тестирование
- Фреймворк: NUnit
- Именование:
MethodName_Scenario_ExpectedResult
- Организация: по
#region категориям (Tokenizer Tests, Parser Tests, Integration Tests)
[Test]
public void Parser_BoldTag_ReturnsTagNode()
{
var parser = new Parser("[b]text[/b]");
var doc = parser.Parse();
Assert.AreEqual(1, doc.Children.Count);
Assert.IsInstanceOf<TagNode>(doc.Children[0]);
}