From 6bc77cf3e27526e6e07b311d1963d710361a0c7e Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 4 Feb 2026 11:24:06 +0000 Subject: [PATCH] Dynamic parameters for macro/procedure/function calls (#9055) * Initial commit The idea is to extend the macro call syntax to accept dynamic parameter values (ie thing:{{more}} etc). Eventually, this will work in all the contexts in which the double angle bracket syntax is valid. This initial commit gets the tests passing, but doesn't yet activate the new functionality. * Test for standalone macro calls with dynamic parameters * Parse attribute macros with the new parser This fixes the tests * Test for attribute macros * Add some examples * Tweak examples * Fix test * Temporarily disable a broken serializer test * Fix/dynamic macro calls test (#9459) * Revert "Temporarily disable a broken serializer test" This reverts commit b3144300ee1c28a2983c659c8e29a6776923d6a2. * 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> * Fix unneeded diff * Minor docs update * Genuflecting to the linter * Remove debug logging * Add change note * Allow single closing square brackets within double square brackets quoted strings * Only allow new style parameter values if the separator is an equals sign * On reflection, new style values should not be allowed for anonymous parameters Backwards compatibility * Docs updates * Docs updates --------- Co-authored-by: lin onetwo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- core/modules/parsers/parseutils.js | 180 +++++++++-- .../parsers/wikiparser/rules/macrodef.js | 2 +- core/modules/widgets/widget.js | 16 +- ...tw5.com_railroad_macro-parameter-value.tid | 4 +- editions/fr-FR/tiddlers/Macro_Call_Syntax.tid | 2 +- .../tiddlers/Macro_Definition_Syntax.tid | 2 +- .../data/macros/dynamic-macros/Attribute.tid | 26 ++ .../data/macros/dynamic-macros/Standalone.tid | 23 ++ .../data/serialize/DynamicMacroMixed.tid | 9 + .../data/serialize/DynamicMacroParams.tid | 9 + .../data/serialize/DynamicWidgetAttribute.tid | 7 + .../tests/data/serialize/MacroCallBlock.tid | 4 + .../tests/data/serialize/MacroCallInline.tid | 2 + .../test/tiddlers/tests/test-html-parser.js | 302 +++++++++++++++++- .../tiddlers/tests/test-wikitext-parser.js | 18 +- .../tw5.com/tiddlers/filters/function.tid | 4 +- .../tw5.com/tiddlers/functions/Functions.tid | 11 +- .../tiddlers/macros/syntax/Call Syntax.tid | 43 +++ ...ameterValue.tid => CallParameterValue.tid} | 6 +- .../macros/syntax/Macro Call Syntax.tid | 30 +- .../macros/syntax/Macro Definition Syntax.tid | 2 +- .../macros/syntax/Procedure Call Syntax.tid | 31 +- .../syntax/Procedure Definition Syntax.tid | 4 +- .../macros/syntax/Procedure Syntax.tid | 2 +- .../tiddlers/procedures/Procedure Calls.tid | 53 +-- .../tiddlers/procedures/Procedures.tid | 4 +- .../tiddlers/procedures/calls/Calls.tid | 56 ++++ .../tiddlers/releasenotes/5.4.0/#9055.tid | 42 +++ .../v5.4.0/Improvements to Macro Calls.tid | 48 +++ ...pends on how the variable was declared.tid | 52 +++ ...pends on how the variable was declared.tid | 52 +-- ...ed via filter expression function call.tid | 2 +- .../tw5.com/tiddlers/wikitext/Macro Calls.tid | 53 +-- .../rules/macrocallblock.js | 2 +- .../wikitext-serialize/utils/parsetree.js | 54 +++- 35 files changed, 880 insertions(+), 277 deletions(-) create mode 100644 editions/test/tiddlers/tests/data/macros/dynamic-macros/Attribute.tid create mode 100644 editions/test/tiddlers/tests/data/macros/dynamic-macros/Standalone.tid create mode 100644 editions/test/tiddlers/tests/data/serialize/DynamicMacroMixed.tid create mode 100644 editions/test/tiddlers/tests/data/serialize/DynamicMacroParams.tid create mode 100644 editions/test/tiddlers/tests/data/serialize/DynamicWidgetAttribute.tid create mode 100644 editions/tw5.com/tiddlers/macros/syntax/Call Syntax.tid rename editions/tw5.com/tiddlers/macros/syntax/{MacroParameterValue.tid => CallParameterValue.tid} (61%) create mode 100644 editions/tw5.com/tiddlers/procedures/calls/Calls.tid create mode 100644 editions/tw5.com/tiddlers/releasenotes/5.4.0/#9055.tid create mode 100644 editions/tw5.com/tiddlers/v5.4.0/Improvements to Macro Calls.tid create mode 100644 editions/tw5.com/tiddlers/variables/Behaviour of called variables depends on how the variable was declared.tid diff --git a/core/modules/parsers/parseutils.js b/core/modules/parsers/parseutils.js index 05a85e1ec..250a82bae 100644 --- a/core/modules/parsers/parseutils.js +++ b/core/modules/parsers/parseutils.js @@ -107,13 +107,14 @@ exports.parseStringLiteral = function(source,pos) { type: "string", start: pos }; - var reString = /(?:"""([\s\S]*?)"""|"([^"]*)")|(?:'([^']*)')/g; + var reString = /(?:"""([\s\S]*?)"""|"([^"]*)")|(?:'([^']*)')|\[\[((?:[^\]]|\](?!\]))*)\]\]/g; reString.lastIndex = pos; var match = reString.exec(source); if(match && match.index === pos) { node.value = match[1] !== undefined ? match[1] :( - match[2] !== undefined ? match[2] : match[3] - ); + match[2] !== undefined ? match[2] : ( + match[3] !== undefined ? match[3] : match[4] + )); node.end = pos + match[0].length; return node; } else { @@ -173,7 +174,7 @@ exports.parseMacroParameter = function(source,pos) { start: pos }; // Define our regexp - const reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/y; + const reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[((?:[^\]]|\](?!\]))*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/y; // Skip whitespace pos = $tw.utils.skipWhiteSpace(source,pos); // Look for the parameter @@ -206,28 +207,152 @@ exports.parseMacroParameter = function(source,pos) { Look for a macro invocation. Returns null if not found, or {type: "transclude", attributes:, start:, end:} */ exports.parseMacroInvocationAsTransclusion = function(source,pos) { - var node = $tw.utils.parseMacroInvocation(source,pos); - if(node) { - var positionalName = 0, - transclusion = { - type: "transclude", - start: node.start, - end: node.end - }; - $tw.utils.addAttributeToParseTreeNode(transclusion,"$variable",node.name); - $tw.utils.each(node.params,function(param) { - var name = param.name; - if(name) { - if(name.charAt(0) === "$") { - name = "$" + name; - } - $tw.utils.addAttributeToParseTreeNode(transclusion,{name: name,type: "string", value: param.value, start: param.start, end: param.end}); - } else { - $tw.utils.addAttributeToParseTreeNode(transclusion,{name: (positionalName++) + "",type: "string", value: param.value, start: param.start, end: param.end}); - } - }); - return transclusion; + var node = { + type: "transclude", + start: pos, + attributes: {}, + orderedAttributes: [] + }; + // Define our regexps + var reVarName = /([^\s>"'=:]+)/g; + // Skip whitespace + pos = $tw.utils.skipWhiteSpace(source,pos); + // Look for a double opening angle bracket + var token = $tw.utils.parseTokenString(source,pos,"<<"); + if(!token) { + return null; } + pos = token.end; + // Get the variable name for the macro + token = $tw.utils.parseTokenRegExp(source,pos,reVarName); + if(!token) { + return null; + } + $tw.utils.addAttributeToParseTreeNode(node,"$variable",token.match[1]); + pos = token.end; + // Check that the tag is terminated by a space or >> + if(!$tw.utils.parseWhiteSpace(source,pos) && !(source.charAt(pos) === ">" && source.charAt(pos + 1) === ">") ) { + return null; + } + // Process attributes + pos = $tw.utils.parseMacroParametersAsAttributes(node,source,pos); + // Skip whitespace + pos = $tw.utils.skipWhiteSpace(source,pos); + // Look for a double closing angle bracket + token = $tw.utils.parseTokenString(source,pos,">>"); + if(!token) { + return null; + } + node.end = token.end; + return node; +}; + +/* +Parse macro parameters as attributes. Returns the position after the last attribute +*/ +exports.parseMacroParametersAsAttributes = function(node,source,pos) { + var position = 0, + attribute = $tw.utils.parseMacroParameterAsAttribute(source,pos); + while(attribute) { + if(!attribute.name) { + attribute.name = (position++) + ""; + attribute.isPositional = true; + } + node.orderedAttributes.push(attribute); + node.attributes[attribute.name] = attribute; + pos = attribute.end; + // Get the next attribute + attribute = $tw.utils.parseMacroParameterAsAttribute(source,pos); + } + node.end = pos; + return pos; +}; + +/* +Parse a macro parameter as an attribute. Returns null if not found, otherwise returns {name:, type: "filtered|string|indirect|macro", value|filter|textReference:, start:, end:,}, with the name being optional +*/ +exports.parseMacroParameterAsAttribute = function(source,pos) { + var node = { + start: pos + }; + // Define our regexps + var reAttributeName = /([^\/\s>"'`=:]+)/g, + reUnquotedAttribute = /((?:(?:>(?!>))|[^\s>"'])+)/g, + reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/g, + reIndirectValue = /\{\{([^\}]+)\}\}/g, + reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/g; + // Skip whitespace + pos = $tw.utils.skipWhiteSpace(source,pos); + // Get the attribute name and the separator token + var nameToken = $tw.utils.parseTokenRegExp(source,pos,reAttributeName), + namePos = nameToken && $tw.utils.skipWhiteSpace(source,nameToken.end), + separatorToken = nameToken && $tw.utils.parseTokenRegExp(source,namePos,/=|:/g), + isNewStyleSeparator = false; // If there is no separator then we don't allow new style values + // 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; + isNewStyleSeparator = (node.assignmentOperator === "="); + } + // Skip whitespace + pos = $tw.utils.skipWhiteSpace(source,pos); + // Look for a string literal + var stringLiteral = $tw.utils.parseStringLiteral(source,pos); + if(stringLiteral) { + pos = stringLiteral.end; + node.type = "string"; + node.value = stringLiteral.value; + // Mark the value as having been quoted in the source + node.quoted = true; + } else { + // Look for a filtered value + var filteredValue = $tw.utils.parseTokenRegExp(source,pos,reFilteredValue); + if(filteredValue && isNewStyleSeparator) { + pos = filteredValue.end; + node.type = "filtered"; + node.filter = filteredValue.match[1]; + } else { + // Look for an indirect value + var indirectValue = $tw.utils.parseTokenRegExp(source,pos,reIndirectValue); + if(indirectValue && isNewStyleSeparator) { + pos = indirectValue.end; + node.type = "indirect"; + node.textReference = indirectValue.match[1]; + } else { + // Look for a unquoted value + var unquotedValue = $tw.utils.parseTokenRegExp(source,pos,reUnquotedAttribute); + if(unquotedValue) { + pos = unquotedValue.end; + node.type = "string"; + node.value = unquotedValue.match[1]; + } else { + // Look for a macro invocation value + var macroInvocation = $tw.utils.parseMacroInvocationAsTransclusion(source,pos); + if(macroInvocation && isNewStyleSeparator) { + pos = macroInvocation.end; + node.type = "macro"; + node.value = macroInvocation; + } else { + var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue); + if(substitutedValue && isNewStyleSeparator) { + pos = substitutedValue.end; + node.type = "substituted"; + node.rawValue = substitutedValue.match[1] || substitutedValue.match[2]; + } else { + } + } + } + } + } + } + // Bail if we don't have a value + if(!node.type) { + return null; + } + // Update the end position + node.end = pos; return node; }; @@ -296,7 +421,7 @@ exports.parseFilterVariable = function(source) { }; /* -Look for an HTML attribute definition. Returns null if not found, otherwise returns {type: "attribute", name:, type: "filtered|string|indirect|macro", value|filter|textReference:, start:, end:,} +Look for an HTML attribute definition. Returns null if not found, otherwise returns {name:, type: "filtered|string|indirect|macro", value|filter|textReference:, start:, end:,} */ exports.parseAttribute = function(source,pos) { var node = { @@ -354,7 +479,7 @@ exports.parseAttribute = function(source,pos) { node.value = unquotedValue.match[1]; } else { // Look for a macro invocation value - var macroInvocation = $tw.utils.parseMacroInvocation(source,pos); + var macroInvocation = $tw.utils.parseMacroInvocationAsTransclusion(source,pos); if(macroInvocation) { pos = macroInvocation.end; node.type = "macro"; @@ -375,6 +500,7 @@ exports.parseAttribute = function(source,pos) { } } } else { + // If there is no equals sign or colon, then this is an attribute with no value, defaulting to "true" node.type = "string"; node.value = "true"; } diff --git a/core/modules/parsers/wikiparser/rules/macrodef.js b/core/modules/parsers/wikiparser/rules/macrodef.js index 72cd4b434..ccfa5df17 100644 --- a/core/modules/parsers/wikiparser/rules/macrodef.js +++ b/core/modules/parsers/wikiparser/rules/macrodef.js @@ -37,7 +37,7 @@ exports.parse = function() { var paramString = this.match[2], params = []; if(paramString !== "") { - var reParam = /\s*([A-Za-z0-9\-_]+)(?:\s*:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|([^"'\s]+)))?/mg, + var reParam = /\s*([A-Za-z0-9\-_]+)(?:\s*:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[((?:[^\]]|\](?!\]))*)\]\]|([^"'\s]+)))?/mg, paramMatch = reParam.exec(paramString); while(paramMatch) { // Save the parameter details diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js index 804d5a153..09b5364e0 100755 --- a/core/modules/widgets/widget.js +++ b/core/modules/widgets/widget.js @@ -416,7 +416,21 @@ Widget.prototype.computeAttribute = function(attribute,options) { value = [value]; } } else if(attribute.type === "macro") { - var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params}); + // Get the macro name + var macroName = attribute.value.attributes["$variable"].value; + // Collect macro parameters + var params = []; + $tw.utils.each(attribute.value.orderedAttributes,function(attr) { + var param = { + value: self.computeAttribute(attr) + }; + if(attr.name && !attr.isPositional) { + param.name = attr.name; + } + params.push(param); + }); + // Invoke the macro + var variableInfo = this.getVariableInfo(macroName,{params: params}); if(options.asList) { value = variableInfo.resultList; } else { diff --git a/editions/fr-FR/tiddlers/$__editions_tw5.com_railroad_macro-parameter-value.tid b/editions/fr-FR/tiddlers/$__editions_tw5.com_railroad_macro-parameter-value.tid index 49dfdb8c5..68e036086 100644 --- a/editions/fr-FR/tiddlers/$__editions_tw5.com_railroad_macro-parameter-value.tid +++ b/editions/fr-FR/tiddlers/$__editions_tw5.com_railroad_macro-parameter-value.tid @@ -1,11 +1,13 @@ created: 20150220191009000 modified: 20150602092431500 -title: $:/editions/tw5.com/railroad/macro-parameter-value +title: $:/editions/tw5.com/railroad/call-parameter-value type: text/vnd.tiddlywiki.railroad ( '"""' [:{/'tout sauf """'/}] '"""' | '"' [:{/'tout sauf "'/}] '"' | "'" [:{/"tout sauf '"/}] "'" | "[[" [:{/"tout sauf ]"/}] "]]" +| "`" [:{/"tout sauf `"/}] "`" +| "```" [:{/"tout sauf ```"/}] "```" | {/"""tout sauf ' " ou espacevierge"""/} ) diff --git a/editions/fr-FR/tiddlers/Macro_Call_Syntax.tid b/editions/fr-FR/tiddlers/Macro_Call_Syntax.tid index 56b93e8ea..04f2be34b 100644 --- a/editions/fr-FR/tiddlers/Macro_Call_Syntax.tid +++ b/editions/fr-FR/tiddlers/Macro_Call_Syntax.tid @@ -25,4 +25,4 @@ The <<.place param-nom>> is a sequence of letters (`A`--`Z`, `a`--`z`), digits ( The <<.place valeur>> is specified as follows<> -<$railroad text={{$:/editions/tw5.com/railroad/macro-parameter-value}}/> +<$railroad text={{$:/editions/tw5.com/railroad/call-parameter-value}}/> diff --git a/editions/fr-FR/tiddlers/Macro_Definition_Syntax.tid b/editions/fr-FR/tiddlers/Macro_Definition_Syntax.tid index bbb92864b..20f096c94 100644 --- a/editions/fr-FR/tiddlers/Macro_Definition_Syntax.tid +++ b/editions/fr-FR/tiddlers/Macro_Definition_Syntax.tid @@ -33,7 +33,7 @@ parametre.nom [: [:espace] ":" [:espace] defaut ] La valeur par <<.place défaut>> d'un paramètre est spécifiée comme suit<<:>> -<$railroad text={{$:/editions/tw5.com/railroad/macro-parameter-value}}/> +<$railroad text={{$:/editions/tw5.com/railroad/call-parameter-value}}/> La définition de la <<.place suite>> se fait comme suit<<:>> diff --git a/editions/test/tiddlers/tests/data/macros/dynamic-macros/Attribute.tid b/editions/test/tiddlers/tests/data/macros/dynamic-macros/Attribute.tid new file mode 100644 index 000000000..2d7313861 --- /dev/null +++ b/editions/test/tiddlers/tests/data/macros/dynamic-macros/Attribute.tid @@ -0,0 +1,26 @@ +title: Macros/Dynamic/Attribute +description: Attribute macrocall with dynamic paramters +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\define mamacromamacro(param:"red") +It is $param$ +\end + +<$text text=<>/> +- +<$text text=<>/> +- +<$text text=<>/> + ++ +title: ExpectedResult + +

It is red +- +It is ba +- +It is param +

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/macros/dynamic-macros/Standalone.tid b/editions/test/tiddlers/tests/data/macros/dynamic-macros/Standalone.tid new file mode 100644 index 000000000..bdce9484d --- /dev/null +++ b/editions/test/tiddlers/tests/data/macros/dynamic-macros/Standalone.tid @@ -0,0 +1,23 @@ +title: Macros/Dynamic/Standalone +description: Standalone macrocall with dynamic paramters +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +\define mamacro(one:"red",two:"green") +It is $one$ and $two$ or <<__one__>> and <<__two__>>. +\end + +<> + +<> + + +<> ++ +title: ExpectedResult + +

It is red and green or red and green.

It is ab and green or ab and green.

It is one and green or one and green.

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/serialize/DynamicMacroMixed.tid b/editions/test/tiddlers/tests/data/serialize/DynamicMacroMixed.tid new file mode 100644 index 000000000..66d7855d1 --- /dev/null +++ b/editions/test/tiddlers/tests/data/serialize/DynamicMacroMixed.tid @@ -0,0 +1,9 @@ +tags: $:/tags/wikitext-serialize-test-spec +title: Serialize/DynamicMacroMixed +type: text/vnd.tiddlywiki + +<> + +<$macrocall $name="mymacro" static="value" dynamic=<>/> + +<> diff --git a/editions/test/tiddlers/tests/data/serialize/DynamicMacroParams.tid b/editions/test/tiddlers/tests/data/serialize/DynamicMacroParams.tid new file mode 100644 index 000000000..0ec199970 --- /dev/null +++ b/editions/test/tiddlers/tests/data/serialize/DynamicMacroParams.tid @@ -0,0 +1,9 @@ +tags: $:/tags/wikitext-serialize-test-spec +title: Serialize/DynamicMacroParams +type: text/vnd.tiddlywiki + +<> + +<addprefix[https:]] }}}>> + +<$macrocall $name="outermacro" inner=<>/> diff --git a/editions/test/tiddlers/tests/data/serialize/DynamicWidgetAttribute.tid b/editions/test/tiddlers/tests/data/serialize/DynamicWidgetAttribute.tid new file mode 100644 index 000000000..c00895eb0 --- /dev/null +++ b/editions/test/tiddlers/tests/data/serialize/DynamicWidgetAttribute.tid @@ -0,0 +1,7 @@ +tags: $:/tags/wikitext-serialize-test-spec +title: Serialize/DynamicWidgetAttribute +type: text/vnd.tiddlywiki + +
>>content
+ +<$button actions=<>/> diff --git a/editions/test/tiddlers/tests/data/serialize/MacroCallBlock.tid b/editions/test/tiddlers/tests/data/serialize/MacroCallBlock.tid index 4bd30d451..f4316efd0 100644 --- a/editions/test/tiddlers/tests/data/serialize/MacroCallBlock.tid +++ b/editions/test/tiddlers/tests/data/serialize/MacroCallBlock.tid @@ -7,3 +7,7 @@ type: text/vnd.tiddlywiki <<.def "macro calls">> <> + +<> + +<> diff --git a/editions/test/tiddlers/tests/data/serialize/MacroCallInline.tid b/editions/test/tiddlers/tests/data/serialize/MacroCallInline.tid index 09127dce0..c8d2a71fc 100644 --- a/editions/test/tiddlers/tests/data/serialize/MacroCallInline.tid +++ b/editions/test/tiddlers/tests/data/serialize/MacroCallInline.tid @@ -3,3 +3,5 @@ title: Serialize/MacroCallInline type: text/vnd.tiddlywiki These are macro calls in a line: <> and <<.def "macro calls">> <> + +Testing unquoted parameters: <> and <>. diff --git a/editions/test/tiddlers/tests/test-html-parser.js b/editions/test/tiddlers/tests/test-html-parser.js index c6c160bb3..c8a6bcbb5 100644 --- a/editions/test/tiddlers/tests/test-html-parser.js +++ b/editions/test/tiddlers/tests/test-html-parser.js @@ -235,11 +235,307 @@ describe("HTML tag new parser tests", function() { expect(parser.parseTag("< $mytag attrib1='something' attrib2=else thing>",0)).toEqual( null ); - expect(parser.parseTag("<$mytag attrib3=<>>",0)).toEqual( - { type : "mytag", start : 0, attributes : { attrib3 : { type : "macro", start : 7, name : "attrib3", value : { type : "macrocall", start : 16, params : [ { type : "macro-parameter", start : 25, value : "two", name : "one", end : 33 }, { type : "macro-parameter", start : 33, value : "four and five", name : "three", end : 55 } ], name : "myMacro", end : 57 }, end : 57 } }, orderedAttributes: [ { type : "macro", start : 7, name : "attrib3", value : { type : "macrocall", start : 16, params : [ { type : "macro-parameter", start : 25, value : "two", name : "one", end : 33 }, { type : "macro-parameter", start : 33, value : "four and five", name : "three", end : 55 } ], name : "myMacro", end : 57 }, end : 57 } ], tag : "$mytag", end : 58 } + expect(parser.parseTag("<$mytag attrib3=<>>", 0)).toEqual( + { + "type": "mytag", + "start": 0, + "attributes": { + "attrib3": { + "start": 7, + "name": "attrib3", + "type": "macro", + "value": { + "type": "transclude", + "start": 16, + "attributes": { + "$variable": { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + "one": { + "start": 25, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 33 + }, + "three": { + "start": 33, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 55 + } + }, + "orderedAttributes": [ + { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + { + "start": 25, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 33 + }, + { + "start": 33, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 55 + } + ], + "end": 57 + }, + "end": 57 + } + }, + "orderedAttributes": [ + { + "start": 7, + "name": "attrib3", + "type": "macro", + "value": { + "type": "transclude", + "start": 16, + "attributes": { + "$variable": { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + "one": { + "start": 25, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 33 + }, + "three": { + "start": 33, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 55 + } + }, + "orderedAttributes": [ + { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + { + "start": 25, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 33 + }, + { + "start": 33, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 55 + } + ], + "end": 57 + }, + "end": 57 + } + ], + "tag": "$mytag", + "end": 58 + } ); expect(parser.parseTag("<$mytag attrib1='something' attrib2=else thing attrib3=<>>",0)).toEqual( - { type : "mytag", start : 0, attributes : { attrib1 : { type : "string", start : 7, name : "attrib1", value : "something", end : 27 }, attrib2 : { type : "string", start : 27, name : "attrib2", value : "else", end : 40 }, thing : { type : "string", start : 40, name : "thing", value : "true", end : 47 }, attrib3 : { type : "macro", start : 47, name : "attrib3", value : { type : "macrocall", start : 55, params : [ { type : "macro-parameter", start : 64, value : "two", name : "one", end : 72 }, { type : "macro-parameter", start : 72, value : "four and five", name : "three", end : 94 } ], name : "myMacro", end : 96 }, end : 96 } }, orderedAttributes: [ { type : "string", start : 7, name : "attrib1", value : "something", end : 27 }, { type : "string", start : 27, name : "attrib2", value : "else", end : 40 }, { type : "string", start : 40, name : "thing", value : "true", end : 47 }, { type : "macro", start : 47, name : "attrib3", value : { type : "macrocall", start : 55, params : [ { type : "macro-parameter", start : 64, value : "two", name : "one", end : 72 }, { type : "macro-parameter", start : 72, value : "four and five", name : "three", end : 94 } ], name : "myMacro", end : 96 }, end : 96 } ], tag : "$mytag", end : 97 } + { + "type": "mytag", + "start": 0, + "attributes": { + "attrib1": { + "start": 7, + "name": "attrib1", + "type": "string", + "value": "something", + "end": 27 + }, + "attrib2": { + "start": 27, + "name": "attrib2", + "type": "string", + "value": "else", + "end": 40 + }, + "thing": { + "start": 40, + "name": "thing", + "type": "string", + "value": "true", + "end": 47 + }, + "attrib3": { + "start": 47, + "name": "attrib3", + "type": "macro", + "value": { + "type": "transclude", + "start": 55, + "attributes": { + "$variable": { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + "one": { + "start": 64, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 72 + }, + "three": { + "start": 72, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 94 + } + }, + "orderedAttributes": [ + { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + { + "start": 64, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 72 + }, + { + "start": 72, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 94 + } + ], + "end": 96 + }, + "end": 96 + } + }, + "orderedAttributes": [ + { + "start": 7, + "name": "attrib1", + "type": "string", + "value": "something", + "end": 27 + }, + { + "start": 27, + "name": "attrib2", + "type": "string", + "value": "else", + "end": 40 + }, + { + "start": 40, + "name": "thing", + "type": "string", + "value": "true", + "end": 47 + }, + { + "start": 47, + "name": "attrib3", + "type": "macro", + "value": { + "type": "transclude", + "start": 55, + "attributes": { + "$variable": { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + "one": { + "start": 64, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 72 + }, + "three": { + "start": 72, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 94 + } + }, + "orderedAttributes": [ + { + "name": "$variable", + "type": "string", + "value": "myMacro" + }, + { + "start": 64, + "name": "one", + "assignmentOperator": ":", + "type": "string", + "value": "two", + "end": 72 + }, + { + "start": 72, + "name": "three", + "assignmentOperator": ":", + "type": "string", + "value": "four and five", + "quoted": true, + "end": 94 + } + ], + "end": 96 + }, + "end": 96 + } + ], + "tag": "$mytag", + "end": 97 + } ); }); diff --git a/editions/test/tiddlers/tests/test-wikitext-parser.js b/editions/test/tiddlers/tests/test-wikitext-parser.js index 59172f867..1fffad01a 100644 --- a/editions/test/tiddlers/tests/test-wikitext-parser.js +++ b/editions/test/tiddlers/tests/test-wikitext-parser.js @@ -261,7 +261,7 @@ describe("WikiText parser tests", function() { ); expect(parse("text <>")).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 <>")).toEqual( @@ -287,7 +287,7 @@ describe("WikiText parser tests", function() { ); expect(parse("text <>' >>")).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 <>","start":12,"end":31}},"orderedAttributes":[{"name":"$variable","type":"string","value":"outie"},{"name":"one","type":"string","value":"my <>","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 <>","quoted":true,"start":12,"end":31}},"orderedAttributes":[{"name":"$variable","type":"string","value":"outie"},{"name":"one","assignmentOperator":":","type":"string","value":"my <>","quoted":true,"start":12,"end":31}]}],"start":0,"end":34}] ); @@ -301,7 +301,7 @@ describe("WikiText parser tests", function() { ); expect(parse("<>")).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<>")).toEqual( @@ -321,12 +321,12 @@ describe("WikiText parser tests", function() { ); expect(parse("<>")).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("<>' >>")).toEqual( - [ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <>", 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 <>", quoted: true, start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", assignmentOperator: ":", type:"string", value: "my <>", quoted: true, start: 7, end: 26} ], end: 29, isBlock: true } ] ); }); @@ -334,23 +334,23 @@ describe("WikiText parser tests", function() { it("should parse tricky macrocall parameters", function() { expect(parse("<am>>")).toEqual( - [{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"pa>am","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"pa>am","start":6,"end":12}],"isBlock":true,"rule":"macrocallblock"}] + [{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"pa>am","start":6,"end":12,"isPositional":true},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"pa>am","start":6,"end":12,"isPositional":true}],"isBlock":true,"rule":"macrocallblock"}] ); expect(parse("< >>")).toEqual( - [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"param>","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param>","start":6,"end":13}],"isBlock":true,"rule":"macrocallblock"}] + [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"param>","start":6,"end":13,"isPositional":true},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param>","start":6,"end":13,"isPositional":true}],"isBlock":true,"rule":"macrocallblock"}] ); expect(parse("<>>")).toEqual( - [{"type":"element","tag":"p",rule:"parseblock","children":[{"type":"transclude","start":0,"end":14,"rule":"macrocallinline","attributes":{"0":{"name":"0","type":"string","value":"param","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param","start":6,"end":12}]},{"type":"text","text":">","start":14,"end":15}],"start":0,"end":15}] + [{"type":"element","rule":"parseblock","tag":"p","children":[{"type":"transclude","start":0,"end":14,"rule":"macrocallinline","attributes":{"0":{"name":"0","type":"string","value":"param","start":6,"end":12,"isPositional":true},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param","start":6,"end":12,"isPositional":true}]},{"type":"text","text":">","start":14,"end":15}],"start":0,"end":15}] ); // equals signs should be allowed expect(parse("<=4 >>")).toEqual( - [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"var>=4","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"var>=4","start":6,"end":13}],"isBlock":true,"rule":"macrocallblock"}] + [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"var>=4","start":6,"end":13,"isPositional":true},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"var>=4","start":6,"end":13,"isPositional":true}],"isBlock":true,"rule":"macrocallblock"}] ); diff --git a/editions/tw5.com/tiddlers/filters/function.tid b/editions/tw5.com/tiddlers/filters/function.tid index f86d21f4f..d3fa3279b 100644 --- a/editions/tw5.com/tiddlers/filters/function.tid +++ b/editions/tw5.com/tiddlers/filters/function.tid @@ -1,6 +1,6 @@ caption: function created: 20220909111836951 -modified: 20230419103154328 +modified: 20260130210336084 op-input: a [[selection of titles|Title Selection]] passed as input to the function <<.place F>> op-output: the [[selection of titles|Title Selection]] returned from the function <<.place F>> op-parameter: first parameter is the [[function name|Functions]], subsequent parameters are passed to the function by position @@ -10,7 +10,7 @@ tags: [[Filter Operators]] title: function Operator type: text/vnd.tiddlywiki -<<.from-version "5.3.0">> The <<.op function>> operator applies a named [[function|Functions]] to the input titles, and returns the results from the function. The function is invoked once with all of the input titles (in contrast, the [[filter Operator]] invokes its function separately for each input title). +<<.from-version "5.3.0">> The <<.op function>> operator applies a named [[function|Functions]] to the input titles, and returns the results from the function. The function is called once with all of the input titles (in contrast, the [[filter Operator]] calls its function separately for each input title). The first parameter of the <<.op function>> operator specifies the name of the function to be called. Subsequent parameters are passed to the function. diff --git a/editions/tw5.com/tiddlers/functions/Functions.tid b/editions/tw5.com/tiddlers/functions/Functions.tid index 0911fdd6d..cb0799b58 100644 --- a/editions/tw5.com/tiddlers/functions/Functions.tid +++ b/editions/tw5.com/tiddlers/functions/Functions.tid @@ -16,12 +16,13 @@ Functions are usually defined with the [[Pragma: \function]]: \end ``` -Functions can be invoked in several ways: +Functions can be called in several ways: -* Directly transclude functions with the syntax `<>` -* Assign functions to widget attributes with the syntax `
>>` -* Invoke functions via the [[function Operator]] with the syntax `[function[myfun],[value],...]` -* Directly invoke functions whose names contain a period as custom filter operators with the syntax `[my.fun[value]]` or `[.myfun[value]]` +* Using the [[Calls]] syntax: +** Directly transclude functions with the syntax `<>` +** Assign functions to widget attributes with the syntax `
>>` +* Call functions via the [[function Operator]] with the syntax `[function[myfun],[value],...]` +* Directly call functions whose names contain a period as custom filter operators with the syntax `[my.fun[value]]` or `[.myfun[value]]` !! How Functions Work diff --git a/editions/tw5.com/tiddlers/macros/syntax/Call Syntax.tid b/editions/tw5.com/tiddlers/macros/syntax/Call Syntax.tid new file mode 100644 index 000000000..8a13a7cea --- /dev/null +++ b/editions/tw5.com/tiddlers/macros/syntax/Call Syntax.tid @@ -0,0 +1,43 @@ +created: 20240310165023000 +modified: 20260125212303316 +tags: [[Call Syntax]] +title: Call Syntax +type: text/vnd.tiddlywiki + +<<.preamble """What follows is a formal presentation of the syntax of the WikiText syntax for procedure/function/macro calls, using [[railroad diagrams|Railroad Diagrams]].""">> + +!! callee-name + +<$railroad text=""" +"<<" [[ callee-name |Calls]] [: [[whitespace|"Filter Whitespace"]] [:{param-value}] ]">>" +"""/> + +* The <<.place callee-name>> is a sequence of non-whitespace characters other than `(` or `>`. + +* <<.place whitespace>> denotes a sequence of [[whitespace characters|Filter Whitespace]]. + +!!! param-value + +Each ''individual'' <<.place param-value>> has the following syntax: + +<$railroad text=""" +\start none +\end none +( +value +| +param-name [:space] ( +":" [:space] value [: space] +| +"=" [:space] new-value [: space] +) +) +"""/> + +* The <<.place param-name>> is a sequence of letters (`A`--`Z`, `a`--`z`), digits (`0`--`9`), hyphens (`-`) and underscores (`_`). + +* The <<.place value>> is specified as follows: + +<$railroad text={{$:/editions/tw5.com/railroad/call-parameter-value}}/> + +* <<.from-version 5.4.0>> The <<.place new-value>> can either be a plain <<.place value>> or a full <<.place callee-name>> call, allowing for dynamic parameter values. diff --git a/editions/tw5.com/tiddlers/macros/syntax/MacroParameterValue.tid b/editions/tw5.com/tiddlers/macros/syntax/CallParameterValue.tid similarity index 61% rename from editions/tw5.com/tiddlers/macros/syntax/MacroParameterValue.tid rename to editions/tw5.com/tiddlers/macros/syntax/CallParameterValue.tid index 81ad3b116..6f1f17443 100644 --- a/editions/tw5.com/tiddlers/macros/syntax/MacroParameterValue.tid +++ b/editions/tw5.com/tiddlers/macros/syntax/CallParameterValue.tid @@ -1,11 +1,13 @@ created: 20150220191009000 -modified: 20150221111554000 -title: $:/editions/tw5.com/railroad/macro-parameter-value +modified: 20260125212303316 +title: $:/editions/tw5.com/railroad/call-parameter-value type: text/vnd.tiddlywiki.railroad ( '"""' [:{/'anything but """'/}] '"""' | '"' [:{/'anything but "'/}] '"' | "'" [:{/"anything but '"/}] "'" | "[[" [:{/"anything but ]"/}] "]]" +| "`" [:{/"anything but `"/}] "`" +| "```" [:{/"anything but ```"/}] "```" | {/"""anything but ' " or whitespace"""/} ) diff --git a/editions/tw5.com/tiddlers/macros/syntax/Macro Call Syntax.tid b/editions/tw5.com/tiddlers/macros/syntax/Macro Call Syntax.tid index f070bbeac..d759c996c 100644 --- a/editions/tw5.com/tiddlers/macros/syntax/Macro Call Syntax.tid +++ b/editions/tw5.com/tiddlers/macros/syntax/Macro Call Syntax.tid @@ -1,31 +1,7 @@ created: 20150221105732000 -modified: 20150221222352000 -tags: [[Macro Syntax]] $:/deprecated +modified: 20260125212303316 +tags: $:/deprecated title: Macro Call Syntax type: text/vnd.tiddlywiki -<<.deprecated-since "5.3.0" "Procedure Call Syntax">> - ----------- - -<<.preamble """What follows is a formal presentation of the syntax of the WikiText syntax for macro calls, using [[railroad diagrams|Railroad Diagrams]]. A [[simpler overview|Macro Calls in WikiText]] is also available.""">> - -<$railroad text=""" -"<<" name [: space [:{param-value}] ]">>" -"""/> - -<<.place space>> denotes a sequence of [[whitespace characters|Filter Whitespace]]. - -The [[macro|Macros]]'s <<.place name>> is a sequence of non-whitespace characters other than `(` or `>`. - -Each individual <<.place param-value>> has the following syntax: - -<$railroad text=""" -[: param-name [:space] ":" [:space] ] value [: space] -"""/> - -The <<.place param-name>> is a sequence of letters (`A`--`Z`, `a`--`z`), digits (`0`--`9`), hyphens (`-`) and underscores (`_`). - -The <<.place value>> is specified as follows: - -<$railroad text={{$:/editions/tw5.com/railroad/macro-parameter-value}}/> +<<.deprecated-since "5.3.0" "Call Syntax">> diff --git a/editions/tw5.com/tiddlers/macros/syntax/Macro Definition Syntax.tid b/editions/tw5.com/tiddlers/macros/syntax/Macro Definition Syntax.tid index ecc389d54..2071555ec 100644 --- a/editions/tw5.com/tiddlers/macros/syntax/Macro Definition Syntax.tid +++ b/editions/tw5.com/tiddlers/macros/syntax/Macro Definition Syntax.tid @@ -37,7 +37,7 @@ param-name [: [:space] ":" [:space] default ] The optional <<.place default>> value of a parameter is specified as follows: -<$railroad text={{$:/editions/tw5.com/railroad/macro-parameter-value}}/> +<$railroad text={{$:/editions/tw5.com/railroad/call-parameter-value}}/> The <<.place rest>> of the definition has the following syntax: diff --git a/editions/tw5.com/tiddlers/macros/syntax/Procedure Call Syntax.tid b/editions/tw5.com/tiddlers/macros/syntax/Procedure Call Syntax.tid index c7ab07644..6d3649fdd 100644 --- a/editions/tw5.com/tiddlers/macros/syntax/Procedure Call Syntax.tid +++ b/editions/tw5.com/tiddlers/macros/syntax/Procedure Call Syntax.tid @@ -1,33 +1,6 @@ created: 20240310165023000 -modified: 20240310172648116 -tags: [[Procedure Syntax]] +modified: 20260125212303316 title: Procedure Call Syntax type: text/vnd.tiddlywiki -<<.preamble """What follows is a formal presentation of the syntax of the WikiText syntax for procedure calls, using [[railroad diagrams|Railroad Diagrams]].""">> - -!! procedure-name - -<$railroad text=""" -"<<" [[ procedure-name |Procedures]] [: [[whitespace|"Filter Whitespace"]] [:{param-value}] ]">>" -"""/> - -* The [[procedure's|Procedures]] <<.place procedure-name>> is a sequence of non-whitespace characters other than `(` or `>`. - -* <<.place whitespace>> denotes a sequence of [[whitespace characters|Filter Whitespace]]. - -!!! param-value - -Each ''individual'' <<.place param-value>> has the following syntax: - -<$railroad text=""" -\start none -\end none -[: param-name [:[[whitespace|"Filter Whitespace"]]] ":" [:[[whitespace|"Filter Whitespace"]]] ] value [: [[whitespace|"Filter Whitespace"]] ] -"""/> - -* The <<.place param-name>> is a sequence of letters (`A`--`Z`, `a`--`z`), digits (`0`--`9`), hyphens (`-`) and underscores (`_`). - -* The <<.place value>> is specified as follows: - -<$railroad text={{$:/editions/tw5.com/railroad/macro-parameter-value}}/> +<<.deprecated-since "5.4.0" "Call Syntax">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/macros/syntax/Procedure Definition Syntax.tid b/editions/tw5.com/tiddlers/macros/syntax/Procedure Definition Syntax.tid index 7a621ee59..957747095 100644 --- a/editions/tw5.com/tiddlers/macros/syntax/Procedure Definition Syntax.tid +++ b/editions/tw5.com/tiddlers/macros/syntax/Procedure Definition Syntax.tid @@ -1,6 +1,6 @@ created: 20240310165023000 modified: 20240310175033730 -tags: [[Procedure Syntax]] +tags: [[Call Syntax]] title: Procedure Definition Syntax type: text/vnd.tiddlywiki @@ -73,7 +73,7 @@ Each ''individual'' <<.place parameter>> has the following syntax: * <<.place default>> is an optional value of a parameter is specified as follows: -<$railroad text={{$:/editions/tw5.com/railroad/macro-parameter-value}}/> +<$railroad text={{$:/editions/tw5.com/railroad/call-parameter-value}}/> !! body diff --git a/editions/tw5.com/tiddlers/macros/syntax/Procedure Syntax.tid b/editions/tw5.com/tiddlers/macros/syntax/Procedure Syntax.tid index 2ef519fcc..a99ac8028 100644 --- a/editions/tw5.com/tiddlers/macros/syntax/Procedure Syntax.tid +++ b/editions/tw5.com/tiddlers/macros/syntax/Procedure Syntax.tid @@ -6,6 +6,6 @@ type: text/vnd.tiddlywiki Plain text description can be found at [[Procedures]] -<> +<> <<.tip "The railroad boxes in the linked tiddlers can be used to navigate.">> diff --git a/editions/tw5.com/tiddlers/procedures/Procedure Calls.tid b/editions/tw5.com/tiddlers/procedures/Procedure Calls.tid index 0078d5396..0327a6956 100644 --- a/editions/tw5.com/tiddlers/procedures/Procedure Calls.tid +++ b/editions/tw5.com/tiddlers/procedures/Procedure Calls.tid @@ -1,56 +1,7 @@ caption: Procedure Calls created: 20221007130006705 -modified: 20230419103154329 -tags: WikiText Procedures +modified: 20260125212303316 title: Procedure Calls type: text/vnd.tiddlywiki -!! Introduction - -This tiddler describes the different ways in which [[procedure|Procedures]] can be called. - -!! Procedure Call Transclusion Shortcut - -To call a [[procedure|Procedures]], place `<<`double angle brackets`>>` around the name and any parameter values. - -``` -<> -``` - -By default, parameters are listed in the same order as in the procedure definition. A parameter can be labelled with its name and a colon to allow them to be listed in a different order. - -If no value is specified for a parameter, the default value given for that parameter in the [[procedure definition|Procedure Definitions]] is used instead. (If no default value was defined, the parameter is blank). - -Each parameter value can be enclosed in `'`single quotes`'`, `"`double quotes`"`, `"""`triple double quotes`"""` or `[[`double square brackets`]]`. Triple double quotes allow a value to contain almost anything. If a value contains no spaces or single or double quotes, it requires no delimiters. - -See the discussion about [[parser modes|WikiText parser mode: macro examples]] - -!! Procedure Calls with <<.wlink TranscludeWidget>> Widget - -The shortcut syntax expands to the <<.wlink TranscludeWidget>> widget with the `$variable` attribute specifying the name of the procedure to transclude. - -``` -<$transclude $variable="my-procedure" param="This is the parameter value"/> -``` - -The widget itself offers greater flexibility than the shortcut syntax, including the ability to specify dynamic parameter values. - -!! Assigning Procedure Calls to Attribute Values - -The text of a procedure can be directly assigned to an attribute of a widget or HTML element. The result of the procedure is not wikified, which means that [[parameter handling|Procedure Parameter Handling]] does not take place. - -``` -
>> -... -
-``` - -!! Using Procedure Calls in Filters - -Procedure calls can be used in filters. The text is not wikified which again means that the parameters will be ignored. - -``` -<$list filter="[]"> -... - -``` \ No newline at end of file +<<.deprecated-since "5.4.0" "Calls">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/procedures/Procedures.tid b/editions/tw5.com/tiddlers/procedures/Procedures.tid index 1643fac18..6d0c81ab5 100644 --- a/editions/tw5.com/tiddlers/procedures/Procedures.tid +++ b/editions/tw5.com/tiddlers/procedures/Procedures.tid @@ -33,6 +33,6 @@ Procedures are implemented as a special kind of [[variable|Variables]]. The only !! Using Procedures * [[Procedure Definitions]] describes how to create procedures -* [[Procedure Calls]] describes how to use procedures +* [[Calls]] describes how to use procedures * [[Procedure Parameter Handling]] describes how procedure parameters work -* [[Procedure Syntax]] is a formal syntax description using railroad diagrams +* [[Call Syntax]] is a formal syntax description using railroad diagrams diff --git a/editions/tw5.com/tiddlers/procedures/calls/Calls.tid b/editions/tw5.com/tiddlers/procedures/calls/Calls.tid new file mode 100644 index 000000000..48822120a --- /dev/null +++ b/editions/tw5.com/tiddlers/procedures/calls/Calls.tid @@ -0,0 +1,56 @@ +caption: Calls +created: 20221007130006705 +modified: 20260125212303316 +tags: WikiText Procedures Functions Macros +title: Calls +type: text/vnd.tiddlywiki + +!! Introduction + +This tiddler describes the different ways in which [[procedures|Procedures]], [[functions|Functions]] and [[macros|Macros]] can be called. See [[Call Syntax]] for a formal description of the syntax. + +!! Call Transclusion Shortcut + +To perform a call, place `<<`double angle brackets`>>` around the callee name and any parameter values. + +``` +<> +``` + +By default, parameters are interpreted as being in the same order as in the definition. A parameter value can be labelled with its name and an equals sign to allow them to be listed in a different order. + +If no value is specified for a parameter, the default value given for that parameter in the [[procedure definition|Procedure Definitions]], [[function definition|Function Definitions]] or [[macro definition|Macro Definitions]] is used instead. (If no default value was defined, the parameter is blank). + +Each parameter value can be enclosed in `'`single quotes`'`, `"`double quotes`"`, `"""`triple double quotes`"""` or `[[`double square brackets`]]`. Triple double quotes allow a value to contain almost anything. If a value contains no spaces or single or double quotes, it requires no delimiters. [[Substituted Attribute Values]] enclosed in single or triple back quotes are also supported. + +See the discussion about [[parser modes|WikiText parser mode: macro examples]] + +!! Calls with <<.wlink TranscludeWidget>> Widget + +The shortcut syntax expands to the <<.wlink TranscludeWidget>> widget with the `$variable` attribute specifying the name of the procedure to transclude. + +``` +<$transclude $variable="my-procedure" param="This is the parameter value"/> +``` + +The widget itself offers greater flexibility than the shortcut syntax, including the ability to override it with a custom widget. + +!! Assigning Results of Calls to Attribute Values + +The text returned from a call can be directly assigned to an attribute of a widget or HTML element. The result of the call is not wikified, which means that [[parameter handling|Procedure Parameter Handling]] does not take place. + +``` +
>> +... +
+``` + +!! Using Calls in Filters + +Calls can be used in filters. The text is not wikified which again means that the parameters will be ignored. + +``` +<$list filter="[]"> +... + +``` \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9055.tid b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9055.tid new file mode 100644 index 000000000..f94c897ed --- /dev/null +++ b/editions/tw5.com/tiddlers/releasenotes/5.4.0/#9055.tid @@ -0,0 +1,42 @@ +title: $:/changenotes/5.4.0/#9055 +description: Dynamic parameters for macro/procedure/function calls +release: 5.4.0 +tags: $:/tags/ChangeNote +change-type: enhancement +change-category: hackability +github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9055 +github-contributors: Jermolene + +This PR extends the handling of macro/procedure/function made via the `<<..>>` syntax to allow parameters to be specified dynamically instead of just as static strings. To indicate the new syntax the colon that usually separates a parameter name from its value is replaced by an equals sign. + +For example, by it is now possible to do things like this: + +``` +<> +``` + +Or even this: + +``` +
>> +``` + +Or this: + +``` +
addprefix[https:] }}}>>> +``` + +Parameters can also be specified for the inner call: + +``` +
addprefix[https:] }}}>>> +``` + +The extended syntax can be used in three different settings: + +* As a standalone construction +* As a widget attribute value +* As a filter operand value + +In all cases, it is now possible to use an equals sign instead of a colon to allow parameter values to be passed as a transclusion, filter expression or nested call. diff --git a/editions/tw5.com/tiddlers/v5.4.0/Improvements to Macro Calls.tid b/editions/tw5.com/tiddlers/v5.4.0/Improvements to Macro Calls.tid new file mode 100644 index 000000000..c46bd93dc --- /dev/null +++ b/editions/tw5.com/tiddlers/v5.4.0/Improvements to Macro Calls.tid @@ -0,0 +1,48 @@ +title: Improvements to Macro Calls in v5.4.0 +tags: v5.4.0 + +<$macrocall $name='wikitext-example-without-html' +src="""\define testmacro(one) +Result: $one$. +\end testmacro + +<> +"""/> + +<$macrocall $name='wikitext-example-without-html' +src="""\function testfunction(one) +[addprefix[Hello]] +\end testfunction + +<> +"""/> + +<$macrocall $name='wikitext-example-without-html' +src="""\define testmacro(one) +Result: $one$. +\end testmacro + +<$text text=<>/> +"""/> + +<$macrocall $name='wikitext-example-without-html' +src="""\function testfunction(one) +[addprefix[Hello]] +\end testfunction + +<$text text=<>/> +"""/> + +<$macrocall $name='wikitext-example' +src="""\define innermacro(one) +:$one$:$one$: +\end innermacro + +\define mymacro(param) +|$param$|$param$| +\end mymacro + +
addprefix[The palette named ]] }}}>>> +Content +
+"""/> diff --git a/editions/tw5.com/tiddlers/variables/Behaviour of called variables depends on how the variable was declared.tid b/editions/tw5.com/tiddlers/variables/Behaviour of called variables depends on how the variable was declared.tid new file mode 100644 index 000000000..e7895140e --- /dev/null +++ b/editions/tw5.com/tiddlers/variables/Behaviour of called variables depends on how the variable was declared.tid @@ -0,0 +1,52 @@ +created: 20230726145210484 +modified: 20260130210336084 +tags: [[Variable Usage]] +title: Behaviour of called variables depends on how the variable was declared +type: text/vnd.tiddlywiki + +\define m1(a1) $a1$ - <<__a1__>> - <> +\procedure p1(a1) $a1$ - <<__a1__>> - <> +\function f1(a1) "$a1$" "-" [<__a1__>] ="-" [] :and[join[ ]] + +Called in normal wikitext context: `<$transclude $variable=myvar/>` or `<>` + +{{Behaviour of variables called via normal wikitext}} + +Called via widget attribute: `
>/>` + +{{Behaviour of variables called via widget attributes}} + +Called via filter operator parameter: `[]` + +{{Behaviour of variables called via filter operator parameter}} + +Called via function call in a filter expression: `[function[.myfun]]` + +{{Behaviour of variables called via filter expression function call}} + +!! Examples + +Below is an example macro, procedure and function definition. All three forms of parameter substitution `$a1$`, `<<__a1__>>`, and `<>` are included in each definition. The output helps illustrate when each form of substitution will or will not have affect. + +``` +\define m1(a1) $a1$ - <<__a1__>> - <> +\procedure p1(a1) $a1$ - <<__a1__>> - <> +\function f1(a1) $a1$ "-" [<__a1__>] ="-" [] :and[join[ ]] +``` + +| !Variable transclusion|!output | +| `<>`|<>| +| `<>`|<>| +| `<>`|<>| +| !Widget attribute|!output | +| `<$text text=<>/>`|<$text text=<>/>| +| `<$text text=<>/>`|<$text text=<>/>| +| `<$text text=<>/>`|<$text text=<>/>| +| !Filter operator parameter|!output | +| `[]`|<$text text={{{[]}}}/>| +| `[]`|<$text text={{{[]}}}/>| +| `[]`|<$text text={{{[]}}}/>| +| !Function call in filter expression|!output | +| `[function[m1],[foo]]`|<$text text={{{[function[m1],[foo]]}}}/>| +| `[function[p1],[foo]]`|<$text text={{{[function[p1],[foo]]}}}/>| +| `[function[f1],[foo]]`|<$text text={{{[function[f1],[foo]]}}}/>| \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/variables/Behaviour of invoked variables depends on how the variable was declared.tid b/editions/tw5.com/tiddlers/variables/Behaviour of invoked variables depends on how the variable was declared.tid index 98fbf6b28..91b9d2782 100644 --- a/editions/tw5.com/tiddlers/variables/Behaviour of invoked variables depends on how the variable was declared.tid +++ b/editions/tw5.com/tiddlers/variables/Behaviour of invoked variables depends on how the variable was declared.tid @@ -1,52 +1,6 @@ created: 20230726145210484 -modified: 20240619211734149 -tags: [[Variable Usage]] -title: Behaviour of invoked variables depends on how the variable was declared +modified: 20260130210336084 +title: Behaviour of called variables depends on how the variable was declared type: text/vnd.tiddlywiki -\define m1(a1) $a1$ - <<__a1__>> - <> -\procedure p1(a1) $a1$ - <<__a1__>> - <> -\function f1(a1) "$a1$" "-" [<__a1__>] ="-" [] :and[join[ ]] - -Invoked in normal wikitext context: `<$transclude $variable=myvar/>` or `<>` - -{{Behaviour of variables invoked via normal wikitext}} - -Invoked via widget attribute: `
>/>` - -{{Behaviour of variables invoked via widget attributes}} - -Invoked via filter operator parameter: `[]` - -{{Behaviour of variables invoked via filter operator parameter}} - -Invoked via function call in a filter expression: `[function[.myfun]]` - -{{Behaviour of variables invoked via filter expression function call}} - -!! Examples - -Below is an example macro, procedure and function definition. All three forms of parameter substitution `$a1$`, `<<__a1__>>`, and `<>` are included in each definition. The output helps illustrate when each form of substitution will or will not have affect. - -``` -\define m1(a1) $a1$ - <<__a1__>> - <> -\procedure p1(a1) $a1$ - <<__a1__>> - <> -\function f1(a1) $a1$ "-" [<__a1__>] ="-" [] :and[join[ ]] -``` - -| !Variable transclusion|!output | -| `<>`|<>| -| `<>`|<>| -| `<>`|<>| -| !Widget attribute|!output | -| `<$text text=<>/>`|<$text text=<>/>| -| `<$text text=<>/>`|<$text text=<>/>| -| `<$text text=<>/>`|<$text text=<>/>| -| !Filter operator parameter|!output | -| `[]`|<$text text={{{[]}}}/>| -| `[]`|<$text text={{{[]}}}/>| -| `[]`|<$text text={{{[]}}}/>| -| !Function call in filter expression|!output | -| `[function[m1],[foo]]`|<$text text={{{[function[m1],[foo]]}}}/>| -| `[function[p1],[foo]]`|<$text text={{{[function[p1],[foo]]}}}/>| -| `[function[f1],[foo]]`|<$text text={{{[function[f1],[foo]]}}}/>| \ No newline at end of file +See [[Behaviour of called variables depends on how the variable was declared]]. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/variables/Behaviour of variables invoked via filter expression function call.tid b/editions/tw5.com/tiddlers/variables/Behaviour of variables invoked via filter expression function call.tid index 52d44c9fd..96ce17682 100644 --- a/editions/tw5.com/tiddlers/variables/Behaviour of variables invoked via filter expression function call.tid +++ b/editions/tw5.com/tiddlers/variables/Behaviour of variables invoked via filter expression function call.tid @@ -6,5 +6,5 @@ type: text/vnd.tiddlywiki |tc-first-col-min-width|k |!how declared|!behaviour| -|\define, <<.wlink SetWidget>>, <<.wlink LetWidget>>, <<.wlink VarsWidget>>, \procedure, \widget|Every function is a variable, but only variables defined using \function are invokable using the <<.olink function>> filter operator. Attempts to use a non-function variable is the same as if the function doesn't exist. The behavior in this case is like the identity function. All filter input is passed unchanged to the output.| +|\define, <<.wlink SetWidget>>, <<.wlink LetWidget>>, <<.wlink VarsWidget>>, \procedure, \widget|Every function is a variable, but only variables defined using \function are callable using the <<.olink function>> filter operator. Attempts to use a non-function variable is the same as if the function doesn't exist. The behavior in this case is like the identity function. All filter input is passed unchanged to the output.| |\function|The body text of the function is treated as a filter expression and evaluated. This filter expression can itself contain a function call. Filter expressions can be factored out into functions arbitrarily deep.| \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/Macro Calls.tid b/editions/tw5.com/tiddlers/wikitext/Macro Calls.tid index eddf28200..3ae3a3cbe 100644 --- a/editions/tw5.com/tiddlers/wikitext/Macro Calls.tid +++ b/editions/tw5.com/tiddlers/wikitext/Macro Calls.tid @@ -1,58 +1,7 @@ caption: Macro Calls created: 20150220182252000 modified: 20230419103154328 -tags: WikiText Macros title: Macro Calls type: text/vnd.tiddlywiki -!! Introduction - -This tiddler describes the different ways in which [[macros|Macros]] can be called. - -!! Macro Call Transclusion Shortcut - -To call a [[macro|Macros]], place `<<`double angle brackets`>>` around the name and any parameter values. - -``` -<> -``` - -By default, parameters are listed in the same order as in the macro's definition. A parameter can be labelled with its name and a colon to allow them to be listed in a different order. - -If no value is specified for a parameter, the default value given for that parameter in the [[macro definition|Macro Definitions]] is used instead. (If no default value was defined, the parameter is blank). - -Each parameter value can be enclosed in `'`single quotes`'`, `"`double quotes`"`, `"""`triple double quotes`"""` or `[[`double square brackets`]]`. Triple double quotes allow a value to contain almost anything. If a value contains no spaces or single or double quotes, it requires no delimiters. - -A more formal [[presentation|Macro Call Syntax]] of this syntax is also available. - -See some [[examples|Macro Calls in WikiText (Examples)]] and discussion about [[parser modes|WikiText parser mode: macro examples]]. - -!! Macro Calls with <<.wlink TranscludeWidget>> Widget - -The shortcut syntax expands to the <<.wlink TranscludeWidget>> widget with the `$variable` attribute specifying the name of the macro to transclude. - -``` -<$transclude $variable="mymacro" param="This is the parameter value"/> -``` - -The widget itself offers greater flexibility than the shortcut syntax, including the ability to specify parameter values. - -!! Assigning Macro Calls to Attribute Values - -The result of a macro can be directly assigned to an attribute of a widget or HTML element. The result of the macro is not wikified, but the [[parameter substitution|Macro Parameter Handling]] is performed. - -``` -
>> -... -
-``` - -!! Using Macro Calls in Filters - -Macro calls can be used in filters: - -``` -<$list filter="[]"> -... - -``` \ No newline at end of file +<<.deprecated-since "5.4.0" "Calls">> \ No newline at end of file diff --git a/plugins/tiddlywiki/wikitext-serialize/rules/macrocallblock.js b/plugins/tiddlywiki/wikitext-serialize/rules/macrocallblock.js index fff204891..6e3499cf2 100644 --- a/plugins/tiddlywiki/wikitext-serialize/rules/macrocallblock.js +++ b/plugins/tiddlywiki/wikitext-serialize/rules/macrocallblock.js @@ -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); } }); } diff --git a/plugins/tiddlywiki/wikitext-serialize/utils/parsetree.js b/plugins/tiddlywiki/wikitext-serialize/utils/parsetree.js index fb1c4e47b..456bf747d 100644 --- a/plugins/tiddlywiki/wikitext-serialize/utils/parsetree.js +++ b/plugins/tiddlywiki/wikitext-serialize/utils/parsetree.js @@ -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;