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

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: 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" rel="stylesheet" t ype="text/css"/>'
56
57
58 class DevsiteHTMLTranslator(HTMLTranslator):
59 """ Custom HTML translator for devsite output.
60
61 Hooked into the HTML builder by setting the html_translator_class
62 option in conf.py
63 """
64 def __init__(self, builder, *args, **kwds):
65 # HTMLTranslator is an old-style Python class, so 'super' doesn't work: use
66 # direct parent invocation.
67 HTMLTranslator.__init__(self, builder, *args, **kwds)
68
69 self.within_ignored_h1 = False
70 self.within_toc = False
71
72 def visit_bullet_list(self, node):
73 if self.within_toc:
74 # Within a TOC, devsite wants <ol>
75 self.body.append(self.starttag(node, 'ol'))
76 else:
77 # Use our own class attribute for <ul>. Don't care about compacted lists.
78 self.body.append(self.starttag(node, 'ul', **{'class': 'small-gap'}))
79
80 def depart_bullet_list(self, node):
81 if self.within_toc:
82 self.body.append('</ol>\n')
83 else:
84 # Override to not pop anything from context
85 self.body.append('</ul>\n')
86
87 def visit_literal_block(self, node):
88 # We don't use Sphinx's buildin pygments integration for code highlighting,
89 # because the devsite requires special <pre> tags for that and handles the
90 # highlighting on its own.
91 attrs = {'class': 'prettyprint'} if node.get('prettyprint', 1) else {}
92 self.body.append(self.starttag(node, 'pre', **attrs))
93
94 def depart_literal_block(self, node):
95 self.body.append('\n</pre>\n')
96
97 def visit_paragraph(self, node):
98 # Don't generate <p>s within the table of contents
99 if not self.within_toc:
100 HTMLTranslator.visit_paragraph(self, node)
101
102 def depart_paragraph(self, node):
103 if not self.within_toc:
104 HTMLTranslator.depart_paragraph(self, node)
105
106 def visit_section(self, node):
107 # devsite needs <section> instead of <div class='section'>
108 self.section_level += 1
109 self.body.append(self.starttag(node, 'section'))
110
111 def depart_section(self, node):
112 self.section_level -= 1
113 self.body.append('</section>')
114
115 def visit_image(self, node):
116 # Paths to images in .rst sources should be absolute. This visitor does the
117 # required transformation for the path to be correct in the final HTML.
118 if self.builder.devsite_production_mode:
119 node['uri'] = '/native-client/' + node['uri']
120 HTMLTranslator.visit_image(self, node)
121
122 def visit_title(self, node):
123 # Why this?
124 #
125 # Sphinx insists on inserting a <h1>Page Title</h1> into the page, but this
126 # is not how the devsite wants it. The devsite inserts the page title on
127 # its own, the the extra h1 is duplication.
128 #
129 # Killing the doctree title node in a custom transform doesn't work, because
130 # Sphinx explicitly looks for it when writing a document. So instead we rig
131 # the HTML produced.
132 #
133 # When a title node is visited, and this is the h1-to-be, we ignore it and
134 # also set a flag that tells visit_Text not to print the actual text of the
135 # header.
136
137 # The h1 node is in the section whose parent is the document itself. Other
138 # sections have this top-section as their parent.
139 if (node.parent and node.parent.parent and
140 isinstance(node.parent.parent, nodes.document)):
141 # Internal flag. Also, nothing is pushed to the context. Our depart_title
142 # doesn't pop anything when this flag is set.
143 self.within_ignored_h1 = True
144 else:
145 HTMLTranslator.visit_title(self, node)
146
147 def depart_title(self, node):
148 if not self.within_ignored_h1:
149 HTMLTranslator.depart_title(self, node)
150 self.within_ignored_h1 = False
151
152 def visit_Text(self, node):
153 if not self.within_ignored_h1:
154 HTMLTranslator.visit_Text(self, node)
155
156 def visit_topic(self, node):
157 if 'contents' in node['classes']:
158 # Detect a TOC: this requires special treatment for devsite.
159 self.within_toc = True
160 # Emit <nav> manually and not through starttage because we don't want
161 # additional class components to be added
162 self.body.append('\n<nav class="inline-toc">')
163
164 def depart_topic(self, node):
165 if self.within_toc:
166 self.within_toc = False
167 self.body.append('</nav>\n')
168
169 def write_colspecs(self):
170 # Override this method from docutils to do nothing. We don't need those
171 # pesky <col width=NN /> tags in our markup.
172 pass
173
174 def visit_admonition(self, node, name=''):
175 self.body.append(self.starttag(node, 'aside', CLASS=node.get('class', '')))
176
177 def depart_admonition(self, node):
178 self.body.append('\n</aside>\n')
179
180
181 class DevsiteBuilder(StandaloneHTMLBuilder):
182 """ Builder for the NaCl devsite HTML output.
183
184 Loosely based on the code of Sphinx's standard SerializingHTMLBuilder.
185 """
186 name = 'devsite'
187 out_suffix = '.html'
188 link_suffix = '.html'
189
190 # Disable the addition of "pi"-permalinks to each section header
191 add_permalinks = False
192
193 def init(self):
194 self.devsite_production_mode = int(self.config.devsite_production_mode) == 1
195 print "----> Devsite builder with production mode = %d" % (
196 self.devsite_production_mode,)
197 self.config_hash = ''
198 self.tags_hash = ''
199 self.theme = None # no theme necessary
200 self.templates = None # no template bridge necessary
201 self.init_translator_class()
202 self.init_highlighter()
203
204 def get_target_uri(self, docname, typ=None):
205 if self.devsite_production_mode:
206 # TODO(eliben): testrst here will have to be replaced with
207 # {{pepperversion}}
208 return '/native-client/testrst/%s' % docname
209 else:
210 return docname + self.link_suffix
211
212 def handle_page(self, pagename, ctx, templatename='page.html',
213 outfilename=None, event_arg=None):
214 ctx['current_page_name'] = pagename
215
216 if not outfilename:
217 outfilename = os.path.join(self.outdir,
218 pagename + self.out_suffix)
219
220 # Emit an event to Sphinx
221 self.app.emit('html-page-context', pagename, templatename,
222 ctx, event_arg)
223
224 ensuredir(os.path.dirname(outfilename))
225 self._dump_context(ctx, outfilename)
226
227 def _dump_context(self, context, filename):
228 """ Do the actual dumping of the page to the file. context is a dict. Some
229 important fields:
230 body - document contents
231 title
232 current_page_name
233 Some special pages (genindex, etc.) may not have some of the fields, so
234 fetch them conservatively.
235 """
236 if not 'body' in context:
237 return
238 # codecs.open is the fast Python 2.x way of emulating the encoding= argument
239 # in Python 3's builtin open.
240 with codecs.open(filename, 'w', encoding='utf-8') as f:
241 f.write(PAGE_TEMPLATE.substitute(
242 doc_title=context.get('title', ''),
243 doc_body=context.get('body'),
244 nonprod_css=self._conditional_nonprod(NONPROD_CSS),
245 devsite_prefix=self._conditional_devsite(DEVSITE_PREFIX),
246 devsite_butterbar=self._conditional_devsite(DEVSITE_BUTTERBAR)))
247
248 def _indent_each_line(self, text, howmany=2):
249 # Can't really use this because it messes up <pre> formatting
250 indent = ' ' * howmany
251 result = []
252 for line in text.splitlines():
253 result.append(indent + line)
254 return '\n'.join(result)
255
256 def _conditional_devsite(self, s):
257 return s if self.devsite_production_mode else ''
258
259 def _conditional_nonprod(self, s):
260 return s if not self.devsite_production_mode else ''
261
262
263 class NaclCodeDirective(Directive):
264 """ Custom "naclcode" directive for code snippets. To keep it under our
265 control.
266 """
267 has_content = True
268 required_arguments = 0
269 optional_arguments = 1
270 option_spec = {
271 'prettyprint': int,
272 }
273
274 def run(self):
275 code = u'\n'.join(self.content)
276 literal = nodes.literal_block(code, code)
277 literal['prettyprint'] = self.options.get('prettyprint', 1)
278 return [literal]
279
280
281 def setup(app):
282 """ Extension registration hook.
283 """
284 app.add_directive('naclcode', NaclCodeDirective)
285 app.add_builder(DevsiteBuilder)
286
287 # "Production mode" for local testing vs. on-server documentation.
288 app.add_config_value('devsite_production_mode', default='1', rebuild='html')
289
290
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698