diff --git a/ui/buffer/rope.go b/ui/buffer/rope.go index bb0238b..dd873fe 100755 --- a/ui/buffer/rope.go +++ b/ui/buffer/rope.go @@ -115,6 +115,7 @@ func (b *RopeBuffer) Bytes() []byte { // Insert copies a byte slice (inserting it) into the position at line, col. func (b *RopeBuffer) Insert(line, col int, value []byte) { b.rope.Insert(b.LineColToPos(line, col), value) + b.shiftAnchors(line, col, utf8.RuneCount(value)) } // Remove deletes any characters between startLine, startCol, and endLine, @@ -126,11 +127,15 @@ func (b *RopeBuffer) Remove(startLine, startCol, endLine, endCol int) { if len := b.rope.Len(); end >= len { end = len if start > end { - start = end + return } } b.rope.Remove(start, end) + // Shift anchors within the range + b.shiftAnchorsRemovedRange(start, startLine, startCol, endLine, endCol) + // Shift anchors after the range + b.shiftAnchors(endLine, endCol+1, start-end-1) } // Returns the number of occurrences of 'sequence' in the buffer, within the range @@ -328,6 +333,31 @@ func (b *RopeBuffer) WriteTo(w io.Writer) (int64, error) { return b.rope.WriteTo(w) } +// Currently meant for the Remove function: imagine if the removed region passes through a Cursor position. +// We want to shift the cursor to the start of the region, based upon where the cursor position is. +func (b *RopeBuffer) shiftAnchorsRemovedRange(startPos, startLine, startCol, endLine, endCol int) { + for _, v := range b.anchors { + cursorLine, cursorCol := v.GetLineCol() + if cursorLine >= startLine && cursorLine <= endLine { + // If the anchor is not within the start or end columns + if (cursorLine == startLine && cursorCol < startCol) || (cursorLine == endLine && cursorCol > endCol) { + continue + } + cursorPos := b.LineColToPos(cursorLine, cursorCol) + v.line, v.col = b.PosToLineCol(cursorPos + (startPos - cursorPos)) + } + } +} + +func (b *RopeBuffer) shiftAnchors(insertLine, insertCol, runeCount int) { + for _, v := range b.anchors { + cursorLine, cursorCol := v.GetLineCol() + if insertLine < cursorLine || (insertLine == cursorLine && insertCol <= cursorCol) { + v.line, v.col = b.PosToLineCol(b.LineColToPos(cursorLine, cursorCol) + runeCount) + } + } +} + // RegisterCursor adds the Cursor to a slice which the Buffer uses to update // each Cursor based on changes that occur in the Buffer. Various functions are // called on the Cursor depending upon where the edits occurred and how it should diff --git a/ui/textedit.go b/ui/textedit.go index 38c7c67..653eb13 100755 --- a/ui/textedit.go +++ b/ui/textedit.go @@ -77,6 +77,7 @@ loop: t.Buffer = buffer.NewRopeBuffer(contents) t.cursor = buffer.NewCursor(&t.Buffer) + t.Buffer.RegisterCursor(&t.cursor) t.selection = buffer.NewRegion(&t.Buffer) // TODO: replace with automatic determination of language via filetype @@ -173,6 +174,7 @@ func (t *TextEdit) Delete(forwards bool) { // If the cursor is not at the first column of the first line... if cursLine > 0 || cursCol > 0 { t.cursor = t.cursor.Left() // Back up to that character + cursLine, cursCol = t.cursor.GetLineCol() bytes := t.Buffer.Slice(cursLine, cursCol, cursLine, cursCol) // Get the char at cursor deletedLine = bytes[0] == '\n' @@ -233,7 +235,6 @@ func (t *TextEdit) Insert(contents string) { default: // Insert character into line t.Buffer.Insert(cursLine, cursCol, []byte(string(ch))) - // t.SetLineCol(t.cury, t.curx+1) // Advance the cursor } }