jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/389852 )
Change subject: Preserve original transclusion's parameter order
......................................................................
Preserve original transclusion's parameter order
* Parameters that were previously present aren't reordered.
Any updates to params are made in place.
* Newly introduced parameters are sorted in between the original
parameters at a place where they preserve templateData order
partially.
* In the absence of templatedata, they will be serialized
in the order in which they were added by VE into data-mw.
* Updated mocha tests.
Change-Id: I2b269d5bfb08efbe56b9293c2aa3e810fb1b29da
---
M lib/html2wt/WikitextSerializer.js
M tests/mocha/templatedata.js
M tests/mockAPI.js
3 files changed, 96 insertions(+), 53 deletions(-)
Approvals:
jenkins-bot: Verified
Arlolra: Looks good to me, approved
diff --git a/lib/html2wt/WikitextSerializer.js
b/lib/html2wt/WikitextSerializer.js
index 2fc2a1a..ad503fc 100644
--- a/lib/html2wt/WikitextSerializer.js
+++ b/lib/html2wt/WikitextSerializer.js
@@ -413,6 +413,72 @@
});
};
+function createParamComparator(dpArgInfo, tplData, dataMwKeys) {
+ // Record order of parameters in new data-mw
+ var newOrder = new Map(Array.from(dataMwKeys).map(function(key, i) {
+ return [key, { order: i }];
+ }));
+ // Record order of parameters in original wikitext (from data-parsoid)
+ var origOrder = new Map(dpArgInfo.map(function(argInfo, i) {
+ return [argInfo.k, { order: i, dist: 0 }];
+ }));
+ // Record order of parameters in templatedata (if present)
+ var tplDataOrder = new Map();
+ var keys = [];
+ if (tplData && Array.isArray(tplData.paramOrder)) {
+ var params = tplData.params;
+ tplData.paramOrder.forEach(function(k, i) {
+ tplDataOrder.set(k, { order: keys.length });
+ keys.push(k);
+ // Aliases have the same sort order as the main name.
+ var aliases = params && params[k] && params[k].aliases;
+ (aliases || []).forEach(function(a) {
+ tplDataOrder.set(a, { order: keys.length });
+ keys.push(a);
+ });
+ });
+ }
+ // Find the closest "original parameter" for each templatedata
parameter,
+ // so that newly-added parameters are placed near the parameters which
+ // templatedata says they should be adjacent to.
+ var nearestOrder = new Map(origOrder);
+ var reduceF = function(acc, val, i) {
+ if (origOrder.has(val)) {
+ acc = origOrder.get(val);
+ }
+ if (!(nearestOrder.has(val) && nearestOrder.get(val).dist <
acc.dist)) {
+ nearestOrder.set(val, acc);
+ }
+ return { order: acc.order, dist: acc.dist + 1 };
+ };
+ // Find closest original parameter before the key.
+ keys.reduce(reduceF, { order: -1, dist: 2 * keys.length });
+ // Find closest original parameter after the key.
+ keys.reduceRight(reduceF, { order: origOrder.size, dist: keys.length });
+
+ // Helper function to return a large number if the given key isn't
+ // in the sort order map
+ var big = Math.max(nearestOrder.size, newOrder.size);
+ var defaultGet = function(map, key) {
+ return map.has(key) ? map.get(key).order : big;
+ };
+
+ return function cmp(a, b) {
+ // primary key is `nearestOrder` (nearest original parameter)
+ var aOrder = defaultGet(nearestOrder, a);
+ var bOrder = defaultGet(nearestOrder, b);
+ if (aOrder !== bOrder) { return aOrder - bOrder; }
+ // secondary key is templatedata order
+ aOrder = defaultGet(tplDataOrder, a);
+ bOrder = defaultGet(tplDataOrder, b);
+ if (aOrder !== bOrder) { return aOrder - bOrder; }
+ // tertiary key is original input order (makes sort stable)
+ aOrder = defaultGet(newOrder, a);
+ bOrder = defaultGet(newOrder, b);
+ return aOrder - bOrder;
+ };
+}
+
// See
https://github.com/wikimedia/mediawiki-extensions-TemplateData/blob/master/Specification.md
// for the templatedata specification.
WSP.serializePart = Promise.method(function(node, type, part, tplData) {
@@ -540,34 +606,8 @@
kvMap.set(k, { serializeAsNamed: serializeAsNamed,
name: name, value: value });
});
}).then(function() {
- var paramOrder = [];
- if (tplData && Array.isArray(tplData.paramOrder)) {
- var params = tplData.params;
- // Aliases have the same sort order as the main name
- paramOrder = tplData.paramOrder.reduce(function(prev,
next) {
- var aliases = params && params[next] &&
params[next].aliases;
- aliases = Array.isArray(aliases) ? aliases : [];
- return prev.concat(next, aliases);
- }, paramOrder);
- }
- var argOrder = [].concat(
- // 1. Use tpldata param order as the first guide
- // Note that:
- // (a) template arg names are case-sensitive
- // (b) arg names in tplData.paramOrder already
- // have their spaces trimmed
- paramOrder,
-
- // 2. Push keys from data-parsoid
- // data-parsoid argInfo also present in data-mw
- // will get processed
- dpArgInfo.map(function(argInfo) { return argInfo.k; }),
-
- // 3. Push keys from data-mw
- // data-mw entry not present in data-parsoid argInfo
- // will get processed
- tplKeysFromDataMw
- );
+ var argOrder = Array.from(kvMap.keys())
+ .sort(createParamComparator(dpArgInfo, tplData,
kvMap.keys()));
var argIndex = 1;
var numericIndex = 1;
@@ -578,10 +618,6 @@
return argOrder.reduce(function(argBuf, param) {
var kv = kvMap.get(param);
- if (!kv) {
- return argBuf;
- }
-
// Add nowiki escapes for the arg value, as required
var escapedValue =
self.wteHandlers.escapeTplArgWT(kv.value, {
serializeAsNamed: kv.serializeAsNamed || param
!== numericIndex.toString(),
@@ -599,10 +635,6 @@
// No WS trimming for positional args
argBuf.push({ dpKey: param, name: null, value:
escapedValue.v });
}
-
- // Clear entry for param
- // Ensures that a param is serialized exactly once!
- kvMap.delete(param);
return argBuf;
}, []);
diff --git a/tests/mocha/templatedata.js b/tests/mocha/templatedata.js
index 71c5a1d..5774d3d 100644
--- a/tests/mocha/templatedata.js
+++ b/tests/mocha/templatedata.js
@@ -83,14 +83,14 @@
},
},
- // 3. flipped f1 & f2 in data-parsoid
+ // 3. flipped f1 & f2 in data-parsoid + newly added f0
{
- 'name': 'Enforce param order',
- 'html': '<span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f2"},{"k":"f1"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"NoFormatWithParamOrder","href":"./Template:NoFormatWithParamOrder"},"params":{"f1":{"wt":"foo"},"f2":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>foo</span>',
+ 'name': 'Preserve original param order + smart insertion of new
params',
+ 'html': '<span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f2"},{"k":"f1"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"NoFormatWithParamOrder","href":"./Template:NoFormatWithParamOrder"},"params":{"f1":{"wt":"foo"},"f2":{"wt":"foo"},"f0":{"wt":"BOO"}},"i":0}}]}'
+ "'" + '>foo</span>',
'wt': {
- 'no_selser':
'{{NoFormatWithParamOrder|f2=foo|f1=foo}}',
- 'new_content':
'{{NoFormatWithParamOrder|f1=foo|f2=foo}}',
- 'edited':
'{{NoFormatWithParamOrder|f1=BAR|f2=foo}}',
+ 'no_selser':
'{{NoFormatWithParamOrder|f2=foo|f1=foo|f0=BOO}}',
+ 'new_content':
'{{NoFormatWithParamOrder|f0=BOO|f1=foo|f2=foo}}',
+ 'edited':
'{{NoFormatWithParamOrder|f2=foo|f0=BOO|f1=BAR}}', // Preserve partial
templatedata order
},
},
@@ -134,7 +134,7 @@
'wt': {
'no_selser': '{{InlineTplWithParamOrder\n|f2 =
foo\n|f1 = foo\n}}',
'new_content':
'{{InlineTplWithParamOrder|f1=foo|f2=foo}}',
- 'edited':
'{{InlineTplWithParamOrder|f1=BAR|f2=foo}}',
+ 'edited':
'{{InlineTplWithParamOrder|f2=foo|f1=BAR}}',
},
},
@@ -145,7 +145,7 @@
'wt': {
'no_selser':
'{{BlockTplWithParamOrder|f2=foo|f1=foo}}',
'new_content': '{{BlockTplWithParamOrder\n| f1 = foo\n|
f2 = foo\n}}',
- 'edited': '{{BlockTplWithParamOrder\n| f1 = BAR\n|
f2 = foo\n}}',
+ 'edited': '{{BlockTplWithParamOrder\n| f2 = foo\n|
f1 = BAR\n}}',
},
},
@@ -167,7 +167,7 @@
'wt': {
'no_selser':
'{{BlockTplWithParamOrder|f2=foo|f1=foo}}SOME TEXT{{InlineTplNoParamOrder\n|f2
= foo\n|f1 = foo\n}}',
'new_content': '{{BlockTplWithParamOrder\n| f1 = foo\n|
f2 = foo\n}}SOME TEXT{{InlineTplNoParamOrder|f1=foo|f2=foo}}',
- 'edited': '{{BlockTplWithParamOrder\n| f1 = BAR\n|
f2 = foo\n}}SOME TEXT{{InlineTplNoParamOrder|f2=foo|f1=foo}}',
+ 'edited': '{{BlockTplWithParamOrder\n| f2 = foo\n|
f1 = BAR\n}}SOME TEXT{{InlineTplNoParamOrder|f2=foo|f1=foo}}',
},
},
@@ -181,7 +181,18 @@
'edited':
'{{WithParamOrderAndAliases|f3=foo|f2=BAR}}',
},
},
- // 12. Inline Formatted template 1
+ // 12. Alias sort order, with both original and alias params
+ // Even aliased parameters should appear in the original order by
default.
+ {
+ 'name': 'Enforce param order with aliases (aliases in original
order)',
+ 'html': '<span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f4"},{"k":"f3"},{"k":"f1"}]]}' + "'
data-mw='" +
'{"parts":[{"template":{"target":{"wt":"WithParamOrderAndAliases","href":"./Template:WithParamOrderAndAliases"},"params":{"f4":{"wt":"foo"},"f3":{"wt":"foo"},"f1":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>foo</span>',
+ 'wt': {
+ 'no_selser':
'{{WithParamOrderAndAliases|f4=foo|f3=foo|f1=foo}}',
+ 'new_content':
'{{WithParamOrderAndAliases|f1=foo|f4=foo|f3=foo}}',
+ 'edited':
'{{WithParamOrderAndAliases|f4=BAR|f3=foo|f1=foo}}',
+ },
+ },
+ // 13. Inline Formatted template 1
{
'html': 'x <span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f1"},{"k":"x"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"InlineFormattedTpl_1","href":"./Template:InlineFormattedTpl_1"},"params":{"f1":{"wt":""},"x":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>something</span> y',
'wt': {
@@ -190,7 +201,7 @@
'edited': 'x {{InlineFormattedTpl_1|f1=|x=BAR}} y',
},
},
- // 13. Inline Formatted template 2
+ // 14. Inline Formatted template 2
{
'html': 'x <span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f1"},{"k":"x"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"InlineFormattedTpl_2","href":"./Template:InlineFormattedTpl_2"},"params":{"f1":{"wt":""},"x":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>something</span> y',
'wt': {
@@ -199,7 +210,7 @@
'edited': 'x \n{{InlineFormattedTpl_2 | f1 = | x
= BAR}} y',
},
},
- // 14. Inline Formatted template 3
+ // 15. Inline Formatted template 3
{
'html': 'x <span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f1"},{"k":"x"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"InlineFormattedTpl_3","href":"./Template:InlineFormattedTpl_3"},"params":{"f1":{"wt":""},"x":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>something</span> y',
'wt': {
@@ -208,7 +219,7 @@
'edited': 'x {{InlineFormattedTpl_3| f1 = | x
= BAR}} y',
},
},
- // 15. Custom block formatting 1
+ // 16. Custom block formatting 1
{
'html': 'x<span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f1"},{"k":"f2"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"BlockFormattedTpl_1","href":"./Template:BlockFormattedTpl_1"},"params":{"f1":{"wt":""},"f2":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>something</span>y',
'wt': {
@@ -217,7 +228,7 @@
'edited': 'x{{BlockFormattedTpl_1\n| f1 = \n| f2 =
BAR\n}}y', // normalized
},
},
- // 16. Custom block formatting 2
+ // 17. Custom block formatting 2
{
'html': 'x<span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f1"},{"k":"f2"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"BlockFormattedTpl_2","href":"./Template:BlockFormattedTpl_2"},"params":{"f1":{"wt":""},"f2":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>something</span>y',
'wt': {
@@ -226,7 +237,7 @@
'edited': 'x\n{{BlockFormattedTpl_2\n| f1 = \n| f2
= BAR\n}}\ny', // normalized
},
},
- // 17. Custom block formatting 3
+ // 18. Custom block formatting 3
{
'html': 'x<span about="#mwt1" typeof="mw:Transclusion"
data-parsoid=' + "'" + '{"pi":[[{"k":"f1"},{"k":"f2"}]]}' + "' data-mw='" +
'{"parts":[{"template":{"target":{"wt":"BlockFormattedTpl_3","href":"./Template:BlockFormattedTpl_3"},"params":{"f1":{"wt":""},"f2":{"wt":"foo"}},"i":0}}]}'
+ "'" + '>something</span>y',
'wt': {
diff --git a/tests/mockAPI.js b/tests/mockAPI.js
index 81c58e0..1e16673 100644
--- a/tests/mockAPI.js
+++ b/tests/mockAPI.js
@@ -269,7 +269,7 @@
// paramOrder fields at this point, so keeping these lean.
var templateData = {
'Template:NoFormatWithParamOrder': {
- 'paramOrder': ['unused1', 'f1', 'unused2', 'f2', 'unused3'],
+ 'paramOrder': ['f0', 'f1', 'unused2', 'f2', 'unused3'],
},
'Template:InlineTplNoParamOrder': {
'format': 'inline',
@@ -287,7 +287,7 @@
},
'Template:WithParamOrderAndAliases': {
'params': {
- 'f1': { 'aliases': ['f3'] }
+ 'f1': { 'aliases': ['f4','f3'] }
},
'paramOrder': ['f1','f2'],
},
--
To view, visit https://gerrit.wikimedia.org/r/389852
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I2b269d5bfb08efbe56b9293c2aa3e810fb1b29da
Gerrit-PatchSet: 9
Gerrit-Project: mediawiki/services/parsoid
Gerrit-Branch: master
Gerrit-Owner: Subramanya Sastry <[email protected]>
Gerrit-Reviewer: Arlolra <[email protected]>
Gerrit-Reviewer: C. Scott Ananian <[email protected]>
Gerrit-Reviewer: Deskana <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: Sbailey <[email protected]>
Gerrit-Reviewer: Subramanya Sastry <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits