// This is the text stored everytime the buffer is saved to check if the buffer is modified
savedText string
- // Provide efficient and easy access to text and lines so the rope toString does not
+ // Provide efficient and easy access to text and lines so the rope String does not
// need to be constantly recalculated
// These variables are updated in the update() function
text string
// NewBuffer creates a new buffer from `txt` with path and name `path`
func NewBuffer(txt, path string) *Buffer {
b := new(Buffer)
- b.r = newRope(txt)
+ b.r = NewRope(txt)
b.path = path
b.name = path
b.savedText = txt
// Update fetches the string from the rope and updates the `text` and `lines` in the buffer
func (b *Buffer) Update() {
- b.text = b.r.toString()
+ b.text = b.r.String()
b.lines = strings.Split(b.text, "\n")
}
// Insert a string into the rope
func (b *Buffer) Insert(idx int, value string) {
- b.r.insert(idx, value)
+ b.r.Insert(idx, value)
b.Update()
}
// Remove a slice of the rope from start to end (exclusive)
func (b *Buffer) Remove(start, end int) {
- b.r.remove(start, end)
+ b.r.Remove(start, end)
b.Update()
}
package main
import (
- // "fmt"
"math"
"unicode/utf8"
)
const (
- ropeSplitLength = 1000
- ropeJoinLength = 500
- ropeRebalanceRatio = 1.2
+ // RopeSplitLength defines how large can a string be before it is split into two nodes
+ RopeSplitLength = 1000
+ // RopeJoinLength defines how short can a string be before it is joined
+ RopeJoinLength = 500
+ // RopeRebalanceRatio = 1.2
)
-func min(a, b int) int {
+// Min takes the min of two ints
+func Min(a, b int) int {
if a > b {
return b
}
return a
}
-func max(a, b int) int {
+// Max takes the max of two ints
+func Max(a, b int) int {
if a > b {
return a
}
return b
}
+// A Rope is a data structure for efficiently manipulating large strings
type Rope struct {
left *Rope
right *Rope
len int
}
-func newRope(str string) *Rope {
+// NewRope returns a new rope from a given string
+func NewRope(str string) *Rope {
r := new(Rope)
r.value = str
r.valueNil = false
r.len = utf8.RuneCountInString(r.value)
- r.adjust()
+ r.Adjust()
return r
}
-func (r *Rope) adjust() {
+// Adjust modifies the rope so it is more balanced
+func (r *Rope) Adjust() {
if !r.valueNil {
- if r.len > ropeSplitLength {
+ 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.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()
+ if r.len < RopeJoinLength {
+ r.value = r.left.String() + r.right.String()
r.valueNil = false
r.left = nil
r.right = nil
}
}
-func (r *Rope) toString() string {
+// String returns the string representation of the rope
+func (r *Rope) String() string {
if !r.valueNil {
return r.value
}
- return r.left.toString() + r.right.toString()
+ return r.left.String() + r.right.String()
}
-func (r *Rope) remove(start, end int) {
+// Remove deletes a slice of the rope from start the to end (exclusive)
+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))
+ 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)
+ r.left.Remove(leftStart, leftEnd)
}
if rightEnd > 0 {
- r.right.remove(rightStart, rightEnd)
+ r.right.Remove(rightStart, rightEnd)
}
r.len = r.left.len + r.right.len
}
- r.adjust()
+ r.Adjust()
}
-func (r *Rope) insert(pos int, value string) {
+// Insert inserts a string into the rope at a specified position
+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.len = utf8.RuneCountInString(r.value)
} else {
if pos < r.left.len {
- r.left.insert(pos, value)
+ r.left.Insert(pos, value)
r.len = r.left.len + r.right.len
} else {
- r.right.insert(pos-r.left.len, value)
+ r.right.Insert(pos-r.left.len, value)
}
}
- r.adjust()
+ r.Adjust()
}