summaryrefslogtreecommitdiff
path: root/sphinx/directives/code.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/directives/code.py')
-rw-r--r--sphinx/directives/code.py121
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]