Files
zyedidia.micro/rope.go
2016-03-17 17:27:57 -04:00

113 lines
2.0 KiB
Go

package main
import (
// "fmt"
"math"
"unicode/utf8"
)
const (
ropeSplitLength = 1000
ropeJoinLength = 500
ropeRebalanceRatio = 1.2
)
func min(a, b int) int {
if a > b {
return b
}
return a
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
type Rope struct {
left *Rope
right *Rope
value string
valueNil bool
len int
}
func newRope(str string) *Rope {
r := new(Rope)
r.value = str
r.valueNil = false
r.len = utf8.RuneCountInString(r.value)
r.adjust()
return r
}
func (r *Rope) adjust() {
if !r.valueNil {
if r.len > ropeSplitLength {
divide := int(math.Floor(float64(r.len) / 2))
r.left = newRope(r.value[:divide])
r.right = newRope(r.value[divide:])
r.valueNil = true
}
} else {
if r.len < ropeJoinLength {
r.value = r.left.toString() + r.right.toString()
r.valueNil = false
r.left = nil
r.right = nil
}
}
}
func (r *Rope) toString() string {
if !r.valueNil {
return r.value
}
return r.left.toString() + r.right.toString()
}
func (r *Rope) remove(start, end int) {
if !r.valueNil {
r.value = string(append([]rune(r.value)[:start], []rune(r.value)[end:]...))
r.valueNil = false
r.len = utf8.RuneCountInString(r.value)
} else {
leftStart := min(start, r.left.len)
leftEnd := min(end, r.left.len)
rightStart := max(0, min(start-r.left.len, r.right.len))
rightEnd := max(0, min(end-r.left.len, r.right.len))
if leftStart < r.left.len {
r.left.remove(leftStart, leftEnd)
}
if rightEnd > 0 {
r.right.remove(rightStart, rightEnd)
}
r.len = r.left.len + r.right.len
}
r.adjust()
}
func (r *Rope) insert(pos int, value string) {
if !r.valueNil {
first := append([]rune(r.value)[:pos], []rune(value)...)
r.value = string(append(first, []rune(r.value)[pos:]...))
r.valueNil = false
r.len = utf8.RuneCountInString(r.value)
} else {
if pos < r.left.len {
r.left.insert(pos, value)
r.len = r.left.len + r.right.len
} else {
r.right.insert(pos-r.left.len, value)
}
}
r.adjust()
}