1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-06-16 10:29:54 +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
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;
// Are we rendering to HTML?
if(this.renderOutput === "text/html") {
@ -56,6 +58,21 @@ MacroCallWidget.prototype.execute = function() {
var parser = this.wiki.parseText(this.parseType,text,
{parseAsInline: !this.parseTreeNode.isBlock});
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 {
// Otherwise, we'll render the text
var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this});

View File

@ -83,52 +83,73 @@ options: see below
Options include
params: array of {name:, value:} for each parameter
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 || {};
var actualParams = options.params || [],
parentWidget = this.parentWidget;
// Check for the variable defined in the parent widget (or an ancestor in the prototype chain)
if(parentWidget && name in parentWidget.variables) {
var variable = parentWidget.variables[name],
value = variable.value;
value = variable.value,
params = this.resolveVariableParameters(variable.params,actualParams);
// 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);
return value;
return {
text: value,
params: params
};
}
// 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) {
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
paramInfo, paramValue;
// 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;
}
/*
Simplified version of getVariableInfo() that just returns the text
*/
Widget.prototype.getVariable = function(name,options) {
return this.getVariableInfo(name,options).text;
};
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
formalParams = formalParams || [];
actualParams = actualParams || [];
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
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) {

View File

@ -1,9 +1,9 @@
caption: Macro Definitions
created: 20150220181617000
modified: 20150221221642000
modified: 20171215152754837
tags: WikiText
title: Macro Definitions in WikiText
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.
@ -35,6 +35,36 @@ eg="""<$set name="address" value="Rabbit Hole Hill">
</$set>"""/>
</$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
Macros are available to the tiddler that defines them, plus any tiddlers that it transcludes.