Авторы статей на «Хабре» часто жалуются, что встроенный редактор текстов на сайте не поддерживает исходную разметку кода и подсвечивает его элементы некорректно. Максим Зинченко, наш разработчик-эксперт, решил эту проблему. В своем посте он рассказал о процессе создания инструмента, который переносит раскраску кода из IDEA в статью на «Хабре», и об особенностях его использования. Подробности — в материале «Как сделать правильную раскраску кода на „Хабре“ и почему это так сложно».

Пару месяцев назад я опубликовал свой первый пост на «Хабре». Возможно, кто-то из вас заметит, что код в статье раскрашен необычным образом, а главное — раскрашен правильно, несмотря на то, что встроенный редактор текстов на сайте не поддерживает исходную разметку кода и часто подсвечивает его элементы некорректно. При этом код не вставлен картинкой, как делают некоторые совсем отчаявшиеся писатели.

В моем случае сохранить разметку было особенно важно, поскольку статья представляла собой описание работы над кодом. Чтобы решить проблему, я создал инструмент, позволяющий перенести подсветку кода в выбранной схеме из IDEA в статью на «Хабре». Я расскажу о процессе создания инструмента и об особенностях его использования.

Содержание

Зачем все это

На первый взгляд может показаться, что это делается из озорства, просто потому, что не устраивает стандартная подсветка, реализуемая через тэг <source>.

В каком-то смысле это, конечно, так, но не совсем.

Во-первых, подсветка внутри <source> не может работать с кусками кода, поскольку информации для раскраски будет недостаточно. Все элементы, объявленные за рамками куска, будут краситься наугад. Эта проблема не имеет решения, так как, насколько я знаю, ни один из сервисов для онлайн-раскраски не позволяет сделать что-то из перечисленного ниже:

  1. Вставить в статью полный код проекта, не отображая его целиком, или вставить ссылку на коммит на GitHub. В конкретных местах статьи использовать вырезки строк из полного кода (c указанием диапазона). При этом подсветка должна определяться на основе полного кода, конечно же.
  2. Указать явно метаинформацию для неопределенных элементов. Довольно сложный путь для пользователя, но я бы согласился и на такое.

Во-вторых, подсветка внутри <source> никогда не сравняется по количеству разных типов элементов с обычной IDE. А из-за проблемы, описанной выше, делать продвинутую раскраску нет никакого смысла: никто же не вставляет в статью полный код проекта, поэтому этот функционал не сможет работать.

В то же время реалии таковы, что в статьи нужно вставлять именно куски кода, причем чем они меньше, тем лучше.

Читать код без подсветки можно, но зачем.

Особенности «Хабр»-разметки

В IntelliJ IDEA есть встроенная поддержка экспорта кода в HTML. Обычное копирование кода кладет в буфер обмена в том числе раскрашенный код, который можно прочитать как HTML.

К сожалению, «Хабр» не разрешает использовать HTML-разметку в статьях напрямую. Причины этого — тайна, покрытая мраком, но, возможно, это связано с унификацией вида статей. Если позволить использовать HTML в статьях, можно будет наверстать такого, что возникнут проблемы с просмотром.

Я в целом поддерживаю идею запрета HTML в статьях, но есть нюанс. Ресурс для айтишников, где часто обсуждается код и при этом нет никакого способа корректно вставить его в статью, — это как-то странно.

Итак, у нас в распоряжении имеются тэги <b>, <i>, <font>. Кроме того, все это работает внутри тэга <code>, который нужен для форматирования. Ну и &nbsp; нам тоже сохранили, что полезно для длинных строк кода и отступов.

Стоит ли говорить, что все стандартные способы получить HTML-код из IDEA дают совсем не такой HTML, так что работа по преобразованию предстоит немаленькая.

Подход

Для начала стоит сказать слова благодарности автору capslocky за его материал по данной теме. Я не стал использовать предложенный в статье инструмент напрямую, да и вряд ли получилось бы, но благодаря этому материалу я понял всю глубину проблемы и в то же время ощутил ветер надежды.

