Expose the footnote mark formatter label method

Behavior change: previously, an error would be rendered once the
custom markers run out. After this patch there is a graceful fallback
to default group rendering (eg. "lower-greek 1000").
This is a slight improvement, but is user-facing so should be
discussed before merging.

In future work we'll render custom marks programmatically so this edge
case would be unreachable, and since the error message only exists to
nudge editors to extend the custom group symbol sequence, this would
also become wasted effort.

This patch splits out a lower-level method which produces the bare
mark label, with no link or wikitext formatting.  The patch narrows
and simplifies the interface so that the method can be made available
to Parsoid, and will be converted to a service in a separate patch.

Bug: T377454
Change-Id: I719b60b46cdef0be7463d76e9125d75ab4f333ae
This commit is contained in:
Adam Wight 2024-10-23 11:29:14 +02:00
parent 873bee7e20
commit 87d1229bf2
5 changed files with 19 additions and 45 deletions

View file

@ -28,7 +28,6 @@
"cite_error_references_duplicate_key": "Invalid <code>&lt;ref&gt;</code> tag; name \"$1\" defined multiple times with different content",
"cite_error_references_invalid_parameters": "Invalid parameter in <code>&lt;references&gt;</code> tag",
"cite_error_references_no_backlink_label": "Ran out of custom backlink labels. Define more in the [[MediaWiki:Cite references link many format backlink labels]] message.",
"cite_error_no_link_label_group": "Ran out of custom link labels for group \"$1\". Define more in the [[MediaWiki:$2]] message.",
"cite_error_references_no_text": "Invalid <code>&lt;ref&gt;</code> tag; no text was provided for refs named <code>$1</code>",
"cite_error_included_ref": "Closing <code>&lt;/ref&gt;</code> missing for <code>&lt;ref&gt;</code> tag",
"cite_error_included_references": "Closing tag missing for <code>&lt;references&gt;</code>",

View file

@ -33,7 +33,6 @@
"cite_error_references_duplicate_key": "Error message shown when multiple refs with same name exist but with different content",
"cite_error_references_invalid_parameters": "Generic error message shown when unknown, unsupported parameters are used in a <code>&lt;references&gt;</code> tag. This can happen when there is a mistake in a parameter name, or a parameter exclusive to the <code>&lt;ref&gt;</code> tag is used in a <code>&lt;references&gt;</code> tag. This and [[MediaWiki:Cite error ref too many keys]] form a pair and should be worded similarly.",
"cite_error_references_no_backlink_label": "Error message shown in the references tag when the same name is used for too many ref tags. Too many in this case is more than there are backlink labels defined in [[MediaWiki:Cite references link many format backlink labels]].\n\nIt is not possible to make a clickable link to this message. \"nowiki\" is mandatory around [[MediaWiki:Cite references link many format backlink labels]].",
"cite_error_no_link_label_group": "Error message shown when there are more references than custom link markers for a group. Gives an actionable remedy.\n\nParameters:\n* $1 - reference group name\n* $2 - System message name, <code>cite_link_label_group-<i>groupname</i></code>",
"cite_error_references_no_text": "This error occurs when the tag <code>&lt;ref name=\"something\"/&gt;</code> is used with the name-option specified and no other tag specifies a cite-text for this name.\n\nParameters:\n* $1 - key of the ref",
"cite_error_included_ref": "Error message shown if the <code>&lt;ref&gt;</code> tag is unbalanced, that means a <code>&lt;ref&gt;</code> is not followed by a <code>&lt;/ref&gt;</code>",
"cite_error_included_references": "Error message shown if a <code>&lt;references&gt;</code> tag is found inside another.",

View file

