From c50cd4cc3ef603a90d9303cd5816c6a3e97b38b4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 7 Jan 2020 09:15:04 -0800 Subject: [PATCH 1/2] Flag lines as wrapped after CUP in windows mode Fixes #2666 --- src/Terminal.test.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ src/Terminal.ts | 12 +++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 90522bf4..9ea3a184 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -1359,6 +1359,48 @@ describe('Terminal', () => { }).to.not.throw(); }); }); + + describe('Windows Mode', () => { + it('should mark lines as wrapped when the line ends in a non-null character after a LF', () => { + const data = [ + 'aaaaaaaaaa\n\r', // cannot wrap as it's the first + 'aaaaaaaaa\n\r', // wrapped (windows mode only) + 'aaaaaaaaa' // not wrapped + ]; + + const normalTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: false}); + normalTerminal.writeSync(data.join('')); + assert.equal(normalTerminal.buffer.lines.get(0).isWrapped, false); + assert.equal(normalTerminal.buffer.lines.get(1).isWrapped, false); + assert.equal(normalTerminal.buffer.lines.get(2).isWrapped, false); + + const windowsModeTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: true}); + windowsModeTerminal.writeSync(data.join('')); + assert.equal(windowsModeTerminal.buffer.lines.get(0).isWrapped, false); + assert.equal(windowsModeTerminal.buffer.lines.get(1).isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character'); + assert.equal(windowsModeTerminal.buffer.lines.get(2).isWrapped, false); + }); + + it('should mark lines as wrapped when the line ends in a non-null character after a CUP', () => { + const data = [ + 'aaaaaaaaaa\x1b[2;1H', // cannot wrap as it's the first + 'aaaaaaaaa\x1b[3;1H', // wrapped (windows mode only) + 'aaaaaaaaa' // not wrapped + ]; + + const normalTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: false}); + normalTerminal.writeSync(data.join('')); + assert.equal(normalTerminal.buffer.lines.get(0).isWrapped, false); + assert.equal(normalTerminal.buffer.lines.get(1).isWrapped, false); + assert.equal(normalTerminal.buffer.lines.get(2).isWrapped, false); + + const windowsModeTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: true}); + windowsModeTerminal.writeSync(data.join('')); + assert.equal(windowsModeTerminal.buffer.lines.get(0).isWrapped, false); + assert.equal(windowsModeTerminal.buffer.lines.get(1).isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character'); + assert.equal(windowsModeTerminal.buffer.lines.get(2).isWrapped, false); + }); + }); }); class TestLinkifier extends Linkifier { diff --git a/src/Terminal.ts b/src/Terminal.ts index 74c2d00e..85cfbe67 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -264,7 +264,17 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _enableWindowsMode(): void { if (!this._windowsMode) { - this._windowsMode = this.onLineFeed(handleWindowsModeLineFeed.bind(null, this._bufferService)); + const disposables: IDisposable[] = []; + disposables.push(this.onLineFeed(handleWindowsModeLineFeed.bind(null, this._bufferService))); + disposables.push(this.addCsiHandler({ final: 'H' }, () => { + handleWindowsModeLineFeed(this._bufferService); + return false; + })); + this._windowsMode = { + dispose: () => { + disposables.forEach(d => d.dispose()); + } + }; } } From 685d14c4d52dc2fe750437499a63169c68dc3f38 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 7 Jan 2020 09:16:16 -0800 Subject: [PATCH 2/2] Improve windows mode method name --- src/Terminal.ts | 6 +++--- src/common/WindowsMode.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 85cfbe67..7d0def9b 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -43,7 +43,7 @@ import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMo import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { handleWindowsModeLineFeed } from 'common/WindowsMode'; +import { updateWindowsModeWrappedState } from 'common/WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; import { IOptionsService, IBufferService, ICoreMouseService, ICoreService, ILogService, IDirtyRowService, IInstantiationService, ICharsetService } from 'common/services/Services'; @@ -265,9 +265,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _enableWindowsMode(): void { if (!this._windowsMode) { const disposables: IDisposable[] = []; - disposables.push(this.onLineFeed(handleWindowsModeLineFeed.bind(null, this._bufferService))); + disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService))); disposables.push(this.addCsiHandler({ final: 'H' }, () => { - handleWindowsModeLineFeed(this._bufferService); + updateWindowsModeWrappedState(this._bufferService); return false; })); this._windowsMode = { diff --git a/src/common/WindowsMode.ts b/src/common/WindowsMode.ts index ff0e7591..7cff094b 100644 --- a/src/common/WindowsMode.ts +++ b/src/common/WindowsMode.ts @@ -6,7 +6,7 @@ import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; import { IBufferService } from 'common/services/Services'; -export function handleWindowsModeLineFeed(bufferService: IBufferService): void { +export function updateWindowsModeWrappedState(bufferService: IBufferService): void { // Winpty does not support wraparound mode which means that lines will never // be marked as wrapped. This causes issues for things like copying a line // retaining the wrapped new line characters or if consumers are listening