aboutsummaryrefslogtreecommitdiffstats
path: root/manual/_ext/qpdf.py
blob: 7665afed6a4973280f94700aab1cb33d6623dc49 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from collections import defaultdict
from operator import itemgetter
import re

from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, Index
from sphinx.roles import XRefRole
from sphinx.util.nodes import make_refnode

# Reference:
#   https://www.sphinx-doc.org/en/master/development/tutorials/todo.html
#   https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html

# cSpell:ignore contnode
# cSpell:ignore docname
# cSpell:ignore docnames
# cSpell:ignore localname
# cSpell:ignore refnode
# cSpell:ignore signode


class OptionDirective(ObjectDescription):
    has_content = True

    def handle_signature(self, sig, signode):
        signode += addnodes.desc_name(text=sig)
        return sig

    def add_target_and_index(self, name_cls, sig, signode):
        m = re.match(r'^--([^\[= ]+)', sig)
        if not m:
            raise Exception('option must start with --')
        option_name = m.group(1)
        signode['ids'].append(f'option-{option_name}')
        qpdf = self.env.get_domain('qpdf')
        qpdf.add_option(sig, option_name)


class OptionIndex(Index):
    name = 'options'
    localname = 'qpdf Command-line Options'
    shortname = 'Options'

    def generate(self, docnames=None):
        content = defaultdict(list)
        options = self.domain.get_objects()
        options = sorted(options, key=itemgetter(0))

        # name, subtype, docname, anchor, extra, qualifier, description
        for name, display_name, typ, docname, anchor, _ in options:
            m = re.match(r'^(--([^\[= ]+))', display_name)
            if not m:
                raise Exception(
                    'OptionIndex.generate: display name not as expected')
            content[m.group(2)[0].lower()].append(
                (m.group(1), 0, docname, anchor, '', '', typ))

        content = sorted(content.items())
        return content, True


class QpdfDomain(Domain):
    name = 'qpdf'
    label = 'qpdf documentation domain'
    roles = {
        'ref': XRefRole()
    }
    directives = {
        'option': OptionDirective,
    }
    indices = {
        OptionIndex,
    }
    initial_data = {
        'options': [],  # object list
    }

    def get_full_qualified_name(self, node):
        return '{}.{}'.format('option', node.arguments[0])

    def get_objects(self):
        for obj in self.data['options']:
            yield(obj)

    def resolve_xref(self, env, from_doc_name, builder, typ, target, node,
                     contnode):
        match = [(docname, anchor)
                 for name, sig, typ, docname, anchor, priority
                 in self.get_objects() if name == f'option.{target[2:]}']

        if len(match) > 0:
            to_doc_name = match[0][0]
            match_target = match[0][1]
            return make_refnode(builder, from_doc_name, to_doc_name,
                                match_target, contnode, match_target)
        else:
            raise Exception(f'invalid option xref ({target})')

    def add_option(self, signature, option_name):
        if self.env.docname != 'cli':
            raise Exception(
                'qpdf:option directives don\'t work outside of cli.rst')

        name = f'option.{option_name}'
        anchor = f'option-{option_name}'

        # name, display_name, type, docname, anchor, priority
        self.data['options'].append(
            (name, signature, '', self.env.docname, anchor, 0))

    def purge_options(self, docname):
        self.data['options'] = list([
            x for x in self.data['options']
            if x[3] != docname
        ])


def purge_options(app, env, docname):
    option = env.get_domain('qpdf')
    option.purge_options(docname)


def setup(app):
    app.add_domain(QpdfDomain)
    app.connect('env-purge-doc', purge_options)

    return {
        'version': '0.1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }