日々drdrする人のメモ

今日も明日もdrdr

競プロライブラリのページをAsciiDocで作った話

この記事は、"IQ1 Advent Calendar 2018"の13日目の記事です。
adventar.org

今回は、自分用の競プロライブラリのWebページを最近AsciiDocで作ったので、この話を書きます。競プロよりもAsciiDocメインの記事です。
tjkendev.github.io

(スマブラSPやってIQ低めてたら記事投稿が当日終わりになったけど許して)

はじめに

以前から、GitHubで競プロのライブラリとして実装をまとめていました。

github.com

しかし、まとめた実装を参照する際、以下のような問題がありました。

  • コードが多くなると使いにくい
    • 素早く必要なコードを探すのが難しくなる
  • コード上に残したメモが読みづらい
    • 特に複雑な数式が読めない
  • 端末上でコピるのもめんどい
    • 何も考えずボタン1つでコピーがしたい!!!!!
  • 等々...

かといって、wikiやWebページをきちんと作って見やすくまとめるのは、作るのが面倒でしませんでした。
それでも最近、見やすくまとめたい欲が上がってきたので、さっくりと簡単にWebページを作ることにしました。

ここからはAsciiDocとその周りについて書いていきます。


環境準備

今回は、書きやすくてHTMLへの変換もしやすそうという気持ちで、軽量マークアップ言語であるAsciiDocでページを作ることにしました。
あと、数式を含めた軽い解説や計算量等の説明を書きたかったので、AsciiDocを拡張したAsciidoctor LaTeXを使いました。
github.com

Asciidoctor-LaTeXはgemからインストールできます。

# Asciidoctor LaTeXのインストール
$ gem install asciidoctor-latex --pre

HTMLファイルへの変換はこんな感じです。

# test.adoc から test.html を生成
$ asciidoctor-latex -b html test.adoc
# *.adoc から *.html を生成
$ asciidoctor-latex -b html -R ./src -D ./dst

AsciiDoc(Asciidoctor LaTeX)では上のように -R と -D オプションを使うと、src内のadocファイルを、ディレクトリ構造を保ちながらdstディレクトリにHTMLファイルを出力してくれます。
今回のように構造的なページを生成したい時に、何も考えずに変換できる便利な機能です。


ページを書く

AsciiDocはコードの状態でも読めるように書けます。
また、AsciiDocはソースコードをincludeすることができるため、ソースコードの部分と解説の部分を分離することができます。(ソースコードだけをまとめて、従来のライブラリとして扱えます)
そして、Asciidoctor LaTeXではLaTeXで書くように$$で数式を囲むと、HTML上でMathJaxを用いて表示してくれます。

  • 例プログラム "ohuton.adoc"
# Asa!

## おきた!

今日も一日ねむみが $\sum_{k=1} \frac{1}{k}$

[source, python]
----
include::./ohuton-nukunuku.py[]
----
# 例プログラム "ohuton.adoc"のHTML変換
$ asciidoctor-latex -b html -a source-highlighter=highlightjs ohuton.adoc

f:id:smijake3:20181201153425p:plain
例プログラムのHTML出力

いい感じです。

ページの見た目

でも、そのまま変換するとページの見た目がアレなので、Asciidoctor stylesheet factoryを使って見た目をよくしてます。

github.com

f:id:smijake3:20181201153322p:plain
例プログラムにgithub.cssを適用したHTML出力

見た目がよくなりました。

拡張機能

ページを書いてると追加したいものが無限に出てきて、その中にはAsciiDocとして難しいことが出てきました。

  • 特定ページのtitleをうまく変更したい...
    • 標準ではtitleの値をある程度しか制御できない
  • コードのコピーボタンを静的に追加したい...
    • 静的に追加するには全てのコード付近にボタンを配置するのは手間
    • JSでボタンを動的に配置するのは、後からボタンが表示されるのが気になる
  • 特定のディレクトリからの相対パスが欲しい...
  • 云々...

最初は、AsciiDoc標準のマクロ等で対応しようとしましたが、試している内に厳しいと感じました。


色々考えた結果、AsciiDocでは拡張機能を追加することができ、かつ拡張機能を自力で実装するのは簡単そうだったので、拡張機能を自分で実装して対応することにしました。

AsciiDocの拡張機能Rubyで書くことができます。
そして、以下の拡張機能のサンプルが存在するので、これをベースに自分で使う機能を実装しました。
github.com

拡張機能は8種類(前処理、後処理、マクロ定義、等)の拡張として実装ができます。
(例えば、相対パスの計算やtitle修正は前処理、コピーするためのボタンの配置は後処理の拡張機能として実装してます)

  • 拡張機能(前処理)のシンプルな実装例 (extension.rb)
require 'asciidoctor/extensions'

include Asciidoctor

# 拡張機能を定義するクラス
class Oyasumi < Extensions::Preprocessor
  def process document, reader
    attrs = document.attributes

    attrs['nukunuku'] = attrs['ohuton-name'] + "ぬくぬく"
    attrs['mouneyou'] = "もう寝よう"

    nil
  end
end

# 拡張機能の登録
Extensions.register do
  preprocessor Oyasumi
end

実装した拡張機能は -r オプションでasciidoctor(-latex)に与えることで適用できます。

// result.adoc
nukunuku = {nukunuku}

mouneyou = {mouneyou}
$ asciidoctor -b html -r ./extension.rb ./result.adoc -a ohuton-name="おふとん"

f:id:smijake3:20181213000333p:plain
result.adocのHTML出力

拡張機能を自前で作れると、AsciiDocでいろんなことができるようになるので便利です。
AsciiDocなら任意のことができるように見えて最強に思えてきます。(IQ1的思考)


ページのデプロイ

ページはGitHub Pagesで公開しています。

ページを公開するために、はじめはmasterブランチのdocsにHTMLを生成するようにしてました。
しかし、コミットするたびにHTMLのdiffが無限に含まれたり、コミット時にデプロイ用のHTMLを生成し忘れることがあったため、あまりよいやり方ではないと思いました。

そこで、手動変換が必要なく、HTMLをmasterブランチと分離できる方法にしました。
具体的には、Travis CIを用いる方法で、

  1. githubにpushする
  2. Travis CIで自動変換し、gh-pagesブランチにpushさせる

という流れでページを生成することにしました。

構築はこちらを参考にしました: Travis CIからgh-pagesにデプロイ · GitBook

現在は、masterへpush後にTravis CIでHTMLを生成し、gh-pagesブランチにpushしてページをデプロイしています。
Travis CIではgemのbundlerが使えるので、Asciidoctor LaTeXの環境構築は難しくなかったです。

Travis CIでは、HTML変換とデプロイの結果が出力されています。
https://travis-ci.org/tjkendev/procon-library


まとめ

AsciiDocでライブラリページを作りました。
簡単によい感じのライブラリページが作れるようになったと思います。