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(status_code) | 202 self.send_response(status_code) |
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 # Given |
| 219 # |
| 220 # # H1 |
| 221 # |
| 222 # [TOC] |
| 223 # |
| 224 # ## first H2 |
| 225 # |
| 226 # ## second H2 |
| 227 # |
| 228 # the markdown.extensions.toc extension generates: |
| 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 # for [TOC]. But, we want the TOC to have its own subheading, so |
| 236 # we rewrite <div class='toc'><ul>...</ul></div> to: |
| 237 # |
| 238 # <div class='toc'> |
| 239 # <h2>Contents</h2> |
| 240 # <div class='toc-aux'> |
| 241 # <ul>...</ul></div></div> |
| 242 # |
| 243 # In addition, if the document only has a single H1, it is usually the |
| 244 # title, and we don't want the title to be in the TOC. So, we remove it |
| 245 # and shift all of the title's children up a level, leaving: |
| 246 # |
| 247 # <div class='toc'> |
| 248 # <h2>Contents</h2> |
| 249 # <div class='toc-aux'> |
| 250 # <ul><li>first H2 |
| 251 # <li>second H2</li></ul></div></div> |
| 252 |
| 253 for toc_node in tree.findall(".//*[@class='toc']"): |
| 254 toc_ul = toc_node[0] |
| 255 if self.has_a_single_h1: |
| 256 toc_ul_li = toc_ul[0] |
| 257 ul_with_the_desired_toc_entries = toc_ul_li[1] |
| 258 else: |
| 259 ul_with_the_desired_toc_entries = toc_ul |
| 260 |
| 261 toc_node.remove(toc_ul) |
| 262 contents = ElementTree.SubElement(toc_node, 'h2') |
| 263 contents.text = 'Contents' |
| 264 contents.tail = '\n' |
| 265 toc_aux = ElementTree.SubElement(toc_node, 'div', {'class': 'toc-aux'}) |
| 266 toc_aux.text = '\n' |
| 267 toc_aux.append(ul_with_the_desired_toc_entries) |
| 268 toc_aux.tail = '\n' |
| 269 |
| 270 |
202 if __name__ == '__main__': | 271 if __name__ == '__main__': |
203 sys.exit(main(sys.argv[1:])) | 272 sys.exit(main(sys.argv[1:])) |
OLD | NEW |