Красена обнови решението на 07.11.2018 15:59 (преди 9 месеца)
+package main
+
+import (
+ "math"
+)
+
+type Editor interface {
+ Insert(position uint, text string) Editor
+ Delete(offset uint, length int) Editor
+ Undo() Editor
+ Redo() Editor
+ String() string
+}
+
+type Buff struct {
+ origin bool
+ offset uint
+ length int
+ text string
+}
+
+var t []*Buff // current version of the editor
+var versions [][]*Buff // all version of the editor
+var indexVersion int // on what version is the editor
+var add Buff // add buffer
+var posAdd int // where to add the text in the add buffer
+
+func (b *Buff) Insert(position uint, text string) Editor {
+ a, pos := findPosition(t, position)
+ var before, current, newcurrent, after []*Buff
+ var helpLeft, helpRight *Buff
+ before = t[:a]
+ current = append(current, t[a])
+ after = t[a+1:]
+ add.text = add.text + text
+ helper := &Buff{origin: false, offset: uint(posAdd), length: len(text), text: text}
+ posAdd += len(text)
+ if pos-len(b.String()) > 1 { // if adding is at end and is far from the original text
+ helpLeft = &Buff{origin: true, offset: current[0].offset, length: current[0].length, text: current[0].text}
+ newcurrent = append(newcurrent, helpLeft, helper)
+ } else {
+ helpLeft = &Buff{origin: true, offset: current[0].offset, length: len(current[0].text[:pos]), text: current[0].text[:pos]}
+ helpRight = &Buff{origin: true, offset: helpLeft.offset + uint(helpLeft.length), length: len(current[0].text[pos:]), text: current[0].text[pos:]}
+ newcurrent = append(newcurrent, helpLeft, helper, helpRight)
+ }
+ newAfter := make([]*Buff, len(after))
+ copy(newAfter, after)
+ x := append(before, newcurrent...)
+ x = append(x, newAfter...)
+ t = make([]*Buff, len(x))
+ copy(t, x)
+ versions = append(versions, x)
+ indexVersion += 1
+ return b
+}
+func (b *Buff) Delete(offset uint, length int) Editor {
+ if len(b.String()) < int(offset) { // if the text to be deleted is at offset more than the length of the editor
+ a := &Buff{origin: b.origin, offset: b.offset, length: b.length, text: b.text}
+ t = nil
+ t = append(t, a)
+ versions = append(versions, t)
+ indexVersion += 1
+ return a
+ }
+ var before, current, after []*Buff
+ i, place := findPosition(t, offset)
+ before = t[:i]
+ current = append(current, t[i])
+ after = t[i+1:]
+ if t[i].length >= length { // if the text is inside one buffer
+ helpBefore := &Buff{origin: current[0].origin, offset: current[0].offset, length: len(current[0].text[:place]), text: current[0].text[:place]}
+ helpAfter := &Buff{origin: current[0].origin, offset: uint(helpBefore.length + length), length: len(current[0].text[helpBefore.length+length:]), text: current[0].text[helpBefore.length+length:]}
+ var newcurrent []*Buff
+ newcurrent = append(newcurrent, helpBefore, helpAfter)
+ newAfter := make([]*Buff, len(after))
+ copy(newAfter, after)
+ newBefore := make([]*Buff, len(before))
+ copy(newBefore, before)
+ x := append(newBefore, newcurrent...)
+ x = append(x, newAfter...)
+ t = make([]*Buff, len(x))
+ copy(t, x)
+ } else { // if text to be deleted is in two or more buffers
+ helper := &Buff{origin: current[0].origin, offset: uint(place), length: len(current[0].text[:place]), text: current[0].text[:place]}
+ var newLength int
+ newLength = length - len(current[0].text[i:])
+ var place int
+ for _, val := range after {
+ if newLength < val.length {
+ break
+ } else {
+ newLength -= len(val.text)
+ place += 1
+ }
+ }
+ if place == len(after) && len(after[len(after)-1].text) < newLength { // if we want to delete out of the string
+ before = append(before, helper)
+ t = make([]*Buff, len(before))
+ copy(t, before)
+ } else { //if the text is in the correct buffer
+ newcurrent := &Buff{origin: after[place].origin, offset: after[place].offset + uint(newLength), length: len(after[place].text[newLength:]), text: after[place].text[newLength:]}
+ newAfter := after[place+1:]
+ newBefore := make([]*Buff, len(before))
+ copy(newBefore, before)
+ x := append(newBefore, newcurrent)
+ x = append(x, newAfter...)
+ t = make([]*Buff, len(x))
+ copy(t, x)
+ }
+ }
+ versions = append(versions, t)
+ indexVersion += 1
+ return b
+}
+
+func (b *Buff) Undo() Editor {
+ if indexVersion == 0 {
+ return b
+ } else {
+ var cnt int
+ for _, value := range versions {
+ if cnt == indexVersion-1 {
+ t = value
+ break
+ } else {
+ cnt += 1
+ }
+ }
+ indexVersion -= 1
+ return b
+ }
+}
+
+func (b *Buff) Redo() Editor {
+ if indexVersion == len(versions)-1 {
+ return b
+ }
+ var cnt int
+ for _, value := range versions {
+ if cnt == indexVersion+1 {
+ t = value
+ break
+ } else {
+ cnt += 1
+ }
+ }
+ indexVersion += 1
+ return b
+}
+
+func (b *Buff) String() string {
+ var res string
+ for _, value := range t {
+ res = res + value.text
+ }
+ return res
+}
+
+func findPosition(t []*Buff, position uint) (i, place int) {
+ var sum int
+ if position == 0 {
+ return i, int(position)
+ }
+ for _, value := range t {
+ if sum >= int(position) {
+ break
+ } else {
+ sum += value.length
+ i += 1
+ }
+ }
+ sum = int(math.Abs(float64(sum - t[i-1].length)))
+ place = int(math.Abs(float64(sum - int(position))))
+ return i - 1, place
+}
+
+func NewEditor(s string) Editor {
+ var r = &Buff{origin: true, offset: 0, length: len(s), text: s}
+ t = nil
+ t = append(t, r)
+ indexVersion = 0
+ versions = nil
+ versions = append(versions, t)
+ return r
+}