Skip to content

Instantly share code, notes, and snippets.

@scott-christopher
Last active October 12, 2016 12:46
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save scott-christopher/b5adebc5c14c7a074d655a871f773b1b to your computer and use it in GitHub Desktop.
Tree Cursor - JS translation of https://hackage.haskell.org/package/rosezipper
const Tree = require('./Tree');
const TreeCursor = require('./TreeCursor');
const notes = [
Tree('', [
Tree('', []),
Tree('', [])
]),
Tree('', [
Tree('', [
Tree('view this note', [])
])
])
];
const cursor = TreeCursor.fromForest(notes);
const target = cursor.nextTree().next().firstChild().firstChild();
const updated = target.modifyContent(note => note.toUpperCase()).toForest();
console.log(updated);
/*
[ Tree {
content: '',
children:
[ Tree { content: '', children: [] },
Tree { content: '', children: [] } ] },
Tree {
content: '',
children:
[ Tree {
content: '',
children: [ Tree { content: 'VIEW THIS NOTE', children: [] } ] } ] } ]
*/
class Tree {
constructor(content, children) {
this.content = content;
this.children = children;
}
}
module.exports = (content, children) => new Tree(content, children);
const Tree = require('./Tree');
class TreeCursor {
constructor(before, after, parents) {
this.before = before;
this.after = after;
this.parents = parents;
}
parent() {
if (this.parents.length > 0) {
const { ls, a, rs } = this.parents[0];
return new On(Tree(a, this.forest()), ls, rs, this.parents.slice(1));
} else {
return null;
}
}
toForest() {
const p = this.parent();
return p === null ? this.forest() : p.toForest();
}
isRoot() {
return this.parents.length === 0;
}
isFirst() {
return this.before.length === 0;
}
isLast() {
return this.after.length === 0;
}
}
class On extends TreeCursor {
constructor(tree, before, after, parents) {
super(before, after, parents);
this.tree = tree;
}
prev() {
return this.prevSpace().prevTree();
}
next() {
return this.nextSpace().nextTree();
}
forest() {
return [].concat(this.before).reverse().concat(this.tree).concat(this.after);
}
prevSpace() {
return new Between(this.before, [this.tree].concat(this.after), this.parents);
}
nextSpace() {
return new Between([this.tree].concat(this.before), this.after, this.parents);
}
root() {
const parent = this.parent();
return parent === null ? this : parent.root();
}
children() {
const thisAsParent = { ls: this.before, a: this.tree.content, rs: this.after };
return new Between([], this.tree.children, [thisAsParent].concat(this.parents));
}
firstChild() {
return this.children().nextTree();
}
lastChild() {
return this.children().last().prevTree();
}
childAt(n) {
if (n < 0) return null;
return this.children().spaceAt(n).nextTree();
}
toTree() {
return this.root().tree;
}
content() {
return this.tree.content;
}
remove() {
return new Between(this.before, this.after, this.parents);
}
isLeaf() {
return this.tree.children.length === 0;
}
setTree(t) {
return new On(t, this.before, this.after, this.parents);
}
modifyTree(f) {
return this.setTree(f(this.tree));
}
setContent(a) {
return this.modifyTree(t => Tree(a, t.children));
}
modifyContent(f) {
return this.setContent(f(this.tree.content));
}
}
class Between extends TreeCursor {
prev() {
const t = this.prevTree();
return t === null ? null : t.prevSpace();
}
next() {
const t = this.nextTree();
return t === null ? null : t.nextSpace();
}
forest() {
[].concat(this.before).reverse().concat(this.after);
}
prevTree() {
return this.before.length > 0
? new On(this.before[0], this.before.slice(1), this.after, this.parents)
: null;
}
nextTree() {
return this.after.length > 0
? new On(this.after[0], this.before, this.after.slice(1), this.parents)
: null;
}
first() {
const siblings = [].concat(this.before).reverse().concat(this.after);
return new Between([], siblings, this.parents);
}
last() {
const siblings = [].concat(this.after).reverse().concat(this.before);
return new Between(siblings, [], this.parents);
}
spaceAt(n) {
const forest = this.forest();
const as = forest.slice(0, n).reverse();
const bs = forest.slice(n);
return new Between(as, bs, this.parents);
}
insert(t) {
return new On(t, this.before, this.after, this.parents);
}
}
const fromTree = t => new On(t, [], [], []);
const fromForest = ts => new Between([], ts, []);
module.exports = {
fromForest,
fromTree
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment