1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-07-16 00:32:44 +00:00

Compare commits

...

23 Commits

Author SHA1 Message Date
Jeremy Ruston e1625afab5 Docs updates 2026-01-31 13:10:02 +00:00
Jeremy Ruston 2e89771129 Merge branch 'master' into dynamic-macro-calls 2026-01-30 20:58:05 +00:00
Jeremy Ruston b6a91521d6 Docs updates 2026-01-25 21:31:53 +00:00
Jeremy Ruston 8ebf052e25 On reflection, new style values should not be allowed for anonymous parameters
Backwards compatibility
2026-01-25 20:27:01 +00:00
Jeremy Ruston a29ae908e2 Only allow new style parameter values if the separator is an equals sign 2026-01-25 20:21:39 +00:00
Jeremy Ruston ff352d63da Allow single closing square brackets within double square brackets quoted strings 2026-01-25 19:56:53 +00:00
Jeremy Ruston 4729e15925 Add change note 2026-01-25 19:35:23 +00:00
Jeremy Ruston 7ec985f8e6 Remove debug logging 2026-01-25 19:26:42 +00:00
Jeremy Ruston cadddc13dc Genuflecting to the linter 2026-01-25 19:23:44 +00:00
Jeremy Ruston a2f38ef51d Merge branch 'master' into dynamic-macro-calls 2026-01-25 19:19:15 +00:00
Jeremy Ruston 54ebf60a05 Merge branch 'master' into dynamic-macro-calls 2025-12-18 21:09:07 +00:00
Jeremy Ruston 5d72fb0608 Minor docs update 2025-12-18 21:08:54 +00:00
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
Jeremy Ruston b3144300ee Temporarily disable a broken serializer test 2025-11-23 21:04:04 +00:00
Jeremy Ruston 205e8cf9c9 Fix test 2025-11-23 21:03:32 +00:00
Jeremy Ruston ad4c1ca5a1 Merge branch 'master' into dynamic-macro-calls 2025-11-23 20:59:41 +00:00
Jeremy Ruston 5136e33f07 Tweak examples 2025-05-15 15:09:05 +01:00
Jeremy Ruston 744f6e7a3b Add some examples 2025-05-12 17:43:45 +01:00
Jeremy Ruston 1ded43f6ea Test for attribute macros 2025-05-12 17:32:30 +01:00
Jeremy Ruston 48e6863a89 Parse attribute macros with the new parser
This fixes the tests
2025-05-12 17:16:08 +01:00
Jeremy Ruston f5d5a23aff Test for standalone macro calls with dynamic parameters 2025-05-12 14:26:21 +01:00
Jeremy Ruston 2d6b6b5011 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.
2025-05-12 14:03:29 +01:00
35 changed files with 880 additions and 277 deletions
+153 -27
View File
@@ -107,13 +107,14 @@ exports.parseStringLiteral = function(source,pos) {
type: "string", type: "string",
start: pos start: pos
}; };
var reString = /(?:"""([\s\S]*?)"""|"([^"]*)")|(?:'([^']*)')/g; var reString = /(?:"""([\s\S]*?)"""|"([^"]*)")|(?:'([^']*)')|\[\[((?:[^\]]|\](?!\]))*)\]\]/g;
reString.lastIndex = pos; reString.lastIndex = pos;
var match = reString.exec(source); var match = reString.exec(source);
if(match && match.index === pos) { if(match && match.index === pos) {
node.value = match[1] !== undefined ? match[1] :( 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; node.end = pos + match[0].length;
return node; return node;
} else { } else {
@@ -173,7 +174,7 @@ exports.parseMacroParameter = function(source,pos) {
start: pos start: pos
}; };
// Define our regexp // 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 // Skip whitespace
pos = $tw.utils.skipWhiteSpace(source,pos); pos = $tw.utils.skipWhiteSpace(source,pos);
// Look for the parameter // 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:} Look for a macro invocation. Returns null if not found, or {type: "transclude", attributes:, start:, end:}
*/ */
exports.parseMacroInvocationAsTransclusion = function(source,pos) { exports.parseMacroInvocationAsTransclusion = function(source,pos) {
var node = $tw.utils.parseMacroInvocation(source,pos); var node = {
if(node) { type: "transclude",
var positionalName = 0, start: pos,
transclusion = { attributes: {},
type: "transclude", orderedAttributes: []
start: node.start, };
end: node.end // Define our regexps
}; var reVarName = /([^\s>"'=:]+)/g;
$tw.utils.addAttributeToParseTreeNode(transclusion,"$variable",node.name); // Skip whitespace
$tw.utils.each(node.params,function(param) { pos = $tw.utils.skipWhiteSpace(source,pos);
var name = param.name; // Look for a double opening angle bracket
if(name) { var token = $tw.utils.parseTokenString(source,pos,"<<");
if(name.charAt(0) === "$") { if(!token) {
name = "$" + name; return null;
}
$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;
} }
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; 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) { exports.parseAttribute = function(source,pos) {
var node = { var node = {
@@ -354,7 +479,7 @@ exports.parseAttribute = function(source,pos) {
node.value = unquotedValue.match[1]; node.value = unquotedValue.match[1];
} else { } else {
// Look for a macro invocation value // Look for a macro invocation value
var macroInvocation = $tw.utils.parseMacroInvocation(source,pos); var macroInvocation = $tw.utils.parseMacroInvocationAsTransclusion(source,pos);
if(macroInvocation) { if(macroInvocation) {
pos = macroInvocation.end; pos = macroInvocation.end;
node.type = "macro"; node.type = "macro";
@@ -375,6 +500,7 @@ exports.parseAttribute = function(source,pos) {
} }
} }
} else { } else {
// If there is no equals sign or colon, then this is an attribute with no value, defaulting to "true"
node.type = "string"; node.type = "string";
node.value = "true"; node.value = "true";
} }
@@ -37,7 +37,7 @@ exports.parse = function() {
var paramString = this.match[2], var paramString = this.match[2],
params = []; params = [];
if(paramString !== "") { 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); paramMatch = reParam.exec(paramString);
while(paramMatch) { while(paramMatch) {
// Save the parameter details // Save the parameter details
+15 -1
View File
@@ -414,7 +414,21 @@ Widget.prototype.computeAttribute = function(attribute,options) {
value = [value]; value = [value];
} }
} else if(attribute.type === "macro") { } 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) { if(options.asList) {
value = variableInfo.resultList; value = variableInfo.resultList;
} else { } else {
@@ -1,11 +1,13 @@
created: 20150220191009000 created: 20150220191009000
modified: 20150602092431500 modified: 20150602092431500
title: $:/editions/tw5.com/railroad/macro-parameter-value title: $:/editions/tw5.com/railroad/call-parameter-value
type: text/vnd.tiddlywiki.railroad type: text/vnd.tiddlywiki.railroad
( '"""' [:{/'tout sauf """'/}] '"""' ( '"""' [:{/'tout sauf """'/}] '"""'
| '"' [:{/'tout sauf "'/}] '"' | '"' [:{/'tout sauf "'/}] '"'
| "'" [:{/"tout sauf '"/}] "'" | "'" [:{/"tout sauf '"/}] "'"
| "[[" [:{/"tout sauf ]"/}] "]]" | "[[" [:{/"tout sauf ]"/}] "]]"
| "`" [:{/"tout sauf `"/}] "`"
| "```" [:{/"tout sauf ```"/}] "```"
| {/"""tout sauf ' " ou espacevierge"""/} | {/"""tout sauf ' " ou espacevierge"""/}
) )
@@ -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<<dp>> The <<.place valeur>> is specified as follows<<dp>>
<$railroad text={{$:/editions/tw5.com/railroad/macro-parameter-value}}/> <$railroad text={{$:/editions/tw5.com/railroad/call-parameter-value}}/>
@@ -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<<:>> 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<<:>> La définition de la <<.place suite>> se fait comme suit<<:>>
@@ -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=<<mamacromamacro>>/>
-
<$text text=<<mamacromamacro param={{{ [[a]addprefix[b]] }}}>>/>
-
<$text text=<<mamacromamacro param>>/>
+
title: ExpectedResult
<p>It is red
-
It is ba
-
It is param
</p>
@@ -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
<<mamacro>>
<<mamacro one={{{ [[b]addprefix[a]] }}}>>
<<mamacro one>>
+
title: ExpectedResult
<p>It is red and green or red and green.</p><p>It is ab and green or ab and green.</p><p>It is one and green or one and green.</p>
@@ -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)$`>>
@@ -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">>/>
@@ -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}}>>/>
@@ -7,3 +7,7 @@ type: text/vnd.tiddlywiki
<<.def "macro calls">> <<.def "macro calls">>
<<alert "primary" "primary alert" width:"60%">> <<alert "primary" "primary alert" width:"60%">>
<<john one:val1 two:val2 three:"quoted value">>
<<test unquoted:value quoted:"value" number:123>>
@@ -3,3 +3,5 @@ title: Serialize/MacroCallInline
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
These are macro calls in a line: <<name "value" "value2">> and <<.def "macro calls">> <<alert "primary" "primary alert" width:"60%">> 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">>.
@@ -235,11 +235,307 @@ describe("HTML tag new parser tests", function() {
expect(parser.parseTag("< $mytag attrib1='something' attrib2=else thing>",0)).toEqual( expect(parser.parseTag("< $mytag attrib1='something' attrib2=else thing>",0)).toEqual(
null null
); );
expect(parser.parseTag("<$mytag attrib3=<<myMacro one:two three:'four and five'>>>",0)).toEqual( expect(parser.parseTag("<$mytag attrib3=<<myMacro one:two three:'four and five'>>>", 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 } {
"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=<<myMacro one:two three:'four and five'>>>",0)).toEqual( expect(parser.parseTag("<$mytag attrib1='something' attrib2=else thing attrib3=<<myMacro one:two three:'four and five'>>>",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
}
); );
}); });
@@ -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( 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( expect(parse("ignored << carrots <<john>>")).toEqual(
@@ -287,7 +287,7 @@ describe("WikiText parser tests", function() {
); );
expect(parse("text <<outie one:'my <<innie>>' >>")).toEqual( 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( 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( expect(parse("<< carrots\n\n<<john>>")).toEqual(
@@ -321,12 +321,12 @@ describe("WikiText parser tests", function() {
); );
expect(parse("<<multiline arg:\"\"\"\n\nwikitext\n\"\"\" >>")).toEqual( 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( 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 } ]
); );
}); });
@@ -334,23 +334,23 @@ describe("WikiText parser tests", function() {
it("should parse tricky macrocall parameters", function() { it("should parse tricky macrocall parameters", function() {
expect(parse("<<john pa>am>>")).toEqual( expect(parse("<<john pa>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("<<john param> >>")).toEqual( expect(parse("<<john param> >>")).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("<<john param>>>")).toEqual( expect(parse("<<john param>>>")).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 // equals signs should be allowed
expect(parse("<<john var>=4 >>")).toEqual( expect(parse("<<john var>=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"}]
); );
@@ -1,6 +1,6 @@
caption: function caption: function
created: 20220909111836951 created: 20220909111836951
modified: 20230419103154328 modified: 20260130210336084
op-input: a [[selection of titles|Title Selection]] passed as input to the function <<.place F>> 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-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 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 title: function Operator
type: text/vnd.tiddlywiki 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. The first parameter of the <<.op function>> operator specifies the name of the function to be called. Subsequent parameters are passed to the function.
@@ -16,12 +16,13 @@ Functions are usually defined with the [[Pragma: \function]]:
\end \end
``` ```
Functions can be invoked in several ways: Functions can be called in several ways:
* Directly transclude functions with the syntax `<<myfun param:"value">>` * Using the [[Calls]] syntax:
* Assign functions to widget attributes with the syntax `<div class=<<myfun param:"value">>>` ** Directly transclude functions with the syntax `<<myfun param:"value">>`
* Invoke functions via the [[function Operator]] with the syntax `[function[myfun],[value],...]` ** Assign functions to widget attributes with the syntax `<div class=<<myfun param:"value">>>`
* Directly invoke functions whose names contain a period as custom filter operators with the syntax `[my.fun[value]]` or `[.myfun[value]]` * 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 !! How Functions Work
@@ -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.
@@ -1,11 +1,13 @@
created: 20150220191009000 created: 20150220191009000
modified: 20150221111554000 modified: 20260125212303316
title: $:/editions/tw5.com/railroad/macro-parameter-value title: $:/editions/tw5.com/railroad/call-parameter-value
type: text/vnd.tiddlywiki.railroad type: text/vnd.tiddlywiki.railroad
( '"""' [:{/'anything but """'/}] '"""' ( '"""' [:{/'anything but """'/}] '"""'
| '"' [:{/'anything but "'/}] '"' | '"' [:{/'anything but "'/}] '"'
| "'" [:{/"anything but '"/}] "'" | "'" [:{/"anything but '"/}] "'"
| "[[" [:{/"anything but ]"/}] "]]" | "[[" [:{/"anything but ]"/}] "]]"
| "`" [:{/"anything but `"/}] "`"
| "```" [:{/"anything but ```"/}] "```"
| {/"""anything but ' " or whitespace"""/} | {/"""anything but ' " or whitespace"""/}
) )
@@ -1,31 +1,7 @@
created: 20150221105732000 created: 20150221105732000
modified: 20150221222352000 modified: 20260125212303316
tags: [[Macro Syntax]] $:/deprecated tags: $:/deprecated
title: Macro Call Syntax title: Macro Call Syntax
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
<<.deprecated-since "5.3.0" "Procedure Call Syntax">> <<.deprecated-since "5.3.0" "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}}/>
@@ -37,7 +37,7 @@ param-name [: [:space] ":" [:space] default ]
The optional <<.place default>> value of a parameter is specified as follows: 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: The <<.place rest>> of the definition has the following syntax:
@@ -1,33 +1,6 @@
created: 20240310165023000 created: 20240310165023000
modified: 20240310172648116 modified: 20260125212303316
tags: [[Procedure Syntax]]
title: Procedure Call Syntax title: Procedure Call Syntax
type: text/vnd.tiddlywiki 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]].""">> <<.deprecated-since "5.4.0" "Call Syntax">>
!! 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}}/>
@@ -1,6 +1,6 @@
created: 20240310165023000 created: 20240310165023000
modified: 20240310175033730 modified: 20240310175033730
tags: [[Procedure Syntax]] tags: [[Call Syntax]]
title: Procedure Definition Syntax title: Procedure Definition Syntax
type: text/vnd.tiddlywiki 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: * <<.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 !! body
@@ -6,6 +6,6 @@ type: text/vnd.tiddlywiki
Plain text description can be found at [[Procedures]] Plain text description can be found at [[Procedures]]
<<list-links filter:"[tag[Procedure Syntax]]">> <<list-links filter:"[tag[Call Syntax]]">>
<<.tip "The railroad boxes in the linked tiddlers can be used to navigate.">> <<.tip "The railroad boxes in the linked tiddlers can be used to navigate.">>
@@ -1,56 +1,7 @@
caption: Procedure Calls caption: Procedure Calls
created: 20221007130006705 created: 20221007130006705
modified: 20230419103154329 modified: 20260125212303316
tags: WikiText Procedures
title: Procedure Calls title: Procedure Calls
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
!! Introduction <<.deprecated-since "5.4.0" "Calls">>
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.
```
<<my-procedure param:"This is the parameter value">>
```
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.
```
<div class=<<myclasses>>>
...
</div>
```
!! 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="[<my-procedure>]">
...
</$list>
```
@@ -33,6 +33,6 @@ Procedures are implemented as a special kind of [[variable|Variables]]. The only
!! Using Procedures !! Using Procedures
* [[Procedure Definitions]] describes how to create 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 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
@@ -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.
```
<<my-procedure param="This is the parameter value">>
```
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.
```
<div class=<<myclasses>>>
...
</div>
```
!! 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="[<my-procedure>]">
...
</$list>
```
@@ -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:
```
<<mymacro param={{Something}}>>
```
Or even this:
```
<div class=<<mymacro param={{Something}}>>>
```
Or this:
```
<div class=<<mymacro param={{{ [<myvar>addprefix[https:] }}}>>>
```
Parameters can also be specified for the inner call:
```
<div class=<<mymacro param={{{ [<innermacro p={{Something}}>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.
@@ -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
<<testmacro one={{{ [[There]addprefix[Hello]] }}}>>
"""/>
<$macrocall $name='wikitext-example-without-html'
src="""\function testfunction(one)
[<one>addprefix[Hello]]
\end testfunction
<<testfunction one={{{ [[re]addprefix[The]] }}}>>
"""/>
<$macrocall $name='wikitext-example-without-html'
src="""\define testmacro(one)
Result: $one$.
\end testmacro
<$text text=<<testmacro one={{{ [[There]addprefix[Hello]] }}}>>/>
"""/>
<$macrocall $name='wikitext-example-without-html'
src="""\function testfunction(one)
[<one>addprefix[Hello]]
\end testfunction
<$text text=<<testfunction one={{{ [[re]addprefix[The]] }}}>>/>
"""/>
<$macrocall $name='wikitext-example'
src="""\define innermacro(one)
:$one$:$one$:
\end innermacro
\define mymacro(param)
|$param$|$param$|
\end mymacro
<div class=<<mymacro param={{{ [<innermacro one={{$:/palette}}>addprefix[The palette named ]] }}}>>>
Content
</div>
"""/>
@@ -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__>> - <<a1>>
\procedure p1(a1) $a1$ - <<__a1__>> - <<a1>>
\function f1(a1) "$a1$" "-" [<__a1__>] ="-" [<a1>] :and[join[ ]]
Called in normal wikitext context: `<$transclude $variable=myvar/>` or `<<myvar>>`
{{Behaviour of variables called via normal wikitext}}
Called via widget attribute: `<div class=<<myvar>>/>`
{{Behaviour of variables called via widget attributes}}
Called via filter operator parameter: `[<myvar>]`
{{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 `<<a1>>` 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__>> - <<a1>>
\procedure p1(a1) $a1$ - <<__a1__>> - <<a1>>
\function f1(a1) $a1$ "-" [<__a1__>] ="-" [<a1>] :and[join[ ]]
```
| !Variable transclusion|!output |
| `<<m1 foo>>`|<<m1 foo>>|
| `<<p1 foo>>`|<<p1 foo>>|
| `<<f1 foo>>`|<<f1 foo>>|
| !Widget attribute|!output |
| `<$text text=<<m1 foo>>/>`|<$text text=<<m1 foo>>/>|
| `<$text text=<<p1 foo>>/>`|<$text text=<<p1 foo>>/>|
| `<$text text=<<f1 foo>>/>`|<$text text=<<f1 foo>>/>|
| !Filter operator parameter|!output |
| `[<m1 foo>]`|<$text text={{{[<m1 foo>]}}}/>|
| `[<p1 foo>]`|<$text text={{{[<p1 foo>]}}}/>|
| `[<f1 foo>]`|<$text text={{{[<f1 foo>]}}}/>|
| !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]]}}}/>|
@@ -1,52 +1,6 @@
created: 20230726145210484 created: 20230726145210484
modified: 20240619211734149 modified: 20260130210336084
tags: [[Variable Usage]] title: Behaviour of called variables depends on how the variable was declared
title: Behaviour of invoked variables depends on how the variable was declared
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
\define m1(a1) $a1$ - <<__a1__>> - <<a1>> See [[Behaviour of called variables depends on how the variable was declared]].
\procedure p1(a1) $a1$ - <<__a1__>> - <<a1>>
\function f1(a1) "$a1$" "-" [<__a1__>] ="-" [<a1>] :and[join[ ]]
Invoked in normal wikitext context: `<$transclude $variable=myvar/>` or `<<myvar>>`
{{Behaviour of variables invoked via normal wikitext}}
Invoked via widget attribute: `<div class=<<myvar>>/>`
{{Behaviour of variables invoked via widget attributes}}
Invoked via filter operator parameter: `[<myvar>]`
{{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 `<<a1>>` 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__>> - <<a1>>
\procedure p1(a1) $a1$ - <<__a1__>> - <<a1>>
\function f1(a1) $a1$ "-" [<__a1__>] ="-" [<a1>] :and[join[ ]]
```
| !Variable transclusion|!output |
| `<<m1 foo>>`|<<m1 foo>>|
| `<<p1 foo>>`|<<p1 foo>>|
| `<<f1 foo>>`|<<f1 foo>>|
| !Widget attribute|!output |
| `<$text text=<<m1 foo>>/>`|<$text text=<<m1 foo>>/>|
| `<$text text=<<p1 foo>>/>`|<$text text=<<p1 foo>>/>|
| `<$text text=<<f1 foo>>/>`|<$text text=<<f1 foo>>/>|
| !Filter operator parameter|!output |
| `[<m1 foo>]`|<$text text={{{[<m1 foo>]}}}/>|
| `[<p1 foo>]`|<$text text={{{[<p1 foo>]}}}/>|
| `[<f1 foo>]`|<$text text={{{[<f1 foo>]}}}/>|
| !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]]}}}/>|
@@ -6,5 +6,5 @@ type: text/vnd.tiddlywiki
|tc-first-col-min-width|k |tc-first-col-min-width|k
|!how declared|!behaviour| |!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.| |\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.|
@@ -1,58 +1,7 @@
caption: Macro Calls caption: Macro Calls
created: 20150220182252000 created: 20150220182252000
modified: 20230419103154328 modified: 20230419103154328
tags: WikiText Macros
title: Macro Calls title: Macro Calls
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
!! Introduction <<.deprecated-since "5.4.0" "Calls">>
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.
```
<<mymacro param:"This is the parameter value">>
```
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.
```
<div class=<<myclasses "Horizontal">>>
...
</div>
```
!! Using Macro Calls in Filters
Macro calls can be used in filters:
```
<$list filter="[<mymacro param:'value'>]">
...
</$list>
```
@@ -18,7 +18,7 @@ exports.serialize = function (node) {
if(node.orderedAttributes) { if(node.orderedAttributes) {
node.orderedAttributes.forEach(function (attribute) { node.orderedAttributes.forEach(function (attribute) {
if(attribute.name !== "$variable") { if(attribute.name !== "$variable") {
result += " " + $tw.utils.serializeAttribute(attribute,{assignmentSymbol:":"}); result += " " + $tw.utils.serializeAttribute(attribute);
} }
}); });
} }
@@ -62,14 +62,27 @@ exports.serializeAttribute = function(node,options) {
} }
// If name is number, means it is a positional attribute and name is omitted // If name is number, means it is a positional attribute and name is omitted
var positional = parseInt(node.name) >= 0, var positional = parseInt(node.name) >= 0,
// `=` in a widget and might be `:` in a macro // Use the original assignment operator if available, otherwise default to '='
assign = positional ? "" : (options.assignmentSymbol || "="), assign = positional ? "" : (node.assignmentOperator || "="),
attributeString = positional ? "" : node.name; attributeString = positional ? "" : node.name;
if(node.type === "string") { if(node.type === "string") {
if(node.value === "true") { if(node.value === "true") {
return attributeString; 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") { } else if(node.type === "filtered") {
attributeString += assign + "{{{" + node.filter + "}}}"; attributeString += assign + "{{{" + node.filter + "}}}";
} else if(node.type === "indirect") { } else if(node.type === "indirect") {
@@ -77,11 +90,36 @@ exports.serializeAttribute = function(node,options) {
} else if(node.type === "substituted") { } else if(node.type === "substituted") {
attributeString += assign + "`" + node.rawValue + "`"; attributeString += assign + "`" + node.rawValue + "`";
} else if(node.type === "macro") { } else if(node.type === "macro") {
if(node.value && typeof node.value === "object" && node.value.type === "macrocall") { if(node.value && typeof node.value === "object") {
var params = node.value.params.map(function(param) { if(node.value.type === "transclude") {
return param.value; // Handle the transclude-based macro call structure
}).join(" "); var macroName = node.value.attributes && node.value.attributes["$variable"] ?
attributeString += assign + "<<" + node.value.name + " " + params + ">>"; 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 { } else {
// Unsupported macro structure // Unsupported macro structure
return null; return null;