added piectable

This commit is contained in:
Felix Angell 2018-06-22 09:43:02 +01:00
parent c1b51df9dd
commit 3713dc6300
5 changed files with 327 additions and 1 deletions

@ -1 +0,0 @@
Subproject commit 4446bac0ef28e0391057ecb181f9eaf823a2b21e

55
piecetable/line.go Normal file
View File

@ -0,0 +1,55 @@
package piecetable
type Line struct {
Buffer string
parent *PieceTable
mods map[int]bool
keys []int
}
func NewLine(data string, parent *PieceTable) *Line {
return &Line{
data, parent, map[int]bool{}, []int{},
}
}
func (l *Line) AppendNode(node *PieceNode) {
nodeIndex := len(l.parent.nodes)
l.mods[nodeIndex] = true
l.keys = append(l.keys, nodeIndex)
l.parent.nodes = append(l.parent.nodes, node)
}
func (l *Line) Len() int {
return len(l.String())
}
func (l *Line) String() string {
data := l.Buffer
for _, keyName := range l.keys {
thing, ok := l.mods[keyName]
// ?
if !ok || !thing {
continue
}
mod := l.parent.nodes[keyName]
if mod.Length >= 0 {
// append!
if mod.Start >= len(data) {
data += mod.Data
continue
}
fst, end := data[:mod.Start], data[mod.Start:]
data = fst + mod.Data + end
} else {
data = data[:mod.Start-1] + data[mod.Start:]
}
}
return data
}

17
piecetable/node.go Normal file
View File

@ -0,0 +1,17 @@
package piecetable
type PieceNode struct {
Index int
Start int
Length int
Data string
}
func NewPiece(data string, line int, start int) *PieceNode {
return &PieceNode{
line,
start,
len(data),
data,
}
}

View File

@ -0,0 +1,149 @@
package piecetable
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDeleteSingle(t *testing.T) {
text := `this is my testing
document i want to see how it fares
and all of that fun
stuff`
output := ` my testing
document i want to see how it fares
and all of that fun
stuff`
table := MakePieceTable(text)
for i := 0; i < len("this is "); i++ {
table.Delete(0, 1)
}
table.Undo()
fmt.Println(table.String())
assert.Equal(t, output, table.String())
}
func TestBadRedo(t *testing.T) {
text := `this is my testing
document i want to see how it fares
and all of that fun
stuff`
output := `this is my piece foo table testing
document i want to see how it fares
and all of that fun
stuff`
table := MakePieceTable(text)
table.Insert("piece table ", 0, 11)
table.Undo()
table.Redo()
// no redo history.
table.Redo()
fmt.Println(table.String())
assert.Equal(t, output, table.String())
}
func TestMultiStringInsertion(t *testing.T) {
text := `this is my testing
document i want to see how it fares
and all of that fun
stuff`
output := `this is my piece foo table testing
document i want to see how it fares
and all of that fun
stuff`
table := MakePieceTable(text)
table.Insert("piece table ", 0, 11)
table.Insert("foo ", 0, 17)
fmt.Println(table.String())
assert.Equal(t, output, table.String())
}
func TestUndo(t *testing.T) {
text := `this is my testing
document i want to see how it fares
and all of that fun
stuff`
output := `this is my piece table testing
document i want to see how it fares
and all of that fun
stuff`
table := MakePieceTable(text)
table.Insert("piece table ", 0, 11)
fmt.Println(table.String())
assert.Equal(t, output, table.String())
table.Undo()
fmt.Println(table.String())
assert.Equal(t, text, table.String())
}
func TestRedo(t *testing.T) {
text := `this is my testing
document i want to see how it fares
and all of that fun
stuff`
output := `this is my piece table testing
document i want to see how it fares
and all of that fun
stuff`
table := MakePieceTable(text)
table.Insert("piece table ", 0, 11)
fmt.Println(table.String())
assert.Equal(t, output, table.String())
table.Undo()
fmt.Println(table.String())
assert.Equal(t, text, table.String())
table.Redo()
fmt.Println(table.String())
assert.Equal(t, output, table.String())
}
func TestStringInsertion(t *testing.T) {
text := `this is my testing
document i want to see how it fares
and all of that fun
stuff`
output := `this is my piece table testing
document i want to see how it fares
and all of that fun
stuff`
table := MakePieceTable(text)
table.Insert("piece table ", 0, 11)
fmt.Println(table.String())
assert.Equal(t, output, table.String())
}
func TestPrintDocument(t *testing.T) {
text := `this is my testing
document i want to see how it fares
and all of that fun
stuff`
table := MakePieceTable(text)
fmt.Println(table.String())
assert.Equal(t, text, table.String(), "Un-modified piece table output doesn't match value expected")
}

106
piecetable/table.go Normal file
View File

@ -0,0 +1,106 @@
package piecetable
import (
"fmt"
"strings"
"unicode/utf8"
)
type PieceTable struct {
Lines []*Line
nodes []*PieceNode
redoList []*PieceNode
}
func MakePieceTable(data string) *PieceTable {
readStrings := strings.Split(data, "\n")
lines := make([]*Line, len(readStrings))
table := &PieceTable{
lines,
[]*PieceNode{},
[]*PieceNode{},
}
for idx, data := range readStrings {
lines[idx] = NewLine(data, table)
}
return table
}
func (p *PieceTable) Redo() {
if len(p.redoList) == 0 {
return
}
action := p.redoList[len(p.redoList)-1]
p.redoList = p.redoList[:len(p.redoList)-1]
actionIndex := len(p.nodes)
p.nodes = append(p.nodes, action)
line := p.Lines[action.Index]
line.mods[actionIndex] = true
}
func (p *PieceTable) Undo() {
if len(p.nodes) == 0 {
return
}
nodeIndex := len(p.nodes) - 1
// get the value we pop
change := p.nodes[nodeIndex]
// remove the node index from
// the mods (i.e. a dangling
// pointer)
line := p.Lines[change.Index]
delete(line.mods, nodeIndex)
// pop the most recent change
p.nodes = p.nodes[:nodeIndex]
// append it so we can redo it later if necessary
p.redoList = append(p.redoList, change)
}
func (p *PieceTable) Delete(line int, idx int) {
node := NewPiece("", line, idx)
node.Length = -1
p.Lines[line].AppendNode(node)
}
// TODO this builds the line and indexes it.
func (p *PieceTable) Index(line int, idx int) rune {
r, _ := utf8.DecodeLastRuneInString(p.Lines[line].String()[idx:])
return r
}
func (p *PieceTable) Insert(val string, line int, idx int) {
node := NewPiece(val, line, idx)
p.Lines[line].AppendNode(node)
}
func (p *PieceTable) Line(idx int) string {
return p.Lines[idx].String()
}
func (p *PieceTable) String() string {
var result string
for idx, line := range p.Lines {
if idx > 0 {
result += string('\n')
}
result += fmt.Sprintf(line.String())
}
return result
}
func (p *PieceTable) Print() {
for _, line := range p.Lines {
fmt.Println(line.Buffer)
}
}