Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(303)

Side by Side Diff: native_client_sdk/src/doc/_sphinxext/devsite_builder.py

Issue 23835002: [NaCl docs] Initial commit of the new docs infrastructure into Chromium. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Ignore Sphinx-y code in presubmit Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright (c) 2013 The Native Client Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 #
6 # This is a Sphinx extension.
7 #
8
9 import codecs
10 import os
11 import string
12 from docutils import nodes
13 from docutils.parsers.rst import Directive, directives
14 from sphinx.util.osutil import ensuredir
15 from sphinx.builders.html import StandaloneHTMLBuilder
16 from sphinx.writers.html import HTMLWriter
17 from sphinx.writers.html import SmartyPantsHTMLTranslator as HTMLTranslator
18
19
20 # TODO(eliben): it may be interesting to use an actual Sphinx template here at
21 # some point.
22 PAGE_TEMPLATE = string.Template(r'''
23 ${devsite_prefix}
24 <html devsite>
25 <head>
26 <title>${doc_title}</title>
27 <meta name="project_path" value="/native-client/_project.yaml" />
28 <meta name="book_path" value="/native-client/_book.yaml" />
29 <link href="/native-client/css/local_extensions.css" rel="stylesheet" type=" text/css"/>
30 ${nonprod_css}
31 <style type="text/css">
32 .nested-def {list-style-type: none; margin-bottom: 0.3em;}
33 .maxheight {height: 200px;}
34 </style>
35 </head>
36 <body>
37 ${devsite_butterbar}
38
39 ${doc_body}
40
41 </body>
42 </html>
43 '''.lstrip())
44
45 DEVSITE_PREFIX = r'''
46 {% setvar pepperversion %}pepper28{% endsetvar %}
47 {% include "native-client/_local_variables.html" %}
48 '''.lstrip()
49
50 DEVSITE_BUTTERBAR = '{{butterbar}}'
51
52 # We want the non-production-mode HTML to resemble the real one, so this points
53 # to a copied version of the devsite CSS that we'll keep locally. It's for
54 # testing purposes only.
55 NONPROD_CSS = '<link href="/_static/css/local_extensions.css"'\
56 'rel="stylesheet" type="text/css"/>'
57
58
59 class DevsiteHTMLTranslator(HTMLTranslator):
60 """ Custom HTML translator for devsite output.
61
62 Hooked into the HTML builder by setting the html_translator_class
63 option in conf.py
64 """
65 def __init__(self, builder, *args, **kwds):
66 # HTMLTranslator is an old-style Python class, so 'super' doesn't work: use
67 # direct parent invocation.
68 HTMLTranslator.__init__(self, builder, *args, **kwds)
69
70 self.within_ignored_h1 = False
71 self.within_toc = False
72
73 def visit_bullet_list(self, node):
74 if self.within_toc:
75 # Within a TOC, devsite wants <ol>
76 self.body.append(self.starttag(node, 'ol'))
77 else:
78 # Use our own class attribute for <ul>. Don't care about compacted lists.
79 self.body.append(self.starttag(node, 'ul', **{'class': 'small-gap'}))
80
81 def depart_bullet_list(self, node):
82 if self.within_toc:
83 self.body.append('</ol>\n')
84 else:
85 # Override to not pop anything from context
86 self.body.append('</ul>\n')
87
88 def visit_literal_block(self, node):
89 # We don't use Sphinx's buildin pygments integration for code highlighting,
90 # because the devsite requires special <pre> tags for that and handles the
91 # highlighting on its own.
92 attrs = {'class': 'prettyprint'} if node.get('prettyprint', 1) else {}
93 self.body.append(self.starttag(node, 'pre', **attrs))
94
95 def depart_literal_block(self, node):
96 self.body.append('\n</pre>\n')
97
98 def visit_paragraph(self, node):
99 # Don't generate <p>s within the table of contents
100 if not self.within_toc:
101 HTMLTranslator.visit_paragraph(self, node)
102
103 def depart_paragraph(self, node):
104 if not self.within_toc:
105 HTMLTranslator.depart_paragraph(self, node)
106
107 def visit_section(self, node):
108 # devsite needs <section> instead of <div class='section'>
109 self.section_level += 1
110 self.body.append(self.starttag(node, 'section'))
111
112 def depart_section(self, node):
113 self.section_level -= 1
114 self.body.append('</section>')
115
116 def visit_image(self, node):
117 # Paths to images in .rst sources should be absolute. This visitor does the
118 # required transformation for the path to be correct in the final HTML.
119 if self.builder.devsite_production_mode:
120 node['uri'] = '/native-client/' + node['uri']
121 HTMLTranslator.visit_image(self, node)
122
123 def visit_title(self, node):
124 # Why this?
125 #
126 # Sphinx insists on inserting a <h1>Page Title</h1> into the page, but this
127 # is not how the devsite wants it. The devsite inserts the page title on
128 # its own, the the extra h1 is duplication.
129 #
130 # Killing the doctree title node in a custom transform doesn't work, because
131 # Sphinx explicitly looks for it when writing a document. So instead we rig
132 # the HTML produced.
133 #
134 # When a title node is visited, and this is the h1-to-be, we ignore it and
135 # also set a flag that tells visit_Text not to print the actual text of the
136 # header.
137
138 # The h1 node is in the section whose parent is the document itself. Other
139 # sections have this top-section as their parent.
140 if (node.parent and node.parent.parent and
141 isinstance(node.parent.parent, nodes.document)):
142 # Internal flag. Also, nothing is pushed to the context. Our depart_title
143 # doesn't pop anything when this flag is set.
144 self.within_ignored_h1 = True
145 else:
146 HTMLTranslator.visit_title(self, node)
147
148 def depart_title(self, node):
149 if not self.within_ignored_h1:
150 HTMLTranslator.depart_title(self, node)
151 self.within_ignored_h1 = False
152
153 def visit_Text(self, node):
154 if not self.within_ignored_h1:
155 HTMLTranslator.visit_Text(self, node)
156
157 def visit_topic(self, node):
158 if 'contents' in node['classes']:
159 # Detect a TOC: this requires special treatment for devsite.
160 self.within_toc = True
161 # Emit <nav> manually and not through starttage because we don't want
162 # additional class components to be added
163 self.body.append('\n<nav class="inline-toc">')
164
165 def depart_topic(self, node):
166 if self.within_toc:
167 self.within_toc = False
168 self.body.append('</nav>\n')
169
170 def write_colspecs(self):
171 # Override this method from docutils to do nothing. We don't need those
172 # pesky <col width=NN /> tags in our markup.
173 pass
174
175 def visit_admonition(self, node, name=''):
176 self.body.append(self.starttag(node, 'aside', CLASS=node.get('class', '')))
177
178 def depart_admonition(self, node=''):
179 self.body.append('\n</aside>\n')
180
181 def unknown_visit(self, node):
182 raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
183
184
185 class DevsiteBuilder(StandaloneHTMLBuilder):
186 """ Builder for the NaCl devsite HTML output.
187
188 Loosely based on the code of Sphinx's standard SerializingHTMLBuilder.
189 """
190 name = 'devsite'
191 out_suffix = '.html'
192 link_suffix = '.html'
193
194 # Disable the addition of "pi"-permalinks to each section header
195 add_permalinks = False
196
197 def init(self):
198 self.devsite_production_mode = int(self.config.devsite_production_mode) == 1
199 print "----> Devsite builder with production mode = %d" % (
200 self.devsite_production_mode,)
201 self.config_hash = ''
202 self.tags_hash = ''
203 self.theme = None # no theme necessary
204 self.templates = None # no template bridge necessary
205 self.init_translator_class()
206 self.init_highlighter()
207
208 def get_target_uri(self, docname, typ=None):
209 if self.devsite_production_mode:
210 # TODO(eliben): testrst here will have to be replaced with
211 # {{pepperversion}}
212 return '/native-client/testrst/%s' % docname
213 else:
214 return docname + self.link_suffix
215
216 def handle_page(self, pagename, ctx, templatename='page.html',
217 outfilename=None, event_arg=None):
218 ctx['current_page_name'] = pagename
219
220 if not outfilename:
221 outfilename = os.path.join(self.outdir,
222 pagename + self.out_suffix)
223
224 # Emit an event to Sphinx
225 self.app.emit('html-page-context', pagename, templatename,
226 ctx, event_arg)
227
228 ensuredir(os.path.dirname(outfilename))
229 self._dump_context(ctx, outfilename)
230
231 def _dump_context(self, context, filename):
232 """ Do the actual dumping of the page to the file. context is a dict. Some
233 important fields:
234 body - document contents
235 title
236 current_page_name
237 Some special pages (genindex, etc.) may not have some of the fields, so
238 fetch them conservatively.
239 """
240 if not 'body' in context:
241 return
242 # codecs.open is the fast Python 2.x way of emulating the encoding= argument
243 # in Python 3's builtin open.
244 with codecs.open(filename, 'w', encoding='utf-8') as f:
245 f.write(PAGE_TEMPLATE.substitute(
246 doc_title=context.get('title', ''),
247 doc_body=context.get('body'),
248 nonprod_css=self._conditional_nonprod(NONPROD_CSS),
249 devsite_prefix=self._conditional_devsite(DEVSITE_PREFIX),
250 devsite_butterbar=self._conditional_devsite(DEVSITE_BUTTERBAR)))
251
252 def _conditional_devsite(self, s):
253 return s if self.devsite_production_mode else ''
254
255 def _conditional_nonprod(self, s):
256 return s if not self.devsite_production_mode else ''
257
258
259 class NaclCodeDirective(Directive):
260 """ Custom "naclcode" directive for code snippets. To keep it under our
261 control.
262 """
263 has_content = True
264 required_arguments = 0
265 optional_arguments = 1
266 option_spec = {
267 'prettyprint': int,
268 }
269
270 def run(self):
271 code = u'\n'.join(self.content)
272 literal = nodes.literal_block(code, code)
273 literal['prettyprint'] = self.options.get('prettyprint', 1)
274 return [literal]
275
276
277 def setup(app):
278 """ Extension registration hook.
279 """
280 app.add_directive('naclcode', NaclCodeDirective)
281 app.add_builder(DevsiteBuilder)
282
283 # "Production mode" for local testing vs. on-server documentation.
284 app.add_config_value('devsite_production_mode', default='1', rebuild='html')
285
286
OLDNEW
« no previous file with comments | « native_client_sdk/src/doc/README ('k') | native_client_sdk/src/doc/_static/css/local_extensions.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698