/Example.js Secret
Last active
October 12, 2016 12:46
Star
You must be signed in to star a gist
Tree Cursor - JS translation of https://hackage.haskell.org/package/rosezipper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: [] } ] } ] } ] | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Tree { | |
constructor(content, children) { | |
this.content = content; | |
this.children = children; | |
} | |
} | |
module.exports = (content, children) => new Tree(content, children); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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