OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Simple Markdown browser for a Git checkout.""" | 6 """Simple Markdown browser for a Git checkout.""" |
7 from __future__ import print_function | 7 from __future__ import print_function |
8 | 8 |
9 import SimpleHTTPServer | 9 import SimpleHTTPServer |
10 import SocketServer | 10 import SocketServer |
11 import argparse | 11 import argparse |
12 import codecs | 12 import codecs |
13 import os | 13 import os |
14 import re | 14 import re |
15 import socket | 15 import socket |
16 import sys | 16 import sys |
17 import threading | 17 import threading |
18 import time | 18 import time |
19 import webbrowser | 19 import webbrowser |
20 from xml.etree import ElementTree | |
20 | 21 |
21 | 22 |
22 THIS_DIR = os.path.realpath(os.path.dirname(__file__)) | 23 THIS_DIR = os.path.realpath(os.path.dirname(__file__)) |
23 SRC_DIR = os.path.dirname(os.path.dirname(THIS_DIR)) | 24 SRC_DIR = os.path.dirname(os.path.dirname(THIS_DIR)) |
24 sys.path.append(os.path.join(SRC_DIR, 'third_party', 'Python-Markdown')) | 25 sys.path.insert(0, os.path.join(SRC_DIR, 'third_party', 'Python-Markdown')) |
25 import markdown | 26 import markdown |
26 | 27 |
27 | 28 |
28 def main(argv): | 29 def main(argv): |
29 parser = argparse.ArgumentParser(prog='md_browser') | 30 parser = argparse.ArgumentParser(prog='md_browser') |
30 parser.add_argument('-p', '--port', type=int, default=8080, | 31 parser.add_argument('-p', '--port', type=int, default=8080, |
31 help='port to run on (default = %(default)s)') | 32 help='port to run on (default = %(default)s)') |
32 parser.add_argument('-d', '--directory', type=str, default=SRC_DIR) | 33 parser.add_argument('-d', '--directory', type=str, default=SRC_DIR) |
33 parser.add_argument('file', nargs='?', | 34 parser.add_argument('file', nargs='?', |
34 help='open file in browser') | 35 help='open file in browser') |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 'markdown.extensions.toc', | 149 'markdown.extensions.toc', |
149 'gitiles_ext_blocks', | 150 'gitiles_ext_blocks', |
150 ] | 151 ] |
151 extension_configs = { | 152 extension_configs = { |
152 'markdown.extensions.toc': { | 153 'markdown.extensions.toc': { |
153 'slugify': _gitiles_slugify | 154 'slugify': _gitiles_slugify |
154 }, | 155 }, |
155 } | 156 } |
156 | 157 |
157 contents = self._Read(path[1:]) | 158 contents = self._Read(path[1:]) |
158 md_fragment = markdown.markdown(contents, | 159 |
159 extensions=extensions, | 160 md = markdown.Markdown(extensions=extensions, |
160 extension_configs=extension_configs, | 161 extension_configs=extension_configs, |
161 output_format='html4').encode('utf-8') | 162 output_format='html4') |
163 | |
164 has_a_single_h1 = (len([line for line in contents.splitlines() | |
165 if (line.startswith('#') and | |
166 not line.startswith('##'))]) == 1) | |
167 | |
168 md.treeprocessors['adjust_toc'] = _AdjustTOC(has_a_single_h1) | |
169 | |
170 md_fragment = md.convert(contents).encode('utf-8') | |
171 | |
162 try: | 172 try: |
163 self._WriteHeader('text/html') | 173 self._WriteHeader('text/html') |
164 self._WriteTemplate('header.html') | 174 self._WriteTemplate('header.html') |
165 self.wfile.write(md_fragment) | 175 self.wfile.write(md_fragment) |
166 self._WriteTemplate('footer.html') | 176 self._WriteTemplate('footer.html') |
167 except: | 177 except: |
168 raise | 178 raise |
169 | 179 |
170 def _DoCSS(self, template): | 180 def _DoCSS(self, template): |
171 self._WriteHeader('text/css') | 181 self._WriteHeader('text/css') |
(...skipping 20 matching lines...) Expand all Loading... | |
192 self.send_response(200) | 202 self.send_response(200) |
193 self.send_header('Content-Type', content_type) | 203 self.send_header('Content-Type', content_type) |
194 self.end_headers() | 204 self.end_headers() |
195 | 205 |
196 def _WriteTemplate(self, template): | 206 def _WriteTemplate(self, template): |
197 contents = self._Read(os.path.join('tools', 'md_browser', template), | 207 contents = self._Read(os.path.join('tools', 'md_browser', template), |
198 relative_to=SRC_DIR) | 208 relative_to=SRC_DIR) |
199 self.wfile.write(contents.encode('utf-8')) | 209 self.wfile.write(contents.encode('utf-8')) |
200 | 210 |
201 | 211 |
212 class _AdjustTOC(markdown.treeprocessors.Treeprocessor): | |
213 def __init__(self, has_a_single_h1): | |
214 super(_AdjustTOC, self).__init__() | |
215 self.has_a_single_h1 = has_a_single_h1 | |
216 | |
217 def run(self, tree): | |
218 # The markdown.extensions.toc extension will replace | |
219 # | |
220 # # H1 | |
221 # | |
222 # [TOC] | |
223 # | |
224 # ## first H2 | |
225 # | |
226 # ## second H2 | |
227 # | |
228 # with the following HTML: | |
229 # | |
230 # <div class='toc'> | |
231 # <ul><li><a>H1</a> | |
232 # <ul><li>first H2 | |
233 # <li>second H2</li></ul></li><ul></div> | |
234 # | |
235 # We want to replace the first ul with the second one, and also patch | |
236 # in a new H2 header for the TOC itself, ending up with: | |
237 # | |
238 # <div class='toc'> | |
239 # <h2>Contents</h2> | |
240 # <div class='toc-aux'> | |
241 # <ul><li>first H2 | |
242 # <li>second H2</li></ul></div></div> | |
243 # | |
244 # If the document has more than one H1, or no H1s at all, we leave | |
245 # the tree alone but still wrap it in a toc-aux div and add the | |
246 # Contents header. | |
247 | |
248 toc_nodes = tree.findall(".//*[@class='toc']") | |
249 if not toc_nodes: | |
250 return | |
251 | |
252 for toc_node in toc_nodes: | |
253 if self.has_a_single_h1: | |
254 toc_ul = toc_node[0] | |
255 toc_ul_li = toc_ul[0] | |
256 toc_ul_li_ul = toc_ul_li[1] | |
257 children = toc_ul_li_ul | |
258 del toc_node[0] | |
259 else: | |
260 children = list(toc_node) | |
jsbell
2016/11/08 17:29:12
I'm unfamiliar with ElementTree and the exact stru
Dirk Pranke
2016/11/08 21:55:07
Ah, good catch.
| |
261 while len(toc_node): | |
262 del toc_node[0] | |
263 | |
264 contents = ElementTree.SubElement(toc_node, 'h2') | |
265 contents.text = 'Contents' | |
266 contents.tail = '\n' | |
267 toc_aux = ElementTree.SubElement(toc_node, 'div', | |
268 attrib={'class': 'toc-aux'}) | |
269 toc_aux.text = '\n' | |
270 toc_aux_ul = ElementTree.SubElement(toc_aux, 'ul') | |
271 toc_aux_ul.text = '\n' | |
272 toc_aux_ul.extend(children) | |
273 toc_aux_ul.tail = '\n' | |
274 toc_aux.tail = '\n' | |
275 | |
276 | |
202 if __name__ == '__main__': | 277 if __name__ == '__main__': |
203 sys.exit(main(sys.argv[1:])) | 278 sys.exit(main(sys.argv[1:])) |
OLD | NEW |