diff options
Diffstat (limited to 'sphinx/directives/code.py')
-rw-r--r-- | sphinx/directives/code.py | 121 |
1 files changed, 96 insertions, 25 deletions
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 9bfac5a6..6ea525b0 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -9,10 +9,13 @@ import sys import codecs +from difflib import unified_diff from docutils import nodes from docutils.parsers.rst import Directive, directives +from six import string_types + from sphinx import addnodes from sphinx.util import parselinenos from sphinx.util.nodes import set_source_info @@ -39,11 +42,26 @@ class Highlight(Directive): except Exception: linenothreshold = 10 else: - linenothreshold = sys.maxint + linenothreshold = sys.maxsize return [addnodes.highlightlang(lang=self.arguments[0].strip(), linenothreshold=linenothreshold)] + +def dedent_lines(lines, dedent): + if not dedent: + return lines + + new_lines = [] + for line in lines: + new_line = line[dedent:] + if line.endswith('\n') and not new_line: + new_line = '\n' # keep CRLF + new_lines.append(new_line) + + return new_lines + + class CodeBlock(Directive): """ Directive for a code block with special highlighting or line numbering @@ -56,7 +74,10 @@ class CodeBlock(Directive): final_argument_whitespace = False option_spec = { 'linenos': directives.flag, + 'dedent': int, + 'lineno-start': int, 'emphasize-lines': directives.unchanged_required, + 'caption': directives.unchanged_required, } def run(self): @@ -67,17 +88,29 @@ class CodeBlock(Directive): try: nlines = len(self.content) hl_lines = [x+1 for x in parselinenos(linespec, nlines)] - except ValueError, err: + except ValueError as err: document = self.state.document return [document.reporter.warning(str(err), line=self.lineno)] else: hl_lines = None + + if 'dedent' in self.options: + lines = code.split('\n') + lines = dedent_lines(lines, self.options['dedent']) + code = '\n'.join(lines) literal = nodes.literal_block(code, code) literal['language'] = self.arguments[0] - literal['linenos'] = 'linenos' in self.options + caption = self.options.get('caption') + if caption: + literal['caption'] = caption + literal['linenos'] = 'linenos' in self.options or \ + 'lineno-start' in self.options + extra_args = literal['highlight_args'] = {} if hl_lines is not None: - literal['highlight_args'] = {'hl_lines': hl_lines} + extra_args['hl_lines'] = hl_lines + if 'lineno-start' in self.options: + extra_args['linenostart'] = self.options['lineno-start'] set_source_info(self, literal) return [literal] @@ -94,7 +127,9 @@ class LiteralInclude(Directive): optional_arguments = 0 final_argument_whitespace = True option_spec = { + 'dedent': int, 'linenos': directives.flag, + 'lineno-start': int, 'tab-width': int, 'language': directives.unchanged_required, 'encoding': directives.encoding, @@ -105,28 +140,18 @@ class LiteralInclude(Directive): 'prepend': directives.unchanged_required, 'append': directives.unchanged_required, 'emphasize-lines': directives.unchanged_required, + 'caption': directives.unchanged, + 'diff': directives.unchanged_required, } - def run(self): - document = self.state.document - if not document.settings.file_insertion_enabled: - return [document.reporter.warning('File insertion disabled', - line=self.lineno)] - env = document.settings.env - rel_filename, filename = env.relfn2path(self.arguments[0]) - - if 'pyobject' in self.options and 'lines' in self.options: - return [document.reporter.warning( - 'Cannot use both "pyobject" and "lines" options', - line=self.lineno)] - - encoding = self.options.get('encoding', env.config.source_encoding) - codec_info = codecs.lookup(encoding) + def read_with_encoding(self, filename, document, codec_info, encoding): f = None try: f = codecs.StreamReaderWriter(open(filename, 'rb'), - codec_info[2], codec_info[3], 'strict') + codec_info[2], codec_info[3], 'strict') lines = f.readlines() + lines = dedent_lines(lines, self.options.get('dedent')) + return lines except (IOError, OSError): return [document.reporter.warning( 'Include file %r not found or reading it failed' % filename, @@ -140,6 +165,42 @@ class LiteralInclude(Directive): if f is not None: f.close() + def run(self): + document = self.state.document + if not document.settings.file_insertion_enabled: + return [document.reporter.warning('File insertion disabled', + line=self.lineno)] + env = document.settings.env + rel_filename, filename = env.relfn2path(self.arguments[0]) + + if 'pyobject' in self.options and 'lines' in self.options: + return [document.reporter.warning( + 'Cannot use both "pyobject" and "lines" options', + line=self.lineno)] + + encoding = self.options.get('encoding', env.config.source_encoding) + codec_info = codecs.lookup(encoding) + + lines = self.read_with_encoding(filename, document, + codec_info, encoding) + if not isinstance(lines[0], string_types): + return lines + + diffsource = self.options.get('diff') + if diffsource is not None: + tmp, fulldiffsource = env.relfn2path(diffsource) + + difflines = self.read_with_encoding(fulldiffsource, document, + codec_info, encoding) + if not isinstance(difflines[0], string_types): + return difflines + diff = unified_diff( + difflines, + lines, + diffsource, + self.arguments[0]) + lines = list(diff) + objectname = self.options.get('pyobject') if objectname is not None: from sphinx.pycode import ModuleAnalyzer @@ -156,7 +217,7 @@ class LiteralInclude(Directive): if linespec is not None: try: linelist = parselinenos(linespec, len(lines)) - except ValueError, err: + except ValueError as err: return [document.reporter.warning(str(err), line=self.lineno)] # just ignore nonexisting lines nlines = len(lines) @@ -170,7 +231,7 @@ class LiteralInclude(Directive): if linespec: try: hl_lines = [x+1 for x in parselinenos(linespec, len(lines))] - except ValueError, err: + except ValueError as err: return [document.reporter.warning(str(err), line=self.lineno)] else: hl_lines = None @@ -202,12 +263,22 @@ class LiteralInclude(Directive): text = text.expandtabs(self.options['tab-width']) retnode = nodes.literal_block(text, text, source=filename) set_source_info(self, retnode) + if diffsource is not None: # if diff is set, set udiff + retnode['language'] = 'udiff' if self.options.get('language', ''): retnode['language'] = self.options['language'] - if 'linenos' in self.options: - retnode['linenos'] = True + retnode['linenos'] = 'linenos' in self.options or \ + 'lineno-start' in self.options + caption = self.options.get('caption') + if caption is not None: + if not caption: + caption = self.arguments[0] + retnode['caption'] = caption + extra_args = retnode['highlight_args'] = {} if hl_lines is not None: - retnode['highlight_args'] = {'hl_lines': hl_lines} + extra_args['hl_lines'] = hl_lines + if 'lineno-start' in self.options: + extra_args['linenostart'] = self.options['lineno-start'] env.note_dependency(rel_filename) return [retnode] |