package core // GapBuffer represents a single line of text using the gap buffer data structure. // It maintains a gap (empty space) in the buffer where the cursor is positioned, // making insertions and deletions at the cursor position very efficient (O(1)). type GapBuffer struct { buffer []rune // The underlying buffer containing text and gap gapStart int // Index where the gap starts gapEnd int // Index where the gap ends (exclusive) } // NewGapBuffer: creates a new gap buffer with the given initial content. // The gap is positioned at the end of the content. func NewGapBuffer(content string) *GapBuffer { runes := []rune(content) initialCapacity := len(runes) + 16 // Extra space for the gap buffer := make([]rune, initialCapacity) copy(buffer, runes) return &GapBuffer{ buffer: buffer, gapStart: len(runes), gapEnd: initialCapacity, } } // NewEmptyGapBuffer: Creates a new empty gap buffer with default capacity. func NewEmptyGapBuffer() *GapBuffer { initialCapacity := 16 return &GapBuffer{ buffer: make([]rune, initialCapacity), gapStart: 0, gapEnd: initialCapacity, } } // GapBuffer.String: Converts the gap buffer to a string, excluding the gap. func (gb *GapBuffer) String() string { result := make([]rune, 0, gb.Len()) result = append(result, gb.buffer[:gb.gapStart]...) result = append(result, gb.buffer[gb.gapEnd:]...) return string(result) } // GapBuffer.Len: Returns the length of the text (excluding the gap). func (gb *GapBuffer) Len() int { return len(gb.buffer) - (gb.gapEnd - gb.gapStart) } // GapBuffer.GapSize: Returns the current size of the gap. func (gb *GapBuffer) GapSize() int { return gb.gapEnd - gb.gapStart } // GapBuffer.Insert: Inserts a string at the specified position. // This moves the gap to the position first, then inserts the text. func (gb *GapBuffer) Insert(pos int, text string) { if pos < 0 || pos > gb.Len() { return // Invalid position } runes := []rune(text) if len(runes) == 0 { return } // Move gap to insertion position gb.moveGap(pos) // Ensure gap is large enough if gb.GapSize() < len(runes) { gb.grow(len(runes)) } // Insert runes at gap start copy(gb.buffer[gb.gapStart:], runes) gb.gapStart += len(runes) } // GapBuffer.Delete: Deletes count runes starting at position pos. func (gb *GapBuffer) Delete(pos, count int) { if pos < 0 || pos >= gb.Len() || count <= 0 { return } // Clamp count to not exceed buffer length if pos+count > gb.Len() { count = gb.Len() - pos } // Move gap to deletion position gb.moveGap(pos) // Expand gap to absorb deleted characters gb.gapEnd += count } // GapBuffer.RuneAt: Returns the rune at the specified position. func (gb *GapBuffer) RuneAt(pos int) rune { if pos < 0 || pos >= gb.Len() { return 0 } if pos < gb.gapStart { return gb.buffer[pos] } return gb.buffer[pos+gb.GapSize()] } // GapBuffer.Substring: Returns a substring from start to end (exclusive). func (gb *GapBuffer) Substring(start, end int) string { if start < 0 { start = 0 } if end > gb.Len() { end = gb.Len() } if start >= end { return "" } result := make([]rune, 0, end-start) for i := start; i < end; i++ { result = append(result, gb.RuneAt(i)) } return string(result) } // GapBuffer.moveGap: Moves the gap to the specified position. func (gb *GapBuffer) moveGap(pos int) { if pos < gb.gapStart { // Move gap left: shift text from [pos, gapStart) to [gapEnd-delta, gapEnd) delta := gb.gapStart - pos copy(gb.buffer[gb.gapEnd-delta:gb.gapEnd], gb.buffer[pos:gb.gapStart]) gb.gapStart = pos gb.gapEnd -= delta } else if pos > gb.gapStart { // Move gap right: shift text from [gapEnd, gapEnd+delta) to [gapStart, gapStart+delta) delta := pos - gb.gapStart copy(gb.buffer[gb.gapStart:gb.gapStart+delta], gb.buffer[gb.gapEnd:gb.gapEnd+delta]) gb.gapStart += delta gb.gapEnd += delta } } // GapBuffer.grow: Expands the buffer to accommodate at least minGapSize additional characters. func (gb *GapBuffer) grow(minGapSize int) { oldLen := len(gb.buffer) newGapSize := minGapSize * 2 // Double the required size for future insertions newLen := oldLen + newGapSize - gb.GapSize() newBuffer := make([]rune, newLen) // Copy text before gap copy(newBuffer, gb.buffer[:gb.gapStart]) // Copy text after gap to new position newGapEnd := newLen - (oldLen - gb.gapEnd) copy(newBuffer[newGapEnd:], gb.buffer[gb.gapEnd:]) gb.buffer = newBuffer gb.gapEnd = newGapEnd } // GapBuffer.Set: Replaces the entire content of the gap buffer. func (gb *GapBuffer) Set(content string) { runes := []rune(content) capacity := len(runes) + 16 gb.buffer = make([]rune, capacity) copy(gb.buffer, runes) gb.gapStart = len(runes) gb.gapEnd = capacity } // GapBuffer.Clear: Removes all content from the gap buffer. func (gb *GapBuffer) Clear() { gb.gapStart = 0 gb.gapEnd = len(gb.buffer) }