Блочные и инлайн-парсеры

Markdown-it — это высокопроизводительный Markdown-парсер для JavaScript, который предоставляет гибкую архитектуру для обработки текста. Основой этой архитектуры являются блочные (block) и инлайн (inline) парсеры, обеспечивающие разделение текста на смысловые уровни и их корректное преобразование в HTML.


Блочные парсеры

Блочные парсеры отвечают за разбиение текста на структурные элементы документа, такие как заголовки, списки, цитаты, блоки кода и параграфы. В Markdown-it каждый блочный элемент представлен объектом Token с типом (type), уровнем вложенности (level) и дополнительными свойствами.

Основные типы блочных токенов

  • paragraph_open / paragraph_close — открывающий и закрывающий теги параграфа.
  • heading_open / heading_close — заголовки h1–h6, в зависимости от уровня.
  • fence / code_block — блоки кода: fence используется для многострочного кода с поддержкой языка, code_block — для простого.
  • blockquote_open / blockquote_close — цитаты.
  • list_item_open / list_item_close и bullet_list_open / ordered_list_open — элементы и контейнеры списков.
  • hr — горизонтальная линия.

Принцип работы блочного парсера

  1. Лексический разбор — исходный текст разбивается на строки и анализируется по регулярным выражениям для выявления блочных конструкций.
  2. Генерация токенов — каждая строка или группа строк преобразуется в один или несколько токенов.
  3. Вложенность — блочные токены могут содержать дочерние токены, например, элементы списка содержат параграфы или другие списки.

Блочные парсеры вызываются один раз для каждой строки или группы строк и определяют структуру документа до того, как начнётся инлайн-парсинг.


Инлайн-парсеры

Инлайн-парсеры отвечают за обработку текста внутри блочных элементов. Они распознают ссылки, эмфазис, изображения, код, автолинки и другие конструкции, которые находятся внутри параграфов, заголовков или блоков цитат.

Типы инлайн-токенов

  • text — обычный текст.
  • em_open / em_close — курсив (text).
  • strong_open / strong_close — жирный текст (text).
  • link_open / link_close — ссылки с атрибутами href и title.
  • image — изображения с атрибутами src и alt.
  • code_inline — встроенный код с использованием обратных кавычек.
  • softbreak / hardbreak — перенос строки: мягкий и жёсткий.

Механизм инлайн-парсинга

Инлайн-парсер работает на уровне токена блочного элемента:

  1. Получает текст блочного токена (например, параграфа).
  2. Скользит по символам текста, применяя правила для распознавания Markdown-конструкций.
  3. Создаёт последовательность инлайн-токенов, которые будут вставлены внутрь блочного токена.
  4. Обеспечивает корректную вложенность, позволяя комбинировать жирный текст, ссылки и код в одном параграфе.

Настройка и расширение парсеров

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

Добавление новых блочных правил

const md = require('markdown-it')();

md.block.ruler.before('paragraph', 'custom_block', (state, startLine, endLine, silent) => {
  const lineText = state.getLines(startLine, startLine + 1, 0, false);
  if (!lineText.startsWith('::custom')) return false;

  if (!silent) {
    const token = state.push('custom_block_open', 'div', 1);
    token.attrs = [['class', 'custom-block']];
    state.push('inline', '', 0).content = lineText.slice(8).trim();
    state.push('custom_block_close', 'div', -1);
  }

  state.line = startLine + 1;
  return true;
});
  • ruler.before — добавляет правило перед существующим.
  • state.push — создаёт новый токен.
  • inline — используется для инлайн-парсинга содержимого блока.

Добавление новых инлайн-правил

md.inline.ruler.after('emphasis', 'highlight', (state, silent) => {
  const pos = state.pos;
  if (state.src.charCodeAt(pos) !== 0x7E) return false; // символ ~
  if (!silent) {
    const token = state.push('highlight_open', 'mark', 1);
    state.push('text', '', 0).content = 'выделенный текст';
    state.push('highlight_close', 'mark', -1);
  }
  state.pos += 1;
  return true;
});
  • inline.ruler.after позволяет вставить правило после существующего.
  • state.src и state.pos управляют текущей позицией в тексте.
  • highlight_open/close создают новый тип инлайн-токена.

Архитектурные особенности

  1. Разделение уровней — блочные парсеры обрабатывают структуру документа, инлайн-парсеры — содержимое блоков.
  2. Система токенов — каждый элемент Markdown представляется токеном с типом, уровнем и вложенными токенами.
  3. Расширяемость через плагины — позволяет добавлять новые правила, переопределять стандартные и создавать собственные синтаксические конструкции.
  4. Согласованность вложенности — блочные и инлайн-токены формируют иерархию, что обеспечивает корректное рендеринг HTML.

Практическая схема работы

  1. Входной Markdown-текст разбивается блочным парсером на токены верхнего уровня.
  2. Каждый блочный токен с типом inline передаётся в инлайн-парсер.
  3. Инлайн-парсер генерирует вложенные токены для форматирования текста.
  4. Рендерер превращает токены в HTML, учитывая вложенность и атрибуты.

Эта архитектура обеспечивает максимальную гибкость при обработке Markdown, позволяя точно контролировать парсинг и расширять синтаксис без изменения ядра библиотеки. Блочные и инлайн-парсеры работают в тандеме, создавая структурированный и форматированный вывод.