Нумерация строк

Библиотека Marked в JavaScript предназначена для преобразования Markdown в HTML. Одним из частых требований при работе с Markdown является поддержка нумерации строк, особенно при отображении кода в виде блоков с подсветкой. В Marked эта функциональность реализуется через комбинацию встроенных возможностей парсера и пользовательских расширений.


Основные принципы работы с нумерацией строк

Marked по умолчанию разбивает входной Markdown на токены: заголовки, абзацы, списки, блоки кода и др. Каждый блок кода представляется токеном вида:

{
  type: 'code',
  text: 'console.log("Hello, world!");',
  lang: 'javascript',
  escaped: false
}

Для нумерации строк ключевой является обработка блока кода перед генерацией HTML. Marked предоставляет renderer — объект с методами для каждого типа токенов. Метод code(code, infostring, escaped) отвечает за вывод блока кода.


Настройка собственного рендерера для нумерации строк

Для нумерации строк создается кастомный рендерер:

const marked = require('marked');

const renderer = new marked.Renderer();

renderer.code = (code, infostring) => {
  const lines = code.split('\n');
  const numberedLines = lines
    .map((line, index) => `${index + 1} ${line}`)
    .join('\n');

  return `
${numberedLines}
`; }; marked.setOptions({ renderer });

Пояснение:

  1. code.split('\n') — разбивает код на отдельные строки.
  2. map((line, index) => ...) — формирует HTML для каждой строки с указанием номера.
  3. Итог собирается в
     для
    сохранения форматирования.

Стилизация нумерации

HTML с номерами строк можно оформить с помощью CSS:

pre {
  display: flex;
  flex-direction: column;
}

.line-number {
  display: inline-block;
  width: 2em;
  user-select: none;
  color: #888;
  text-align: right;
  margin-right: 1em;
}

Особенности:

  • user-select: none предотвращает выделение номеров при копировании кода.
  • flex-direction: column обеспечивает вертикальное расположение строк.
  • margin-right отделяет номер от текста кода.

Поддержка подсветки синтаксиса

Для интеграции с библиотеками подсветки кода (Prism.js, Highlight.js) номера строк можно добавить отдельным элементом или через CSS counter:

pre.code-block {
  counter-reset: linenumber;
}

pre.code-block code span {
  counter-increment: linenumber;
}

pre.code-block code span::before {
  content: counter(linenumber);
  display: inline-block;
  width: 2em;
  text-align: right;
  margin-right: 1em;
  color: #888;
}

В этом подходе HTML не нуждается в явных номерах строк, они генерируются на стороне CSS.


Автоматизация нумерации через токены Marked

Можно использовать событие lexer для добавления номеров еще на этапе токенизации:

const tokens = marked.lexer(markdownText);

tokens.forEach(token => {
  if (token.type === 'code') {
    const lines = token.text.split('\n');
    token.text = lines
      .map((line, index) => `${index + 1} ${line}`)
      .join('\n');
  }
});

const html = marked.parser(tokens);

Такой метод позволяет:

  • Изменять код до генерации HTML.
  • Легко комбинировать с другими расширениями Marked.
  • Обеспечивать единообразие оформления кода с нумерацией.

Особенности работы с разными платформами

  • Node.js: рекомендуется использовать кастомный рендерер или модификацию токенов.
  • Браузер: при больших объемах кода лучше использовать CSS-счетчики для оптимизации рендеринга.
  • Подсветка синтаксиса: номера строк лучше отделять от текста, чтобы не ломать работу Highlight.js или Prism.js.

Практические советы

  1. Всегда использовать
     для
    сохранения форматирования.
  2. Если требуется копирование кода без номеров, оборачивать номера в отдельный .
  3. При динамическом рендеринге Markdown на клиенте учитывать производительность: генерация HTML через map для тысяч строк может быть ресурсоемкой.
  4. Для темных и светлых схем интерфейса нумерацию строк удобно стилизовать через переменные CSS.
javascript42 — изучаем JavaScript вместе