From 49de7160bd50bcd0cbaf7fcb5de5b4b06cb43f7e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 26 Nov 2017 02:52:11 +0100 Subject: [PATCH] m.code: patch the builtin include directive for our code support. Uglyyyy. --- doc/plugins/math-and-code-snippet.cpp | 6 ++ doc/plugins/math-and-code.rst | 17 +++++ pelican-plugins/m/code.py | 89 ++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 doc/plugins/math-and-code-snippet.cpp diff --git a/doc/plugins/math-and-code-snippet.cpp b/doc/plugins/math-and-code-snippet.cpp new file mode 100644 index 00000000..c469fc7f --- /dev/null +++ b/doc/plugins/math-and-code-snippet.cpp @@ -0,0 +1,6 @@ +#include + +int main() { + std::cout << "Hello world!" << std::endl; + return 0; +} diff --git a/doc/plugins/math-and-code.rst b/doc/plugins/math-and-code.rst index 38333e3d..7f7eca07 100644 --- a/doc/plugins/math-and-code.rst +++ b/doc/plugins/math-and-code.rst @@ -191,6 +191,23 @@ option to highlight lines; if you want to add additional CSS classes, use the return 0; } +The builtin `include directive `_ +is also patched to use the improved code directive. Simply specify external +code snippets filename and set the language using the :rst:`:code:` option. +All options of the :rst:`.. code::` directive are supported as well. + +.. code-figure:: + + .. code:: rst + + .. include:: snippet.cpp + :code: c++ + :start-line: 2 + + .. include:: math-and-code-snippet.cpp + :code: c++ + :start-line: 2 + For inline code highlighting, use :rst:`:code:` interpreted text role. To specify which language should be highlighted, derive a custom role from it: diff --git a/pelican-plugins/m/code.py b/pelican-plugins/m/code.py index 8f643dc6..ed8f3576 100644 --- a/pelican-plugins/m/code.py +++ b/pelican-plugins/m/code.py @@ -22,11 +22,15 @@ # DEALINGS IN THE SOFTWARE. # +import os.path + import docutils from docutils.parsers import rst from docutils.parsers.rst.roles import set_classes +from docutils.utils.error_reporting import SafeString, ErrorString, locale_encoding from docutils.parsers.rst import Directive, directives -from docutils import nodes, utils +import docutils.parsers.rst.directives.misc +from docutils import io, nodes, utils, statemachine from pygments import highlight from pygments.formatters import HtmlFormatter @@ -63,6 +67,88 @@ class Code(Directive): pre.append(content) return [pre] +class Include(docutils.parsers.rst.directives.misc.Include): + def run(self): + """ + Verbatim copy of docutils.parsers.rst.directives.misc.Include.run() + that just calls to our Code instead of builtin CodeBlock but otherwise + just passes it back to the parent implementation. + """ + if not 'code' in self.options: + return docutils.parsers.rst.directives.misc.Include.run(self) + + source = self.state_machine.input_lines.source( + self.lineno - self.state_machine.input_offset - 1) + source_dir = os.path.dirname(os.path.abspath(source)) + path = directives.path(self.arguments[0]) + if path.startswith('<') and path.endswith('>'): + path = os.path.join(self.standard_include_path, path[1:-1]) + path = os.path.normpath(os.path.join(source_dir, path)) + path = utils.relative_path(None, path) + path = nodes.reprunicode(path) + encoding = self.options.get( + 'encoding', self.state.document.settings.input_encoding) + e_handler=self.state.document.settings.input_encoding_error_handler + tab_width = self.options.get( + 'tab-width', self.state.document.settings.tab_width) + try: + self.state.document.settings.record_dependencies.add(path) + include_file = io.FileInput(source_path=path, + encoding=encoding, + error_handler=e_handler) + except UnicodeEncodeError as error: + raise self.severe('Problems with "%s" directive path:\n' + 'Cannot encode input file path "%s" ' + '(wrong locale?).' % + (self.name, SafeString(path))) + except IOError as error: + raise self.severe('Problems with "%s" directive path:\n%s.' % + (self.name, ErrorString(error))) + startline = self.options.get('start-line', None) + endline = self.options.get('end-line', None) + try: + if startline or (endline is not None): + lines = include_file.readlines() + rawtext = ''.join(lines[startline:endline]) + else: + rawtext = include_file.read() + except UnicodeError as error: + raise self.severe('Problem with "%s" directive:\n%s' % + (self.name, ErrorString(error))) + # start-after/end-before: no restrictions on newlines in match-text, + # and no restrictions on matching inside lines vs. line boundaries + after_text = self.options.get('start-after', None) + if after_text: + # skip content in rawtext before *and incl.* a matching text + after_index = rawtext.find(after_text) + if after_index < 0: + raise self.severe('Problem with "start-after" option of "%s" ' + 'directive:\nText not found.' % self.name) + rawtext = rawtext[after_index + len(after_text):] + before_text = self.options.get('end-before', None) + if before_text: + # skip content in rawtext after *and incl.* a matching text + before_index = rawtext.find(before_text) + if before_index < 0: + raise self.severe('Problem with "end-before" option of "%s" ' + 'directive:\nText not found.' % self.name) + rawtext = rawtext[:before_index] + + include_lines = statemachine.string2lines(rawtext, tab_width, + convert_whitespace=True) + + self.options['source'] = path + codeblock = Code(self.name, + [self.options.pop('code')], # arguments + self.options, + include_lines, # content + self.lineno, + self.content_offset, + self.block_text, + self.state, + self.state_machine) + return codeblock.run() + def code(role, rawtext, text, lineno, inliner, options={}, content=[]): set_classes(options) classes = ['m-code'] @@ -90,4 +176,5 @@ code.options = {'class': directives.class_option, def register(): rst.directives.register_directive('code', Code) + rst.directives.register_directive('include', Include) rst.roles.register_canonical_role('code', code) -- 2.30.2