1"""
2cloud_sptheme.ext.docfield_markup -- monkeypatches sphinx to allow ``~`` in docfields.
3"""
4#=============================================================================
5# imports
6#=============================================================================
7# core
8import logging; log = logging.getLogger(__name__)
9# site
10# pkg
11from cloud_sptheme import __version__
12from cloud_sptheme.utils import patchapplier, monkeypatch
13# local
14__all__ = [
15    "setup",
16]
17
18#=============================================================================
19# patch
20#=============================================================================
21@patchapplier
22def _patch_docfield():
23    from sphinx.util.docfields import Field, nodes, addnodes
24
25    # NOTE: would like to just wrap make_xref(), but have to override first arg (rawsource) of
26    #       pending_xref() call, so have to replicate the code.
27
28    @monkeypatch(Field)
29    def make_xref(_wrapped, self, rolename, domain, target, innernode=addnodes.literal_emphasis,
30                  contnode=None, env=None):
31        #------------------------------------------------------------------------
32        # custom prep work before real sphinx code.
33        #------------------------------------------------------------------------
34
35        # given raw text in 'target', parse into 'title' and effective 'target'
36        # NOTE: this tries to replicate the markup convention used in PyXRefRole.process_link()
37        rawtext = title = target
38        if rawtext.startswith("~"):
39            # if the first character is a tilde, don't display the module/class
40            # parts of the content
41            target = target.lstrip("~")
42            title = target.rpartition(".")[2]
43
44        # create wrapper for innernode(title,title) calls, which honors custom title.
45        # NOTE: doing as wrapper so code below will match sphinx as closely as possible.
46        #       could instead just plug this in as contnode (if not already set)
47        node_type = innernode
48
49        def innernode(*ignored):
50            if issubclass(node_type, nodes.Text):
51                # Text classes want rawtext second
52                return node_type(title, rawtext)
53            else:
54                # Element classes want rawtext first
55                return node_type(rawtext, title)
56
57        #------------------------------------------------------------------------
58        # remainder of this should match sphinx code exactly, except where noted.
59        #------------------------------------------------------------------------
60        if not rolename:
61            return contnode or innernode(target, target)
62        # NOTE: passing <title> as first arg, sphinx passes ''
63        refnode = addnodes.pending_xref(title, refdomain=domain, refexplicit=False,
64                                        reftype=rolename, reftarget=target)
65        refnode += contnode or innernode(target, target)
66        if env:
67            env.domains[domain].process_field_xref(refnode)
68        return refnode
69
70#=============================================================================
71# sphinx entrypoint
72#=============================================================================
73def setup(app):
74    # don't apply our patch unless actually loaded by sphinx
75    _patch_docfield()
76
77    # identifies the version of our extension
78    return {'version': __version__}
79
80#=============================================================================
81# eoc
82#=============================================================================
83