diff --git a/core/modules/filters.js b/core/modules/filters.js index 5cc1de32c..d8edb7238 100644 --- a/core/modules/filters.js +++ b/core/modules/filters.js @@ -235,7 +235,7 @@ exports.compileFilter = function(filterString,options) { this.filterCache = Object.create(null); this.filterCacheCount = 0; } - if(this.filterCache[filterString] !== undefined && !wrappers.prefix && !wrappers.operator) { + if(this.filterCache[filterString] !== undefined && !wrappers.prefix && !wrappers.operation && !wrappers.operator) { return this.filterCache[filterString]; } var filterParseTree; @@ -258,58 +258,64 @@ exports.compileFilter = function(filterString,options) { var operationSubFunction = function(source,widget) { var accumulator = source, results = [], - currTiddlerTitle = widget && widget.getVariable("currentTiddler"); - $tw.utils.each(operation.operators,function(operator) { - var operands = [], - operatorName,operatorFunction; - if(!operator.operator) { - // Use the "title" operator if no operator is specified - operatorName = "title"; - } else if(!filterOperators[operator.operator]) { - // Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator - operatorName = "[unknown]"; - } else { - // Use the operator function - operatorName = operator.operator; - } - operatorFunction = filterOperators[operatorName]; - $tw.utils.each(operator.operands,function(operand) { - if(operand.indirect) { - operand.value = self.getTextReference(operand.text,"",currTiddlerTitle); - } else if(operand.variable) { - var varTree = $tw.utils.parseFilterVariable(operand.text); - operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || ""; - } else { - operand.value = operand.text; - } - operands.push(operand.value); - }); - // Wrap the filter operator module if required - if(wrappers.operator) { - operatorFunction = wrappers.operator.bind(self,operatorFunction); - } - // Invoke the appropriate filteroperator module - results = operatorFunction(accumulator,{ - parseTree: operator, - operator: operator.operator, - operatorName: operatorName, - operand: operands.length > 0 ? operands[0] : undefined, - operands: operands, - prefix: operator.prefix, - suffix: operator.suffix, - suffixes: operator.suffixes, - regexp: operator.regexp - },{ - wiki: self, - widget: widget + currTiddlerTitle = widget && widget.getVariable("currentTiddler"), + handleOperation = function() { + $tw.utils.each(operation.operators,function(operator) { + var operands = [], + operatorName,operatorFunction; + if(!operator.operator) { + // Use the "title" operator if no operator is specified + operatorName = "title"; + } else if(!filterOperators[operator.operator]) { + // Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator + operatorName = "[unknown]"; + } else { + // Use the operator function + operatorName = operator.operator; + } + operatorFunction = filterOperators[operatorName]; + $tw.utils.each(operator.operands,function(operand) { + if(operand.indirect) { + operand.value = self.getTextReference(operand.text,"",currTiddlerTitle); + } else if(operand.variable) { + var varTree = $tw.utils.parseFilterVariable(operand.text); + operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || ""; + } else { + operand.value = operand.text; + } + operands.push(operand.value); }); - if($tw.utils.isArray(results)) { - accumulator = self.makeTiddlerIterator(results); - } else { - accumulator = results; - } - }); - if($tw.utils.isArray(results)) { + // Wrap the filter operator module if required + if(wrappers.operator) { + operatorFunction = wrappers.operator.bind(self,operatorFunction); + } + // Invoke the appropriate filteroperator module + results = operatorFunction(accumulator,{ + parseTree: operator, + operator: operator.operator, + operatorName: operatorName, + operand: operands.length > 0 ? operands[0] : undefined, + operands: operands, + prefix: operator.prefix, + suffix: operator.suffix, + suffixes: operator.suffixes, + regexp: operator.regexp + },{ + wiki: self, + widget: widget + }); + if($tw.utils.isArray(results)) { + accumulator = self.makeTiddlerIterator(results); + } else { + accumulator = results; + } + }); + }; + if(wrappers.operation) { + handleOperation = wrappers.operation.bind(self,handleOperation,operation); + } + handleOperation(); + if($tw.utils.isArray(results)) { return results; } else { var resultArray = []; diff --git a/core/modules/filters/inspect.js b/core/modules/filters/inspect.js index af44ab426..483048863 100644 --- a/core/modules/filters/inspect.js +++ b/core/modules/filters/inspect.js @@ -15,8 +15,12 @@ Export our filter function exports.inspect = function(source,operator,options) { var self = this, inputFilter = operator.operands[0] || "", - output = {input: [],runs: [], inputFilter: inputFilter}, - currentRun; + output = { + input: [], + runs: [], + inputFilter: inputFilter + }, + currentRun,currentOperation; // Save the input source(function(tiddler,title) { output.input.push(title); @@ -30,15 +34,23 @@ exports.inspect = function(source,operator,options) { input: results.toArray(), prefixName: innerOptions.prefixName, suffixes: innerOptions.suffixes, - operators: [] + operations: [] }; - currentRun = details.operators; + currentRun = details.operations; var innerResults = filterRunPrefixFunction.call(null,operationFunction,innerOptions); innerResults(results,innerSource,innerWidget); details.output = results.toArray(); output.runs.push(details); }; }, + operation: function(operationFunction,operation) { + var details = { + operators: [] + } + currentOperation = details.operators; + currentRun.push(details); + operationFunction(); + }, operator: function(operatorFunction,innerSource,innerOperator,innerOptions) { var details = { operatorName: innerOperator.operatorName, @@ -53,7 +65,7 @@ exports.inspect = function(source,operator,options) { innerSource(function(tiddler,title) { details.input.push(title); }); - currentRun.push(details); + currentOperation.push(details); var innerResults = operatorFunction.apply(null,Array.prototype.slice.call(arguments,1)); if(!$tw.utils.isArray(innerResults)) { var resultArray = []; diff --git a/core/wiki/macros/inspect-filter.tid b/core/wiki/macros/inspect-filter.tid index 2285e5092..039a71ddd 100644 --- a/core/wiki/macros/inspect-filter.tid +++ b/core/wiki/macros/inspect-filter.tid @@ -93,6 +93,22 @@ tags: $:/tags/Macro \end inspect-operator +\procedure inspect-operation(jsonOperation,indexOperation) +
+
+ Evaluation + <$text text=<> /> +
+
+ <$list filter="[jsonindexes[operators]nsort[]]" variable="indexOperator"> + <$let transclusion={{{ [[operator-]addsuffix] }}}> + <$transclude $variable="inspect-operator" jsonOperator={{{ [jsonextract[operators],] }}}/> + + +
+
+\end inspect-operation + \procedure inspect-run(jsonRun)
@@ -107,11 +123,13 @@ tags: $:/tags/Macro
<$transclude $variable="inspect-list" jsonList={{{ [jsonextract[input]] }}} class="tc-box tc-inspect-input-box"/> - <$list filter="[jsonindexes[operators]nsort[]]" variable="indexOperator"> - <$let transclusion={{{ [[operator-]addsuffix] }}}> - <$transclude $variable="inspect-operator" jsonOperator={{{ [jsonextract[operators],] }}}/> - - +
+ <$list filter="[jsonindexes[operations]nsort[]]" variable="indexOperation"> + <$let transclusion={{{ [[operation-]addsuffix] }}}> + <$transclude $variable="inspect-operation" jsonOperation={{{ [jsonextract[operations],] }}} indexOperation=<>/> + + +
<$transclude $variable="inspect-list" jsonList={{{ [jsonextract[output]] }}} class="tc-box tc-inspect-output-box">
diff --git a/editions/test/tiddlers/tests/data/filter-wrappers/Simple.tid b/editions/test/tiddlers/tests/data/filter-wrappers/Simple.tid index 5cd4547a0..b82fe64ad 100644 --- a/editions/test/tiddlers/tests/data/filter-wrappers/Simple.tid +++ b/editions/test/tiddlers/tests/data/filter-wrappers/Simple.tid @@ -8,7 +8,7 @@ title: Output \whitespace trim \procedure test-filter() -1 2 3 +1 2 3 :sort[length[]add[1]] \end \function test-filter-wrapper() @@ -21,7 +21,7 @@ title: Output + title: ExpectedResult -

1 2 3-{ +

1 2 3 :sort[length[]add[1]]-{ "input": [ "$:/core", "ExpectedResult", @@ -32,28 +32,32 @@ title: ExpectedResult "input": [], "prefixName": "or", "suffixes": [], - "operators": [ + "operations": [ { - "operatorName": "title", - "operands": [ - "1" - ], - "parseTree": { - "operator": "title", - "operands": [ - { - "text": "1", - "value": "1" - } - ] - }, - "input": [ - "$:/core", - "ExpectedResult", - "Output" - ], - "output": [ - "1" + "operators": [ + { + "operatorName": "title", + "operands": [ + "1" + ], + "parseTree": { + "operator": "title", + "operands": [ + { + "text": "1", + "value": "1" + } + ] + }, + "input": [ + "$:/core", + "ExpectedResult", + "Output" + ], + "output": [ + "1" + ] + } ] } ], @@ -67,28 +71,32 @@ title: ExpectedResult ], "prefixName": "or", "suffixes": [], - "operators": [ + "operations": [ { - "operatorName": "title", - "operands": [ - "2" - ], - "parseTree": { - "operator": "title", - "operands": [ - { - "text": "2", - "value": "2" - } - ] - }, - "input": [ - "$:/core", - "ExpectedResult", - "Output" - ], - "output": [ - "2" + "operators": [ + { + "operatorName": "title", + "operands": [ + "2" + ], + "parseTree": { + "operator": "title", + "operands": [ + { + "text": "2", + "value": "2" + } + ] + }, + "input": [ + "$:/core", + "ExpectedResult", + "Output" + ], + "output": [ + "2" + ] + } ] } ], @@ -104,28 +112,186 @@ title: ExpectedResult ], "prefixName": "or", "suffixes": [], - "operators": [ + "operations": [ { - "operatorName": "title", - "operands": [ - "3" - ], - "parseTree": { - "operator": "title", - "operands": [ - { - "text": "3", - "value": "3" - } - ] - }, - "input": [ - "$:/core", - "ExpectedResult", - "Output" - ], - "output": [ - "3" + "operators": [ + { + "operatorName": "title", + "operands": [ + "3" + ], + "parseTree": { + "operator": "title", + "operands": [ + { + "text": "3", + "value": "3" + } + ] + }, + "input": [ + "$:/core", + "ExpectedResult", + "Output" + ], + "output": [ + "3" + ] + } + ] + } + ], + "output": [ + "1", + "2", + "3" + ] + }, + { + "input": [ + "1", + "2", + "3" + ], + "prefixName": "sort", + "suffixes": [], + "operations": [ + { + "operators": [ + { + "operatorName": "length", + "operands": [ + "" + ], + "parseTree": { + "operator": "length", + "operands": [ + { + "text": "", + "value": "" + } + ] + }, + "input": [ + "1" + ], + "output": [ + "1" + ] + }, + { + "operatorName": "add", + "operands": [ + "1" + ], + "parseTree": { + "operator": "add", + "operands": [ + { + "text": "1", + "value": "1" + } + ] + }, + "input": [ + "1" + ], + "output": [ + "2" + ] + } + ] + }, + { + "operators": [ + { + "operatorName": "length", + "operands": [ + "" + ], + "parseTree": { + "operator": "length", + "operands": [ + { + "text": "", + "value": "" + } + ] + }, + "input": [ + "2" + ], + "output": [ + "1" + ] + }, + { + "operatorName": "add", + "operands": [ + "1" + ], + "parseTree": { + "operator": "add", + "operands": [ + { + "text": "1", + "value": "1" + } + ] + }, + "input": [ + "1" + ], + "output": [ + "2" + ] + } + ] + }, + { + "operators": [ + { + "operatorName": "length", + "operands": [ + "" + ], + "parseTree": { + "operator": "length", + "operands": [ + { + "text": "", + "value": "" + } + ] + }, + "input": [ + "3" + ], + "output": [ + "1" + ] + }, + { + "operatorName": "add", + "operands": [ + "1" + ], + "parseTree": { + "operator": "add", + "operands": [ + { + "text": "1", + "value": "1" + } + ] + }, + "input": [ + "1" + ], + "output": [ + "2" + ] + } ] } ], @@ -136,7 +302,7 @@ title: ExpectedResult ] } ], - "inputFilter": "1 2 3", + "inputFilter": "1 2 3 :sort[length[]add[1]]", "output": [ "1", "2", diff --git a/editions/tw5.com/tiddlers/filters/inspect Operator.tid b/editions/tw5.com/tiddlers/filters/inspect Operator.tid index 937327c5d..ebdfaceaf 100644 --- a/editions/tw5.com/tiddlers/filters/inspect Operator.tid +++ b/editions/tw5.com/tiddlers/filters/inspect Operator.tid @@ -25,4 +25,3 @@ The JSON object contains the following properties: *** `operands`: an array of string operands passed to the operator *** `input`: the input titles passed to the operator *** `output`: the output titles resulting from evaluating the operator - diff --git a/editions/tw5.com/tiddlers/macros/examples/inspect-filter Macro (Examples).tid b/editions/tw5.com/tiddlers/macros/examples/inspect-filter Macro (Examples).tid index 662686de2..d3c832054 100644 --- a/editions/tw5.com/tiddlers/macros/examples/inspect-filter Macro (Examples).tid +++ b/editions/tw5.com/tiddlers/macros/examples/inspect-filter Macro (Examples).tid @@ -6,7 +6,7 @@ type: text/vnd.tiddlywiki <$macrocall $name=".example" n="1" eg=""" -<$transclude $variable="inspect-filter" filter="[tags[]prefix[$:/]] :sort[length[]] +[first[2]tagging[]]" inputFilter="[all[tiddlers]]"/> +<$transclude $variable="inspect-filter" filter="[tags[]prefix[$:/]] :sort[length[]add[1]] +[first[2]tagging[]]" inputFilter="[all[tiddlers]]"/> """/> <$macrocall $name=".example" n="2" eg=""" diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid index e67b03718..0f0317f54 100644 --- a/themes/tiddlywiki/vanilla/base.tid +++ b/themes/tiddlywiki/vanilla/base.tid @@ -3591,6 +3591,7 @@ span.tc-translink > a:first-child { .tc-inspect-filter-box.tc-inspect-filter-box-horizontal > .tc-box > .tc-box-content, .tc-inspect-filter-box.tc-inspect-filter-box-horizontal .tc-inspect-run-box > .tc-box-content, +.tc-inspect-filter-box.tc-inspect-filter-box-horizontal .tc-inspect-operation-box > .tc-box-content, .tc-inspect-filter-box.tc-inspect-filter-box-horizontal .tc-inspect-operator-box > .tc-box-content { display: flex; flex-direction: row; @@ -3598,11 +3599,23 @@ span.tc-translink > a:first-child { align-items: flex-start; } +.tc-inspect-operations-wrapper { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: stretch; +} + .tc-inspect-run-box { --box-background-color: #ffc; --box-foreground-color: #440; } +.tc-inspect-operation-box { + --box-background-color: #cfc; + --box-foreground-color: #040; +} + .tc-inspect-operator-box { --box-background-color: #fcc; --box-foreground-color: #400;