Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Defines a :epkg:`sphinx` extension for a quote.
5"""
6from docutils import nodes
7from docutils.parsers.rst import directives
9import sphinx
10from sphinx.locale import _
11from docutils.parsers.rst.directives.admonitions import BaseAdmonition
12from docutils.statemachine import StringList
13from sphinx.util.nodes import nested_parse_with_titles
16class quote_node(nodes.admonition):
17 """
18 Defines ``quote`` node.
19 """
20 pass
23class QuoteNode(BaseAdmonition):
24 """
25 A ``quotedef`` entry, displayed in the form of an admonition.
26 It takes the following options:
28 * *author*
29 * *book*
30 * *year*
31 * *pages*
32 * *tag*
33 * *source*
34 * *lid* or *label*
35 * *index*, additional index words beside the title and the author
36 * *date*, if the text was written or declared at specific date
38 Example::
40 .. quote::
41 :author: author
42 :book: book
43 :year: year
44 :pages: pages (optional)
45 :tag: something
46 :lid: id (used for further reference)
47 :source: optional
48 :index: word
50 A monkey could...
51 """
53 node_class = quote_node
54 has_content = True
55 required_arguments = 0
56 optional_arguments = 0
57 final_argument_whitespace = False
58 option_spec = {
59 'author': directives.unchanged,
60 'book': directives.unchanged,
61 'year': directives.unchanged,
62 'pages': directives.unchanged,
63 'tag': directives.unchanged,
64 'lid': directives.unchanged,
65 'label': directives.unchanged,
66 'source': directives.unchanged,
67 'class': directives.class_option,
68 'index': directives.unchanged,
69 'date': directives.unchanged,
70 }
72 def run(self):
73 """
74 Builds the mathdef text.
75 """
76 env = self.state.document.settings.env if hasattr(
77 self.state.document.settings, "env") else None
78 docname = None if env is None else env.docname
79 if docname is not None:
80 docname = docname.replace("\\", "/").split("/")[-1]
82 if not self.options.get('class'):
83 self.options['class'] = ['admonition-quote']
85 # body
86 (quote,) = super(QuoteNode, self).run()
87 if isinstance(quote, nodes.system_message):
88 return [quote] # pragma: no cover
90 # mid
91 tag = self.options.get('tag', 'quotetag').strip()
92 if len(tag) == 0:
93 raise ValueError("tag is empty") # pragma: no cover
95 def __(text):
96 if text:
97 return _(text)
98 return ""
100 # book
101 author = __(self.options.get('author', "").strip())
102 book = __(self.options.get('book', "").strip())
103 pages = __(self.options.get('pages', "").strip())
104 year = __(self.options.get('year', "").strip())
105 source = __(self.options.get('source', "").strip())
106 index = __(self.options.get('index', "").strip())
107 date = __(self.options.get('date', "").strip())
109 indexes = []
110 if index:
111 indexes.append(index) # pragma: no cover
113 # add a label
114 lid = self.options.get('lid', self.options.get('label', None))
115 if lid:
116 tnl = ['', ".. _{0}:".format(lid), ""]
117 else:
118 tnl = [] # pragma: no cover
120 if author:
121 tnl.append("**{0}**, ".format(author))
122 indexes.append(author)
123 if book:
124 tnl.append("*{0}*".format(book))
125 indexes.append(book)
126 if pages:
127 tnl.append(", {0}".format(pages))
128 if date:
129 tnl.append(" ({0})".format(date))
130 if source:
131 if source.startswith("http"):
132 tnl.append(", `source <{0}>`_".format(source))
133 else:
134 tnl.append(", {0}".format(source))
135 tnl.append('')
136 tnl.append(".. index:: " + ", ".join(indexes))
137 tnl.append('')
139 content = StringList(tnl)
140 content = content + self.content
141 node = quote_node()
143 try:
144 nested_parse_with_titles(self.state, content, node)
145 except Exception as e: # pragma: no cover
146 from sphinx.util import logging
147 logger = logging.getLogger("blogpost")
148 logger.warning(
149 "[blogpost] unable to parse '{0}' - '{1}' - {2}".format(author, book, e))
150 raise e
152 node['tag'] = tag
153 node['author'] = author
154 node['pages'] = pages
155 node['year'] = year
156 node['label'] = lid
157 node['source'] = source
158 node['book'] = book
159 node['index'] = index
160 node['content'] = '\n'.join(self.content)
161 node['classes'] += ["quote"]
163 return [node]
166def visit_quote_node(self, node):
167 """
168 visit_quote_node
169 """
170 self.visit_admonition(node)
173def depart_quote_node(self, node):
174 """
175 depart_quote_node,
176 see https://github.com/sphinx-doc/sphinx/blob/master/sphinx/writers/html.py
177 """
178 self.depart_admonition(node)
181def visit_quote_node_rst(self, node):
182 """
183 visit_quote_node
184 """
185 self.new_state(0)
186 self.add_text(".. quote::")
187 for k, v in sorted(node.attributes.items()):
188 if k in ("content", 'classes'):
189 continue
190 if v:
191 self.new_state(4)
192 self.add_text(":{0}: {1}".format(k, v))
193 self.end_state(wrap=False, end=None)
194 self.add_text(self.nl)
195 self.new_state(4)
196 self.add_text(node['content'])
197 self.end_state()
198 self.end_state()
199 raise nodes.SkipNode
202def depart_quote_node_rst(self, node):
203 """
204 depart_quote_node,
205 see https://github.com/sphinx-doc/sphinx/blob/master/sphinx/writers/html.py
206 """
207 pass
210def setup(app):
211 """
212 setup for ``mathdef`` (sphinx)
213 """
214 if hasattr(app, "add_mapping"):
215 app.add_mapping('quote', quote_node)
217 app.add_node(quote_node,
218 html=(visit_quote_node, depart_quote_node),
219 epub=(visit_quote_node, depart_quote_node),
220 elatex=(visit_quote_node, depart_quote_node),
221 latex=(visit_quote_node, depart_quote_node),
222 text=(visit_quote_node, depart_quote_node),
223 md=(visit_quote_node, depart_quote_node),
224 rst=(visit_quote_node_rst, depart_quote_node_rst))
226 app.add_directive('quote', QuoteNode)
227 return {'version': sphinx.__display_version__, 'parallel_read_safe': True}