summaryrefslogtreecommitdiff
path: root/sandbox/code-block-directive/pygments_code_block_directive.py
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/code-block-directive/pygments_code_block_directive.py')
-rwxr-xr-xsandbox/code-block-directive/pygments_code_block_directive.py143
1 files changed, 95 insertions, 48 deletions
diff --git a/sandbox/code-block-directive/pygments_code_block_directive.py b/sandbox/code-block-directive/pygments_code_block_directive.py
index 0a115d905..e2abe30b6 100755
--- a/sandbox/code-block-directive/pygments_code_block_directive.py
+++ b/sandbox/code-block-directive/pygments_code_block_directive.py
@@ -1,6 +1,7 @@
#!/usr/bin/python
+# coding: utf-8
-# :Author: the Pygments team; Felix Wiemann; Guenter Milde
+# :Author: Georg Brandl; Felix Wiemann; Günter Milde
# :Date: $Date$
# :Copyright: This module has been placed in the public domain.
#
@@ -9,7 +10,7 @@
#
# .. class:: borderless
#
-# ========== ===========================================================
+# ========== =============================================================
# 2007-06-01 Removed redundancy from class values.
# 2007-06-04 Merge of successive tokens of same type
# (code taken from pygments.formatters.others).
@@ -20,26 +21,29 @@
# (misnamed as docutils_formatter) as class DocutilsInterface
# 2007-06-08 Failsave implementation (fallback to a standard literal block
# if pygments not found)
-# ========== ===========================================================
+# 2010-11-27 Rename directive and class from "code-block" to "code".
+# Fix fallback if pygments not found.
+# Use class-based interface.
+# Add "number-lines" option.
+# ========== =============================================================
#
# ::
-"""Define and register a code-block directive using pygments
-"""
+"""Define and register a code directive using pygments"""
# Requirements
# ------------
# ::
from docutils import nodes
-from docutils.parsers.rst import directives
+from docutils.parsers.rst import directives, Directive
try:
import pygments
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import _get_ttype_class
+ with_pygments = True
except ImportError:
- pass
-
+ with_pygments = False
# Customisation
@@ -50,16 +54,13 @@ except ImportError:
unstyled_tokens = ['']
-# DocutilsInterface
+# Tokenizer
# -----------------
#
# This interface class combines code from
# pygments.formatters.html and pygments.formatters.others.
-#
-# It does not require anything of docutils and could also become a part of
-# pygments::
-class DocutilsInterface(object):
+class Tokenizer(object):
"""Parse `code` string and yield "classified" tokens.
Arguments
@@ -72,7 +73,6 @@ class DocutilsInterface(object):
Yields the tokens as ``(ttype_class, value)`` tuples,
where ttype_class is taken from pygments.token.STANDARD_TYPES and
corresponds to the class argument used in pygments html output.
-
"""
def __init__(self, code, language):
@@ -84,13 +84,15 @@ class DocutilsInterface(object):
try:
lexer = get_lexer_by_name(self.language)
except ValueError:
- # info: "no pygments lexer for %s, using 'text'"%self.language
+ # info: 'no pygments lexer for %s, using "text"' % self.language
lexer = get_lexer_by_name('text')
return pygments.lex(self.code, lexer)
def join(self, tokens):
- """join subsequent tokens of same token-type
+ """Join subsequent tokens of same token-type.
+
+ Also, leave out the final '\n' (added by pygments).
"""
tokens = iter(tokens)
(lasttype, lastval) = tokens.next()
@@ -100,54 +102,100 @@ class DocutilsInterface(object):
else:
yield(lasttype, lastval)
(lasttype, lastval) = (ttype, value)
- yield(lasttype, lastval)
+ if lastval != '\n':
+ yield(lasttype, lastval)
def __iter__(self):
- """parse code string and yield "clasified" tokens
+ """parse code string and yield "classified" tokens
"""
- try:
- tokens = self.lex()
- except IOError:
- print "INFO: Pygments lexer not found, using fallback"
- # TODO: write message to INFO
- yield ('', self.code)
- return
-
+ tokens = self.lex()
for ttype, value in self.join(tokens):
+ # yield (ttype, value)
yield (_get_ttype_class(ttype), value)
+class NumberLines(object):
+ """Insert linenumber-tokens in front of every newline
+
+ Nontrivial, as we need to weave these into the possibly
+ multi-line tokens from pygments.
+ """
+
+ def __init__(self, tokens, startline, fmt_str):
+ self.tokens = tokens
+ self.lineno = startline
+ self.fmt_str = fmt_str
-# code_block_directive
+ def __iter__(self):
+ yield ('ln', self.fmt_str % self.lineno)
+ for ttype, value in self.tokens:
+ lines = value.split('\n')
+ for line in lines[:-1]:
+ yield (ttype, line + '\n')
+ self.lineno += 1
+ yield ('ln', self.fmt_str % self.lineno)
+ yield (ttype, lines[-1])
+
+
+# CodeBlock directive
# --------------------
# ::
-def code_block_directive(name, arguments, options, content, lineno,
- content_offset, block_text, state, state_machine):
- """parse and classify content of a code_block
+class CodeBlock(Directive):
+ """Parse and mark up content of a code block.
"""
- language = arguments[0]
- # create a literal block element and set class argument
- code_block = nodes.literal_block(classes=["code-block", language])
-
- # parse content with pygments and add to code_block element
- for cls, value in DocutilsInterface(u'\n'.join(content), language):
- if cls in unstyled_tokens:
- # insert as Text to decrease the verbosity of the output.
- code_block += nodes.Text(value, value)
+ required_arguments = 1
+ option_spec = {'class': directives.class_option,
+ 'number-lines': directives.unchanged
+ }
+ has_content = True
+
+ def run(self):
+ language = self.arguments[0]
+ # Process number-lines with optional argument `startline`
+ startline = self.options.get('number-lines', '1')
+ try:
+ startline = int(startline or 1) # default to 1 for empty str
+ except ValueError:
+ raise self.error(
+ ':number-lines: option with non-integer start value')
+ self.assert_has_content()
+
+ # create a literal block element and set class argument
+ code_block = nodes.literal_block(classes=['code', language]
+ + self.options['class'])
+
+ # iterator returning code tokens
+ if with_pygments:
+ tokens = Tokenizer(u'\n'.join(self.content), language)
else:
- code_block += nodes.inline(value, value, classes=[cls])
+ # TODO: warning or info?
+ self.warning('Cannot highlight code, Pygments lexer not found.')
+ tokens = [('', u'\n'.join(self.content))]
+
+ if 'number-lines' in self.options:
+ # pad linenumbers, e.g. endline == 100 -> fmt_str = '%3d '
+ endline = startline + len(self.content)
+ fmt_str = "%%%dd " % len(str(endline))
+ # print startline, '...', endline, repr(fmt_str)
+ tokens = NumberLines(tokens, startline, fmt_str)
+
+ # parse content with pygments and add to code_block element
+ for cls, value in tokens:
+ if cls in unstyled_tokens:
+ # insert as Text to decrease the verbosity of the output.
+ code_block += nodes.Text(value, value)
+ else:
+ code_block += nodes.inline(value, value, classes=[cls])
- return [code_block]
+ return [code_block]
# Register Directive
# ------------------
# ::
-code_block_directive.arguments = (1, 0, 1)
-code_block_directive.content = 1
-directives.register_directive('code-block', code_block_directive)
+directives.register_directive('code', CodeBlock)
# .. _doctutils: http://docutils.sf.net/
# .. _pygments: http://pygments.org/
@@ -163,15 +211,14 @@ directives.register_directive('code-block', code_block_directive)
if __name__ == '__main__':
from docutils.core import publish_cmdline, default_description
- description = "code-block directive test output" + default_description
+ description = 'code-block directive test output' + default_description
try:
import locale
locale.setlocale(locale.LC_ALL, '')
except:
pass
# Uncomment the desired output format:
- publish_cmdline(writer_name='pseudoxml', description=description)
+ # publish_cmdline(writer_name='pseudoxml', description=description)
# publish_cmdline(writer_name='xml', description=description)
- # publish_cmdline(writer_name='html', description=description)
+ publish_cmdline(writer_name='html', description=description)
# publish_cmdline(writer_name='latex', description=description)
- # publish_cmdline(writer_name='newlatex2e', description=description)