@@ -659,10 +659,23 @@ class InsertIndexInfo:
659659PARAM_SYNONYMS = ("param " , "parameter " , "arg " , "argument " , "keyword " , "kwarg " , "kwparam " )
660660
661661
662- def line_before_node (node : Node ) -> int :
663- line = node .line
664- assert line
665- return line - 2
662+ def node_line_no (node : Node ) -> int | None :
663+ """
664+ Get the 1-indexed line on which the node starts if possible. If not, return
665+ None.
666+
667+ Descend through the first children until we locate one with a line number or
668+ return None if None of them have one.
669+
670+ I'm not aware of any rst on which this returns None, to find out would
671+ require a more detailed analysis of the docutils rst parser source code. An
672+ example where the node doesn't have a line number but the first child does
673+ is all `definition_list` nodes. It seems like bullet_list and option_list
674+ get line numbers, but enum_list also doesn't. *shrug*.
675+ """
676+ while node .line is None and node .children :
677+ node = node .children [0 ]
678+ return node .line
666679
667680
668681def tag_name (node : Node ) -> str :
@@ -690,39 +703,29 @@ def get_insert_index(app: Sphinx, lines: list[str]) -> InsertIndexInfo | None:
690703 # Find a top level child which is a field_list that contains a field whose
691704 # name starts with one of the PARAM_SYNONYMS. This is the parameter list. We
692705 # hope there is at most of these.
693- for idx , child in enumerate ( doc .children ) :
706+ for child in doc .children :
694707 if tag_name (child ) != "field_list" :
695708 continue
696709
697- if any (c .children [0 ].astext ().startswith (PARAM_SYNONYMS ) for c in child .children ):
698- idx = idx
699- break
700- else :
701- idx = - 1
710+ if not any (c .children [0 ].astext ().startswith (PARAM_SYNONYMS ) for c in child .children ):
711+ continue
702712
703- if idx == - 1 :
704- # No parameters
705- pass
706- elif idx + 1 < len ( doc . children ):
707- # Unfortunately docutils only tells us the line numbers that nodes start on,
708- # not the range (boo!). So insert before the line before the next sibling.
709- at = line_before_node ( doc . children [ idx + 1 ] )
713+ # Found it! Try to insert before the next sibling. If there is no next
714+ # sibling, insert at end.
715+ # If there is a next sibling but we can't locate a line number, insert
716+ # at end. (I don't know of any input where this happens.)
717+ next_sibling = child . next_node ( descend = False , siblings = True )
718+ line_no = node_line_no ( next_sibling ) if next_sibling else None
719+ at = line_no - 2 if line_no else len ( lines )
710720 return InsertIndexInfo (insert_index = at , found_param = True )
711- else :
712- # No next sibling, insert at end
713- return InsertIndexInfo (insert_index = len (lines ), found_param = True )
714721
715722 # 4. Insert before examples
716723 # TODO: Maybe adjust which tags to insert ahead of
717- for idx , child in enumerate (doc .children ):
718- if tag_name (child ) not in ["literal_block" , "paragraph" , "field_list" ]:
719- idx = idx
720- break
721- else :
722- idx = - 1
723-
724- if idx != - 1 :
725- at = line_before_node (doc .children [idx ])
724+ for child in doc .children :
725+ if tag_name (child ) in ["literal_block" , "paragraph" , "field_list" ]:
726+ continue
727+ line_no = node_line_no (child )
728+ at = line_no - 2 if line_no else len (lines )
726729 return InsertIndexInfo (insert_index = at , found_directive = True )
727730
728731 # 5. Otherwise, insert at end
0 commit comments