Димитър обнови решението на 03.11.2018 18:32 (преди 9 месеца)
+package main
+
+import (
+        "strings"
+)
+
+type Editor interface {
+        // Insert text starting from given position.
+        Insert(position uint, text string) Editor
+
+        // Delete length items from offset.
+        Delete(offset, length uint) Editor
+
+        // Undo reverts latest change.
+        Undo() Editor
+
+        // Redo re-applies latest undone change.
+        Redo() Editor
+
+        // String returns complete representation of what a file looks
+        // like after all manipulations.
+        String() string
+}
+
+type Storage struct {
+        origin []byte
+        add    []byte
+}
+
+func (s *Storage) Initialize(text string) {
+        s.origin = []byte(text)
+}
+
+func (s *Storage) Add(text string) (position int, length int) {
+        position = len(s.add)
+        textInBytes := []byte(text)
+        length = len(textInBytes)
+        s.add = append(s.add, textInBytes...)
+        return
+}
+
+func (s *Storage) GetFromOriginBuffer(offset int, length int) string {
+        return string(s.origin[offset : offset+length])
+}
+
+func (s *Storage) GetFromAddBuffer(offset int, length int) string {
+        return string(s.add[offset : offset+length])
+}
+
+type Span struct {
+        origin bool
+        offset int
+        length int
+}
+
+type PieceTable struct {
+        storage Storage
+        spans   []Span
+}
+
+func (pt *PieceTable) Initialize(text string) {
+        pt.storage.Initialize(text)
+        pt.spans = append(pt.spans, Span{origin: true, offset: 0, length: len([]byte(text))})
+}
+
+func (pt *PieceTable) Insert(position int, text string) {
+        posInAddBuffer, lengthInBytes := pt.storage.Add(text)
+        newSpan := Span{origin: false, offset: posInAddBuffer, length: lengthInBytes}
+        pt.insertSpan(position, newSpan)
+}
+
+func (pt *PieceTable) Delete(offset, length int) {
+        position := 0
+        start := offset
+        end := offset + length
+        flag := false
+        for i, v := range pt.spans {
+                position += v.length
+                if position > start && position >= end && !flag {
+                        pt.spans[i].length = offset - (position - v.length)
+                        pt.insertSpansByIndex(i+1, Span{origin: v.origin, offset: pt.spans[i].offset + pt.spans[i].length + length, length: position - end})
+                        break
+                } else if position > start && position < end && !flag {
+                        flag = true
+                        pt.spans[i].length = offset - (position - v.length)
+                } else if flag && position < end {
+                        defer pt.deleteSpanByIndex(i)
+                } else if flag && position >= end {
+                        pt.spans[i].offset += end - (position - v.length)
+                        pt.spans[i].length -= end - (position - v.length)
+                        break
+                }
+        }
+}
+
+func (pt *PieceTable) String() string {
+        text := new(strings.Builder)
+        for _, v := range pt.spans {
+                if v.origin {
+                        text.WriteString(pt.storage.GetFromOriginBuffer(v.offset, v.length))
+                } else {
+                        text.WriteString(pt.storage.GetFromAddBuffer(v.offset, v.length))
+                }
+        }
+        return text.String()
+}
+
+func (pt *PieceTable) insertSpan(positionInFinalText int, span Span) {
+        position := 0
+        for i, v := range pt.spans {
+                position += v.length
+                if position > positionInFinalText {
+                        pt.spans[i].length -= position - positionInFinalText
+                        pt.insertSpansByIndex(i+1, span, Span{origin: v.origin, offset: pt.spans[i].length, length: position - positionInFinalText})
+                        position = -1
+                        break
+                }
+        }
+        if position != -1 {
+                pt.spans = append(pt.spans, span)
+        }
+}
+
+func (pt *PieceTable) insertSpansByIndex(pos int, spans ...Span) {
+        left := make([]Span, 0)
+        left = append(left, pt.spans[:pos]...)
+        left = append(left, spans...)
+        left = append(left, pt.spans[pos:]...)
+        pt.spans = left
+}
+
+func (pt *PieceTable) deleteSpanByIndex(i int) {
+        pt.spans = append(pt.spans[:i], pt.spans[i+1:]...)
+}
+
+type SimpleEditor struct {
+        pt        PieceTable
+        undoStack []PieceTable
+        redoStack []PieceTable
+}
+
+func (se *SimpleEditor) Initialize(text string) {
+        se.pt.Initialize(text)
+}
+
+func (se *SimpleEditor) Insert(position uint, text string) Editor {
+        if len(se.redoStack) > 0 {
+                se.redoStack = se.redoStack[0:0]
+        }
+        se.undoStack = append(se.undoStack, CopyPieceTable(se.pt))
+        se.pt.Insert(int(position), text)
+        return se
+}
+
+func (se *SimpleEditor) Delete(offset, length uint) Editor {
+        if len(se.redoStack) > 0 {
+                se.redoStack = se.redoStack[0:0]
+        }
+        se.undoStack = append(se.undoStack, CopyPieceTable(se.pt))
+        se.pt.Delete(int(offset), int(length))
+        return se
+}
+
+func (se *SimpleEditor) Undo() Editor {
+        if len(se.undoStack) > 0 {
+                se.redoStack = append(se.redoStack, CopyPieceTable(se.pt))
+                se.pt = se.undoStack[len(se.undoStack)-1]
+                se.undoStack = se.undoStack[:len(se.undoStack)-1]
+        }
+        return se
+}
+
+func (se *SimpleEditor) Redo() Editor {
+        if len(se.redoStack) > 0 {
+                se.undoStack = append(se.undoStack, CopyPieceTable(se.pt))
+                se.pt = se.redoStack[len(se.redoStack)-1]
+                se.redoStack = se.redoStack[:len(se.redoStack)-1]
+        }
+        return se
+}
+
+func (se *SimpleEditor) String() string {
+        return se.pt.String()
+}
+
+func CopyPieceTable(pt PieceTable) PieceTable {
+        newPt := new(PieceTable)
+        newPt.storage = pt.storage
+        newPt.spans = make([]Span, len(pt.spans))
+        copy(newPt.spans, pt.spans)
+        return *newPt
+}
+
+func NewEditor(text string) Editor {
+        result := new(SimpleEditor)
+        result.pt.Initialize(text)
+        return result
+}

Ако изпълниш
go doc builtin append, ще установиш, че кастването наtextкъм[]byteе излишно.FromOriginBufferе малко по-добро име отGetFromOriginBuffer.FromOriginе още по-добро.Originпък е супер.Същото важи и за
GetFromAddBuffer.За повече информация: https://golang.org/doc/effective_go.html#Getters