mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-04 03:40:44 +09:00
Use a different rope implementation
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/vinzmay/go-rope"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
@@ -10,7 +11,7 @@ import (
|
||||
// simple functions for saving and wrapper functions for modifying the rope
|
||||
type Buffer struct {
|
||||
// Stores the text of the buffer
|
||||
r *Rope
|
||||
r *rope.Rope
|
||||
|
||||
// Path to the file on disk
|
||||
path string
|
||||
@@ -35,7 +36,11 @@ type Buffer struct {
|
||||
// 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)
|
||||
if txt == "" {
|
||||
b.r = new(rope.Rope)
|
||||
} else {
|
||||
b.r = rope.New(txt)
|
||||
}
|
||||
b.path = path
|
||||
b.name = path
|
||||
b.savedText = txt
|
||||
@@ -54,7 +59,11 @@ func (b *Buffer) UpdateRules() {
|
||||
|
||||
// Update fetches the string from the rope and updates the `text` and `lines` in the buffer
|
||||
func (b *Buffer) Update() {
|
||||
b.text = b.r.String()
|
||||
if b.r.Len() == 0 {
|
||||
b.text = ""
|
||||
} else {
|
||||
b.text = b.r.String()
|
||||
}
|
||||
b.lines = strings.Split(b.text, "\n")
|
||||
}
|
||||
|
||||
@@ -79,7 +88,7 @@ func (b *Buffer) IsDirty() bool {
|
||||
|
||||
// Insert a string into the rope
|
||||
func (b *Buffer) Insert(idx int, value string) {
|
||||
b.r.Insert(idx, value)
|
||||
b.r = b.r.Insert(idx, value)
|
||||
b.Update()
|
||||
}
|
||||
|
||||
@@ -93,12 +102,15 @@ func (b *Buffer) Remove(start, end int) string {
|
||||
end = b.Len()
|
||||
}
|
||||
removed := b.text[start:end]
|
||||
b.r.Remove(start, end)
|
||||
// The rope implenentation I am using wants indicies starting at 1 instead of 0
|
||||
start++
|
||||
end++
|
||||
b.r = b.r.Delete(start, end-start)
|
||||
b.Update()
|
||||
return removed
|
||||
}
|
||||
|
||||
// Len gives the length of the buffer
|
||||
func (b *Buffer) Len() int {
|
||||
return b.r.len
|
||||
return b.r.Len()
|
||||
}
|
||||
|
||||
105
src/rope.go
105
src/rope.go
@@ -1,105 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
// RopeSplitLength is the threshold used to split a leaf node into two child nodes.
|
||||
RopeSplitLength = 1000000000
|
||||
// RopeJoinLength is the threshold used to join two child nodes into one leaf node.
|
||||
RopeJoinLength = 500
|
||||
// RopeRebalanceRatio = 1.2
|
||||
)
|
||||
|
||||
// A Rope is a data structure for efficiently manipulating large strings
|
||||
type Rope struct {
|
||||
left *Rope
|
||||
right *Rope
|
||||
value string
|
||||
valueNil bool
|
||||
|
||||
len int
|
||||
}
|
||||
|
||||
// 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 = Count(r.value)
|
||||
|
||||
r.Adjust()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Adjust modifies the rope so it is more balanced
|
||||
func (r *Rope) Adjust() {
|
||||
if !r.valueNil {
|
||||
if r.len > RopeSplitLength {
|
||||
divide := int(math.Floor(float64(r.len) / 2))
|
||||
runes := []rune(r.value)
|
||||
r.left = NewRope(string(runes[:divide]))
|
||||
r.right = NewRope(string(runes[divide:]))
|
||||
r.valueNil = true
|
||||
}
|
||||
} else {
|
||||
if r.len < RopeJoinLength {
|
||||
r.value = r.left.String() + r.right.String()
|
||||
r.valueNil = false
|
||||
r.left = nil
|
||||
r.right = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of the rope
|
||||
func (r *Rope) String() string {
|
||||
if !r.valueNil {
|
||||
return r.value
|
||||
}
|
||||
return r.left.String() + r.right.String()
|
||||
}
|
||||
|
||||
// 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 = Count(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()
|
||||
}
|
||||
|
||||
// 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.valueNil = false
|
||||
r.len = Count(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()
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestInsert(t *testing.T) {
|
||||
var tests = []struct {
|
||||
origStr string
|
||||
insertStr string
|
||||
insertPos int
|
||||
want string
|
||||
}{
|
||||
{"foo", " bar", 3, "foo bar"},
|
||||
{"üñîç", "ø∂é", 4, "üñîçø∂é"},
|
||||
{"test", "3", 2, "te3st"},
|
||||
{"", "test", 0, "test"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
r := NewRope(test.origStr)
|
||||
r.Insert(test.insertPos, test.insertStr)
|
||||
got := r.String()
|
||||
if got != test.want {
|
||||
t.Errorf("Insert(%d, %s) = %s", test.insertPos, test.insertStr, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
var tests = []struct {
|
||||
inputStr string
|
||||
removeStart int
|
||||
removeEnd int
|
||||
want string
|
||||
}{
|
||||
{"foo bar", 3, 7, "foo"},
|
||||
{"üñîçø∂é", 0, 3, "çø∂é"},
|
||||
{"test", 0, 4, ""},
|
||||
}
|
||||
for _, test := range tests {
|
||||
r := NewRope(test.inputStr)
|
||||
r.Remove(test.removeStart, test.removeEnd)
|
||||
got := r.String()
|
||||
if got != test.want {
|
||||
t.Errorf("Remove(%d, %d) = %s", test.removeStart, test.removeEnd, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user