1080
1078
"""Block quote."""
1081
1079
indented, indent, line_offset, blank_finish = \
1082
1080
self.state_machine.get_indented()
1083
blockquote, messages = self.block_quote(indented, line_offset)
1084
self.parent += blockquote
1085
self.parent += messages
1081
elements = self.block_quote(indented, line_offset)
1082
self.parent += elements
1086
1083
if not blank_finish:
1087
1084
self.parent += self.unindent_warning('Block quote')
1088
1085
return context, next_state, []
1090
1087
def block_quote(self, indented, line_offset):
1091
blockquote_lines, attribution_lines, attribution_offset = \
1092
self.check_attribution(indented, line_offset)
1093
blockquote = nodes.block_quote()
1094
self.nested_parse(blockquote_lines, line_offset, blockquote)
1096
if attribution_lines:
1097
attribution, messages = self.parse_attribution(attribution_lines,
1099
blockquote += attribution
1100
return blockquote, messages
1094
new_line_offset) = self.split_attribution(indented, line_offset)
1095
blockquote = nodes.block_quote()
1096
self.nested_parse(blockquote_lines, line_offset, blockquote)
1097
elements.append(blockquote)
1098
if attribution_lines:
1099
attribution, messages = self.parse_attribution(
1100
attribution_lines, attribution_offset)
1101
blockquote += attribution
1102
elements += messages
1103
line_offset = new_line_offset
1104
while indented and not indented[0]:
1105
indented = indented[1:]
1102
# u'\u2014' is an em-dash:
1109
# U+2014 is an em-dash:
1103
1110
attribution_pattern = re.compile(ur'(---?(?!-)|\u2014) *(?=[^ \n])')
1105
def check_attribution(self, indented, line_offset):
1112
def split_attribution(self, indented, line_offset):
1107
Check for an attribution in the last contiguous block of `indented`.
1114
Check for a block quote attribution and split it off:
1109
* First line after last blank line must begin with "--" (etc.).
1116
* First line after a blank line must begin with a dash ("--", "---",
1117
em-dash; matches `self.attribution_pattern`).
1110
1118
* Every line after that must have consistent indentation.
1119
* Attributions must be preceded by block quote content.
1112
Return a 3-tuple: (block quote lines, attribution lines,
1113
attribution offset).
1121
Return a tuple of: (block quote content lines, content offset,
1122
attribution lines, attribution offset, remaining indented lines).
1115
#import pdb ; pdb.set_trace()
1117
nonblank_seen = None
1119
for i in range(len(indented) - 1, 0, -1): # don't check first line
1120
this_line_blank = not indented[i].strip()
1121
if nonblank_seen and this_line_blank:
1122
match = self.attribution_pattern.match(indented[i + 1])
1125
nonblank_seen = False
1126
for i in range(len(indented)):
1127
line = indented[i].rstrip()
1129
if nonblank_seen and blank == i - 1: # last line blank
1130
match = self.attribution_pattern.match(line)
1132
attribution_end, indent = self.check_attribution(
1135
a_lines = indented[i:attribution_end]
1136
a_lines.trim_left(match.end(), end=1)
1137
a_lines.trim_left(indent, start=1)
1138
return (indented[:i], a_lines,
1139
i, indented[attribution_end:],
1140
line_offset + attribution_end)
1141
nonblank_seen = True
1145
return (indented, None, None, None, None)
1147
def check_attribution(self, indented, attribution_start):
1149
Check attribution shape.
1150
Return the index past the end of the attribution, and the indent.
1153
i = attribution_start + 1
1154
for i in range(attribution_start + 1, len(indented)):
1155
line = indented[i].rstrip()
1126
elif not this_line_blank:
1128
if blank and len(indented) - blank > 2: # multi-line attribution
1129
indent = (len(indented[blank + 2])
1130
- len(indented[blank + 2].lstrip()))
1131
for j in range(blank + 3, len(indented)):
1132
if ( indented[j] # may be blank last line
1133
and indent != (len(indented[j])
1134
- len(indented[j].lstrip()))):
1139
a_lines = indented[blank + 1:]
1140
a_lines.trim_left(match.end(), end=1)
1141
a_lines.trim_left(indent, start=1)
1142
return (indented[:blank], a_lines, line_offset + blank + 1)
1159
indent = len(line) - len(line.lstrip())
1160
elif len(line) - len(line.lstrip()) != indent:
1161
return None, None # bad shape; not an attribution
1144
return (indented, None, None)
1163
# return index of line after last attribution line:
1165
return i, (indent or 0)
1146
1167
def parse_attribution(self, indented, line_offset):
1147
1168
text = '\n'.join(indented).rstrip()
1965
1986
def directive(self, match, **option_presets):
1966
1987
"""Returns a 2-tuple: list of nodes, and a "blank finish" boolean."""
1967
1988
type_name = match.group(1)
1968
directive_function, messages = directives.directive(
1989
directive_class, messages = directives.directive(
1969
1990
type_name, self.memo.language, self.document)
1970
1991
self.parent += messages
1971
if directive_function:
1972
1993
return self.run_directive(
1973
directive_function, match, type_name, option_presets)
1994
directive_class, match, type_name, option_presets)
1975
1996
return self.unknown_directive(type_name)
1977
def run_directive(self, directive_fn, match, type_name, option_presets):
1998
def run_directive(self, directive, match, type_name, option_presets):
1979
2000
Parse a directive then run its directive function.
1983
- `directive_fn`: The function implementing the directive. Uses
1984
function attributes ``arguments``, ``options``, and/or ``content``
2004
- `directive`: The class implementing the directive. Must be
2005
a subclass of `rst.Directive`.
1987
2007
- `match`: A regular expression match object which matched the first
1988
2008
line of the directive.
2007
2030
arguments, options, content, content_offset = (
2008
2031
self.parse_directive_block(indented, line_offset,
2009
directive_fn, option_presets))
2032
directive, option_presets))
2010
2033
except MarkupError, detail:
2011
2034
error = self.reporter.error(
2012
2035
'Error in "%s" directive:\n%s.' % (type_name,
2013
2036
' '.join(detail.args)),
2014
2037
nodes.literal_block(block_text, block_text), line=lineno)
2015
2038
return [error], blank_finish
2016
result = directive_fn(type_name, arguments, options, content, lineno,
2017
content_offset, block_text, self,
2039
directive_instance = directive(
2040
type_name, arguments, options, content, lineno,
2041
content_offset, block_text, self, self.state_machine)
2043
result = directive_instance.run()
2044
except docutils.parsers.rst.DirectiveError, directive_error:
2045
msg_node = self.reporter.system_message(directive_error.level,
2046
directive_error.message)
2047
msg_node += nodes.literal_block(block_text, block_text)
2048
msg_node['line'] = lineno
2050
assert isinstance(result, list), \
2051
'Directive "%s" must return a list of nodes.' % type_name
2052
for i in range(len(result)):
2053
assert isinstance(result[i], nodes.Node), \
2054
('Directive "%s" returned non-Node object (index %s): %r'
2055
% (type_name, i, result[i]))
2019
2056
return (result,
2020
2057
blank_finish or self.state_machine.is_next_line_blank())
2022
def parse_directive_block(self, indented, line_offset, directive_fn,
2059
def parse_directive_block(self, indented, line_offset, directive,
2023
2060
option_presets):
2026
argument_spec = getattr(directive_fn, 'arguments', None)
2027
if argument_spec and argument_spec[:2] == (0, 0):
2028
argument_spec = None
2029
option_spec = getattr(directive_fn, 'options', None)
2030
content_spec = getattr(directive_fn, 'content', None)
2061
option_spec = directive.option_spec
2062
has_content = directive.has_content
2031
2063
if indented and not indented[0].strip():
2032
2064
indented.trim_start()
2033
2065
line_offset += 1
2034
2066
while indented and not indented[-1].strip():
2035
2067
indented.trim_end()
2036
if indented and (argument_spec or option_spec):
2068
if indented and (directive.required_arguments
2069
or directive.optional_arguments
2037
2071
for i in range(len(indented)):
2038
2072
if not indented[i].strip():
2080
2119
raise MarkupError(data)
2081
2120
return options, arg_block
2083
def parse_directive_arguments(self, argument_spec, arg_block):
2084
required, optional, last_whitespace = argument_spec
2122
def parse_directive_arguments(self, directive, arg_block):
2123
required = directive.required_arguments
2124
optional = directive.optional_arguments
2085
2125
arg_text = '\n'.join(arg_block)
2086
2126
arguments = arg_text.split()
2087
2127
if len(arguments) < required:
2088
2128
raise MarkupError('%s argument(s) required, %s supplied'
2089
2129
% (required, len(arguments)))
2090
2130
elif len(arguments) > required + optional:
2131
if directive.final_argument_whitespace:
2092
2132
arguments = arg_text.split(None, required + optional - 1)
2094
2134
raise MarkupError(