Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kin filter: Recursively looking for kinship between tiddler titles #3511

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f5790f0
Filter operator that gathering "family" of tiddler
bimlas Oct 17, 2018
fff1ca0
Remove unneeded code
bimlas Oct 19, 2018
c8dee83
Do not use global `$tw`
bimlas Oct 19, 2018
5890ea9
Use as a real filter
bimlas Oct 19, 2018
304937d
List the kindred of input tiddles if there is no operand
bimlas Oct 24, 2018
de6bf92
Add negation option
bimlas Oct 26, 2018
55a28ef
Use to/with/from instead of up/both/down
bimlas Oct 25, 2018
bb0aa4e
Add documentation
bimlas Oct 27, 2018
7d3834a
Rename from `kindred` to `kin`
bimlas Oct 27, 2018
fdfe6de
Visualization of the filter results in the examples
bimlas Oct 31, 2018
05b9a53
Solve the problem with cyclical connections
bimlas Nov 1, 2018
f31afea
Modify examples, clarify documentation
bimlas Nov 1, 2018
ccc4d87
Change indentation, make it more readable
bimlas Nov 6, 2018
8a84087
Modify to suit to coding style
bimlas Nov 6, 2018
1d71b34
Merge remote-tracking branch 'upstream/master' into kin-filter
bimlas Nov 7, 2018
0ae7bf2
Use the new, builtin syntax for suffixes
bimlas Nov 7, 2018
dbc2d75
Prepare code to allow additional suffix options
bimlas Nov 7, 2018
152c903
Add `depth` option to specify max depth (0 means no limit)
bimlas Nov 7, 2018
2814191
Update documentation, be more verbose
bimlas Nov 7, 2018
3750994
Flip arrows on the picture; purpose of trees in examples
bimlas Nov 8, 2018
6437124
Use "older" standards; add tests
bimlas Nov 9, 2018
d6ae19b
Rename `findListingsOfTiddler` to `findTiddlersByField`
bimlas Nov 9, 2018
1832e43
Drop `describe` block of `kin` test, `it` is enough
bimlas Nov 9, 2018
ea5ab1e
Found a usage of `includes()`, changed to `indexOf()`
bimlas Nov 9, 2018
881bc32
Additional tests
bimlas Nov 9, 2018
e37596a
Accidentally changed DefaultTiddlers
bimlas Nov 20, 2018
1906752
Increment in a separate statement
bimlas Nov 20, 2018
cbbc88d
Keep `findListingsOfTiddler()` as alias of `findTiddlersByField()`
bimlas Nov 20, 2018
717e6f5
Use cache mechanism
bimlas Nov 20, 2018
660b4d3
Revert accidentally changed whitespace
bimlas Dec 10, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions core/modules/filters/kin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*\
title: $:/core/modules/filters/kin.js
type: application/javascript
module-type: filteroperator

Filter operator that recursively finds kindred between tiddlers

\*/
(function () {

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

// TODO: Should I set global tw to true?

function collectFamilyMembers(tiddler, title, fieldname, direction) {
var family_members_from = [],
family_members_to = [];

function addToResultsIfNotFoundAlready(list, title) {
if (list.includes(title)) {
return false;
}
list.push(title);
return true
}

function findKindredFrom(tiddler, title) {
if (addToResultsIfNotFoundAlready(family_members_from, title)) {
if (tiddler) {
tiddler.getFieldList(fieldname).forEach(function (target_title) {
findKindredFrom($tw.wiki.getTiddler(target_title), target_title);
});
}
}
}

function findKindredTo(title) {
if (addToResultsIfNotFoundAlready(family_members_to, title)) {
$tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) {
findKindredTo(target_title);
});
}
}

if ((direction === 'from') || (direction === 'with')) {
findKindredFrom(tiddler, title);
}
if ((direction === 'to') || (direction === 'with')) {
findKindredTo(title);
}
return uniqueArray(family_members_from.concat(family_members_to));
}

// TODO: Is there a better way for unique?
function uniqueArray(input) {
var seen = {},
output = [],
len = input.length,
j = 0;
for (var i = 0; i < len; i++) {
var item = input[i];
if (seen[item] !== 1) {
seen[item] = 1;
output[j++] = item;
}
}
return output;
}

/*
Export our filter function

TODO: May I add tests? (editions/test/tiddlers/tests)
*/
exports.kin = function (source, operator, options) {
var results = [],
needs_exclusion = operator.prefix === '!',
suffix_list = (operator.suffix || '').split(':'),
fieldname = (suffix_list[0] || 'tags').toLowerCase(),
direction = (suffix_list[1] || 'with').toLowerCase();

if ((operator.operand === '') && (needs_exclusion)) {
return [];
}

if (operator.operand !== '') {
var title_from_family = operator.operand,
tiddler_from_family = $tw.wiki.getTiddler(title_from_family),
family_members = collectFamilyMembers(tiddler_from_family, title_from_family, fieldname, direction);

source(function (tiddler, title) {
if (needs_exclusion !== family_members.includes(title)) {
results.push(title);
}
});
} else {
source(function (tiddler, title) {
results = results.concat(collectFamilyMembers(tiddler, title, fieldname, direction));
});
results = uniqueArray(results);
}

return results;
}
})();
59 changes: 59 additions & 0 deletions editions/tw5.com/tiddlers/filters/examples/kin.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
created: 20181026114434874
modified: 20181102160039607
tags: [[Operator Examples]] [[kin Operator]]
title: kin Operator (Examples)
type: text/vnd.tiddlywiki

