private abstract class RopeNode
# Length of the node
var length = 0
+
+ # Transforms the current node to a Leaf.
+ # This might be costly to invoke since this produces a FlatString concatenation.
+ # Can be used internally to limit the growth of the Rope when working with small leaves.
+ fun to_leaf: Leaf is abstract
end
# Node that represents a concatenation between two nodes (of any RopeNode type)
super RopeNode
# Left child of the node
- var _left: nullable RopeNode = null
+ var left: nullable RopeNode
# Right child of the node
- var _right: nullable RopeNode = null
-
- fun left: nullable RopeNode do return _left
- fun right: nullable RopeNode do return _right
+ var right: nullable RopeNode
- fun left=(l: RopeNode)
+ init(l: nullable RopeNode, r: nullable RopeNode)
do
- _left = l
- length = l.length
- if _right != null then length += _right.length
+ left = l
+ right = r
+ if l != null then length += l.length
+ if r != null then length += r.length
end
- fun right=(r: RopeNode)
+ redef fun to_leaf
do
- _right = r
- length = r.length
- if _left != null then length += _left.length
+ if left == null then
+ if right == null then return new StringLeaf("".as(FlatString))
+ return right.to_leaf
+ end
+ if right == null then return left.as(not null).to_leaf
+ return new StringLeaf((left.to_leaf.str.as(FlatString) + right.to_leaf.str.as(FlatString)).as(FlatString))
end
end
# Leaf of a Rope, contains a FlatString
-private class Leaf
+private abstract class Leaf
super RopeNode
# Encapsulated FlatString in the leaf node
- var str: FlatString
+ var str: FlatText
+
+end
+
+private class StringLeaf
+ super Leaf
init(val: FlatString) do
self.str = val
length = str.length
end
+ redef fun to_leaf do return self
+end
+
+# Used as a cache when using indexed access to a substring in the Rope
+private class LeafCache
+ # Cached leaf
+ var leaf: Leaf
+ # Position in Rope
+ var pos: Int
end
# Basic structure, binary tree with a root node.
# Cached version of self as a flat String
private var str_representation: nullable NativeString = null
+ private var leaf_cache: nullable LeafCache = null
+
# Empty Rope
init do from("")
# Creates a new Rope with `s` as root
init from(s: String) do
- if s isa RopeString then root = s.root else root = new Leaf(s.as(FlatString))
+ if s isa RopeString then root = s.root else root = new StringLeaf(s.as(FlatString))
end
private init from_root(r: RopeNode)
return new Path(root.as(Leaf), length, st)
end
end
- return get_node_from(root.as(not null), 0, position, new List[PathElement])
+ return get_node_from(root, 0, position, new List[PathElement])
end
# Special case for when the required pos is length
private fun get_node_from(node: RopeNode, curr_pos: Int, seek_pos: Int, stack: List[PathElement]): Path
do
assert curr_pos >= 0
- if node isa Leaf then return new Path(node, seek_pos - curr_pos, stack)
+ if node isa Leaf then
+ self.leaf_cache = new LeafCache(node, curr_pos)
+ return new Path(node, seek_pos - curr_pos, stack)
+ end
node = node.as(Concat)
if node.left != null then
return ret
end
- redef fun +(o) do return insert_at(o.to_s, length)
+ redef fun +(o) do
+ if self.length == 0 then return o.to_s
+ if o.length == 0 then return self
+ var str = o.to_s
+ if str isa FlatString then
+ return new RopeString.from_root(new Concat(root, new StringLeaf(str)))
+ else if str isa RopeString then
+ return new RopeString.from_root(new Concat(root, str.root))
+ else
+ abort
+ end
+ end
redef fun *(n)
do
var path = node_at(pos)
- var last_concat = new Concat
+ var last_concat: Concat
if path.offset == 0 then
- last_concat.right = path.leaf
- if str isa FlatString then last_concat.left = new Leaf(str) else last_concat.left = str.as(RopeString).root
+ if str isa FlatString then
+ last_concat = new Concat(new StringLeaf(str), path.leaf)
+ else
+ last_concat = new Concat(str.as(RopeString).root, path.leaf)
+ end
else if path.offset == path.leaf.length then
- if str isa FlatString then last_concat.right = new Leaf(str) else last_concat.right = str.as(RopeString).root
- last_concat.left = path.leaf
+ if str isa FlatString then
+ last_concat = new Concat(path.leaf, new StringLeaf(str))
+ else
+ last_concat = new Concat(path.leaf, str.as(RopeString).root)
+ end
else
var s = path.leaf.str
var l_half = s.substring(0, s.length - path.offset)
var r_half = s.substring_from(s.length - path.offset)
- var cct = new Concat
- cct.right = new Leaf(r_half.as(FlatString))
- last_concat.left = new Leaf(l_half.as(FlatString))
- if str isa FlatString then last_concat.right = new Leaf(str) else last_concat.right = str.as(RopeString).root
- cct.left = last_concat
- last_concat = cct
+ var cct: Concat
+ var ll = new StringLeaf(l_half.as(FlatString))
+ if str isa FlatString then
+ cct = new Concat(ll, new StringLeaf(str))
+ else
+ cct = new Concat(ll, str.as(RopeString).root)
+ end
+ last_concat = new Concat(cct, new StringLeaf(r_half.as(FlatString)))
end
for i in path.stack.reverse_iterator do
- var nod = new Concat
if i.left then
- nod.right = i.node.right.as(not null)
- nod.left = last_concat
+ last_concat = new Concat(last_concat, i.node.right)
else
- nod.left = i.node.left.as(not null)
- nod.right = last_concat
+ last_concat = new Concat(i.node.left, last_concat)
end
- last_concat = nod
end
return new RopeString.from_root(last_concat)
var lf = path.leaf
var offset = path.offset
- if path.leaf.str.length - offset > len then lf = new Leaf(lf.str.substring(offset,len).as(FlatString)) else lf = new Leaf(lf.str.substring_from(offset).as(FlatString))
+ if path.leaf.str.length - offset > len then lf = new StringLeaf(lf.str.substring(offset,len).as(FlatString)) else lf = new StringLeaf(lf.str.substring_from(offset).as(FlatString))
var nod: RopeNode = lf
for i in path.stack.reverse_iterator do
if i.right then continue
- var tmp = new Concat
- tmp.left = nod
- var r = i.node.right
- if r != null then tmp.right = r
- nod = tmp
+ nod = new Concat(nod, i.node.right)
end
var ret = new RopeString
path = ret.node_at(len-1)
offset = path.offset
- nod = new Leaf(path.leaf.str.substring(0, offset+1).as(FlatString))
+ nod = new StringLeaf(path.leaf.str.substring(0, offset+1).as(FlatString))
for i in path.stack.reverse_iterator do
if i.left then continue
- var tmp = new Concat
- tmp.right = nod
- var l = i.node.left
- if l != null then tmp.left = l
- nod = tmp
+ nod = new Concat(i.node.left, nod)
end
ret.root = nod
redef fun [](pos)
do
assert pos < tgt.length
+ if tgt.leaf_cache != null and pos >= tgt.leaf_cache.pos and (tgt.leaf_cache.pos + tgt.leaf_cache.leaf.length) > pos then return tgt.leaf_cache.leaf.str.chars[pos - tgt.leaf_cache.pos]
var path = tgt.node_at(pos)
return path.leaf.str.chars[path.offset]
end