From 7b5af04d1f0550031070516133c5098c21ce4982 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 27 Oct 2017 13:13:19 -0700 Subject: [PATCH] Send last line terminal data when process exits Part of #32042 Part of #29840 --- .../parts/terminal/common/terminal.ts | 7 +-- .../electron-browser/terminalInstance.ts | 46 +++++++++++-------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 607df896b7878..68add6effcc4b 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -364,9 +364,10 @@ export interface ITerminalInstance { * * @param listener The listener function which takes new line strings added to the terminal, * excluding ANSI escape sequences. The line event will fire when an LF character is added to - * the terminal (ie. the line is not wrapped), note that this means taht the line data will - * never fire for the last line, until the line is ended with a LF character. The lineData - * string will contain the fully wrapped line, not containing any LF/CR characters. + * the terminal (ie. the line is not wrapped). Note that this means that the line data will + * not fire for the last line, until either the line is ended with a LF character of the process + * is exited. The lineData string will contain the fully wrapped line, not containing any LF/CR + * characters. */ onLineData(listener: (lineData: string) => void): IDisposable; diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 1dfbf753096f3..652d4bf4d2041 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -107,6 +107,7 @@ export class TerminalInstance implements ITerminalInstance { private _preLaunchInputQueue: string; private _initialCwd: string; private _windowsShellHelper: WindowsShellHelper; + private _onLineDataListeners: ((lineData: string) => void)[]; private _widgetManager: TerminalWidgetManager; private _linkHandler: TerminalLinkHandler; @@ -140,6 +141,7 @@ export class TerminalInstance implements ITerminalInstance { this._instanceDisposables = []; this._processDisposables = []; this._skipTerminalCommands = []; + this._onLineDataListeners = []; this._isExiting = false; this._hadFocusOnExit = false; this._processState = ProcessState.UNINITIALIZED; @@ -272,6 +274,7 @@ export class TerminalInstance implements ITerminalInstance { if (this._shellLaunchConfig.initialText) { this._xterm.writeln(this._shellLaunchConfig.initialText); } + this._xterm.on('lineFeed', () => this._onLineFeed()); this._process.on('message', (message) => this._sendPtyDataToXterm(message)); this._xterm.on('data', (data) => { if (this._processId) { @@ -451,6 +454,8 @@ export class TerminalInstance implements ITerminalInstance { this._wrapperElement = null; } if (this._xterm) { + const buffer = (this._xterm.buffer); + this._sendLineData(buffer, buffer.ybase + buffer.y); this._xterm.destroy(); this._xterm = null; } @@ -828,31 +833,36 @@ export class TerminalInstance implements ITerminalInstance { } public onLineData(listener: (lineData: string) => void): lifecycle.IDisposable { - if (!this._xterm) { - throw new Error('xterm must be initialized'); - } - const lineFeedListener = () => { - const buffer = (this._xterm.buffer); - const newLine = buffer.lines.get(buffer.ybase + buffer.y); - if (!newLine.isWrapped) { - let i = buffer.ybase + buffer.y - 1; - let lineData = buffer.translateBufferLineToString(i, true); - while (i >= 0 && buffer.lines.get(i--).isWrapped) { - lineData = buffer.translateBufferLineToString(i, true) + lineData; - } - listener(lineData); - } - }; - this._xterm.on('lineFeed', lineFeedListener); + this._onLineDataListeners.push(listener); return { dispose: () => { - if (this._xterm) { - this._xterm.off('lineFeed', lineFeedListener); + const i = this._onLineDataListeners.indexOf(listener); + if (i >= 0) { + this._onLineDataListeners.splice(i, 1); } } }; } + private _onLineFeed(): void { + if (this._onLineDataListeners.length === 0) { + return; + } + const buffer = (this._xterm.buffer); + const newLine = buffer.lines.get(buffer.ybase + buffer.y); + if (!newLine.isWrapped) { + this._sendLineData(buffer, buffer.ybase + buffer.y - 1); + } + } + + private _sendLineData(buffer: any, lineIndex: number): void { + let lineData = buffer.translateBufferLineToString(lineIndex, true); + while (lineIndex >= 0 && buffer.lines.get(lineIndex--).isWrapped) { + lineData = buffer.translateBufferLineToString(lineIndex, true) + lineData; + } + this._onLineDataListeners.forEach(listener => listener(lineData)); + } + public onExit(listener: (exitCode: number) => void): lifecycle.IDisposable { if (this._process) { this._process.on('exit', listener);