\define item-class(highlightfilter) <$list filter="""[$highlightfilter$is[current]first[]]""">highlighted-toc-item</$list>

\define each-level(tiddlername, highlightfilter)
<li>
<$wikify name="transcluded-item-class" text=<<item-class """$highlightfilter$""">> >
<$link class=<<transcluded-item-class """$highlightfilter$""">> >[[$(currentTiddler)$]]</$link>
</$wikify>
<ul>
<$list filter="""$tiddlername$ +[kin[]tag[$(currentTiddler)$]]""">
<<each-level """$tiddlername$""" """$highlightfilter$""">>
</$list>
</ul>
</li>
\end

\define kin-toc(highlightfilter)
<div class="tc-table-of-contents">
<$tiddler tiddler="TableOfContents">
<ul>
<<each-level "[[Filter Syntax]] [[kin Operator]]" """$highlightfilter$""">>
</ul>
</$tiddler>
</div>
\end

\define kin-example-with-toc(number filter comment)
<<.operator-example """$number$""" """[$filter$]""" """$comment$""">>
<$reveal type="nomatch" state="""$:/state/kin-example-with-toc-$number$""" text="show">
<$button set="""$:/state/kin-example-with-toc-$number$""" setTo="show">Show tree</$button>
</$reveal>
<$reveal type="match" state="""$:/state/kin-example-with-toc-$number$""" text="show">
<$button set="""$:/state/kin-example-with-toc-$number$""" setTo="hide">Hide tree</$button>
<<kin-toc """$filter$""">>
</$reveal>
\end

<style>
.highlighted-toc-item a {
color: red !important;
}
</style>

Family tree of [[Filter Syntax]] and [[kin Operator]] (to really understand, look at the [[TableOfContents]])

<<kin-toc "!is[current]">>

<<kin-example-with-toc 1 "kin[Filter Syntax]" "input titles which are family members of the parameter title">>
<<kin-example-with-toc 2 "[Filter Syntax]] [[kin Operator]] +[kin[]" "collected titles which are family members of input tiddlers">>
<<kin-example-with-toc 3 "kin:tags:from[Filter Syntax]" "ancestors of tiddler based on `tags` field (`tags` points to parents)">>
<<kin-example-with-toc 4 "kin:list:to[Filter Syntax]" "ancestors of tiddler based on `list` field (`list` points to children)<br>[[Reference]] listing [[Concepts]], but [[Concepts]] does not listing [[Filters]] so they do not belong to the same family based on `list`">>
<<kin-example-with-toc 5 "kin::from[Filter Expression]kin::to[Filters]" "subset of the family tree">>
<<kin-example-with-toc 6 "kin[Filter Syntax]kin[kin Operator]" "common family members of each of the specified titles (intersection)">>
<<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kin[TableOfContents]first[10]]" "first 10 tiddlers which are not related to [[TableOfContents]] (by `tags`)">>
30 changes: 30 additions & 0 deletions editions/tw5.com/tiddlers/filters/kin.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
caption: kin
created: 20181025133804171
modified: 20181102155624126
op-input: a [[selection of titles|Title Selection]]
op-neg-output: ''with parameter <<.place T>>''<br>&raquo; those input titles which are ''<<.em not>> kin with <<.place T>>''<br>''without parameter <<.place T>>''<br>&raquo; ignored
op-output: ''with parameter <<.place T>>''<br>&raquo; those input titles which are ''kin with <<.place T>>'' <br>''without <<.place T>>''<br>&raquo; ''all'' tiddler titles which are ''kin with input titles''
op-parameter: a tiddler title or nothing
op-parameter-name: T
op-purpose: recursively looking for kinship between tiddler titles
op-suffix: [[field|TiddlerFields]] which connecting tiddlers (defaulting to <<.field tags>>) and direction (defaulting to `with`)
tags: [[Negatable Operators]] [[Tag Operators]] [[Filter Operators]] [[Field Operators]]
title: kin Operator
type: text/vnd.tiddlywiki

The syntax of suffix is:

<$railroad text="""
[: "<field>" ] ":" [: ("to" |: "with" | "from") ]
"""/>

The `<field>` suffix is assumed to be a [[title list|Title List]].

|!Direction|!Members|
|^`to`|Find the title of <<.place T>> in the target field of input tiddlers recursively + <<.place T>> itself|
|^`with`|Union of `from` and `to`|
|^`from`|Find the input titles in the target field of <<.place T>> recursively + <<.place T>> itself|

<<.op {{!!caption}}>> is a [[modifier|Selection Constructors]], but without <<.place T>> is a [[constructor|Selection Constructors]].

<<.operator-examples "kin">>