New insertafter filter operator (#6771)

* Implement insertafter operator (like insertbefore)

Currently, the behavior of insertafter if the target is not found is to
append the inserted tiddler to the end of the list, like insertbefore
does. In the next commit, we'll add a suffix to customize what both
insertafter and insertbefore do when the target is not found.

* Add failing tests for insertafter suffixes

Also includes tests for insertbefore suffixes (start/end), since we'll
be implementing both of those at the same time.

* Add start/end suffixes for insertafter/before

The tests that exercise the start/end suffixes now pass.
This commit is contained in:
Robin Munn 2022-07-13 23:08:17 +07:00 committed by GitHub
parent 4cd3c065e7
commit 18d8173dcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 167 additions and 6 deletions

View File

@ -0,0 +1,46 @@
/*\
title: $:/core/modules/filters/insertafter.js
type: application/javascript
module-type: filteroperator
Insert an item after another item in a list
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Order a list
*/
exports.insertafter = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
results.push(title);
});
var target = operator.operands[1] || (options.widget && options.widget.getVariable(operator.suffix || "currentTiddler"));
if(target !== operator.operand) {
// Remove the entry from the list if it is present
var pos = results.indexOf(operator.operand);
if(pos !== -1) {
results.splice(pos,1);
}
// Insert the entry after the target marker
pos = results.indexOf(target);
if(pos !== -1) {
results.splice(pos+1,0,operator.operand);
} else {
var suffix = operator.operands.length > 1 ? operator.suffix : "";
if(suffix === "start") {
results.splice(0,0,operator.operand);
} else {
results.push(operator.operand);
}
}
}
return results;
};
})();

View File

@ -32,7 +32,12 @@ exports.insertbefore = function(source,operator,options) {
if(pos !== -1) {
results.splice(pos,0,operator.operand);
} else {
results.push(operator.operand);
var suffix = operator.operands.length > 1 ? operator.suffix : "";
if(suffix == "start") {
results.splice(0,0,operator.operand);
} else {
results.push(operator.operand);
}
}
}
return results;

View File

