Skip to content

Commit 6d03962

Browse files
Lookup preceding object (in tree order)
1 parent 59acdc5 commit 6d03962

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

lib/SymbolTree.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,37 @@ module.exports = class SymbolTree {
110110
return this._node(object).parent;
111111
}
112112

113+
/**
114+
* Find the preceding object (A) of the given object (B).
115+
* An object A is preceding an object B if A and B are in the same tree
116+
* and A comes before B in tree order.
117+
* @method preceding
118+
* @param {Object} object
119+
* @param {Object} [treeRoot] If set, `treeRoot` must be an inclusive ancestor
120+
* of the return value (or else null is returned). This check _assumes_
121+
* that `root` is also an inclusive ancestor of the given `node`
122+
* @returns {?Object}
123+
*/
124+
preceding(object, treeRoot) {
125+
if (object === treeRoot) {
126+
return null;
127+
}
128+
129+
// if we have a previous sibling, return the last child of the last child of the ...
130+
if (this._node(object).prev) {
131+
object = this._node(object).prev;
132+
133+
while (this._node(object).last) {
134+
object = this._node(object).last;
135+
}
136+
137+
return object;
138+
}
139+
140+
// if there is no previous sibling return the parent (might be null)
141+
return this._node(object).parent;
142+
}
143+
113144
/**
114145
* Remove the object from this tree.
115146
* `O(1)`

test/SymbolTree.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,69 @@ test('Multiple SymbolTree instances should not conflict', function(t) {
437437

438438
t.end();
439439
});
440+
441+
test('look up preceding with a previous sibling', function(t) {
442+
const tree = new SymbolTree();
443+
const a = {};
444+
const b = {};
445+
446+
tree.insertAfter(b, a);
447+
448+
t.equal(null, tree.preceding(a));
449+
t.equal(a, tree.preceding(b));
450+
451+
t.end();
452+
});
453+
454+
test('look up preceding with a previous sibling with a child', function(t) {
455+
const tree = new SymbolTree();
456+
const a = {};
457+
const aa = {};
458+
const ab = {};
459+
const b = {};
460+
461+
tree.insertLast(aa, a);
462+
tree.insertLast(ab, a);
463+
tree.insertAfter(b, a);
464+
465+
t.equal(null, tree.preceding(a));
466+
t.equal(a , tree.preceding(aa));
467+
t.equal(aa , tree.preceding(ab));
468+
t.equal(ab , tree.preceding(b));
469+
470+
t.end();
471+
});
472+
473+
test('look up preceding with a previous sibling with a descendants', function(t) {
474+
const tree = new SymbolTree();
475+
const a = {};
476+
const aa = {};
477+
const ab = {};
478+
const aba = {};
479+
const abaa = {};
480+
const b = {};
481+
482+
tree.insertLast(aa, a);
483+
tree.insertLast(ab, a);
484+
tree.insertLast(aba, ab);
485+
tree.insertLast(abaa, aba);
486+
tree.insertAfter(b, a);
487+
488+
t.equal(abaa, tree.preceding(b));
489+
490+
t.end();
491+
});
492+
493+
test('look up preceding using a specified root', function(t) {
494+
const tree = new SymbolTree();
495+
const a = {};
496+
const aa = {};
497+
498+
tree.insertLast(aa, a);
499+
500+
t.equal(null, tree.preceding(a, a));
501+
t.equal(a , tree.preceding(aa, a));
502+
t.equal(null, tree.preceding(aa, aa));
503+
504+
t.end();
505+
});

0 commit comments

Comments
 (0)