mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Cite
synced 2024-11-27 16:30:12 +00:00
Can use extends
before its parent
If `extends` is encountered before the parent ref, we reserve the sequence number and leave a placeholder to record the link between ref name and number. This is necessary to render a list like, "[1] [2.1] [2]", or to use subreferencing when the parent ref is declared in the references tag. When a placeholder is encountered during references section rendering, it means that the parent was never declared. Change-Id: I611cd1d73f775908926a803fae90d039ce122ab6
This commit is contained in:
parent
a27c33a2e7
commit
008526b3aa
|
@ -69,9 +69,16 @@ class FootnoteBodyFormatter {
|
|||
// Note: This builds a string of wikitext, not html.
|
||||
$parserInput = "\n";
|
||||
$indented = false;
|
||||
// After sorting the list, we can assume that references are in the same order as their
|
||||
// numbering. Subreferences will come immediately after their parent.
|
||||
uasort(
|
||||
$groupRefs,
|
||||
function ( array $a, array $b ) : int {
|
||||
return ( $a['number'] ?? '' ) <=> ( $b['number'] ?? '' ) ?:
|
||||
( $a['extendsIndex'] ?? '0' ) <=> ( $b['extendsIndex'] ?? '0' );
|
||||
}
|
||||
);
|
||||
foreach ( $groupRefs as $key => $value ) {
|
||||
// FIXME: This assumes extended references appear immediately after their parent in the
|
||||
// data structure. Reorder the refs according to their stored numbering.
|
||||
if ( !$indented && isset( $value['extends'] ) ) {
|
||||
// The nested <ol> must be inside the parent's <li>
|
||||
if ( preg_match( '#</li>\s*$#D', $parserInput, $matches, PREG_OFFSET_CAPTURE ) ) {
|
||||
|
@ -125,10 +132,13 @@ class FootnoteBodyFormatter {
|
|||
* @return string Wikitext, wrapped in a single <li> element
|
||||
*/
|
||||
private function referencesFormatEntry( $key, array $val, bool $isSectionPreview ) : string {
|
||||
$text = $this->referenceText( $key, $val['text'], $isSectionPreview );
|
||||
$text = $this->referenceText( $key, ( $val['text'] ?? null ), $isSectionPreview );
|
||||
$error = '';
|
||||
$extraAttributes = '';
|
||||
|
||||
// TODO: Show an error if isset( $val['__placeholder__'] ), this is caused by extends
|
||||
// with a missing parent.
|
||||
|
||||
if ( isset( $val['dir'] ) ) {
|
||||
$dir = strtolower( $val['dir'] );
|
||||
if ( in_array( $dir, [ 'ltr', 'rtl' ] ) ) {
|
||||
|
|
|
@ -169,6 +169,13 @@ class ReferenceStack {
|
|||
// This is an anonymous reference, which will be given a numeric index.
|
||||
$this->refs[$group][] = &$ref;
|
||||
$action = 'new';
|
||||
} elseif ( isset( $this->refs[$group][$name]['__placeholder__'] ) ) {
|
||||
// Populate a placeholder.
|
||||
unset( $this->refs[$group][$name]['__placeholder__'] );
|
||||
unset( $ref['number'] );
|
||||
$ref = array_merge( $ref, $this->refs[$group][$name] );
|
||||
$this->refs[$group][$name] =& $ref;
|
||||
$action = 'new';
|
||||
} elseif ( !isset( $this->refs[$group][$name] ) ) {
|
||||
// Valid key with first occurrence
|
||||
$this->refs[$group][$name] = &$ref;
|
||||
|
@ -205,18 +212,22 @@ class ReferenceStack {
|
|||
$ref['number'] = $ref['number'] ?? ++$this->groupRefSequence[$group];
|
||||
|
||||
if ( $extends ) {
|
||||
if ( isset( $this->refs[$group][$extends] ) ) {
|
||||
$this->extendsCount[$group][$extends] =
|
||||
( $this->extendsCount[$group][$extends] ?? 0 ) + 1;
|
||||
$this->extendsCount[$group][$extends] =
|
||||
( $this->extendsCount[$group][$extends] ?? 0 ) + 1;
|
||||
|
||||
$ref['extends'] = $extends;
|
||||
$ref['extendsIndex'] = $this->extendsCount[$group][$extends];
|
||||
$ref['extends'] = $extends;
|
||||
$ref['extendsIndex'] = $this->extendsCount[$group][$extends];
|
||||
|
||||
if ( isset( $this->refs[$group][$extends]['number'] ) ) {
|
||||
// Adopt the parent's number.
|
||||
// TODO: Do we need to roll back the group ref sequence here?
|
||||
$ref['number'] = $this->refs[$group][$extends]['number'];
|
||||
} else {
|
||||
// TODO: check parent existence in a second, pre-render stage of validation.
|
||||
// This should be an error, not silent degradation.
|
||||
$extends = null;
|
||||
// Transfer my number to parent ref.
|
||||
$this->refs[$group][$extends] = [
|
||||
'number' => $ref['number'],
|
||||
'__placeholder__' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,8 +85,6 @@ wgCiteBookReferencing=true
|
|||
</ol></div>
|
||||
!! end
|
||||
|
||||
# TODO:
|
||||
# * Should render an error.
|
||||
!! test
|
||||
T236256 - Extending a reference that doesn't exist #1
|
||||
!! config
|
||||
|
@ -94,14 +92,14 @@ wgCiteBookReferencing=true
|
|||
!! wikitext
|
||||
<ref extends="a">page 1</ref>
|
||||
!! html/php
|
||||
<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup><div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">page 1</span>
|
||||
<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1.1]</a></sup><div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-a-"><span class="mw-cite-backlink">↑ </span> <span class="error mw-ext-cite-error" lang="en" dir="ltr">Cite error: Invalid <code><ref></code> tag;
|
||||
no text was provided for refs named <code>a</code></span><ol class="mw-extended-references"><li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">page 1</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
</ol></div>
|
||||
!! end
|
||||
|
||||
# TODO:
|
||||
# * Should render an error at the second reference.
|
||||
!! test
|
||||
T236256 - Extending a reference that doesn't exist #2
|
||||
!! config
|
||||
|
@ -112,11 +110,13 @@ wgCiteBookReferencing=true
|
|||
!! html/php
|
||||
<p><sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
|
||||
</p>
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[2]</a></sup><div class="mw-references-wrap"><ol class="references">
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[2.1]</a></sup><div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">book</span>
|
||||
</li>
|
||||
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page 1</span>
|
||||
<li id="cite_note-a-"><span class="mw-cite-backlink">↑ </span> <span class="error mw-ext-cite-error" lang="en" dir="ltr">Cite error: Invalid <code><ref></code> tag;
|
||||
no text was provided for refs named <code>a</code></span><ol class="mw-extended-references"><li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page 1</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
</ol></div>
|
||||
!! end
|
||||
|
||||
|
@ -278,8 +278,6 @@ wgCiteBookReferencing=true
|
|||
</ol></div>
|
||||
!! end
|
||||
|
||||
# TODO:
|
||||
# * Should render an error at the second reference.
|
||||
!! test
|
||||
T236256 - Base and book reference in different groups
|
||||
!! config
|
||||
|
@ -291,20 +289,20 @@ wgCiteBookReferencing=true
|
|||
<references group="g2"/>
|
||||
!! html/php
|
||||
<p><sup id="cite_ref-a_1-0" class="reference"><a href="#cite_note-a-1">[g1 1]</a></sup>
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[g2 1]</a></sup>
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[g2 1.1]</a></sup>
|
||||
</p>
|
||||
<div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-a-1"><span class="mw-cite-backlink"><a href="#cite_ref-a_1-0">↑</a></span> <span class="reference-text">book</span>
|
||||
</li>
|
||||
</ol></div>
|
||||
<div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page</span>
|
||||
<li id="cite_note-a-"><span class="mw-cite-backlink">↑ </span> <span class="error mw-ext-cite-error" lang="en" dir="ltr">Cite error: Invalid <code><ref></code> tag;
|
||||
no text was provided for refs named <code>a</code></span><ol class="mw-extended-references"><li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
</ol></div>
|
||||
!! end
|
||||
|
||||
# TODO:
|
||||
# * Should render an error at the second reference.
|
||||
!! test
|
||||
T236256 - Extending in the unnamed default group
|
||||
!! config
|
||||
|
@ -315,19 +313,19 @@ wgCiteBookReferencing=true
|
|||
<references group="g1"/>
|
||||
!! html/php
|
||||
<p><sup id="cite_ref-a_1-0" class="reference"><a href="#cite_note-a-1">[g1 1]</a></sup>
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[1]</a></sup>
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[1.1]</a></sup>
|
||||
</p>
|
||||
<div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-a-1"><span class="mw-cite-backlink"><a href="#cite_ref-a_1-0">↑</a></span> <span class="reference-text">book</span>
|
||||
</li>
|
||||
</ol></div><div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page</span>
|
||||
<li id="cite_note-a-"><span class="mw-cite-backlink">↑ </span> <span class="error mw-ext-cite-error" lang="en" dir="ltr">Cite error: Invalid <code><ref></code> tag;
|
||||
no text was provided for refs named <code>a</code></span><ol class="mw-extended-references"><li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
</ol></div>
|
||||
!! end
|
||||
|
||||
# TODO:
|
||||
# * Should render an error at the second reference.
|
||||
!! test
|
||||
T236256 - Base in the unnamed default group
|
||||
!! config
|
||||
|
@ -338,11 +336,13 @@ wgCiteBookReferencing=true
|
|||
<references group="g1"/>
|
||||
!! html/php
|
||||
<p><sup id="cite_ref-a_1-0" class="reference"><a href="#cite_note-a-1">[1]</a></sup>
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[g1 1]</a></sup>
|
||||
<sup id="cite_ref-2" class="reference"><a href="#cite_note-2">[g1 1.1]</a></sup>
|
||||
</p>
|
||||
<div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page</span>
|
||||
<li id="cite_note-a-"><span class="mw-cite-backlink">↑ </span> <span class="error mw-ext-cite-error" lang="en" dir="ltr">Cite error: Invalid <code><ref></code> tag;
|
||||
no text was provided for refs named <code>a</code></span><ol class="mw-extended-references"><li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">page</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
</ol></div><div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-a-1"><span class="mw-cite-backlink"><a href="#cite_ref-a_1-0">↑</a></span> <span class="reference-text">book</span>
|
||||
</li>
|
||||
|
@ -439,6 +439,8 @@ invalid names, e.g. too many</span>
|
|||
</ol></div>
|
||||
!! end
|
||||
|
||||
# TODO
|
||||
# * This should be invalid, there's no way to know that `footwo` should be subnumbered in a one-pass parse.
|
||||
!! test
|
||||
Extends <ref> defined in <references> called with #tag
|
||||
!! wikitext
|
||||
|
@ -460,6 +462,58 @@ Extends <ref> defined in <references> called with #tag
|
|||
</ol></div>
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Extends parent <ref> defined in <references> called with #tag
|
||||
!! wikitext
|
||||
<ref>BAR</ref>
|
||||
<ref name="footwo" extends="foo">page 7</ref>
|
||||
<ref name="foo" />
|
||||
|
||||
{{#tag:references|
|
||||
<ref name="foo">book name</ref>
|
||||
}}
|
||||
!! html
|
||||
<p><sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>
|
||||
<sup id="cite_ref-footwo_2-0" class="reference"><a href="#cite_note-footwo-2">[2.1]</a></sup>
|
||||
<sup id="cite_ref-foo_3-0" class="reference"><a href="#cite_note-foo-3">[2]</a></sup>
|
||||
</p>
|
||||
<div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-1"><span class="mw-cite-backlink"><a href="#cite_ref-1">↑</a></span> <span class="reference-text">BAR</span>
|
||||
</li>
|
||||
<li id="cite_note-foo-3"><span class="mw-cite-backlink"><a href="#cite_ref-foo_3-0">↑</a></span> <span class="reference-text">book name</span>
|
||||
<ol class="mw-extended-references"><li id="cite_note-footwo-2"><span class="mw-cite-backlink"><a href="#cite_ref-footwo_2-0">↑</a></span> <span class="reference-text">page 7</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
</ol></div>
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Interleaved extends groups
|
||||
!! wikitext
|
||||
<ref name="a">text-a</ref>
|
||||
<ref name="c">text-c</ref>
|
||||
|
||||
<ref extends="a">page b</ref>
|
||||
<ref extends="c">page d</ref>
|
||||
<references />
|
||||
!! html
|
||||
<p><sup id="cite_ref-a_1-0" class="reference"><a href="#cite_note-a-1">[1]</a></sup>
|
||||
<sup id="cite_ref-c_2-0" class="reference"><a href="#cite_note-c-2">[2]</a></sup>
|
||||
</p><p><sup id="cite_ref-3" class="reference"><a href="#cite_note-3">[1.1]</a></sup>
|
||||
<sup id="cite_ref-4" class="reference"><a href="#cite_note-4">[2.1]</a></sup>
|
||||
</p>
|
||||
<div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-a-1"><span class="mw-cite-backlink"><a href="#cite_ref-a_1-0">↑</a></span> <span class="reference-text">text-a</span>
|
||||
<ol class="mw-extended-references"><li id="cite_note-3"><span class="mw-cite-backlink"><a href="#cite_ref-3">↑</a></span> <span class="reference-text">page b</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
<li id="cite_note-c-2"><span class="mw-cite-backlink"><a href="#cite_ref-c_2-0">↑</a></span> <span class="reference-text">text-c</span>
|
||||
<ol class="mw-extended-references"><li id="cite_note-4"><span class="mw-cite-backlink"><a href="#cite_ref-4">↑</a></span> <span class="reference-text">page d</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
</ol></div>
|
||||
!! end
|
||||
|
||||
!! test
|
||||
Multiple groups with extends
|
||||
!! wikitext
|
||||
|
@ -530,6 +584,7 @@ wgCiteBookReferencing=true
|
|||
<ref name="a">book</ref>
|
||||
<ref>another</ref>
|
||||
<ref extends="a">page 2</ref>
|
||||
|
||||
<references />
|
||||
!! html/php
|
||||
<p><sup id="cite_ref-a_1-0" class="reference"><a href="#cite_note-a-1">[1]</a></sup>
|
||||
|
@ -538,10 +593,10 @@ wgCiteBookReferencing=true
|
|||
</p>
|
||||
<div class="mw-references-wrap"><ol class="references">
|
||||
<li id="cite_note-a-1"><span class="mw-cite-backlink"><a href="#cite_ref-a_1-0">↑</a></span> <span class="reference-text">book</span>
|
||||
</li>
|
||||
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">another</span>
|
||||
<ol class="mw-extended-references"><li id="cite_note-3"><span class="mw-cite-backlink"><a href="#cite_ref-3">↑</a></span> <span class="reference-text">page 2</span>
|
||||
</li>
|
||||
</ol></li>
|
||||
<li id="cite_note-2"><span class="mw-cite-backlink"><a href="#cite_ref-2">↑</a></span> <span class="reference-text">another</span>
|
||||
</li>
|
||||
</ol></div>
|
||||
!! end
|
||||
|
|
|
@ -423,6 +423,8 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => 'a',
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
],
|
||||
],
|
||||
[
|
||||
|
@ -434,11 +436,17 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => 'a',
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
],
|
||||
'b' => [
|
||||
'number' => 1,
|
||||
'__placeholder__' => true,
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
[ 'new', [], 'text-a', 'a', null, 'foo', 1 ],
|
||||
[ 'new', [], 'text-a', 'a', 'b', 'foo', 1 ],
|
||||
]
|
||||
],
|
||||
'Named extends before parent' => [
|
||||
|
@ -454,6 +462,8 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => 'a',
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
],
|
||||
[
|
||||
'count' => 0,
|
||||
|
@ -461,7 +471,7 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'key' => 2,
|
||||
'name' => 'b',
|
||||
'text' => 'text-b',
|
||||
'number' => 2,
|
||||
'number' => 1,
|
||||
]
|
||||
],
|
||||
[
|
||||
|
@ -473,6 +483,8 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => 'a',
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
],
|
||||
'b' => [
|
||||
'count' => 0,
|
||||
|
@ -480,12 +492,12 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'key' => 2,
|
||||
'name' => 'b',
|
||||
'text' => 'text-b',
|
||||
'number' => 2,
|
||||
'number' => 1,
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
[ 'new', [], 'text-a', 'a', null, 'foo', 1 ],
|
||||
[ 'new', [], 'text-a', 'a', 'b', 'foo', 1 ],
|
||||
[ 'new', [], 'text-b', 'b', null, 'foo', 2 ],
|
||||
]
|
||||
],
|
||||
|
@ -553,6 +565,8 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => null,
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
]
|
||||
],
|
||||
[
|
||||
|
@ -564,11 +578,17 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => null,
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
],
|
||||
]
|
||||
'b' => [
|
||||
'number' => 1,
|
||||
'__placeholder__' => true,
|
||||
]
|
||||
],
|
||||
],
|
||||
[
|
||||
[ 'new', [], 'text-a', null, null, 'foo', 1 ],
|
||||
[ 'new', [], 'text-a', null, 'b', 'foo', 1 ],
|
||||
]
|
||||
],
|
||||
'Anonymous extends before parent' => [
|
||||
|
@ -584,6 +604,8 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => null,
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
],
|
||||
[
|
||||
'count' => 0,
|
||||
|
@ -591,7 +613,7 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'key' => 2,
|
||||
'name' => 'b',
|
||||
'text' => 'text-b',
|
||||
'number' => 2,
|
||||
'number' => 1,
|
||||
]
|
||||
],
|
||||
[
|
||||
|
@ -603,6 +625,8 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'name' => null,
|
||||
'text' => 'text-a',
|
||||
'number' => 1,
|
||||
'extends' => 'b',
|
||||
'extendsIndex' => 1,
|
||||
],
|
||||
'b' => [
|
||||
'count' => 0,
|
||||
|
@ -610,12 +634,12 @@ class ReferenceStackTest extends MediaWikiUnitTestCase {
|
|||
'key' => 2,
|
||||
'name' => 'b',
|
||||
'text' => 'text-b',
|
||||
'number' => 2,
|
||||
'number' => 1,
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
[ 'new', [], 'text-a', null, null, 'foo', 1 ],
|
||||
[ 'new', [], 'text-a', null, 'b', 'foo', 1 ],
|
||||
[ 'new', [], 'text-b', 'b', null, 'foo', 2 ],
|
||||
]
|
||||
],
|
||||
|
|
Loading…
Reference in a new issue