| | |
| | |
| | |
| |
|
| | |
| | package edit |
| |
|
| | import ( |
| | "fmt" |
| | "sort" |
| | ) |
| |
|
| | |
| | type Buffer struct { |
| | old []byte |
| | q edits |
| | } |
| |
|
| | |
| | type edit struct { |
| | start int |
| | end int |
| | new string |
| | } |
| |
|
| | |
| | type edits []edit |
| |
|
| | func (x edits) Len() int { return len(x) } |
| | func (x edits) Swap(i, j int) { x[i], x[j] = x[j], x[i] } |
| | func (x edits) Less(i, j int) bool { |
| | if x[i].start != x[j].start { |
| | return x[i].start < x[j].start |
| | } |
| | return x[i].end < x[j].end |
| | } |
| |
|
| | |
| | |
| | |
| | func NewBuffer(data []byte) *Buffer { |
| | return &Buffer{old: data} |
| | } |
| |
|
| | func (b *Buffer) Insert(pos int, new string) { |
| | if pos < 0 || pos > len(b.old) { |
| | panic("invalid edit position") |
| | } |
| | b.q = append(b.q, edit{pos, pos, new}) |
| | } |
| |
|
| | func (b *Buffer) Delete(start, end int) { |
| | if end < start || start < 0 || end > len(b.old) { |
| | panic("invalid edit position") |
| | } |
| | b.q = append(b.q, edit{start, end, ""}) |
| | } |
| |
|
| | func (b *Buffer) Replace(start, end int, new string) { |
| | if end < start || start < 0 || end > len(b.old) { |
| | panic("invalid edit position") |
| | } |
| | b.q = append(b.q, edit{start, end, new}) |
| | } |
| |
|
| | |
| | |
| | func (b *Buffer) Bytes() []byte { |
| | |
| | |
| | |
| | sort.Stable(b.q) |
| |
|
| | var new []byte |
| | offset := 0 |
| | for i, e := range b.q { |
| | if e.start < offset { |
| | e0 := b.q[i-1] |
| | panic(fmt.Sprintf("overlapping edits: [%d,%d)->%q, [%d,%d)->%q", e0.start, e0.end, e0.new, e.start, e.end, e.new)) |
| | } |
| | new = append(new, b.old[offset:e.start]...) |
| | offset = e.end |
| | new = append(new, e.new...) |
| | } |
| | new = append(new, b.old[offset:]...) |
| | return new |
| | } |
| |
|
| | |
| | |
| | func (b *Buffer) String() string { |
| | return string(b.Bytes()) |
| | } |
| |
|