From ae04a425c06f9b6f262b4e6a11aa71cde70f389d Mon Sep 17 00:00:00 2001 From: Rob Hoelz Date: Thu, 26 Mar 2020 08:15:02 -0500 Subject: [PATCH] Add backlinks indexer (#4421) * Add tests for backlinks * Add backlinks indexer * Use backlinks indexer in getTiddlerBacklinks if available * Extract link extraction into its own method This way we can provide an arbitrary parse tree, rather than just a title, which will allow us to compare lists of outgoing links between versions of a single tiddler * Use new extractLinks method in backlinks indexer ...rather than copy-pasting the implementation * Remove ES6-isms TiddlyWiki needs to work with browsers that only support ES5 --- core/modules/indexers/backlinks-index.js | 86 ++++++++++++ core/modules/wiki.js | 61 ++++---- .../test/tiddlers/tests/test-backlinks.js | 132 ++++++++++++++++++ 3 files changed, 255 insertions(+), 24 deletions(-) create mode 100644 core/modules/indexers/backlinks-index.js create mode 100644 editions/test/tiddlers/tests/test-backlinks.js diff --git a/core/modules/indexers/backlinks-index.js b/core/modules/indexers/backlinks-index.js new file mode 100644 index 000000000..5902e2829 --- /dev/null +++ b/core/modules/indexers/backlinks-index.js @@ -0,0 +1,86 @@ +/*\ +title: $:/core/modules/indexers/backlinks-indexer.js +type: application/javascript +module-type: indexer + +Indexes the tiddlers' backlinks + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global modules: false */ +"use strict"; + + +function BacklinksIndexer(wiki) { + this.wiki = wiki; +} + +BacklinksIndexer.prototype.init = function() { + this.index = null; +} + +BacklinksIndexer.prototype.rebuild = function() { + this.index = null; +} + +BacklinksIndexer.prototype._getLinks = function(tiddler) { + var parser = this.wiki.parseText(tiddler.fields.type, tiddler.fields.text, {}); + if(parser) { + return this.wiki.extractLinks(parser.tree); + } + return []; +} + +BacklinksIndexer.prototype.update = function(updateDescriptor) { + if(!this.index) { + return; + } + var newLinks = [], + oldLinks = [], + self = this; + if(updateDescriptor.old.exists) { + oldLinks = this._getLinks(updateDescriptor.old.tiddler); + } + if(updateDescriptor.new.exists) { + newLinks = this._getLinks(updateDescriptor.new.tiddler); + } + + $tw.utils.each(oldLinks,function(link) { + if(self.index[link]) { + delete self.index[link][updateDescriptor.old.tiddler.fields.title]; + } + }); + $tw.utils.each(newLinks,function(link) { + if(!self.index[link]) { + self.index[link] = Object.create(null); + } + self.index[link][updateDescriptor.new.tiddler.fields.title] = true; + }); +} + +BacklinksIndexer.prototype.lookup = function(title) { + if(!this.index) { + this.index = Object.create(null); + var self = this; + this.wiki.forEachTiddler(function(title,tiddler) { + var links = self._getLinks(tiddler); + $tw.utils.each(links, function(link) { + if(!self.index[link]) { + self.index[link] = Object.create(null); + } + self.index[link][title] = true; + }); + }); + } + if(this.index[title]) { + return Object.keys(this.index[title]); + } else { + return []; + } +} + +exports.BacklinksIndexer = BacklinksIndexer; + +})(); diff --git a/core/modules/wiki.js b/core/modules/wiki.js index fc8b42eda..d44940cb6 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -414,6 +414,30 @@ exports.forEachTiddler = function(/* [options,]callback */) { } }; +/* +Return an array of tiddler titles that are directly linked within the given parse tree + */ +exports.extractLinks = function(parseTreeRoot) { + // Count up the links + var links = [], + checkParseTree = function(parseTree) { + for(var t=0; t