From 95bd694a65f13c949345bd38a59a80fae8516d64 Mon Sep 17 00:00:00 2001 From: Ben Webber Date: Tue, 22 Feb 2022 16:38:40 +0000 Subject: [PATCH] Support case insensitive matching in prefix/suffix operators (#6468) * Support case insensitive matching in prefix/suffix operators Support `caseinsensitive`/`caseinsensitive` suffixes in the following filter operators: * prefix * suffix * removeprefix * removesuffix The suffixes have the same behaviour as in the match operator. Closes: #6407 * Do not filter titles if suffix/removesuffix operand is empty Issue: #6407 --- core/modules/filters/prefix.js | 42 +++++++++++++------ core/modules/filters/removeprefix.js | 22 +++++++--- core/modules/filters/removesuffix.js | 26 +++++++++--- core/modules/filters/suffix.js | 42 ++++++++++++++----- editions/test/tiddlers/tests/test-filters.js | 27 +++++++++++- .../tiddlers/filters/examples/prefix.tid | 7 ++-- .../filters/examples/removeprefix.tid | 3 +- .../filters/examples/removesuffix.tid | 5 +-- .../tiddlers/filters/examples/suffix.tid | 5 ++- editions/tw5.com/tiddlers/filters/prefix.tid | 19 ++++++++- .../tw5.com/tiddlers/filters/removeprefix.tid | 21 ++++++++-- .../tw5.com/tiddlers/filters/removesuffix.tid | 21 ++++++++-- editions/tw5.com/tiddlers/filters/suffix.tid | 19 ++++++++- 13 files changed, 205 insertions(+), 54 deletions(-) diff --git a/core/modules/filters/prefix.js b/core/modules/filters/prefix.js index 49bb8110f..97dd56f4e 100644 --- a/core/modules/filters/prefix.js +++ b/core/modules/filters/prefix.js @@ -16,19 +16,37 @@ Filter operator for checking if a title starts with a prefix Export our filter function */ exports.prefix = function(source,operator,options) { - var results = []; - if(operator.prefix === "!") { - source(function(tiddler,title) { - if(title.substr(0,operator.operand.length) !== operator.operand) { - results.push(title); - } - }); + var results = [], + suffixes = (operator.suffixes || [])[0] || []; + if(suffixes.indexOf("caseinsensitive") !== -1) { + var operand = operator.operand.toLowerCase(); + if(operator.prefix === "!") { + source(function(tiddler,title) { + if(title.toLowerCase().substr(0,operand.length) !== operand) { + results.push(title); + } + }); + } else { + source(function(tiddler,title) { + if(title.toLowerCase().substr(0,operand.length) === operand) { + results.push(title); + } + }); + } } else { - source(function(tiddler,title) { - if(title.substr(0,operator.operand.length) === operator.operand) { - results.push(title); - } - }); + if(operator.prefix === "!") { + source(function(tiddler,title) { + if(title.substr(0,operator.operand.length) !== operator.operand) { + results.push(title); + } + }); + } else { + source(function(tiddler,title) { + if(title.substr(0,operator.operand.length) === operator.operand) { + results.push(title); + } + }); + } } return results; }; diff --git a/core/modules/filters/removeprefix.js b/core/modules/filters/removeprefix.js index 3e93dcd00..185e1fc87 100644 --- a/core/modules/filters/removeprefix.js +++ b/core/modules/filters/removeprefix.js @@ -16,12 +16,22 @@ Filter operator for removing a prefix from each title in the list. Titles that d Export our filter function */ exports.removeprefix = function(source,operator,options) { - var results = []; - source(function(tiddler,title) { - if(title.substr(0,operator.operand.length) === operator.operand) { - results.push(title.substr(operator.operand.length)); - } - }); + var results = [], + suffixes = (operator.suffixes || [])[0] || []; + if(suffixes.indexOf("caseinsensitive") !== -1) { + var operand = operator.operand.toLowerCase(); + source(function(tiddler,title) { + if(title.toLowerCase().substr(0,operand.length) === operand) { + results.push(title.substr(operand.length)); + } + }); + } else { + source(function(tiddler,title) { + if(title.substr(0,operator.operand.length) === operator.operand) { + results.push(title.substr(operator.operand.length)); + } + }); + } return results; }; diff --git a/core/modules/filters/removesuffix.js b/core/modules/filters/removesuffix.js index 3d305aabf..76c0c7b35 100644 --- a/core/modules/filters/removesuffix.js +++ b/core/modules/filters/removesuffix.js @@ -16,12 +16,26 @@ Filter operator for removing a suffix from each title in the list. Titles that d Export our filter function */ exports.removesuffix = function(source,operator,options) { - var results = []; - source(function(tiddler,title) { - if(title && title.substr(-operator.operand.length) === operator.operand) { - results.push(title.substr(0,title.length - operator.operand.length)); - } - }); + var results = [], + suffixes = (operator.suffixes || [])[0] || []; + if (!operator.operand) { + source(function(tiddler,title) { + results.push(title); + }); + } else if(suffixes.indexOf("caseinsensitive") !== -1) { + var operand = operator.operand.toLowerCase(); + source(function(tiddler,title) { + if(title && title.toLowerCase().substr(-operand.length) === operand) { + results.push(title.substr(0,title.length - operand.length)); + } + }); + } else { + source(function(tiddler,title) { + if(title && title.substr(-operator.operand.length) === operator.operand) { + results.push(title.substr(0,title.length - operator.operand.length)); + } + }); + } return results; }; diff --git a/core/modules/filters/suffix.js b/core/modules/filters/suffix.js index a93733cc8..96b32c4a9 100644 --- a/core/modules/filters/suffix.js +++ b/core/modules/filters/suffix.js @@ -16,19 +16,41 @@ Filter operator for checking if a title ends with a suffix Export our filter function */ exports.suffix = function(source,operator,options) { - var results = []; - if(operator.prefix === "!") { + var results = [], + suffixes = (operator.suffixes || [])[0] || []; + if (!operator.operand) { source(function(tiddler,title) { - if(title.substr(-operator.operand.length) !== operator.operand) { - results.push(title); - } + results.push(title); }); + } else if(suffixes.indexOf("caseinsensitive") !== -1) { + var operand = operator.operand.toLowerCase(); + if(operator.prefix === "!") { + source(function(tiddler,title) { + if(title.toLowerCase().substr(-operand.length) !== operand) { + results.push(title); + } + }); + } else { + source(function(tiddler,title) { + if(title.toLowerCase().substr(-operand.length) === operand) { + results.push(title); + } + }); + } } else { - source(function(tiddler,title) { - if(title.substr(-operator.operand.length) === operator.operand) { - results.push(title); - } - }); + if(operator.prefix === "!") { + source(function(tiddler,title) { + if(title.substr(-operator.operand.length) !== operator.operand) { + results.push(title); + } + }); + } else { + source(function(tiddler,title) { + if(title.substr(-operator.operand.length) === operator.operand) { + results.push(title); + } + }); + } } return results; }; diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index 4e69eeead..bee1fc9a1 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -314,7 +314,32 @@ Tests the filtering mechanism. it("should handle the prefix operator", function() { expect(wiki.filterTiddlers("[prefix[Tiddler]]").join(",")).toBe("Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[prefix:casesensitive[tiddler]]").join(",")).toBe(""); + expect(wiki.filterTiddlers("[prefix:caseinsensitive[tiddler]]").join(",")).toBe("Tiddler Three,TiddlerOne"); expect(wiki.filterTiddlers("[prefix[nothing]]").join(",")).toBe(""); + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]prefix[]]").join(",")).toBe("ABCDE,abcde"); + }); + + it("should handle the suffix operator", function() { + expect(wiki.filterTiddlers("[suffix[One]]").join(",")).toBe("TiddlerOne"); + expect(wiki.filterTiddlers("[suffix:casesensitive[one]]").join(",")).toBe("one"); + expect(wiki.filterTiddlers("[suffix:caseinsensitive[one]]").join(",")).toBe("one,TiddlerOne"); + expect(wiki.filterTiddlers("[suffix[nothing]]").join(",")).toBe(""); + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]suffix[]]").join(",")).toBe("ABCDE,abcde"); + }); + + it("should handle the removeprefix operator", function() { + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removeprefix[ABC]]").join(",")).toBe("DE"); + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removeprefix:casesensitive[ABC]]").join(",")).toBe("DE"); + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removeprefix:caseinsensitive[abc]]").join(",")).toBe("DE,de"); + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removeprefix[]]").join(",")).toBe("ABCDE,abcde"); + }); + + it("should handle the removesuffix operator", function() { + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removesuffix[DE]]").join(",")).toBe("ABC"); + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removesuffix:casesensitive[DE]]").join(",")).toBe("ABC"); + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removesuffix:caseinsensitive[de]]").join(",")).toBe("ABC,abc") + expect(wiki.filterTiddlers("[enlist[ABCDE abcde]removesuffix[]]").join(",")).toBe("ABCDE,abcde"); }); it("should handle the sort and sortcs operators", function() { @@ -992,4 +1017,4 @@ Tests the filtering mechanism. }); })(); - \ No newline at end of file + diff --git a/editions/tw5.com/tiddlers/filters/examples/prefix.tid b/editions/tw5.com/tiddlers/filters/examples/prefix.tid index 742efbd3c..9b1207f8b 100644 --- a/editions/tw5.com/tiddlers/filters/examples/prefix.tid +++ b/editions/tw5.com/tiddlers/filters/examples/prefix.tid @@ -1,9 +1,10 @@ created: 20150123223129000 -modified: 20150123223321000 +modified: 20220218023400000 tags: [[prefix Operator]] [[Operator Examples]] title: prefix Operator (Examples) type: text/vnd.tiddlywiki <<.operator-example 1 "[tag[task]!prefix[Go]]">> -<<.operator-example 2 "[prefix[$:/languages/]]">> -<<.operator-example 3 "[prefix[$:/]]" "same as `[is[system]]`">> +<<.operator-example 2 "[tag[task]!prefix:caseinsensitive[go]]">> +<<.operator-example 3 "[prefix[$:/languages/]]">> +<<.operator-example 4 "[prefix[$:/]]" "same as `[is[system]]`">> diff --git a/editions/tw5.com/tiddlers/filters/examples/removeprefix.tid b/editions/tw5.com/tiddlers/filters/examples/removeprefix.tid index 99026a4bc..c5b4098a6 100644 --- a/editions/tw5.com/tiddlers/filters/examples/removeprefix.tid +++ b/editions/tw5.com/tiddlers/filters/examples/removeprefix.tid @@ -1,7 +1,8 @@ created: 20150118132851000 -modified: 20150123210429000 +modified: 20220218023400000 tags: [[removeprefix Operator]] [[Operator Examples]] title: removeprefix Operator (Examples) type: text/vnd.tiddlywiki <<.operator-example 1 "[[My Cat]] [[Your Garden]] [[My Favourite Armchair]] +[removeprefix[My ]]">> +<<.operator-example 2 "[[My Cat]] [[Your Garden]] [[My Favourite Armchair]] +[removeprefix:caseinsensitive[my ]]">> diff --git a/editions/tw5.com/tiddlers/filters/examples/removesuffix.tid b/editions/tw5.com/tiddlers/filters/examples/removesuffix.tid index 63546f2a0..36849d364 100644 --- a/editions/tw5.com/tiddlers/filters/examples/removesuffix.tid +++ b/editions/tw5.com/tiddlers/filters/examples/removesuffix.tid @@ -1,9 +1,8 @@ created: 20150118132851000 -modified: 20150123211000000 +modified: 20220218023400000 tags: [[removesuffix Operator]] [[Operator Examples]] title: removesuffix Operator (Examples) type: text/vnd.tiddlywiki <<.operator-example 1 "SIMPLEX Googolplex Complex +[removesuffix[plex]]">> - - +<<.operator-example 2 "SIMPLEX Googolplex Complex +[removesuffix:caseinsensitive[plex]]">> diff --git a/editions/tw5.com/tiddlers/filters/examples/suffix.tid b/editions/tw5.com/tiddlers/filters/examples/suffix.tid index 869fc1304..8946a1e5c 100644 --- a/editions/tw5.com/tiddlers/filters/examples/suffix.tid +++ b/editions/tw5.com/tiddlers/filters/examples/suffix.tid @@ -1,8 +1,9 @@ created: 20150124113652000 -modified: 20150124113925000 +modified: 20220218023400000 tags: [[suffix Operator]] [[Operator Examples]] title: suffix Operator (Examples) type: text/vnd.tiddlywiki <<.operator-example 1 "[suffix[.jpg]]">> -<<.operator-example 2 "[tag[task]!suffix[ing]]">> +<<.operator-example 2 "[suffix:caseinsensitive[.JPG]]">> +<<.operator-example 3 "[tag[task]!suffix[ing]]">> diff --git a/editions/tw5.com/tiddlers/filters/prefix.tid b/editions/tw5.com/tiddlers/filters/prefix.tid index 0e048b2dd..b96cdc599 100644 --- a/editions/tw5.com/tiddlers/filters/prefix.tid +++ b/editions/tw5.com/tiddlers/filters/prefix.tid @@ -1,5 +1,5 @@ created: 20140410103123179 -modified: 20150203192735000 +modified: 20220218023400000 tags: [[Filter Operators]] [[String Operators]] [[Negatable Operators]] title: prefix Operator type: text/vnd.tiddlywiki @@ -10,7 +10,22 @@ op-parameter: a string of characters op-parameter-name: S op-output: those input titles that start with <<.place S>> op-neg-output: those input tiddlers that do <<.em not>> start with <<.place S>> +op-suffix: the <<.op prefix>> operator uses a rich suffix, see below for details -<<.s-matching-is-case-sensitive>> +<<.from-version "5.2.2">> + +The <<.op prefix>> operator uses an extended syntax that permits multiple flags to be passed: + +``` +[prefix:[]] +``` + +* ''flag list'': a comma delimited list of flags +* ''operand'': filter operand + +The available flags are: + +* ''casesensitive'': (default), this flag forces a case-sensitive match, where upper and lower case letters are considered different +* ''caseinsensitive'': overrides the default so that upper and lower case letters are considered identical for matching purposes <<.operator-examples "prefix">> diff --git a/editions/tw5.com/tiddlers/filters/removeprefix.tid b/editions/tw5.com/tiddlers/filters/removeprefix.tid index 262207268..0b6156052 100644 --- a/editions/tw5.com/tiddlers/filters/removeprefix.tid +++ b/editions/tw5.com/tiddlers/filters/removeprefix.tid @@ -1,5 +1,5 @@ created: 20140410103123179 -modified: 20150203190709000 +modified: 20220218023400000 tags: [[Filter Operators]] [[String Operators]] title: removeprefix Operator type: text/vnd.tiddlywiki @@ -9,9 +9,24 @@ op-input: a [[selection of titles|Title Selection]] op-parameter: a string of characters op-parameter-name: S op-output: those input titles that start with <<.place S>>, but with those characters discarded - -<<.s-matching-is-case-sensitive>> +op-suffix: the <<.op removeprefix>> operator uses a rich suffix, see below for details <<.tip " This filters out input titles that do not start with S. For removing S without filtering out input titles that don't start with S, see [[trim|trim Operator]].">> +<<.from-version "5.2.2">> + +The <<.op removeprefix>> operator uses an extended syntax that permits multiple flags to be passed: + +``` +[removeprefix:[]] +``` + +* ''flag list'': a comma delimited list of flags +* ''operand'': filter operand + +The available flags are: + +* ''casesensitive'': (default), this flag forces a case-sensitive match, where upper and lower case letters are considered different +* ''caseinsensitive'': overrides the default so that upper and lower case letters are considered identical for matching purposes + <<.operator-examples "removeprefix">> diff --git a/editions/tw5.com/tiddlers/filters/removesuffix.tid b/editions/tw5.com/tiddlers/filters/removesuffix.tid index 0c14a1947..4a1812ae0 100644 --- a/editions/tw5.com/tiddlers/filters/removesuffix.tid +++ b/editions/tw5.com/tiddlers/filters/removesuffix.tid @@ -1,5 +1,5 @@ created: 20140828133830424 -modified: 20150203190744000 +modified: 20220218023400000 tags: [[Filter Operators]] [[String Operators]] title: removesuffix Operator type: text/vnd.tiddlywiki @@ -9,9 +9,24 @@ op-input: a [[selection of titles|Title Selection]] op-parameter: a string of characters op-parameter-name: S op-output: those input titles that end with <<.place S>>, but with those characters discarded - -<<.s-matching-is-case-sensitive>> +op-suffix: the <<.op removesuffix>> operator uses a rich suffix, see below for details <<.tip " This filters out input titles that do not end with S. For removing S without filtering out input titles that don't end with S, see [[trim|trim Operator]].">> +<<.from-version "5.2.2">> + +The <<.op removesuffix>> operator uses an extended syntax that permits multiple flags to be passed: + +``` +[removesuffix:[]] +``` + +* ''flag list'': a comma delimited list of flags +* ''operand'': filter operand + +The available flags are: + +* ''casesensitive'': (default), this flag forces a case-sensitive match, where upper and lower case letters are considered different +* ''caseinsensitive'': overrides the default so that upper and lower case letters are considered identical for matching purposes + <<.operator-examples "removesuffix">> diff --git a/editions/tw5.com/tiddlers/filters/suffix.tid b/editions/tw5.com/tiddlers/filters/suffix.tid index 76355385b..1c1a7bb9b 100644 --- a/editions/tw5.com/tiddlers/filters/suffix.tid +++ b/editions/tw5.com/tiddlers/filters/suffix.tid @@ -1,5 +1,5 @@ created: 20140828133830424 -modified: 20150203192738000 +modified: 20220218023400000 tags: [[Filter Operators]] [[String Operators]] [[Negatable Operators]] title: suffix Operator type: text/vnd.tiddlywiki @@ -10,7 +10,22 @@ op-parameter: a string of characters op-parameter-name: S op-output: those input titles that end with <<.place S>> op-neg-output: those input tiddlers that do <<.em not>> end with <<.place S>> +op-suffix: the <<.op suffix>> operator uses a rich suffix, see below for details -<<.s-matching-is-case-sensitive>> +<<.from-version "5.2.2">> + +The <<.op suffix>> operator uses an extended syntax that permits multiple flags to be passed: + +``` +[suffix:[]] +``` + +* ''flag list'': a comma delimited list of flags +* ''operand'': filter operand + +The available flags are: + +* ''casesensitive'': (default), this flag forces a case-sensitive match, where upper and lower case letters are considered different +* ''caseinsensitive'': overrides the default so that upper and lower case letters are considered identical for matching purposes <<.operator-examples "suffix">>