From 69ac026ecd69b8667b7b1c89f88f3a141f4976e4 Mon Sep 17 00:00:00 2001 From: milde Date: Thu, 14 Dec 2023 22:38:49 +0000 Subject: [PATCH] Add/fix source and line attributes for doctree elements. Add "source" and "line" attributes to ``, `
`, ``, , and `` nodes. Fix "line" attribute for ``, ``, and `` nodes, cf. [r9475]. git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk@9500 929543f6-e4f2-0310-98a6-ba3bd3dd1d04 --- docutils/docutils/parsers/rst/directives/body.py | 3 +- docutils/docutils/parsers/rst/directives/images.py | 4 ++ docutils/docutils/parsers/rst/states.py | 73 ++++++++++++---------- .../test_parsers/test_rst/includes/include14.txt | 4 +- .../test_parsers/test_rst/test_definition_lists.py | 3 + .../test_rst/test_directives/test_include.py | 2 +- .../test/test_parsers/test_rst/test_source_line.py | 63 +++++++++++++------ 7 files changed, 97 insertions(+), 55 deletions(-) diff --git a/docutils/docutils/parsers/rst/directives/body.py b/docutils/docutils/parsers/rst/directives/body.py index 9c2e52183..924df8eb3 100644 --- a/docutils/docutils/parsers/rst/directives/body.py +++ b/docutils/docutils/parsers/rst/directives/body.py @@ -210,7 +210,8 @@ class MathBlock(Directive): if not block: continue node = nodes.math_block(self.block_text, block, **self.options) - node.line = self.content_offset + 1 + (node.source, + node.line) = self.state_machine.get_source_and_line(self.lineno) self.add_name(node) _nodes.append(node) return _nodes diff --git a/docutils/docutils/parsers/rst/directives/images.py b/docutils/docutils/parsers/rst/directives/images.py index d7d5baeea..6a1eeed7f 100644 --- a/docutils/docutils/parsers/rst/directives/images.py +++ b/docutils/docutils/parsers/rst/directives/images.py @@ -97,6 +97,8 @@ class Image(Directive): del self.options['target'] set_classes(self.options) image_node = nodes.image(self.block_text, **self.options) + (image_node.source, + image_node.line) = self.state_machine.get_source_and_line(self.lineno) self.add_name(image_node) if reference_node: reference_node += image_node @@ -130,6 +132,8 @@ class Figure(Image): if isinstance(image_node, nodes.system_message): return [image_node] figure_node = nodes.figure('', image_node) + (figure_node.source, figure_node.line + ) = self.state_machine.get_source_and_line(self.lineno) if figwidth == 'image': if PIL and self.state.document.settings.file_insertion_enabled: imagepath = url2pathname(image_node['uri']) diff --git a/docutils/docutils/parsers/rst/states.py b/docutils/docutils/parsers/rst/states.py index 50b86f7ed..304f9ffa6 100644 --- a/docutils/docutils/parsers/rst/states.py +++ b/docutils/docutils/parsers/rst/states.py @@ -1265,18 +1265,17 @@ class Body(RSTState): def bullet(self, match, context, next_state): """Bullet list item.""" - bulletlist = nodes.bullet_list() - (bulletlist.source, - bulletlist.line) = self.state_machine.get_source_and_line() - self.parent += bulletlist - bulletlist['bullet'] = match.string[0] + ul = nodes.bullet_list() + ul.source, ul.line = self.state_machine.get_source_and_line() + self.parent += ul + ul['bullet'] = match.string[0] i, blank_finish = self.list_item(match.end()) - bulletlist += i + ul += i offset = self.state_machine.line_offset + 1 # next line new_line_offset, blank_finish = self.nested_list_parse( self.state_machine.input_lines[offset:], input_offset=self.state_machine.abs_line_offset() + 1, - node=bulletlist, initial_state='BulletList', + node=ul, initial_state='BulletList', blank_finish=blank_finish) self.goto_line(new_line_offset) if not blank_finish: @@ -1284,6 +1283,7 @@ class Body(RSTState): return [], next_state, [] def list_item(self, indent): + src, srcline = self.state_machine.get_source_and_line() if self.state_machine.line[indent:]: indented, line_offset, blank_finish = ( self.state_machine.get_known_indented(indent)) @@ -1291,6 +1291,7 @@ class Body(RSTState): indented, indent, line_offset, blank_finish = ( self.state_machine.get_first_known_indented(indent)) listitem = nodes.list_item('\n'.join(indented)) + listitem.source, listitem.line = src, srcline if indented: self.nested_parse(indented, input_offset=line_offset, node=listitem) @@ -2728,17 +2729,18 @@ class Text(RSTState): def indent(self, match, context, next_state): """Definition list item.""" - definitionlist = nodes.definition_list() - (definitionlist.src, - definitionlist.line) = self.state_machine.get_source_and_line() - definitionlistitem, blank_finish = self.definition_list_item(context) - definitionlist += definitionlistitem - self.parent += definitionlist + dl = nodes.definition_list() + # the definition list starts on the line before the indent: + lineno = self.state_machine.abs_line_number() - 1 + dl.source, dl.line = self.state_machine.get_source_and_line(lineno) + dl_item, blank_finish = self.definition_list_item(context) + dl += dl_item + self.parent += dl offset = self.state_machine.line_offset + 1 # next line newline_offset, blank_finish = self.nested_list_parse( self.state_machine.input_lines[offset:], input_offset=self.state_machine.abs_line_offset() + 1, - node=definitionlist, initial_state='DefinitionList', + node=dl, initial_state='DefinitionList', blank_finish=blank_finish, blank_finish_state='Definition') self.goto_line(newline_offset) if not blank_finish: @@ -2840,24 +2842,30 @@ class Text(RSTState): return parent_node.children def definition_list_item(self, termline): + # the parser is already on the second (indented) line: + dd_lineno = self.state_machine.abs_line_number() + dt_lineno = dd_lineno - 1 (indented, indent, line_offset, blank_finish ) = self.state_machine.get_indented() - itemnode = nodes.definition_list_item( - '\n'.join(termline + list(indented))) - lineno = self.state_machine.abs_line_number() - 1 - (itemnode.source, - itemnode.line) = self.state_machine.get_source_and_line(lineno) - termlist, messages = self.term(termline, lineno) - itemnode += termlist - definition = nodes.definition('', *messages) - itemnode += definition + dl_item = nodes.definition_list_item( + '\n'.join(termline + list(indented))) + (dl_item.source, + dl_item.line) = self.state_machine.get_source_and_line(dt_lineno) + dt_nodes, messages = self.term(termline, dt_lineno) + dl_item += dt_nodes + dd = nodes.definition('', *messages) + dd.source, dd.line = self.state_machine.get_source_and_line(dd_lineno) + dl_item += dd if termline[0][-2:] == '::': - definition += self.reporter.info( + dd += self.reporter.info( 'Blank line missing before literal block (after the "::")? ' 'Interpreted as a definition list item.', - line=lineno+1) - self.nested_parse(indented, input_offset=line_offset, node=definition) - return itemnode, blank_finish + line=dd_lineno) + # TODO: drop a definition if it is an empty comment to allow + # definition list items with several terms? + # https://sourceforge.net/p/docutils/feature-requests/60/ + self.nested_parse(indented, input_offset=line_offset, node=dd) + return dl_item, blank_finish classifier_delimiter = re.compile(' +: +') @@ -2865,10 +2873,9 @@ class Text(RSTState): """Return a definition_list's term and optional classifiers.""" assert len(lines) == 1 text_nodes, messages = self.inline_text(lines[0], lineno) - term_node = nodes.term(lines[0]) - (term_node.source, - term_node.line) = self.state_machine.get_source_and_line(lineno) - node_list = [term_node] + dt = nodes.term(lines[0]) + dt.source, dt.line = self.state_machine.get_source_and_line(lineno) + node_list = [dt] for i in range(len(text_nodes)): node = text_nodes[i] if isinstance(node, nodes.Text): @@ -2921,8 +2928,8 @@ class Definition(SpecializedText): def indent(self, match, context, next_state): """Definition list item.""" - itemnode, blank_finish = self.definition_list_item(context) - self.parent += itemnode + dl_item, blank_finish = self.definition_list_item(context) + self.parent += dl_item self.blank_finish = blank_finish return [], 'DefinitionList', [] diff --git a/docutils/test/test_parsers/test_rst/includes/include14.txt b/docutils/test/test_parsers/test_rst/includes/include14.txt index 6c3efa5eb..f43f3f587 100644 --- a/docutils/test/test_parsers/test_rst/includes/include14.txt +++ b/docutils/test/test_parsers/test_rst/includes/include14.txt @@ -15,5 +15,5 @@ With *inline* element in line 2. Generic admonition text in line 16 -line 18 - definition list item in line 19 +term on line 18 + definition in line 19 diff --git a/docutils/test/test_parsers/test_rst/test_definition_lists.py b/docutils/test/test_parsers/test_rst/test_definition_lists.py index 1e0981176..eaca0854c 100755 --- a/docutils/test/test_parsers/test_rst/test_definition_lists.py +++ b/docutils/test/test_parsers/test_rst/test_definition_lists.py @@ -23,6 +23,9 @@ from docutils.utils import new_document class ParserTestCase(unittest.TestCase): + + maxDiff = None + def test_parser(self): parser = Parser() settings = get_default_settings(Parser) diff --git a/docutils/test/test_parsers/test_rst/test_directives/test_include.py b/docutils/test/test_parsers/test_rst/test_directives/test_include.py index 3e73a1fbc..84d328b9b 100755 --- a/docutils/test/test_parsers/test_rst/test_directives/test_include.py +++ b/docutils/test/test_parsers/test_rst/test_directives/test_include.py @@ -772,7 +772,7 @@ f"""\ A literal block:: - + Blank line missing before literal block (after the "::")? Interpreted as a definition list item. diff --git a/docutils/test/test_parsers/test_rst/test_source_line.py b/docutils/test/test_parsers/test_rst/test_source_line.py index 55c5e6c97..fea9ac250 100644 --- a/docutils/test/test_parsers/test_rst/test_source_line.py +++ b/docutils/test/test_parsers/test_rst/test_source_line.py @@ -81,9 +81,11 @@ With *inline* element in line 2. in line 6 * bullet list in line 9 -* second item in line 10 +* + second item in line 10 + paragraph starts in line 11 -1. enumerated list in line 12 +#. enumerated list in line 14 """, """\ @@ -100,16 +102,17 @@ With *inline* element in line 2. attribution in line 6 - + bullet list in line 9 - - + + second item in line 10 - - - - enumerated list in line 12 + paragraph starts in line 11 + + + + enumerated list in line 14 """], ["""\ Paragraph @@ -191,14 +194,14 @@ f"""\ attribution in line 6 - + bullet list in line 9 - + second item in line 10 - + enumerated list in line 12 @@ -206,13 +209,13 @@ f"""\ line 14 Generic admonition text in line 16 - - - - line 18 - + + + + term on line 18 + - definition list item in line 19 + definition in line 19 """], ["""\ Paragraph @@ -244,6 +247,30 @@ Final paragraph in line 11 Final paragraph in line 11 """], +["""\ +Paragraph + +.. image:: line-3.png + :width: 3em + +.. figure:: line-6.png + + caption on line 8 + +Final paragraph in line 10 +""", +"""\ + + + Paragraph + +
+ + + caption on line 8 + + Final paragraph in line 10 +"""], ]) -- 2.11.4.GIT