markdown で sphinx する

Windows で sphinx する手順を説明します。
EDITOR は VSCode を使います。

# Python インストール

https://www.python.org/

最新の 64bit 版をインストールします。
Windows版では `py.exe` が PATH の通ったところにインストールされます。
以降、これを使います。

```
PS> py -V
Python 3.10.0
```

# VSCode インストール

https://code.visualstudio.com/

# Project を作る

新規のフォルダーを作ります。
とりあえず `hello` とします。

## vscode でフォルダを開く

`Initialize Repository (git init)` しとく。

## venv を作る

このあと sphinx に関連する python パッケージをインストールしたり、パッケージに付属するツールを使うので `PATH` の管理を兼ねて、 `venv` を導入します。

vscode で `Terminal` - `New Terminal`。
`PS hello>` が Windows の PowerShell です。

```
PS hello> echo .venv >> .gitignore
PS hello> py -m venv .venv
```

VENV に入る。

:::note info
Windows 版の venv では、`.venv/Scripts` に実行ファイル(`shpinx-build` や `sphinx-autobuild`　など)が入ります。
(.venv) 状態では、ここに `PATH` が通っています。
:::

```
PS hello> .venv/Scripts/Activate.ps1
(.venv) PS hello>
```

以下の通知が来たら `YES`
![venv_notice.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/81ba2454-c23c-dc54-d67e-371d13a54375.jpeg)


## sphinx をインストールする

`.venv\Scripts/pip.exe` を使います。

```
(.venv) PS hello> pip install sphinx
```

:::note info
.venv/Scripts/pip.exe により、
.venv/Lib/site-package/sphinx がインストールされます。
:::

## sphinx project を初期化します。

あとで、`conf.py` を編集すればいいので適当な内容で作成します。

```
(.venv) PS hello> sphinx-quickstart.exe -q -p hello -a me
```

:::note info
* -q: quiet
* -p: project名
* -a: author名
:::

以下のファイルとフォルダが作成されます。

|パス|内容|備考|
|-|-|-|
|_build/| 空|
| _static/| 空|cssやJavascript置き場 |
| _templates/| 空|テンプレートのカスタマイズ|
| make.bat|Windows向け便利コマンド|本記事では使いません|
| Makefile|Unix系向け便利コマンド|本記事では使いません|
| conf.py| sphinx の設定ファイル|重要|
| index.rst| sphinx rootドキュメント|重要。conf.pyと同じフォルダの `index` という規約|

## とりあえずビルドしてみる

```
(.venv) PS hello> sphinx-build.exe . _build
```

:::note info
* 第1引数: conf.py のあるフォルダ
* 第2引数: ビルド結果の出力先
:::

`_build` に html が出力されます。

`_build` を gitignore に追加します。

```
(.venv) PS hello> echo _build >> .gitignore
```

## sphinx-autobuild を取り入れよう

```
(.venv) PS hello> pip install sphinx-autobuild
(.venv) PS hello> sphinx-autobuild.exe . _build --open-browser --port=0
```

:::note info
* --open-browser ブラウザを開く
* --port=0 ランダムで空きポートを使う
:::

`_build` フォルダをホストする http-server が起動します。
また、更新を監視して自動ビルドしてくれます。
試しに、 `index.rst` を適当に書き換えてみてください。自動でビルドされて内容が反映されます。
`Ctrl-C` で `sphinx-autobuild` 停止させます。

## フォルダ構成を変える

今の構成だと `sphinx-build` が `.venv` にもファイルを探しにいってしまうなど問題があるので sphinx のソースフォルダを作って隔離します。
conf.py と index.rst を `docs` を作って移動してください。
以下は消して OK です( `docs` に移動してもよい)。

* _static/ (空)
* _templates/ (空)
* make.bat (消してOK)
* Makefile (消してOK)

この辺で一回 `git commit` しておくとよいかもしれない。
必要なファイルは以下の３つです。

* docs/conf.py
* docs/index.rst
* .gitignore

適宜保存してください。
この記事では、 `Makefile` `make.bat` は使わずに
直接 `sphinx-build` コマンドなどを使います。

## sphinx-autobuild の task を作る

`c-s-p` `> Tasks: Configure Task` `Create tasks.json file from template` `Others`

```json:.vscode/tasks.json
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "sphinx-autobuild",
            "type": "shell",
            "command": ".venv/Scripts/sphinx-autobuild.exe docs _build --open-browser --port=0",
            "options": {
                "env": {
                    "PATH": "${workspaceFolder}\\.venv\\Scripts;${env:PATH}"
                }
            },
            "problemMatcher": [],
            "isBackground": true
        }
    ]
}
```

