@@ -313,18 +313,32 @@ class QAPIDoc(object):
"""
class Section(object):
- def __init__(self, name=None):
+ def __init__(self, parser, name=None, indent=0):
+ # parser, for error messages about indentation
+ self._parser = parser
# optional section name (argument/member or section name)
self.name = name
# the list of lines for this section
self.text = ''
+ # the expected indent level of the text of this section
+ self._indent = indent
def append(self, line):
+ # Strip leading spaces corresponding to the expected indent level
+ # Blank lines are always OK.
+ if line:
+ spacecount = len(line) - len(line.lstrip(" "))
+ if spacecount > self._indent:
+ spacecount = self._indent
+ if spacecount < self._indent:
+ raise QAPIParseError(self._parser, "unexpected de-indent")
+ line = line[spacecount:]
+
self.text += line.rstrip() + '\n'
class ArgSection(Section):
- def __init__(self, name):
- QAPIDoc.Section.__init__(self, name)
+ def __init__(self, parser, name, indent=0):
+ QAPIDoc.Section.__init__(self, parser, name, indent)
self.member = None
def connect(self, member):
@@ -338,7 +352,7 @@ class QAPIDoc(object):
self._parser = parser
self.info = info
self.symbol = None
- self.body = QAPIDoc.Section()
+ self.body = QAPIDoc.Section(parser)
# dict mapping parameter name to ArgSection
self.args = OrderedDict()
self.features = OrderedDict()
@@ -443,7 +457,18 @@ class QAPIDoc(object):
if name.startswith('@') and name.endswith(':'):
line = line[len(name)+1:]
- self._start_args_section(name[1:-1])
+ if not line or line.isspace():
+ # Line was just the "@arg:" header; following lines
+ # are not indented
+ indent = 0
+ line = ''
+ else:
+ # Line is "@arg: first line of description"; following
+ # lines should be indented by len(name) + 1, and we
+ # pad out this first line so it is handled the same way
+ indent = len(name) + 1
+ line = ' ' * indent + line
+ self._start_args_section(name[1:-1], indent)
elif self._is_section_tag(name):
self._append_line = self._append_various_line
self._append_various_line(line)
@@ -465,7 +490,17 @@ class QAPIDoc(object):
if name.startswith('@') and name.endswith(':'):
line = line[len(name)+1:]
- self._start_features_section(name[1:-1])
+ if not line or line.isspace():
+ # Line is just the "@name:" header, no ident for following lines
+ indent = 0
+ line = ''
+ else:
+ # Line is "@arg: first line of description"; following
+ # lines should be indented by len(name) + 3, and we
+ # pad out this first line so it is handled the same way
+ indent = len(name) + 1
+ line = ' ' * indent + line
+ self._start_features_section(name[1:-1], indent)
elif self._is_section_tag(name):
self._append_line = self._append_various_line
self._append_various_line(line)
@@ -498,11 +533,23 @@ class QAPIDoc(object):
% (name, self.sections[0].name))
elif self._is_section_tag(name):
line = line[len(name)+1:]
- self._start_section(name[:-1])
+ if not line or line.isspace():
+ # Line is just "SectionName:", no indent for following lines
+ indent = 0
+ line = ''
+ elif name.startswith("Example"):
+ # The "Examples" section is literal-text, so preserve
+ # all the indentation as-is
+ indent = 0
+ else:
+ # Line is "SectionName: some text", indent required
+ indent = len(name) + 1
+ line = ' ' * indent + line
+ self._start_section(name[:-1], indent)
self._append_freeform(line)
- def _start_symbol_section(self, symbols_dict, name):
+ def _start_symbol_section(self, symbols_dict, name, indent):
# FIXME invalid names other than the empty string aren't flagged
if not name:
raise QAPIParseError(self._parser, "invalid parameter name")
@@ -511,21 +558,21 @@ class QAPIDoc(object):
"'%s' parameter name duplicated" % name)
assert not self.sections
self._end_section()
- self._section = QAPIDoc.ArgSection(name)
+ self._section = QAPIDoc.ArgSection(self._parser, name, indent)
symbols_dict[name] = self._section
- def _start_args_section(self, name):
- self._start_symbol_section(self.args, name)
+ def _start_args_section(self, name, indent):
+ self._start_symbol_section(self.args, name, indent)
- def _start_features_section(self, name):
- self._start_symbol_section(self.features, name)
+ def _start_features_section(self, name, indent):
+ self._start_symbol_section(self.features, name, indent)
- def _start_section(self, name=None):
+ def _start_section(self, name=None, indent=0):
if name in ('Returns', 'Since') and self.has_section(name):
raise QAPIParseError(self._parser,
"duplicated '%s' section" % name)
self._end_section()
- self._section = QAPIDoc.Section(name)
+ self._section = QAPIDoc.Section(self._parser, name, indent)
self.sections.append(self._section)
def _end_section(self):
@@ -548,7 +595,7 @@ class QAPIDoc(object):
def connect_member(self, member):
if member.name not in self.args:
# Undocumented TODO outlaw
- self.args[member.name] = QAPIDoc.ArgSection(member.name)
+ self.args[member.name] = QAPIDoc.ArgSection(self._parser, member.name)
self.args[member.name].connect(member)
def connect_feature(self, feature):
@@ -556,7 +603,8 @@ class QAPIDoc(object):
raise QAPISemError(feature.info,
"feature '%s' lacks documentation"
% feature.name)
- self.features[feature.name] = QAPIDoc.ArgSection(feature.name)
+ self.features[feature.name] = QAPIDoc.ArgSection(self._parser,
+ feature.name)
self.features[feature.name].connect(feature)
def check_expr(self, expr):
Make the handling of indentation in doc comments more sophisticated, so that when we see a section like: Notes: some text some more text indented line 3 we save it for the doc-comment processing code as: some text some more text indented line 3 and when we see a section with the heading on its own line: Notes: some text some more text indented text we also accept that and save it in the same form. The exception is that we always retain indentation as-is for Examples sections, because these are literal text. If we detect that the comment document text is not indented as much as we expect it to be, we throw a parse error. (We don't complain about over-indented sections, because for rST this can be legitimate markup.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- scripts/qapi/parser.py | 82 +++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 17 deletions(-) -- 2.20.1