You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
97 lines
3.6 KiB
Python
97 lines
3.6 KiB
Python
9 years ago
|
# Define a new directive `code-block` (aliased as `sourcecode`) that uses the
|
||
|
# `pygments` source highlighter to render code in color.
|
||
|
#
|
||
|
# Incorporates code from the `Pygments`_ documentation for `Using Pygments in
|
||
|
# ReST documents`_ and `Octopress`_.
|
||
|
#
|
||
|
# .. _Pygments: http://pygments.org/
|
||
|
# .. _Using Pygments in ReST documents: http://pygments.org/docs/rstdirective/
|
||
|
# .. _Octopress: http://octopress.org/
|
||
|
|
||
|
import re
|
||
|
import os
|
||
|
import md5
|
||
|
import __main__
|
||
|
|
||
|
# Absolute path to pygments cache dir
|
||
|
PYGMENTS_CACHE_DIR = os.path.abspath(os.path.join(os.path.dirname(__main__.__file__), '../../.pygments-cache'))
|
||
|
|
||
|
# Ensure cache dir exists
|
||
|
if not os.path.exists(PYGMENTS_CACHE_DIR):
|
||
|
os.makedirs(PYGMENTS_CACHE_DIR)
|
||
|
|
||
|
from pygments.formatters import HtmlFormatter
|
||
|
|
||
|
from docutils import nodes
|
||
|
from docutils.parsers.rst import directives, Directive
|
||
|
|
||
|
from pygments import highlight
|
||
|
from pygments.lexers import get_lexer_by_name, TextLexer
|
||
|
|
||
|
class Pygments(Directive):
|
||
|
""" Source code syntax hightlighting.
|
||
|
"""
|
||
|
required_arguments = 1
|
||
|
optional_arguments = 0
|
||
|
final_argument_whitespace = True
|
||
|
string_opts = ['title', 'url', 'caption']
|
||
|
option_spec = dict([(key, directives.unchanged) for key in string_opts])
|
||
|
has_content = True
|
||
|
|
||
|
def run(self):
|
||
|
self.assert_has_content()
|
||
|
try:
|
||
|
lexer_name = self.arguments[0]
|
||
|
lexer = get_lexer_by_name(lexer_name)
|
||
|
except ValueError:
|
||
|
# no lexer found - use the text one instead of an exception
|
||
|
lexer_name = 'text'
|
||
|
lexer = TextLexer()
|
||
|
formatter = HtmlFormatter()
|
||
|
|
||
|
# Construct cache filename
|
||
|
cache_file = None
|
||
|
content_text = u'\n'.join(self.content)
|
||
|
cache_file_name = '%s-%s.html' % (lexer_name, md5.new(content_text).hexdigest())
|
||
|
cached_path = os.path.join(PYGMENTS_CACHE_DIR, cache_file_name)
|
||
|
|
||
|
# Look for cached version, otherwise parse
|
||
|
if os.path.exists(cached_path):
|
||
|
cache_file = open(cached_path, 'r')
|
||
|
parsed = cache_file.read()
|
||
|
else:
|
||
|
parsed = highlight(content_text, lexer, formatter)
|
||
|
|
||
|
# Strip pre tag and everything outside it
|
||
|
pres = re.compile("<pre>(.+)<\/pre>", re.S)
|
||
|
stripped = pres.search(parsed).group(1)
|
||
|
|
||
|
# Create tabular code with line numbers
|
||
|
table = '<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers">'
|
||
|
lined = ''
|
||
|
for idx, line in enumerate(stripped.splitlines(True)):
|
||
|
table += '<span class="line-number">%d</span>\n' % (idx + 1)
|
||
|
lined += '<span class="line">%s</span>' % line
|
||
|
table += '</pre></td><td class="code"><pre><code class="%s">%s</code></pre></td></tr></table></div>' % (lexer_name, lined)
|
||
|
|
||
|
# Add wrapper with optional caption and link
|
||
|
code = '<figure class="code">'
|
||
|
if self.options:
|
||
|
caption = ('<span>%s</span>' % self.options['caption']) if 'caption' in self.options else ''
|
||
|
title = self.options['title'] if 'title' in self.options else 'link'
|
||
|
link = ('<a href="%s">%s</a>' % (self.options['url'], title)) if 'url' in self.options else ''
|
||
|
|
||
|
if caption or link:
|
||
|
code += '<figcaption>%s %s</figcaption>' % (caption, link)
|
||
|
code += '%s</figure>' % table
|
||
|
|
||
|
# Write cache
|
||
|
if cache_file is None:
|
||
|
cache_file = open(cached_path, 'w')
|
||
|
cache_file.write(parsed)
|
||
|
cache_file.close()
|
||
|
|
||
|
return [nodes.raw('', code, format='html')]
|
||
|
|
||
|
directives.register_directive('code-block', Pygments)
|
||
|
directives.register_directive('sourcecode', Pygments)
|