From 520244176963f761aba9de69254869a02ece70ee Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Sat, 29 Aug 2020 18:27:58 +0700 Subject: [PATCH] Add suffix and parameter to trim operator (#4811) * Add suffix and parameter to trim operator Fixes #4809 * Unit tests for new trim operator parameters * Mention trim operator in 5.1.23 release notes * Address review comments * Move regex escaping into utils.js trim functions This way the trimPrefix and trimSuffix functions from utils.js are safe to call without regex-escaping their parameters, which should make them easier to use from other parts of the Javascript code. --- core/modules/filters/strings.js | 28 +++++++++++++++-- core/modules/utils/utils.js | 30 +++++++++++++++++++ .../prerelease/tiddlers/Release 5.1.23.tid | 1 + editions/test/tiddlers/tests/test-filters.js | 12 ++++++++ .../examples/trim Operator (Examples).tid | 8 +++++ .../tw5.com/tiddlers/filters/removeprefix.tid | 2 ++ .../tw5.com/tiddlers/filters/removesuffix.tid | 2 ++ .../tiddlers/filters/trim Operator.tid | 7 +++-- 8 files changed, 85 insertions(+), 5 deletions(-) diff --git a/core/modules/filters/strings.js b/core/modules/filters/strings.js index 947dad2f5..014284b20 100644 --- a/core/modules/filters/strings.js +++ b/core/modules/filters/strings.js @@ -34,9 +34,31 @@ exports.titlecase = makeStringBinaryOperator( function(a) {return [$tw.utils.toTitleCase(a)];} ); -exports.trim = makeStringBinaryOperator( - function(a) {return [$tw.utils.trim(a)];} -); +exports.trim = function(source,operator,options) { + var result = [], + suffix = operator.suffix || "", + operand = (operator.operand || ""), + fnCalc; + if(suffix === "prefix") { + fnCalc = function(a,b) {return [$tw.utils.trimPrefix(a,b)];} + } else if(suffix === "suffix") { + fnCalc = function(a,b) {return [$tw.utils.trimSuffix(a,b)];} + } else { + if(operand === "") { + fnCalc = function(a) {return [$tw.utils.trim(a)];} + } else { + fnCalc = function(a,b) {return [$tw.utils.trimSuffix($tw.utils.trimPrefix(a,b),b)];} + } + } + source(function(tiddler,title) { + Array.prototype.push.apply(result,fnCalc(title,operand)); + }); + return result; +}; + +// makeStringBinaryOperator( +// function(a) {return [$tw.utils.trim(a)];} +// ); exports.split = makeStringBinaryOperator( function(a,b) {return ("" + a).split(b);} diff --git a/core/modules/utils/utils.js b/core/modules/utils/utils.js index a5fb24697..9018af3a0 100644 --- a/core/modules/utils/utils.js +++ b/core/modules/utils/utils.js @@ -94,6 +94,36 @@ exports.trim = function(str) { } }; +exports.trimPrefix = function(str,unwanted) { + if(typeof str === "string" && typeof unwanted === "string") { + if(unwanted === "") { + return str.replace(/^\s\s*/, ''); + } else { + // Safely regexp-escape the unwanted text + unwanted = unwanted.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'); + var regex = new RegExp('^(' + unwanted + ')+'); + return str.replace(regex, ''); + } + } else { + return str; + } +}; + +exports.trimSuffix = function(str,unwanted) { + if(typeof str === "string" && typeof unwanted === "string") { + if(unwanted === "") { + return str.replace(/\s\s*$/, ''); + } else { + // Safely regexp-escape the unwanted text + unwanted = unwanted.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'); + var regex = new RegExp('(' + unwanted + ')+$'); + return str.replace(regex, ''); + } + } else { + return str; + } +}; + /* Convert a string to sentence case (ie capitalise first letter) */ diff --git a/editions/prerelease/tiddlers/Release 5.1.23.tid b/editions/prerelease/tiddlers/Release 5.1.23.tid index 36d542fd2..65827f810 100644 --- a/editions/prerelease/tiddlers/Release 5.1.23.tid +++ b/editions/prerelease/tiddlers/Release 5.1.23.tid @@ -62,6 +62,7 @@ type: text/vnd.tiddlywiki * [[Extended|https://github.com/Jermolene/TiddlyWiki5/pull/4741]] [[WidgetMessage: tm-import-tiddlers]] to override the title $:/Import and to better control whether the import tiddler is opened automatically * [[Extended|https://github.com/Jermolene/TiddlyWiki5/pull/4740]] the EditWidget to pass all attributes through to the sub-widget * [[Extended|https://github.com/Jermolene/TiddlyWiki5/commit/bd2cf5c46498222a32ebda92da3ae50bde33decb]] the internal `<$element>` widget to add a hook so that plugins can intercept DOM node creation +* [[Extended|https://github.com/Jermolene/TiddlyWiki5/pull/4811]] the [[trim Operator]] to optionally trim off a prefixing or suffixing string from input tiddler titles ! Bug Fixes diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index f9feed6cf..4c2673d2c 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -467,6 +467,18 @@ function runTests(wiki) { // Ensure that join correctly handles empty strings in source expect(wiki.filterTiddlers("[[]] Paul +[join[-]]").join(",")).toBe("-Paul"); expect(wiki.filterTiddlers("[[ John ]] [[Paul ]] [[ George]] Ringo +[trim[]join[-]]").join(",")).toBe("John-Paul-George-Ringo"); + expect(wiki.filterTiddlers("[[ abc ]] [[def ]] [[ ghi]] +[trim[]]").join(",")).toBe("abc,def,ghi"); + expect(wiki.filterTiddlers("[[ abc ]] [[def ]] [[ ghi]] +[trim:prefix[]]").join(",")).toBe("abc ,def ,ghi"); + expect(wiki.filterTiddlers("[[ abc ]] [[def ]] [[ ghi]] +[trim:suffix[]]").join(",")).toBe(" abc,def, ghi"); + expect(wiki.filterTiddlers("[[ abacus ]] [[ baobab ]] +[trim[ab]]").join(",")).toBe(" abacus , baobab "); + expect(wiki.filterTiddlers("[[ abacus ]] [[ baobab ]] +[trim[a]]").join(",")).toBe(" abacus , baobab "); + expect(wiki.filterTiddlers("abacus baobab +[trim[a]]").join(",")).toBe("bacus,baobab"); + expect(wiki.filterTiddlers("abacus baobab +[trim[ab]]").join(",")).toBe("acus,baob"); + expect(wiki.filterTiddlers("abacus baobab +[trim:prefix[ab]]").join(",")).toBe("acus,baobab"); + expect(wiki.filterTiddlers("abacus baobab +[trim:suffix[ab]]").join(",")).toBe("abacus,baob"); + // Trim doesn't hiccup on regexp special characters + expect(wiki.filterTiddlers("[[.*abacus.*]] [[.+baobab.+]] +[trim[.*]]").join(",")).toBe("abacus,.+baobab.+"); + expect(wiki.filterTiddlers("[[.*abacus.*]] [[.+baobab.+]] +[trim[.+]]").join(",")).toBe(".*abacus.*,baobab"); }); it("should handle the mathematics operators", function() { diff --git a/editions/tw5.com/tiddlers/filters/examples/trim Operator (Examples).tid b/editions/tw5.com/tiddlers/filters/examples/trim Operator (Examples).tid index 18a1562bd..e47640047 100644 --- a/editions/tw5.com/tiddlers/filters/examples/trim Operator (Examples).tid +++ b/editions/tw5.com/tiddlers/filters/examples/trim Operator (Examples).tid @@ -5,3 +5,11 @@ title: trim Operator (Examples) type: text/vnd.tiddlywiki <<.operator-example 1 "[[ a b ]trim[]addprefix[-]addsuffix[-]]">> + +<<.operator-example 2 "[[ abc ]] [[def ]] [[ ghi]] +[trim[]addprefix[-]addsuffix[-]]">> +<<.operator-example 3 "[[ abc ]] [[def ]] [[ ghi]] +[trim:prefix[]addprefix[-]addsuffix[-]]">> +<<.operator-example 4 "[[ abc ]] [[def ]] [[ ghi]] +[trim:suffix[]addprefix[-]addsuffix[-]]">> + +<<.operator-example 5 "abacus baobab +[trim[ab]]">> +<<.operator-example 6 "abacus baobab +[trim:prefix[ab]]">> +<<.operator-example 7 "abacus baobab +[trim:suffix[ab]]">> diff --git a/editions/tw5.com/tiddlers/filters/removeprefix.tid b/editions/tw5.com/tiddlers/filters/removeprefix.tid index 10f8e87eb..262207268 100644 --- a/editions/tw5.com/tiddlers/filters/removeprefix.tid +++ b/editions/tw5.com/tiddlers/filters/removeprefix.tid @@ -12,4 +12,6 @@ op-output: those input titles that start with <<.place S>>, but with those chara <<.s-matching-is-case-sensitive>> +<<.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]].">> + <<.operator-examples "removeprefix">> diff --git a/editions/tw5.com/tiddlers/filters/removesuffix.tid b/editions/tw5.com/tiddlers/filters/removesuffix.tid index b1ecd495c..0c14a1947 100644 --- a/editions/tw5.com/tiddlers/filters/removesuffix.tid +++ b/editions/tw5.com/tiddlers/filters/removesuffix.tid @@ -12,4 +12,6 @@ op-output: those input titles that end with <<.place S>>, but with those charact <<.s-matching-is-case-sensitive>> +<<.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]].">> + <<.operator-examples "removesuffix">> diff --git a/editions/tw5.com/tiddlers/filters/trim Operator.tid b/editions/tw5.com/tiddlers/filters/trim Operator.tid index e791c2f87..199c8cd7f 100644 --- a/editions/tw5.com/tiddlers/filters/trim Operator.tid +++ b/editions/tw5.com/tiddlers/filters/trim Operator.tid @@ -1,13 +1,16 @@ caption: trim created: 20190613153740241 modified: 20190613153820282 +op-purpose: returns each item in the list with whitespace, or a given character string, trimmed from the start and/or end op-input: a [[selection of titles|Title Selection]] -op-output: the input titles with whitespace trimmed from the start and end -op-purpose: returns each item in the list with whitespace trimmed from the start and end +op-parameter: <<.from-version "5.1.23">> a string of characters +op-parameter-name: S +op-output: the input titles with <<.place S>>, or whitespace if <<.place S>> is not specified, trimmed from the start and/or end tags: [[Filter Operators]] title: trim Operator type: text/vnd.tiddlywiki <<.from-version "5.1.20">> +<<.from-version "5.1.23">>The <<.op trim>> filter allows a parameter specifying a string to trim. <<.operator-examples "trim">>