Александър обнови решението на 07.11.2018 12:46 (преди 9 месеца)
+package main
+
+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 piece struct {
+        origin bool
+        offset uint
+        length uint
+}
+
+type pieceTable []piece
+
+type editorState struct {
+        pieces *pieceTable
+        prev   *editorState
+        next   *editorState
+}
+
+// ConcreteEditor is an implementation of the Editor interface
+type ConcreteEditor struct {
+        originBuffer string
+        addBuffer    string
+        state        *editorState
+}
+
+func (ce *ConcreteEditor) Insert(position uint, text string) Editor {
+        var currentPosition, i uint
+
+        for i < (uint)(len(*ce.state.pieces)) {
+                if currentPosition == position {
+                        ce.commitTable(ce.insertBeforePiece(i, text))
+
+                        return ce
+                } else if currentPosition < position && position < (currentPosition+(*ce.state.pieces)[i].length) {
+                        ce.commitTable(ce.insertWithinPiece(i, position-currentPosition, text))
+
+                        return ce
+                }
+
+                currentPosition += (*ce.state.pieces)[i].length
+                i++
+        }
+
+        ce.commitTable(ce.insertAtTheEnd(text))
+
+        return ce
+}
+
+func (ce *ConcreteEditor) insertWithinPiece(pieceNum uint, pieceOffset uint, text string) *pieceTable {
+        newTable := make(pieceTable, 0, len(*ce.state.pieces)+2)
+        toSplit := (*ce.state.pieces)[pieceNum]
+
+        leftP := piece{origin: toSplit.origin, offset: toSplit.offset, length: pieceOffset}
+        midP := piece{origin: false, offset: (uint)(len(ce.addBuffer)), length: (uint)(len(text))}
+        rightP := piece{origin: toSplit.origin, offset: toSplit.offset + pieceOffset, length: toSplit.length - pieceOffset}
+
+        newTable = append(newTable, (*ce.state.pieces)[:pieceNum]...)
+        newTable = append(newTable, leftP, midP, rightP)
+        newTable = append(newTable, (*ce.state.pieces)[pieceNum+1:]...)
+
+        ce.addBuffer += text
+
+        return &newTable
+}
+
+func (ce *ConcreteEditor) insertBeforePiece(pieceNum uint, text string) *pieceTable {
+        newTable := make(pieceTable, 0, len(*ce.state.pieces)+1)
+
+        midP := piece{origin: false, offset: (uint)(len(ce.addBuffer)), length: (uint)(len(text))}
+
+        newTable = append(newTable, (*ce.state.pieces)[:pieceNum]...)
+        newTable = append(newTable, midP)
+        newTable = append(newTable, (*ce.state.pieces)[pieceNum:]...)
+
+        ce.addBuffer += text
+
+        return &newTable
+}
+
+func (ce *ConcreteEditor) insertAtTheEnd(text string) *pieceTable {
+        newTable := make(pieceTable, 0, len(*ce.state.pieces)+1)
+        midP := piece{origin: false, offset: (uint)(len(ce.addBuffer)), length: (uint)(len(text))}
+
+        ce.addBuffer += text
+
+        newTable = append(*ce.state.pieces, midP)
+
+        return &newTable
+}
+
+func (ce *ConcreteEditor) Delete(offset, length uint) Editor {
+        var currentStart, charsToDelete uint
+
+        leftToDelete := length
+        currentOffset := offset
+
+        newTable := make(pieceTable, 0, len(*ce.state.pieces)+1)
+
+        // for i < (uint)(len(*ce.state.pieces)) {
+        for i, currentPiece := range *ce.state.pieces {
+                // currentPiece = (*ce.state.pieces)[i]
+
+                if leftToDelete == 0 || currentOffset < currentStart || currentOffset > currentStart+currentPiece.length {
+                        // nothing to do with this piece, just copy it to the new table
+                        newTable = append(newTable, currentPiece)
+                } else {
+                        rightPieceEmpty := currentPiece.length <= (currentOffset - currentStart + leftToDelete)
+
+                        if rightPieceEmpty {
+                                charsToDelete = currentPiece.length - currentOffset - currentStart
+                        } else {
+                                charsToDelete = leftToDelete
+                        }
+
+                        leftPiece := piece{
+                                origin: currentPiece.origin,
+                                offset: currentPiece.offset,
+                                length: currentOffset - currentStart}
+
+                        rightPiece := piece{
+                                origin: currentPiece.origin,
+                                offset: currentPiece.offset + currentOffset + charsToDelete,
+                                length: currentPiece.length - charsToDelete - (currentOffset - currentStart)}
+
+                        if leftPiece.length > 0 {
+                                newTable = append(newTable, leftPiece)
+                        }
+
+                        if !rightPieceEmpty {
+                                newTable = append(newTable, rightPiece)
+                        }
+
+                        leftToDelete -= charsToDelete
+                        currentOffset += charsToDelete
+                }
+
+                currentStart += currentPiece.length
+                i++
+        }
+
+        ce.commitTable(&newTable)
+
+        return ce
+}
+
+func (ce *ConcreteEditor) commitTable(newTable *pieceTable) {
+        newState := editorState{pieces: newTable}
+
+        ce.state.next = &newState
+        newState.prev = ce.state
+        ce.state = &newState
+}
+
+func (ce *ConcreteEditor) Undo() Editor {
+        if ce.state.prev != nil {
+                ce.state = ce.state.prev
+        }
+
+        return ce
+}
+
+func (ce *ConcreteEditor) Redo() Editor {
+        if ce.state.next != nil {
+                ce.state = ce.state.next
+        }
+
+        return ce
+}
+
+func (ce *ConcreteEditor) String() string {
+        var result = ""
+        for _, piece := range *ce.state.pieces {
+                var buffer *string
+
+                if piece.origin {
+                        buffer = &ce.originBuffer
+                } else {
+                        buffer = &ce.addBuffer
+                }
+
+                result += (*buffer)[piece.offset:(piece.offset + piece.length)]
+        }
+
+        return result
+}
+
+// NewEditor creates an editor instance from a string
+func NewEditor(s string) ConcreteEditor {
+        pieces := pieceTable{piece{origin: true, offset: 0, length: (uint)(len(s))}}
+        state := editorState{pieces: &pieces}
+
+        return ConcreteEditor{originBuffer: s, addBuffer: "", state: &state}
+}
