1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-06-25 06:43:15 +00:00

Make macro parameters available as variables in wikified macros (#3063)

First commit
This commit is contained in:
Jeremy Ruston 2017-12-16 09:10:10 +00:00 committed by GitHub
parent d2ff164c07
commit c83231871d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 33 deletions

View File

@ -48,7 +48,9 @@ MacroCallWidget.prototype.execute = function() {
} }
}); });
// Get the macro value // Get the macro value
var text = this.getVariable(this.parseTreeNode.name || this.getAttribute("$name"),{params: params}), var macroName = this.parseTreeNode.name || this.getAttribute("$name"),
variableInfo = this.getVariableInfo(macroName,{params: params}),
text = variableInfo.text,
parseTreeNodes; parseTreeNodes;
// Are we rendering to HTML? // Are we rendering to HTML?
if(this.renderOutput === "text/html") { if(this.renderOutput === "text/html") {
@ -56,6 +58,21 @@ MacroCallWidget.prototype.execute = function() {
var parser = this.wiki.parseText(this.parseType,text, var parser = this.wiki.parseText(this.parseType,text,
{parseAsInline: !this.parseTreeNode.isBlock}); {parseAsInline: !this.parseTreeNode.isBlock});
parseTreeNodes = parser ? parser.tree : []; parseTreeNodes = parser ? parser.tree : [];
// Wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
var attributes = {};
$tw.utils.each(variableInfo.params,function(param) {
var name = "__" + param.name + "__";
attributes[name] = {
name: name,
type: "string",
value: param.value
};
});
parseTreeNodes = [{
type: "vars",
attributes: attributes,
children: parseTreeNodes
}];
} else { } else {
// Otherwise, we'll render the text // Otherwise, we'll render the text
var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this}); var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this});

View File

@ -83,52 +83,73 @@ options: see below
Options include Options include
params: array of {name:, value:} for each parameter params: array of {name:, value:} for each parameter
defaultValue: default value if the variable is not defined defaultValue: default value if the variable is not defined
Returns an object with the following fields:
params: array of {name:,value:} of parameters passed to wikitext variables
text: text of variable, with parameters properly substituted
*/ */
Widget.prototype.getVariable = function(name,options) { Widget.prototype.getVariableInfo = function(name,options) {
options = options || {}; options = options || {};
var actualParams = options.params || [], var actualParams = options.params || [],
parentWidget = this.parentWidget; parentWidget = this.parentWidget;
// Check for the variable defined in the parent widget (or an ancestor in the prototype chain) // Check for the variable defined in the parent widget (or an ancestor in the prototype chain)
if(parentWidget && name in parentWidget.variables) { if(parentWidget && name in parentWidget.variables) {
var variable = parentWidget.variables[name], var variable = parentWidget.variables[name],
value = variable.value; value = variable.value,
params = this.resolveVariableParameters(variable.params,actualParams);
// Substitute any parameters specified in the definition // Substitute any parameters specified in the definition
value = this.substituteVariableParameters(value,variable.params,actualParams); $tw.utils.each(params,function(param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
});
value = this.substituteVariableReferences(value); value = this.substituteVariableReferences(value);
return value; return {
text: value,
params: params
};
} }
// If the variable doesn't exist in the parent widget then look for a macro module // If the variable doesn't exist in the parent widget then look for a macro module
return this.evaluateMacroModule(name,actualParams,options.defaultValue); return {
text: this.evaluateMacroModule(name,actualParams,options.defaultValue)
};
}; };
Widget.prototype.substituteVariableParameters = function(text,formalParams,actualParams) { /*
if(formalParams) { Simplified version of getVariableInfo() that just returns the text
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call */
paramInfo, paramValue; Widget.prototype.getVariable = function(name,options) {
// Step through each of the parameters in the macro definition return this.getVariableInfo(name,options).text;
for(var p=0; p<formalParams.length; p++) { };
// Check if we've got a macro call parameter with the same name
paramInfo = formalParams[p]; Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
paramValue = undefined; formalParams = formalParams || [];
for(var m=0; m<actualParams.length; m++) { actualParams = actualParams || [];
if(actualParams[m].name === paramInfo.name) { var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
paramValue = actualParams[m].value; paramInfo, paramValue,
} results = [];
// Step through each of the parameters in the macro definition
for(var p=0; p<formalParams.length; p++) {
// Check if we've got a macro call parameter with the same name
paramInfo = formalParams[p];
paramValue = undefined;
for(var m=0; m<actualParams.length; m++) {
if(actualParams[m].name === paramInfo.name) {
paramValue = actualParams[m].value;
} }
// If not, use the next available anonymous macro call parameter
while(nextAnonParameter < actualParams.length && actualParams[nextAnonParameter].name) {
nextAnonParameter++;
}
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
paramValue = actualParams[nextAnonParameter++].value;
}
// If we've still not got a value, use the default, if any
paramValue = paramValue || paramInfo["default"] || "";
// Replace any instances of this parameter
text = $tw.utils.replaceString(text,new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
} }
// If not, use the next available anonymous macro call parameter
while(nextAnonParameter < actualParams.length && actualParams[nextAnonParameter].name) {
nextAnonParameter++;
}
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
paramValue = actualParams[nextAnonParameter++].value;
}
// If we've still not got a value, use the default, if any
paramValue = paramValue || paramInfo["default"] || "";
// Store the parameter name and value
results.push({name: paramInfo.name, value: paramValue});
} }
return text; return results;
}; };
Widget.prototype.substituteVariableReferences = function(text) { Widget.prototype.substituteVariableReferences = function(text) {

View File

@ -1,9 +1,9 @@
caption: Macro Definitions
created: 20150220181617000 created: 20150220181617000
modified: 20150221221642000 modified: 20171215152754837
tags: WikiText tags: WikiText
title: Macro Definitions in WikiText title: Macro Definitions in WikiText
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
caption: Macro Definitions
A [[macro|Macros]] is defined using a `\define` [[pragma|Pragma]]. Like any pragma, this can only appear at the start of a tiddler. A [[macro|Macros]] is defined using a `\define` [[pragma|Pragma]]. Like any pragma, this can only appear at the start of a tiddler.
@ -35,6 +35,36 @@ eg="""<$set name="address" value="Rabbit Hole Hill">
</$set>"""/> </$set>"""/>
</$importvariables> </$importvariables>
!! Parameters as Variables
The parameters to a wikitext macro are also available as special variables named as the parameter name wrapped in double underscores. For example, the example above could also be expressed as:
```
\define sayhi(name:"Bugs Bunny") Hi, I'm <$text text=<<__name__>>/>.
```
Accessing parameters as variables only works in macros that are wikified and not, for example, when a macro is used as an attribute value. The advantage of the technique is that it avoids the parameter value being substituted into the macro as a literal string, which in turn can help avoid issues with parameters that contain quotes.
For example, consider this macro. It is intended to wrap a DIV around another macro invocation, passing through the single parameter to the inner macro:
```
\define related-tags(base-tag)
<div class="wrapper">
<$macrocall $name="anothermacro" param="""$base-tag$"""/>
</div>
\end
```
The code above will fail if the macro is invoked with the argument containing triple double quotes (for example `<<related-tags 'Triple """ Quotes'>>`). Using parameter variables offers a workaround:
```
\define related-tags(base-tag)
<div class="wrapper">
<$macrocall $name="anothermacro" param=<<__base-tag__>>/>
</div>
\end
```
!! Scope !! Scope
Macros are available to the tiddler that defines them, plus any tiddlers that it transcludes. Macros are available to the tiddler that defines them, plus any tiddlers that it transcludes.