Skip to content

Commit

Permalink
Initial contribution of @theia/terminal-external
Browse files Browse the repository at this point in the history
The following commit adds a new extension `@theia/terminal-external`.
The extension adds support for spawning external terminals from Electron applications.

+ Supports opening native terminals by calling `Open New External Terminal` from the command palette.
+ The commit contributes the following preferences:
    - `terminal.external.windowsExec`
    - `terminal.external.osxExec`
    - `terminal.external.linuxExec`
to allow users to customize the desired terminal application to run on their current OS.

Signed-off-by: Duc Nguyen <duc.a.nguyen@ericsson.com>
  • Loading branch information
DucNgn committed Mar 17, 2021
1 parent e8df8a6 commit e6af6d0
Show file tree
Hide file tree
Showing 15 changed files with 700 additions and 0 deletions.
3 changes: 3 additions & 0 deletions configs/root-compilation.tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@
{
"path": "../packages/terminal/compile.tsconfig.json"
},
{
"path": "../packages/terminal-external/compile.tsconfig.json"
},
{
"path": "../packages/typehierarchy/compile.tsconfig.json"
},
Expand Down
3 changes: 3 additions & 0 deletions examples/electron/compile.tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@
{
"path": "../../packages/terminal/compile.tsconfig.json"
},
{
"path": "../../packages/terminal-external/compile.tsconfig.json"
},
{
"path": "../../packages/typehierarchy/compile.tsconfig.json"
},
Expand Down
1 change: 1 addition & 0 deletions examples/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@theia/search-in-workspace": "1.11.0",
"@theia/task": "1.11.0",
"@theia/terminal": "1.11.0",
"@theia/terminal-external": "1.11.0",
"@theia/timeline": "1.11.0",
"@theia/typehierarchy": "1.11.0",
"@theia/userstorage": "1.11.0",
Expand Down
10 changes: 10 additions & 0 deletions packages/terminal-external/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: [
'../../configs/build.eslintrc.json'
],
parserOptions: {
tsconfigRootDir: __dirname,
project: 'compile.tsconfig.json'
}
};
32 changes: 32 additions & 0 deletions packages/terminal-external/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<div align='center'>

<br />

<img src='https://raw.githubusercontent.com/eclipse-theia/theia/master/logo/theia.svg?sanitize=true' alt='theia-ext-logo' width='100px' />

<h2>ECLIPSE THEIA - TERMINAL-EXTERNAL EXTENSION</h2>

<hr />

</div>

## Description

The `@theia/terminal-external` extension contributes the ability to spawn external terminals from `electron` applications.

**Note:** The extension does not support browser applications.

## Additional Information

- [Theia - GitHub](https://github.com/eclipse-theia/theia)
- [Theia - Website](https://theia-ide.org/)

## License

- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/)
- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp)

## Trademark

"Theia" is a trademark of the Eclipse Foundation
https://www.eclipse.org/theia
22 changes: 22 additions & 0 deletions packages/terminal-external/compile.tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../configs/base.tsconfig",
"compilerOptions": {
"composite": true,
"rootDir": "src",
"outDir": "lib"
},
"include": [
"src"
],
"references": [
{
"path": "../core/compile.tsconfig.json"
},
{
"path": "../editor/compile.tsconfig.json"
},
{
"path": "../workspace/compile.tsconfig.json"
}
]
}
48 changes: 48 additions & 0 deletions packages/terminal-external/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@theia/terminal-external",
"version": "1.11.0",
"description": "Theia - External Terminal Extension",
"dependencies": {
"@theia/core": "1.11.0",
"@theia/editor": "1.11.0",
"@theia/workspace": "1.11.0"
},
"publishConfig": {
"access": "public"
},
"theiaExtensions": [
{
"backendElectron": "lib/electron-node/terminal-external-backend-module",
"frontendElectron": "lib/electron-browser/terminal-external-frontend-module"
}
],
"keywords": [
"theia-extension"
],
"license": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0",
"repository": {
"type": "git",
"url": "https://github.com/eclipse-theia/theia.git"
},
"bugs": {
"url": "https://github.com/eclipse-theia/theia/issues"
},
"homepage": "https://github.com/eclipse-theia/theia",
"files": [
"lib",
"src"
],
"scripts": {
"lint": "theiaext lint",
"build": "theiaext build",
"watch": "theiaext watch",
"clean": "theiaext clean",
"test": "theiaext test"
},
"devDependencies": {
"@theia/ext-scripts": "^1.9.0"
},
"nyc": {
"extends": "../../configs/nyc.json"
}
}
43 changes: 43 additions & 0 deletions packages/terminal-external/src/common/terminal-external.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/********************************************************************************
* Copyright (C) 2021 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 WITH Classpath-exception-2.0
********************************************************************************/

export const TerminalExternalService = Symbol('TerminalExternalService');
export const terminalExternalServicePath = '/services/terminal-external';

export interface TerminalExternalConfiguration {
'terminal.external.windowsExec': string
'terminal.external.osxExec': string
'terminal.external.linuxExec': string
}

