[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[no subject]
From: |
Patrice Dumas |
Date: |
Sun, 29 Sep 2024 14:03:20 -0400 (EDT) |
branch: master
commit cbd1ca03c766497ed12ad224c0d5e87145280992
Author: Patrice Dumas <pertusus@free.fr>
AuthorDate: Sat Jul 6 12:16:08 2024 +0200
* tp/Texinfo/Convert/Plaintext.pm (format_ref, _convert),
tp/Texinfo/Convert/Info.pm (format_ref): split format_ref function out
of _convert to format *ref commands. Put format_ref both in
Plaintext.pm and Info.pm to be able to format cross references
differently in both formats.
* tp/Texinfo/Convert/Plaintext.pm (format_ref): do not treat reference
in multitable as if they were in @w. Do not disallow breaks.
---
ChangeLog | 11 +
tp/Texinfo/Convert/Info.pm | 298 ++++++++++++++
tp/Texinfo/Convert/Plaintext.pm | 563 +++++++++++++--------------
tp/t/results/multitable/ref_in_multitable.pl | 8 +-
4 files changed, 586 insertions(+), 294 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index f59902cfb5..397798e331 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2024-07-06 Patrice Dumas <pertusus@free.fr>
+
+ * tp/Texinfo/Convert/Plaintext.pm (format_ref, _convert),
+ tp/Texinfo/Convert/Info.pm (format_ref): split format_ref function out
+ of _convert to format *ref commands. Put format_ref both in
+ Plaintext.pm and Info.pm to be able to format cross references
+ differently in both formats.
+
+ * tp/Texinfo/Convert/Plaintext.pm (format_ref): do not treat reference
+ in multitable as if they were in @w. Do not disallow breaks.
+
2024-07-06 Patrice Dumas <pertusus@free.fr>
Convert definition line parts in plaintext in code style only when
needed
diff --git a/tp/Texinfo/Convert/Info.pm b/tp/Texinfo/Convert/Info.pm
index 582239f491..89f40699e1 100644
--- a/tp/Texinfo/Convert/Info.pm
+++ b/tp/Texinfo/Convert/Info.pm
@@ -504,6 +504,304 @@ sub format_error_outside_of_any_node($$)
}
}
+sub format_ref($$$$)
+{
+ my $self = shift;
+ my $cmdname = shift;
+ my $element = shift;
+ my $formatter = shift;
+
+ my @args;
+ for my $arg (@{$element->{'args'}}) {
+ if (defined $arg->{'contents'}) {
+ push @args, $arg;
+ } else {
+ push @args, undef;
+ }
+ }
+ $args[0] = {'text' => ''} if (!defined($args[0]));
+
+ my $node_arg = $element->{'args'}->[0];
+
+ # normalize node name, to get a ref with the right formatting
+ # NOTE as a consequence, the line numbers appearing in case of errors
+ # correspond to the node lines numbers, and not the @ref.
+ my $label_element;
+ my $target_element;
+
+ my $identifiers_target;
+ if ($self->{'document'}) {
+ $identifiers_target = $self->{'document'}->labels_information();
+ }
+
+ if ($node_arg and $node_arg->{'extra'}
+ and !$node_arg->{'extra'}->{'manual_content'}
+ and defined($node_arg->{'extra'}->{'normalized'})
+ and $identifiers_target
+ and $identifiers_target->{$node_arg->{'extra'}->{'normalized'}}) {
+ $target_element
+ = $identifiers_target->{$node_arg->{'extra'}->{'normalized'}};
+ $label_element
+ = Texinfo::Common::get_label_element($target_element);
+ if (defined($label_element) and !$label_element->{'contents'}) {
+ $label_element = undef;
+ }
+ }
+ if (!defined($label_element)) {
+ $label_element = $args[0];
+ }
+
+ # if it a reference to a float with a label, $arg[1] is
+ # set to '$type $number' or '$number' if there is no type.
+ if (! defined($args[1])
+ and $target_element and $target_element->{'cmdname'}
+ and $target_element->{'cmdname'} eq 'float') {
+ my $name = $self->float_type_number($target_element);
+ $args[1] = $name;
+ }
+ if ($cmdname eq 'inforef' and scalar(@args) >= 3) {
+ $args[3] = $args[2];
+ $args[2] = undef;
+ }
+
+ # Treat cross-reference commands in a multitable cell as if they
+ # were surrounded by @w{ ... }, so not to split output across
+ # lines, leading text from other columns appearing to be part of the
+ # cross-reference.
+ my $in_multitable = 0;
+ if ($self->{'document_context'}->[-1]->{'in_multitable'}) {
+ $in_multitable = 1;
+ $formatter->{'w'}++;
+ set_space_protection($formatter->{'container'}, 1, undef)
+ if ($formatter->{'w'} == 1);
+ }
+ # Disallow breaks in runs of Chinese text in node names, because a
+ # break would be normalized to a single space by the Info reader, and
+ # the node wouldn't be found.
+ set_space_protection($formatter->{'container'},
+ undef, undef, undef, undef, 1);
+
+ if ($cmdname eq 'xref') {
+ $self->_convert({'type' => '_stop_upper_case',
+ 'contents' => [{'text' => '*Note '}]});
+ } else {
+ $self->_convert({'type' => '_stop_upper_case',
+ 'contents' => [{'text' => '*note '}]});
+ }
+ my $name;
+ if (defined($args[1])) {
+ $name = $args[1];
+ } elsif (defined($args[2])) {
+ $name = $args[2];
+ }
+ my $file;
+ if (defined($args[3])) {
+ $file = {'contents' => [
+ {'text' => '('},
+ {'type' => '_stop_upper_case',
+ 'contents' => [{'type' => '_code',
+ 'contents' => [$args[3]]}],},
+ {'text' => ')'},]};
+ } elsif (defined($args[4])) {
+ # add a () such that the node is considered to be external,
+ # even though the manual name is not known. This should only
+ # happen if a book argument is given, but no manual name.
+ $file = {'text' => '()'};
+ }
+
+ if ($name) {
+ push @{$self->{'count_context'}}, {'lines' => 0, 'bytes' => 0};
+ $self->_convert($name);
+ my $name_text = $self->_stream_result();
+ # needed, as last word is added only when : is added below
+ # NB this mixes encoded and unencoded strings but is ok for
+ # checking for : only
+ my $name_text_checked = $name_text
+ .get_pending($self->{'formatters'}->[-1]->{'container'});
+ my $quoting_required = 0;
+ if ($name_text_checked =~ /:/m) {
+ if ($self->{'info_special_chars_warning'}) {
+ $self->plaintext_line_warn($self, sprintf(__(
+ "\@%s cross-reference name should not contain `:'"),
+ $cmdname), $element->{'source_info'});
+ }
+ if ($self->{'info_special_chars_quote'}) {
+ $quoting_required = 1;
+ }
+ }
+ my $pre_quote = $quoting_required ? "\x{7f}" : '';
+ my $post_quote = $pre_quote;
+
+ $self->_stream_output(
+ add_text($formatter->{'container'}, "$post_quote: "),
+ $formatter->{'container'});
+ my $result = $self->_stream_result();
+
+ # Note post_quote has to be added first to flush output
+ $result =~ s/^(\s*)/$1$pre_quote/ if $pre_quote;
+
+ my $lines_added = $self->{'count_context'}->[-1]->{'lines'};
+ pop @{$self->{'count_context'}};
+ $self->_stream_output_encoded($result);
+ $self->{'count_context'}->[-1]->{'lines'} += $lines_added;
+ }
+
+ if ($file) {
+ $self->_convert($file);
+ }
+
+ my $node_name;
+
+ # Get the node name to be output.
+ # Due to the paragraph formatter holding pending text, converting
+ # the node name with the current formatter does not yield all the
+ # converted text. To get the full node name (and no more), we
+ # can convert in a new context, using convert_line_new_context.
+ # However, it is slow to do this for every node. So in the most
+ # frequent case when the node name is a simple text element, use
+ # that text instead.
+ if ($label_element and $label_element->{'contents'}
+ and scalar(@{$label_element->{'contents'}}) == 1
+ and defined($label_element->{'contents'}->[0]->{'text'})) {
+ $node_name = $label_element->{'contents'}->[0]->{'text'};
+ } else {
+ $self->{'silent'} = 0 if (!defined($self->{'silent'}));
+ $self->{'silent'}++;
+
+ ($node_name, undef) = $self->convert_line_new_context(
+ {'type' => '_code',
+ 'contents' => [$label_element]},
+ {'suppress_styles' => 1,
+ 'no_added_eol' => 1});
+ $self->{'silent'}--;
+ }
+ if (defined($file) and $node_name !~ /\S/) {
+ # Some Info reader versions, at least the Info reader from
+ # Texinfo 6.8 and 7.1 cannot follow a cross-reference
+ # consisting only of a manual name, such as *Note (manual)::.
+ # The Emacs Info reader does not seem to have this problem.
+ # Add a Top node to have a node name.
+ # Should probably be removed about 10-15 years after Info
+ # reader have been fixed.
+ $label_element = {'text' => 'Top'};
+ }
+
+ my $check_chars;
+ if ($name) {
+ $check_chars = quotemeta ",\t.";
+ } else {
+ $check_chars = quotemeta ":";
+ }
+
+ my $quoting_required = 0;
+ if ($node_name =~ /([$check_chars])/m) {
+ if ($self->{'info_special_chars_warning'}) {
+ $self->plaintext_line_warn($self, sprintf(__(
+ "\@%s node name should not contain `%s'"), $cmdname, $1),
+ $element->{'source_info'});
+ }
+ if ($self->{'info_special_chars_quote'}) {
+ $quoting_required = 1;
+ }
+ }
+
+ my $pre_quote = $quoting_required ? "\x{7f}" : '';
+ my $post_quote = $pre_quote;
+
+ # node name
+ $self->_stream_output(
+ add_next($self->{'formatters'}->[-1]->{'container'}, $pre_quote),
+ $self->{'formatters'}[-1]{'container'})
+ if $pre_quote;
+
+ $self->{'formatters'}->[-1]->{'suppress_styles'} = 1;
+ $self->_convert({'type' => '_stop_upper_case',
+ 'contents' => [
+ {'type' => '_code',
+ 'contents' => [$label_element]}]});
+ delete $self->{'formatters'}->[-1]->{'suppress_styles'};
+
+ $self->_stream_output(
+ add_next($self->{'formatters'}->[-1]->{'container'}, $post_quote),
+ $self->{'formatters'}[-1]{'container'})
+ if $post_quote;
+
+ if (!$name) {
+ $self->_stream_output(
+ add_next($self->{'formatters'}->[-1]->{'container'}, '::'),
+ $self->{'formatters'}[-1]{'container'});
+ }
+
+ # Check if punctuation follows the ref command with a label
+ # argument. If not, add a full stop.
+ if ($name) {
+ # Find next element
+ my $next;
+ my $current_contents = $element->{'parent'}->{'contents'};
+ my $contents_nr = scalar(@$current_contents);
+ for (my $i = 0; $i < $contents_nr - 1; $i++) {
+ if ($current_contents->[$i] == $element) {
+ $next = $current_contents->[$i+1];
+ last;
+ }
+ }
+
+ if (!($next and $next->{'text'}
+ and $next->{'text'} =~ /^[\.,]/)) {
+ # In the past, it was explicily described in the manual that
+ # some punctuation was automatically added for @pxref only,
+ # while the other commands required a following full stop or
+ # comma.
+ #
+ # It is better if the user manages to find a wording with a
+ # comma or full stop following naturally the ref command.
+ # However, it is not possible in general except for @xref -- and
+ # even for @xref it may be cumbersome. Therefore we only warn
+ # that a comma or full stop is missing with @xref such that the
+ # user tries to add it in that case, in the other case, we
+ # automatically add a full stop without warning.
+ #
+ # There cannot be a perfect solution, as these issues stem from
+ # the Info language design where it is not possible to
+ # distinguish if punctuation used in cross reference is
+ # part of the text or is added and should be considered as markup.
+ if ($cmdname eq 'xref') {
+ if ($next and defined($next->{'text'})
+ and $next->{'text'} =~ /\S/) {
+ my $text = $next->{'text'};
+ $text =~ s/^\s*//;
+ my $char = substr($text, 0, 1);
+ $self->plaintext_line_warn($self, sprintf(__(
+ "`.' or `,' must follow \@xref, not %s"),
+ $char), $element->{'source_info'});
+ } else {
+ $self->plaintext_line_warn($self,
+ __("`.' or `,' must follow \@xref"),
+ $element->{'source_info'});
+ }
+ }
+ my @added = ({'text' => '.'});
+ # The added full stop does not end a sentence. Info readers will
+ # have a chance of guessing correctly whether the full stop was
+ # added by whether it is followed by 2 spaces (although this
+ # doesn't help at the end of a line nor when a parenthesis
+ # follows the ref command).
+ push @added, {'cmdname' => ':'};
+ for my $added_element (@added) {
+ $self->_convert($added_element);
+ }
+ }
+ }
+
+ if ($in_multitable) {
+ $formatter->{'w'}--;
+ set_space_protection($formatter->{'container'}, 0, undef)
+ if ($formatter->{'w'} == 0);
+ }
+ set_space_protection($formatter->{'container'},
+ undef,undef,undef,undef,0); # double_width_no_break
+}
+
my @directions = ('Next', 'Prev', 'Up');
sub format_node($$)
{
diff --git a/tp/Texinfo/Convert/Plaintext.pm b/tp/Texinfo/Convert/Plaintext.pm
index 8a0678ec48..b7febb86bd 100644
--- a/tp/Texinfo/Convert/Plaintext.pm
+++ b/tp/Texinfo/Convert/Plaintext.pm
@@ -1956,6 +1956,278 @@ sub process_printindex($$;$)
_add_lines_count($self, 1);
}
+sub format_ref($$$$)
+{
+ my $self = shift;
+ my $cmdname = shift;
+ my $element = shift;
+ my $formatter = shift;
+
+ my @args;
+ for my $arg (@{$element->{'args'}}) {
+ if (defined $arg->{'contents'}) {
+ push @args, $arg;
+ } else {
+ push @args, undef;
+ }
+ }
+ $args[0] = {'text' => ''} if (!defined($args[0]));
+
+ my $node_arg = $element->{'args'}->[0];
+
+ # normalize node name, to get a ref with the right formatting
+ # NOTE as a consequence, the line numbers appearing in case of errors
+ # correspond to the node lines numbers, and not the @ref.
+ my $label_element;
+ my $target_element;
+
+ my $identifiers_target;
+ if ($self->{'document'}) {
+ $identifiers_target = $self->{'document'}->labels_information();
+ }
+
+ if ($node_arg and $node_arg->{'extra'}
+ and !$node_arg->{'extra'}->{'manual_content'}
+ and defined($node_arg->{'extra'}->{'normalized'})
+ and $identifiers_target
+ and $identifiers_target->{$node_arg->{'extra'}->{'normalized'}}) {
+ $target_element
+ = $identifiers_target->{$node_arg->{'extra'}->{'normalized'}};
+ $label_element
+ = Texinfo::Common::get_label_element($target_element);
+ if (defined($label_element) and !$label_element->{'contents'}) {
+ $label_element = undef;
+ }
+ }
+ if (!defined($label_element)) {
+ $label_element = $args[0];
+ }
+
+ # if it a reference to a float with a label, $arg[1] is
+ # set to '$type $number' or '$number' if there is no type.
+ if (! defined($args[1])
+ and $target_element and $target_element->{'cmdname'}
+ and $target_element->{'cmdname'} eq 'float') {
+ my $name = $self->float_type_number($target_element);
+ $args[1] = $name;
+ }
+ if ($cmdname eq 'inforef' and scalar(@args) >= 3) {
+ $args[3] = $args[2];
+ $args[2] = undef;
+ }
+
+ if ($cmdname eq 'xref') {
+ _convert($self, {'type' => '_stop_upper_case',
+ 'contents' => [{'text' => '*Note '}]});
+ } else {
+ _convert($self, {'type' => '_stop_upper_case',
+ 'contents' => [{'text' => '*note '}]});
+ }
+ my $name;
+ if (defined($args[1])) {
+ $name = $args[1];
+ } elsif (defined($args[2])) {
+ $name = $args[2];
+ }
+ my $file;
+ if (defined($args[3])) {
+ $file = {'contents' => [
+ {'text' => '('},
+ {'type' => '_stop_upper_case',
+ 'contents' => [{'type' => '_code',
+ 'contents' => [$args[3]]}],},
+ {'text' => ')'},]};
+ } elsif (defined($args[4])) {
+ # add a () such that the node is considered to be external,
+ # even though the manual name is not known. This should only
+ # happen if a book argument is given, but no manual name.
+ $file = {'text' => '()'};
+ }
+
+ if ($name) {
+ push @{$self->{'count_context'}}, {'lines' => 0, 'bytes' => 0};
+ $self->_convert($name);
+ my $name_text = _stream_result($self);
+ # needed, as last word is added only when : is added below
+ # NB this mixes encoded and unencoded strings but is ok for
+ # checking for : only
+ my $name_text_checked = $name_text
+ .get_pending($self->{'formatters'}->[-1]->{'container'});
+ my $quoting_required = 0;
+ if ($name_text_checked =~ /:/m) {
+ if ($self->{'info_special_chars_warning'}) {
+ $self->plaintext_line_warn($self, sprintf(__(
+ "\@%s cross-reference name should not contain `:'"),
+ $cmdname), $element->{'source_info'});
+ }
+ if ($self->{'info_special_chars_quote'}) {
+ $quoting_required = 1;
+ }
+ }
+ my $pre_quote = $quoting_required ? "\x{7f}" : '';
+ my $post_quote = $pre_quote;
+
+ _stream_output($self,
+ add_text($formatter->{'container'}, "$post_quote: "),
+ $formatter->{'container'});
+ my $result = _stream_result($self);
+
+ # Note post_quote has to be added first to flush output
+ $result =~ s/^(\s*)/$1$pre_quote/ if $pre_quote;
+
+ my $lines_added = $self->{'count_context'}->[-1]->{'lines'};
+ pop @{$self->{'count_context'}};
+ _stream_output_encoded($self, $result);
+ $self->{'count_context'}->[-1]->{'lines'} += $lines_added;
+ }
+
+ if ($file) {
+ _convert($self, $file);
+ }
+
+ my $node_name;
+
+ # Get the node name to be output.
+ # Due to the paragraph formatter holding pending text, converting
+ # the node name with the current formatter does not yield all the
+ # converted text. To get the full node name (and no more), we
+ # can convert in a new context, using convert_line_new_context.
+ # However, it is slow to do this for every node. So in the most
+ # frequent case when the node name is a simple text element, use
+ # that text instead.
+ if ($label_element and $label_element->{'contents'}
+ and scalar(@{$label_element->{'contents'}}) == 1
+ and defined($label_element->{'contents'}->[0]->{'text'})) {
+ $node_name = $label_element->{'contents'}->[0]->{'text'};
+ } else {
+ $self->{'silent'} = 0 if (!defined($self->{'silent'}));
+ $self->{'silent'}++;
+
+ ($node_name, undef) = $self->convert_line_new_context(
+ {'type' => '_code',
+ 'contents' => [$label_element]},
+ {'suppress_styles' => 1,
+ 'no_added_eol' => 1});
+ $self->{'silent'}--;
+ }
+ if (defined($file) and $node_name !~ /\S/) {
+ # Some Info reader versions, at least the Info reader from
+ # Texinfo 6.8 and 7.1 cannot follow a cross-reference
+ # consisting only of a manual name, such as *Note (manual)::.
+ # The Emacs Info reader does not seem to have this problem.
+ # Add a Top node to have a node name.
+ # Should probably be removed about 10-15 years after Info
+ # reader have been fixed.
+ $label_element = {'text' => 'Top'};
+ }
+
+ my $check_chars;
+ if ($name) {
+ $check_chars = quotemeta ",\t.";
+ } else {
+ $check_chars = quotemeta ":";
+ }
+
+ my $quoting_required = 0;
+ if ($node_name =~ /([$check_chars])/m) {
+ if ($self->{'info_special_chars_warning'}) {
+ $self->plaintext_line_warn($self, sprintf(__(
+ "\@%s node name should not contain `%s'"), $cmdname, $1),
+ $element->{'source_info'});
+ }
+ if ($self->{'info_special_chars_quote'}) {
+ $quoting_required = 1;
+ }
+ }
+
+ my $pre_quote = $quoting_required ? "\x{7f}" : '';
+ my $post_quote = $pre_quote;
+
+ # node name
+ _stream_output($self,
+ add_next($self->{'formatters'}->[-1]->{'container'}, $pre_quote),
+ $self->{'formatters'}[-1]{'container'})
+ if $pre_quote;
+
+ $self->{'formatters'}->[-1]->{'suppress_styles'} = 1;
+ _convert($self, {'type' => '_stop_upper_case',
+ 'contents' => [
+ {'type' => '_code',
+ 'contents' => [$label_element]}]});
+ delete $self->{'formatters'}->[-1]->{'suppress_styles'};
+
+ _stream_output($self,
+ add_next($self->{'formatters'}->[-1]->{'container'}, $post_quote),
+ $self->{'formatters'}[-1]{'container'})
+ if $post_quote;
+
+ if (!$name) {
+ _stream_output($self,
+ add_next($self->{'formatters'}->[-1]->{'container'}, '::'),
+ $self->{'formatters'}[-1]{'container'});
+ }
+
+ # Check if punctuation follows the ref command with a label
+ # argument. If not, add a full stop.
+ if ($name) {
+ # Find next element
+ my $next;
+ my $current_contents = $element->{'parent'}->{'contents'};
+ my $contents_nr = scalar(@$current_contents);
+ for (my $i = 0; $i < $contents_nr - 1; $i++) {
+ if ($current_contents->[$i] == $element) {
+ $next = $current_contents->[$i+1];
+ last;
+ }
+ }
+
+ if (!($next and $next->{'text'}
+ and $next->{'text'} =~ /^[\.,]/)) {
+ # In the past, it was explicily described in the manual that
+ # some punctuation was automatically added for @pxref only,
+ # while the other commands required a following full stop or
+ # comma.
+ #
+ # It is better if the user manages to find a wording with a
+ # comma or full stop following naturally the ref command.
+ # However, it is not possible in general except for @xref -- and
+ # even for @xref it may be cumbersome. Therefore we only warn
+ # that a comma or full stop is missing with @xref such that the
+ # user tries to add it in that case, in the other case, we
+ # automatically add a full stop without warning.
+ #
+ # There cannot be a perfect solution, as these issues stem from
+ # the Info language design where it is not possible to
+ # distinguish if punctuation used in cross reference is
+ # part of the text or is added and should be considered as markup.
+ if ($cmdname eq 'xref') {
+ if ($next and defined($next->{'text'})
+ and $next->{'text'} =~ /\S/) {
+ my $text = $next->{'text'};
+ $text =~ s/^\s*//;
+ my $char = substr($text, 0, 1);
+ $self->plaintext_line_warn($self, sprintf(__(
+ "`.' or `,' must follow \@xref, not %s"),
+ $char), $element->{'source_info'});
+ } else {
+ $self->plaintext_line_warn($self,
+ __("`.' or `,' must follow \@xref"),
+ $element->{'source_info'});
+ }
+ }
+ my @added = ({'text' => '.'});
+ # The added full stop does not end a sentence. Info readers will
+ # have a chance of guessing correctly whether the full stop was
+ # added by whether it is followed by 2 spaces (although this
+ # doesn't help at the end of a line nor when a parenthesis
+ # follows the ref command).
+ push @added, {'cmdname' => ':'};
+ for my $added_element (@added) {
+ _convert($self, $added_element);
+ }
+ }
+ }
+}
sub format_node($$)
{
@@ -2762,296 +3034,7 @@ sub _convert($$)
# no args may happen with bogus @-commands without argument, maybe only
# at the end of a document
if ($element->{'args'}) {
- my @args;
- for my $arg (@{$element->{'args'}}) {
- if (defined $arg->{'contents'}) {
- push @args, $arg;
- } else {
- push @args, undef;
- }
- }
- $args[0] = {'text' => ''} if (!defined($args[0]));
-
- my $node_arg = $element->{'args'}->[0];
-
- # normalize node name, to get a ref with the right formatting
- # NOTE as a consequence, the line numbers appearing in case of errors
- # correspond to the node lines numbers, and not the @ref.
- my $label_element;
- my $target_element;
-
- my $identifiers_target;
- if ($self->{'document'}) {
- $identifiers_target = $self->{'document'}->labels_information();
- }
-
- if ($node_arg and $node_arg->{'extra'}
- and !$node_arg->{'extra'}->{'manual_content'}
- and defined($node_arg->{'extra'}->{'normalized'})
- and $identifiers_target
- and $identifiers_target->{$node_arg->{'extra'}->{'normalized'}})
{
- $target_element
- = $identifiers_target->{$node_arg->{'extra'}->{'normalized'}};
- $label_element
- = Texinfo::Common::get_label_element($target_element);
- if (defined($label_element) and !$label_element->{'contents'}) {
- $label_element = undef;
- }
- }
- if (!defined($label_element)) {
- $label_element = $args[0];
- }
-
- # if it a reference to a float with a label, $arg[1] is
- # set to '$type $number' or '$number' if there is no type.
- if (! defined($args[1])
- and $target_element and $target_element->{'cmdname'}
- and $target_element->{'cmdname'} eq 'float') {
- my $name = $self->float_type_number($target_element);
- $args[1] = $name;
- }
- if ($cmdname eq 'inforef' and scalar(@args) >= 3) {
- $args[3] = $args[2];
- $args[2] = undef;
- }
-
- # Treat cross-reference commands in a multitable cell as if they
- # were surrounded by @w{ ... }, so not to split output across
- # lines, leading text from other columns appearing to be part of the
- # cross-reference.
- my $in_multitable = 0;
- if ($self->{'document_context'}->[-1]->{'in_multitable'}) {
- $in_multitable = 1;
- $formatter->{'w'}++;
- set_space_protection($formatter->{'container'}, 1, undef)
- if ($formatter->{'w'} == 1);
- }
- # Disallow breaks in runs of Chinese text in node names, because a
- # break would be normalized to a single space by the Info reader, and
- # the node wouldn't be found.
- set_space_protection($formatter->{'container'},
- undef, undef, undef, undef, 1);
-
- if ($cmdname eq 'xref') {
- _convert($self, {'type' => '_stop_upper_case',
- 'contents' => [{'text' => '*Note '}]});
- } else {
- _convert($self, {'type' => '_stop_upper_case',
- 'contents' => [{'text' => '*note '}]});
- }
- my $name;
- if (defined($args[1])) {
- $name = $args[1];
- } elsif (defined($args[2])) {
- $name = $args[2];
- }
- my $file;
- if (defined($args[3])) {
- $file = {'contents' => [
- {'text' => '('},
- {'type' => '_stop_upper_case',
- 'contents' => [{'type' => '_code',
- 'contents' => [$args[3]]}],},
- {'text' => ')'},]};
- } elsif (defined($args[4])) {
- # add a () such that the node is considered to be external,
- # even though the manual name is not known. This should only
- # happen if a book argument is given, but no manual name.
- $file = {'text' => '()'};
- }
-
- if ($name) {
- push @{$self->{'count_context'}}, {'lines' => 0, 'bytes' => 0};
- $self->_convert($name);
- my $name_text = _stream_result($self);
- # needed, as last word is added only when : is added below
- # NB this mixes encoded and unencoded strings but is ok for
- # checking for : only
- my $name_text_checked = $name_text
- .get_pending($self->{'formatters'}->[-1]->{'container'});
- my $quoting_required = 0;
- if ($name_text_checked =~ /:/m) {
- if ($self->{'info_special_chars_warning'}) {
- $self->plaintext_line_warn($self, sprintf(__(
- "\@%s cross-reference name should not contain `:'"),
- $cmdname), $element->{'source_info'});
- }
- if ($self->{'info_special_chars_quote'}) {
- $quoting_required = 1;
- }
- }
- my $pre_quote = $quoting_required ? "\x{7f}" : '';
- my $post_quote = $pre_quote;
-
- _stream_output($self,
- add_text($formatter->{'container'}, "$post_quote: "),
- $formatter->{'container'});
- my $result = _stream_result($self);
-
- # Note post_quote has to be added first to flush output
- $result =~ s/^(\s*)/$1$pre_quote/ if $pre_quote;
-
- my $lines_added = $self->{'count_context'}->[-1]->{'lines'};
- pop @{$self->{'count_context'}};
- _stream_output_encoded($self, $result);
- $self->{'count_context'}->[-1]->{'lines'} += $lines_added;
- }
-
- if ($file) {
- _convert($self, $file);
- }
-
- my $node_name;
-
- # Get the node name to be output.
- # Due to the paragraph formatter holding pending text, converting
- # the node name with the current formatter does not yield all the
- # converted text. To get the full node name (and no more), we
- # can convert in a new context, using convert_line_new_context.
- # However, it is slow to do this for every node. So in the most
- # frequent case when the node name is a simple text element, use
- # that text instead.
- if ($label_element and $label_element->{'contents'}
- and scalar(@{$label_element->{'contents'}}) == 1
- and defined($label_element->{'contents'}->[0]->{'text'})) {
- $node_name = $label_element->{'contents'}->[0]->{'text'};
- } else {
- $self->{'silent'} = 0 if (!defined($self->{'silent'}));
- $self->{'silent'}++;
-
- ($node_name, undef) = $self->convert_line_new_context(
- {'type' => '_code',
- 'contents' => [$label_element]},
- {'suppress_styles' => 1,
- 'no_added_eol' => 1});
- $self->{'silent'}--;
- }
- if (defined($file) and $node_name !~ /\S/) {
- # Some Info reader versions, at least the Info reader from
- # Texinfo 6.8 and 7.1 cannot follow a cross-reference
- # consisting only of a manual name, such as *Note (manual)::.
- # The Emacs Info reader does not seem to have this problem.
- # Add a Top node to have a node name.
- # Should probably be removed about 10-15 years after Info
- # reader have been fixed.
- $label_element = {'text' => 'Top'};
- }
-
- my $check_chars;
- if ($name) {
- $check_chars = quotemeta ",\t.";
- } else {
- $check_chars = quotemeta ":";
- }
-
- my $quoting_required = 0;
- if ($node_name =~ /([$check_chars])/m) {
- if ($self->{'info_special_chars_warning'}) {
- $self->plaintext_line_warn($self, sprintf(__(
- "\@%s node name should not contain `%s'"), $cmdname, $1),
- $element->{'source_info'});
- }
- if ($self->{'info_special_chars_quote'}) {
- $quoting_required = 1;
- }
- }
-
- my $pre_quote = $quoting_required ? "\x{7f}" : '';
- my $post_quote = $pre_quote;
-
- # node name
- _stream_output($self,
- add_next($self->{'formatters'}->[-1]->{'container'}, $pre_quote),
- $self->{'formatters'}[-1]{'container'})
- if $pre_quote;
-
- $self->{'formatters'}->[-1]->{'suppress_styles'} = 1;
- _convert($self, {'type' => '_stop_upper_case',
- 'contents' => [
- {'type' => '_code',
- 'contents' => [$label_element]}]});
- delete $self->{'formatters'}->[-1]->{'suppress_styles'};
-
- _stream_output($self,
- add_next($self->{'formatters'}->[-1]->{'container'}, $post_quote),
- $self->{'formatters'}[-1]{'container'})
- if $post_quote;
-
- if (!$name) {
- _stream_output($self,
- add_next($self->{'formatters'}->[-1]->{'container'}, '::'),
- $self->{'formatters'}[-1]{'container'});
- }
-
- # Check if punctuation follows the ref command with a label
- # argument. If not, add a full stop.
- if ($name) {
- # Find next element
- my $next;
- my $current_contents = $element->{'parent'}->{'contents'};
- my $contents_nr = scalar(@$current_contents);
- for (my $i = 0; $i < $contents_nr - 1; $i++) {
- if ($current_contents->[$i] == $element) {
- $next = $current_contents->[$i+1];
- last;
- }
- }
-
- if (!($next and $next->{'text'}
- and $next->{'text'} =~ /^[\.,]/)) {
- # In the past, it was explicily described in the manual that
- # some punctuation was automatically added for @pxref only,
- # while the other commands required a following full stop or
- # comma.
- #
- # It is better if the user manages to find a wording with a
- # comma or full stop following naturally the ref command.
- # However, it is not possible in general except for @xref -- and
- # even for @xref it may be cumbersome. Therefore we only warn
- # that a comma or full stop is missing with @xref such that the
- # user tries to add it in that case, in the other case, we
- # automatically add a full stop without warning.
- #
- # There cannot be a perfect solution, as these issues stem from
- # the Info language design where it is not possible to
- # distinguish if punctuation used in cross reference is
- # part of the text or is added and should be considered as
markup.
- if ($cmdname eq 'xref') {
- if ($next and defined($next->{'text'})
- and $next->{'text'} =~ /\S/) {
- my $text = $next->{'text'};
- $text =~ s/^\s*//;
- my $char = substr($text, 0, 1);
- $self->plaintext_line_warn($self, sprintf(__(
- "`.' or `,' must follow \@xref, not %s"),
- $char), $element->{'source_info'});
- } else {
- $self->plaintext_line_warn($self,
- __("`.' or `,' must follow \@xref"),
- $element->{'source_info'});
- }
- }
- my @added = ({'text' => '.'});
- # The added full stop does not end a sentence. Info readers will
- # have a chance of guessing correctly whether the full stop was
- # added by whether it is followed by 2 spaces (although this
- # doesn't help at the end of a line nor when a parenthesis
- # follows the ref command).
- push @added, {'cmdname' => ':'};
- for my $added_element (@added) {
- _convert($self, $added_element);
- }
- }
- }
-
- if ($in_multitable) {
- $formatter->{'w'}--;
- set_space_protection($formatter->{'container'}, 0, undef)
- if ($formatter->{'w'} == 0);
- }
- set_space_protection($formatter->{'container'},
- undef,undef,undef,undef,0); # double_width_no_break
- return;
+ $self->format_ref($cmdname, $element, $formatter);
}
return;
} elsif ($cmdname eq 'image') {
diff --git a/tp/t/results/multitable/ref_in_multitable.pl
b/tp/t/results/multitable/ref_in_multitable.pl
index 936e40e2ae..cd0f1a9880 100644
--- a/tp/t/results/multitable/ref_in_multitable.pl
+++ b/tp/t/results/multitable/ref_in_multitable.pl
@@ -538,11 +538,11 @@ $result_floats{'ref_in_multitable'} = {};
$result_converted{'plaintext'}->{'ref_in_multitable'} = '*note XXX XXX XXX XXX
XXX XXX XXX XXX XXX XXX XXX XXX XX XXX XXX XXX XXX
XXX XXX XXX XXX XXX XXX XXX XXX XX::.
-XXX XXX XXX XXX XXX XXX See
-XXX XXX XXX XXX XXX XXX *note RRR RRR RRR RRR RRR RRR RRR RRR RRR RRRR::.
+XXX XXX XXX XXX XXX XXX See *note RRR RRR RRR RRR RRR RRR RRR RRR RRR
+XXX XXX XXX XXX XXX XXX RRRR::.
XX
-XXX XXX XXX XXX XXX XXX See
-XXX XXX XXX XXX XXX XXX *note SSS SSS SSS SSS SSS SSS SSS SSS SSS SSS SSS
SSS SSSSS::.
+XXX XXX XXX XXX XXX XXX See *note SSS SSS SSS SSS SSS SSS SSS SSS SSS
+XXX XXX XXX XXX XXX XXX SSS SSS SSS SSSSS::.
XX
adsf(1) second column
- master updated (3ee7ac39a8 -> 5ec100b947), Patrice Dumas, 2024/09/29
- [no subject], Patrice Dumas, 2024/09/29
- [no subject],
Patrice Dumas <=
- [no subject], Patrice Dumas, 2024/09/29
- [no subject], Patrice Dumas, 2024/09/29
- [no subject], Patrice Dumas, 2024/09/29
- [no subject], Patrice Dumas, 2024/09/29
- [no subject], Patrice Dumas, 2024/09/29
- [no subject], Patrice Dumas, 2024/09/29