`c-s-p` `> Tasks: Run Task` `sphinx-autobuild` で実行できます。
記事を書くときは裏で起動させておくと便利です。

## myst_parser を導入する

`rst` に追加して `md` でも記事を書けるようにします。

```
(.venv) PS hello> pip install myst_parser
```

```conf.py
extensions = [
    "myst_parser"
]
```

## index.rst を index.md に書き換える

```rst:index.rst
.. hello documentation master file, created by
   sphinx-quickstart on Sun Jan  2 22:20:10 2022.
   You can adapt this file completely to your liking, but it should at least
   contain the root `toctree` directive.

Welcome to hello's documentation!
==============================

.. toctree::
   :maxdepth: 2
   :caption: Contents:

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
```

以下のように書き換えます。

````md:index.md
% hello documentation master file, created by
% sphinx-quickstart on Sun Jan  2 22:20:10 2022.
% You can adapt this file completely to your liking, but it should at least
% contain the root `toctree` directive.

# Welcome to hello's documentation!

```{toctree}
:maxdepth: 2
:caption: Contents
```

## Indices and tables

* {ref}`genindex`
* {ref}`modindex`
* {ref}`search`
````

myst は markdown に追加して sphinx の directive や role を追加した、ようなものです。

<https://myst-parser.readthedocs.io/en/latest/syntax/syntax.html>

### コメント
行頭の `%`

### directive

rst のブロック要素。
`..` から始まる `indent` ブロックがディレクティブです。

```
.. toctree::
   :maxdepth: 2
   :caption: Contents:
```


````
```{toctree} <= ディレクティブ名波括弧でくくる
:maxdepth: 2 <= キーバリューをインデント無しで
:caption: Contents <= value 側に コロンがあるとエラーになるので注意！
```
````

```
index.md:8: WARNING: Directive 'toctree': Invalid options YAML: mapping values are not allowed here
  in "<unicode string>", line 2, column 18:
    caption: Contents:
                     ^
```

### role

rst のインライン要素。

```
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
```

`:` を波括弧で置き換えます。

```
* {ref}`genindex`
* {ref}`modindex`
* {ref}`search`
```

以上で、sphinx の新規プロジェクトを markdown で始める手順終わりです。

# gettext で 多言語対応させる

<https://www.sphinx-doc.org/ja/master/usage/advanced/intl.html>

sphinx では言語変換機能があります。
ベースのサイトを日本語の記事で作成し、変換カタログによって変換された英語サイトを作成できます。
日本語を `_build/ja` に出力し、英語を `_build/en` に出力して HTML の端っこに言語切り替えリンクを追加
する方法があります。

`hello` に対して英語を追加する手順を説明します。
日本語を `_build` に出力し、英語を `_build/en` に出力する変則的な感じでまいります。

## sphinx-intlのインストール

```
(.venv) PS hello> pip install sphinx-intl
```

## conf.pyの修正

```py
language = "ja" # -l 未指定のときのデフォルト
locale_dirs = ['locale/']   # path is example but recommended.
gettext_compact = False     # optional.
```

## pot 作成(元記事が変わる度)

```
(.venv) PS hello> sphinx-build -M gettext docs docs/_pot 
```

_pot ファイルはたぶんコミットしなくてよいので .gitignore に追加します。


```
(.venv) PS hello> echo _pot >> .gitignore
```

mo (poのバイナリファイル) も追加します。

```
(.venv) PS hello> echo "*.mo" >> .gitignore
```

### pot 作成のタスク例

```json:tasks.json
        {
            "label": "sphinx-pot",
            "type": "shell",
            "command": ".venv/Scripts/sphinx-build.exe -M gettext docs docs/_pot",
            "options": {
                "env": {
                    "PATH": "${workspaceFolder}\\.venv\\Scripts;${env:PATH}"
                }
            },
            "problemMatcher": [],
        },
```

## po 作成・更新(元記事が変わる度)

```
(.venv) PS hello> cd docs
(.venv) PS hello/docs> sphinx-intl update -p _pot/gettext -l en
Create: locale/en\LC_MESSAGES\index.po
```

各 rst や md に対して１対１で
`locale/en/LC_MESSAGES/*.po` が作成されます。
これをメッセージカタログとして翻訳します。

### po作成 task例

```json:tasks.json
        {
            "label": "sphinx-po",
            "type": "shell",
            "command": "${workspaceFolder}/.venv/Scripts/sphinx-intl.exe update -p _pot/gettext -l en",
            "options": {
                "cwd": "${workspaceFolder}/docs",
                "env": {
                    "PATH": "${workspaceFolder}\\.venv\\Scripts;${env:PATH}"
                }
            },
            "problemMatcher": [],
        }
```

## po 編集

