From 79bc8e0bc64f50028b3e0a159afb8d96ca65bf4a Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 30 Jun 2026 09:45:26 +0200 Subject: [PATCH] Demonstrate that cursor positioning escapes collapse blank rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ConPTY presents its child's stdout as a screen buffer and uses CUP (`\x1b[;H`) to skip over blank rows rather than emitting LFs for them. Our escape interpreter swallows CUP via the catch-all "valid CSI final byte we don't implement" branch, so the blank rows the child put between non-blank ones disappear and the surrounding lines collapse together — which is what makes the delta-rendered diff in the screenshot look like its blank lines and section breaks were removed. Co-Authored-By: Claude Opus 4.7 (1M context) --- pkg/gocui/view_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pkg/gocui/view_test.go b/pkg/gocui/view_test.go index f65418821..58c83e126 100644 --- a/pkg/gocui/view_test.go +++ b/pkg/gocui/view_test.go @@ -238,6 +238,27 @@ func TestContainsColoredText(t *testing.T) { } } +func TestWriteCursorPositionEscape(t *testing.T) { + // ConPTY presents its child's output as a screen buffer and uses cursor + // positioning escapes (CUP, `\x1b[;H`) to skip over blank rows + // rather than emitting empty LFs for them. The escape interpreter must + // synthesize the row advances those CUPs imply; otherwise non-blank rows + // that the child separated with blank lines end up adjacent in the view. + v := NewView("name", 0, 0, 20, 10, OutputNormal) + // "a", then "skip to row 3" (i.e. one blank row), then "b". + v.writeString("a\r\n\x1b[3;1Hb\r\n") + + got := make([][]string, 0, len(v.lines)) + for _, l := range v.lines { + got = append(got, cellsToStrings(l.cells)) + } + + /* EXPECTED: + assert.Equal(t, [][]string{{"a"}, {}, {"b"}}, got) + ACTUAL: */ + assert.Equal(t, [][]string{{"a"}, {"b"}}, got) +} + func stringToCells(s string) []cell { var cells []cell state := -1