export interface TerminalExternalService {

/**
* Open a native terminal in the designated working directory.
*
* @param configuration the configuration for opening external terminals.
* @param cwd the current working directory where the terminal should open from.
*/
openTerminal(configuration: TerminalExternalConfiguration, cwd: string): Promise<void>;

/**
* Determine the default exec of the external terminal.
*
* @returns the default terminal exec.
*/
getDefaultExec(): Promise<string>;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/********************************************************************************
* Copyright (C) 2021 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 WITH Classpath-exception-2.0
********************************************************************************/

import { inject, injectable } from 'inversify';
import {
Command,
CommandContribution,
CommandRegistry
} from '@theia/core/lib/common';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { QuickPickService } from '@theia/core/lib/common/quick-pick-service';
import {
KeybindingContribution,
KeybindingRegistry,
LabelProvider
} from '@theia/core/lib/browser';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { TerminalExternalService } from '../common/terminal-external';
import { TerminalExternalPreferenceService } from './terminal-external-preference';

export namespace TerminalExternalCommands {
export const OPEN_NATIVE_CONSOLE: Command = {
id: 'workbench.action.terminal.openNativeConsole',
label: 'Open New External Terminal'
};
}

@injectable()
export class TerminalExternalFrontendContribution implements CommandContribution, KeybindingContribution {

@inject(EditorManager)
private readonly editorManager: EditorManager;

@inject(EnvVariablesServer)
private readonly envVariablesServer: EnvVariablesServer;

@inject(LabelProvider)
private readonly labelProvider: LabelProvider;

@inject(QuickPickService)
private readonly quickPickService: QuickPickService;

@inject(TerminalExternalService)
private readonly terminalExternalService: TerminalExternalService;

@inject(TerminalExternalPreferenceService)
private readonly terminalExternalPreferences: TerminalExternalPreferenceService;

@inject(WorkspaceService)
private readonly workspaceService: WorkspaceService;

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(TerminalExternalCommands.OPEN_NATIVE_CONSOLE, {
execute: async () => this.openTerminalExternal()
});
}

registerKeybindings(keybindings: KeybindingRegistry): void {
keybindings.registerKeybinding({
command: TerminalExternalCommands.OPEN_NATIVE_CONSOLE.id,
keybinding: 'shift+ctrlcmd+c'
});
}

/**
* Open a native console on the host machine.
*
* If multi-root workspace opened, displays a quick pick to let users choose which workspace to spawn the terminal.
* If only one workspace opened, the terminal spawns at the root of the current workspace.
* If no workspaces opened and there's an active editor, the terminal spawns at the parent folder of that file.
* If no workspaces opened and no active editors, the terminal spawns at user home directory.
*/
protected async openTerminalExternal(): Promise<void> {
const configuration = this.terminalExternalPreferences.getTerminalExternalConfiguration();

if (this.workspaceService.isMultiRootWorkspaceOpened) {
const chosenWorkspaceRoot = await this.selectTerminalExternalCwd();
if (chosenWorkspaceRoot) {
await this.terminalExternalService.openTerminal(configuration, chosenWorkspaceRoot);
}
return;
}

if (this.workspaceService.opened) {
const workspaceRootUri = this.workspaceService.tryGetRoots()[0].resource;
await this.terminalExternalService.openTerminal(configuration, workspaceRootUri.toString());
return;
}

const activeEditorUri = this.editorManager.activeEditor?.editor.uri;
if (activeEditorUri) {
await this.terminalExternalService.openTerminal(configuration, activeEditorUri.parent.toString());
} else {
const userHomeDir = await this.envVariablesServer.getHomeDirUri();
await this.terminalExternalService.openTerminal(configuration, userHomeDir);
}
}

/**
* Display a quick pick for user to choose a target workspace in opened workspaces.
*/
protected async selectTerminalExternalCwd(): Promise<string | undefined> {
const roots = this.workspaceService.tryGetRoots();
return this.quickPickService.show(roots.map(
({ resource }) => ({
label: this.labelProvider.getName(resource),
description: this.labelProvider.getLongName(resource),
value: resource.toString()
})
), { placeholder: 'Select current working directory for new external terminal' });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/********************************************************************************
* Copyright (C) 2021 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 WITH Classpath-exception-2.0
********************************************************************************/

import { ContainerModule, interfaces } from 'inversify';
import { CommandContribution } from '@theia/core/lib/common';
import {
KeybindingContribution,
WebSocketConnectionProvider
} from '@theia/core/lib/browser';
import { bindTerminalExternalPreferences } from './terminal-external-preference';
import { TerminalExternalFrontendContribution } from './terminal-external-contribution';
import { TerminalExternalService, terminalExternalServicePath } from '../common/terminal-external';

export default new ContainerModule((bind: interfaces.Bind) => {
bind(TerminalExternalFrontendContribution).toSelf().inSingletonScope();
bindTerminalExternalPreferences(bind);
[CommandContribution, KeybindingContribution].forEach(serviceIdentifier =>
bind(serviceIdentifier).toService(TerminalExternalFrontendContribution)
);
bind(TerminalExternalService).toDynamicValue(ctx =>
WebSocketConnectionProvider.createProxy<TerminalExternalService>(ctx.container, terminalExternalServicePath)
).inSingletonScope();
});
Loading

0 comments on commit e6af6d0

Please sign in to comment.