docutils用にsphinxのliteralincludeのsubsetを作って使ってみる。

literalinclude?

sphinxで利用可能なdirectiveの1つ。 以下の様な形で別のファイルに定義した記述をあたかも自身のcode-blockとして記述したかのように使えるもの。

ここはReSTの文章。

.. literalinclude:: <filename.<ext>>


ここもReSTの文章。

似たようなものとしてincludeが存在する。

  • include 外部ファイルをReSTとして取り込む
  • literalinclude 外部ファイルを(特定の言語の)code-blockとして取り込む

直接流用出来ないか試す

直接sphinxのdirectiveを流用出来ないか試してみる。元にするのはrst2html5

my_rst2html5.py

from docutils.core import publish_cmdline, default_description
from docutils.parsers.rst import directives
from sphinx.directives.code import LiteralInclude

directives.register_directive("literalinclude", LiteralInclude)
description = (
    u'Generates HTML 5 documents from standalone '
    u'reStructuredText sources ' + default_description
)

publish_cmdline(writer_name='html5', description=description)

使ってみる。動かない。

$ cat <<-EOS > 00hello.rst
hello
========================================

.. literalinclude:: 00hello.py

hai
EOS
$ cat <<-EOS > 00hello.py
print("hello")
EOS
$ python my_rst2html.py --traceback 00hello.rst
...
    result = directive_instance.run()
  File "/home/podhmo/venvs/my3/lib/python3.5/site-packages/sphinx/directives/code.py", line 414, in run
    env = document.settings.env
AttributeError: 'Values' object has no attribute 'env'

普通に怒られる。document.settings.envというのはsphinxに含まれているEnvironmentオブジェクトのことなので普通には動かない。

諦めてsubsetを作る

以下の様な感じにするとsubsetを作れなくはない。

import os.path
from docutils.core import publish_cmdline, default_description
from docutils.parsers import rst
from docutils import nodes
from docutils.parsers.rst import directives


class LiteralInclude(rst.Directive):
    has_content = False
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    option_spec = {'dedent': int, }

    def run(self):
        document = self.state.document
        dedent = self.options.get("dedent", 0)

        try:
            filename = self.arguments[0]
            filepath = os.path.join(os.path.dirname(document.settings._source), filename)
            with open(filepath) as rf:
                text = rf.read()

            if dedent > 0:
                text = "".join([line[dedent:] for line in text.splitlines(True)])
            retnode = nodes.literal_block(text, text, source=filename)
            self.add_name(retnode)
            return [retnode]
        except Exception as exc:
            return [document.reporter.warning(str(exc), line=self.lineno)]


directives.register_directive("literalinclude", LiteralInclude)
description = (
    u'Generates HTML 5 documents from standalone '
    u'reStructuredText sources ' + default_description
)

publish_cmdline(writer_name='html5', description=description)

今度は動く。subsetなので完全な互換性はない。

$ python my_rst2html5.py --traceback hello.rst
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

...snip

<body>
<div class="document" id="hello">
<h1 class="title">hello</h1>

<pre class="literal-block">print(&quot;hello&quot;)
</pre>
<p>hai</p>
</div>
</body>
</html>

ところで

gitstはこちら。 ところで、rawgitというサービスを使うとgist上にuploadしたhtmlのレンダリング結果を見ることができるらしい。

こういう感じに(上の文章中のものとはちょっと違うコードではあるけれど)。

ただ、rst2html5で生成されるhtmlはやっぱりスマートフォンではあんまり良い感じの見た目にならない感じっぽい(少なくともviewportの指定などが不足している)。かなしい。

そもそもしたかったことは

そもそもなんでこんなことをしたかったかというと、sphinxのように複数のページから成るドキュメントを作りたいのではなく。単なる1枚のページをそれなりに綺麗な見た目で作りたいのだけれど。その1枚のページを作るための入力のファイル自体は分けたいと思う事が多かったので。sphinxだとちょっとover-killっぽい。

markdownでReSTのdirectiveに似たようなものを作る方法が確立して一般化されているなら(ユーザーが新たな文法を増やさずとも好き勝手に機能を追加できる記法が存在する)別にdocutilsというかReSTにこだわらなくても良いのだけれど。