# 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>", re.S)
        stripped = pres.search(parsed).group(1)

        # Create tabular code with line numbers
        table = '
'
        lined = ''
        for idx, line in enumerate(stripped.splitlines(True)):
            table += '%d\n' % (idx + 1)
            lined  += '%s' % line
        table += '
%s
' % (lexer_name, lined) # Add wrapper with optional caption and link code = '
' if self.options: caption = ('%s' % self.options['caption']) if 'caption' in self.options else '' title = self.options['title'] if 'title' in self.options else 'link' link = ('%s' % (self.options['url'], title)) if 'url' in self.options else '' if caption or link: code += '
%s %s
' % (caption, link) code += '%s
' % 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)