Stephen Searles 7 years ago
parent 5bcb8c736a
commit 98d79d60a8
  1. 12
      acedoc.go
  2. 285
      acedoc_test.go
  3. 23
      delta.go

@ -68,13 +68,13 @@ func (d Delta) nRows() uint {
return r
}
func (d Delta) rows() uint {
r := d.End.Row - d.Start.Row
func (d Delta) rows() int {
r := int(d.End.Row) - int(d.Start.Row)
return r
}
func (d Delta) cols() uint {
r := d.End.Column
func (d Delta) cols() int {
r := int(d.End.Column) - int(d.Start.Column)
return r
}
@ -82,6 +82,10 @@ func (d *Document) Contents() string {
d.mtx.RLock()
defer d.mtx.RUnlock()
return d.contents()
}
func (d *Document) contents() string {
return strings.Join(d.lines, "\n")
}

@ -1,7 +1,290 @@
package acedoc
import "testing"
import (
"encoding/json"
"fmt"
"testing"
)
func TestDelta2(t *testing.T) {
src := `
[
{
"delta": {
"start": {
"row": 0,
"column": 0
},
"end": {
"row": 0,
"column": 1
},
"action": "insert",
"lines": [
"a"
]
},
"after": "a"
},
{
"delta": {
"start": {
"row": 0,
"column": 1
},
"end": {
"row": 0,
"column": 2
},
"action": "insert",
"lines": [
"b"
]
},
"after": "ab"
},
{
"delta": {
"start": {
"row": 0,
"column": 2
},
"end": {
"row": 0,
"column": 3
},
"action": "insert",
"lines": [
"c"
]
},
"after": "abc"
},
{
"delta": {
"start": {
"row": 0,
"column": 3
},
"end": {
"row": 0,
"column": 4
},
"action": "insert",
"lines": [
"d"
]
},
"after": "abcd"
},
{
"delta": {
"start": {
"row": 0,
"column": 4
},
"end": {
"row": 0,
"column": 5
},
"action": "insert",
"lines": [
"e"
]
},
"after": "abcde"
},
{
"delta": {
"start": {
"row": 0,
"column": 5
},
"end": {
"row": 0,
"column": 6
},
"action": "insert",
"lines": [
"f"
]
},
"after": "abcdef"
},
{
"delta": {
"start": {
"row": 0,
"column": 2
},
"end": {
"row": 1,
"column": 0
},
"action": "insert",
"lines": [
"",
""
]
},
"after": "ab\ncdef"
},
{
"delta": {
"start": {
"row": 1,
"column": 0
},
"end": {
"row": 2,
"column": 0
},
"action": "insert",
"lines": [
"",
""
]
},
"after": "ab\n\ncdef"
},
{
"delta": {
"start": {
"row": 2,
"column": 1
},
"end": {
"row": 2,
"column": 2
},
"action": "remove",
"lines": [
"d"
]
},
"after": "ab\n\ncef"
},
{
"delta": {
"start": {
"row": 0,
"column": 2
},
"end": {
"row": 1,
"column": 0
},
"action": "remove",
"lines": [
"",
""
]
},
"after": "ab\ncef"
},
{
"delta": {
"start": {
"row": 0,
"column": 0
},
"end": {
"row": 1,
"column": 0
},
"action": "insert",
"lines": [
"",
""
]
},
"after": "\nab\ncef"
},
{
"delta": {
"start": {
"row": 2,
"column": 3
},
"end": {
"row": 3,
"column": 0
},
"action": "insert",
"lines": [
"",
""
]
},
"after": "\nab\ncef\n"
},
{
"delta": {
"start": {
"row": 3,
"column": 0
},
"end": {
"row": 3,
"column": 1
},
"action": "insert",
"lines": [
"m"
]
},
"after": "\nab\ncef\nm"
},
{
"delta": {
"start": {
"row": 2,
"column": 0
},
"end": {
"row": 2,
"column": 1
},
"action": "remove",
"lines": [
"c"
]
},
"after": "\nab\nef\nm"
}
]
`
type c struct {
After string
Delta Delta
}
cases := []c{}
err := json.Unmarshal([]byte(src), &cases)
if err != nil {
t.Fatal(err)
}
doc := NewString("")
for i, c := range cases {
t.Run(fmt.Sprint(i), func(t *testing.T) {
before := doc.Contents()
err := doc.Apply(c.Delta)
if err != nil {
t.Error(err)
return
}
if doc.Contents() != c.After {
t.Errorf("unexpected output after applying delta %q: %q -> %q, expected: %q", c.Delta, before, doc.Contents(), c.After)
}
})
}
}
func TestDelta(t *testing.T) {
type c struct {
name string

@ -2,6 +2,7 @@ package acedoc
import (
"fmt"
"math"
"strings"
)
@ -60,7 +61,6 @@ func (d DeltaAction) MarshalJSON() ([]byte, error) {
}
func (d *DeltaAction) UnmarshalJSON(b []byte) error {
fmt.Println(string(b))
if string(b) == "null" {
return nil
}
@ -81,6 +81,10 @@ type Delta struct {
source *Client
}
func (d Delta) String() string {
return fmt.Sprintf("%s:%q(%v->%v)", d.Action, strings.Join(d.Lines, "\n"), d.Start, d.End)
}
func (d Delta) Equal(other Delta) bool {
if d.Start != other.Start {
return false
@ -133,15 +137,18 @@ func (d *Document) applyInsert(dl Delta) {
row := dl.Start.Row
col := dl.Start.Column
line := d.lines[row]
if uint(len(d.lines)) == row {
d.lines = append(d.lines, "")
}
line := d.line(row)
if dl.nLines() == 1 {
d.lines[row] = line[:col] + dl.line(0) + line[col:]
} else {
newlines := []string{}
if row != 0 {
newlines = append(newlines, d.lines[:row-1]...) // old content
}
newlines = append(newlines, d.lines[:row]...) // old content
newlines = append(newlines, d.lines[row][:col]+dl.Lines[0])
newlines = append(newlines, dl.Lines[1:]...) // new content
newlines[len(newlines)-1] += d.lines[row][col:]
@ -204,7 +211,7 @@ func (d *Document) Apply(dls ...Delta) error {
func (d *Document) validate(dl Delta) error {
if !d.InDocument(dl.Start) {
return fmt.Errorf("start is not in document")
return fmt.Errorf("start %v is not in document %q", dl.Start, d.contents())
}
if dl.Action == DeltaRemove && !d.InDocument(dl.End) {
@ -217,8 +224,8 @@ func (d *Document) validate(dl Delta) error {
lastDlLine := dl.line(dl.nRows() - 1)
if uint(len(lastDlLine)) != dl.cols() {
return fmt.Errorf("delta has %d chars on the final line, but positions show range of %d chars", len(lastDlLine), dl.cols())
if !(len(lastDlLine) == 0 && dl.cols() < 0) && len(lastDlLine) != int(math.Abs(float64(dl.cols()))) {
return fmt.Errorf("delta %v has %d chars on the final line, but positions (%v -> %v) show range of %d chars", dl, len(lastDlLine), dl.Start, dl.End, dl.cols())
}
return nil

Loading…
Cancel
Save