From 8b11a6d3d11429ff47b258565b1065add6c4b413 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 12 Sep 2017 21:53:46 +0200 Subject: [PATCH] plugins: new m.code plugin. Unifies and simplifies the highlighting without additional useless wrapper
s and meaningless classes. --- pelican-plugins/m/code.py | 69 +++++++++++++++++++++++++++++++++ pelican-plugins/m/htmlsanity.py | 35 +++++------------ 2 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 pelican-plugins/m/code.py diff --git a/pelican-plugins/m/code.py b/pelican-plugins/m/code.py new file mode 100644 index 00000000..176c7a04 --- /dev/null +++ b/pelican-plugins/m/code.py @@ -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) diff --git a/pelican-plugins/m/htmlsanity.py b/pelican-plugins/m/htmlsanity.py index e57da755..8b68f3f3 100644 --- a/pelican-plugins/m/htmlsanity.py +++ b/pelican-plugins/m/htmlsanity.py @@ -242,37 +242,20 @@ class SaneHtmlTranslator(HTMLTranslator): self.section_level -= 1 self.body.append('\n') - # Literal -- print as (instead of some
) + # Literal -- print as (instead of some ) 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('%s' - % self.encode(token)) - else: - self.body.append(self.encode(token)) - self.body.append('') - # 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('') + # Literal block -- use
 without nested  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('
\n') + # Anchor -- drop the useless classes def visit_reference(self, node): atts = {} -- 2.30.2