Similar to T385666: Show references with the same details as re-use in the legacy rendering (Merge Use Case), specifically the small change in ReferenceStack.php.
User story:
As reader I want to see sub-references with the same main content and the same additional details content as re-uses of one sub-reference.
Acceptance criteria:
- We want to merge sub-reference by comparing their Wikitext content similar to how it is done when two refs with the same name are "merged" if the content is identical
- Duplicates should be merged in the reference list, and get the same number (e.g. [1.1]) in their corresponding footnote markers.
- Since, we are aiming to roll this out without Parsoid, before we deploy we will need to discuss with Content Transform Team a solution to tweak tooling to ignore the visual diffs.
- Mobile app rendering should be reasonable and ref previews must still work.
Parsoid -> VE data migration
Change to the document rendered by Parsoid
Wikitext (example page):
<ref name="main">Book</ref> <ref name="main" details="page 1" /> <ref name="main" details="page 1" />
Current Parsoid rendering:
<sup class="mw-ref reference" id="cite_ref-main_1-0" typeof="mw:Extension/ref"
data-mw="{"name":"ref","attrs":{"name":"main"},"body":{"id":"mw-reference-text-cite_note-main-1"}}">
<a href="#cite_note-main-1">
<span class="mw-reflink-text">[1]</span>
</a>
</sup>
<sup class="mw-ref reference" id="cite_ref-2" typeof="mw:Extension/ref"
data-mw="{"name":"ref","attrs":{"name":"main","details":"page 1"},"mainRef":"main","body":{"id":"mw-reference-text-cite_note-2"}}">
<a href="#cite_note-2">
<span class="mw-reflink-text">[1.1]</span></a></sup>
<sup class="mw-ref reference" id="cite_ref-3" typeof="mw:Extension/ref"
data-mw="{"name":"ref","attrs":{"name":"main","details":"page 1"},"mainRef":"main","body":{"id":"mw-reference-text-cite_note-3"}}">
<a href="#cite_note-3">
<span class="mw-reflink-text">[1.2]</span>
</a>
</sup>
<div class="mw-references-wrap" typeof="mw:Extension/references"
data-mw="{"name":"references","attrs":{},"autoGenerated":true,"body":{"html":"\n<sup typeof=\"mw:Extension/ref\" data-parsoid=\"{}\" data-mw='{\"name\":\"ref\",\"attrs\":{\"name\":\"main\",\"group\":\"\"},\"body\":{\"id\":\"mw-reference-text-cite_note-main-1\"},\"isSyntheticMainRef\":1}'>Book</sup>"}}">
<ol class="mw-references references">
<li id="cite_note-main-1" data-mw-footnote-number="1">
<span class="mw-cite-backlink">...</span>
<span id="mw-reference-text-cite_note-main-1"
class="mw-reference-text reference-text">Book</span>
<ol class="mw-subreference-list">
<li id="cite_note-2" data-mw-footnote-number="1.1">
<span class="mw-cite-backlink">...</span>
<span id="mw-reference-text-cite_note-2"
class="mw-reference-text reference-text">page 1</span>
</li>
<li id="cite_note-3" data-mw-footnote-number="1.2">
<span class="mw-cite-backlink">...</span>
<span id="mw-reference-text-cite_note-3"
class="mw-reference-text reference-text">page 1</span>
</li>
</ol></li>
</ol>
</div>New Parsoid rendering (assumption):
<sup class="mw-ref reference" id="cite_ref-main_1-0" typeof="mw:Extension/ref"
data-mw="{"name":"ref","attrs":{"name":"main"},"body":{"id":"mw-reference-text-cite_note-main-1"}}">
<a href="#cite_note-main-1">
<span class="mw-reflink-text">[1]</span>
</a>
</sup>
<sup class="mw-ref reference" id="cite_ref-2" typeof="mw:Extension/ref"
data-mw="{"name":"ref","attrs":{"name":"main","details":"page 1"},"mainRef":"main","body":{"id":"mw-reference-text-cite_note-2"}}">
<a href="#cite_note-2">
<span class="mw-reflink-text">[1.1]</span></a></sup>
<sup class="mw-ref reference" id="cite_ref-3" typeof="mw:Extension/ref"
data-mw="{"name":"ref","attrs":{"name":"main","details":"page 1"},"mainRef":"main","body":{"id":"mw-reference-text-cite_note-2"}}">
<a href="#cite_note-2">
<span class="mw-reflink-text">[1.1]</span>
</a>
</sup>
<div class="mw-references-wrap" typeof="mw:Extension/references"
data-mw="{"name":"references","attrs":{},"autoGenerated":true,"body":{"html":"\n<sup typeof=\"mw:Extension/ref\" data-parsoid=\"{}\" data-mw='{\"name\":\"ref\",\"attrs\":{\"name\":\"main\",\"group\":\"\"},\"body\":{\"id\":\"mw-reference-text-cite_note-main-1\"},\"isSyntheticMainRef\":1}'>Book</sup>"}}">
<ol class="mw-references references">
<li id="cite_note-main-1" data-mw-footnote-number="1">
<span class="mw-cite-backlink">...</span>
<span id="mw-reference-text-cite_note-main-1"
class="mw-reference-text reference-text">Book</span>
<ol class="mw-subreference-list">
<li id="cite_note-2" data-mw-footnote-number="1.1">
<span class="mw-cite-backlink">...</span>
<span id="mw-reference-text-cite_note-2"
class="mw-reference-text reference-text">page 1</span>
</li>
</ol></li>
</ol>
</div>Impact on VE
To investigate. We should probably create a lookup for each main ref, which lists its subrefs by reference list item ID. When two subrefs have the same refListItemId, they should share the same InternalList listIndex.
Attributes should be the same for deduplicated refs, and different for undeduplicated refs. If VE is not deduplicating, none of these attributes can overlap between subrefs, in other words we should never have a mix of same and different attributes.
- listKey
- listIndex
- data-mw.body.id
- mainBody
Mock
Next steps
- Discuss if this can be done independently of according VE changes
Yes, Parsoid and VE changes are independent of each other. Tested with 1202996: Parsoid: Render sub-refs sharing the same details as reuse | https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Cite/+/1202996 . The patch also works on master ( apart from the Converter tests that depend on the stacked VE change )
-
If applicable use an internal feature switch
Implementation notes
- Cite Parsoid integration extractRefFromNode is the function which decorates the ref node with metadata during wt2dom rendering.
- This function should compare each new subref against already-known subrefs of the same main node, to find any with identical wikitext.
- When identical wikitext is found, the reflist body->id and should be reused. *Maybe* this can be done using a mechanism similar to named ref reuse, which adopts the entire $ref from the previous matching ref:
$ref = $refGroup->lookupRefByName( $refName );
Currently, there is an unconditional addRef for each subref,
$ref = $referencesData->addRef( $refGroup, $refName, $refDir, $details );
We want to write a new code path which finds the old subref and assigns it to $ref.
Note:
- We also need to change the converter code in VE to work with the merged sub-refs. Currently it's assuming that sub-refs always get a different listKey. We need to change that to make sure that sub-refs sharing the same reflistItemId also get the same listKey so they are recognized by VE as reuse.

