From 98d79d60a8f31b5793213540b3cfb330fc21dcbc Mon Sep 17 00:00:00 2001 From: Stephen Searles Date: Tue, 13 Jun 2017 19:43:03 -0700 Subject: [PATCH] wip --- acedoc.go | 12 ++- acedoc_test.go | 285 ++++++++++++++++++++++++++++++++++++++++++++++++- delta.go | 23 ++-- 3 files changed, 307 insertions(+), 13 deletions(-) diff --git a/acedoc.go b/acedoc.go index 582b073..2de1e4e 100644 --- a/acedoc.go +++ b/acedoc.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") } diff --git a/acedoc_test.go b/acedoc_test.go index 7de21fd..4ac7f4e 100644 --- a/acedoc_test.go +++ b/acedoc_test.go @@ -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 diff --git a/delta.go b/delta.go index aa04518..0076199 100644 --- a/delta.go +++ b/delta.go @@ -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