From 9aba07f8f83bac4173a0e9de54da97d4b3592629 Mon Sep 17 00:00:00 2001 From: blashaq Date: Thu, 21 Nov 2024 18:16:19 +0000 Subject: [PATCH 01/11] putty-style ED2 sequence handling as terminal option --- src/common/InputHandler.test.ts | 31 +++++++++++++++++++++++++++ src/common/InputHandler.ts | 28 +++++++++++++++++++----- src/common/services/OptionsService.ts | 3 ++- typings/xterm-headless.d.ts | 6 ++++++ typings/xterm.d.ts | 6 ++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index baae735c..c2198b82 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -438,6 +438,37 @@ describe('InputHandler', () => { inputHandler.eraseInLine(Params.fromArray([2])); assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false); }); + it('ED2 with scrollOnDisplayErase turned on', async () => { + const inputHandler = new TestInputHandler( + bufferService, + new MockCharsetService(), + new MockCoreService(), + new MockLogService(), + new MockOptionsService({ scrollOnDisplayErase: true }), + new MockOscLinkService(), + new MockCoreMouseService(), + new MockUnicodeService() + ); + const aLine = Array(bufferService.cols + 1).join('a'); + // add 2 full lines of text. + await inputHandler.parseP(aLine); + await inputHandler.parseP(aLine); + + inputHandler.eraseInDisplay(Params.fromArray([2])); + // those 2 lines should have been pushed to scrollback. + assert.equal(bufferService.rows + 2, bufferService.buffer.lines.length); + assert.equal(bufferService.buffer.ybase, 2); + assert.equal(bufferService.buffer.lines.get(0)?.translateToString(), aLine); + assert.equal(bufferService.buffer.lines.get(1)?.translateToString(), aLine); + + // Move to last line and add more text. + bufferService.buffer.y = bufferService.rows - 1; + bufferService.buffer.x = 0; + await inputHandler.parseP(aLine); + inputHandler.eraseInDisplay(Params.fromArray([2])); + // Screen should have been scrolled by a full screen size. + assert.equal(bufferService.rows * 2 + 2, bufferService.buffer.lines.length); + }); it('eraseInDisplay', async () => { const bufferService = new MockBufferService(80, 7); const inputHandler = new TestInputHandler( diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index b94d7855..48973691 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -1220,12 +1220,30 @@ export class InputHandler extends Disposable implements IInputHandler { this._dirtyRowTracker.markDirty(0); break; case 2: - j = this._bufferService.rows; - this._dirtyRowTracker.markDirty(j - 1); - while (j--) { - this._resetBufferLine(j, respectProtect); + if (this._optionsService.rawOptions.scrollOnDisplayErase) { + let fouldLastLineToKeep = false; + j = this._bufferService.rows; + const x = this._activeBuffer.getBlankLine(this._eraseAttrData()); + while (j > 0 && !fouldLastLineToKeep) { + j--; + const currentLine = this._activeBuffer.lines.get(this._activeBuffer.ybase + j); + if (currentLine?.translateToString() !== x.translateToString()) { + fouldLastLineToKeep = true; + this._dirtyRowTracker.markRangeDirty(0, j); + } + } + for (; j >= 0; j--) { + this._bufferService.scroll(this._eraseAttrData()); + } + } + else { + j = this._bufferService.rows; + this._dirtyRowTracker.markDirty(j - 1); + while (j--) { + this._resetBufferLine(j, respectProtect); + } + this._dirtyRowTracker.markDirty(0); } - this._dirtyRowTracker.markDirty(0); break; case 3: // Clear scrollback (everything not in viewport) diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index a757c179..772b0a0a 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -54,7 +54,8 @@ export const DEFAULT_OPTIONS: Readonly> = { convertEol: false, termName: 'xterm', cancelEvents: false, - overviewRuler: {} + overviewRuler: {}, + scrollOnDisplayErase: false }; const FONT_WEIGHT_OPTIONS: Extract[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index 3cbde44b..fdaf8fed 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -248,6 +248,12 @@ declare module '@xterm/headless' { * All features are disabled by default for security reasons. */ windowOptions?: IWindowOptions; + + /** + * If enabled ED2 (clear screen) escape sequence will push erased text to scrollback. + * This emulates PuTTY default clear screen behaviour. + */ + scrollOnDisplayErase?: boolean } /** diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index f9cf14f9..6514261e 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -331,6 +331,12 @@ declare module '@xterm/xterm' { * decorations underneath the scroll bar. */ overviewRuler?: IOverviewRulerOptions; + + /** + * If enabled ED2 (clear screen) escape sequence will push erased text to scrollback. + * This emulates PuTTY default clear screen behaviour. + */ + scrollOnDisplayErase?: boolean } /** From 780f9a31ff8320b97ee1bfd3c388cb2bd07784cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20B=C5=82asiak?= Date: Thu, 21 Nov 2024 22:46:06 +0100 Subject: [PATCH 02/11] Update src/common/InputHandler.ts Co-authored-by: jerch --- src/common/InputHandler.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 48973691..81cf086c 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -1221,15 +1221,12 @@ export class InputHandler extends Disposable implements IInputHandler { break; case 2: if (this._optionsService.rawOptions.scrollOnDisplayErase) { - let fouldLastLineToKeep = false; j = this._bufferService.rows; - const x = this._activeBuffer.getBlankLine(this._eraseAttrData()); - while (j > 0 && !fouldLastLineToKeep) { - j--; + this._dirtyRowTracker.markRangeDirty(0, j - 1); + while (j--) { const currentLine = this._activeBuffer.lines.get(this._activeBuffer.ybase + j); - if (currentLine?.translateToString() !== x.translateToString()) { - fouldLastLineToKeep = true; - this._dirtyRowTracker.markRangeDirty(0, j); + if (currentLine?.getTrimmedLength()) { + break; } } for (; j >= 0; j--) { From 92c21a695057403f41ad54f8f9864f03b7ed28d9 Mon Sep 17 00:00:00 2001 From: blashaq Date: Sat, 23 Nov 2024 10:58:19 +0000 Subject: [PATCH 03/11] + scrollOnDisplayErase option --- src/common/services/Services.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 0ceff36c..080f4a8e 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -252,6 +252,7 @@ export interface ITerminalOptions { windowOptions?: IWindowOptions; wordSeparator?: string; overviewRuler?: IOverviewRulerOptions; + scrollOnDisplayErase?: boolean; [key: string]: any; cancelEvents: boolean; From 904ddbc884437d82b2e6b1eb7ca0588302243763 Mon Sep 17 00:00:00 2001 From: blashaq Date: Sat, 23 Nov 2024 16:59:30 +0000 Subject: [PATCH 04/11] code style fix --- typings/xterm-headless.d.ts | 9 +++++---- typings/xterm.d.ts | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index fdaf8fed..b7aa1b59 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -249,11 +249,12 @@ declare module '@xterm/headless' { */ windowOptions?: IWindowOptions; - /** - * If enabled ED2 (clear screen) escape sequence will push erased text to scrollback. - * This emulates PuTTY default clear screen behaviour. + /** + * If enabled ED2 (clear screen) escape sequence will push + * erased text to scrollback. + * This emulates PuTTY default clear screen behavior. */ - scrollOnDisplayErase?: boolean + scrollOnDisplayErase?: boolean; } /** diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 6514261e..9becb0b3 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -333,10 +333,11 @@ declare module '@xterm/xterm' { overviewRuler?: IOverviewRulerOptions; /** - * If enabled ED2 (clear screen) escape sequence will push erased text to scrollback. - * This emulates PuTTY default clear screen behaviour. + * If enabled ED2 (clear screen) escape sequence will push + * erased text to scrollback. + * This emulates PuTTY default clear screen behavior. */ - scrollOnDisplayErase?: boolean + scrollOnDisplayErase?: boolean; } /** From e3e11b1a90bf45f846aab7e88539e0f627dbbc9e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 14 Mar 2025 10:26:30 -0700 Subject: [PATCH 05/11] Reflow on resize using similar logic to conpty This aligns reflowing much closer to how conpty does it. This was always an issue but only became a big issue recently because conpty 1.22+ opts to passthrough sequences rather than reprinting aggressively. This means that the conpty buffer being in sync with the xterm.js buffer is more important, otherwise the cursor will show up in a seemingly random position. The existing reflow appears to differ somewhat in conpty, like it seems to reflow at the word level, not the character level like xterm.js, but refining that closer if not worth the effort since conpty may end up relying on the terminal's buffer in the future[1]. Fixes #5319 Fixes #3513 Related #4231 Related microsoft/vscode#241978 [1]: https://github.com/microsoft/terminal/blob/main/doc/specs/%2313000%20-%20In-process%20ConPTY.md --- package.json | 2 +- src/common/buffer/Buffer.ts | 75 ++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6b3f6910..6ed4f2bb 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "jsdom": "^18.0.1", "mocha": "^10.1.0", "mustache": "^4.2.0", - "node-pty": "1.1.0-beta19", + "node-pty": "^1.1.0-beta31", "nyc": "^15.1.0", "source-map-loader": "^3.0.0", "source-map-support": "^0.5.20", diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 81ab156b..af4b240f 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -320,7 +320,37 @@ export class Buffer implements IBuffer { if (toRemove.length > 0) { const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove); reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout); + + // For conpty, it has its own copy of the buffer _without scrollback_ internally. Its behavior + // when reflowing larger is to insert empty lines at the bottom of the buffer as when lines + // unwrap conpty's view cannot pull scrollback down, so it adds empty lines at the end. + let removedInViewport = 0; + const isWindowsMode = this._optionsService.rawOptions.windowsMode || this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined; + if (isWindowsMode) { + for (let i = (toRemove.length / 2) - 1; i >= 0; i--) { + if (toRemove[i * 2 + 0] > this.ybase + removedInViewport) { + removedInViewport += toRemove[i * 2 + 1]; + } + } + } + this._reflowLargerAdjustViewport(newCols, newRows, newLayoutResult.countRemoved); + + // Apply empty lines for any removed in viewport for conpty. + if (isWindowsMode) { + if (removedInViewport > 0) { + for (let i = 0; i < removedInViewport; i++) { + // Just add the new missing rows on Windows as conpty reprints the screen with it's + // view of the world. Once a line enters scrollback for conpty it remains there + this.lines.push(new BufferLine(newCols, this.getNullCell(DEFAULT_ATTR_DATA))); + } + if (this.ybase === this.ydisp) { + this.ydisp += removedInViewport; + } + this.ybase += removedInViewport; + this.y -= removedInViewport; + } + } } } @@ -352,7 +382,7 @@ export class Buffer implements IBuffer { const nullCell = this.getNullCell(DEFAULT_ATTR_DATA); // Gather all BufferLines that need to be inserted into the Buffer here so that they can be // batched up and only committed once - const toInsert = []; + const toInsert: { start: number, newLines: IBufferLine[] }[] = []; let countToInsert = 0; // Go backwards as many lines may be trimmed and this will avoid considering them for (let y = this.lines.length - 1; y >= 0; y--) { @@ -467,6 +497,20 @@ export class Buffer implements IBuffer { this.savedY = Math.min(this.savedY + linesToAdd, this.ybase + newRows - 1); } + // For conpty, it has its own copy of the buffer _without scrollback_ internally. Its behavior + // when reflowing smaller is to reflow all lines inside the viewport, and removing empty or + // whitespace only lines from the bottom, until non-whitespace is hit in order to prevent + // content from being pushed into the scrollback. + let addedInViewport = 0; + const isWindowsMode = this._optionsService.rawOptions.windowsMode || this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined; + if (isWindowsMode) { + for (let i = toInsert.length - 1; i >= 0; i--) { + if (toInsert[i].start > this.ybase + addedInViewport) { + addedInViewport += toInsert[i].newLines.length; + } + } + } + // Rearrange lines in the buffer if there are any insertions, this is done at the end rather // than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many // costly calls to CircularList.splice. @@ -520,6 +564,35 @@ export class Buffer implements IBuffer { this.lines.onTrimEmitter.fire(amountToTrim); } } + + // Apply empty lines to remove calculated earlier for conpty. + if (isWindowsMode) { + if (addedInViewport > 0) { + let emptyLinesAtBottom = 0; + for (let i = this.lines.length - 1; i >= this.ybase + this.y; i--) { + const line = this.lines.get(i) as BufferLine; + if (line.isWrapped || line.getTrimmedLength() > 0) { + break; + } + emptyLinesAtBottom++; + } + const emptyLinesToRemove = Math.min(addedInViewport, emptyLinesAtBottom); + if (emptyLinesToRemove > 0) { + for (let i = 0; i < emptyLinesToRemove; i++) { + this.lines.pop(); + } + if (this.ybase === this.ydisp) { + this.ydisp -= emptyLinesToRemove; + } + this.ybase -= emptyLinesToRemove; + this.y += emptyLinesToRemove; + this.lines.onDeleteEmitter.fire({ + index: this.lines.length - emptyLinesToRemove, + amount: emptyLinesToRemove + }); + } + } + } } /** From 16772e8d8b326cd5a001f1b281aa54ec0b7a620f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 14 Mar 2025 10:36:22 -0700 Subject: [PATCH 06/11] Update lock file --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5ed534e8..794c4f35 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3296,10 +3296,10 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-pty@1.1.0-beta19: - version "1.1.0-beta19" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta19.tgz#a74dc04429903c5ac49ee81a15a24590da67d4f3" - integrity sha512-/p4Zu56EYDdXjjaLWzrIlFyrBnND11LQGP0/L6GEVGURfCNkAlHc3Twg/2I4NPxghimHXgvDlwp7Z2GtvDIh8A== +node-pty@^1.1.0-beta31: + version "1.1.0-beta9" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta9.tgz#ed643cb3b398d031b4e31c216e8f3b0042435f1d" + integrity sha512-/Ue38pvXJdgRZ3+me1FgfglLd301GhJN0NStiotdt61tm43N5htUyR/IXOUzOKuNaFmCwIhy6nwb77Ky41LMbw== dependencies: node-addon-api "^7.1.0" From 2c836ede76e4c709b3fb7a99defe2e83e83b18d5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 21 Apr 2025 06:34:45 -0700 Subject: [PATCH 07/11] Tweak API position/docs --- typings/xterm-headless.d.ts | 14 +++++++------- typings/xterm.d.ts | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index b3ed9ea4..3878d223 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -186,6 +186,13 @@ declare module '@xterm/headless' { */ scrollback?: number; + /** + * If enabled ED2 (clear screen) escape sequence will push + * erased text to scrollback. + * This emulates PuTTY default clear screen behavior. + */ + scrollOnDisplayErase?: boolean; + /** * The scrolling speed multiplier used for adjusting normal scrolling speed. */ @@ -255,13 +262,6 @@ declare module '@xterm/headless' { * All features are disabled by default for security reasons. */ windowOptions?: IWindowOptions; - - /** - * If enabled ED2 (clear screen) escape sequence will push - * erased text to scrollback. - * This emulates PuTTY default clear screen behavior. - */ - scrollOnDisplayErase?: boolean; } /** diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 33da30de..9f0e0fb4 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -257,6 +257,13 @@ declare module '@xterm/xterm' { */ scrollback?: number; + /** + * If enabled the Erase in Display All (ED2) escape sequence will push + * erased text to scrollback, instead of clearing only the viewport portion. + * This emulates PuTTY's default clear screen behavior. + */ + scrollOnDisplayErase?: boolean; + /** * Whether to scroll to the bottom whenever there is some user input. The * default is true. @@ -338,13 +345,6 @@ declare module '@xterm/xterm' { * decorations underneath the scroll bar. */ overviewRuler?: IOverviewRulerOptions; - - /** - * If enabled ED2 (clear screen) escape sequence will push - * erased text to scrollback. - * This emulates PuTTY default clear screen behavior. - */ - scrollOnDisplayErase?: boolean; } /** From 42946d72ba7572db596c919c830d750b7e68012c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 21 Apr 2025 06:37:17 -0700 Subject: [PATCH 08/11] More alphabetical fix --- src/common/services/OptionsService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 0843d6b7..4d2353f5 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -32,6 +32,7 @@ export const DEFAULT_OPTIONS: Readonly> = { logLevel: 'info', logger: null, scrollback: 1000, + scrollOnDisplayErase: false, scrollOnUserInput: true, scrollSensitivity: 1, screenReaderMode: false, @@ -55,8 +56,7 @@ export const DEFAULT_OPTIONS: Readonly> = { convertEol: false, termName: 'xterm', cancelEvents: false, - overviewRuler: {}, - scrollOnDisplayErase: false + overviewRuler: {} }; const FONT_WEIGHT_OPTIONS: Extract[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; From cd23a21bbfb99670816491822c7c8aac87b9982b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 21 Apr 2025 06:37:48 -0700 Subject: [PATCH 09/11] Align jsdoc --- typings/xterm-headless.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index 3878d223..f0bc42da 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -187,9 +187,9 @@ declare module '@xterm/headless' { scrollback?: number; /** - * If enabled ED2 (clear screen) escape sequence will push - * erased text to scrollback. - * This emulates PuTTY default clear screen behavior. + * If enabled the Erase in Display All (ED2) escape sequence will push + * erased text to scrollback, instead of clearing only the viewport portion. + * This emulates PuTTY's default clear screen behavior. */ scrollOnDisplayErase?: boolean; From e5b838a01b8b6af0317c4ad0bf2c8b53c6751c47 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 21 Apr 2025 06:39:15 -0700 Subject: [PATCH 10/11] scrollOnDisplayErase -> scrollOnEraseInDisplay --- src/common/InputHandler.test.ts | 4 ++-- src/common/InputHandler.ts | 2 +- src/common/services/OptionsService.ts | 2 +- src/common/services/Services.ts | 2 +- typings/xterm-headless.d.ts | 2 +- typings/xterm.d.ts | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index e750349e..78ce646a 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -438,13 +438,13 @@ describe('InputHandler', () => { inputHandler.eraseInLine(Params.fromArray([2])); assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false); }); - it('ED2 with scrollOnDisplayErase turned on', async () => { + it('ED2 with scrollOnEraseInDisplay turned on', async () => { const inputHandler = new TestInputHandler( bufferService, new MockCharsetService(), new MockCoreService(), new MockLogService(), - new MockOptionsService({ scrollOnDisplayErase: true }), + new MockOptionsService({ scrollOnEraseInDisplay: true }), new MockOscLinkService(), new MockCoreMouseService(), new MockUnicodeService() diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index f656014d..0e151173 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -1220,7 +1220,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._dirtyRowTracker.markDirty(0); break; case 2: - if (this._optionsService.rawOptions.scrollOnDisplayErase) { + if (this._optionsService.rawOptions.scrollOnEraseInDisplay) { j = this._bufferService.rows; this._dirtyRowTracker.markRangeDirty(0, j - 1); while (j--) { diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 4d2353f5..6ad48b93 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -32,7 +32,7 @@ export const DEFAULT_OPTIONS: Readonly> = { logLevel: 'info', logger: null, scrollback: 1000, - scrollOnDisplayErase: false, + scrollOnEraseInDisplay: false, scrollOnUserInput: true, scrollSensitivity: 1, screenReaderMode: false, diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 33b3eb56..9c3aebf7 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -254,7 +254,7 @@ export interface ITerminalOptions { windowOptions?: IWindowOptions; wordSeparator?: string; overviewRuler?: IOverviewRulerOptions; - scrollOnDisplayErase?: boolean; + scrollOnEraseInDisplay?: boolean; [key: string]: any; cancelEvents: boolean; diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index f0bc42da..8d1facc7 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -191,7 +191,7 @@ declare module '@xterm/headless' { * erased text to scrollback, instead of clearing only the viewport portion. * This emulates PuTTY's default clear screen behavior. */ - scrollOnDisplayErase?: boolean; + scrollOnEraseInDisplay?: boolean; /** * The scrolling speed multiplier used for adjusting normal scrolling speed. diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 9f0e0fb4..1310009e 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -262,7 +262,7 @@ declare module '@xterm/xterm' { * erased text to scrollback, instead of clearing only the viewport portion. * This emulates PuTTY's default clear screen behavior. */ - scrollOnDisplayErase?: boolean; + scrollOnEraseInDisplay?: boolean; /** * Whether to scroll to the bottom whenever there is some user input. The From e9587cf68fdf85c2c41270782f04c33ecd83540f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 21 Apr 2025 07:00:27 -0700 Subject: [PATCH 11/11] Refresh viewport after clear or ED Fixes #5327 --- src/browser/CoreBrowserTerminal.ts | 8 +++++++- src/browser/Viewport.ts | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 58da9344..14ff9433 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -532,7 +532,13 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea!.focus(); this.textarea!.select(); })); - this._register(this._onScroll.event(() => this._selectionService!.refresh())); + this._register(Event.any( + this._onScroll.event, + this._inputHandler.onScroll + )(() => { + this._selectionService!.refresh(); + this._viewport?.queueSync(); + })); this._register(this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement)); this._register(addDisposableListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e))); diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 4cec08fd..20e4db09 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -93,8 +93,8 @@ export class Viewport extends Disposable { ].join('\n'); })); - this._register(this._bufferService.onResize(() => this._queueSync())); - this._register(this._bufferService.buffers.onBufferActivate(() => this._queueSync())); + this._register(this._bufferService.onResize(() => this.queueSync())); + this._register(this._bufferService.buffers.onBufferActivate(() => this.queueSync())); this._register(this._bufferService.onScroll(() => this._sync())); this._register(this._scrollableElement.onScroll(e => this._handleScroll(e))); @@ -126,7 +126,7 @@ export class Viewport extends Disposable { }; } - private _queueSync(ydisp?: number): void { + public queueSync(ydisp?: number): void { // Update state if (ydisp !== undefined) { this._latestYDisp = ydisp;