Парсер Markdown, написанный на Go. Легко расширяемый, соответствует стандартам, хорошо структурированный.
goldmark соответствует стандарту CommonMark 0.31.2.
Мне требовался парсер Markdown для Go, который удовлетворяет следующим требованиям:
blackfriday.v2 — быстрое и широко используемое решение, но оно не соответствует стандарту CommonMark и не может быть расширено извне пакета, поскольку его AST использует структуры вместо интерфейсов.
Кроме того, его поведение отличается от поведения других реализаций в некоторых случаях, особенно относительно списков: Глубоко вложенные списки выводятся некорректно #329, Списочный блок не может иметь вторую строку #244, и т. д. Эта особенность иногда вызывает проблемы. Если вы мигрируете ваш текст в формате Markdown с GitHub на wiki-системы, основанные на blackfriday, многие списки сразу же будут повреждены.
Как было указано выше, CommonMark сложен и труден для реализации, поэтому парсеры Markdown, основанные на CommonMark, встречаются редко.
@username
в Markdown? Вы можете легко это сделать с помощью goldmark. Вы можете добавлять свои узлы AST, парсеры для элементов уровня абзаца, парсеры для элементов уровня строки, преобразователи для абзацев, преобразователи для всей структуры AST и рендереры.go test --fuzz
.$ go get github.com/yuin/goldmark
Импортировать пакеты:
import (
"bytes"
"github.com/yuin/goldmark"
)
Конвертировать документы в формате Markdown с режимом соответствия стандартам CommonMark:
var buf bytes.Buffer
if err := goldmark.Convert(source, &buf); err != nil {
panic(err)
}
var buf bytes.Buffer
if err := goldmark.Convert(source, &buf, parser.WithContext(ctx)); err != nil {
panic(err)
}
Функциональная опция | Тип | Описание |
---|---|---|
parser.WithContext |
parser.Context |
Контекст для этапа парсинга. |
Функциональная опция | Тип | Описание |
---|---|---|
parser.WithIDs |
parser.IDs |
IDs позволяет вам менять логику, связанную с ID элементов (например, автоматическое генерирование ID заголовков). |
import (
"bytes"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
)
md := goldmark.New(
goldmark.WithExtensions(extension.GFM),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
goldmark.WithRendererOptions(
html.WithHardWraps(),
html.WithXHTML(),
),
)
var buf bytes.Buffer
if err := md.Convert(source, &buf); err != nil {
panic(err)
}
```| Функциональный параметр | Тип | Описание |
| ------------------------ | ---- | ----------- |
| `goldmark.WithParser` | `parser.Parser` | Этот параметр должен передаваться до `goldmark.WithParserOptions` и `goldmark.WithExtensions` |
| `goldmark.WithRenderer` | `renderer.Renderer` | Этот параметр должен передаваться до `goldmark.WithRendererOptions` и `goldmark.WithExtensions` |
| `goldmark.WithParserOptions` | `...parser.Option` | |
| `goldmark.WithRendererOptions` | `...renderer.Option` | |
| `goldmark.WithExtensions` | `...goldmark.Extender` | |Параметры парсера и рендера
------------------------------
### Параметры парсера
| Функциональный параметр | Тип | Описание |
| ----------------------- | ---- | ----------- |
| `parser.WithBlockParsers` | `util.PrioritizedSlice`, чьи элементы являются `parser.BlockParser` | Парсеры для анализа элементов уровня блока. |
| `parser.WithInlineParsers` | `util.PrioritizedSlice`, чьи элементы являются `parser.InlineParser` | Парсеры для анализа элементов уровня встроенной строки. |
| `parser.WithParagraphTransformers` | `util.PrioritizedSlice`, чьи элементы являются `parser.ParagraphTransformer` | Преобразователи для преобразования узлов абзаца. |
| `parser.WithASTTransformers` | `util.PrioritizedSlice`, чьи элементы являются `parser.ASTTransformer` | Преобразователи для преобразования AST. |
| `parser.WithAutoHeadingID` | `-` | Включает автоматическое создание ID для заголовков. |
| `parser.WithAttributes` | `-` | Включает пользовательские атрибуты. В настоящее время поддерживаются только заголовки с атрибутами. |
### Параметры рендера HTML
| Функциональный параметр | Тип | Описание |
| ----------------------- | ---- | ----------- |
| `html.WithWriter` | `html.Writer` | `html.Writer` для записи содержимого в `io.Writer`. |
| `html.WithHardWraps` | `-` | Отображение новой строки как `<br>`. |
| `html.WithXHTML` | `-` | Отображение как XHTML. |
| `html.WithUnsafe` | `-` | По умолчанию, goldmark не отображает сырой HTML или потенциально опасные ссылки. С этим параметром goldmark отображает такое содержимое так, как это записано. |
### Встроенные расширения- `extension.Table`
- [GitHub Flavored Markdown: Tables](https://github.github.com/gfm/#tables-extension-)
- `extension.Strikethrough`
- [GitHub Flavored Markdown: Strikethrough](https://github.github.com/gfm/#strikethrough-extension-)
- `extension.Linkify`
- [GitHub Flavored Markdown: Autolinks](https://github.github.com/gfm/#autolinks-extension-)
- `extension.TaskList`
- [GitHub Flavored Markdown: Task list items](https://github.github.com/gfm/#task-list-items-extension-)
- `extension.GFM`
- Это расширение включает таблицы, зачеркивание, автоссылки и задачи.
- Это расширение не фильтрует теги, определённые в [6.11: Недопустимый сырой HTML (расширение)](https://github.github.com/gfm/#disallowed-raw-html-extension-).
Если вам требуется фильтровать HTML-теги, см. раздел [Безопасность](#security).
- Если вам требуется парсинг эмодзи GitHub, вы можете использовать [goldmark-emoji](https://github.com/yuin/goldmark-emoji) расширение.
- `extension.DefinitionList`
- [PHP Markdown Extra: Определитель списков](https://michelf.ca/projects/php-markdown/extra/#def-list)
- `extension.Footnote`
- [PHP Markdown Extra: Примечания](https://michelf.ca/projects/php-markdown/extra/#footnotes)
- `extension.Typographer`
- Это расширение заменяет знаки препинания на типографские сущности, как [smartypants](https://daringfireball.net/projects/smartypants/).
- `extension.CJK`
- Это расширение является сокращением для связанных с CJK функциональностей.
### Атрибуты
Параметр `parser.WithAttribute` позволяет вам определять атрибуты для некоторых элементов.
На данный момент атрибуты поддерживаются только для заголовков.
**Атрибуты обсуждаются на форуме [CommonMark](https://talk.commonmark.org/t/consistent-attribute-syntax/272).
Синтаксис может измениться в будущем.**#### Заголовки
```markdown
## Заголовок ## {#id .className attrName=attrValue class="class1 class2"}
## Заголовок {#id .className attrName=attrValue class="class1 class2"}
Заголовок {#id .className attrName=attrValue}
=================
Расширение таблиц реализует таблицы (расширение), определенное в спецификации GitHub Flavored Markdown.
Спецификации определены для XHTML, поэтому спецификация использует некоторые устаревшие атрибуты для HTML5. Вы можете переопределить метод выравнивания ячеек таблицы через параметры.
Функциональный параметр | Тип | Описание |
---|---|---|
extension.WithTableCellAlignMethod |
extension.TableCellAlignMethod |
Параметр указывает, как выравнены ячейки таблицы. |
Расширение Типограф преобразует простые знаки препинания ASCII в HTML сущности типографского оформления.
По умолчанию замены следуют:
Знак препинания | По умолчанию сущность |
---|---|
' |
‘ , ’
|
" |
“ , ”
|
-- |
– |
--- |
— |
... |
… |
<< |
« |
>> |
» |
Вы можете переопределить стандартные замены через extensions.WithTypographicSubstitutions
:
markdown := goldmark.New(
goldmark.WithExtensions(
extension.NewTypographer(
extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{
extension.LeftSingleQuote: []byte("‚"),
extension.RightSingleQuote: nil, // nil отключает замену
}),
),
),
)
```### Расширение Linkify
Расширение Linkify реализует [Автоссылки](https://github.github.com/gfm/#autolinks-extension/), как
определяется в [Спецификации GitHub Flavored Markdown](https://github.github.com/gfm/).
Поскольку спецификация не определяет детали URL, существует множество неоднозначных случаев.
Вы можете переопределить шаблоны автоссылок через параметры.
| Функциональный параметр | Тип | Описание |
| ------------------------ | ---- | ----------- |
| `extension.WithLinkifyAllowedProtocols` | `[][]byte \| []string` | Список допустимых протоколов, таких как `[]string{"http:"}` |
| `extension.WithLinkifyURLRegexp` | `*regexp.Regexp` | Шаблон регулярного выражения, который определяет URL, включая протоколы |
| `extension.WithLinkifyWWWRegexp` | `*regexp.Regexp` | Шаблон регулярного выражения, который определяет URL, начинающийся с `www.`. Этот шаблон соответствует [расширенной автоссылке www](https://github.github.com/gfm/#extended-www-autolink/) |
| `extension.WithLinkifyEmailRegexp` | `*regexp.Regexp` | Шаблон регулярного выражения, который определяет адрес электронной почты |
Пример использования [xurls](https://github.com/mvdan/xurls):
```go
import "mvdan.cc/xurls/v2"
markdown := goldmark.New(
goldmark.WithRendererOptions(
html.WithXHTML(),
html.WithUnsafe(),
),
goldmark.WithExtensions(
extension.NewLinkify(
extension.WithLinkifyAllowedProtocols([]string{
"http:",
"https:",
}),
extension.WithLinkifyURLRegexp(
xurls.Strict(),
),
),
),
)
Расширение примечаний реализует PHP Markdown Extra: Примечания.Это расширение имеет некоторые опции:
Функциональная опция | Тип | Описание |
---|---|---|
extension.WithFootnoteIDPrefix |
[]byte | string |
префикс для атрибутов ID. |
extension.WithFootnoteIDPrefixFunction |
func(gast.Node) []byte |
функция, которая определяет атрибут ID для данного узла. |
extension.WithFootnoteLinkTitle |
[]byte | string |
опциональный атрибут title для ссылок примечаний. |
extension.WithFootnoteBacklinkTitle |
[]byte | string |
опциональный атрибут title для обратных ссылок примечаний. |
extension.WithFootnoteLinkClass |
[]byte | string |
класс для ссылок примечаний. По умолчанию это footnote-ref . |
extension.WithFootnoteBacklinkClass |
[]byte | string |
класс для обратных ссылок примечаний. По умолчанию это footnote-backref . |
extension.WithFootnoteBacklinkHTML |
[]byte | string |
класс для обратных ссылок примечаний. По умолчанию это ↩︎ . |
Некоторые опции могут иметь специальные замены. Встречи «^^» в строке будут заменены соответствующим номером примечания в выводе HTML. Встречи «%%» будут заменены номером ссылки (примечания могут иметь несколько ссылок).
extension.WithFootnoteIDPrefix
и extension.WithFootnoteIDPrefixFunction
полезны, если вы отображаете несколько документов Markdown внутри одного HTML-документа, чтобы избежать столкновения атрибутов ID примечаний.
extension.WithFootnoteIDPrefix
устанавливает фиксированный префикс ID, поэтому вы можете писать коды следующего типа:
for _, path := range files {
source := readAll(path)
prefix := getPrefix(path)
}
``` markdown := goldmark.New(
goldmark.WithExtensions(
NewFootnote(
WithFootnoteIDPrefix(path),
),
),
)
var b bytes.Buffer
err := markdown.Convert(source, &b)
if err != nil {
t.Error(err.Error())
}
}
````extension.WithFootnoteIDPrefixFunction` определяет префикс ID, вызывая данную функцию, поэтому вы можете писать код в следующем виде:
```go
markdown := goldmark.New(
goldmark.WithExtensions(
NewFootnote(
WithFootnoteIDPrefixFunction(func(n gast.Node) []byte {
v, ok := n.OwnerDocument().Meta()["footnote-prefix"]
if ok {
return util.StringToReadOnlyBytes(v.(string))
}
return nil
}),
),
),
)
for _, path := range files {
source := readAll(path)
var b bytes.Buffer
doc := markdown.Parser().Parse(text.NewReader(source))
doc.Meta()["footnote-prefix"] = getPrefix(path)
err := markdown.Renderer().Render(&b, source, doc)
}
Вы можете использовать goldmark-meta, чтобы определить префикс ID в документе markdown:
---
title: document title
slug: article1
footnote-prefix: article1
---
# Моя статья
CommonMark придает совместимость высокий приоритет, а исходный markdown был создан западными пользователями. Поэтому CommonMark не учитывает особенности языков типа CJK.
Это расширение предоставляет дополнительные опции для пользователей CJK.| Функциональная опция | Тип | Описание |
| --------------------- | ---- | ----------- |
| extension.WithEastAsianLineBreaks
| ...extension.EastAsianLineBreaksStyle
| Нестрогий конец строки отображается как новая строка. Некоторым азиатским пользователям это может показаться лишним пробелом. С помощью этой опции, нестрогие концы строк между широкими символами CJK будут игнорироваться. По умолчанию используется EastAsianLineBreaksStyleSimple
. |
| extension.WithEscapedSpace
| -
| Без пробелов вокруг акцента, начального знака пунктуации CJK, он не будет интерпретирован как акцент (как определено в спецификации CommonMark). С помощью этой опции, можно избежать этого неудобства поведения, помещая "невидимые" пробелы вокруг акцента, например, 太郎は\ **「こんにちわ」**\ といった
. |#### Стили завершения строк| Стиль | Описание |
| ----- | ----------- |
| EastAsianLineBreaksStyleSimple
| Пропускаются мягкие переходы строки, если обе стороны перехода содержат широкие азиатские символы. Это поведение аналогично опции east-asian-line-breaks
в Pandoc. |
| EastAsianLineBreaksCSS3Draft
| Этот вариант реализует правила преобразования разделителей строк уровня CSS Text Level 3 Segment Break Transformation Rules с некоторыми улучшениями. |
EastAsianLineBreaksStyleSimple
Входной Markdown:
私{は}プログラマー{です}{。}
東京{の}会社{に}勤めています{。}
Go{で}Webアプリケーション{を}開発しています{。}
Выход:
<p>私はプログラマーです。東京の会社に勤めています。<br>GoでWebアプリケーションを開発しています。</p>
EastAsianLineBreaksCSS3Draft
Входной Markdown:
私{は}プログラマー{です}{。}
東京{の}会社{に}勤めています{。}
Go{で}Webアプリケーション{を}開発しています{。}
Выход:
<p>私はプログラマーです。東京の会社に勤めています。GoでWebアプリケーションを開発しています。</p>
По умолчанию, goldmark не отображает сырой HTML или потенциально опасные URL. Если вам требуется более строгое управление над непроверенным контентом, рекомендуется использовать санитайзер HTML, такой как bluemonday.
Вы можете запустить этот бенчмарк в директории _benchmark
.### против других библиотек на Go
blackfriday v2 кажется самым быстрым, но поскольку он не соответствует стандарту CommonMark, его производительность нельзя прямым образом сравнивать с другими библиотеками, поддерживающими этот стандарт.
goldmark, в свою очередь, создает чистую, расширяемую структуру AST, полностью соответствует стандарту CommonMark и потребляет меньше памяти при этом обеспечивая достаточно высокую скорость работы.
BenchmarkMarkdown/Blackfriday-v2-8 302 3743747 ns/op 3290445 B/op 20050 allocs/op
BenchmarkMarkdown/GoldMark-8 280 4200974 ns/op 2559738 B/op 13435 allocs/op
BenchmarkMarkdown/CommonMark-8 226 5283686 ns/op 2702490 B/op 20792 allocs/op
BenchmarkMarkdown/Lute-8 12 92652857 ns/op 10602649 B/op 40555 allocs/op
BenchmarkMarkdown/GoMarkdown-8 13 81380167 ns/op 2245002 B/op 22889 allocs/op
Как видно, производительность goldmark соответствует производительности cmark.Расширения
--------------------
### Список расширений
- [goldmark-meta](https://github.com/yuin/goldmark-meta): Расширение YAML метаданных для парсера Markdown goldmark.
- [goldmark-highlighting](https://github.com/yuin/goldmark-highlighting): Расширение синтаксического выделения для парсера Markdown goldmark.
- [goldmark-emoji](https://github.com/yuin/goldmark-emoji): Расширение эмодзи для парсера Markdown goldmark.
- [goldmark-mathjax](https://github.com/litao91/goldmark-mathjax): Поддержка MathJax для парсера Markdown goldmark.
- [goldmark-pdf](https://github.com/stephenafamo/goldmark-pdf): Представление в виде PDF, которое можно передать методу `goldmark.WithRenderer()`.
- [goldmark-hashtag](https://github.com/abhinav/goldmark-hashtag): Добавляет поддержку тегирования на основе `#hashtag` для goldmark.
- [goldmark-wikilink](https://github.com/abhinav/goldmark-wikilink): Добавляет поддержку ссылок типа `[[wiki]]` для goldmark.
- [goldmark-anchor](https://github.com/abhinav/goldmark-anchor): Добавляет анкоры (постоянные ссылки) рядом со всеми заголовками в документе.
- [goldmark-figure](https://github.com/mangoumbrella/goldmark-figure): Добавляет поддержку отображения абзацев, начинающихся с изображения, как элементы `<figure>`.
- [goldmark-frontmatter](https://github.com/abhinav/goldmark-frontmatter): Добавляет поддержку YAML, TOML и пользовательских данных в начале документов.
- [goldmark-toc](https://github.com/abhinav/goldmark-toc): Добавляет поддержку генерации оглавлений для документов goldmark.
- [goldmark-mermaid](https://github.com/abhinav/goldmark-mermaid): Добавляет поддержку отображения диаграмм Mermaid в документах goldmark.
- [goldmark-pikchr](https://github.com/jchenry/goldmark-pikchr): Добавляет поддержку отображения диаграмм Pikchr в документах goldmark.
- [goldmark-embed](https://github.com/com/13rac1/goldmark-embed): Добавляет поддержку отображения встраиваемых объектов из ссылок YouTube.
- [goldmark-latex](https://github.com/soypat/goldmark-latex): Представление LaTeX, которое можно передать методу `goldmark.WithRenderer()`.
- [goldmark-fences](https://github.com/stefanfritsch/goldmark-fences): Поддержка разделителей стилей Pandoc для парсера Markdown goldmark.
- [goldmark-d2](https://github.com/FurqanSoftware/goldmark-d2): Добавляет поддержку диаграмм D2.
- [goldmark-katex](https://github.com/FurqanSoftware/goldmark-katex): Добавляет поддержку математических выражений и уравнений KaTeX.
- [goldmark-img64](https://github.com/tenkoh/goldmark-img64): Добавляет поддержку встраивания изображений в документ как DataURL (зашифрованные base64).
- [goldmark-enclave](https://github.com/quailyquaily/goldmark-enclave): Добавляет поддержку встраивания видео с YouTube/Bilibili, [oembed X](https://publish.x.com/), [графики TradingView](https://www.tradingview.com/widget/), [виджета Quaily](https://quaily.com), [встроенных Spotify](https://developer.spotify.com/documentation/embeds), [встроенных Dify](https://dify.ai/) и аудио HTML в документ.
- [goldmark-wiki-table](https://github.com/movsb/goldmark-wiki-table): Добавляет поддержку встраивания таблиц Wikipedia.
- [goldmark-tgmd](https://github.com/Mad-Pixels/goldmark-tgmd): Представитель рендера Markdown для Telegram, который можно передать в `goldmark.WithRenderer()`.
### Загрузка расширений в режиме выполнения
[goldmark-dynamic](https://github.com/yuin/goldmark-dynamic) позволяет вам писать расширение для goldmark на Lua и загружать его в режиме выполнения без необходимости повторной компиляции.Для получения подробностей обратитесь к [goldmark-dynamic](https://github.com/yuin/goldmark-dynamic).
Goldmark внутренний (для разработчиков расширений)
---------------------------------------------------
### Обзор
Структура процессинга Markdown в Goldmark представлена ниже.
<Markdown в []byte, parser.Context>
|
V
+-------- parser.Parser ---------------------------
| 1. Парсинг блочных элементов в AST
| 1. Если парсится абзац, применяется
| ast.ParagraphTransformer
| 2. Прохождение по AST и парсинг блоков.
| 1. Обработка разделителей(элементов выделения) в конце
| парсинга блока
| 3. Применение parser.ASTTransformers к AST
|
V
<ast.Node>
|
V
+------- renderer.Renderer ------------------------
| 1. Прохождение по AST и применение renderer.NodeRenderer
| соответствующего типа узла
|
V
<Выход>
### Парсинг
Документы Markdown читаются через интерфейс `text.Reader`.
Узлы AST не содержат конкретного текста. Узлы AST имеют информацию о сегментах документов, представленную как `text.Segment`.
`text.Segment` имеет три атрибута: `Start`, `End`, `Padding`.
(TBC)
**TODO**
Примеры расширений можно найти в директории `extension`.
Сводка:1. Определите узел AST как структуру, содержащую встраивание `ast.BaseBlock` или `ast.BaseInline`.
2. Напишите парсер, реализующий `parser.BlockParser` или `parser.InlineParser`.
3. Напишите рендерер, реализующий `renderer.NodeRenderer`.
4. Определите ваше расширение для Goldmark, реализующее `goldmark.Extender`.Пожертвования
--------------------
BTC: 1NEDSyUmo4SMTDP83JJQSWi1MvQUGGNMZB
Лицензия
--------------------
MIT
Автор
--------------------
Юсуке Инузuka
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )