aboutsummaryrefslogtreecommitdiffstats
path: root/nikola/plugins/compile/rest/listing.py
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/plugins/compile/rest/listing.py')
-rw-r--r--nikola/plugins/compile/rest/listing.py112
1 files changed, 88 insertions, 24 deletions
diff --git a/nikola/plugins/compile/rest/listing.py b/nikola/plugins/compile/rest/listing.py
index 23ec254..b8340cf 100644
--- a/nikola/plugins/compile/rest/listing.py
+++ b/nikola/plugins/compile/rest/listing.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2014 Roberto Alsina and others.
+# Copyright © 2012-2015 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -31,43 +31,95 @@
from __future__ import unicode_literals
import io
import os
+import uuid
try:
from urlparse import urlunsplit
except ImportError:
from urllib.parse import urlunsplit # NOQA
+import docutils.parsers.rst.directives.body
+import docutils.parsers.rst.directives.misc
from docutils import core
from docutils import nodes
from docutils.parsers.rst import Directive, directives
+from docutils.parsers.rst.roles import set_classes
from docutils.parsers.rst.directives.misc import Include
-try:
- from docutils.parsers.rst.directives.body import CodeBlock
-except ImportError: # docutils < 0.9 (Debian Sid For The Loss)
- class CodeBlock(Directive):
- required_arguments = 1
- has_content = True
- option_spec = {}
- CODE = '<pre>{0}</pre>'
-
- def run(self):
- """ Required by the Directive interface. Create docutils nodes """
- return [nodes.raw('', self.CODE.format('\n'.join(self.content)), format='html')]
- directives.register_directive('code', CodeBlock)
+from pygments.lexers import get_lexer_by_name
+import pygments
+import pygments.util
+from nikola import utils
from nikola.plugin_categories import RestExtension
-# Add sphinx compatibility option
-CodeBlock.option_spec['linenos'] = directives.unchanged
-
-class FlexibleCodeBlock(CodeBlock):
+# A sanitized version of docutils.parsers.rst.directives.body.CodeBlock.
+class CodeBlock(Directive):
+ """Parse and mark up content of a code block."""
+ optional_arguments = 1
+ option_spec = {'class': directives.class_option,
+ 'name': directives.unchanged,
+ 'number-lines': directives.unchanged, # integer or None
+ 'linenos': directives.unchanged,
+ 'tab-width': directives.nonnegative_int}
+ has_content = True
def run(self):
+ self.assert_has_content()
+
if 'linenos' in self.options:
self.options['number-lines'] = self.options['linenos']
- return super(FlexibleCodeBlock, self).run()
-CodeBlock = FlexibleCodeBlock
+ if 'tab-width' in self.options:
+ self.content = [x.replace('\t', ' ' * self.options['tab-width']) for x in self.content]
+
+ if self.arguments:
+ language = self.arguments[0]
+ else:
+ language = 'text'
+ set_classes(self.options)
+ classes = ['code']
+ if language:
+ classes.append(language)
+ if 'classes' in self.options:
+ classes.extend(self.options['classes'])
+
+ code = '\n'.join(self.content)
+
+ try:
+ lexer = get_lexer_by_name(language)
+ except pygments.util.ClassNotFound:
+ raise self.error('Cannot find pygments lexer for language "{0}"'.format(language))
+
+ if 'number-lines' in self.options:
+ linenos = 'table'
+ # optional argument `startline`, defaults to 1
+ try:
+ linenostart = int(self.options['number-lines'] or 1)
+ except ValueError:
+ raise self.error(':number-lines: with non-integer start value')
+ else:
+ linenos = False
+ linenostart = 1 # actually unused
+
+ if self.site.invariant: # for testing purposes
+ anchor_ref = 'rest_code_' + 'fixedvaluethatisnotauuid'
+ else:
+ anchor_ref = 'rest_code_' + uuid.uuid4().hex
+
+ formatter = utils.NikolaPygmentsHTML(anchor_ref=anchor_ref, classes=classes, linenos=linenos, linenostart=linenostart)
+ out = pygments.highlight(code, lexer, formatter)
+ node = nodes.raw('', out, format='html')
+
+ self.add_name(node)
+ # if called from "include", set the source
+ if 'source' in self.options:
+ node.attributes['source'] = self.options['source']
+
+ return [node]
+
+# Monkey-patch: replace insane docutils CodeBlock with our implementation.
+docutils.parsers.rst.directives.body.CodeBlock = CodeBlock
+docutils.parsers.rst.directives.misc.CodeBlock = CodeBlock
class Plugin(RestExtension):
@@ -79,11 +131,15 @@ class Plugin(RestExtension):
# Even though listings don't use CodeBlock anymore, I am
# leaving these to make the code directive work with
# docutils < 0.9
+ CodeBlock.site = site
+ directives.register_directive('code', CodeBlock)
directives.register_directive('code-block', CodeBlock)
directives.register_directive('sourcecode', CodeBlock)
directives.register_directive('listing', Listing)
+ Listing.folders = site.config['LISTINGS_FOLDERS']
return super(Plugin, self).set_site(site)
+
# Add sphinx compatibility option
listing_spec = Include.option_spec
listing_spec['linenos'] = directives.unchanged
@@ -104,9 +160,17 @@ class Listing(Include):
option_spec = listing_spec
def run(self):
- fname = self.arguments.pop(0)
+ _fname = self.arguments.pop(0)
+ fname = _fname.replace('/', os.sep)
lang = self.arguments.pop(0)
- fpath = os.path.join('listings', fname)
+ if len(self.folders) == 1:
+ listings_folder = next(iter(self.folders.keys()))
+ if fname.startswith(listings_folder):
+ fpath = os.path.join(fname) # new syntax: specify folder name
+ else:
+ fpath = os.path.join(listings_folder, fname) # old syntax: don't specify folder name
+ else:
+ fpath = os.path.join(fname) # must be new syntax: specify folder name
self.arguments.insert(0, fpath)
self.options['code'] = lang
if 'linenos' in self.options:
@@ -114,9 +178,9 @@ class Listing(Include):
with io.open(fpath, 'r+', encoding='utf8') as fileobject:
self.content = fileobject.read().splitlines()
self.state.document.settings.record_dependencies.add(fpath)
- target = urlunsplit(("link", 'listing', fname, '', ''))
+ target = urlunsplit(("link", 'listing', fpath.replace('\\', '/'), '', ''))
generated_nodes = (
- [core.publish_doctree('`{0} <{1}>`_'.format(fname, target))[0]])
+ [core.publish_doctree('`{0} <{1}>`_'.format(_fname, target))[0]])
generated_nodes += self.get_code_from_file(fileobject)
return generated_nodes