Парсер Markdown текста
Что такое Markdown
Если коротко - правила форматирования текста и перевода этого формата в HTML представление.
Подробно можно почитать на сайте проекта Markdown (англ.).
Существующие реализации
Есть несколько проектов реализаций работы с markdown и редактирования markdown, в частности
Showdown - редактор и парсер markdown на JavaScript (англ.).
Обычно реализации опираются на оригинальный исходный код Markdown, котрый написан на перле и имеет
несколько моментов, которые лично мне не нравятся:
- Преобразование с использованием RegExp: исходный текст много раз прогоняется через
регулярные выражения и в итоге мы получаем строку, содержащую HTML, т.е. просто работа с текстом.
- Жестко заданный HTML: мы не можем быстро поменять правила формирования HTML, для
этого нам придется переписать парсер.
- Т.к. идет работа просто с текстом, то для выделения всех ссылок на внешние ресурсы нам нужно делать дополнительный парсинг.
- Требовательное отношение парсеров к исходному тексту, ошибки при работе со сложным текстом.
Моя реализация
Код написан на JavaScript в инфраструктуре jQuery (из jQuery используется только реализация $.each(...) ) т.к. в дальнейшем планируется разработка редактора.
Реализация состоит из 2-х частей:
- Собственно парсер, который строит DOM модель
- Генератор HTML для DOM объектов
Такой подход дает множество преимуществ:
- Имея объектную модель мы можем оперировать объектами как нам захочется
- У нас есть массив ссылок на ресурсы, которые используются в тексте
- Мы можем легко заменять генератор HTML и генерить не только HTML
- Мы можем построить содержание текста (TOC) и сгенерить HTML для содержания с
автоматической генерацией ссылок на разделы текста
- Более корректный разбор текста, чем это делает showdown.
Отрицательная сторона: мой парсер работает в 2 раза медленнее, чем showdown.
Дополнение: после некоторых оптимизаций ситуация стала неоднозначной (время - мс)
Парсер | IE7 | FF3 | Safari | Chrome |
Showdown | 64 | 29 | 27 | 41 |
Моя реализация | 100 | 109 | 23 | 21 |
Парсер и DOM модель
Основные элемены DOM модели:
- ROOT - корневой элемент документа, в нем могут находится только P и Section элементы.
- Section - раздел текста, у него есть ряд свойств: заголовок и уровень. В нем могут находится только P и
Section элементы, при этом Section элементы должны иметь более низкий уровень.
- P - абзац, в нем могут находится элементы: L, QUOTE, PRE, HR, BR, EM, CODE, LINK, MEDIA.
- L - список, может быть как нумерованным, так и ненумерованным, в нем может находится только элемент LI.
- LI - элемент списка, в нем может находится только элемент P.
- QUOTE - цитата, в этом элементе может находится только элемент P.
- PRE - преформатированный текст, никакие элементы в нем находиться не могут.
- HR - горизонтальный разделитель, никакие элементы в нем находиться не могут.
- BR - перевод строки, никакие элементы в нем находиться не могут.
- EM - выделение текста (обычно жирный или курсив), в нем могут находится элементы: BR, EM, CODE, LINK, MEDIA.
- CODE - inline вставка преформатированного текста, никакие элементы в нем находиться не могут.
- LINK - ссылка на ресурс, в нем могут находится элементы: EM, CODE, LINK, MEDIA.
- MEDIA - отображение ресурса, никакие элементы в нем находиться не могут.
- URL - ссылка на ресурс и ее параметры
ROOT и URL пренадлежат документу, остальные элементы размещаются внутри ROOT.
Генератор HTML
Генератор состоит из 2-х частей: генератор HTML для всех элементов, кроме MEDIA и генератор отображения элемента MEDIA.
Генератор HTML для всех элементов, кроме MEDIA
для таких элементов нужно реализовать всего пару методов на элемент: генерация кода перед содержимым
и генерация кода после содержимого.
Генератор HTML для элементов MEDIA
Для этого элемента его содержимое находится вне текста и тип отображения диктуется ссылкой на содержимое,
поэтому для отображения элементов MEDIA идет анализ ссылки того, что нужно отобразить и в зависимости
от результатов вызывается соответствующий метод генерации кода.
Используя этот подход мы можем
совершенно прозрачно для того, кто пишет текст, генерить HTML код для отображения картинок,
swf, роликов с youtube и других подобных сайтов!
Обычно во всех WYSIWYG редакторах для веба используется другой подход: пользователь при форматировании текста должен
воспользоваться специальным инструментом, который вставляет только один тип ресурса (даже не тип, а код для него)
и очень часто панель такого редактора содержит кучу кнопок для вставки разных ресурсов. Я считаю это неправильным, т.к.
совершенно не напрягаясь можно написать код автоматического распознования типа вставляемого ресурса.
На данный момент распознаются:
- Картинки: jpg, gif, png.
- Flash ролики
- YouTube видео
Мои расширения синтаксиса Markdown
Имея объектную модель можно достаточно просто расширять синтаксис.
На данный момент я сделал пока только такие расширения:
- Можно выделять не только текст, но и абзацы, для этого достаточно в первой строке абзаца вставить признак выделения:
!i:
- абзац с сообщением
!!:
- абзац с предупреждением
!!!:
- абзац с очень важным сообщением
- Можно указать выравнивание в абзаце, для этого достаточно в первой строке абзаца вставить метку, аналогичную выделению:
!<:
- выравнивание по правому краю
!+:
- выравнивание по центру
!>:
- выравнивание по левому краю
Пока выравнивание можно объединить с выделением, но насколько это правильно - вопрос.
- У отображения ресурсов можно задавать размер:

