Разработка 08 мар 2019 ~3 мин.

Jekyll Spoiler Plugin. Куда и как прятать код

Когда писал предыдущую статью, столкнулся с одной простой проблемой: большие куски кода занимали больше места, чем сам текст.

Поиск готового решения для спойлеров привел меня на вот эту страничку. Решение простое и гениальное, но имело один большой недостаток: текст внутри спойлера не форматировался.

Свое решение я опишу в этой статье.

TL;DR. Для тех, кому лень читать целиком.

Это нужно положить в папку _plugins:

module Jekyll
  class SpoilerBlock < Liquid::Block
    def initialize (tag_name, markup, tokens)
      super
      @summary = markup.strip
    end

    def render(context)
      site = context.registers[:site]
      converter = site.find_converter_instance(Jekyll::Converters::Markdown)

      output = '<details class="spoiler">'
      output << "<summary class=\"spoiler-title\">#{@summary.empty? ? 'Открыть' : @summary}</summary>"
      output << '<div class="spoiler-body">'
      output << converter.convert(super)
      output << '</div>'
      output << '</details>'
    end
  end
end

Liquid::Template.register_tag('spoiler', Jekyll::SpoilerBlock)

Проблема

Если любой код поместить внутрь HTML тега, то он будет отрисован как есть. То есть вот этот кусок кода:

<details class="spoiler">
<summary class="spoiler-title">Заголовок спойлера</summary>
<div class="spoiler-body">
**Тут должен быть жирный текст**

```
А это *должен* быть кусок кода
```
</div>
</details>

Выдаст вот такой результат:

Заголовок спойлера
**Тут должен быть жирный текст** ``` А это *должен* быть кусок кода ```

Мы можем заставить Jekyll парсить код внутри HTML элементов:

# _config.yml
kramdown:
  parse_block_html: true

Но результат сильно лучше не станет:

Поехавшая разметка

Решение

Решение напрашивается само собой: нужно содержание спойлера индивидуально прогонять через конвертёр.

Когда-то я читал спецификацию по Ruby, но для меня он до сей поры остается языком инопланетян. (Говорят, что Ruby очень популярен в Японии. Кажется, это все объясняет). Я даже кажется нашел используемый в Liquid фильтр для конвертации. Но так и не смог его победить.

В итоге после короткого гугления нашел вот этот рецепт:

site = context.registers[:site]
converter = site.find_converter_instance(Jekyll::Converters::Markdown)
converter.convert(text)

Остается все это дело собрать воедино вот так:

Все то же, что и в TL;DR выше
module Jekyll
  class SpoilerBlock < Liquid::Block
    def initialize (tag_name, markup, tokens)
      super
      @summary = markup.strip
    end

    def render(context)
      site = context.registers[:site]
      converter = site.find_converter_instance(Jekyll::Converters::Markdown)

      output = '<details class="spoiler">'
      output << "<summary class=\"spoiler-title\">#{@summary.empty? ? 'Открыть' : @summary}</summary>"
      output << '<div class="spoiler-body">'
      output << converter.convert(super)
      output << '</div>'
      output << '</details>'
    end
  end
end

Liquid::Template.register_tag('spoiler', Jekyll::SpoilerBlock)

Положить в папку _plugins. И далее можно использовать в любом Markdown документе вот так:

{% spoiler Заголовок спойлера %}
```yaml
content:
    - "some string"
```
{% endspoiler %}

Вот с таким вот результатом:

Заголовок спойлера
content:
    - "some string"

Используйте на здоровье :grin: