Merge pull request #5224 from Blashaq/master

putty-style ED2 sequence handling as terminal option
This commit is contained in:
Daniel Imms
2025-04-21 06:54:42 -07:00
committed by GitHub
6 changed files with 67 additions and 5 deletions
+31
View File
@@ -438,6 +438,37 @@ describe('InputHandler', () => {
inputHandler.eraseInLine(Params.fromArray([2]));
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false);
});
it('ED2 with scrollOnEraseInDisplay turned on', async () => {
const inputHandler = new TestInputHandler(
bufferService,
new MockCharsetService(),
new MockCoreService(),
new MockLogService(),
new MockOptionsService({ scrollOnEraseInDisplay: 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(
+20 -5
View File
@@ -1220,12 +1220,27 @@ 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.scrollOnEraseInDisplay) {
j = this._bufferService.rows;
this._dirtyRowTracker.markRangeDirty(0, j - 1);
while (j--) {
const currentLine = this._activeBuffer.lines.get(this._activeBuffer.ybase + j);
if (currentLine?.getTrimmedLength()) {
break;
}
}
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)
+1
View File
@@ -32,6 +32,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
logLevel: 'info',
logger: null,
scrollback: 1000,
scrollOnEraseInDisplay: false,
scrollOnUserInput: true,
scrollSensitivity: 1,
screenReaderMode: false,
+1
View File
@@ -254,6 +254,7 @@ export interface ITerminalOptions {
windowOptions?: IWindowOptions;
wordSeparator?: string;
overviewRuler?: IOverviewRulerOptions;
scrollOnEraseInDisplay?: boolean;
[key: string]: any;
cancelEvents: boolean;
+7
View File
@@ -186,6 +186,13 @@ declare module '@xterm/headless' {
*/
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.
*/
scrollOnEraseInDisplay?: boolean;
/**
* The scrolling speed multiplier used for adjusting normal scrolling speed.
*/
+7
View File
@@ -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.
*/
scrollOnEraseInDisplay?: boolean;
/**
* Whether to scroll to the bottom whenever there is some user input. The
* default is true.