И еще побочный эффект, который я пока решил не удалять: если написать такой текст !</img.png>
, то
вместо ссылки будет отображена картинка.
- Нумерованный списки можно обозначать не только цифрами, а так же символом решетки:
#.
- Теперь можно задавать подсветку синтаксиса для PRE блоков, для этого используется вовремя подоспевшая подсветка синтаксиса.
Для указания языка, который нужно подсветить, достаточно в первой строке блока вставить метку, аналогичную той, что ставится у абзаца:
!JS:
- синтаксис JavaScript
!CS:
- синтаксис C#
!XML:
- синтаксис XML
!HTML:
- синтаксис HTML
Вообще можно использовать любой синтаксис из $.DmSyntax
, если пакет синтаксиса не подключен, то синтаксис выделяться не будет.
- По-умолчанию медиа выводится в тексте, в особых случаях можно медиа размещать в отдельном блоке,
для этого достаточно написать два восклицательных знака вместо одного:
!
.
Планы по развитию
- Возможность задавать подсветку синтаксиса для PRE блоков, примерно как выделение для абзацев, вот и подсветка подоспела...
- Реализация надстрочного (
^надстрочного^
) и подстрочного (~подстрочного~
) текста.
- Замены в тексте: -- => —, (c) => ©, (r) => ®, (tm) => ™, ... => ….
- Дополнительный атрибут отображения ресурсов: в тексте или в отдельном блоке, так же теперь можно задавать выравнивание в абзаце
- Сноски
- Реализация на C# для использования на сервере
- Реализация вставки таблиц - есть мысли как это сделать просто и сердито с поддержкой col-, rowspan и выравниваний в ячейках
- Отображение разных типов MEDIA: rutube, и т.д.
- Реализация поддержки HTML вставок (хотя это есть зло!)
Пример работы парсера и генератора HTML
Исходный код
Можно менять текст в контроле, при потере фокуса текст будет обработан.
HTML, сгенеренный из DOM модели генератором по-умолчанию:
Нужно учесть, что именно такой вид отображения сделать только для того, что бы показать структуру получившихся объектов,
для получения чего-то более удобочитаемого нужно или переписать генераторы HTML или использовать другой CSS.