@ -78,7 +78,6 @@ class Cite {
$this->referenceStack = new ReferenceStack();
$anchorFormatter = new AnchorFormatter();
$this->footnoteMarkFormatter = new FootnoteMarkFormatter(
$this->errorReporter,
$anchorFormatter,
$messageLocalizer
);

View file

@ -17,16 +17,13 @@ class FootnoteMarkFormatter {
private array $linkLabels = [];
private AnchorFormatter $anchorFormatter;
private ErrorReporter $errorReporter;
private ReferenceMessageLocalizer $messageLocalizer;
public function __construct(
ErrorReporter $errorReporter,
AnchorFormatter $anchorFormatter,
ReferenceMessageLocalizer $messageLocalizer
) {
$this->anchorFormatter = $anchorFormatter;
$this->errorReporter = $errorReporter;
$this->messageLocalizer = $messageLocalizer;
}
@ -41,7 +38,7 @@ class FootnoteMarkFormatter {
* @return string HTML
*/
public function linkRef( Parser $parser, ReferenceStackItem $ref ): string {
$label = $this->makeLabel( $ref, $parser );
$label = $this->makeLabel( $ref->group, $ref->number, $ref->extendsIndex );
$key = $ref->name ?? $ref->key;
// TODO: Use count without decrementing.
@ -58,18 +55,19 @@ class FootnoteMarkFormatter {
);
}
private function makeLabel( ReferenceStackItem $ref, Parser $parser ): string {
$label = $this->fetchCustomizedLinkLabel( $parser, $ref->group, $ref->number ) ??
$this->makeDefaultLabel( $ref );
if ( $ref->extendsIndex !== null ) {
$label .= '.' . $this->messageLocalizer->localizeDigits( (string)$ref->extendsIndex );
public function makeLabel( string $group, int $number, ?int $extendsIndex = null ): string {
$label = $this->fetchCustomizedLinkLabel( $group, $number ) ??
$this->makeDefaultLabel( $group, $number );
if ( $extendsIndex !== null ) {
// TODO: design better behavior, especially when using custom group markers.
$label .= '.' . $this->messageLocalizer->localizeDigits( (string)$extendsIndex );
}
return $label;
}
private function makeDefaultLabel( ReferenceStackItem $ref ): string {
$label = $this->messageLocalizer->localizeDigits( (string)$ref->number );
return $ref->group === Cite::DEFAULT_GROUP ? $label : "$ref->group $label";
public function makeDefaultLabel( string $group, int $number ): string {
$label = $this->messageLocalizer->localizeDigits( (string)$number );
return $group === Cite::DEFAULT_GROUP ? $label : "$group $label";
}
/**
@ -78,13 +76,12 @@ class FootnoteMarkFormatter {
* [ 'a', 'b', 'c', ...].
* Return an error if the offset > the # of array items
*
* @param Parser $parser
* @param string $group The group name
* @param int $number Expected to start at 1
* @param string $group
* @param int $number
*
* @return string|null Returns null if no custom labels for this group exist
* @return ?string Returns null if no custom label can be found
*/
private function fetchCustomizedLinkLabel( Parser $parser, string $group, int $number ): ?string {
private function fetchCustomizedLinkLabel( string $group, int $number ): ?string {
if ( $group === Cite::DEFAULT_GROUP ) {
return null;
}
@ -101,12 +98,7 @@ class FootnoteMarkFormatter {
}
// Error message in case we run out of custom labels
return $this->linkLabels[$group][$number - 1] ?? $this->errorReporter->plain(
$parser,
'cite_error_no_link_label_group',
$group,
$message
);
return $this->linkLabels[$group][$number - 1] ?? null;
}
}

View file

@ -4,7 +4,6 @@ namespace Cite\Tests\Integration;
use Cite\AnchorFormatter;
use Cite\Cite;
use Cite\ErrorReporter;
use Cite\FootnoteMarkFormatter;
use Cite\ReferenceMessageLocalizer;
use Cite\Tests\TestUtils;
@ -22,10 +21,6 @@ class FootnoteMarkFormatterTest extends \MediaWikiIntegrationTestCase {
* @dataProvider provideLinkRef
*/
public function testLinkRef( array $ref, string $expectedOutput ) {
$mockErrorReporter = $this->createMock( ErrorReporter::class );
$mockErrorReporter->method( 'plain' )->willReturnCallback(
static fn ( $parser, ...$args ) => implode( '|', $args )
);
$anchorFormatter = $this->createMock( AnchorFormatter::class );
$anchorFormatter->method( 'jumpLink' )->willReturnArgument( 0 );
$anchorFormatter->method( 'backLinkTarget' )->willReturnCallback(
@ -49,7 +44,6 @@ class FootnoteMarkFormatterTest extends \MediaWikiIntegrationTestCase {
$mockParser = $this->createNoOpMock( Parser::class, [ 'recursiveTagParse' ] );
$mockParser->method( 'recursiveTagParse' )->willReturnArgument( 0 );
$formatter = new FootnoteMarkFormatter(
$mockErrorReporter,
$anchorFormatter,
$messageLocalizer
);
@ -95,8 +89,7 @@ class FootnoteMarkFormatterTest extends \MediaWikiIntegrationTestCase {
'number' => 10,
'key' => 4,
],
'(cite_reference_link|4+|4|' .
'cite_error_no_link_label_group&#124;foo&#124;cite_link_label_group-foo)'
'(cite_reference_link|4+|4|foo 10)'
],
'Named ref' => [
[
@ -145,19 +138,13 @@ class FootnoteMarkFormatterTest extends \MediaWikiIntegrationTestCase {
return $msg;
}
);
$mockErrorReporter = $this->createMock( ErrorReporter::class );
$mockErrorReporter->method( 'plain' )->willReturnCallback(
static fn ( $parser, ...$args ) => implode( '|', $args )
);
/** @var FootnoteMarkFormatter $formatter */
$formatter = TestingAccessWrapper::newFromObject( new FootnoteMarkFormatter(
$mockErrorReporter,
$this->createMock( AnchorFormatter::class ),
$mockMessageLocalizer
) );
$parser = $this->createNoOpMock( Parser::class );
$output = $formatter->fetchCustomizedLinkLabel( $parser, $group, $offset );
$output = $formatter->fetchCustomizedLinkLabel( $group, $offset );
$this->assertSame( $expectedLabel, $output );
}
@ -169,20 +156,18 @@ class FootnoteMarkFormatterTest extends \MediaWikiIntegrationTestCase {
yield [ 'a', 1, 'foo', 'a b c' ];
yield [ 'b', 2, 'foo', 'a b c' ];
yield [ 'å', 1, 'foo', 'å β' ];
yield [ 'cite_error_no_link_label_group|foo|cite_link_label_group-foo', 4, 'foo', 'a b c' ];
yield [ null, 4, 'foo', 'a b c' ];
}
public function testDefaultGroupCannotHaveCustomLinkLabels() {
/** @var FootnoteMarkFormatter $formatter */
$formatter = TestingAccessWrapper::newFromObject( new FootnoteMarkFormatter(
$this->createNoOpMock( ErrorReporter::class ),
$this->createNoOpMock( AnchorFormatter::class ),
// Assert that ReferenceMessageLocalizer::msg( 'cite_link_label_group-' ) isn't called
$this->createNoOpMock( ReferenceMessageLocalizer::class )
) );
$parser = $this->createNoOpMock( Parser::class );
$this->assertNull( $formatter->fetchCustomizedLinkLabel( $parser, Cite::DEFAULT_GROUP, 1 ) );
$this->assertNull( $formatter->fetchCustomizedLinkLabel( Cite::DEFAULT_GROUP, 1 ) );
}
}