Единственный минус этой публикации — большое количества кода в сочетании с очень скупым разъяснением того, что он делает и зачем.

Попробую исправить ситуацию и описать, что вам придется сделать со своей HTML-разметкой кода, если вы захотите привести его к виду, готовому к вставке на «Хабр».

  1. Перед экспортом нужно установить в IDEA нужную цветовую схему, например с сайта Color Themes. Экспортироваться код будет с выбранной схемой. Лучше выбирать схему с белым фоном (поскольку фон нельзя задать на «Хабре») и без подчеркиваний. Я не придумал, как их легко перетащить, так как не очень-то и хотелось.
  2. Работаем только с внутренностями тэга <pre>. Даже если вы используете экспорт не из IDEA, а какой-то другой, наверняка в HTML разметке будет этот тэг, так как без него сложно правильно отформатировать код. Сам тэг убираем, заменяя его на <code>.
  3. Текст, скорее всего, будет представлен в виде списка <span> с разными стилями. От них всех придется избавиться. Многие сервисы раскраски сводят стили в таблицу стилей, что логично, и используют ссылки на имена стилей. Конкретно IDEA пока такого не делает, что несколько облегчает задачу (настройки стиля лежат прямо в <span>).
  4. Задаем цвет шрифта через тэг <font>. Цвет фона задать, к сожалению, не получится.
  5. Свойство font-style:italic превращаем в пару тэгов <i></i>, а font-weight:bold — в <b></b>.
  6. Заменяем все пробелы на &nbsp;.
  7. Переводы строк в виде <br> заменяем на \n.
  8. При HTML-разметке в IDEA получаются пустые строки со стилями и строки из пробелов со стилями. Такие стили лучше выкинуть: это сильно сократит длину и увеличит понятность кода.
  9. Следим за тем, чтобы у переводов строк не было никакого стиля. Иначе возникнут проблемы с пустыми строками.

Последний пункт проиллюстрирую примером:

<code>

1<font color="000000">

</font>2

</code>

Данный код будет превращен «Хабром» в 12. Это же касается тэгов <b> и <i>, а также любых их сочетаний. У переводов строк не должно быть стиля, и тогда все будет хорошо.

Реализация

Поначалу задача написания конвертера для произвольного HTML-кода казалась мне довольно сложной. Однако если делать решение под конкретный вариант HTML, то все оказывается не так плохо. Мне удалось сделать все на чистом RegExp, то есть даже без парсинга HTML. Основной проблемой оказалось выявление особенностей «Хабр»-разметки.

Чтобы у переводов строк не было стилей, пришлось сделать довольно хитрые замены, которые, наверное, являются самыми непонятными (см. функцию popupBr). Идея в том, чтобы тэги <br> после каждой замены «всплывали» из глубины тэгов форматирования наружу. Таким образом, после всех замен тэг <br> оказывается вне форматирования.

Кроме того, оказалось, что IDEA кладет в буфер обмена не только Rich Text, но и довольно хитрые объекты типа application/x-java-jvm-local-objectref. Беда в том, что наличие таких объектов в буфере обмена приводит к постоянным ошибкам в моей консоли на тему constructing DataFlavor. К сожалению, тут ничего поделать нельзя: просто JDK так работает с буфером обмена. Для меня было открытием наличие такого вот кода. Видимо, умные дяди, которые написали это, считают, что и так сойдет. В общем, не пугайтесь ошибок, которые могут возникнуть при работе с инструментом.

Проект написан на Kotlin и живет здесь.

Предложения по усовершенствованию всячески приветствуются! Например, было бы хорошо оформить данный инструмент как плагин для IDEA. Простого способа сделать это я пока не нашел: исходники плагина Copy as HTML, к сожалению, закрыты, а разбираться, как написать такой плагин с нуля, слишком долго.