Skip to content

Commit 7c07a98

Browse files
authored
Merge pull request #1358 from dgageot/editor-backspace-bug
Fix one more issue with the editor
2 parents 3d94fbe + 566227a commit 7c07a98

File tree

2 files changed

+83
-9
lines changed

2 files changed

+83
-9
lines changed

pkg/tui/components/editor/backspace_cursor_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,55 @@ func TestBackspaceCursorPosition(t *testing.T) {
101101
assert.Equal(t, "CCC", lines[2])
102102
})
103103
}
104+
105+
func TestBackspaceOnSoftWrappedLine(t *testing.T) {
106+
t.Parallel()
107+
108+
t.Run("backspace after newline on soft-wrapped text", func(t *testing.T) {
109+
t.Parallel()
110+
111+
ta := textarea.New()
112+
ta.SetWidth(20) // Small width to force wrapping
113+
ta.SetHeight(10)
114+
ta.Focus()
115+
116+
e := &editor{
117+
textarea: ta,
118+
userTyped: true,
119+
}
120+
121+
// Enter text that overflows to line 2 (soft-wraps)
122+
longText := "this is a long text that wraps"
123+
e.textarea.SetValue(longText)
124+
e.textarea.MoveToEnd()
125+
126+
t.Logf("After long text - Line: %d, LineInfo: %+v, Value: %q",
127+
e.textarea.Line(), e.textarea.LineInfo(), e.textarea.Value())
128+
129+
// Press shift+enter to add a newline (simulated by directly adding \n)
130+
e.textarea.InsertString("\n")
131+
132+
t.Logf("After newline - Line: %d, LineInfo: %+v, Value: %q",
133+
e.textarea.Line(), e.textarea.LineInfo(), e.textarea.Value())
134+
135+
// Type a few characters
136+
e.textarea.InsertString("abc")
137+
138+
t.Logf("After typing abc - Line: %d, LineInfo: %+v, Value: %q",
139+
e.textarea.Line(), e.textarea.LineInfo(), e.textarea.Value())
140+
141+
// Now backspace
142+
_, _ = e.handleGraphemeBackspace()
143+
144+
value := e.textarea.Value()
145+
t.Logf("After backspace - Line: %d, LineInfo: %+v, Value: %q",
146+
e.textarea.Line(), e.textarea.LineInfo(), value)
147+
148+
// The value should be the long text + newline + "ab"
149+
expectedValue := longText + "\nab"
150+
assert.Equal(t, expectedValue, value, "value should have one char removed")
151+
152+
// Cursor should still be on logical line 1 (the line after the newline)
153+
assert.Equal(t, 1, e.textarea.Line(), "cursor should stay on line 1")
154+
})
155+
}

pkg/tui/components/editor/editor.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -837,18 +837,40 @@ func (e *editor) handleGraphemeBackspace() (layout.Model, tea.Cmd) {
837837
lines[currentLine] = newBeforeCursor + afterCursor
838838
newValue := strings.Join(lines, "\n")
839839

840-
// Calculate new cursor position
840+
// Calculate new cursor column position within the current line
841841
newCol := len([]rune(newBeforeCursor))
842842

843-
e.textarea.SetValue(newValue)
844-
// Position cursor on the correct line and column.
845-
// SetValue does not reset cursor position, so we must first go to the
846-
// beginning of the input, then navigate down to the target line.
847-
e.textarea.MoveToBegin()
848-
for range currentLine {
849-
e.textarea.CursorDown()
843+
// Build text before cursor position (all lines before current + new before cursor)
844+
var beforeParts []string
845+
for i := range currentLine {
846+
beforeParts = append(beforeParts, lines[i])
847+
}
848+
beforeParts = append(beforeParts, newBeforeCursor)
849+
textBeforeCursor := strings.Join(beforeParts, "\n")
850+
851+
// Build text after cursor position (after cursor on current line + remaining lines)
852+
var textAfterCursor string
853+
textAfterCursor = afterCursor
854+
for i := currentLine + 1; i < len(lines); i++ {
855+
textAfterCursor += "\n" + lines[i]
856+
}
857+
858+
// Set the text before cursor and move to end
859+
e.textarea.SetValue(textBeforeCursor)
860+
e.textarea.MoveToEnd()
861+
862+
// Now insert the text after cursor - this positions cursor correctly
863+
if textAfterCursor != "" {
864+
e.textarea.SetValue(newValue)
865+
e.textarea.MoveToBegin()
866+
867+
// Keep calling CursorDown until we're on the target logical line
868+
for e.textarea.Line() < currentLine {
869+
e.textarea.CursorDown()
870+
}
871+
872+
e.textarea.SetCursorColumn(newCol)
850873
}
851-
e.textarea.SetCursorColumn(newCol)
852874

853875
e.refreshSuggestion()
854876
return e, tea.Batch(textarea.Blink, e.updateCompletionQuery())

0 commit comments

Comments
 (0)