Библиотека 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 });
Пояснение:
code.split('\n') — разбивает код на отдельные
строки.map((line, index) => ...) — формирует HTML для
каждой строки с указанием номера. для
сохранения форматирования.
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;
}
Особенности:
Для интеграции с библиотеками подсветки кода (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.
Можно использовать событие 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);
Такой метод позволяет:
для
сохранения форматирования.
.map для тысяч
строк может быть ресурсоемкой.