mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-12-24 17:10:29 +00:00
Refactor function invocation
* Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text * Refactor the function operator and unknown operator to use the new helper * Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated * Refactor the transclude widget to use the new helper * Update tests
This commit is contained in:
parent
d4ab427ceb
commit
34afe4e143
@ -269,7 +269,7 @@ exports.compileFilter = function(filterString) {
|
||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||
} else if(operand.variable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
operand.value = widget.getVariable(varTree.name,{params:varTree.params,defaultValue: ""});
|
||||
operand.value = widget.evaluateVariable(varTree.name,{params: varTree.params, source: source})[0] || "";
|
||||
} else {
|
||||
operand.value = operand.text;
|
||||
}
|
||||
|
@ -17,15 +17,9 @@ Export our filter function
|
||||
*/
|
||||
exports.function = function(source,operator,options) {
|
||||
var functionName = operator.operands[0],
|
||||
customDefinition = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName);
|
||||
if(customDefinition && customDefinition.srcVariable && customDefinition.srcVariable.isFunctionDefinition) {
|
||||
var variables = Object.create(null);
|
||||
// Go through each of the defined parameters, and make a variable with the value of the corresponding operand
|
||||
$tw.utils.each(customDefinition.srcVariable.params,function(param,index) {
|
||||
var value = operator.operands[1 + index]; // Skip over the first operand that gives the function name
|
||||
variables[param.name] = value === undefined ? param["default"] || "" : value;
|
||||
});
|
||||
return options.wiki.filterTiddlers(customDefinition.srcVariable.value,options.widget.makeFakeWidgetWithVariables(variables),source);
|
||||
variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName);
|
||||
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
return options.widget.evaluateVariable(functionName,{params: operator.operands.slice(1), source: source});
|
||||
}
|
||||
// Return the input list if the function wasn't found
|
||||
var results = [];
|
||||
|
@ -22,15 +22,9 @@ Export our filter function
|
||||
exports["[unknown]"] = function(source,operator,options) {
|
||||
// Check for a user defined filter operator
|
||||
if(operator.operator.charAt(0) === ".") {
|
||||
var customDefinition = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator);
|
||||
if(customDefinition && customDefinition.srcVariable && customDefinition.srcVariable.isFunctionDefinition) {
|
||||
var variables = Object.create(null);
|
||||
// Go through each of the defined parameters, and make a variable with the value of the corresponding operand
|
||||
$tw.utils.each(customDefinition.srcVariable.params,function(param,index) {
|
||||
var value = operator.operands[index];
|
||||
variables[param.name] = value === undefined ? param["default"] || "" : value;
|
||||
});
|
||||
var list = options.wiki.filterTiddlers(customDefinition.srcVariable.value,options.widget.makeFakeWidgetWithVariables(variables),source);
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator);
|
||||
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
var list = options.widget.evaluateVariable(operator.operator,{params: operator.operands, source: source});
|
||||
if(operator.prefix === "!") {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
|
@ -176,13 +176,56 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}),
|
||||
srcVariable = variableInfo && variableInfo.srcVariable;
|
||||
if(srcVariable) {
|
||||
var cacheKey = (parseAsInline ? "inlineParser" : "blockParser") + (this.transcludeType || "");
|
||||
if(variableInfo.isCacheable && srcVariable[cacheKey]) {
|
||||
parser = srcVariable[cacheKey];
|
||||
if(srcVariable.isFunctionDefinition) {
|
||||
// Function to return parameters by name or position
|
||||
var fnGetParam = function(name,index) {
|
||||
// Parameter names starting with dollar must be escaped to double dollars
|
||||
if(name.charAt(0) === "$") {
|
||||
name = "$" + name;
|
||||
}
|
||||
// Look for the parameter by name
|
||||
if(self.hasAttribute(name)) {
|
||||
return self.getAttribute(name);
|
||||
// Look for the parameter by index
|
||||
} else if(self.hasAttribute(index + "")) {
|
||||
return self.getAttribute(index + "");
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
result = this.evaluateVariable(this.transcludeVariable,{params: fnGetParam})[0] || "";
|
||||
parser = {
|
||||
tree: [{
|
||||
type: "text",
|
||||
text: result
|
||||
}],
|
||||
source: result,
|
||||
type: "text/vnd.tiddlywiki"
|
||||
};
|
||||
if(parseAsInline) {
|
||||
parser.tree[0] = {
|
||||
type: "text",
|
||||
text: result
|
||||
};
|
||||
} else {
|
||||
parser.tree[0] = {
|
||||
type: "element",
|
||||
tag: "p",
|
||||
children: [{
|
||||
type: "text",
|
||||
text: result
|
||||
}]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace});
|
||||
if(variableInfo.isCacheable) {
|
||||
srcVariable[cacheKey] = parser;
|
||||
var cacheKey = (parseAsInline ? "inlineParser" : "blockParser") + (this.transcludeType || "");
|
||||
if(variableInfo.isCacheable && srcVariable[cacheKey]) {
|
||||
parser = srcVariable[cacheKey];
|
||||
} else {
|
||||
parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace});
|
||||
if(variableInfo.isCacheable) {
|
||||
srcVariable[cacheKey] = parser;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(parser) {
|
||||
@ -206,25 +249,6 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
|
||||
}
|
||||
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"])
|
||||
});
|
||||
} else if(srcVariable.isFunctionDefinition) {
|
||||
var actualParams = this.getOrderedTransclusionParameters(),
|
||||
variables = {};
|
||||
$tw.utils.each(srcVariable.params,function(param,index) {
|
||||
var name = param.name;
|
||||
// Parameter names starting with dollar must be escaped to double dollars
|
||||
if(name.charAt(0) === "$") {
|
||||
name = "$" + name;
|
||||
}
|
||||
if(self.hasAttribute(name)) {
|
||||
variables[name] = self.getAttribute(name);
|
||||
} else if(self.hasAttribute(index + "")) {
|
||||
variables[name] = self.getAttribute(index + "");
|
||||
} else {
|
||||
variables[name] = param["default"] || "";
|
||||
}
|
||||
});
|
||||
var result = this.wiki.filterTiddlers(srcVariable.value,this.makeFakeWidgetWithVariables(variables),this.wiki.makeTiddlerIterator([]))[0] || "";
|
||||
parser = this.wiki.parseText(this.transcludeType,result || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace});
|
||||
} else {
|
||||
// For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
|
||||
parser = {
|
||||
|
@ -170,6 +170,11 @@ Widget.prototype.getVariable = function(name,options) {
|
||||
return this.getVariableInfo(name,options).text;
|
||||
};
|
||||
|
||||
/*
|
||||
Maps actual parameters onto formal parameters, returning an array of {name:,value:} objects
|
||||
formalParams - {name:,default:} (default value is optional)
|
||||
actualParams - {name:,value:} (name is optional)
|
||||
*/
|
||||
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
|
||||
formalParams = formalParams || [];
|
||||
actualParams = actualParams || [];
|
||||
@ -310,10 +315,69 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
||||
return self.getVariableInfo(name,opts);
|
||||
};
|
||||
},
|
||||
makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables
|
||||
makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables,
|
||||
evaluateVariable: self.evaluateVariable,
|
||||
wiki: self.wiki
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Evaluate a variable and associated actual parameters and result the resulting array.
|
||||
The way that the variable is evaluated depends upon its type:
|
||||
* Functions are evaluated as parameterised filter strings
|
||||
* Macros are returned as plain text with substitution of parameters
|
||||
* Procedures and widgets are returned as plain text
|
||||
|
||||
Options are:
|
||||
params - the actual parameters – may be one of:
|
||||
* an array of values that may be an anonymous string value, or a {name:, value:} pair
|
||||
* a hashmap of {name: value} pairs
|
||||
* a function invoked with parameters (name,index) that returns a parameter value by name or position
|
||||
source - iterator for source tiddlers
|
||||
*/
|
||||
Widget.prototype.evaluateVariable = function(name,options) {
|
||||
options = options || {};
|
||||
var params = options.params || [];
|
||||
// Get the details of the variable (includes processing text substitution for macros
|
||||
var variableInfo = this.getVariableInfo(name,{params: params,defaultValue: ""});
|
||||
// Process function parameters
|
||||
var variables = Object.create(null);
|
||||
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
// Apply default parameter values
|
||||
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
|
||||
if(param["default"]) {
|
||||
variables[param.name] = param["default"];
|
||||
}
|
||||
});
|
||||
if($tw.utils.isArray(params)) {
|
||||
// Parameters are an array of values or {name:, value:} pairs
|
||||
$tw.utils.each(params,function(param,index) {
|
||||
if(typeof param === "string") {
|
||||
var paramInfo = variableInfo.srcVariable.params[index];
|
||||
if(paramInfo) {
|
||||
variables[paramInfo.name] = param;
|
||||
}
|
||||
} else {
|
||||
variables[param.name] = param.value;
|
||||
}
|
||||
});
|
||||
} else if(typeof params === "function") {
|
||||
// Parameters are passed via a function
|
||||
$tw.utils.each(variableInfo.srcVariable.params,function(param,index) {
|
||||
variables[param.name] = params(param.name,index) || param["default"] || "";
|
||||
});
|
||||
} else {
|
||||
// Parameters are a hashmap
|
||||
$tw.utils.each(params,function(value,name) {
|
||||
variables[name] = value;
|
||||
});
|
||||
}
|
||||
return this.wiki.filterTiddlers(variableInfo.text,this.makeFakeWidgetWithVariables(variables),options.source);
|
||||
} else {
|
||||
return [variableInfo.text];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed.
|
||||
Options include:
|
||||
@ -348,15 +412,9 @@ Widget.prototype.computeAttribute = function(attribute) {
|
||||
} else if(attribute.type === "macro") {
|
||||
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
|
||||
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
// It's a function definition
|
||||
var variables = Object.create(null);
|
||||
// Go through each of the defined parameters, and make a variable with the value of the corresponding provided parameter
|
||||
var params = this.resolveVariableParameters(variableInfo.srcVariable.params,attribute.value.params);
|
||||
$tw.utils.each(params,function(param,index) {
|
||||
variables[param.name] = param.value;
|
||||
});
|
||||
var list = self.wiki.filterTiddlers(variableInfo.text,this.makeFakeWidgetWithVariables(variables));
|
||||
value = list[0] || "";
|
||||
// It is a function definition. Go through each of the defined parameters, and make a variable with the value of the corresponding provided parameter
|
||||
var paramArray = this.resolveVariableParameters(variableInfo.srcVariable.params,attribute.value.params);
|
||||
value = this.evaluateVariable(attribute.value.name,{params: paramArray})[0] || "";
|
||||
} else {
|
||||
value = variableInfo.text;
|
||||
}
|
||||
|
@ -33,4 +33,4 @@ $param$ with a ''buffalo''
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>Going to lunch with a <strong>buffalo</strong></p><p>Going to breakfastwith a<strong>buffalo</strong></p><p>Going to dinner with a <strong>buffalo</strong></p>Going to lunch with a buffalo with a buffaloGoing to dinner with a buffalo
|
||||
<p>Going to lunch with a ''buffalo''</p><p>Going to breakfastwith a<strong>buffalo</strong></p><p>Going to dinner with a <strong>buffalo</strong></p>Going to lunch with a buffalo with a buffaloGoing to dinner with a buffalo
|
Loading…
Reference in New Issue
Block a user