0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

🗑【Sphinx】indexディレクティブからjindexディレクティブを作る

Last updated at Posted at 2021-09-06

後日談(最新)

jindex ディレクティブの機能を内容したSphinx拡張を作りました。

本記事は「開発日誌」的な意味合い以外には、特に読む必要はありません。

後日談

使ってみると「分類」に応じて表示場所は変わるけど、同じ分類の中では期待する順番では表示されない。色々調べてみて、挙動にクセはありますがGlossaryにある機能で十分であることがわかりました。

詳細は別途記事を起こして、こちらからリンクを貼る予定です。本記事は「これくらいの労力で拡張ディレクティブを作ることができるよ」のサンプル事例として残していおきます。
※T.B.D.はなくす予定です。

1.はじめに

  1. glossary ディレクティブは 用語<spc>:<spc>分類 とすることで、索引ページで 分類 に応じた分類をする。ひらがな1文字なら「読み」で分類。
  2. 同じことを index でもやりたい。

という動機で始めたjindexディレクティブを作成するまでの過程をQiitaで共有。

使い方

  • .. index:: の代わりに .. jindex:: を使用
  • 名称の末尾に「 <spc>:<spc>分類名 」を追記する。
    • この「分類名」が「ひらがな一文字」なら、それで分類される。

補足

「2.管渠」「3.作業概要」を見て必要な情報を拾ってください。

2.環境

  • Windows10/cygwin64
  • Python 3.8.10
  • pip 21.2.4(2021-09-07現在の最新)
  • spihnx 4.1.2(2021-09-07現在の最新)

3.作業概要

  • index ディレクティブ
  • データ構造
  • domains/index.py の改良
  • jindex.py の作成(T.B.D.)
  • conf.py の編集(T.B.D.)
  • サンプルと動作確認(T.B.D.)

4.作業詳細

index ディレクティブ

  1. sphinxcontrib.yt を眺めた経験から、「class で定義されている」「名前に index がある」「directive という名前の何かを継承している」と当たりが付ける。
  2. Sphinxがインストールされているディレクトリでファイルを下がる。
  3. domains/index.py を見つける。
  4. これを調べて indexnodes['entries'] のデータ構造が知りたくなる。

実行したコマンド。

egrep -i `class.*index.*directive' */*.py
  • */ は適当に増減する。
  • ファイルが多かったり、ディレクトリが深い場合は find を使う。

データ構造

  1. それっぽい文字列を予想して、egrep で探す。
  2. addnodes.py を見つける。

addnodes.py にあった説明

Node for index entries.

This node is created by the index directive and has one attribute,
entries. Its value is a list of 5-tuples of (entrytype, entryname, target, ignored, key).

entrytype is one of "single", "pair", "double", "triple".

key is categorization characters (usually a single character) for
general index page. For the details of this, please see also:
:rst:dir:glossary and issue #2320.

  • ignored と説明されている場所は。 entrytypesingle の時は、entryname の先頭に ! があると main を設定している。何か意味があるのかもしれない。

domains/index.py の改良

  1. cp -p index.py index.py.orig でオリジナルを保存
  2. print文でentryデータの内容を確認しつつ、改良を使える。

diff -u で出力した変更内容は次の通り

--- index.py.orig       2021-09-05 01:23:03.208772600 +0900
+++ index.py    2021-09-07 01:03:51.222260000 +0900
@@ -8,6 +8,7 @@
     :license: BSD, see LICENSE for details.
 """

+import re
 from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple

 from docutils import nodes
@@ -87,8 +88,15 @@
         indexnode['entries'] = []
         indexnode['inline'] = False
         self.set_source_info(indexnode)
+        entries = []
         for entry in arguments:
-            indexnode['entries'].extend(process_index_entry(entry, targetnode['ids'][0]))
+            entries.extend(process_index_entry(entry, targetnode['ids'][0]))
+        #print(entries)
+        for entry in entries:
+            parts = re.split(' +: +', entry[1]) + [None]
+            # entry: (indextype, text, target, 'main' or '', index_key)
+            indexnode['entries'].extend([(entry[0], parts[0], entry[2], entry[3], parts[1])])
+        #print(indexnode['entries'])
         return [indexnode, targetnode]
  • process_index_entry() で作られたデータを一端作業用の変数にいれて、entryname (コード内では entry[1] )の内容を見てデータを作り直す。

jindex.py の作成(T.B.D.)

※ディレクトリは読み替えてください。

関連ファイルの整理

動くコードがあるので、これをSphinx拡張に仕立て上げる。

その1

cd /usr/local/lib/python3.8/site-packages/sphinx
mv domains/index.py domains/index.py.new
cp -p domains/index.py.orig domains/index.py
cp domains/index.py.new ~/sphinx/_ext/jindex.py

その2

cd
cd ~/sphinx/_ext/
svn add jindex.py
svn ci -m "copied from site-packages/sphinx/domains/index.py"
vi jindex.py

jindex.py 編集内容

  • 不要な import を削除

  • Class IndexClass JIndex に変更

class JIndexDirective(SphinxDirective):
  • 末尾に : 文字列 があれば取り出して分類名として使う
        for entry in entries:
            parts = re.split(' +: +', entry[1]) + [None]
            # entry: (indextype, text, target, 'main' or '', index_key)
            indexnode['entries'].extend([(entry[0], parts[0], entry[2], entry[3], parts[1])])
  • setup() は この JIndex を使うように変更
def setup(app: "Sphinx") -> Dict[str, Any]:
    app.add_directive('jindex', JIndexDirective)

    return {
        'version': '4.1.2k1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }

編集した jindex.py の内容

import re
from typing import TYPE_CHECKING, Any, Dict, List, Tuple

from docutils import nodes
from docutils.nodes import Node
from docutils.parsers.rst import directives

from sphinx import addnodes
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import process_index_entry
from sphinx.util.typing import OptionSpec

if TYPE_CHECKING:
    from sphinx.application import Sphinx


class JIndexDirective(SphinxDirective):
    has_content = False
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    option_spec: OptionSpec = {
        'name': directives.unchanged,
    }

    def run(self) -> List[Node]:
        arguments = self.arguments[0].split('\n')

        if 'name' in self.options:
            targetname = self.options['name']
            targetnode = nodes.target('', '', names=[targetname])
        else:
            targetid = 'index-%s' % self.env.new_serialno('index')
            targetnode = nodes.target('', '', ids=[targetid])

        self.state.document.note_explicit_target(targetnode)
        indexnode = addnodes.index()
        indexnode['entries'] = []
        indexnode['inline'] = False
        self.set_source_info(indexnode)
        entries = []
        for entry in arguments:
            entries.extend(process_index_entry(entry, targetnode['ids'][0]))
        #print(entries)
        for entry in entries:
            parts = re.split(' +: +', entry[1]) + [None]
            # entry: (indextype, text, target, 'main' or '', index_key)
            indexnode['entries'].extend([(entry[0], parts[0], entry[2], entry[3], parts[1])])
        #print(indexnode['entries'])
        return [indexnode, targetnode]


def setup(app: "Sphinx") -> Dict[str, Any]:
    app.add_directive('jindex', JIndexDirective)

    return {
        'version': '4.1.2k1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }

conf.py の編集(T.B.D.)

サンプルと動作確認(T.B.D.)

5.関連情報

以上

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?