提交 01fa1f6c 编写于 作者: D Daniel Imms

Move onLineData to an xterm addon and test

Part of #133757
上级 4d694341
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { OperatingSystem } from 'vs/base/common/platform';
import type { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm';
/**
* Provides extensions to the xterm object in a modular, testable way.
*/
export class LineDataEventAddon extends DisposableStore implements ITerminalAddon {
private _xterm?: XTermTerminal;
private _isOsSet = false;
private readonly _onLineData = this.add(new Emitter<string>());
readonly onLineData = this._onLineData.event;
activate(xterm: XTermTerminal) {
this._xterm = xterm;
// Fire onLineData when a line feed occurs, taking into account wrapped lines
xterm.onLineFeed(() => {
const buffer = xterm.buffer;
const newLine = buffer.active.getLine(buffer.active.baseY + buffer.active.cursorY);
if (newLine && !newLine.isWrapped) {
this._sendLineData(buffer.active, buffer.active.baseY + buffer.active.cursorY - 1);
}
});
// Fire onLineData when disposing object to flush last line
this.add(toDisposable(() => {
const buffer = xterm.buffer;
this._sendLineData(buffer.active, buffer.active.baseY + buffer.active.cursorY);
}));
}
setOperatingSystem(os: OperatingSystem) {
if (this._isOsSet || !this._xterm) {
return;
}
this._isOsSet = true;
// Force line data to be sent when the cursor is moved, the main purpose for
// this is because ConPTY will often not do a line feed but instead move the
// cursor, in which case we still want to send the current line's data to tasks.
if (os === OperatingSystem.Windows) {
const xterm = this._xterm;
xterm.parser.registerCsiHandler({ final: 'H' }, () => {
const buffer = xterm.buffer;
this._sendLineData(buffer.active, buffer.active.baseY + buffer.active.cursorY);
return false;
});
}
}
private _sendLineData(buffer: IBuffer, lineIndex: number): void {
let line = buffer.getLine(lineIndex);
if (!line) {
return;
}
let lineData = line.translateToString(true);
while (lineIndex > 0 && line.isWrapped) {
line = buffer.getLine(--lineIndex);
if (!line) {
break;
}
lineData = line.translateToString(false) + lineData;
}
this._onLineData.fire(lineData);
}
}
......@@ -73,6 +73,7 @@ import { ISeparator, template } from 'vs/base/common/labels';
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { LineDataEventAddon } from 'vs/workbench/contrib/terminal/browser/addons/lineDataEventAddon';
// How long in milliseconds should an average frame take to render for a notification to appear
// which suggests the fallback DOM-based renderer
......@@ -645,6 +646,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
});
this._xterm = xterm;
this._xtermCore = (xterm as any)._core as XTermCore;
const lineDataEventAddon = new LineDataEventAddon();
this._xterm.loadAddon(lineDataEventAddon);
this._updateUnicodeVersion();
this.updateAccessibilitySupport();
this._terminalInstanceService.getXtermSearchConstructor().then(addonCtor => {
......@@ -655,10 +658,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
// onLineData events containing initialText
if (this._shellLaunchConfig.initialText) {
this._xterm.writeln(this._shellLaunchConfig.initialText, () => {
xterm.onLineFeed(() => this._onLineFeed());
lineDataEventAddon.onLineData(e => this._onLineData.fire(e));
});
} else {
this._xterm.onLineFeed(() => this._onLineFeed());
lineDataEventAddon.onLineData(e => this._onLineData.fire(e));
}
// Delay the creation of the bell listener to avoid showing the bell when the terminal
// starts up or reconnects
......@@ -697,15 +700,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
return;
}
if (this._processManager.os) {
lineDataEventAddon.setOperatingSystem(this._processManager.os);
}
if (this._processManager.os === OperatingSystem.Windows) {
xterm.setOption('windowsMode', processTraits.requiresWindowsMode || false);
// Force line data to be sent when the cursor is moved, the main purpose for
// this is because ConPTY will often not do a line feed but instead move the
// cursor, in which case we still want to send the current line's data to tasks.
xterm.parser.registerCsiHandler({ final: 'H' }, () => {
this._onCursorMove();
return false;
});
}
this._linkManager = this._instantiationService.createInstance(TerminalLinkManager, xterm, this._processManager!);
this._areLinksReady = true;
......@@ -1075,11 +1074,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this._horizontalScrollbar = undefined;
}
}
if (this._xterm) {
const buffer = this._xterm.buffer;
this._sendLineData(buffer.active, buffer.active.baseY + buffer.active.cursorY);
this._xterm.dispose();
}
this._xterm?.dispose();
if (this._pressAnyKeyToCloseListener) {
this._pressAnyKeyToCloseListener.dispose();
......@@ -1525,41 +1520,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
this.reuseTerminal(this._shellLaunchConfig, true);
}
private _onLineFeed(): void {
const buffer = this._xterm!.buffer;
const newLine = buffer.active.getLine(buffer.active.baseY + buffer.active.cursorY);
if (newLine && !newLine.isWrapped) {
this._sendLineData(buffer.active, buffer.active.baseY + buffer.active.cursorY - 1);
}
}
private _onCursorMove(): void {
const buffer = this._xterm!.buffer;
this._sendLineData(buffer.active, buffer.active.baseY + buffer.active.cursorY);
}
private _onTitleChange(title: string): void {
if (this.isTitleSetByProcess) {
this.refreshTabLabels(title, TitleEventSource.Sequence);
}
}
private _sendLineData(buffer: IBuffer, lineIndex: number): void {
let line = buffer.getLine(lineIndex);
if (!line) {
return;
}
let lineData = line.translateToString(true);
while (lineIndex > 0 && line.isWrapped) {
line = buffer.getLine(--lineIndex);
if (!line) {
break;
}
lineData = line.translateToString(false) + lineData;
}
this._onLineData.fire(lineData);
}
private _onKey(key: string, ev: KeyboardEvent): void {
const event = new StandardKeyboardEvent(ev);
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Terminal } from 'xterm';
import { LineDataEventAddon } from 'vs/workbench/contrib/terminal/browser/addons/lineDataEventAddon';
import { deepStrictEqual } from 'assert';
async function writeP(terminal: Terminal, data: string): Promise<void> {
return new Promise<void>(r => terminal.write(data, r));
}
suite.only('XtermExtensions', () => {
let xterm: Terminal;
let lineDataEventAddon: LineDataEventAddon;
suite('onLineData', () => {
let events: string[];
setup(() => {
xterm = new Terminal({
cols: 4
});
lineDataEventAddon = new LineDataEventAddon();
xterm.loadAddon(lineDataEventAddon);
events = [];
lineDataEventAddon.onLineData(e => events.push(e));
});
test('should fire when a non-wrapped line ends with a \\n', async () => {
await writeP(xterm, 'foo');
deepStrictEqual(events, []);
await writeP(xterm, '\n\r');
deepStrictEqual(events, ['foo']);
await writeP(xterm, 'bar');
deepStrictEqual(events, ['foo']);
await writeP(xterm, '\n');
deepStrictEqual(events, ['foo', 'bar']);
});
test('should not fire soft wrapped lines', async () => {
await writeP(xterm, 'foo.');
deepStrictEqual(events, []);
await writeP(xterm, 'bar.');
deepStrictEqual(events, []);
await writeP(xterm, 'baz.');
deepStrictEqual(events, []);
});
test('should fire when a wrapped line ends with a \\n', async () => {
await writeP(xterm, 'foo.bar.baz.');
deepStrictEqual(events, []);
await writeP(xterm, '\n\r');
deepStrictEqual(events, ['foo.bar.baz.']);
});
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册