Джон МакФарлейн — Помимо уценки
Первоначально это эссе было опубликовано по адресу https://talk.commonmark.org/t/beyond-markdown/2787 в Апрель 2018 г.
При разработке Commonmark мы старались, насколько это возможно, оставаться верным оригинальному описанию синтаксиса Markdown Джона Грубера. Мы отклонялись от него лишь изредка, в интересах устранения двусмысленность и увеличивающееся единообразие, а с добавлением нескольких элементы синтаксиса, которые теперь практически вездесущи (например, огороженный код блоки и краткие справочные ссылки).
Есть очень веские причины для такого консерватизма. Но это уважение к прошлому сделало спецификацию CommonMark очень сложный зверь. Есть 17 принципы, управляющие ударением, например, и эти правила по-прежнему оставляют дела нерешенными. Правила для списка элементы и блоки HTML тоже очень сложный. Все эти правила приводят к неожиданным результатам иногда, и они делают написание парсера для CommonMark сложным роман.
Что, если бы мы не были прикованы к прошлому? Что, если мы попытаемся создать легкий синтаксис разметки, который сохраняет все хорошее, что есть в Markdown, в то время как пересмотр некоторых функций, которые привели к раздуванию и сложности в спецификация CommonMark?
Позвольте мне сразу прояснить, что я не предлагаю никаких изменений в цели проекта Commonmark. Если эти размышления к чему-нибудь приведут, вероятно, это должен быть совершенно новый проект под новым названием. И, будучи реалистичным, бремя поддержания обратной совместимости легкий по сравнению с огромными практическими затратами на перемещение существующих системы на новый облегченный язык разметки. Тем не менее… я думаю, что это может быть полезно мечтать.
Далее я рассмотрю шесть функций Markdown, которые я думаю, создали больше всего трудностей, и я предложу, как каждая боль точку можно исправить.
1. Акцент
В Markdown выделение создается за счет окружающего текста *
или _
символов, *вот так*
.
Сильный акцент создается путем их удвоения, **вот так**
. Все это звучит очень просто, и это
визуально ясно, какой из них является сильным акцентом.
К сожалению, этих простых утверждений недостаточно, чтобы точно определить синтаксис. Рассмотрим, например,
**этот* текст**
Наши простые правила согласуются с обоими этими показаниями:
-
это* текст
-
этот текст*
Итак, чтобы полностью указать разбор выделения, нам нужны дополнительные правила. 17 обескураживающе сложных правил в спецификации CommonMark предназначены для форсировать виды чтений, которые люди сочтут наиболее естественными.
Мне кажется, что использование двойных символов для сильного
ударение, и возможность выделения даже части слова, как в fan*tas*tic
, сделали проблему с указанием акцента
синтаксический анализ намного хуже, значительно увеличивая неоднозначность, которую должна иметь спецификация. решать. В зависимости от контекста, строка из трех ***
в
середина слова может быть любой из следующих:
- Символ
*
, за которым следует начало сильного акцент. - Конец сильного ударения, за которым следует
*
характер. - Конец обычного выделения, символ
*
, затем начало нормального ударения. - За окончанием сильного ударения следует начало нормального акцент.
- За концом обычного акцента следует начало сильного акцент.
- Конец обычного ударения, за которым следует литерал
**
. - Литерал
**
с последующим началом нормального акцент. - Литерал
***
.
Как закрепить упор
Чтобы значительно уменьшить двусмысленность, мы можем удалить двойное
разделители символов для сильного выделения. Вместо этого используйте один _
для штатного упора, и одинарный *
для
сильный акцент. Акцент теперь будет начинаться с левого фланга, но не
правосторонний разделитель и заканчивается правосторонним, но не
левосторонний разделитель того же вида.
Для выделения внутри слова нам потребуется специальный синтаксис:
вентилятор~_tas_~tic
Акцент внутри слова встречается крайне редко, так что это хороший компромисс.
это немного сложнее, в обмен на упрощение правил (и
концептуальная модель) для акцента в целом. Специальный персонаж
здесь действует как пробел для разбора акцента
(позволяя внутрисловному _
начинать и заканчивать ударение), но
не отображается как пробел. (Таким образом, он ведет себя как экранированный пробел
в reStructuredText.)
Добавлено 22.04.2022: Это также дало бы нам способ выразить
что-то, что в настоящее время невыразимо в общем знаке: a[b]c
. На данный момент никак
написать это с правилами общего знака, так как в a*[b]*c
первый *
правофланговый и самый
второй *
левофланговый. Но с предложенным синтаксисом это
можно записать как a~_[b]_~c
.
Обычная обработка ссылок делает невозможным классифицировать любой элемент синтаксиса, пока весь документ не будет проанализирован. Например, рассмотрим
[фу] [бар] [баз] [бар]: адрес
Это интерпретируется как
foo[баз]
Но предположим, что мы определяем ссылку для на базе
вместо бар
:
[фу] [бар] [баз] [баз]: адрес
Тогда получаем:
[foo]бар
Таким образом, мы не можем сказать, является ли [foo]
буквально заключенным в квадратные скобки
текст или ссылка с описанием ссылки foo
, пока мы не проанализируем
весь документ.
Это очень затрудняет подсветку синтаксиса, а также усложняет написание парсеров. Например, вы не можете парсить ссылки, то разрешать ссылки в AST после анализа документа.
Как исправить справочные ссылки
Сделать ссылочные ссылки узнаваемыми только по их форме, независимыми какие ссылки определены в документе.
[фу] [бар] [баз]
будет проанализировано как ссылка с текстом ссылки foo
куда угодно
URL определяется для ссылки бар
(или ничего, если нет
определено), за которым следует буквальный текст [баз]
.
Ссылки на ярлыки, такие как
[фу] [foo]: адрес
пришлось бы запретить (если только мы не собирались заставлять писателей экранировать все буквальные скобки). Компактная форма может быть вместо этого используется:
[фу] [] [foo]: адрес
Это немного больше, но ясно и недвусмысленно, что есть ссылка.
3. Кодовые блоки с отступом и списки
Разбор блоков кода с отступом прост, но их присутствие усложняет правила для элементов списка.
При указании синтаксиса для элементов списка нам нужно указать, насколько далеко содержимое должно иметь отступ, чтобы считаться частью списка элемент. Первоначальные документы синтаксиса Markdown намекали на «четыре пробела».
правило», требующее отступа в четыре пробела, но реализации редко последовало за этим, и большинство людей находят нелогичным, что 9— содержимое должно быть с отступом сюда.Это неплохое правило, но оно добавляет сложности: нужно следить не только позиции маркера списка, но и позиции первое содержимое, не являющееся пробелом, которое следует за ним. И тут нужен спец. правила для таких случаев, как пустые элементы списка и элементы списка, начинающиеся с код с отступом. Наконец, многие до сих пор находят удивительным, что для например, это не вложенный список:
- а - б
Таким образом, можно спросить: почему бы просто не потребовать, чтобы содержимое списка элемент должен иметь отступ хотя бы на один пробел после маркера списка? Это очевидное минимальное правило. Что блокирует это наличие кода с отступом блоки. Если контент блочного уровня под элементом списка начинается с одного пробела отступ после маркера списка, тогда код с отступом должен быть с отступом в пять пробелов после маркера списка.
99. Вот мой пункт списка. И это код с отступом! Хотя это совпадает с абзацем выше!
Подводя итог: большая часть сложности правил для элементов списка мотивировано необходимостью иметь дело с блоками кода с отступом.
Как исправить отступ кодовые блоки и списки
Огражденные блоки кода теперь обычно предпочтительнее кодовых блоков с отступом, потому что вы можете указать синтаксис для выделения, и вам не нужно отступ/отступ при копировании и вставке кода. Поскольку у нас есть огороженный код блоки, нам не нужны блоки кода с отступом. Итак, мы можем просто избавиться от их.
Это освобождает отступ для более гибкого использования для обозначения списка вложенности, и мы можем принять простое и очевидное правило, что содержимое элемента списка должен иметь отступ не менее одного пробела относительно списка маркер.
Еще одним преимуществом удаления блоков кода с отступом является то, что начальный отступы теперь можно вообще игнорировать, за исключением тех случаев, когда они влияют на списки.
4. Необработанный HTML
С самого начала вы могли вставлять необработанный HTML в Markdown документы, и он будет передан дословно. Идея в том, что вы может вернуться к необработанному HTML для всего, что не может быть выражено в простой текст.
Звучит проще, чем есть на самом деле. С начала, Markdown.pl
различает встроенный и блочный уровни
HTML. Встроенные теги HTML передавались дословно, но их содержимое
можно интерпретировать как Markdown:
**привет**
даст вам
привет
HTML-контент блочного уровня, как было оговорено, должен быть разделен пустые строки, а начальный и конечный теги не должны иметь отступ. В таком HTML-блоки, все будет передаваться дословно, а не интерпретируется как уценка. Итак,
<дел> *привет* дел>
даст вам
<дел> *привет* дел>
Это вызвало несколько проблем. Во-первых, как мы идентифицируем блочный уровень
содержание? Нужно ли жестко запрограммировать список HTML-элементов, которые могут измениться?
по мере развития HTML? Как насчет таких элементов, как
, которые
может происходить во встроенном или блочном контексте?
Во-вторых, как насчет блочного HTML, который не разделен должным образом и с отступом?
приветпривет
Должны ли синтаксические анализаторы рассматривать его как встроенный HTML и генерировать неверный HTML?
В-третьих, как определить конец блока HTML? Учитывая, что теги
могут быть вложенными, для этого требуется нетривиальный анализ HTML. выпущенный
версия CommonMark для блоков HTML была разработана, чтобы упростить
анализировать необработанные блоки HTML (без неопределенного просмотра вперед или полного
реализация парсинга HTML), а также сделать возможным
авторам включать содержимое CommonMark в HTML-теги блочного уровня, если
они хотели. Но результат довольно сложный: семь различных пар
начальных и конечных условий. Правила для встроенного HTML также сложны,
с большим количеством определений. Кроме того, поскольку Markdown стал полезен не только для создания
HTML, но для создания документов в различных форматах
то, как HTML выделяется для необработанной передачи, стало казаться немного
произвольный. Тем, кто пишет в других форматах, будет полезен способ
пройти через сырой контент, тоже. Вместо того, чтобы проходить через необработанный HTML, мы должны ввести специальный
синтаксис, который позволяет передавать необработанный контент любого формата. Для этого
мы можем перегрузить наши существующие контейнеры для необработанных строк: диапазоны кода и
кодовые блоки: Но мы можем сделать и LaTeX: Мы могли бы даже передать различное необработанное содержимое в разные
форматы, например включая версии HTML и LaTeX сложного
фигура. Может ли список прерывать абзац, как здесь? Оригинальная документация по синтаксису Markdown не решает этого вопроса, но Однако было сделано одно исключение: когда текст абзаца сам по себе часть элемента списка, пустая строка не требуется. Если бы это исключение не было сделано, то мы бы не смогли
распознать вложенный список в этом случае: Размышляя о спецификации CommonMark для элементов списка, мы поняли
что поведение не содержит списка, тогда не содержит подсписка. Мы считаем, что принцип единообразия
это важно. Действительно, то, как мы указываем элементы списка и блочные кавычки
предполагает это. Это означает, что мы оказались перед выбором: либо требовать
пустая строка между текстом абзаца и следующим списком или списки разрешений
прерывать абзацы и рисковать случайным толкованием абзаца
текст в виде списка. Мы выбрали первый вариант, чтобы исключить его из обсуждения, поскольку он
в Markdown очень распространено иметь узкие подсписки без предшествующего
пустая строка. Поэтому мы выбрали второй вариант, смягчив ущерб
с уродливой эвристикой (мы позволяем только упорядоченному списку прерывать
абзац, когда номер списка равен Нам нужна пустая строка между текстом абзаца и списком.
Всегда. То есть даже в подсписках. Таким образом, чтобы составить плотный список с
подсписок, вы должны написать: Мы будем говорить, что список плотный, если он содержит хотя бы одну пару элементов
без пустой строки между ними, поэтому в приведенном выше примере внутренний список
плотный, а внешний список — нет. Чтобы сжать оба списка: Markdown не предлагает общего способа добавления атрибутов (таких как классы или
идентификаторы) к элементам. Это лишает его нативного способа создания
внутренние ссылки на разделы документа. (Многие реализации имеют
представил несколько разных способов автоматической генерации идентификаторов
из заголовков.) Это также лишает его естественного механизма расширения.
В Markdown есть контейнеры для встроенных строк (например, выделения), блоков (например, block
цитата), и необработанный встроенный контент (кодовые области), и необработанный блочный контент (код
блоки). Если бы к ним можно было присоединить произвольные атрибуты, они могли бы
манипулировать фильтрами для получения очень гибкого вывода. Например,
можно рассматривать блочную цитату с классом «предупреждение» как предупреждение
предостережение, или можно рассматривать кодовый блок с классом «точка» как
точечная диаграмма graphviz, которая будет отображаться как изображение. В настоящее время, однако,
единственный способ прикрепить атрибуты к элементу — это выпадающий список
HTML. Введите синтаксис для спецификации атрибута. Вслед за пандоком,
используйте для этого фигурные скобки Разрешить добавление атрибутов в строку до любого блока
элемент и непосредственно после любого встроенного элемента: Здесь в заголовок добавлен идентификатор Спецификаторы атрибутов должны помещаться в одну строку, но можно использовать несколько
(и затем будут объединены): Возможно, было бы полезно добавить синтаксис для неукрашенного встроенного
spans и огороженный универсальный блочный контейнер, как в pandoc. Но мы можем
использовать выделение для встроенного контейнера и кавычки для блока
контейнер, так что это не было бы абсолютно необходимым. Акцент Справочные ссылки Код Списки HTML Атрибуты Итак, как я уже говорил, что-то всегда беспокоило меня в строке У вас есть смирение, благородство и чувство чести, которые действительно очень редки. Моя реакция всегда была такой: « …что очень редко ? Разве они не имеют в виду, что — это ?» Эта мысль всегда сопровождалась: «Ой, подождите, не только чувство чести бывает редко. Смирение и благородство тоже». А потом: «Хорошо, так почему я продолжаю хотеть, чтобы это было просто чувство чести, которое редко встречается?» И, в конце концов, я бы сдался. Это было за несколько лет до того, как я узнал термин относительное предложение , еще больше, прежде чем я узнал разницу между ограничительными (также известными как интегрированные) относительными предложениями и неограничительными (также известными как неинтегрированные или дополнительные), как показано здесь: Интегрированный родственник выделяет только ультрафиолетовый свет, который мы купили для целей обнаружения мочи, из всех других ультрафиолетовых лучей, о которых я, возможно, хотел бы поговорить, например, для моей коллекции флуоресцентных минералов или моего домашнего солярия. Или, если уже есть только один ультрафиолетовый свет, о котором я мог бы разумно говорить, встроенное относительное предложение может быть просто фоновым напоминанием о том, почему мы его купили. (Случаи, подобные последнему, объясняют, почему некоторые грамматики предпочитают более общий термин 9).0429 интегрировал относительное предложение в ограничительное относительное предложение , поскольку относительное предложение не используется для ограничения некоторого набора возможных ссылок. Чтобы сделать это еще более ясным, рассмотрим солнце, которое является источником всей жизни .) Дополнительный родственник, OTOH, должен относиться к объекту, который определенно уже был идентифицирован — к некоторому конкретному ультрафиолетовому свету, который мы все знаем. о. В отличие от встроенного относительного предложения, дополнительное относительное предложение обычно представляет новую информацию или, по крайней мере, информацию о том, что говорящий Такова семантическая разница между интегрированными и дополнительными относительными предложениями. Семантическое различие соответствует синтаксическому различию. Я начну с интегрированного относительного предложения: УФ-свет, который мы купили , имеет следующую структуру: Относительное предложение образует часть («составляющую») с существительным, и вся эта составляющая (обозначенная N’ ) — это то, что присоединяется к определителю. (Откуда мы это знаем? Я приберегу это для другого поста, но если вам не терпится, см. обзор на стр. 1061-1063 в CGEL.) Однако дополнительное относительное предложение находится выше в дереве. Некоторые утверждают, что его лучше всего представить следующим образом, что будет достаточно для наших целей: Интересным свидетельством этих различных структур является тот факт, что имена собственные (которые действуют как полные NP) могут обозначаться с помощью дополнительные относительные предложения (например, Чарли Браун, который никогда не выигрывает ), но не интегрированные (* Чарли Браун, который/который никогда не выигрывает ). Если вы хотите поставить интегрированное относительное предложение с собственным существительным, вы должны превратить его в нарицательное и поставить с определителем: Чарли Браун, которого я знаю; каждый Линус, которого я когда-либо встречал . Возвращаясь к тексту песни, , которые действительно очень редки, должны быть интегрированным относительным предложением, потому что дополнительные не могут начинаться с , что . Следовательно, самое далекое, что он может сделать, чтобы сформировать составную часть, — это чувство чести . Дальше этого, и он принимает определитель a , и это должно быть дополнительное относительное предложение. Таким образом, наша составляющая — это чувство чести, которые действительно очень редки , и у нас есть ряд разногласий между чувством и — это . Но подождите: я предполагаю, что авторы песни хотели, чтобы относительное предложение модифицировало только чувство чести , а не смирение и благородство . То есть, я предполагаю, что они хотели сказать, что у Чарли Брауна есть какое-то неспецифическое смирение и благородство (может быть, потому, что нет разных разновидностей ч. и н.), но что у него нет просто какого-то старого смысла. чести: У него очень редкий s. ч. Что, если это предположение неверно? Что, если бы авторы песен хотели сказать, что у Чарли Брауна есть не обычное смирение или благородство, а особая, редкая разновидность каждого из них? Вероятно, они хотели сказать это; в конце концов, они выбрали это редкий вместо это редкий . Опять же, проблема заключается в том, что интегрированное относительное предложение , которое встречается очень редко, , должно модифицировать N, а не весь NP. Он прекрасно справляется с этим с абстрактным существительным, например смирение . Просто представьте себе такую структуру: Смирение неоднозначно между N и полным NP. Так же и дворянство . Однако при согласовании их с NP чувство чести , вы фактически объявили их NP, и теперь они больше не имеют права на интегрированное относительное предложение. Если вы хотите говорить не о смирении, благородстве и чувстве чести вообще, а о редких видах каждого, вы просто не сможете сделать это так, как пытались здесь авторы песен. Еще один аспект для размышления: возможно, авторы песен не ограничивали себя только редкими видами смирения, благородства и чувства чести. Может быть, они хотели сказать, что эти качества в любом виде встречаются редко. В этом случае то, что они хотели, было дополнительным относительным предложением, которое может быть связано с полными NP без проблем. В таком случае на было бы хорошо, но они должны были использовать на вместо на . (И, как заметил по крайней мере один комментатор, вы также можете просто сказать , что действительно очень редко, — другими словами, редко кто-либо обладает этими качествами, что равносильно тому, что сказать, что эти качества редки.) Я думаю, это действительно здорово, что теоретический синтаксис может пролить свет на то, что было для меня загадкой в течение многих лет, и точно определить источник оскорбления. Хотя я не указал, где мой анализ согласуется и не совпадает с мнением комментаторов, я верю, что каждый из них может принять соответствующие части этого поста в качестве моего ответа. Markdown.pl
создала недопустимый HTML для
дважды вложенный элемент Как исправить необработанный HTML
Это необработанный HTML: ` jpg">`{=html}.
А вот HTML-блок:
```{=html}
<дел>
<дел>
```
```{=латекс}
\begin{tikzpicture}
\node[inner sep=0pt] (Рассел) в (0,0)
{\includegraphics[width=0,25\textwidth]{bertrand_russell.jpg}};
\node[inner sep=0pt] (белая точка) в (5,-6)
{\includegraphics[width=0,25\textwidth]{alfred_north_whitehead.jpg}};
\draw[<->,толстый] (russell.south east) -- (whitehead.north west)
node[midway,fill=white] {Принципы математики};
\end{tikzpicture}
```
5. Списки и пустые строки
Проверка абзаца.
- Пункт один
- Пункт второй
Markdown.pl Для
и его набора тестов требуется пустая строка между
текст абзаца и следующий список. Как показывает набор тестов, это
требование было введено во избежание случайного создания
списки по таким вещам, как: Я думаю, что он весил 200 фунтов, может быть, даже
220. Но он был не выше пяти футов ростом.
- Параграф первый
параграф второй
- элемент подсписка один
- элемент второго подсписка
- а
- б
- с
- д
Markdown.pl
нарушает то, что мы назвали принцип единообразия, который гласит, что содержимое списка
элемент должен иметь то же значение, что и за пределами списка
элемент. Этот принцип подразумевает, что если а
- б
- с
- а
- б
- с
- д
1
). Как исправить списки и пробелы
строки
- а
- б
- с
- д
- а
- б
- с
- д
6. Атрибуты
Как исправить атрибуты
{}
. Идентификатор указывается с #
. Простое слово рассматривается как класс (или начальный .
может потребоваться как в pandoc). Используйте =
для
произвольный атрибут ключ/значение. {#мой заголовок}
# *Синий заголовок*{синяя позиция=левая}
myheader
блок, а класс синий
и атрибут ключ/значение position=left
добавляются к выделенному тексту Синий Название
. {#моепредупреждение}
{предупреждение}
> Не пытайтесь повторить это дома!
> Это может быть опасно.
~
ведет себя как пробел в том, что касается разбора акцента, но визуализируется как
ничего.
теперь будет восприниматься как обычный текст и
сбежал.
{=ФОРМАТ}
; в блочном контексте — огороженный блок кода с информацией
строка {=ФОРМАТ}
. {класс #идентификатор ключ=значение}
. Смирение, благородство и т. д., часть II « Буквальное мышление
делает его более заметным, чем это было бы в интегрированном относительном предложении.