chiark / gitweb /
plugins: new m.code plugin.
authorVladimír Vondruš <mosra@centrum.cz>
Tue, 12 Sep 2017 19:53:46 +0000 (21:53 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Thu, 14 Sep 2017 22:11:11 +0000 (00:11 +0200)
Unifies and simplifies the highlighting without additional useless
wrapper <div>s and meaningless classes.

pelican-plugins/m/code.py [new file with mode: 0644]
pelican-plugins/m/htmlsanity.py

diff --git a/pelican-plugins/m/code.py b/pelican-plugins/m/code.py
new file mode 100644 (file)
index 0000000..176c7a0
--- /dev/null
@@ -0,0 +1,69 @@
+import docutils
+from docutils.parsers import rst
+from docutils.parsers.rst.roles import set_classes
+from docutils.parsers.rst import Directive, directives
+from docutils import nodes, utils
+
+from pygments import highlight
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import TextLexer, get_lexer_by_name
+
+class Code(Directive):
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+    option_spec = {
+        'hl_lines': directives.unchanged,
+        'class': directives.class_option
+    }
+    has_content = True
+
+    def run(self):
+        self.assert_has_content()
+        set_classes(self.options)
+
+        classes = ['m-code']
+        if 'classes' in self.options:
+            classes += self.options['classes']
+            del self.options['classes']
+
+        try:
+            lexer = get_lexer_by_name(self.arguments[0])
+        except ValueError:
+            lexer = TextLexer()
+        formatter = HtmlFormatter(nowrap=True, **self.options)
+        parsed = highlight('\n'.join(self.content), lexer, formatter)
+
+        content = nodes.raw('', parsed, format='html')
+        pre = nodes.literal_block('', classes=classes)
+        pre.append(content)
+        return [pre]
+
+def code(role, rawtext, text, lineno, inliner, options={}, content=[]):
+    set_classes(options)
+    classes = ['m-code']
+    if 'classes' in options:
+        classes += options['classes']
+        del options['classes']
+
+    # Not sure why language is duplicated in classes?
+    language = options.get('language', '')
+    if language in classes: classes.remove(language)
+    try:
+        lexer = get_lexer_by_name(language)
+    except ValueError:
+        lexer = TextLexer()
+    formatter = HtmlFormatter(nowrap=True)
+    parsed = highlight(utils.unescape(text), lexer, formatter).strip()
+
+    content = nodes.raw('', parsed, format='html')
+    node = nodes.literal(rawtext, '', classes=classes, **options)
+    node.append(content)
+    return [node], []
+
+code.options = {'class': directives.class_option,
+                'language': directives.unchanged}
+
+def register():
+    rst.directives.register_directive('code', Code)
+    rst.roles.register_canonical_role('code', code)
index e57da75522c9d60a7b486caaa0a8810d4399b6be..8b68f3f3994886e8c4e29e3958c7e00e0df6326e 100644 (file)
@@ -242,37 +242,20 @@ class SaneHtmlTranslator(HTMLTranslator):
         self.section_level -= 1
         self.body.append('</section>\n')
 
-    # Literal -- print as <code> (instead of some <div>)
+    # Literal -- print as <code> (instead of some <span>)
     def visit_literal(self, node):
-        # special case: "code" role
-        classes = node.get('classes', [])
-        if 'code' in classes:
-            # filter 'code' from class arguments
-            node['classes'] = [cls for cls in classes if cls != 'code']
-            self.body.append(self.starttag(node, 'code', '', CLASS='highlight'))
-            return
-        self.body.append(
-            self.starttag(node, 'code', '', CLASS='highlight'))
-        text = node.astext()
-        # remove hard line breaks (except if in a parsed-literal block)
-        if not isinstance(node.parent, nodes.literal_block):
-            text = text.replace('\n', ' ')
-        # Protect text like ``--an-option`` and the regular expression
-        # ``[+]?(\d+(\.\d*)?|\.\d+)`` from bad line wrapping
-        for token in self.words_and_spaces.findall(text):
-            if token.strip() and self.in_word_wrap_point.search(token):
-                self.body.append('<span class="pre">%s</span>'
-                                    % self.encode(token))
-            else:
-                self.body.append(self.encode(token))
-        self.body.append('</code>')
-        # Content already processed:
-        raise nodes.SkipNode
+        self.body.append(self.starttag(node, 'code', ''))
 
     def depart_literal(self, node):
-        # skipped unless literal element is from "code" role:
         self.body.append('</code>')
 
+    # Literal block -- use <pre> without nested <code> and useless classes
+    def visit_literal_block(self, node):
+        self.body.append(self.starttag(node, 'pre', ''))
+
+    def depart_literal_block(self, node):
+        self.body.append('</pre>\n')
+
     # Anchor -- drop the useless classes
     def visit_reference(self, node):
         atts = {}