@ -747,7 +747,46 @@ Tests the filtering mechanism.
expect(wiki.filterTiddlers("a [[b c]] +[append{TiddlerSix!!filter}]").join(",")).toBe("a,b c,one,a a,[subfilter{hasList!!list}]");
});
it("should handle the insertafter operator", function() {
var widget = require("$:/core/modules/widgets/widget.js");
var rootWidget = new widget.widget({ type:"widget", children:[ {type:"widget", children:[]} ] },
{ wiki:wiki, document:$tw.document});
rootWidget.makeChildWidgets();
var anchorWidget = rootWidget.children[0];
rootWidget.setVariable("myVar","c");
rootWidget.setVariable("tidTitle","e");
rootWidget.setVariable("tidList","one tid with spaces");
// Position title specified as suffix.
expect(wiki.filterTiddlers("a b c d e f +[insertafter:myVar[f]]",anchorWidget).join(",")).toBe("a,b,c,f,d,e");
expect(wiki.filterTiddlers("a b c d e f +[insertafter:myVar<tidTitle>]",anchorWidget).join(",")).toBe("a,b,c,e,d,f");
expect(wiki.filterTiddlers("a b c d e f +[insertafter:myVar[gg gg]]",anchorWidget).join(",")).toBe("a,b,c,gg gg,d,e,f");
expect(wiki.filterTiddlers("a b c d e +[insertafter:myVar<tidList>]",anchorWidget).join(",")).toBe("a,b,c,one tid with spaces,d,e");
expect(wiki.filterTiddlers("a b c d e f +[insertafter:tidTitle{TiddlerOne!!tags}]",anchorWidget).join(",")).toBe("a,b,c,d,e,one,f");
// Position title specified as parameter.
expect(wiki.filterTiddlers("a b c d e +[insertafter[f],[a]]",anchorWidget).join(",")).toBe("a,f,b,c,d,e");
expect(wiki.filterTiddlers("a b c d e +[insertafter[f],<myVar>]",anchorWidget).join(",")).toBe("a,b,c,f,d,e");
// Parameter takes precedence over suffix.
expect(wiki.filterTiddlers("a b c d e +[insertafter:myVar[f],[a]]",anchorWidget).join(",")).toBe("a,f,b,c,d,e");
// No position title.
expect(wiki.filterTiddlers("a b c [[with space]] +[insertafter[b]]").join(",")).toBe("a,c,with space,b");
// Position title does not exist, and no suffix given.
expect(wiki.filterTiddlers("a b c d e +[insertafter:foo[b]]").join(",")).toBe("a,c,d,e,b");
expect(wiki.filterTiddlers("a b c d e +[insertafter[b],[foo]]").join(",")).toBe("a,c,d,e,b");
expect(wiki.filterTiddlers("a b c d e +[insertafter[b],<foo>]").join(",")).toBe("a,c,d,e,b");
// Position title does not exist, but "start" or "end" given as suffix
expect(wiki.filterTiddlers("a b c d e +[insertafter:start[b],[foo]]").join(",")).toBe("b,a,c,d,e");
expect(wiki.filterTiddlers("a b c d e +[insertafter:start[b],<foo>]").join(",")).toBe("b,a,c,d,e");
expect(wiki.filterTiddlers("a b c d e +[insertafter:end[b],[foo]]").join(",")).toBe("a,c,d,e,b");
expect(wiki.filterTiddlers("a b c d e +[insertafter:end[b],<foo>]").join(",")).toBe("a,c,d,e,b");
});
it("should handle the insertbefore operator", function() {
var widget = require("$:/core/modules/widgets/widget.js");
var rootWidget = new widget.widget({ type:"widget", children:[ {type:"widget", children:[]} ] },
@ -775,10 +814,16 @@ Tests the filtering mechanism.
// No position title.
expect(wiki.filterTiddlers("a b c [[with space]] +[insertbefore[b]]").join(",")).toBe("a,c,with space,b");
// Position title does not exist.
// Position title does not exist, and no suffix given.
expect(wiki.filterTiddlers("a b c d e +[insertbefore:foo[b]]").join(",")).toBe("a,c,d,e,b");
expect(wiki.filterTiddlers("a b c d e +[insertbefore[b],[foo]]").join(",")).toBe("a,c,d,e,b");
expect(wiki.filterTiddlers("a b c d e +[insertbefore[b],<foo>]").join(",")).toBe("a,c,d,e,b");
// Position title does not exist, but "start" or "end" given as suffix
expect(wiki.filterTiddlers("a b c d e +[insertbefore:start[b],[foo]]").join(",")).toBe("b,a,c,d,e");
expect(wiki.filterTiddlers("a b c d e +[insertbefore:start[b],<foo>]").join(",")).toBe("b,a,c,d,e");
expect(wiki.filterTiddlers("a b c d e +[insertbefore:end[b],[foo]]").join(",")).toBe("a,c,d,e,b");
expect(wiki.filterTiddlers("a b c d e +[insertbefore:end[b],<foo>]").join(",")).toBe("a,c,d,e,b");
});
it("should handle the move operator", function() {

View File

@ -0,0 +1,28 @@
created: 20220223004441865
modified: 20220223004441865
tags: [[Operator Examples]] [[insertafter Operator]]
title: insertafter Operator (Examples)
type: text/vnd.tiddlywiki
\define after-title() Friday
\define missing-title() Yesterday
\define display-variable(name)
''<$text text=<<__name__>>/>'': <code><$text text={{{ [<__name__>getvariable[]] }}}/></code>
\end
These examples use the following predefined variables:
* <<display-variable after-title>>
* <<display-variable missing-title>>
<<.operator-example 1 """[list[Days of the Week]insertafter[Today]]""">>
<<.operator-example 2 """[list[Days of the Week]insertafter[Today],[Tuesday]]""">>
<<.operator-example 3 """[list[Days of the Week]insertafter[Today],<after-title>]""">>
<<.operator-example 4 """[list[Days of the Week]insertafter:after-title[Today]]""">>
<<.operator-example 5 """[list[Days of the Week]insertafter[Today],<missing-title>]""">>
<<.operator-example 6 """[list[Days of the Week]insertafter:missing-title[Today]]""">>

View File

@ -0,0 +1,31 @@
caption: insertafter
created: 20170406090122441
modified: 20220223004441865
op-input: a [[selection of titles|Title Selection]]
op-output: the input tiddler list with the new entry inserted
op-parameter: the <<.op insertafter>> operator accepts 1 or 2 parameters, see below for details
op-purpose: insert an item <<.place T>> into a list immediately after an item <<.place A>>
op-suffix: (optional) the name of a variable containing the title of the tiddler after which this one should be inserted
tags: [[Filter Operators]] [[Order Operators]] [[Listops Operators]]
title: insertafter Operator
type: text/vnd.tiddlywiki
<<.from-version "5.2.3">>
The <<.op insertafter>> operator requires at least one parameter which specifies the title to insert into the input list. A second parameter can be used to specify the title after which the new title should be inserted.
```
insertafter:<after-title-variable>[<title>],[<after-title>]
```
* ''title'' : a title <<.place T>> to insert in the input list.
* ''after-title'' : (optional). Insert <<.place T>> after this title <<.place A>> in the input list.
* ''after-title-variable'' : (optional). The name of a variable specifying <<.place A>> instead of the `after-title` parameter.
If the item <<.place A>> isn't present in the input list then the new item is inserted at the end of the list. <<.from-version "5.2.3">> The suffixes ''start'' and ''end'' can be spedified to control where the new item is inserted when <<.place A>> is not found. The suffix ''end'' is the default, inserting the new item at the end of the list. The suffix ''start'' will cause the new item to be inserted at the start of the list when <<.place A>> is not found.
<<.tip "Either [[parameter|Filter Parameter]] can be a string, a text reference or a variable">>
<<.tip "If <<.place A>> is specified as both a suffix and a parameter, the parameter takes precedence">>
<<.operator-examples "insertafter">>

View File

@ -5,7 +5,7 @@ op-input: a [[selection of titles|Title Selection]]
op-output: the input tiddler list with the new entry inserted
op-parameter: <<.from-version "5.2.2">> the <<.op insertbefore>> operator accepts 1 or 2 parameters, see below for details
op-purpose: insert an item <<.place T>> into a list immediately before an item <<.place B>>
op-suffix: (optional) the name of a variable containing the title of the tiddler before which this one should be inserted
op-suffix: <<.from-version "5.2.3">> (optional) the name of a variable containing the title of the tiddler before which this one should be inserted
tags: [[Filter Operators]] [[Order Operators]] [[Listops Operators]]
title: insertbefore Operator
type: text/vnd.tiddlywiki
@ -14,15 +14,21 @@ type: text/vnd.tiddlywiki
The <<.op insertbefore>> operator requires at least one parameter which specifies the title to insert into the input list. A second parameter can be used to specify the title before which the new title should be inserted.
<<.from-version "5.2.3">>
Using the suffix to specify <<.place B>>, the title before which the new title should be inserted, is deprecated. Instead, the two-parameter form is recommended. If the two-parameter form is used, the suffixes ''start'' and ''end'' can be used to specify where the item should be inserted if <<.place B>> is not found.
```
insertbefore:<before-title-variable>[<title>],[<before-title>]
insertbefore:<before-title-variable>[<title>]
insertbefore:<missing-location>[<title>],[<before-title>]
```
* ''title'' : a title <<.place T>> to insert in the input list.
* ''before-title'' : (optional). Insert <<.place T>> before this title <<.place B>> in the input list.
* ''before-title-variable'' : (optional). The name of a variable specifying <<.place B>> instead of the `before-title` parameter.
* ''missing-location'' : (optional). Either `start` or `end`: where to insert <<.place T>> if <<.place B>> is not found in the list.
If the item <<.place B>> isn't present in the input list then the new item is inserted at the end of the list.
If the item <<.place B>> isn't present in the input list then the new item is inserted at the end of the list. <<.from-version "5.2.3">> The suffixes ''start'' and ''end'' can be spedified to control where the new item is inserted when <<.place B>> is not found. The suffix ''end'' is the default, inserting the new item at the end of the list. The suffix ''start'' will cause the new item to be inserted at the start of the list when <<.place B>> is not found.
<<.tip "Either [[parameter|Filter Parameter]] can be a string, a text reference or a variable">>