Compare commits

...

2 commits

Author SHA1 Message Date
Jeremy Ruston
30e6092819 Fix unneeded diff 2025-11-24 11:11:33 +00:00
lin onetwo
4305a0bb92
Fix/dynamic macro calls test (#9459)
* Revert "Temporarily disable a broken serializer test"

This reverts commit b3144300ee.

* restore synamic parameter parse result

* lint

* lint

* remove duplicate

* Update core/modules/parsers/parseutils.js

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update editions/test/tiddlers/tests/data/serialize/DynamicWidgetAttribute.tid

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update editions/test/tiddlers/tests/data/serialize/DynamicWidgetAttribute.tid

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: mixed qouted and unquoted

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 11:10:42 +00:00
13 changed files with 114 additions and 17 deletions

View file

@ -296,6 +296,8 @@ exports.parseMacroParameterAsAttribute = function(source,pos) {
// If we have a name and a separator then we have a named attribute
if(nameToken && separatorToken) {
node.name = nameToken.match[1];
// key value separator is `=` or `:`
node.assignmentOperator = separatorToken.match[0];
pos = separatorToken.end;
}
// Skip whitespace
@ -306,6 +308,8 @@ exports.parseMacroParameterAsAttribute = function(source,pos) {
pos = stringLiteral.end;
node.type = "string";
node.value = stringLiteral.value;
// Mark the value as having been quoted in the source
node.quoted = true;
} else {
// console.log(`Failed to parse string literal ${source} at ${pos} with node as ${JSON.stringify(node)}`);
// Look for a filtered value

View file

@ -10,4 +10,4 @@ HelloThere
[[TiddlyWiki on the Web]]
[[Testimonials and Reviews]]
GettingStarted
Community
Community

View file

@ -1,4 +1,4 @@
tags: $:/tags/wikitext-serialize-test-spec-x
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/Attribute
type: text/vnd.tiddlywiki

View file

@ -0,0 +1,9 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/DynamicMacroMixed
type: text/vnd.tiddlywiki
<<mymacro static:"value" dynamic={{reference}} filter={{{ [tag[test]] }}}>>
<$macrocall $name="mymacro" static="value" dynamic=<<inner>>/>
<<mymacro `substituted $(var)$`>>

View file

@ -0,0 +1,9 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/DynamicMacroParams
type: text/vnd.tiddlywiki
<<mymacro param={{Something}}>>
<<mymacro param={{{ [<myvar>addprefix[https:]] }}}>>
<$macrocall $name="outermacro" inner=<<innermacro arg="value">>/>

View file

@ -0,0 +1,7 @@
tags: $:/tags/wikitext-serialize-test-spec
title: Serialize/DynamicWidgetAttribute
type: text/vnd.tiddlywiki
<div class=<<mymacro param={{Something}}>>>content</div>
<$button actions=<<myactions target={{!!title}}>>/>

View file

@ -7,3 +7,7 @@ type: text/vnd.tiddlywiki
<<.def "macro calls">>
<<alert "primary" "primary alert" width:"60%">>
<<john one:val1 two:val2 three:"quoted value">>
<<test unquoted:value quoted:"value" number:123>>

View file

@ -3,3 +3,5 @@ title: Serialize/MacroCallInline
type: text/vnd.tiddlywiki
These are macro calls in a line: <<name "value" "value2">> and <<.def "macro calls">> <<alert "primary" "primary alert" width:"60%">>
Testing unquoted parameters: <<john one:val1 two:val2>> and <<test param:value other:"quoted">>.

View file

@ -256,6 +256,7 @@ describe("HTML tag new parser tests", function() {
"one": {
"start": 25,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 33
@ -263,8 +264,10 @@ describe("HTML tag new parser tests", function() {
"three": {
"start": 33,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 55
}
},
@ -277,6 +280,7 @@ describe("HTML tag new parser tests", function() {
{
"start": 25,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 33
@ -284,8 +288,10 @@ describe("HTML tag new parser tests", function() {
{
"start": 33,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 55
}
],
@ -311,6 +317,7 @@ describe("HTML tag new parser tests", function() {
"one": {
"start": 25,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 33
@ -318,8 +325,10 @@ describe("HTML tag new parser tests", function() {
"three": {
"start": 33,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 55
}
},
@ -332,6 +341,7 @@ describe("HTML tag new parser tests", function() {
{
"start": 25,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 33
@ -339,8 +349,10 @@ describe("HTML tag new parser tests", function() {
{
"start": 33,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 55
}
],
@ -395,6 +407,7 @@ describe("HTML tag new parser tests", function() {
"one": {
"start": 64,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 72
@ -402,8 +415,10 @@ describe("HTML tag new parser tests", function() {
"three": {
"start": 72,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 94
}
},
@ -416,6 +431,7 @@ describe("HTML tag new parser tests", function() {
{
"start": 64,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 72
@ -423,8 +439,10 @@ describe("HTML tag new parser tests", function() {
{
"start": 72,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 94
}
],
@ -471,6 +489,7 @@ describe("HTML tag new parser tests", function() {
"one": {
"start": 64,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 72
@ -478,8 +497,10 @@ describe("HTML tag new parser tests", function() {
"three": {
"start": 72,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 94
}
},
@ -492,6 +513,7 @@ describe("HTML tag new parser tests", function() {
{
"start": 64,
"name": "one",
"assignmentOperator": ":",
"type": "string",
"value": "two",
"end": 72
@ -499,8 +521,10 @@ describe("HTML tag new parser tests", function() {
{
"start": 72,
"name": "three",
"assignmentOperator": ":",
"type": "string",
"value": "four and five",
"quoted": true,
"end": 94
}
],

View file

@ -261,7 +261,7 @@ describe("WikiText parser tests", function() {
);
expect(parse("text <<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual(
[{"type":"element","tag":"p",rule:"parseblock","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":92,"rule":"macrocallinline","attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":11,"end":20},"two":{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},"three":{"name":"three","type":"string","value":"val '3'","start":35,"end":52},"four":{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},"five":{"name":"five","type":"string","value":"val 5","start":73,"end":89}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":11,"end":20},{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},{"name":"three","type":"string","value":"val '3'","start":35,"end":52},{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},{"name":"five","type":"string","value":"val 5","start":73,"end":89}]}],"start":0,"end":92}]
[{"type":"element","tag":"p",rule:"parseblock","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":92,"rule":"macrocallinline","attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","assignmentOperator":":","type":"string","value":"val1","start":11,"end":20},"two":{"name":"two","assignmentOperator":":","type":"string","value":"val \"2\"","quoted":true,"start":20,"end":35},"three":{"name":"three","assignmentOperator":":","type":"string","value":"val '3'","quoted":true,"start":35,"end":52},"four":{"name":"four","assignmentOperator":":","type":"string","value":"val 4\"5'","quoted":true,"start":52,"end":73},"five":{"name":"five","assignmentOperator":":","type":"string","value":"val 5","quoted":true,"start":73,"end":89}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","assignmentOperator":":","type":"string","value":"val1","start":11,"end":20},{"name":"two","assignmentOperator":":","type":"string","value":"val \"2\"","quoted":true,"start":20,"end":35},{"name":"three","assignmentOperator":":","type":"string","value":"val '3'","quoted":true,"start":35,"end":52},{"name":"four","assignmentOperator":":","type":"string","value":"val 4\"5'","quoted":true,"start":52,"end":73},{"name":"five","assignmentOperator":":","type":"string","value":"val 5","quoted":true,"start":73,"end":89}]}],"start":0,"end":92}]
);
expect(parse("ignored << carrots <<john>>")).toEqual(
@ -287,7 +287,7 @@ describe("WikiText parser tests", function() {
);
expect(parse("text <<outie one:'my <<innie>>' >>")).toEqual(
[{"type":"element","tag":"p",rule:"parseblock","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":34,"rule":"macrocallinline","attributes":{"$variable":{"name":"$variable","type":"string","value":"outie"},"one":{"name":"one","type":"string","value":"my <<innie>>","start":12,"end":31}},"orderedAttributes":[{"name":"$variable","type":"string","value":"outie"},{"name":"one","type":"string","value":"my <<innie>>","start":12,"end":31}]}],"start":0,"end":34}]
[{"type":"element","tag":"p",rule:"parseblock","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":34,"rule":"macrocallinline","attributes":{"$variable":{"name":"$variable","type":"string","value":"outie"},"one":{"name":"one","assignmentOperator":":","type":"string","value":"my <<innie>>","quoted":true,"start":12,"end":31}},"orderedAttributes":[{"name":"$variable","type":"string","value":"outie"},{"name":"one","assignmentOperator":":","type":"string","value":"my <<innie>>","quoted":true,"start":12,"end":31}]}],"start":0,"end":34}]
);
@ -301,7 +301,7 @@ describe("WikiText parser tests", function() {
);
expect(parse("<<john one:val1 two: 'val \"2\"' three: \"val '3'\" four: \"\"\"val 4\"5'\"\"\" five: [[val 5]] >>")).toEqual(
[{"type":"transclude","start":0,"end":87,"rule":"macrocallblock","attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":6,"end":15},"two":{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},"three":{"name":"three","type":"string","value":"val '3'","start":30,"end":47},"four":{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},"five":{"name":"five","type":"string","value":"val 5","start":68,"end":84}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":6,"end":15},{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},{"name":"three","type":"string","value":"val '3'","start":30,"end":47},{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},{"name":"five","type":"string","value":"val 5","start":68,"end":84}],"isBlock":true}]
[{"type":"transclude","start":0,"end":87,"rule":"macrocallblock","attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","assignmentOperator":":","type":"string","value":"val1","start":6,"end":15},"two":{"name":"two","assignmentOperator":":","type":"string","value":"val \"2\"","quoted":true,"start":15,"end":30},"three":{"name":"three","assignmentOperator":":","type":"string","value":"val '3'","quoted":true,"start":30,"end":47},"four":{"name":"four","assignmentOperator":":","type":"string","value":"val 4\"5'","quoted":true,"start":47,"end":68},"five":{"name":"five","assignmentOperator":":","type":"string","value":"val 5","quoted":true,"start":68,"end":84}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","assignmentOperator":":","type":"string","value":"val1","start":6,"end":15},{"name":"two","assignmentOperator":":","type":"string","value":"val \"2\"","quoted":true,"start":15,"end":30},{"name":"three","assignmentOperator":":","type":"string","value":"val '3'","quoted":true,"start":30,"end":47},{"name":"four","assignmentOperator":":","type":"string","value":"val 4\"5'","quoted":true,"start":47,"end":68},{"name":"five","assignmentOperator":":","type":"string","value":"val 5","quoted":true,"start":68,"end":84}],"isBlock":true}]
);
expect(parse("<< carrots\n\n<<john>>")).toEqual(
@ -321,12 +321,12 @@ describe("WikiText parser tests", function() {
);
expect(parse("<<multiline arg:\"\"\"\n\nwikitext\n\"\"\" >>")).toEqual(
[{"type":"transclude","start":0,"end":36,"rule":"macrocallblock","attributes":{"$variable":{"name":"$variable","type":"string","value":"multiline"},"arg":{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}},"orderedAttributes":[{"name":"$variable","type":"string","value":"multiline"},{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}],"isBlock":true}]
[{"type":"transclude","start":0,"end":36,"rule":"macrocallblock","attributes":{"$variable":{"name":"$variable","type":"string","value":"multiline"},"arg":{"name":"arg","assignmentOperator":":","type":"string","value":"\n\nwikitext\n","quoted":true,"start":11,"end":33}},"orderedAttributes":[{"name":"$variable","type":"string","value":"multiline"},{"name":"arg","assignmentOperator":":","type":"string","value":"\n\nwikitext\n","quoted":true,"start":11,"end":33}],"isBlock":true}]
);
expect(parse("<<outie one:'my <<innie>>' >>")).toEqual(
[ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <<innie>>", start: 7, end: 26} ], end: 29, isBlock: true } ]
[ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", assignmentOperator: ":", type:"string", value: "my <<innie>>", quoted: true, start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", assignmentOperator: ":", type:"string", value: "my <<innie>>", quoted: true, start: 7, end: 26} ], end: 29, isBlock: true } ]
);
});

View file

@ -10,4 +10,4 @@ HelloThere
[[TiddlyWiki on the Web]]
[[Testimonials and Reviews]]
GettingStarted
Community
Community

View file

@ -18,7 +18,7 @@ exports.serialize = function (node) {
if(node.orderedAttributes) {
node.orderedAttributes.forEach(function (attribute) {
if(attribute.name !== "$variable") {
result += " " + $tw.utils.serializeAttribute(attribute,{assignmentSymbol:":"});
result += " " + $tw.utils.serializeAttribute(attribute);
}
});
}

View file

@ -62,14 +62,27 @@ exports.serializeAttribute = function(node,options) {
}
// If name is number, means it is a positional attribute and name is omitted
var positional = parseInt(node.name) >= 0,
// `=` in a widget and might be `:` in a macro
assign = positional ? "" : (options.assignmentSymbol || "="),
// Use the original assignment operator if available, otherwise default to '='
assign = positional ? "" : (node.assignmentOperator || "="),
attributeString = positional ? "" : node.name;
if(node.type === "string") {
if(node.value === "true") {
return attributeString;
}
attributeString += assign + '"' + node.value + '"';
// For macro parameters (using ':' separator), preserve unquoted values
// For widget attributes (using '=' separator), always use quotes
if(assign === ":" && !node.quoted) {
attributeString += assign + node.value;
} else if(assign === "") {
// Positional parameter
if(!node.quoted) {
attributeString += node.value;
} else {
attributeString += '"' + node.value + '"';
}
} else {
attributeString += assign + '"' + node.value + '"';
}
} else if(node.type === "filtered") {
attributeString += assign + "{{{" + node.filter + "}}}";
} else if(node.type === "indirect") {
@ -77,11 +90,36 @@ exports.serializeAttribute = function(node,options) {
} else if(node.type === "substituted") {
attributeString += assign + "`" + node.rawValue + "`";
} else if(node.type === "macro") {
if(node.value && typeof node.value === "object" && node.value.type === "macrocall") {
var params = node.value.params.map(function(param) {
return param.value;
}).join(" ");
attributeString += assign + "<<" + node.value.name + " " + params + ">>";
if(node.value && typeof node.value === "object") {
if(node.value.type === "transclude") {
// Handle the transclude-based macro call structure
var macroName = node.value.attributes && node.value.attributes["$variable"] ?
node.value.attributes["$variable"].value : "";
if(!macroName) {
return null;
}
var params = [];
if(node.value.orderedAttributes) {
node.value.orderedAttributes.forEach(function(attr) {
if(attr.name !== "$variable") {
var paramStr = exports.serializeAttribute(attr);
if(paramStr) {
params.push(paramStr);
}
}
});
}
attributeString += assign + "<<" + macroName + (params.length > 0 ? " " + params.join(" ") : "") + ">>";
} else if(node.value.type === "macrocall") {
// Handle the classical macrocall structure for backwards compatibility
var params = node.value.params.map(function(param) {
return param.value;
}).join(" ");
attributeString += assign + "<<" + node.value.name + " " + params + ">>";
} else {
// Unsupported macro structure
return null;
}
} else {
// Unsupported macro structure
return null;