From be040ea8a2cc8962f1a28a313e4c9ebc2d5c0e31 Mon Sep 17 00:00:00 2001 From: Jermolene Date: Fri, 25 Jul 2014 15:26:44 +0100 Subject: [PATCH] Add support for variable filter operands Fixes #583 --- core/modules/filters.js | 45 +++++++++++-------- editions/test/tiddlers/tests/test-filters.js | 19 ++++++++ .../dev/TiddlerFilter Formal Grammar.tid | 4 +- .../filters/Introduction to Filters.tid | 12 ++++- 4 files changed, 58 insertions(+), 22 deletions(-) diff --git a/core/modules/filters.js b/core/modules/filters.js index 474928ba7..bc1138990 100644 --- a/core/modules/filters.js +++ b/core/modules/filters.js @@ -33,7 +33,7 @@ function parseFilterOperation(operators,filterString,p) { operator.prefix = filterString.charAt(p++); } // Get the operator name - var nextBracketPos = filterString.substring(p).search(/[\[\{\/]/); + var nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/); if(nextBracketPos === -1) { throw "Missing [ in filter expression"; } @@ -54,24 +54,28 @@ function parseFilterOperation(operators,filterString,p) { p = nextBracketPos + 1; switch (bracket) { - case '{': // Curly brackets - operator.indirect = true; - nextBracketPos = filterString.indexOf('}',p); - break; - case '[': // Square brackets - nextBracketPos = filterString.indexOf(']',p); - break; - case '/': // regexp brackets - var rex = /^((?:[^\\\/]*|\\.)*)\/(?:\(([mygi]+)\))?/g, - rexMatch = rex.exec(filterString.substring(p)); - if(rexMatch) { - operator.regexp = new RegExp(rexMatch[1], rexMatch[2]); - nextBracketPos = p + rex.lastIndex - 1; - } - else { - throw "Unterminated regular expression in filter expression"; - } - break; + case "{": // Curly brackets + operator.indirect = true; + nextBracketPos = filterString.indexOf("}",p); + break; + case "[": // Square brackets + nextBracketPos = filterString.indexOf("]",p); + break; + case "<": // Angle brackets + operator.variable = true; + nextBracketPos = filterString.indexOf(">",p); + break; + case "/": // regexp brackets + var rex = /^((?:[^\\\/]*|\\.)*)\/(?:\(([mygi]+)\))?/g, + rexMatch = rex.exec(filterString.substring(p)); + if(rexMatch) { + operator.regexp = new RegExp(rexMatch[1], rexMatch[2]); + nextBracketPos = p + rex.lastIndex - 1; + } + else { + throw "Unterminated regular expression in filter expression"; + } + break; } if(nextBracketPos === -1) { @@ -193,6 +197,9 @@ exports.compileFilter = function(filterString) { if(operator.indirect) { operand = self.getTextReference(operator.operand,"",currTiddlerTitle); } + if(operator.variable) { + operand = widget.getVariable(operator.operand,""); + } results = operatorFunction(accumulator,{ operator: operator.operator, operand: operand, diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index 347fbe40a..a523fe5fe 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -270,6 +270,25 @@ describe("Filter tests", function() { expect(wiki.filterTiddlers("[modifier{!!modifier}] +[sort[title]]",fakeWidget).join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,one,Tiddler Three"); }); + it("should handle variable operands", function() { + + var widget = require("$:/core/modules/widgets/widget.js"); + // Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers + var rootWidget = new widget.widget({ + type: "widget", + children: [{type: "widget", children: []}] + },{ + wiki: $tw.wiki, + document: $tw.document + }); + rootWidget.makeChildWidgets(); + var anchorWidget = rootWidget.children[0]; + rootWidget.setVariable("myVar","Tidd"); + rootWidget.setVariable("myVar2","JoeBloggs"); + expect(wiki.filterTiddlers("[prefix] +[sort[title]]",anchorWidget).join(",")).toBe("Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[modifier] +[sort[title]]",anchorWidget).join(",")).toBe("TiddlerOne"); + }); + it("should handle the before and after operators", function() { expect(wiki.filterTiddlers("[list[TiddlerSeventh]after[TiddlerOne]]").join(",")).toBe("Tiddler Three"); expect(wiki.filterTiddlers("[list[TiddlerSeventh]after[a fourth tiddler]]").join(",")).toBe("MissingTiddler"); diff --git a/editions/tw5.com/tiddlers/dev/TiddlerFilter Formal Grammar.tid b/editions/tw5.com/tiddlers/dev/TiddlerFilter Formal Grammar.tid index b2ae39ff7..8f29fc39d 100644 --- a/editions/tw5.com/tiddlers/dev/TiddlerFilter Formal Grammar.tid +++ b/editions/tw5.com/tiddlers/dev/TiddlerFilter Formal Grammar.tid @@ -1,5 +1,5 @@ created: 20140210141217955 -modified: 20140303091312363 +modified: 20140725091312363 tags: dev title: TiddlerFilter Formal Grammar type: text/vnd.tiddlywiki @@ -34,7 +34,7 @@ Whitespace is matched with javascript "\s+", which matches space, tab, carriage ''<opt-operator-suffix>'' ::= ''<string>'' | "" -''<operand>'' ::= "[" ''<search-string>'' "]" | "{" ''<indirect-search-string>'' "}" | ''<regex>'' +''<operand>'' ::= "[" ''<search-string>'' "]" | "{" ''<indirect-search-string>'' "}" | "<" ''<variable-name-string>'' ">" | ''<regex>'' ''<regex>'' ::= "/" ''<string>'' "/" ''<opt-regex-args>'' diff --git a/editions/tw5.com/tiddlers/filters/Introduction to Filters.tid b/editions/tw5.com/tiddlers/filters/Introduction to Filters.tid index 87fa10bd9..7f69f571f 100644 --- a/editions/tw5.com/tiddlers/filters/Introduction to Filters.tid +++ b/editions/tw5.com/tiddlers/filters/Introduction to Filters.tid @@ -1,5 +1,5 @@ created: 20140410101941871 -modified: 20140410103229640 +modified: 20140725103229640 tags: introduction title: Introduction to Filters type: text/vnd.tiddlywiki @@ -67,6 +67,16 @@ If a filter operator is written with curly brackets around the operand then it i [search{$:/temp/search}] ``` +! Variable Operands + +If a filter operator is written with angle brackets around the operand then it is taken to be the name of a variable containing the actual value. For example, this filter selects all tiddlers containing the title of the current tiddler: + +``` +[search] +``` + +(Note that the `currentTiddler` variable is used to track the current tiddler). + ! Regular Expression Operands The "field" filter also accepts [[regular expressions|http://en.wikipedia.org/wiki/Regular_expression]] with the syntax `/regexp/(modifier)`. For example: