Skip to content

Commit d6250a4

Browse files
committed
fix(terminal): prevent prompt from being erased on tab switch and creation
The ghost-line resize fix in _fit() unconditionally erased the prompt region before reflowing. This caused two bugs: 1. Tab switching: show() called _fit() which erased the prompt, but dimensions hadn't changed so fitAddon.fit() was a no-op — no SIGWINCH fired and the shell never redrew the prompt. 2. New terminal (panel hidden): spawn() used proposeDimensions() to create the PTY at the actual container size while xterm was still at default 80×24. The later _fit() saw a dimension mismatch, erased the prompt, and resized xterm — but the PTY was already at that size so SIGWINCH was a no-op. Fix: only erase the prompt region when dimensions are actually changing, and call fitAddon.fit() before spawn() so xterm and PTY start at the same size.
1 parent 9d0c4f1 commit d6250a4

2 files changed

Lines changed: 40 additions & 25 deletions

File tree

src/extensionsIntegrated/Terminal/TerminalInstance.js

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -289,35 +289,43 @@ define(function (require, exports, module) {
289289
}
290290

291291
try {
292-
// Clear the prompt region before reflow to prevent ghost lines.
293-
// xterm.js write() is asynchronous, so we must wait for the
294-
// clear to be processed before calling fit().
292+
// Only clear the prompt region when dimensions are actually
293+
// changing — i.e. a real reflow will happen. When dimensions
294+
// are unchanged (e.g. tab switch, panel re-focus) clearing
295+
// would erase the prompt without a subsequent SIGWINCH to
296+
// make the shell redraw it.
295297
if (this.terminal && this.isAlive) {
296-
const buf = this.terminal.buffer.active;
297-
let promptStart = buf.cursorY;
298-
299-
// Walk upward through wrapped lines to find prompt start
300-
while (promptStart > 0) {
301-
const line = buf.getLine(buf.baseY + promptStart);
302-
if (!line || !line.isWrapped) {
303-
break;
298+
const proposed = this.fitAddon.proposeDimensions();
299+
const dimensionsChanged = proposed &&
300+
(proposed.cols !== this.terminal.cols || proposed.rows !== this.terminal.rows);
301+
302+
if (dimensionsChanged) {
303+
const buf = this.terminal.buffer.active;
304+
let promptStart = buf.cursorY;
305+
306+
// Walk upward through wrapped lines to find prompt start
307+
while (promptStart > 0) {
308+
const line = buf.getLine(buf.baseY + promptStart);
309+
if (!line || !line.isWrapped) {
310+
break;
311+
}
312+
promptStart--;
304313
}
305-
promptStart--;
306-
}
307314

308-
// Erase from prompt start to end of screen, then fit
309-
// once the erase has been applied to the buffer.
310-
this.terminal.write(
311-
"\x1b[" + (promptStart + 1) + ";1H\x1b[J",
312-
() => {
313-
try {
314-
this.fitAddon.fit();
315-
} catch (e) {
316-
// Container might not be visible yet
315+
// Erase from prompt start to end of screen, then fit
316+
// once the erase has been applied to the buffer.
317+
this.terminal.write(
318+
"\x1b[" + (promptStart + 1) + ";1H\x1b[J",
319+
() => {
320+
try {
321+
this.fitAddon.fit();
322+
} catch (e) {
323+
// Container might not be visible yet
324+
}
317325
}
318-
}
319-
);
320-
return;
326+
);
327+
return;
328+
}
321329
}
322330

323331
this.fitAddon.fit();

src/extensionsIntegrated/Terminal/main.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,13 @@ define(function (require, exports, module) {
306306
panel.show();
307307
}
308308

309+
// Fit the terminal now that the panel is visible so xterm
310+
// has the correct dimensions before the PTY is spawned.
311+
// Without this, xterm stays at default 80x24 while the PTY
312+
// is created at the actual container size, causing a later
313+
// _fit() to erase the prompt without a real resize/SIGWINCH.
314+
try { instance.fitAddon.fit(); } catch (e) { /* not ready */ }
315+
309316
// Spawn PTY process
310317
await instance.spawn();
311318
}

0 commit comments

Comments
 (0)