deploy: current vibn theia state
Made-with: Cursor
This commit is contained in:
173
packages/terminal/src/node/base-terminal-server.ts
Normal file
173
packages/terminal/src/node/base-terminal-server.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||
import { ILogger, DisposableCollection } from '@theia/core/lib/common';
|
||||
import {
|
||||
IBaseTerminalServer,
|
||||
IBaseTerminalServerOptions,
|
||||
IBaseTerminalClient,
|
||||
TerminalProcessInfo,
|
||||
TerminalExitReason
|
||||
} from '../common/base-terminal-protocol';
|
||||
import { TerminalProcess, ProcessManager, TaskTerminalProcess } from '@theia/process/lib/node';
|
||||
import { ShellProcess } from './shell-process';
|
||||
|
||||
@injectable()
|
||||
export abstract class BaseTerminalServer implements IBaseTerminalServer {
|
||||
protected client: IBaseTerminalClient | undefined = undefined;
|
||||
protected terminalToDispose = new Map<number, DisposableCollection>();
|
||||
|
||||
constructor(
|
||||
@inject(ProcessManager) protected readonly processManager: ProcessManager,
|
||||
@inject(ILogger) @named('terminal') protected readonly logger: ILogger
|
||||
) {
|
||||
processManager.onDelete(id => {
|
||||
const toDispose = this.terminalToDispose.get(id);
|
||||
if (toDispose !== undefined) {
|
||||
toDispose.dispose();
|
||||
this.terminalToDispose.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
abstract create(options: IBaseTerminalServerOptions): Promise<number>;
|
||||
|
||||
async attach(id: number): Promise<number> {
|
||||
const term = this.processManager.get(id);
|
||||
|
||||
if (term && term instanceof TerminalProcess) {
|
||||
return term.id;
|
||||
} else {
|
||||
this.logger.warn(`Couldn't attach - can't find terminal with id: ${id} `);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
async onAttachAttempted(id: number): Promise<void> {
|
||||
const terminal = this.processManager.get(id);
|
||||
if (terminal instanceof TaskTerminalProcess) {
|
||||
terminal.attachmentAttempted = true;
|
||||
if (terminal.exited) {
|
||||
// Didn't execute `unregisterProcess` on terminal `exit` event to enable attaching task output to terminal,
|
||||
// Fixes https://github.com/eclipse-theia/theia/issues/2961
|
||||
terminal.unregisterProcess();
|
||||
} else {
|
||||
this.postAttachAttempted(terminal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getProcessId(id: number): Promise<number> {
|
||||
const terminal = this.processManager.get(id);
|
||||
if (!(terminal instanceof TerminalProcess)) {
|
||||
throw new Error(`terminal "${id}" does not exist`);
|
||||
}
|
||||
return terminal.pid;
|
||||
}
|
||||
|
||||
async getProcessInfo(id: number): Promise<TerminalProcessInfo> {
|
||||
const terminal = this.processManager.get(id);
|
||||
if (!(terminal instanceof TerminalProcess)) {
|
||||
throw new Error(`terminal "${id}" does not exist`);
|
||||
}
|
||||
return {
|
||||
executable: terminal.executable,
|
||||
arguments: terminal.arguments,
|
||||
};
|
||||
}
|
||||
|
||||
async getCwdURI(id: number): Promise<string> {
|
||||
const terminal = this.processManager.get(id);
|
||||
if (!(terminal instanceof TerminalProcess)) {
|
||||
throw new Error(`terminal "${id}" does not exist`);
|
||||
}
|
||||
return terminal.getCwdURI();
|
||||
}
|
||||
|
||||
async close(id: number): Promise<void> {
|
||||
const term = this.processManager.get(id);
|
||||
|
||||
if (term instanceof TerminalProcess) {
|
||||
term.kill();
|
||||
}
|
||||
}
|
||||
|
||||
async getDefaultShell(): Promise<string> {
|
||||
return ShellProcess.getShellExecutablePath();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
async resize(id: number, cols: number, rows: number): Promise<void> {
|
||||
const term = this.processManager.get(id);
|
||||
if (term && term instanceof TerminalProcess) {
|
||||
term.resize(cols, rows);
|
||||
} else {
|
||||
console.warn("Couldn't resize terminal " + id + ", because it doesn't exist.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the client to receive notifications on. */
|
||||
setClient(client: IBaseTerminalClient | undefined): void {
|
||||
this.client = client;
|
||||
if (!this.client) {
|
||||
return;
|
||||
}
|
||||
this.client.updateTerminalEnvVariables();
|
||||
}
|
||||
|
||||
protected notifyClientOnExit(term: TerminalProcess): DisposableCollection {
|
||||
const toDispose = new DisposableCollection();
|
||||
|
||||
toDispose.push(term.onError(error => {
|
||||
this.logger.error(`Terminal pid: ${term.pid} error: ${error}, closing it.`);
|
||||
|
||||
if (this.client !== undefined) {
|
||||
this.client.onTerminalError({
|
||||
terminalId: term.id,
|
||||
error: new Error(`Failed to execute terminal process (${error.code})`),
|
||||
attached: term instanceof TaskTerminalProcess && term.attachmentAttempted
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
toDispose.push(term.onExit(event => {
|
||||
if (this.client !== undefined) {
|
||||
this.client.onTerminalExitChanged({
|
||||
terminalId: term.id,
|
||||
code: event.code,
|
||||
reason: TerminalExitReason.Process,
|
||||
signal: event.signal,
|
||||
attached: term instanceof TaskTerminalProcess && term.attachmentAttempted
|
||||
});
|
||||
}
|
||||
}));
|
||||
return toDispose;
|
||||
}
|
||||
|
||||
protected postCreate(term: TerminalProcess): void {
|
||||
const toDispose = this.notifyClientOnExit(term);
|
||||
this.terminalToDispose.set(term.id, toDispose);
|
||||
}
|
||||
|
||||
protected postAttachAttempted(term: TaskTerminalProcess): void {
|
||||
const toDispose = this.notifyClientOnExit(term);
|
||||
this.terminalToDispose.set(term.id, toDispose);
|
||||
}
|
||||
}
|
||||
46
packages/terminal/src/node/buffering-stream.spec.ts
Normal file
46
packages/terminal/src/node/buffering-stream.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2022 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { wait } from '@theia/core/lib/common/promise-util';
|
||||
import { expect } from 'chai';
|
||||
import { BufferBufferingStream } from './buffering-stream';
|
||||
|
||||
describe('BufferringStream', () => {
|
||||
|
||||
it('should emit whatever data was buffered before the timeout', async () => {
|
||||
const buffer = new BufferBufferingStream({ emitInterval: 1000 });
|
||||
const chunkPromise = waitForChunk(buffer);
|
||||
buffer.push(Buffer.from([0]));
|
||||
await wait(100);
|
||||
buffer.push(Buffer.from([1]));
|
||||
await wait(100);
|
||||
buffer.push(Buffer.from([2, 3, 4]));
|
||||
const chunk = await chunkPromise;
|
||||
expect(chunk).deep.eq(Buffer.from([0, 1, 2, 3, 4]));
|
||||
});
|
||||
|
||||
it('should not emit chunks bigger than maxChunkSize', async () => {
|
||||
const buffer = new BufferBufferingStream({ maxChunkSize: 2 });
|
||||
buffer.push(Buffer.from([0, 1, 2, 3, 4, 5]));
|
||||
expect(await waitForChunk(buffer)).deep.eq(Buffer.from([0, 1]));
|
||||
expect(await waitForChunk(buffer)).deep.eq(Buffer.from([2, 3]));
|
||||
expect(await waitForChunk(buffer)).deep.eq(Buffer.from([4, 5]));
|
||||
});
|
||||
|
||||
function waitForChunk(buffer: BufferBufferingStream): Promise<Buffer> {
|
||||
return new Promise(resolve => buffer.onData(resolve));
|
||||
}
|
||||
});
|
||||
95
packages/terminal/src/node/buffering-stream.ts
Normal file
95
packages/terminal/src/node/buffering-stream.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2022 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { Emitter, Event } from '@theia/core/lib/common/event';
|
||||
|
||||
export interface BufferingStreamOptions {
|
||||
/**
|
||||
* Max size in bytes of the chunks being emitted.
|
||||
*/
|
||||
maxChunkSize?: number
|
||||
/**
|
||||
* Amount of time in milliseconds to wait between the moment we start
|
||||
* buffering data and when we emit the buffered chunk.
|
||||
*/
|
||||
emitInterval?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* This component will buffer whatever is pushed to it and emit chunks back
|
||||
* every {@link BufferingStreamOptions.emitInterval}. It will also ensure that
|
||||
* the emitted chunks never exceed {@link BufferingStreamOptions.maxChunkSize}.
|
||||
*/
|
||||
export class BufferingStream<T> {
|
||||
protected buffer?: T;
|
||||
protected timeout?: NodeJS.Timeout;
|
||||
protected maxChunkSize: number;
|
||||
protected emitInterval: number;
|
||||
|
||||
protected onDataEmitter = new Emitter<T>();
|
||||
protected readonly concat: (left: T, right: T) => T;
|
||||
protected readonly slice: (what: T, start?: number, end?: number) => T;
|
||||
protected readonly length: (what: T) => number;
|
||||
|
||||
constructor(options: BufferingStreamOptions = {}, concat: (left: T, right: T) => T, slice: (what: T, start?: number, end?: number) => T, length: (what: T) => number) {
|
||||
this.emitInterval = options.emitInterval ?? 16; // ms
|
||||
this.maxChunkSize = options.maxChunkSize ?? (256 * 1024); // bytes
|
||||
this.concat = concat;
|
||||
this.slice = slice;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
get onData(): Event<T> {
|
||||
return this.onDataEmitter.event;
|
||||
}
|
||||
|
||||
push(chunk: T): void {
|
||||
if (this.buffer) {
|
||||
this.buffer = this.concat(this.buffer, chunk);
|
||||
} else {
|
||||
this.buffer = chunk;
|
||||
this.timeout = setTimeout(() => this.emitBufferedChunk(), this.emitInterval);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
clearTimeout(this.timeout);
|
||||
this.buffer = undefined;
|
||||
this.onDataEmitter.dispose();
|
||||
}
|
||||
|
||||
protected emitBufferedChunk(): void {
|
||||
this.onDataEmitter.fire(this.slice(this.buffer!, 0, this.maxChunkSize));
|
||||
if (this.length(this.buffer!) <= this.maxChunkSize) {
|
||||
this.buffer = undefined;
|
||||
} else {
|
||||
this.buffer = this.slice(this.buffer!, this.maxChunkSize);
|
||||
this.timeout = setTimeout(() => this.emitBufferedChunk(), this.emitInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class StringBufferingStream extends BufferingStream<string> {
|
||||
constructor(options: BufferingStreamOptions = {}) {
|
||||
super(options, (left, right) => left.concat(right), (what, start, end) => what.slice(start, end), what => what.length);
|
||||
}
|
||||
}
|
||||
|
||||
export class BufferBufferingStream extends BufferingStream<Buffer> {
|
||||
constructor(options: BufferingStreamOptions = {}) {
|
||||
super(options, (left, right) => Buffer.concat([left, right]), (what, start, end) => what.slice(start, end), what => what.length);
|
||||
}
|
||||
}
|
||||
17
packages/terminal/src/node/index.ts
Normal file
17
packages/terminal/src/node/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 TypeFox and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
export * from './terminal-backend-module';
|
||||
102
packages/terminal/src/node/shell-process.ts
Normal file
102
packages/terminal/src/node/shell-process.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
||||
import * as os from 'os';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { TerminalProcess, TerminalProcessOptions, ProcessManager, MultiRingBuffer } from '@theia/process/lib/node';
|
||||
import { isWindows, isOSX } from '@theia/core/lib/common';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { FileUri } from '@theia/core/lib/common/file-uri';
|
||||
import { EnvironmentUtils } from '@theia/core/lib/node/environment-utils';
|
||||
import { parseArgs } from '@theia/process/lib/node/utils';
|
||||
|
||||
export const ShellProcessFactory = Symbol('ShellProcessFactory');
|
||||
export type ShellProcessFactory = (options: ShellProcessOptions) => ShellProcess;
|
||||
|
||||
export const ShellProcessOptions = Symbol('ShellProcessOptions');
|
||||
export interface ShellProcessOptions {
|
||||
shell?: string,
|
||||
args?: string[] | string,
|
||||
rootURI?: string,
|
||||
cols?: number,
|
||||
rows?: number,
|
||||
env?: { [key: string]: string | null },
|
||||
strictEnv?: boolean,
|
||||
isPseudo?: boolean,
|
||||
}
|
||||
|
||||
export function getRootPath(rootURI?: string): string {
|
||||
if (rootURI) {
|
||||
const uri = new URI(rootURI);
|
||||
return FileUri.fsPath(uri);
|
||||
} else {
|
||||
return os.homedir();
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ShellProcess extends TerminalProcess {
|
||||
|
||||
protected static defaultCols = 80;
|
||||
protected static defaultRows = 24;
|
||||
|
||||
constructor( // eslint-disable-next-line @typescript-eslint/indent
|
||||
@inject(ShellProcessOptions) options: ShellProcessOptions,
|
||||
@inject(ProcessManager) processManager: ProcessManager,
|
||||
@inject(MultiRingBuffer) ringBuffer: MultiRingBuffer,
|
||||
@inject(ILogger) @named('terminal') logger: ILogger,
|
||||
@inject(EnvironmentUtils) environmentUtils: EnvironmentUtils,
|
||||
) {
|
||||
const env = { 'COLORTERM': 'truecolor' };
|
||||
super(<TerminalProcessOptions>{
|
||||
command: options.shell || ShellProcess.getShellExecutablePath(),
|
||||
args: options.args || ShellProcess.getShellExecutableArgs(),
|
||||
options: {
|
||||
name: 'xterm-256color',
|
||||
cols: options.cols || ShellProcess.defaultCols,
|
||||
rows: options.rows || ShellProcess.defaultRows,
|
||||
cwd: getRootPath(options.rootURI),
|
||||
env: options.strictEnv !== true ? Object.assign(env, environmentUtils.mergeProcessEnv(options.env)) : Object.assign(env, options.env),
|
||||
},
|
||||
isPseudo: options.isPseudo,
|
||||
}, processManager, ringBuffer, logger);
|
||||
}
|
||||
|
||||
public static getShellExecutablePath(): string {
|
||||
const shell = process.env.THEIA_SHELL;
|
||||
if (shell) {
|
||||
return shell;
|
||||
}
|
||||
if (isWindows) {
|
||||
return 'cmd.exe';
|
||||
} else {
|
||||
return process.env.SHELL!;
|
||||
}
|
||||
}
|
||||
|
||||
public static getShellExecutableArgs(): string[] {
|
||||
const args = process.env.THEIA_SHELL_ARGS;
|
||||
if (args) {
|
||||
return parseArgs(args);
|
||||
}
|
||||
if (isOSX) {
|
||||
return ['-l'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
40
packages/terminal/src/node/shell-terminal-server.spec.ts
Normal file
40
packages/terminal/src/node/shell-terminal-server.spec.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
import * as chai from 'chai';
|
||||
import { createTerminalTestContainer } from './test/terminal-test-container';
|
||||
import { IShellTerminalServer } from '../common/shell-terminal-protocol';
|
||||
|
||||
/**
|
||||
* Globals
|
||||
*/
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('ShellServer', function (): void {
|
||||
|
||||
this.timeout(5000);
|
||||
let shellTerminalServer: IShellTerminalServer;
|
||||
|
||||
beforeEach(() => {
|
||||
shellTerminalServer = createTerminalTestContainer().get(IShellTerminalServer);
|
||||
});
|
||||
|
||||
it('test shell terminal create', async function (): Promise<void> {
|
||||
const createResult = shellTerminalServer.create({});
|
||||
|
||||
expect(await createResult).to.be.greaterThan(-1);
|
||||
});
|
||||
});
|
||||
245
packages/terminal/src/node/shell-terminal-server.ts
Normal file
245
packages/terminal/src/node/shell-terminal-server.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
import { exec } from 'child_process';
|
||||
|
||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import { EnvironmentUtils } from '@theia/core/lib/node/environment-utils';
|
||||
import { BaseTerminalServer } from './base-terminal-server';
|
||||
import { ShellProcessFactory, getRootPath } from './shell-process';
|
||||
import { ProcessManager, TerminalProcess } from '@theia/process/lib/node';
|
||||
import { isWindows } from '@theia/core/lib/common/os';
|
||||
import * as cp from 'child_process';
|
||||
import {
|
||||
EnvironmentVariableCollectionWithPersistence, EnvironmentVariableMutatorType, NO_ROOT_URI, SerializableEnvironmentVariableCollection,
|
||||
IShellTerminalServer, IShellTerminalServerOptions
|
||||
}
|
||||
from '../common/shell-terminal-protocol';
|
||||
import { URI } from '@theia/core';
|
||||
import { MultiKeyMap } from '@theia/core/lib/common/collections';
|
||||
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdown-string';
|
||||
|
||||
interface SerializedExtensionEnvironmentVariableCollection {
|
||||
extensionIdentifier: string,
|
||||
rootUri: string,
|
||||
collection: SerializableEnvironmentVariableCollection,
|
||||
}
|
||||
|
||||
interface WindowsProcess {
|
||||
ProcessId: number;
|
||||
ParentProcessId: number;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ShellTerminalServer extends BaseTerminalServer implements IShellTerminalServer {
|
||||
@inject(EnvironmentUtils) protected environmentUtils: EnvironmentUtils;
|
||||
|
||||
readonly collections: MultiKeyMap<string, EnvironmentVariableCollectionWithPersistence> = new MultiKeyMap(2);
|
||||
|
||||
constructor(
|
||||
@inject(ShellProcessFactory) protected readonly shellFactory: ShellProcessFactory,
|
||||
@inject(ProcessManager) processManager: ProcessManager,
|
||||
@inject(ILogger) @named('terminal') logger: ILogger) {
|
||||
super(processManager, logger);
|
||||
}
|
||||
|
||||
async create(options: IShellTerminalServerOptions): Promise<number> {
|
||||
try {
|
||||
if (options.strictEnv !== true) {
|
||||
options.env = this.environmentUtils.mergeProcessEnv(options.env);
|
||||
this.applyToProcessEnvironment(URI.fromFilePath(getRootPath(options.rootURI)), options.env);
|
||||
}
|
||||
const term = this.shellFactory(options);
|
||||
this.postCreate(term);
|
||||
return term.id;
|
||||
} catch (error) {
|
||||
this.logger.error('Error while creating terminal', error);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// copied and modified from https://github.com/microsoft/vscode/blob/4636be2b71c87bfb0bfe3c94278b447a5efcc1f1/src/vs/workbench/contrib/debug/node/terminals.ts#L32-L75
|
||||
private spawnAsPromised(command: string, args: string[]): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let stdout = '';
|
||||
const child = cp.spawn(command, args, {
|
||||
shell: true
|
||||
});
|
||||
if (child.pid) {
|
||||
child.stdout.on('data', (data: Buffer) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
}
|
||||
child.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
child.on('close', code => {
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public hasChildProcesses(processId: number | undefined): Promise<boolean> {
|
||||
if (processId) {
|
||||
// if shell has at least one child process, assume that shell is busy
|
||||
if (isWindows) {
|
||||
return new Promise(resolve => {
|
||||
exec(
|
||||
'powershell -Command "Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId | ConvertTo-Json"',
|
||||
(error, stdout) => {
|
||||
if (error) {
|
||||
this.logger.error(`Failed to get Windows process list: ${error}`);
|
||||
return resolve(true); // assume busy on error
|
||||
}
|
||||
|
||||
try {
|
||||
const processes: WindowsProcess[] = JSON.parse(stdout);
|
||||
const hasChild = processes.some(proc => proc.ParentProcessId === processId);
|
||||
return resolve(hasChild);
|
||||
} catch (parseError) {
|
||||
this.logger.error(`Failed to parse process list JSON: ${parseError}`);
|
||||
return resolve(true); // assume busy on parse error
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return this.spawnAsPromised('/usr/bin/pgrep', ['-lP', String(processId)]).then(stdout => {
|
||||
const r = stdout.trim();
|
||||
if (r.length === 0 || r.indexOf(' tmux') >= 0) { // ignore 'tmux';
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}, error => true);
|
||||
}
|
||||
}
|
||||
// fall back to safe side
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
applyToProcessEnvironment(cwdUri: URI, env: { [key: string]: string | null }): void {
|
||||
let lowerToActualVariableNames: {
|
||||
[lowerKey: string]: string | undefined
|
||||
} | undefined;
|
||||
if (isWindows) {
|
||||
lowerToActualVariableNames = {};
|
||||
Object.keys(env).forEach(e => lowerToActualVariableNames![e.toLowerCase()] = e);
|
||||
}
|
||||
this.collections.forEach((mutators, [extensionIdentifier, rootUri]) => {
|
||||
if (rootUri === NO_ROOT_URI || this.matchesRootUri(cwdUri, rootUri)) {
|
||||
mutators.variableMutators.forEach((mutator, variable) => {
|
||||
const actualVariable = isWindows ? lowerToActualVariableNames![variable.toLowerCase()] || variable : variable;
|
||||
switch (mutator.type) {
|
||||
case EnvironmentVariableMutatorType.Append:
|
||||
env[actualVariable] = (env[actualVariable] || '') + mutator.value;
|
||||
break;
|
||||
case EnvironmentVariableMutatorType.Prepend:
|
||||
env[actualVariable] = mutator.value + (env[actualVariable] || '');
|
||||
break;
|
||||
case EnvironmentVariableMutatorType.Replace:
|
||||
env[actualVariable] = mutator.value;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
matchesRootUri(cwdUri: URI, rootUri: string): boolean {
|
||||
return new URI(rootUri).isEqualOrParent(cwdUri);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// some code copied and modified from https://github.com/microsoft/vscode/blob/1.49.0/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts
|
||||
|
||||
setCollection(extensionIdentifier: string, baseUri: string, persistent: boolean,
|
||||
collection: SerializableEnvironmentVariableCollection): void {
|
||||
this.doSetCollection(extensionIdentifier, baseUri, persistent, collection);
|
||||
this.updateCollections();
|
||||
}
|
||||
|
||||
private doSetCollection(extensionIdentifier: string, baseUri: string, persistent: boolean,
|
||||
collection: SerializableEnvironmentVariableCollection): void {
|
||||
this.collections.set([extensionIdentifier, baseUri], {
|
||||
persistent: persistent,
|
||||
description: collection.description,
|
||||
variableMutators: new Map(collection.mutators)
|
||||
});
|
||||
}
|
||||
|
||||
restorePersisted(jsonValue: string): void {
|
||||
const collectionsJson: SerializedExtensionEnvironmentVariableCollection[] = JSON.parse(jsonValue);
|
||||
collectionsJson.forEach(c => this.doSetCollection(c.extensionIdentifier, c.rootUri ?? NO_ROOT_URI, true, c.collection));
|
||||
|
||||
}
|
||||
|
||||
deleteCollection(extensionIdentifier: string): void {
|
||||
this.collections.delete([extensionIdentifier]);
|
||||
this.updateCollections();
|
||||
}
|
||||
|
||||
private updateCollections(): void {
|
||||
this.persistCollections();
|
||||
}
|
||||
|
||||
protected persistCollections(): void {
|
||||
const collectionsJson: SerializedExtensionEnvironmentVariableCollection[] = [];
|
||||
this.collections.forEach((collection, [extensionIdentifier, rootUri]) => {
|
||||
if (collection.persistent) {
|
||||
collectionsJson.push({
|
||||
extensionIdentifier,
|
||||
rootUri,
|
||||
collection: {
|
||||
description: collection.description,
|
||||
mutators: [...this.collections.get([extensionIdentifier, rootUri])!.variableMutators.entries()]
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
if (this.client) {
|
||||
const stringifiedJson = JSON.stringify(collectionsJson);
|
||||
this.client.storeTerminalEnvVariables(stringifiedJson);
|
||||
}
|
||||
}
|
||||
|
||||
async getEnvVarCollectionDescriptionsByExtension(id: number): Promise<Map<string, (string | MarkdownString | undefined)[]>> {
|
||||
const terminal = this.processManager.get(id);
|
||||
if (!(terminal instanceof TerminalProcess)) {
|
||||
throw new Error(`terminal "${id}" does not exist`);
|
||||
}
|
||||
const result = new Map<string, (string | MarkdownString | undefined)[]>();
|
||||
this.collections.forEach((value, key) => {
|
||||
const prev = result.get(key[0]) || [];
|
||||
prev.push(value.description);
|
||||
result.set(key[0], prev);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
async getEnvVarCollections(): Promise<[string, string, boolean, SerializableEnvironmentVariableCollection][]> {
|
||||
const result: [string, string, boolean, SerializableEnvironmentVariableCollection][] = [];
|
||||
|
||||
this.collections.forEach((value, [extensionIdentifier, rootUri]) => {
|
||||
result.push([extensionIdentifier, rootUri, value.persistent, { description: value.description, mutators: [...value.variableMutators.entries()] }]);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2018 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { createTerminalTestContainer } from './test/terminal-test-container';
|
||||
import { BackendApplication } from '@theia/core/lib/node/backend-application';
|
||||
import { IShellTerminalServer } from '../common/shell-terminal-protocol';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import { terminalsPath } from '../common/terminal-protocol';
|
||||
import { TestWebSocketChannelSetup } from '@theia/core/lib/node/messaging/test/test-web-socket-channel';
|
||||
|
||||
describe('Terminal Backend Contribution', function (): void {
|
||||
|
||||
this.timeout(10000);
|
||||
let server: http.Server | https.Server;
|
||||
let shellTerminalServer: IShellTerminalServer;
|
||||
|
||||
beforeEach(async () => {
|
||||
const container = createTerminalTestContainer();
|
||||
const application = container.get(BackendApplication);
|
||||
shellTerminalServer = container.get(IShellTerminalServer);
|
||||
server = await application.start(3000, 'localhost');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
const s = server;
|
||||
server = undefined!;
|
||||
shellTerminalServer = undefined!;
|
||||
s.close();
|
||||
});
|
||||
|
||||
it('is data received from the terminal ws server', async () => {
|
||||
const terminalId = await shellTerminalServer.create({});
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const path = `${terminalsPath}/${terminalId}`;
|
||||
const { connectionProvider } = new TestWebSocketChannelSetup({ server, path });
|
||||
|
||||
connectionProvider.listen(path, (path2, channel) => {
|
||||
channel.onError(reject);
|
||||
channel.onClose(event => reject(new Error(`channel is closed with '${event.code}' code and '${event.reason}' reason}`)));
|
||||
if (path2 === path) {
|
||||
resolve();
|
||||
channel.close();
|
||||
}
|
||||
}, false);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
60
packages/terminal/src/node/terminal-backend-contribution.ts
Normal file
60
packages/terminal/src/node/terminal-backend-contribution.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 TypeFox and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
||||
import { ILogger } from '@theia/core/lib/common';
|
||||
import { TerminalProcess, ProcessManager } from '@theia/process/lib/node';
|
||||
import { terminalsPath } from '../common/terminal-protocol';
|
||||
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
|
||||
import { StringBufferingStream } from './buffering-stream';
|
||||
|
||||
@injectable()
|
||||
export class TerminalBackendContribution implements MessagingService.Contribution {
|
||||
protected readonly decoder = new TextDecoder('utf-8');
|
||||
|
||||
@inject(ProcessManager)
|
||||
protected readonly processManager: ProcessManager;
|
||||
|
||||
@inject(ILogger) @named('terminal')
|
||||
protected readonly logger: ILogger;
|
||||
|
||||
configure(service: MessagingService): void {
|
||||
service.registerChannelHandler(`${terminalsPath}/:id`, (params: { id: string }, channel) => {
|
||||
const id = parseInt(params.id, 10);
|
||||
const termProcess = this.processManager.get(id);
|
||||
if (termProcess instanceof TerminalProcess) {
|
||||
const output = termProcess.createOutputStream();
|
||||
// Create a RPC connection to the terminal process
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
channel.onMessage(e => {
|
||||
termProcess.write(e().readString());
|
||||
});
|
||||
|
||||
const buffer = new StringBufferingStream();
|
||||
buffer.onData(chunk => {
|
||||
channel.getWriteBuffer().writeString(chunk).commit();
|
||||
});
|
||||
output.on('data', chunk => {
|
||||
buffer.push(chunk);
|
||||
});
|
||||
channel.onClose(() => {
|
||||
buffer.dispose();
|
||||
output.dispose();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
81
packages/terminal/src/node/terminal-backend-module.ts
Normal file
81
packages/terminal/src/node/terminal-backend-module.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 TypeFox and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { ContainerModule, Container, interfaces } from '@theia/core/shared/inversify';
|
||||
import { TerminalBackendContribution } from './terminal-backend-contribution';
|
||||
import { ConnectionHandler, RpcConnectionHandler } from '@theia/core/lib/common/messaging';
|
||||
import { ShellProcess, ShellProcessFactory, ShellProcessOptions } from './shell-process';
|
||||
import { ITerminalServer, terminalPath } from '../common/terminal-protocol';
|
||||
import { IBaseTerminalClient, DispatchingBaseTerminalClient, IBaseTerminalServer } from '../common/base-terminal-protocol';
|
||||
import { TerminalServer } from './terminal-server';
|
||||
import { IShellTerminalServer, shellTerminalPath } from '../common/shell-terminal-protocol';
|
||||
import { ShellTerminalServer } from '../node/shell-terminal-server';
|
||||
import { TerminalWatcher } from '../common/terminal-watcher';
|
||||
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
|
||||
import { bindTerminalPreferences } from '../common/terminal-preferences';
|
||||
|
||||
export function bindTerminalServer(bind: interfaces.Bind, { path, identifier, constructor }: {
|
||||
path: string,
|
||||
identifier: interfaces.ServiceIdentifier<IBaseTerminalServer>,
|
||||
constructor: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
new(...args: any[]): IBaseTerminalServer;
|
||||
}
|
||||
}): void {
|
||||
const dispatchingClient = new DispatchingBaseTerminalClient();
|
||||
bind<IBaseTerminalServer>(identifier).to(constructor).inSingletonScope().onActivation((context, terminalServer) => {
|
||||
terminalServer.setClient(dispatchingClient);
|
||||
dispatchingClient.push(context.container.get(TerminalWatcher).getTerminalClient());
|
||||
terminalServer.setClient = () => {
|
||||
throw new Error('use TerminalWatcher');
|
||||
};
|
||||
return terminalServer;
|
||||
});
|
||||
bind(ConnectionHandler).toDynamicValue(ctx =>
|
||||
new RpcConnectionHandler<IBaseTerminalClient>(path, client => {
|
||||
const disposable = dispatchingClient.push(client);
|
||||
client.onDidCloseConnection(() => disposable.dispose());
|
||||
return ctx.container.get(identifier);
|
||||
})
|
||||
).inSingletonScope();
|
||||
}
|
||||
|
||||
export default new ContainerModule(bind => {
|
||||
bind(MessagingService.Contribution).to(TerminalBackendContribution).inSingletonScope();
|
||||
|
||||
bind(ShellProcess).toSelf().inTransientScope();
|
||||
bind(ShellProcessFactory).toFactory(ctx =>
|
||||
(options: ShellProcessOptions) => {
|
||||
const child = new Container({ defaultScope: 'Singleton' });
|
||||
child.parent = ctx.container;
|
||||
child.bind(ShellProcessOptions).toConstantValue(options);
|
||||
return child.get(ShellProcess);
|
||||
}
|
||||
);
|
||||
|
||||
bind(TerminalWatcher).toSelf().inSingletonScope();
|
||||
bindTerminalServer(bind, {
|
||||
path: terminalPath,
|
||||
identifier: ITerminalServer,
|
||||
constructor: TerminalServer
|
||||
});
|
||||
bindTerminalServer(bind, {
|
||||
path: shellTerminalPath,
|
||||
identifier: IShellTerminalServer,
|
||||
constructor: ShellTerminalServer
|
||||
});
|
||||
bindTerminalPreferences(bind);
|
||||
});
|
||||
42
packages/terminal/src/node/terminal-server.spec.ts
Normal file
42
packages/terminal/src/node/terminal-server.spec.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import * as chai from 'chai';
|
||||
import { createTerminalTestContainer } from './test/terminal-test-container';
|
||||
import { ITerminalServer } from '../common/terminal-protocol';
|
||||
|
||||
/**
|
||||
* Globals
|
||||
*/
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('TerminalServer', function (): void {
|
||||
|
||||
this.timeout(5000);
|
||||
let terminalServer: ITerminalServer;
|
||||
|
||||
beforeEach(() => {
|
||||
const container = createTerminalTestContainer();
|
||||
terminalServer = container.get(ITerminalServer);
|
||||
});
|
||||
|
||||
it('test terminal create', async function (): Promise<void> {
|
||||
const args = ['--version'];
|
||||
const createResult = await terminalServer.create({ command: process.execPath, 'args': args });
|
||||
expect(createResult).to.be.greaterThan(-1);
|
||||
});
|
||||
});
|
||||
52
packages/terminal/src/node/terminal-server.ts
Normal file
52
packages/terminal/src/node/terminal-server.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
|
||||
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
||||
import { ILogger } from '@theia/core/lib/common/logger';
|
||||
import {
|
||||
ITerminalServer,
|
||||
ITerminalServerOptions
|
||||
} from '../common/terminal-protocol';
|
||||
import { BaseTerminalServer } from './base-terminal-server';
|
||||
import { TerminalProcessFactory, ProcessManager } from '@theia/process/lib/node';
|
||||
|
||||
@injectable()
|
||||
export class TerminalServer extends BaseTerminalServer implements ITerminalServer {
|
||||
|
||||
@inject(TerminalProcessFactory) protected readonly terminalFactory: TerminalProcessFactory;
|
||||
|
||||
constructor(
|
||||
@inject(ProcessManager) processManager: ProcessManager,
|
||||
@inject(ILogger) @named('terminal') logger: ILogger,
|
||||
) {
|
||||
super(processManager, logger);
|
||||
}
|
||||
|
||||
create(options: ITerminalServerOptions): Promise<number> {
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
const term = this.terminalFactory(options);
|
||||
term.onStart(_ => {
|
||||
this.postCreate(term);
|
||||
resolve(term.id);
|
||||
});
|
||||
term.onError(error => {
|
||||
this.logger.error('Error while creating terminal', error);
|
||||
resolve(-1);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
39
packages/terminal/src/node/test/terminal-test-container.ts
Normal file
39
packages/terminal/src/node/test/terminal-test-container.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// *****************************************************************************
|
||||
// Copyright (C) 2017 Ericsson and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// http://www.eclipse.org/legal/epl-2.0.
|
||||
//
|
||||
// This Source Code may also be made available under the following Secondary
|
||||
// Licenses when the conditions for such availability set forth in the Eclipse
|
||||
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
||||
// with the GNU Classpath Exception which is available at
|
||||
// https://www.gnu.org/software/classpath/license.html.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
||||
// *****************************************************************************
|
||||
import { Container } from '@theia/core/shared/inversify';
|
||||
import { bindLogger } from '@theia/core/lib/node/logger-backend-module';
|
||||
import { backendApplicationModule } from '@theia/core/lib/node/backend-application-module';
|
||||
import processBackendModule from '@theia/process/lib/node/process-backend-module';
|
||||
import { messagingBackendModule } from '@theia/core/lib/node/messaging/messaging-backend-module';
|
||||
import terminalBackendModule from '../terminal-backend-module';
|
||||
import { ApplicationPackage } from '@theia/core/shared/@theia/application-package';
|
||||
import { ProcessUtils } from '@theia/core/lib/node/process-utils';
|
||||
|
||||
export function createTerminalTestContainer(): Container {
|
||||
const container = new Container();
|
||||
|
||||
container.load(backendApplicationModule);
|
||||
container.rebind(ApplicationPackage).toConstantValue({} as ApplicationPackage);
|
||||
container.rebind(ProcessUtils).toConstantValue(new class extends ProcessUtils {
|
||||
override terminateProcessTree(): void { }
|
||||
});
|
||||
|
||||
bindLogger(container.bind.bind(container));
|
||||
container.load(messagingBackendModule);
|
||||
container.load(processBackendModule);
|
||||
container.load(terminalBackendModule);
|
||||
return container;
|
||||
}
|
||||
Reference in New Issue
Block a user