適当に元記事を日本語化してカタログ作成。

記事を日本語化してから、`pot作成` と `po更新` をやりなおしました。

```
#: ../../index.md:8
msgid "Contents"
msgstr "中身"

#: ../../index.md:6
msgid "ようこそハロー"
msgstr "welcome hello"

#: ../../index.md:13
msgid "索引と検索"
msgstr "index and search"
```

## build

```
(.venv) PS hello> sphinx-build docs _build/en -D language=en -A language=en
```

```json:tasks.json
        {
            "label": "sphinx-build(en)",
            "type": "shell",
            "command": ".venv/Scripts/sphinx-build.exe docs _build/en -D language=en -A language=en",
            "options": {
                "env": {
                    "PATH": "${workspaceFolder}\\.venv\\Scripts;${env:PATH}"
                }
            },
            "problemMatcher": [],
        },
```

最初の `sphinx-autobuild` の URL に `/en` を追加すると英語化されたページが見えます。

## テンプレートに言語切り替えを追加する

[jinja](https://jinja.palletsprojects.com/en/2.10.x/templates/) テンプレート的な意味で、

* 日本語ビルドから見て英語サイトは、 `pathto(root_doc) + "en/" + pagename`
* 英語ビルドから見て日本語サイトは、 `(pathto(root_doc) + "../" + pagename)`

です。

サイドバーに `language.html` を追加します。

```html:docs/_templates/language.html
{% if language == "ja" %}
{% set href = (pathto(root_doc) + "en/" + pagename) if root_doc!=pagename else "en/" %}
<ul>
    <li>日本語
    <li><a href="{{ href }}">英語</a>
</ul>
{% elif language == "en" %}
{% set href = (pathto(root_doc) + "../" + pagename) if root_doc!=pagename else "../" %}
<ul>
    <li><a href="{{ href }}">Japanese</a>
    <li>English
</ul>
{% else %}
unknown {{language}}
{% endif %}
```

:::note warn
alabaster は html_sidebars が効かないので適当なテーマに変更します
:::

```py:conf.py
html_theme = 'bizstyle' # default alabaster は html_sidebars が効かないので適当なテーマに変更
html_sidebars = {
    '**': ['language.html', # 上記のカスタムサイドバー
       'globaltoc.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'],
}
```

![language.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/8efa4bb2-c42a-ce39-806f-07c3551cfb55.gif)


# github actions

gh-pages で master にソース、 gh-pages branch に sphinx ビルド結果をデプロイする例。

venv で pip した パッケージのリストをコミットする。

```
(.venv) PS hello> pip freeze > requirements.txt
```

github アクションの `yml` を作る。

```yml:.github/workflows/sphinx.yml
name: Sphinx

on:
  push:
    branches:
      - master
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    steps:
      - uses: actions/checkout@v1
      - run: sudo apt install gettext
      - run: pip install -r requirements.txt # venv の pip をすべてインストール
      - name: sphinx build(ja)
        run: sphinx-build docs _build
      - name: sphinx build(en) # 英語版がある場合のみ
        run: sphinx-build docs _build/en -D language=en -A language=en
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./_build # sphinx の出力を gh-pages branch に送り込む
```

試しに <https://github.com/ousttrue/hello_sphinx/actions> に作成しました。

## gh-pages の設定

github の repository の `settings` - `pages`

![gh-pages.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/cbc9efcc-f4c2-cce5-06d1-8470c8b0e15f.jpeg)

`save` すると `deploy` action が発動します。

https://ousttrue.github.io/hello_sphinx/

できあがり。

# sphinx そのものの説明

## toctree

`conf.py` と同じフォルダの `index` をルートとする木です。
フォルダ構造で勝手に決まるのではなく、人間が手作業で木の枝を追加します。

`toctree` ディレクティブで子記事を指定します。
拡張子抜きの相対パスで rst や md を列挙します。

````markdown
```{toctree}
:maxdepth: 1
child_article
child_dir/index
```
````

# vscode の problemMatcher に設定する例

```json:.vscode/tasks.json
        {
            "label": "sphinx-build",
            "type": "shell",
            "command": ".venv/Scripts/sphinx-build content/ja/docs build -b=dirhtml -E",
            "presentation": {
                "reveal": "always",
                "echo": true,
                "showReuseMessage": false,
                "clear": true,
            },
            "problemMatcher": [
                {
                    "owner": "sphinx",
                    "fileLocation": "absolute",
                    "pattern": [
                        {
                            "regexp": "^(\\w:[^:]+):(\\d+):\\s*(\\w+):\\s*(.+)$",
                            "file": 1,
                            "line": 2,
                            "severity": 3,
                            "message": 4,
                        }
                    ]
                }
            ]
        }
```