fix: editor whitespaces

This commit is contained in:
2026-08-03 12:06:30 +02:00
parent 42e9571834
commit 63b6a645e0
3 changed files with 112 additions and 13 deletions

View File

@@ -761,7 +761,7 @@ func normalizeLineEndings(value string) string {
type editorVisualLine struct {
text string
start, end int
start, displayStart, end int
logicalStart, logicalEnd int
}
@@ -826,9 +826,11 @@ func moveEditorCursorLine(value string, cursor, delta, wrapWidth int, normal boo
if targetIndex == index {
return cursor
}
column := lipgloss.Width(string(runes[lines[index].start:clamp(cursor, lines[index].start, lines[index].end)]))
current := lines[index]
columnStart := min(current.end, max(current.start, current.displayStart))
column := lipgloss.Width(string(runes[columnStart:clamp(cursor, columnStart, current.end)]))
target := lines[targetIndex]
position, usedWidth := target.start, 0
position, usedWidth := max(target.start, target.displayStart), 0
for position < target.end {
runeWidth := lipgloss.Width(string(runes[position]))
if usedWidth+runeWidth > column {
@@ -887,23 +889,39 @@ func editorCursorVisualPosition(editor textEditor, width int) (int, int) {
visual := editorVisualLines(editor.Text, width)
index := editorVisualLineIndex(visual, cursor)
line := visual[index]
column := lipgloss.Width(string(runes[line.start:clamp(cursor, line.start, line.end)]))
columnStart := min(line.end, max(line.start, line.displayStart))
column := lipgloss.Width(string(runes[columnStart:clamp(cursor, columnStart, line.end)]))
return index, column
}
func wrapEditorLogicalLine(runes []rune, start, end, width int) []editorVisualLine {
if start == end {
return []editorVisualLine{{
start: start, end: end, logicalStart: start, logicalEnd: end,
start: start, displayStart: start, end: end,
logicalStart: start, logicalEnd: end,
}}
}
var lines []editorVisualLine
for offset := start; offset < end; {
next := offset
displayStart := offset
if offset > start {
for displayStart < end && unicode.IsSpace(runes[displayStart]) {
displayStart++
}
}
if displayStart == end {
lines = append(lines, editorVisualLine{
start: offset, displayStart: displayStart, end: end,
logicalStart: start, logicalEnd: end,
})
break
}
next := displayStart
lineWidth := 0
for next < end {
runeWidth := lipgloss.Width(string(runes[next]))
if next > offset && lineWidth+runeWidth > width {
if next > displayStart && lineWidth+runeWidth > width {
break
}
lineWidth += runeWidth
@@ -912,14 +930,32 @@ func wrapEditorLogicalLine(runes []rune, start, end, width int) []editorVisualLi
break
}
}
if next == offset {
if next == displayStart {
next++
}
lineEnd := next
if next < end {
breakAt := -1
haveWord := false
for index := displayStart; index < next; index++ {
if unicode.IsSpace(runes[index]) {
if haveWord {
breakAt = index
}
} else {
haveWord = true
}
}
if breakAt > displayStart {
lineEnd = breakAt
}
}
lines = append(lines, editorVisualLine{
text: string(runes[offset:next]), start: offset, end: next,
text: string(runes[displayStart:lineEnd]),
start: offset, displayStart: displayStart, end: lineEnd,
logicalStart: start, logicalEnd: end,
})
offset = next
offset = lineEnd
}
return lines
}
@@ -941,12 +977,16 @@ func renderEditorVisualLine(
underlineEnd = "\x1b[24m"
)
runes := []rune(line.text)
displayCursor := cursor
if displayCursor < line.displayStart {
displayCursor = line.displayStart
}
var rendered strings.Builder
selected := false
protectedColor := ""
markdownStyle := editorMarkdownPlain
for offset, value := range runes {
position := line.start + offset
position := line.displayStart + offset
nextProtectedColor := ""
if position < protectedPrefix {
nextProtectedColor = editorMarkdownTheme.Dim
@@ -991,7 +1031,7 @@ func renderEditorVisualLine(
}
selected = nowSelected
}
if showCursor && position == cursor {
if showCursor && position == displayCursor {
switch mode {
case textEditorInsert:
if hardwareCursor {