提交 20f9bf12 编写于 作者: D Daniel Imms

Implement start of terminal renderers

Part of #46192
上级 86e6f5fe
......@@ -349,6 +349,25 @@ declare module 'vscode' {
onData: Event<string>;
}
// A TerminalRender does not own a process, it's similar to an output window
// but it understands ANSI
export interface TerminalRenderer {
// Extensions can set the name (what appears in the dropdown)
name: string;
// Setting to undefined will reset to use the maximum available
// dimensions: TerminalDimensions;
// Write to xterm.js
write(data: string): void; // out
// key press, or sendText was triggered by an extension on the terminal
onData: Event<string>; // in
// Fires when the panel area is resized, this DOES NOT fire when `dimensions` is set
// onDidChangeDimensions: Event<TerminalDimensions>;
}
export namespace window {
/**
* The currently opened terminals or an empty array.
......@@ -362,6 +381,8 @@ declare module 'vscode' {
* [createTerminal](#window.createTerminal) API or commands.
*/
export const onDidOpenTerminal: Event<Terminal>;
export function createTerminalRenderer(name: string): TerminalRenderer;
}
//#endregion
......
......@@ -60,6 +60,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
return TPromise.as(this.terminalService.createTerminal(shellLaunchConfig).id);
}
public $createTerminalRenderer(name: string): TPromise<number> {
return TPromise.as(this.terminalService.createTerminalRenderer(name).id);
}
public $show(terminalId: number, preserveFocus: boolean): void {
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
......@@ -81,6 +85,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}
public $write(terminalId: number, text: string): void {
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
terminalInstance.write(text);
}
}
public $sendText(terminalId: number, text: string, addNewLine: boolean): void {
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
......
......@@ -425,6 +425,9 @@ export function createApiFactory(
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
createTerminalRenderer(name: string): vscode.TerminalRenderer {
return extHostTerminalService.createTerminalRenderer(name);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
},
......
......@@ -316,9 +316,11 @@ export interface MainThreadProgressShape extends IDisposable {
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): TPromise<number>;
$createTerminalRenderer(name: string): TPromise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$write(terminalId: number, text: string): void;
$show(terminalId: number, preserveFocus: boolean): void;
$registerOnDataListener(terminalId: number): void;
......
......@@ -17,9 +17,7 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration
import { ILogService } from 'vs/platform/log/common/log';
export class ExtHostTerminal implements vscode.Terminal {
private _name: string;
private _id: number;
private _proxy: MainThreadTerminalServiceShape;
private _disposed: boolean;
private _queuedRequests: ApiRequest[];
private _pidPromise: Promise<number>;
......@@ -33,12 +31,10 @@ export class ExtHostTerminal implements vscode.Terminal {
}
constructor(
proxy: MainThreadTerminalServiceShape,
name: string = '',
private _proxy: MainThreadTerminalServiceShape,
private _name: string,
id?: number
) {
this._proxy = proxy;
this._name = name;
if (id) {
this._id = id;
}
......@@ -122,6 +118,50 @@ export class ExtHostTerminal implements vscode.Terminal {
}
}
export class ExtHostTerminalRenderer implements vscode.TerminalRenderer {
private _id: number;
// private _disposed: boolean;
private _queuedRequests: ApiRequest[];
public get name(): string { return this._name; }
private readonly _onData: Emitter<string> = new Emitter<string>();
public get onData(): Event<string> { return this._onData && this._onData.event; }
constructor(
private _proxy: MainThreadTerminalServiceShape,
private _name: string
) {
this._proxy.$createTerminalRenderer(this._name).then((id) => {
this._id = id;
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
});
this._queuedRequests = [];
});
}
public write(data: string): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$write, []);
}
private _queueApiRequest(callback: (...args: any[]) => void, args: any[]) {
let request: ApiRequest = new ApiRequest(callback, args);
if (!this._id) {
this._queuedRequests.push(request);
return;
}
request.run(this._proxy, this._id);
}
private _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
}
}
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _proxy: MainThreadTerminalServiceShape;
private _terminals: ExtHostTerminal[] = [];
......@@ -143,21 +183,27 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, name);
const terminal = new ExtHostTerminal(this._proxy, name);
terminal.create(shellPath, shellArgs);
this._terminals.push(terminal);
return terminal;
}
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, options.name);
const terminal = new ExtHostTerminal(this._proxy, options.name);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/);
this._terminals.push(terminal);
return terminal;
}
public createTerminalRenderer(name: string): vscode.TerminalRenderer {
const terminalRenderer = new ExtHostTerminalRenderer(this._proxy, name);
console.log('Creating new terminal renderer');
return terminalRenderer;
}
public $acceptTerminalProcessData(id: number, data: string): void {
let index = this._getTerminalIndexById(id);
const index = this._getTerminalIndexById(id);
if (index === null) {
return;
}
......@@ -166,28 +212,28 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public $acceptTerminalClosed(id: number): void {
let index = this._getTerminalIndexById(id);
const index = this._getTerminalIndexById(id);
if (index === null) {
return;
}
let terminal = this._terminals.splice(index, 1)[0];
const terminal = this._terminals.splice(index, 1)[0];
this._onDidCloseTerminal.fire(terminal);
}
public $acceptTerminalOpened(id: number, name: string): void {
let index = this._getTerminalIndexById(id);
const index = this._getTerminalIndexById(id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._onDidOpenTerminal.fire(this.terminals[index]);
return;
}
let terminal = new ExtHostTerminal(this._proxy, name, id);
const terminal = new ExtHostTerminal(this._proxy, name, id);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
}
public $acceptTerminalProcessId(id: number, processId: number): void {
let terminal = this._getTerminalById(id);
const terminal = this._getTerminalById(id);
if (terminal) {
terminal._setProcessId(processId);
}
......
......@@ -156,6 +156,12 @@ export interface IShellLaunchConfig {
* of the terminal. Use \x1b over \033 or \e for the escape control character.
*/
initialText?: string;
/**
* When true the terminal will be created with no process. This is primarily used to give
* extensions full control over the terminal.
*/
isRendererOnly?: boolean;
}
export interface ITerminalService {
......@@ -181,6 +187,12 @@ export interface ITerminalService {
* default shell selection dialog may display.
*/
createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance;
/**
* Creates a terminal renderer.
* @param name The name of the terminal.
*/
createTerminalRenderer(name: string): ITerminalInstance;
/**
* Creates a raw terminal instance, this should not be used outside of the terminal part.
*/
......@@ -396,6 +408,12 @@ export interface ITerminalInstance {
*/
sendText(text: string, addNewLine: boolean): void;
/**
* Write text directly to the terminal, skipping the process if it exists.
* @param text The text to write.
*/
write(text: string): void;
/** Scroll the terminal buffer down 1 line. */
scrollDownLine(): void;
/** Scroll the terminal buffer down 1 page. */
......
......@@ -71,6 +71,7 @@ export abstract class TerminalService implements ITerminalService {
protected abstract _showTerminalCloseConfirmation(): TPromise<boolean>;
public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance;
public abstract createTerminalRenderer(name: string): ITerminalInstance;
public abstract createInstance(terminalFocusContextKey: IContextKey<boolean>, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance;
public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance;
public abstract selectDefaultWindowsShell(): TPromise<string>;
......
......@@ -101,11 +101,10 @@ export class TerminalInstance implements ITerminalInstance {
public get onRequestExtHostProcess(): Event<ITerminalInstance> { return this._onRequestExtHostProcess.event; }
public constructor(
private _terminalFocusContextKey: IContextKey<boolean>,
private _configHelper: TerminalConfigHelper,
private readonly _terminalFocusContextKey: IContextKey<boolean>,
private readonly _configHelper: TerminalConfigHelper,
private _container: HTMLElement,
private _shellLaunchConfig: IShellLaunchConfig,
doCreateProcess: boolean,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@INotificationService private readonly _notificationService: INotificationService,
......@@ -114,7 +113,7 @@ export class TerminalInstance implements ITerminalInstance {
@IClipboardService private readonly _clipboardService: IClipboardService,
@IThemeService private readonly _themeService: IThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILogService private _logService: ILogService,
@ILogService private readonly _logService: ILogService,
@IStorageService private readonly _storageService: IStorageService,
) {
this._disposables = [];
......@@ -132,7 +131,7 @@ export class TerminalInstance implements ITerminalInstance {
this._logService.trace(`terminalInstance#ctor (id: ${this.id})`, this._shellLaunchConfig);
this._initDimensions();
if (doCreateProcess) {
if (!this.shellLaunchConfig.isRendererOnly) {
this._createProcess();
}
......@@ -582,6 +581,15 @@ export class TerminalInstance implements ITerminalInstance {
document.execCommand('paste');
}
public write(text: string): void {
this._xtermReadyPromise.then(() => {
if (!this._xterm) {
return;
}
this._xterm.write(text);
});
}
public sendText(text: string, addNewLine: boolean): void {
this._processManager.ptyProcessReady.then(() => {
// Normalize line endings to 'enter' press.
......
......@@ -90,8 +90,12 @@ export class TerminalService extends AbstractTerminalService implements ITermina
return instance;
}
public createTerminalRenderer(name: string): ITerminalInstance {
return this.createTerminal({ name, isRendererOnly: true });
}
public createInstance(terminalFocusContextKey: IContextKey<boolean>, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance {
const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig, true);
const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig);
this._onInstanceCreated.fire(instance);
return instance;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册