diff --git a/che-theia-terminal/src/browser/server-definition/base-terminal-protocol.ts b/che-theia-terminal/src/browser/server-definition/remote-terminal-protocol.ts similarity index 56% rename from che-theia-terminal/src/browser/server-definition/base-terminal-protocol.ts rename to che-theia-terminal/src/browser/server-definition/remote-terminal-protocol.ts index a4e0a0a..45e9d54 100644 --- a/che-theia-terminal/src/browser/server-definition/base-terminal-protocol.ts +++ b/che-theia-terminal/src/browser/server-definition/remote-terminal-protocol.ts @@ -9,9 +9,8 @@ **********************************************************************/ import { injectable } from 'inversify'; -import { TerminalWatcher } from '@theia/terminal/lib/common/terminal-watcher'; -import { IBaseTerminalClient, IBaseTerminalExitEvent, IBaseTerminalErrorEvent } from '@theia/terminal/lib/common/base-terminal-protocol'; import { JsonRpcProxy } from '@theia/core'; +import { Emitter, Event } from '@theia/core/lib/common/event'; export const TERMINAL_SERVER_TYPE = 'terminal'; export const CONNECT_TERMINAL_SEGMENT = 'connect'; @@ -50,18 +49,49 @@ export interface RemoteTerminalServer { export const RemoteTerminalServerProxy = Symbol('RemoteTerminalServerProxy'); export type RemoteTerminalServerProxy = JsonRpcProxy; -/** - * For now this class it's a stub. Real implementation depends on - * https://github.com/eclipse/che-machine-exec/issues/5 - */ +// Terminal exec exit event +export class ExecExitEvent { + id: number; +} + +// Terminal exec error event +export class ExecErrorEvent { + id: number; + stack: string; +} + +// Terminal exec client +export interface TerminalExecClient { + onExecExit(event: ExecExitEvent): void; + onExecError(event: ExecErrorEvent): void; +} + @injectable() -export class RemoteTerminaWatcher extends TerminalWatcher { - getTerminalClient(): IBaseTerminalClient { +export class RemoteTerminalWatcher { + + private onRemoteTerminalExitEmitter = new Emitter(); + private onRemoteTerminalErrorEmitter = new Emitter(); + + getTerminalExecClient(): TerminalExecClient { + + const exitEmitter = this.onRemoteTerminalExitEmitter; + const errorEmitter = this.onRemoteTerminalErrorEmitter; + return { - onTerminalExitChanged(event: IBaseTerminalExitEvent) { + onExecExit(event: ExecExitEvent) { + exitEmitter.fire(event); }, - onTerminalError(event: IBaseTerminalErrorEvent) { + onExecError(event: ExecErrorEvent) { + errorEmitter.fire(event); } }; } + + get onTerminalExecExit(): Event { + return this.onRemoteTerminalExitEmitter.event; + } + + get onTerminalExecError(): Event { + return this.onRemoteTerminalErrorEmitter.event; + } } diff --git a/che-theia-terminal/src/browser/server-definition/terminal-proxy-creator.ts b/che-theia-terminal/src/browser/server-definition/terminal-proxy-creator.ts index 006e2b5..ccac519 100644 --- a/che-theia-terminal/src/browser/server-definition/terminal-proxy-creator.ts +++ b/che-theia-terminal/src/browser/server-definition/terminal-proxy-creator.ts @@ -10,7 +10,7 @@ import { injectable, inject } from 'inversify'; import { RemoteWebSocketConnectionProvider, } from './remote-connection'; -import { CONNECT_TERMINAL_SEGMENT, RemoteTerminalServerProxy, RemoteTerminalServer } from './base-terminal-protocol'; +import { CONNECT_TERMINAL_SEGMENT, RemoteTerminalServerProxy, RemoteTerminalServer, RemoteTerminalWatcher } from './remote-terminal-protocol'; import URI from '@theia/core/lib/common/uri'; export type TerminalApiEndPointProvider = () => Promise; @@ -20,13 +20,19 @@ export type TerminalProxyCreatorProvider = () => Promise; @injectable() export class TerminalProxyCreator { + private remoteTermServer: RemoteTerminalServerProxy; + constructor(@inject(RemoteWebSocketConnectionProvider) protected readonly connProvider: RemoteWebSocketConnectionProvider, @inject('term-api-end-point') protected readonly apiEndPoint: string, + @inject(RemoteTerminalWatcher) protected readonly terminalWatcher: RemoteTerminalWatcher, ) { } create(): RemoteTerminalServerProxy { - const url = new URI(this.apiEndPoint).resolve(CONNECT_TERMINAL_SEGMENT); - return this.connProvider.createProxy(url.toString()); + if (!this.remoteTermServer) { + const url = new URI(this.apiEndPoint).resolve(CONNECT_TERMINAL_SEGMENT); + this.remoteTermServer = this.connProvider.createProxy(url.toString(), this.terminalWatcher.getTerminalExecClient()); + } + return this.remoteTermServer; } } diff --git a/che-theia-terminal/src/browser/terminal-frontend-module.ts b/che-theia-terminal/src/browser/terminal-frontend-module.ts index cd8c061..79a67ba 100755 --- a/che-theia-terminal/src/browser/terminal-frontend-module.ts +++ b/che-theia-terminal/src/browser/terminal-frontend-module.ts @@ -24,7 +24,7 @@ import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-servi import { TerminalWidget, TerminalWidgetOptions } from '@theia/terminal/lib/browser/base/terminal-widget'; import { RemoteTerminalWidget } from './terminal-widget/remote-terminal-widget'; import { RemoteTerminaActiveKeybingContext } from './contribution/keybinding-context'; -import { RemoteTerminalServerProxy, RemoteTerminalServer } from './server-definition/base-terminal-protocol'; +import { RemoteTerminalServerProxy, RemoteTerminalServer, RemoteTerminalWatcher } from './server-definition/remote-terminal-protocol'; export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => { bind(KeybindingContext).to(RemoteTerminaActiveKeybingContext).inSingletonScope(); @@ -42,6 +42,8 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un bind(RemoteTerminalServer).toService(RemoteTerminalServerProxy); + bind(RemoteTerminalWatcher).toSelf().inSingletonScope(); + let terminalNum = 0; bind(WidgetFactory).toDynamicValue(ctx => ({ id: REMOTE_TERMINAL_WIDGET_FACTORY_ID, @@ -102,7 +104,7 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un } return reject('Unabel to find che-machine-exec server.'); }).catch(err => { - console.log('Failed get terminal proxy. Cause: ', err); + console.log('Failed to get terminal proxy. Cause: ', err); return reject(err); }); }); diff --git a/che-theia-terminal/src/browser/terminal-widget/remote-terminal-widget.ts b/che-theia-terminal/src/browser/terminal-widget/remote-terminal-widget.ts index 1b5fe94..d759cfb 100644 --- a/che-theia-terminal/src/browser/terminal-widget/remote-terminal-widget.ts +++ b/che-theia-terminal/src/browser/terminal-widget/remote-terminal-widget.ts @@ -8,11 +8,11 @@ // Copied from 'terminal-widget.ts' with some modifications, CQ: https://dev.eclipse.org/ipzilla/show_bug.cgi?id=16269 /* tslint:enable */ -import { injectable, inject } from 'inversify'; +import { injectable, inject, postConstruct } from 'inversify'; import { TerminalWidgetImpl } from '@theia/terminal/lib/browser/terminal-widget-impl'; import { IBaseTerminalServer } from '@theia/terminal/lib/common/base-terminal-protocol'; import { TerminalProxyCreator, TerminalProxyCreatorProvider } from '../server-definition/terminal-proxy-creator'; -import { ATTACH_TERMINAL_SEGMENT, RemoteTerminalServerProxy } from '../server-definition/base-terminal-protocol'; +import { ATTACH_TERMINAL_SEGMENT, RemoteTerminalServerProxy, RemoteTerminalWatcher } from '../server-definition/remote-terminal-protocol'; import { RemoteWebSocketConnectionProvider } from '../server-definition/remote-connection'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { Disposable } from 'vscode-jsonrpc'; @@ -43,9 +43,30 @@ export class RemoteTerminalWidget extends TerminalWidgetImpl { @inject(RemoteWebSocketConnectionProvider) protected readonly remoteWebSocketConnectionProvider: RemoteWebSocketConnectionProvider; + @inject(RemoteTerminalWatcher) + protected readonly remoteTerminalWatcher: RemoteTerminalWatcher; + @inject(RemoteTerminalWidgetOptions) options: RemoteTerminalWidgetOptions; + @postConstruct() + protected init(): void { + super.init(); + + this.toDispose.push(this.remoteTerminalWatcher.onTerminalExecExit(exitEvent => { + if (this.terminalId === exitEvent.id) { + this.dispose(); + } + })); + + this.toDispose.push(this.remoteTerminalWatcher.onTerminalExecError(errEvent => { + if (this.terminalId === errEvent.id) { + this.dispose(); + this.logger.error(`Terminal error: ${errEvent.stack}`); + } + })); + } + async start(id?: number): Promise { try { if (!this.termServer) { diff --git a/che-theia-terminal/src/node/workspace-service-impl.ts b/che-theia-terminal/src/node/workspace-service-impl.ts index 9b15717..c2adf06 100755 --- a/che-theia-terminal/src/node/workspace-service-impl.ts +++ b/che-theia-terminal/src/node/workspace-service-impl.ts @@ -12,7 +12,7 @@ import { injectable, inject } from 'inversify'; import WorkspaceClient, { IRemoteAPI, IWorkspace, IServer, IMachine, IRequestError, IRestAPIConfig } from '@eclipse-che/workspace-client'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { CHEWorkspaceService } from '../common/workspace-service'; -import { TERMINAL_SERVER_TYPE } from '../browser/server-definition/base-terminal-protocol'; +import { TERMINAL_SERVER_TYPE } from '../browser/server-definition/remote-terminal-protocol'; const TYPE: string = 'type'; const EDITOR_SERVER_TYPE: string = 'ide';