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

Basic Backtranscludes filter #6081

Merged
merged 8 commits into from
Feb 18, 2024
Merged
26 changes: 26 additions & 0 deletions core/modules/filters/backtranscludes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*\
title: $:/core/modules/filters/backtranscludes.js
type: application/javascript
module-type: filteroperator

Filter operator for returning all the backtranscludes from a tiddler

\*/
(function(){

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

/*
Export our filter function
*/
exports.backtranscludes = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
$tw.utils.pushTop(results,options.wiki.getTiddlerBacktranscludes(title));
});
return results;
};

})();
26 changes: 26 additions & 0 deletions core/modules/filters/transcludes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*\
title: $:/core/modules/filters/transcludes.js
type: application/javascript
module-type: filteroperator

Filter operator for returning all the transcludes from a tiddler

\*/
(function(){

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

/*
Export our filter function
*/
exports.transcludes = function(source,operator,options) {
var results = new $tw.utils.LinkedList();
source(function(tiddler,title) {
results.pushTop(options.wiki.getTiddlerTranscludes(title));
});
return results.toArray();
};

})();
119 changes: 119 additions & 0 deletions core/modules/indexers/back-indexer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*\
title: $:/core/modules/indexers/back-indexer.js
type: application/javascript
module-type: indexer

By parsing the tiddler text, indexes the tiddlers' back links, back transclusions, block level back links.

\*/
function BackIndexer(wiki) {
this.wiki = wiki;
}

BackIndexer.prototype.init = function() {
this.subIndexers = {
link: new BackSubIndexer(this,"extractLinks"),
transclude: new BackSubIndexer(this,"extractTranscludes"),
};
};

BackIndexer.prototype.rebuild = function() {
$tw.utils.each(this.subIndexers,function(subIndexer) {
subIndexer.rebuild();
});
};

BackIndexer.prototype.update = function(updateDescriptor) {
$tw.utils.each(this.subIndexers,function(subIndexer) {
subIndexer.update(updateDescriptor);
});
};
function BackSubIndexer(indexer,extractor) {
this.wiki = indexer.wiki;
this.indexer = indexer;
this.extractor = extractor;
/**
* {
* [target title, e.g. tiddler title being linked to]:
* {
* [source title, e.g. tiddler title that has link syntax in its text]: true
* }
* }
*/
this.index = null;
}

BackSubIndexer.prototype.init = function() {
// lazy init until first lookup
this.index = null;
}

BackSubIndexer.prototype._init = function() {
this.index = Object.create(null);
var self = this;
this.wiki.forEachTiddler(function(sourceTitle,tiddler) {
var newTargets = self._getTarget(tiddler);
$tw.utils.each(newTargets, function(target) {
if(!self.index[target]) {
self.index[target] = Object.create(null);
}
self.index[target][sourceTitle] = true;
});
});
}

BackSubIndexer.prototype.rebuild = function() {
this.index = null;
}

/*
* Get things that is being referenced in the text, e.g. tiddler names in the link syntax.
*/
BackSubIndexer.prototype._getTarget = function(tiddler) {
var parser = this.wiki.parseText(tiddler.fields.type, tiddler.fields.text, {});
if(parser) {
return this.wiki[this.extractor](parser.tree);
}
return [];
}

BackSubIndexer.prototype.update = function(updateDescriptor) {
// lazy init/update until first lookup
if(!this.index) {
return;
}
var newTargets = [],
oldTargets = [],
self = this;
if(updateDescriptor.old.exists) {
oldTargets = this._getTarget(updateDescriptor.old.tiddler);
}
if(updateDescriptor.new.exists) {
newTargets = this._getTarget(updateDescriptor.new.tiddler);
}

$tw.utils.each(oldTargets,function(target) {
if(self.index[target]) {
delete self.index[target][updateDescriptor.old.tiddler.fields.title];
}
});
$tw.utils.each(newTargets,function(target) {
if(!self.index[target]) {
self.index[target] = Object.create(null);
}
self.index[target][updateDescriptor.new.tiddler.fields.title] = true;
});
}

BackSubIndexer.prototype.lookup = function(title) {
if(!this.index) {
this._init();
}
if(this.index[title]) {
return Object.keys(this.index[title]);
} else {
return [];
}
}

exports.BackIndexer = BackIndexer;
86 changes: 0 additions & 86 deletions core/modules/indexers/backlinks-index.js

This file was deleted.

66 changes: 64 additions & 2 deletions core/modules/wiki.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,8 @@ Return an array of tiddler titles that link to the specified tiddler
*/
exports.getTiddlerBacklinks = function(targetTitle) {
var self = this,
backlinksIndexer = this.getIndexer("BacklinksIndexer"),
backlinks = backlinksIndexer && backlinksIndexer.lookup(targetTitle);
backIndexer = this.getIndexer("BackIndexer"),
backlinks = backIndexer && backIndexer.subIndexers.link.lookup(targetTitle);

if(!backlinks) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this if seems unnecessary, should I delete it?

backlinks = [];
Expand All @@ -549,6 +549,68 @@ exports.getTiddlerBacklinks = function(targetTitle) {
return backlinks;
};


/*
Return an array of tiddler titles that are directly transcluded within the given parse tree
*/
exports.extractTranscludes = function(parseTreeRoot) {
// Count up the transcludes
var transcludes = [],
checkParseTree = function(parseTree, parentNode) {
for(var t=0; t<parseTree.length; t++) {
var parseTreeNode = parseTree[t];
if(parseTreeNode.type === "transclude" && parseTreeNode.attributes.$tiddler && parseTreeNode.attributes.$tiddler.type === "string") {
var value;
// if it is Transclusion with Templates like `{{Index||$:/core/ui/TagTemplate}}`, the `$tiddler` will point to the template. We need to find the actual target tiddler from parent node
if(parentNode && parentNode.type === "tiddler" && parentNode.attributes.tiddler && parentNode.attributes.tiddler.type === "string") {
value = parentNode.attributes.tiddler.value;
} else {
value = parseTreeNode.attributes.$tiddler.value;
}
if(transcludes.indexOf(value) === -1) {
transcludes.push(value);
}
}
if(parseTreeNode.children) {
checkParseTree(parseTreeNode.children, parseTreeNode);
}
}
};
checkParseTree(parseTreeRoot);
return transcludes;
};


/*
Return an array of tiddler titles that are transcluded from the specified tiddler
*/
exports.getTiddlerTranscludes = function(title) {
var self = this;
// We'll cache the transcludes so they only get computed if the tiddler changes
return this.getCacheForTiddler(title,"transcludes",function() {
// Parse the tiddler
var parser = self.parseTiddler(title);
if(parser) {
return self.extractTranscludes(parser.tree);
}
return [];
});
};

/*
Return an array of tiddler titles that transclude to the specified tiddler
*/
exports.getTiddlerBacktranscludes = function(targetTitle) {
var self = this,
backIndexer = this.getIndexer("BackIndexer"),
backtranscludes = backIndexer && backIndexer.subIndexers.transclude.lookup(targetTitle);

if(!backtranscludes) {
backtranscludes = [];
}
return backtranscludes;
};

/*
Return a hashmap of tiddler titles that are referenced but not defined. Each value is the number of times the missing tiddler is referenced
*/
Expand Down
Loading
Loading