From 9aba07f8f83bac4173a0e9de54da97d4b3592629 Mon Sep 17 00:00:00 2001 From: blashaq Date: Thu, 21 Nov 2024 18:16:19 +0000 Subject: [PATCH 1/8] 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 2/8] 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 3/8] + 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 4/8] 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 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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