1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-09-26 22:28:18 +00:00

Refactor function evaluation code to avoid adding evaluateVariable method

Adding the new widget method was causing backwards compatibility issues. For example, see this discussion:

https://talk.tiddlywiki.org/t/tw-v5-3-0-pre-problem-with-autocomplete-plugin/6958
This commit is contained in:
jeremy@jermolene.com 2023-06-10 08:58:04 +01:00
parent 98e72558d0
commit cc383e6d1e
5 changed files with 43 additions and 82 deletions

View File

@ -269,7 +269,8 @@ exports.compileFilter = function(filterString) {
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle); operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
} else if(operand.variable) { } else if(operand.variable) {
var varTree = $tw.utils.parseFilterVariable(operand.text); var varTree = $tw.utils.parseFilterVariable(operand.text);
operand.value = widget.evaluateVariable(varTree.name,{params: varTree.params, source: source})[0] || ""; var variableInfo = widget.getVariableInfo(varTree.name,{params: varTree.params, source: source});
operand.value = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || "";
} else { } else {
operand.value = operand.text; operand.value = operand.text;
} }

View File

@ -17,9 +17,13 @@ Export our filter function
*/ */
exports.function = function(source,operator,options) { exports.function = function(source,operator,options) {
var functionName = operator.operands[0], var functionName = operator.operands[0],
variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName); params = [];
$tw.utils.each(operator.operands.slice(1),function(param) {
params.push({value: param});
});
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
return options.widget.evaluateVariable(functionName,{params: operator.operands.slice(1), source: source}); return variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
} }
// Return the input list if the function wasn't found // Return the input list if the function wasn't found
var results = []; var results = [];

View File

@ -22,9 +22,13 @@ Export our filter function
exports["[unknown]"] = function(source,operator,options) { exports["[unknown]"] = function(source,operator,options) {
// Check for a user defined filter operator // Check for a user defined filter operator
if(operator.operator.indexOf(".") !== -1) { if(operator.operator.indexOf(".") !== -1) {
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator); var params = [];
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { $tw.utils.each(operator.operands,function(param) {
var list = options.widget.evaluateVariable(operator.operator,{params: operator.operands, source: source}); params.push({value: param});
});
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
if(variableInfo && variableInfo.srcVariable) {
var list = variableInfo.resultList ? variableInfo.resultList : [variableInfo.text];
if(operator.prefix === "!") { if(operator.prefix === "!") {
var results = []; var results = [];
source(function(tiddler,title) { source(function(tiddler,title) {

View File

@ -179,22 +179,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
if(variableInfo.text) { if(variableInfo.text) {
if(srcVariable.isFunctionDefinition) { if(srcVariable.isFunctionDefinition) {
// Function to return parameters by name or position // Function to return parameters by name or position
var fnGetParam = function(name,index) { var result = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || "";
// 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 = { parser = {
tree: [{ tree: [{
type: "text", type: "text",

View File

@ -112,14 +112,18 @@ Get the prevailing value of a context variable
name: name of variable name: name of variable
options: see below 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
source: optional source iterator for evaluating function invocations
allowSelfAssigned: if true, includes the current widget in the context chain instead of just the parent allowSelfAssigned: if true, includes the current widget in the context chain instead of just the parent
Returns an object with the following fields: Returns an object with the following fields:
params: array of {name:,value:} of parameters passed to wikitext variables params: array of {name:,value:} or {value:} of parameters to be applied
text: text of variable, with parameters properly substituted text: text of variable, with parameters properly substituted
resultList: result of variable evaluation as an array
srcVariable: reference to the object defining the variable
*/ */
Widget.prototype.getVariableInfo = function(name,options) { Widget.prototype.getVariableInfo = function(name,options) {
options = options || {}; options = options || {};
@ -135,7 +139,8 @@ Widget.prototype.getVariableInfo = function(name,options) {
if(variable) { if(variable) {
var originalValue = variable.value, var originalValue = variable.value,
value = originalValue, value = originalValue,
params = []; params = [],
resultList = [value];
// Only substitute parameter and variable references if this variable was defined with the \define pragma // Only substitute parameter and variable references if this variable was defined with the \define pragma
if(variable.isMacroDefinition) { if(variable.isMacroDefinition) {
params = self.resolveVariableParameters(variable.params,actualParams); params = self.resolveVariableParameters(variable.params,actualParams);
@ -144,10 +149,28 @@ Widget.prototype.getVariableInfo = function(name,options) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value); value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
}); });
value = self.substituteVariableReferences(value,options); value = self.substituteVariableReferences(value,options);
resultList = [value];
} else if(variable.isFunctionDefinition) {
// Function evaluations
params = self.resolveVariableParameters(variable.params,actualParams);
var variables = Object.create(null);
// Apply default parameter values
$tw.utils.each(variable.params,function(param,index) {
if(param["default"]) {
variables[param.name] = param["default"];
}
});
// Parameters are an array of {value:} or {name:, value:} pairs
$tw.utils.each(params,function(param) {
variables[param.name] = param.value;
});
resultList = this.wiki.filterTiddlers(value,this.makeFakeWidgetWithVariables(variables),options.source);
value = resultList[0] || "";
} }
return { return {
text: value, text: value,
params: params, params: params,
resultList: resultList,
srcVariable: variable, srcVariable: variable,
isCacheable: originalValue === value isCacheable: originalValue === value
}; };
@ -159,6 +182,7 @@ Widget.prototype.getVariableInfo = function(name,options) {
} }
return { return {
text: text, text: text,
resultList: [text],
srcVariable: {} srcVariable: {}
}; };
}; };
@ -317,62 +341,11 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
}; };
}, },
makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables, makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables,
evaluateVariable: self.evaluateVariable,
resolveVariableParameters: self.resolveVariableParameters, resolveVariableParameters: self.resolveVariableParameters,
wiki: self.wiki wiki: self.wiki
}; };
}; };
/*
Evaluate a variable and associated actual parameters and return 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(this.resolveVariableParameters(variableInfo.srcVariable.params,params),function(param) {
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. Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed.
Options include: Options include:
@ -406,13 +379,7 @@ Widget.prototype.computeAttribute = function(attribute) {
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || ""; value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || "";
} else if(attribute.type === "macro") { } else if(attribute.type === "macro") {
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params}); var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { value = variableInfo.text;
// 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;
}
} else { // String attribute } else { // String attribute
value = attribute.value; value = attribute.value;
} }