On Sat, Jun 28, 2025 at 08:47:33PM +0100, Gavin Smith wrote: > > As an example of a manual whose structure is not a tree, but is > > well-thought out, see info.texi. I'd expect to see such warnings > > there as well (unless the few explicit node pointers it has will avoid > > that), although the structure of that manual is fine. > > I've downloaded info.texi from > <https://cgit.git.savannah.gnu.org/cgit/emacs.git/plain/doc/misc/info.texi>, > commented out the @include lines, and ran it with the current development > version of texi2any. There were no warnings issued. > > The only irregularities I could find in this manual are multiple menu entries > for the same node (in "Help-Inv" and "Help-M"). > > It's possible that there will be warnings about this by default depending > on what changes are made.
And now I have warnings with this manual (patch at the end of this mail): $ TEXINFO_XS_STRUCTURE=0 ../tta/perl/texi2any.pl info.texi info.texi:461: warning: unexpected node `Help-]' in menu info.texi:462: warning: unexpected node `Help-]' in menu info.texi:623: warning: unexpected node `Help-FOO' in menu info.texi:624: warning: unexpected node `Help-FOO' in menu The first two warnings are in relation to this menu: @menu * ]: Help-]. Node telling about ]. * stuff: Help-]. Same node. * Help-]:: Yet again, same node. @end menu The `Help-FOO' warnings are similar. This is an improvement on previous warnings IMHO as the line number in the menu is directly referenced. For a change like this, I regenerate the test results and go through the results to see if they are satisfactory. I will also have to write all of this in C for the XS code, as well. Patrice: the "$menu_node_relations->{'associated_section'}->{'section_directions'}->{'next'}->{'associated_node'}->{'element'}" stuff is very verbose. All this is doing is finding the next node based on sectioning. So I wonder if this relation structure could be simplified. Maybe the "node relations" and "section relations" could be "fused" so you don't have to hop back and forth from one to the other? diff --git a/tta/perl/Texinfo/Structuring.pm b/tta/perl/Texinfo/Structuring.pm index 9353d64219..3188465f53 100644 --- a/tta/perl/Texinfo/Structuring.pm +++ b/tta/perl/Texinfo/Structuring.pm @@ -844,12 +844,105 @@ sub check_node_tree_menu_structure($) } } + # Go through all the menus and check if they match subordinate + # nodes. + if ($customization_information->get_conf('CHECK_NORMAL_MENU_STRUCTURE')) { + NODE_RELATION: + foreach my $node_relations (@{$nodes_list}) { + if ($node_relations->{'menus'}) { + next if !$node_relations->{'associated_section'}; + my $section_childs = $node_relations->{'associated_section'} + ->{'section_childs'}; + next if !defined($section_childs) or !@{$section_childs}; + + my $first_child_node_relations = $section_childs->[0]->{'associated_node'}; + next NODE_RELATION if !defined($first_child_node_relations); + + # Set to the first subordinate section, which should appear first in the + # menu. + my $section_node = $first_child_node_relations->{'element'}; + next if !defined($section_node); + + MENU: + foreach my $menu (@{$node_relations->{'menus'}}) { + # Loop through each each entry in the menu and + # check if it is the menu entry we were expecting + # to see based on what came before. + foreach my $menu_content (@{$menu->{'contents'}}) { + next if !$menu_content->{'type'} + or $menu_content->{'type'} ne 'menu_entry'; + + my ($menu_node_name, $menu_node); + foreach my $content (@{$menu_content->{'contents'}}) { + next if $content->{'type'} ne 'menu_entry_node'; + if ($content->{'extra'}) { + if (!$content->{'extra'}->{'manual_content'}) { + if (defined($content->{'extra'}->{'normalized'})) { + $menu_node_name = $content->{'extra'}->{'normalized'}; + $menu_node = $identifier_target->{$menu_node_name}; + } + } + } + last; # menu_entry_node found + } + next MENU if !defined($menu_node); + + my $menu_node_element_number = $menu_node->{'extra'}->{'node_number'}; + my $menu_node_relations + = $nodes_list->[$menu_node_element_number - 1]; + next MENU if + !$menu_node_relations + or !defined($menu_node_relations->{'associated_section'}); + + if (!defined($section_node) or $menu_node ne $section_node) { + # TODO: split this into two warnings: one for + # the first menu entry, and one for following + # menu entries. Also report the previous menu + # entry. + if (defined($section_node)) { + $registrar->line_warn( + sprintf(__("node `%s' in menu where `%s' expected"), + target_element_to_texi_label($menu_node), + target_element_to_texi_label($section_node)), + $menu_content->{'source_info'}, 0, + $customization_information->get_conf('DEBUG')); + #$node_errors{$node->{'extra'}->{'node_number'}} = 1; + } else { + $registrar->line_warn( + sprintf(__("unexpected node `%s' in menu"), + target_element_to_texi_label($menu_node)), + $menu_content->{'source_info'}, 0, + $customization_information->get_conf('DEBUG')); + #$node_errors{$node->{'extra'}->{'node_number'}} = 1; + } + } + + + $node_errors{$menu_node_element_number} = 1; + + # Now set section_node for the section that is + # expected to follow the current menu node. + if (!defined($menu_node_relations->{'associated_section'}->{'section_directions'}) + or !defined($menu_node_relations->{'associated_section'}->{'section_directions'}->{'next'}) + or !defined($menu_node_relations->{'associated_section'}->{'section_directions'}->{'next'}->{'associated_node'}) + or !defined($menu_node_relations->{'associated_section'}->{'section_directions'}->{'next'}->{'associated_node'}->{'element'})) { + undef $section_node; + } else { + $section_node = $menu_node_relations->{'associated_section'}->{'section_directions'}->{'next'}->{'associated_node'}->{'element'}; + } + } + } + } + } + } + my %cached_menu_nodes; # check for node up / menu up mismatch if ($customization_information->get_conf('CHECK_MISSING_MENU_ENTRY')) { foreach my $node_relations (@{$nodes_list}) { my $node = $node_relations->{'element'}; + next if $node_errors{$node->{'extra'}->{'node_number'}}; my $section_relations = $node_relations->{'associated_section'}; next if !defined($section_relations); @@ -911,6 +1004,8 @@ sub check_node_tree_menu_structure($) } } } + return; + # Any problems with 'up' direction should have been found by previous code. #my @checked_node_directions_names = ('next', 'prev', 'up');