@@ -700,14 +700,51 @@ def depart_desc(self, node: Element) -> None:
700700 self .body .append (CR + r'\end{fulllineitems}' + BLANKLINE )
701701
702702 def _visit_signature_line (self , node : Element ) -> None :
703+ def next_sibling (e : Element ) -> Element | None :
704+ try :
705+ return e .parent [e .parent .index (e ) + 1 ]
706+ except (AttributeError , IndexError ):
707+ return None
708+
709+ def has_multi_line (e : Element ) -> bool :
710+ return e .get ('multi_line_parameter_list' )
711+
712+ self .has_tplist = False
713+
703714 for child in node :
715+ if isinstance (child , addnodes .desc_tparameterlist ):
716+ self .has_tplist = True
717+ # recall that return annotations must follow an argument list,
718+ # so signatures of the form "foo[tplist] -> retann" will not
719+ # be encountered (if they should, the `domains.python.py_sig_re`
720+ # pattern must be modified accordingly)
721+ arglist = next_sibling (child )
722+ assert isinstance (arglist , addnodes .desc_parameterlist )
723+ # tplist + arglist: \macro{name}{tplist}{arglist}{return}
724+ multi_tplist = has_multi_line (child )
725+ multi_arglist = has_multi_line (arglist )
726+
727+ if multi_tplist :
728+ if multi_arglist :
729+ self .body .append (CR + r'\pysigwithonelineperargwithonelinepertparg{' )
730+ else :
731+ self .body .append (CR + r'\pysiglinewithargsretwithonelinepertparg{' )
732+ else :
733+ if multi_arglist :
734+ self .body .append (CR + r'\pysigwithonelineperargwithtplist{' )
735+ else :
736+ self .body .append (CR + r'\pysiglinewithargsretwithtplist{' )
737+ break
738+
704739 if isinstance (child , addnodes .desc_parameterlist ):
705- if child .get ('multi_line_parameter_list' ):
740+ # arglist only: \macro{name}{arglist}{return}
741+ if has_multi_line (child ):
706742 self .body .append (CR + r'\pysigwithonelineperarg{' )
707743 else :
708744 self .body .append (CR + r'\pysiglinewithargsret{' )
709745 break
710746 else :
747+ # no tplist, no arglist: \macro{name}
711748 self .body .append (CR + r'\pysigline{' )
712749
713750 def _depart_signature_line (self , node : Element ) -> None :
@@ -784,34 +821,47 @@ def visit_desc_returns(self, node: Element) -> None:
784821 def depart_desc_returns (self , node : Element ) -> None :
785822 self .body .append (r'}' )
786823
787- def visit_desc_parameterlist (self , node : Element ) -> None :
788- # close name, open parameterlist
789- self .body .append ('}{' )
824+ def _visit_sig_parameter_list (self , node : Element , parameter_group : type [Element ]) -> None :
825+ """Visit a signature parameters or type parameters list.
826+
827+ The *parameter_group* value is the type of a child node acting as a required parameter
828+ or as a set of contiguous optional parameters.
829+
830+ The caller is responsible for closing adding surrounding LaTeX macro argument start
831+ and stop tokens.
832+ """
790833 self .is_first_param = True
791834 self .optional_param_level = 0
792835 self .params_left_at_level = 0
793836 self .param_group_index = 0
794837 # Counts as what we call a parameter group either a required parameter, or a
795838 # set of contiguous optional ones.
796- self .list_is_required_param = [isinstance (c , addnodes .desc_parameter )
797- for c in node .children ]
839+ self .list_is_required_param = [isinstance (c , parameter_group ) for c in node .children ]
798840 # How many required parameters are left.
799841 self .required_params_left = sum (self .list_is_required_param )
800842 self .param_separator = r'\sphinxparamcomma '
801843 self .multi_line_parameter_list = node .get ('multi_line_parameter_list' , False )
802844
845+ def visit_desc_parameterlist (self , node : Element ) -> None :
846+ if not self .has_tplist :
847+ # close name argument (#1), open parameters list argument (#2)
848+ self .body .append ('}{' )
849+ self ._visit_sig_parameter_list (node , addnodes .desc_parameter )
850+
803851 def depart_desc_parameterlist (self , node : Element ) -> None :
804852 # close parameterlist, open return annotation
805853 self .body .append ('}{' )
806854
807855 def visit_desc_tparameterlist (self , node : Element ) -> None :
808- # not supported yet
809- raise nodes .SkipNode
856+ # close name argument (#1), open type parameters list argument (#2)
857+ self .body .append ('}{' )
858+ self ._visit_sig_parameter_list (node , addnodes .desc_tparameter )
810859
811860 def depart_desc_tparameterlist (self , node : Element ) -> None :
812- pass
861+ # close type parameters list, open parameters list argument (#3)
862+ self .body .append ('}{' )
813863
814- def visit_desc_parameter (self , node : Element ) -> None :
864+ def _visit_sig_parameter (self , node : Element , parameter_macro : str ) -> None :
815865 if self .is_first_param :
816866 self .is_first_param = False
817867 elif not self .multi_line_parameter_list and not self .required_params_left :
@@ -821,7 +871,10 @@ def visit_desc_parameter(self, node: Element) -> None:
821871 else :
822872 self .params_left_at_level -= 1
823873 if not node .hasattr ('noemph' ):
824- self .body .append (r'\sphinxparam{' )
874+ self .body .append (parameter_macro )
875+
876+ def visit_desc_parameter (self , node : Element ) -> None :
877+ self ._visit_sig_parameter (node , r'\sphinxparam{' )
825878
826879 def depart_desc_parameter (self , node : Element ) -> None :
827880 if not node .hasattr ('noemph' ):
@@ -844,11 +897,10 @@ def depart_desc_parameter(self, node: Element) -> None:
844897 self .param_group_index += 1
845898
846899 def visit_desc_tparameter (self , node : Element ) -> None :
847- # not supported yet
848- raise nodes .SkipNode
900+ self ._visit_sig_parameter (node , r'\sphinxtypeparam{' )
849901
850902 def depart_desc_tparameter (self , node : Element ) -> None :
851- pass
903+ self . depart_desc_parameter ( node )
852904
853905 def visit_desc_optional (self , node : Element ) -> None :
854906 self .params_left_at_level = sum ([isinstance (c , addnodes .desc_parameter )
0 commit comments