File: domains.py

package info (click to toggle)
sphinx-hoverxref 1.4.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,296 kB
  • sloc: javascript: 2,538; python: 912; makefile: 16
file content (180 lines) | stat: -rw-r--r-- 6,574 bytes parent folder | download
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import docutils

from sphinx.util import logging

logger = logging.getLogger(__name__)


class HoverXRefBaseDomain:

    hoverxref_types = (
        'hoverxref',
        'hoverxreftooltip',
        'hoverxrefmodal',
    )

    def _inject_hoverxref_data(self, env, refnode, typ):
        from .extension import CSS_CLASSES, CSS_DEFAULT_CLASS

        classes = [CSS_DEFAULT_CLASS]
        type_class = None
        if typ == 'hoverxreftooltip':
            type_class = 'tooltip'
        elif typ == 'hoverxrefmodal':
            type_class = 'modal'

        if not type_class:
            type_class = env.config.hoverxref_role_types.get(typ)
            if not type_class:
                default = env.config.hoverxref_default_type
                type_class = default
                logger.info(
                    'Using default style (%s) for unknown typ (%s). '
                    'Define it in hoverxref_role_types.',
                    default,
                    typ,
                )

        # Examples: hxr-tooltip, hxr-modal
        classes.append(CSS_CLASSES[type_class])

        refnode.replace_attr('classes', classes)
        # TODO: log something else here, so we can unique identify this node
        logger.debug(
            ':%s: _hoverxref injected. classes=%s',
            typ,
            classes,
        )

    def _is_ignored_ref(self, env, target):
        # HACK: skip all references if the builder is non-html. We shouldn't
        # have overridden the Domain in first instance at ``setup_domains``
        # function, but at that time ``app.builder`` is not yet initialized. If
        # we suscribe ourselves to ``builder-initied`` it's too late and our
        # override does not take effect. Other builders (e.g. LatexBuilder) may
        # fail with internal functions we use (e.g. builder.get_outfilename).
        # So, we are skipping it here :(
        if env.app.builder.format != 'html':
            return True

        if target in env.config.hoverxref_ignore_refs:
            logger.info(
                'Ignoring reference in hoverxref_ignore_refs. target=%s',
                target,
            )
            return True
        return False


class HoverXRefPythonDomainMixin(HoverXRefBaseDomain):

    def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
        refnode = super().resolve_xref(env, fromdocname, builder, typ, target, node, contnode)
        if refnode is None:
            return refnode

        if self._is_ignored_ref(env, target):
            return refnode

        self._inject_hoverxref_data(env, refnode, typ)
        return refnode


class HoverXRefStandardDomainMixin(HoverXRefBaseDomain):
    """
    Mixin for ``StandardDomain`` to save the values after the xref resolution.

    ``:ref:`` are treating as a different node in Sphinx
    (``sphinx.addnodes.pending_xref``). These nodes are translated to regular
    ``docsutils.nodes.reference`` for this domain class.

    This class add the required ``hoverxref`` and ``modal``/``tooltip`` to tell
    the frontend to show a modal/tooltip on this element.
    """

    def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
        if typ in self.hoverxref_types:
            resolver = self._resolve_ref_xref
            return resolver(env, fromdocname, builder, typ, target, node, contnode)

        return super().resolve_xref(env, fromdocname, builder, typ, target, node, contnode)

    # NOTE: We could override more ``_resolve_xref`` method apply hover in more places
    def _resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
        refnode = super()._resolve_ref_xref(env, fromdocname, builder, typ, target, node, contnode)
        if refnode is None:
            return refnode

        if any([
                self._is_ignored_ref(env, target),
                not (env.config.hoverxref_auto_ref or typ in self.hoverxref_types)
        ]):
            return refnode


        self._inject_hoverxref_data(env, refnode, typ)
        return refnode

    def _resolve_obj_xref(self, env, fromdocname, builder, typ, target, node, contnode):
        refnode = super()._resolve_obj_xref(env, fromdocname, builder, typ, target, node, contnode)
        if refnode is None:
            return refnode

        if any([
                self._is_ignored_ref(env, target),
                typ not in env.config.hoverxref_roles,
        ]):
            return refnode

        self._inject_hoverxref_data(env, refnode, typ)
        return refnode

    # TODO: combine this method with ``_resolve_obj_xref``
    def _resolve_numref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
        refnode = super()._resolve_numref_xref(env, fromdocname, builder, typ, target, node, contnode)
        if refnode is None:
            return refnode

        if any([
                self._is_ignored_ref(env, target),
                typ not in env.config.hoverxref_roles,
        ]):
            return refnode

        self._inject_hoverxref_data(env, refnode, typ)
        return refnode


class HoverXRefBibtexDomainMixin(HoverXRefBaseDomain):
    """
    Mixin for ``BibtexDomain`` to save the values after the xref resolution.

    This class add the required ``hoverxref`` and ``modal``/``tooltip`` to tell
    the frontend to show a modal/tooltip on this element.

    https://github.com/mcmtroffaes/sphinxcontrib-bibtex/blob/2.4.1/src/sphinxcontrib/bibtex/domain.py#L281
    """

    def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
        textnode = super().resolve_xref(env, fromdocname, builder, typ, target, node, contnode)
        if textnode is None:
            return textnode

        if any([
                self._is_ignored_ref(env, target),
                not (env.config.hoverxref_auto_ref or typ in self.hoverxref_types or 'cite' in env.config.hoverxref_domains)
        ]):
            return textnode

        # The structure of the node generated by bibtex is between two
        # ``#text`` nodes and we need to add the classes into the ``reference``
        # node to get the ``href=`` attribute from it
        #
        # (Pdb++) textnode.children
        # [<#text: 'Nelson ['>, <reference: <#text: 'Nel87'>>, <#text: ']'>]
        refnode_index = textnode.first_child_matching_class(docutils.nodes.reference)
        if refnode_index:
            refnode = textnode.children[refnode_index]
            self._inject_hoverxref_data(env, refnode, typ)

        return textnode