1 | """ |
---|
2 | New Doctree Transforms |
---|
3 | ~~~~~~~~~~~~~~~~~~~~~~ |
---|
4 | |
---|
5 | .. autoclass:: BibliographyTransform |
---|
6 | :show-inheritance: |
---|
7 | |
---|
8 | .. autoattribute:: default_priority |
---|
9 | .. automethod:: apply |
---|
10 | |
---|
11 | .. autofunction:: node_text_transform |
---|
12 | |
---|
13 | .. autofunction:: transform_curly_bracket_strip |
---|
14 | |
---|
15 | .. autofunction:: transform_url_command |
---|
16 | """ |
---|
17 | |
---|
18 | import docutils.nodes |
---|
19 | import docutils.transforms |
---|
20 | |
---|
21 | from pybtex.plugin import find_plugin |
---|
22 | |
---|
23 | from sphinxcontrib.bibtex.nodes import bibliography |
---|
24 | |
---|
25 | |
---|
26 | def node_text_transform(node, transform): |
---|
27 | """Apply transformation to all Text nodes within node.""" |
---|
28 | for child in node.children: |
---|
29 | if isinstance(child, docutils.nodes.Text): |
---|
30 | node.replace(child, transform(child)) |
---|
31 | else: |
---|
32 | node_text_transform(child, transform) |
---|
33 | |
---|
34 | |
---|
35 | def transform_curly_bracket_strip(textnode): |
---|
36 | """Strip curly brackets from text.""" |
---|
37 | text = textnode.astext() |
---|
38 | if '{' in text or '}' in text: |
---|
39 | text = text.replace('{', '').replace('}', '') |
---|
40 | return docutils.nodes.Text(text) |
---|
41 | else: |
---|
42 | return textnode |
---|
43 | |
---|
44 | |
---|
45 | def transform_url_command(textnode): |
---|
46 | """Convert '\\\\url{...}' into a proper docutils hyperlink.""" |
---|
47 | text = textnode.astext() |
---|
48 | if '\\url' in text: |
---|
49 | text1, _, text = text.partition('\\url') |
---|
50 | text2, _, text3 = text.partition('}') |
---|
51 | text2 = text2.lstrip(' {') |
---|
52 | ref = docutils.nodes.reference(refuri=text2) |
---|
53 | ref += docutils.nodes.Text(text2) |
---|
54 | node = docutils.nodes.inline() |
---|
55 | node += transform_url_command(docutils.nodes.Text(text1)) |
---|
56 | node += ref |
---|
57 | node += transform_url_command(docutils.nodes.Text(text3)) |
---|
58 | return node |
---|
59 | else: |
---|
60 | return textnode |
---|
61 | |
---|
62 | |
---|
63 | class BibliographyTransform(docutils.transforms.Transform): |
---|
64 | |
---|
65 | """A docutils transform to generate citation entries for |
---|
66 | bibliography nodes. |
---|
67 | """ |
---|
68 | |
---|
69 | # transform must be applied before references are resolved |
---|
70 | default_priority = 10 |
---|
71 | """Priority of the transform. See |
---|
72 | http://docutils.sourceforge.net/docs/ref/transforms.html |
---|
73 | """ |
---|
74 | |
---|
75 | def apply(self): |
---|
76 | """Transform each |
---|
77 | :class:`~sphinxcontrib.bibtex.nodes.bibliography` node into a |
---|
78 | list of citations. |
---|
79 | """ |
---|
80 | env = self.document.settings.env |
---|
81 | docname = env.docname |
---|
82 | for bibnode in self.document.traverse(bibliography): |
---|
83 | id_ = bibnode['ids'][0] |
---|
84 | bibcache = env.bibtex_cache.get_bibliography_cache( |
---|
85 | docname=docname, id_=id_) |
---|
86 | entries = env.bibtex_cache.get_bibliography_entries( |
---|
87 | docname=docname, id_=id_, warn=env.app.warn) |
---|
88 | # locate and instantiate style and backend plugins |
---|
89 | style = find_plugin('pybtex.style.formatting', bibcache.style)() |
---|
90 | backend = find_plugin('pybtex.backends', 'docutils')() |
---|
91 | # create citation nodes for all references |
---|
92 | if bibcache.list_ == "enumerated": |
---|
93 | nodes = docutils.nodes.enumerated_list() |
---|
94 | nodes['enumtype'] = bibcache.enumtype |
---|
95 | if bibcache.start >= 1: |
---|
96 | nodes['start'] = bibcache.start |
---|
97 | env.bibtex_cache.set_enum_count( |
---|
98 | env.docname, bibcache.start) |
---|
99 | else: |
---|
100 | nodes['start'] = env.bibtex_cache.get_enum_count( |
---|
101 | env.docname) |
---|
102 | elif bibcache.list_ == "bullet": |
---|
103 | nodes = docutils.nodes.bullet_list() |
---|
104 | else: # "citation" |
---|
105 | nodes = docutils.nodes.paragraph() |
---|
106 | # remind: style.format_entries modifies entries in unpickable way |
---|
107 | for entry in style.format_entries(entries): |
---|
108 | if bibcache.list_ in ["enumerated", "bullet"]: |
---|
109 | citation = docutils.nodes.list_item() |
---|
110 | citation += backend.paragraph(entry) |
---|
111 | else: # "citation" |
---|
112 | citation = backend.citation(entry, self.document) |
---|
113 | # backend.citation(...) uses entry.key as citation label |
---|
114 | # we change it to entry.label later onwards |
---|
115 | # but we must note the entry.label now; |
---|
116 | # at this point, we also already prefix the label |
---|
117 | key = citation[0].astext() |
---|
118 | bibcache.labels[key] = bibcache.labelprefix + entry.label |
---|
119 | node_text_transform(citation, transform_url_command) |
---|
120 | if bibcache.curly_bracket_strip: |
---|
121 | node_text_transform( |
---|
122 | citation, |
---|
123 | transform_curly_bracket_strip) |
---|
124 | nodes += citation |
---|
125 | if bibcache.list_ == "enumerated": |
---|
126 | env.bibtex_cache.inc_enum_count(env.docname) |
---|
127 | bibnode.replace_self(nodes) |
---|