mirror of
https://github.com/Jermolene/TiddlyWiki5.git
synced 2025-12-05 18:20:38 -08:00
Introduce multi-valued variables and let filter run prefix (#8972)
* Introduce let filter run prefix for assigning filter run result to a variable
* Get rid of the special behaviour for all[]
Not needed because the input to the filter run is available
* Fix tests
* Fix tests
* Cleanup
* Support for saving result lists in a variable
Extend let filter run prefix to store list of results, and add varlist operator for accessing variables as a list.
We already had partial support for variables returning a list of values in order for functions to work, now we extend it so that any variable can be used to store a list
We should extend the set widget so that it returns a result list that can be accessed with the varlist operator
* Docs update
* Introduce letlist widget for assigning lists to variables
Terrible name. Annoyingly, we can't overload the existing let or set widgets.
* Docs update
* Update DefaultTiddlers to highlight the new docs
* Fixed varlist crash with empty parameter
* Switch to triple brace syntax for assigning filtered lists
* Docs update
* Test for multivalued functions
* varlist operator: fixed crash accessing non-existent variable
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2712068743
* Dispense with the letlist widget
What this PR actually does is rename the letlist widget to "let". The result is the same as using the letlist widget, but it is backwards compatible by virtue of the fact that all existing ways to access variables will only see the single value for the variable.
* Refactor the let filter run prefix to assign the input list to the variable named by the filter run
These semantics are much simpler, and allow the variable name to be computed.
* Missed off 211b135265
* Docs update
* Bug fix
* Introduce round brackets for multi-valued filter operands
Allowing us to drop the varlist operator
* Introduce => as a shortcut syntax for the let filter run prefix
Also relax the requirement for a filter run prefix to be followed by an opening square bracket
* Fix bug exposed in "Filter Operators" tiddler
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2740003414
* Fix bug with missing variable attributes
See https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972#issuecomment-2752792329
* Fix bug with round brackets for 2nd parameter onwards
* Allow functions to take multivalued parameters
* Simplify title operator
* Extend title operator to allow negated form to use multi-valued variables
* Remove duplicate test
* Update action-log widget to log multi-valued attributes
* Docs updates
* Fix typos
* Happy linter happy life
* Fix version numbers of from-version procedures
* Another incorrect version number
* Add change note
* Fix filenames of tests
* Typo
* Update let.js
* Docs updates
This commit is contained in:
parent
ed4a186c9c
commit
5d1c1eaf87
33 changed files with 530 additions and 95 deletions
41
core/modules/filterrunprefixes/let.js
Normal file
41
core/modules/filterrunprefixes/let.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/let.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Assign a value to a variable
|
||||
|
||||
\*/
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.let = function(operationSubFunction,options) {
|
||||
// Return the filter run prefix function
|
||||
return function(results,source,widget) {
|
||||
// Save the result list
|
||||
var resultList = results.toArray();
|
||||
// Clear the results
|
||||
results.clear();
|
||||
// Evaluate the subfunction to get the variable name
|
||||
var subFunctionResults = operationSubFunction(source,widget);
|
||||
if(subFunctionResults.length === 0) {
|
||||
return;
|
||||
}
|
||||
var name = subFunctionResults[0];
|
||||
if(typeof name !== "string" || name.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Assign the result of the subfunction to the variable
|
||||
var variables = {};
|
||||
variables[name] = resultList;
|
||||
// Return the variables
|
||||
return {
|
||||
variables: variables
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -35,7 +35,7 @@ function parseFilterOperation(operators,filterString,p) {
|
|||
operator.prefix = filterString.charAt(p++);
|
||||
}
|
||||
// Get the operator name
|
||||
nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/);
|
||||
nextBracketPos = filterString.substring(p).search(/[\[\{<\/\(]/);
|
||||
if(nextBracketPos === -1) {
|
||||
throw "Missing [ in filter expression";
|
||||
}
|
||||
|
|
@ -79,6 +79,10 @@ function parseFilterOperation(operators,filterString,p) {
|
|||
operand.variable = true;
|
||||
nextBracketPos = filterString.indexOf(">",p);
|
||||
break;
|
||||
case "(": // Round brackets
|
||||
operand.multiValuedVariable = true;
|
||||
nextBracketPos = filterString.indexOf(")",p);
|
||||
break;
|
||||
case "/": // regexp brackets
|
||||
var rex = /^((?:[^\\\/]|\\.)*)\/(?:\(([mygi]+)\))?/g,
|
||||
rexMatch = rex.exec(filterString.substring(p));
|
||||
|
|
@ -112,7 +116,7 @@ function parseFilterOperation(operators,filterString,p) {
|
|||
// Check for multiple operands
|
||||
while(filterString.charAt(p) === ",") {
|
||||
p++;
|
||||
if(/^[\[\{<\/]/.test(filterString.substring(p))) {
|
||||
if(/^[\[\{<\/\(]/.test(filterString.substring(p))) {
|
||||
nextBracketPos = p;
|
||||
p++;
|
||||
parseOperand(filterString.charAt(nextBracketPos));
|
||||
|
|
@ -141,7 +145,15 @@ exports.parseFilter = function(filterString) {
|
|||
p = 0, // Current position in the filter string
|
||||
match;
|
||||
var whitespaceRegExp = /(\s+)/mg,
|
||||
operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
// Groups:
|
||||
// 1 - entire filter run prefix
|
||||
// 2 - filter run prefix itself
|
||||
// 3 - filter run prefix suffixes
|
||||
// 4 - opening square bracket following filter run prefix
|
||||
// 5 - double quoted string following filter run prefix
|
||||
// 6 - single quoted string following filter run prefix
|
||||
// 7 - anything except for whitespace and square brackets
|
||||
operandRegExp = /((?:\+|\-|~|(?:=>?)|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
while(p < filterString.length) {
|
||||
// Skip any whitespace
|
||||
whitespaceRegExp.lastIndex = p;
|
||||
|
|
@ -152,38 +164,45 @@ exports.parseFilter = function(filterString) {
|
|||
// Match the start of the operation
|
||||
if(p < filterString.length) {
|
||||
operandRegExp.lastIndex = p;
|
||||
match = operandRegExp.exec(filterString);
|
||||
if(!match || match.index !== p) {
|
||||
throw $tw.language.getString("Error/FilterSyntax");
|
||||
}
|
||||
var operation = {
|
||||
prefix: "",
|
||||
operators: []
|
||||
};
|
||||
if(match[1]) {
|
||||
operation.prefix = match[1];
|
||||
p = p + operation.prefix.length;
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
match = operandRegExp.exec(filterString);
|
||||
if(match && match.index === p) {
|
||||
// If there is a filter run prefix
|
||||
if(match[1]) {
|
||||
operation.prefix = match[1];
|
||||
p = p + operation.prefix.length;
|
||||
// Name for named prefixes
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
// Suffixes for filter run prefix
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
// Opening square bracket
|
||||
if(match[4]) {
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
}
|
||||
}
|
||||
if(match[4]) { // Opening square bracket
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
// No filter run prefix
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
}
|
||||
// Quoted strings and unquoted title
|
||||
if(match[5] || match[6] || match[7]) { // Double quoted string, single quoted string or unquoted title
|
||||
operation.operators.push(
|
||||
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
|
||||
|
|
@ -251,6 +270,8 @@ exports.compileFilter = function(filterString) {
|
|||
results = [];
|
||||
$tw.utils.each(operation.operators,function(operator) {
|
||||
var operands = [],
|
||||
multiValueOperands = [],
|
||||
isMultiValueOperand = [],
|
||||
operatorFunction;
|
||||
if(!operator.operator) {
|
||||
// Use the "title" operator if no operator is specified
|
||||
|
|
@ -266,13 +287,29 @@ exports.compileFilter = function(filterString) {
|
|||
if(operand.indirect) {
|
||||
var currTiddlerTitle = widget && widget.getVariable("currentTiddler");
|
||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||
operand.multiValue = [operand.value];
|
||||
} else if(operand.variable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
|
||||
operand.multiValue = [operand.value];
|
||||
} else if(operand.multiValuedVariable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
|
||||
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
|
||||
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || [];
|
||||
operand.value = operand.multiValue[0] || "";
|
||||
} else {
|
||||
operand.value = "";
|
||||
operand.multiValue = [];
|
||||
}
|
||||
operand.isMultiValueOperand = true;
|
||||
} else {
|
||||
operand.value = operand.text;
|
||||
operand.multiValue = [operand.value];
|
||||
}
|
||||
operands.push(operand.value);
|
||||
multiValueOperands.push(operand.multiValue);
|
||||
isMultiValueOperand.push(!!operand.isMultiValueOperand);
|
||||
});
|
||||
|
||||
// Invoke the appropriate filteroperator module
|
||||
|
|
@ -280,6 +317,8 @@ exports.compileFilter = function(filterString) {
|
|||
operator: operator.operator,
|
||||
operand: operands.length > 0 ? operands[0] : undefined,
|
||||
operands: operands,
|
||||
multiValueOperands: multiValueOperands,
|
||||
isMultiValueOperand: isMultiValueOperand,
|
||||
prefix: operator.prefix,
|
||||
suffix: operator.suffix,
|
||||
suffixes: operator.suffixes,
|
||||
|
|
@ -319,6 +358,8 @@ exports.compileFilter = function(filterString) {
|
|||
return filterRunPrefixes["and"](operationSubFunction, options);
|
||||
case "~": // This operation is unioned into the result only if the main result so far is empty
|
||||
return filterRunPrefixes["else"](operationSubFunction, options);
|
||||
case "=>": // This operation is applied to the main results so far, and the results are assigned to a variable
|
||||
return filterRunPrefixes["let"](operationSubFunction, options);
|
||||
default:
|
||||
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
|
||||
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
|
||||
|
|
@ -345,7 +386,13 @@ exports.compileFilter = function(filterString) {
|
|||
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
||||
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
var operationResult = operationFunction(results,source,widget);
|
||||
if(operationResult) {
|
||||
if(operationResult.variables) {
|
||||
// If the filter run prefix has returned variables, create a new fake widget with those variables
|
||||
widget = widget.makeFakeWidgetWithVariables(operationResult.variables);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push("/**-- Excessive filter recursion --**/");
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ exports.function = function(source,operator,options) {
|
|||
var functionName = operator.operands[0],
|
||||
params = [],
|
||||
results;
|
||||
$tw.utils.each(operator.operands.slice(1),function(param) {
|
||||
params.push({value: param});
|
||||
$tw.utils.each(operator.multiValueOperands.slice(1),function(paramList) {
|
||||
params.push({value: paramList[0] || "",multiValue: paramList});
|
||||
});
|
||||
// console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`);
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@ exports.title = function(source,operator,options) {
|
|||
var results = [];
|
||||
if(operator.prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler && tiddler.fields.title !== operator.operand) {
|
||||
var titleList = operator.multiValueOperands[0] || [];
|
||||
if(tiddler && titleList.indexOf(tiddler.fields.title) === -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push(operator.operand);
|
||||
Array.prototype.push.apply(results,operator.multiValueOperands[0]);
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ exports["[unknown]"] = function(source,operator,options) {
|
|||
// Check for a user defined filter operator
|
||||
if(operator.operator.indexOf(".") !== -1) {
|
||||
var params = [];
|
||||
$tw.utils.each(operator.operands,function(param) {
|
||||
params.push({value: param});
|
||||
$tw.utils.each(operator.multiValueOperands,function(paramList) {
|
||||
params.push({value: paramList[0] || "",multiValue: paramList});
|
||||
});
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
|
||||
if(variableInfo && variableInfo.srcVariable) {
|
||||
|
|
|
|||
|
|
@ -49,14 +49,26 @@ exports.warning = function(text) {
|
|||
};
|
||||
|
||||
/*
|
||||
Log a table of name: value pairs
|
||||
Log a table of name: value or name: [values...] pairs
|
||||
*/
|
||||
exports.logTable = function(data) {
|
||||
if(console.table) {
|
||||
var hasArrays = false;
|
||||
$tw.utils.each(data,function(value,name) {
|
||||
if($tw.utils.isArray(value)) {
|
||||
hasArrays = true;
|
||||
}
|
||||
});
|
||||
if(console.table && !hasArrays) {
|
||||
console.table(data);
|
||||
} else {
|
||||
$tw.utils.each(data,function(value,name) {
|
||||
console.log(name + ": " + value);
|
||||
if($tw.utils.isArray(value)) {
|
||||
for(var t=0; t<value.length; t++) {
|
||||
console.log(`${name}[${t}]: ${value[t]}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`${name}: ${value}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,23 +51,29 @@ LogWidget.prototype.invokeAction = function(triggeringWidget,event) {
|
|||
};
|
||||
|
||||
LogWidget.prototype.log = function() {
|
||||
var data = {},
|
||||
var self = this,
|
||||
data = {}, // Hashmap by attribute name with string or array of string values
|
||||
dataCount,
|
||||
allVars = {},
|
||||
allVars = {}, // Hashmap by variable name with string or array of string values
|
||||
filteredVars;
|
||||
|
||||
$tw.utils.each(this.attributes,function(attribute,name) {
|
||||
// Collect the attributes to be logged
|
||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||
if(name.substring(0,2) !== "$$") {
|
||||
data[name] = attribute;
|
||||
var resultList = self.computeAttribute(attribute,{asList: true});
|
||||
if(resultList.length <= 1) {
|
||||
data[name] = resultList[0] || "";
|
||||
} else {
|
||||
data[name] = resultList;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Collect values of all variables, using the source text for functions
|
||||
for(var v in this.variables) {
|
||||
var variable = this.parentWidget && this.parentWidget.variables[v];
|
||||
if(variable && variable.isFunctionDefinition) {
|
||||
allVars[v] = variable.value;
|
||||
var variableInfo = this.getVariableInfo(v);
|
||||
if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) {
|
||||
allVars[v] = variableInfo.text;
|
||||
} else {
|
||||
allVars[v] = this.getVariable(v,{defaultValue:""});
|
||||
allVars[v] = variableInfo.resultList.length > 1 ? variableInfo.resultList : variableInfo.text;
|
||||
}
|
||||
}
|
||||
if(this.filter) {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ LetWidget.prototype.computeAttributes = function() {
|
|||
self = this;
|
||||
this.currentValueFor = Object.create(null);
|
||||
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(this.parseTreeNode),function(attribute) {
|
||||
var value = self.computeAttribute(attribute),
|
||||
var value = self.computeAttribute(attribute,{asList: true}),
|
||||
name = attribute.name;
|
||||
// Now that it's prepped, we're allowed to look this variable up
|
||||
// when defining later variables
|
||||
|
|
@ -56,7 +56,7 @@ LetWidget.prototype.computeAttributes = function() {
|
|||
});
|
||||
// Run through again, setting variables and looking for differences
|
||||
$tw.utils.each(this.currentValueFor,function(value,name) {
|
||||
if (self.attributes[name] !== value) {
|
||||
if(!$tw.utils.isArrayEqual(self.attributes[name],value)) {
|
||||
self.attributes[name] = value;
|
||||
self.setVariable(name,value);
|
||||
changedAttributes[name] = true;
|
||||
|
|
@ -69,8 +69,10 @@ LetWidget.prototype.getVariableInfo = function(name,options) {
|
|||
// Special handling: If this variable exists in this very $let, we can
|
||||
// use it, but only if it's been staged.
|
||||
if ($tw.utils.hop(this.currentValueFor,name)) {
|
||||
var value = this.currentValueFor[name];
|
||||
return {
|
||||
text: this.currentValueFor[name]
|
||||
text: value[0] || "",
|
||||
resultList: value
|
||||
};
|
||||
}
|
||||
return Widget.prototype.getVariableInfo.call(this,name,options);
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ Widget.prototype.execute = function() {
|
|||
/*
|
||||
Set the value of a context variable
|
||||
name: name of the variable
|
||||
value: value of the variable
|
||||
value: value of the variable, can be a string or an array
|
||||
params: array of {name:, default:} for each parameter
|
||||
isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed)
|
||||
options includes:
|
||||
|
|
@ -90,8 +90,10 @@ options includes:
|
|||
*/
|
||||
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
|
||||
options = options || {};
|
||||
var valueIsArray = $tw.utils.isArray(value);
|
||||
this.variables[name] = {
|
||||
value: value,
|
||||
value: valueIsArray ? (value[0] || "") : value,
|
||||
resultList: valueIsArray ? value : [value],
|
||||
params: params,
|
||||
isMacroDefinition: !!isMacroDefinition,
|
||||
isFunctionDefinition: !!options.isFunctionDefinition,
|
||||
|
|
@ -114,7 +116,7 @@ allowSelfAssigned: if true, includes the current widget in the context chain ins
|
|||
|
||||
Returns an object with the following fields:
|
||||
|
||||
params: array of {name:,value:} or {value:} of parameters to be applied
|
||||
params: array of {name:,value:,multiValue:} of parameters to be applied (name is optional)
|
||||
text: text of variable, with parameters properly substituted
|
||||
resultList: result of variable evaluation as an array
|
||||
srcVariable: reference to the object defining the variable
|
||||
|
|
@ -140,7 +142,9 @@ Widget.prototype.getVariableInfo = function(name,options) {
|
|||
params = self.resolveVariableParameters(variable.params,actualParams);
|
||||
// Substitute any parameters specified in the definition
|
||||
$tw.utils.each(params,function(param) {
|
||||
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
|
||||
if("name" in param) {
|
||||
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
|
||||
}
|
||||
});
|
||||
value = self.substituteVariableReferences(value,options);
|
||||
resultList = [value];
|
||||
|
|
@ -154,13 +158,20 @@ Widget.prototype.getVariableInfo = function(name,options) {
|
|||
variables[param.name] = param["default"];
|
||||
}
|
||||
});
|
||||
// Parameters are an array of {value:} or {name:, value:} pairs
|
||||
// Parameters are an array of {name:, value:, multivalue:} pairs (name and multivalue are optional)
|
||||
$tw.utils.each(params,function(param) {
|
||||
variables[param.name] = param.value;
|
||||
if(param.multiValue) {
|
||||
variables[param.name] = param.multiValue;
|
||||
} else {
|
||||
variables[param.name] = param.value || "";
|
||||
}
|
||||
});
|
||||
resultList = this.wiki.filterTiddlers(value,this.makeFakeWidgetWithVariables(variables),options.source);
|
||||
value = resultList[0] || "";
|
||||
} else {
|
||||
if(variable.resultList) {
|
||||
resultList = variable.resultList;
|
||||
}
|
||||
params = variable.params;
|
||||
}
|
||||
return {
|
||||
|
|
@ -192,22 +203,24 @@ Widget.prototype.getVariable = function(name,options) {
|
|||
/*
|
||||
Maps actual parameters onto formal parameters, returning an array of {name:,value:} objects
|
||||
formalParams - Array of {name:,default:} (default value is optional)
|
||||
actualParams - Array of string values or {name:,value:} (name is optional)
|
||||
actualParams - Array of string values or {name:,value:,multiValue} (name and multiValue is optional)
|
||||
*/
|
||||
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
|
||||
formalParams = formalParams || [];
|
||||
actualParams = actualParams || [];
|
||||
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
|
||||
paramInfo, paramValue,
|
||||
paramInfo, paramValue, paramMultiValue,
|
||||
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;
|
||||
paramMultiValue = undefined;
|
||||
for(var m=0; m<actualParams.length; m++) {
|
||||
if(typeof actualParams[m] !== "string" && actualParams[m].name === paramInfo.name) {
|
||||
paramValue = actualParams[m].value;
|
||||
paramMultiValue = actualParams[m].multiValue || [paramValue]
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
|
|
@ -217,11 +230,13 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
|
|||
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
|
||||
var param = actualParams[nextAnonParameter++];
|
||||
paramValue = typeof param === "string" ? param : param.value;
|
||||
paramMultiValue = typeof param === "string" ? [param] : (param.multiValue || [paramValue]);
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
paramMultiValue = paramMultiValue || [paramValue];
|
||||
// Store the parameter name and value
|
||||
results.push({name: paramInfo.name, value: paramValue});
|
||||
results.push({name: paramInfo.name, value: paramValue, multiValue: paramMultiValue});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
|
@ -310,7 +325,7 @@ Widget.prototype.getStateQualifier = function(name) {
|
|||
};
|
||||
|
||||
/*
|
||||
Make a fake widget with specified variables, suitable for variable lookup in filters
|
||||
Make a fake widget with specified variables, suitable for variable lookup in filters. Each variable can be a string or an array of strings
|
||||
*/
|
||||
Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
||||
var self = this,
|
||||
|
|
@ -318,7 +333,12 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
|||
return {
|
||||
getVariable: function(name,opts) {
|
||||
if($tw.utils.hop(variables,name)) {
|
||||
return variables[name];
|
||||
var value = variables[name];
|
||||
if($tw.utils.isArray(value)) {
|
||||
return value[0];
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
opts = opts || {};
|
||||
opts.variables = variables;
|
||||
|
|
@ -327,9 +347,18 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
|||
},
|
||||
getVariableInfo: function(name,opts) {
|
||||
if($tw.utils.hop(variables,name)) {
|
||||
return {
|
||||
text: variables[name]
|
||||
};
|
||||
var value = variables[name];
|
||||
if($tw.utils.isArray(value)) {
|
||||
return {
|
||||
text: value[0],
|
||||
resultList: value
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
text: value,
|
||||
resultList: [value]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
opts = opts || {};
|
||||
opts.variables = $tw.utils.extend({},variables,opts.variables);
|
||||
|
|
@ -366,20 +395,45 @@ Widget.prototype.computeAttributes = function(options) {
|
|||
return changedAttributes;
|
||||
};
|
||||
|
||||
Widget.prototype.computeAttribute = function(attribute) {
|
||||
/*
|
||||
Compute the value of a single attribute. Options include:
|
||||
asList: boolean if true returns results as an array instead of a single value
|
||||
*/
|
||||
Widget.prototype.computeAttribute = function(attribute,options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
value;
|
||||
if(attribute.type === "filtered") {
|
||||
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
|
||||
value = this.wiki.filterTiddlers(attribute.filter,this);
|
||||
if(!options.asList) {
|
||||
value = value[0] || "";
|
||||
}
|
||||
} else if(attribute.type === "indirect") {
|
||||
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || "";
|
||||
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
|
||||
if(value && options.asList) {
|
||||
value = [value];
|
||||
}
|
||||
} else if(attribute.type === "macro") {
|
||||
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
|
||||
value = variableInfo.text;
|
||||
if(options.asList) {
|
||||
value = variableInfo.resultList;
|
||||
} else {
|
||||
value = variableInfo.text;
|
||||
}
|
||||
} else if(attribute.type === "substituted") {
|
||||
value = this.wiki.getSubstitutedText(attribute.rawValue,this) || "";
|
||||
if(options.asList) {
|
||||
value = [value];
|
||||
}
|
||||
} else { // String attribute
|
||||
value = attribute.value;
|
||||
if(options.asList) {
|
||||
if(value === undefined) {
|
||||
value = [];
|
||||
} else {
|
||||
value = [value];
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,12 +2,5 @@ created: 20131127215321439
|
|||
modified: 20140912135951542
|
||||
title: $:/DefaultTiddlers
|
||||
|
||||
[[Hire the founder of TiddlyWiki]]
|
||||
[[TiddlyWiki Pre-release]]
|
||||
HelloThere
|
||||
[[Quick Start]]
|
||||
[[Find Out More]]
|
||||
[[TiddlyWiki on the Web]]
|
||||
[[Testimonials and Reviews]]
|
||||
GettingStarted
|
||||
Community
|
||||
[[Multi-Valued Variables]]
|
||||
[[Let Filter Run Prefix]]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
title: LetFilterRunPrefix/ResultList
|
||||
description: Using the "let" filter run prefix to store result lists, not just single values
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [all[tiddlers]] :let[[varname]] [(varname)sort[]join[,]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>$:/core,ExpectedResult,Output</p>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
title: LetFilterRunPrefix/ResultListUnnamedVariable
|
||||
description: Using the "let" filter run prefix to store result lists, not just single values
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [all[tiddlers]] :let[all[]tag[nothing]] [(varname)sort[]join[,]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p></p>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
title: LetFilterRunPrefix/ShortcutSyntax
|
||||
description: Simple usage of "let" filter run prefix
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [[magpie]] =>varname [<varname>] +[join[-]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>magpie</p>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
title: LetFilterRunPrefix/Simple
|
||||
description: Simple usage of "let" filter run prefix
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [[magpie]] :let[[varname]] [<varname>] +[join[-]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>magpie</p>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
title: MultiValuedVariables/Function
|
||||
description: Multi-valued functions
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function myfunc() [all[tiddlers]sort[]]
|
||||
|
||||
<$let varname=<<myfunc>>>
|
||||
<$text text={{{ [(varname)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
title: MultiValuedVariables/MissingVariable
|
||||
description: Using multivalued operands with a missing variable
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [(varname)] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p></p>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
title: MultiValuedVariables/NegatedTitle
|
||||
description: Multi-valued operands
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$let
|
||||
exclude={{{ $:/core Output }}}
|
||||
>
|
||||
<$text text={{{ [all[tiddlers]!title(exclude)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
ExpectedResult
|
||||
</p>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
title: MultiValuedVariables/Operands
|
||||
description: Multi-valued operands
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function myfunc() [all[tiddlers]sort[]]
|
||||
|
||||
<$let varname=<<myfunc>>>
|
||||
<$text text={{{ [(varname)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
title: MultiValuedVariables/Parameters
|
||||
description: Multi-valued function parameters
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function myfunc(input) [(input)sort[]]
|
||||
|
||||
<$let
|
||||
input={{{ [all[tiddlers]] }}}
|
||||
output={{{ [function[myfunc],(input)] }}}
|
||||
>
|
||||
<$text text={{{ [(output)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
title: MultiValuedVariables/ParametersShortcut
|
||||
description: Multi-valued function parameters using shortcut syntax
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function my.func(input) [(input)sort[]]
|
||||
|
||||
<$let
|
||||
input={{{ [all[tiddlers]] }}}
|
||||
output={{{ [my.func(input)] }}}
|
||||
>
|
||||
<$text text={{{ [(output)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
title: MultiValuedVariables/Simple
|
||||
description: Simple usage of multivalued assignments with the "let" widget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$let varname={{{ [all[tiddlers]sort[]] }}}
|
||||
varname2=<<varname>>>
|
||||
<$text text={{{ [(varname2)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
|
|
@ -60,10 +60,10 @@ describe("Widget module", function() {
|
|||
childNode = childNode.children[0];
|
||||
}
|
||||
|
||||
expect(childNode.getVariableInfo("macro",{allowSelfAssigned:true}).params).toEqual([{name:"a",value:"aa"}]);
|
||||
expect(childNode.getVariableInfo("macro",{allowSelfAssigned:true}).params).toEqual([{name:"a",value:"aa",multiValue:["aa"]}]);
|
||||
|
||||
// function params
|
||||
expect(childNode.getVariableInfo("fn", {allowSelfAssigned:true}).params).toEqual([{name:"f",value:"ff"}]);
|
||||
expect(childNode.getVariableInfo("fn", {allowSelfAssigned:true}).params).toEqual([{name:"f",value:"ff",multiValue:["ff"]}]);
|
||||
// functions have a text and a value
|
||||
expect(childNode.getVariableInfo("x", {allowSelfAssigned:true}).text).toBe("fff");
|
||||
expect(childNode.getVariableInfo("x", {allowSelfAssigned:true}).srcVariable.value).toBe("[<fn>]");
|
||||
|
|
@ -73,7 +73,7 @@ describe("Widget module", function() {
|
|||
expect(childNode.getVariableInfo("$my.widget", {allowSelfAssigned:true}).params).toEqual([{name:"w",default:"ww"}]);
|
||||
|
||||
// no params expected
|
||||
expect(childNode.getVariableInfo("abc", {allowSelfAssigned:true})).toEqual({text:"def"});
|
||||
expect(childNode.getVariableInfo("abc", {allowSelfAssigned:true})).toEqual({text:"def", resultList: [ "def" ]});
|
||||
|
||||
// debugger; Find code in browser
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ In technical / logical terms:
|
|||
|`-[run]` |`:except[run]` |difference of sets |... AND NOT run |
|
||||
|`~[run]` |`:else[run]` |else |... ELSE run |
|
||||
|`=[run]` |`:all[run]` |union of sets without de-duplication |... OR run |
|
||||
|`=>[run]` |`:let[run]` |<<.from-version "5.4.0">> assign results to a variable |... LET run |
|
||||
|
||||
The input of a run is normally a list of all the non-[[shadow|ShadowTiddlers]] tiddler titles in the wiki (in no particular order).<br>But the `+` prefix can change this:
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
created: 20250307212252946
|
||||
from-version: 5.4.0
|
||||
modified: 20250307212252946
|
||||
rp-input: all titles from previous filter runs
|
||||
rp-output: an empty title list is always returned from the "let" filter run prefix
|
||||
rp-purpose: assign the title list resulting from previous filter runs to a multi-valued variable
|
||||
tags: [[Named Filter Run Prefix]]
|
||||
title: Let Filter Run Prefix
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<$railroad text="""
|
||||
\start none
|
||||
\end none
|
||||
( ":let" )
|
||||
[[run|"Filter Run"]]
|
||||
"""/>
|
||||
|
||||
The `:let` filter run prefix assigns the title list resulting from previous filter runs to a [[multi-valued variable|Multi-Valued Variable]]. The variable is named with the first result returned by the filter run.
|
||||
|
||||
The variable is made available to the remaining [[filter runs|Filter Run]] in the [[filter expression|Filter Expression]]. Only the first item in the result list is returned when the variable is accessed in the usual way (or an empty string if the result list is empty). Using round brackets instead of angle brackets around a variable name as an operand retrieves the complete list of items in the result list.
|
||||
|
||||
This prefix has an optional [[shortcut syntax|Shortcut Filter Run Prefix]] symbol `=>run`. For example:
|
||||
|
||||
```
|
||||
=[<myfun1>] =[<myfun2>] =>myvar
|
||||
```
|
||||
|
||||
The `:let` filter run prefix always clears the current result list.
|
||||
|
|
@ -23,6 +23,7 @@ A named filter run prefix can precede any [[run|Filter Run]] of a [[filter expre
|
|||
[[<":or"> |"Or Filter Run Prefix"]] |
|
||||
[[<":reduce"> |"Reduce Filter Run Prefix"]] |
|
||||
[[<":sort"> /"v5.2.0"/ |"Sort Filter Run Prefix"]] |
|
||||
[[<":let"> /"v5.4.0"/ |"Let Filter Run Prefix"]] |
|
||||
[[<":then"> /"v5.3.0"/ |"Then Filter Run Prefix"]]) [[run|"Filter Run"]]
|
||||
"""/>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Shortcut prefixes are commonly used by advanced users because they are fast to t
|
|||
<$railroad text="""
|
||||
\start none
|
||||
\end none
|
||||
(-|:"+"|"-"|"~"|"=")
|
||||
(-|:"+"|"-"|"~"|"="|"=>")
|
||||
[[run|"Filter Run"]]
|
||||
"""/>
|
||||
|
||||
|
|
@ -23,7 +23,8 @@ If a run has:
|
|||
|
||||
* the prefix `~`, if the filter output so far is an empty list then the output titles of the run are [[dominantly appended|Dominant Append]] to the filter's output. If the filter output so far is not an empty list then the run is ignored. <<.from-version "5.1.18">>
|
||||
|
||||
* the prefix `=`, output titles are appended to the filter's output without de-duplication. <<.from-version "5.1.20">>
|
||||
* the prefix `=`, output titles are appended to the filter's output without de-duplication. <<.from-version "5.1.20">>
|
||||
|
||||
* the prefix `=>`, the input is assigned to the variable named with the output title. <<.from-version "5.4.0">>
|
||||
|
||||
{{Interchangeable Filter Run Prefixes}}
|
||||
|
|
@ -16,4 +16,6 @@ op-neg-output: the input, but with tiddler <<.place T>> filtered out if it exist
|
|||
|
||||
<<.op title>> is a [[constructor|Selection Constructors]] (except in the form `!title`), but <<.olink2 "field:title" field>> is a [[modifier|Selection Constructors]].
|
||||
|
||||
<<.from-version "5.4.0">> If the operand is quoted with round brackets then the <<.op title>> operator returns the complete list of titles assigned to the multi-valued variable. When negated, the title operator with multi-valued operands returns all the titles that are not present in the operand list.
|
||||
|
||||
<<.operator-examples "title">>
|
||||
|
|
|
|||
16
editions/tw5.com/tiddlers/releasenotes/5.4.0/#8972.tid
Normal file
16
editions/tw5.com/tiddlers/releasenotes/5.4.0/#8972.tid
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
title: $:/changenotes/5.4.0/#8972
|
||||
description: Multi-valued variables and let filter run prefix
|
||||
release: 5.4.0
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: enhancement
|
||||
change-category: hackability
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/8972
|
||||
github-contributors: Jermolene
|
||||
|
||||
This PR introduces a new filter run prefix `:let` that assigns the result of the filter run to a variable that is made available for the remaining filter runs of the filter expression. It solves the problem that previously it was impossible to compute values for filter operator parameters; parameters could only be a literal string, text reference or variable reference.
|
||||
|
||||
This PR also introduces multi-valued variables, the ability to store a list of results in a variable, not just a single string. They can be assigned with the new `:let` filter run prefix, or the existing `<$let>` widget. The full list of values can be retrieved using round brackets instead of the usual angle brackets. In all other contexts only the first item in the list is used as the variable value.
|
||||
|
||||
* [[Multi-Valued Variables]]
|
||||
* [[Let Filter Run Prefix]]
|
||||
* [[LetWidget]]
|
||||
|
|
@ -3,11 +3,5 @@ modified: 20140912135951542
|
|||
title: $:/DefaultTiddlers
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
[[Hire the founder of TiddlyWiki]]
|
||||
HelloThere
|
||||
[[Quick Start]]
|
||||
[[Find Out More]]
|
||||
[[TiddlyWiki on the Web]]
|
||||
[[Testimonials and Reviews]]
|
||||
GettingStarted
|
||||
Community
|
||||
[[Multi-Valued Variables]]
|
||||
[[Let Filter Run Prefix]]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
title: Multi-Valued Variables
|
||||
created: 20250307212252946
|
||||
modified: 20250307212252946
|
||||
tags: Concepts Variables
|
||||
|
||||
<<.from-version "5.4.0">> In ordinary usage, [[variables|Variables]] contain a single snippet of text. With the introduction of multi-valued variables. it is now possible to store a list of multiple values in a single variable. When accessed in the usual way, only the first value is returned, but using round brackets instead of angle brackets around the variable name allows access to the complete list of the values. This makes multi-valued variables largely invisible unless you specifically need to use them.
|
||||
|
||||
! Setting Multi-Valued Variables
|
||||
|
||||
!! LetWidget
|
||||
|
||||
The <<.wid let>> widget allows multi-valued variables to be set in one operation, each to the complete list of results obtained from evaluating an attribute that is defined via a filtered transclusion. For example:
|
||||
|
||||
```
|
||||
<$let
|
||||
varname={{{ [all[tiddlers]sort[]] }}}
|
||||
>
|
||||
```
|
||||
|
||||
The <<.wid let>> widget also allows the complete list of return values from a function to be assigned to a multi-valued variable. For example:
|
||||
|
||||
```
|
||||
<$let
|
||||
varname=<<my.func>>
|
||||
>
|
||||
```
|
||||
|
||||
!! [[Let Filter Run Prefix]]
|
||||
|
||||
The `:let` filter run prefix (or its shortcut syntax `=>`) assigns the complete list of results of a filter run to a multi-valued variable.
|
||||
|
||||
! Retrieving Multi-Valued Variables
|
||||
|
||||
!! [[title Operator]]
|
||||
|
||||
!! Multi-valued Parameters for Filter Operators
|
||||
|
||||
|
||||
|
||||
! Examples
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
<$let varname={{{ [all[tiddlers]sort[]] }}}>
|
||||
<$text text={{{ [(varname)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
```
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
created: 20141002133113496
|
||||
modified: 20240422084347375
|
||||
modified: 20250307212252946
|
||||
tags: Concepts WikiText
|
||||
title: Variables
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
!! Introduction
|
||||
|
||||
* A <<.def variable>> is a ''snippet of text'' that can be accessed by name.
|
||||
* The text is referred to as the variable's <<.def value>>.
|
||||
* A <<.def variable>> is a ''snippet of text'' that can be accessed by name
|
||||
* The text is referred to as the variable's <<.def value>>
|
||||
|
||||
<<.from-version "5.4.0">> In ordinary usage, variables contain a single snippet of text. With the introduction of [[Multi-Valued Variables]] it is possible to store a list of multiple values in a single variable. When accessed in the usual way, only the first value is returned, but using round brackets instead of the usual angle brackets retrieves the complete list of values. This makes the existence of multi-valued variables invisible unless you specifically need to use them.
|
||||
|
||||
Variables are defined by [[widgets|Widgets]]. Several core widgets define variables, the most common being the <<.wlink LetWidget>>, <<.wlink SetWidget>> and <<.wlink ListWidget>> widgets.
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ In addition there are optional attributes that can be used:
|
|||
|$$message |A message to display as the title of the information logged. Useful when several `action-log` widgets are used in sequence. |
|
||||
|$$all |Set to "yes" to log all variables in a collapsed table. Note that if there is nothing specified to log, all variables are always logged instead.|
|
||||
|
||||
<<.from-version "5.4.0">> Any [[multi-valued variables|Multi-Valued Variables]] or attributes are logged as a list of values.
|
||||
|
||||
<<.tip """A handy tip if an action widget is not behaving as expected is to temporarily change it to an `<$action-log>` widget so that the attributes can be observed.""">>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
title: LetWidget
|
||||
created: 20211028115900000
|
||||
modified: 20221001094658854
|
||||
modified: 20250307212252946
|
||||
tags: Widgets
|
||||
caption: let
|
||||
|
||||
! Introduction
|
||||
|
||||
<<.from-version "5.2.1">> The <<.wid let>> widget allows multiple variables to be set in one operation. In some situations it can result in simpler code than using the more flexible <<.wlink SetWidget>> widget. It differs from the <<.wlink VarsWidget>> widget in that variables you're defining may depend on earlier variables defined within the same <<.wid let>>.
|
||||
<<.from-version "5.2.1">> The <<.wid let>> widget allows more than one variable to be set in one operation. In some situations it can result in simpler code than using the more flexible <<.wlink SetWidget>> widget. It differs from the <<.wlink VarsWidget>> widget in that variables you're defining may depend on earlier variables defined within the same <<.wid let>>.
|
||||
|
||||
! Content and Attributes
|
||||
|
||||
|
|
@ -19,6 +19,18 @@ Attributes are evaluated in the order they are written. Attributes with the same
|
|||
|
||||
<<.note """<<.from-version "5.2.4">> There is no longer any restriction on using variable names that start with the $ character.""">>
|
||||
|
||||
! Multi-Valued Variables
|
||||
|
||||
<<.from-version "5.4.0">> The <<.wid let>> widget also allows [[multi-valued variables|Multi-Valued Variables]] to be set in one operation to the complete list of results obtained from evaluating an attribute.
|
||||
|
||||
Almost all operations that work with variables only consider the first item in the list. Using round brackets instead of angle brackets around the variable name gives access to the complete list of results. For example:
|
||||
|
||||
```
|
||||
<$let varname={{{ [all[tiddlers]sort[]] }}}>
|
||||
<$text text={{{ [(varname)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
```
|
||||
|
||||
! Examples
|
||||
|
||||
Consider a case where you need to set multiple variables, where some depend on the evaluation of others.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue