Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for terminal profiles. #12066

Merged
merged 4 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/core/i18n/nls.de.json
Original file line number Diff line number Diff line change
Expand Up @@ -447,12 +447,24 @@
"confirmCloseNever": "Niemals bestätigen.",
"enableCopy": "Aktivieren von ctrl-c (cmd-c unter macOS) zum Kopieren von markiertem Text",
"enablePaste": "Aktivieren von ctrl-v (cmd-v unter macOS) zum Einfügen aus der Zwischenablage",
"profileNew": "Neues Terminal (mit Profil)...",
"profileDefault": "Standardprofil wählen...",
"selectProfile": "Wählen Sie ein Profil für das neue Terminal",
"shellArgsLinux": "Die Befehlszeilenargumente, die im Linux-Terminal zu verwenden sind.",
"shellArgsOsx": "Die Befehlszeilenargumente, die im macOS-Terminal zu verwenden sind.",
"shellArgsWindows": "Die Befehlszeilenargumente, die im Windows-Terminal zu verwenden sind.",
"shellLinux": "Der Pfad der Shell, die das Terminal unter Linux verwendet (Standard: '{0}'}).",
"shellOsx": "Der Pfad der Shell, die das Terminal unter macOS verwendet (Standard: '{0}'}).",
"shellWindows": "Der Pfad der Shell, die das Terminal unter Windows verwendet. (Standard: '{0}').",
"shell.deprecated": "Dies ist veraltet, neu können Sie Ihre Shell konfigurieren, indem Sie ein Profil unter 'terminal.integrated.profiles.{0}' anlegen und dessen Namen in 'terminal.integrated.defaultProfile.{0}' als Standard setzen.",
"defaultProfile": "Das Standardprofil unter {0}",
"profiles": "Die Profile welche zur Erzeugung eines Terminals verwendet werden können. Setzen Sie den Pfad von Hand mit optionalen Parametern.\n\nSezen Sie ein Profile auf `null` um es zu verbergen, z.B.: `{0}: null`.",
"profilePath": "Der Pfad der Shell, den dieses Profil benutzt.",
"profileSource": "Eine Profilquelle, die Shellpfade automatisch erkennt. Unübliche Installationsorte werden nicht unterstützt und müssen manuell erfasst werden",
"profileArgs": "Die Shellparameter, welche dieses Profil verwendet.",
"profileEnv": "Ein Objekt mit Umgebungsvariablen die zum Terminalprozess hinzugefügt werden. Setzen Sie Variablen auf `null` um sie aus der Basisumgebung zu löschen",
"profileIcon": "Eine codicon ID zur Verwendung mit diesem Terminal. \nterminal-tmux:\"$(terminal-tmux)\"",
"profileColor": "ID einer Terminal-Themenfarbe zur Verwendung mit diesem Terminal.",
"terminate": "Ende",
"terminateActive": "Möchten Sie die aktive Terminalsitzung beenden?",
"terminateActiveMultiple": "Sollen die {0} aktiven Terminalsitzungen beendet werden?"
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/common/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export class URI {
return new URI(Uri.revive(components));
}

public static fromFilePath(path: string): URI {
return new URI(Uri.file(path));
}

private readonly codeUri: Uri;
private _path: Path | undefined;

Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ export interface CommandRegistryExt {
}

export interface TerminalServiceExt {
$startProfile(providerId: string, cancellationToken: theia.CancellationToken): Promise<string>;
$terminalCreated(id: string, name: string): void;
$terminalNameChanged(id: string, name: string): void;
$terminalOpened(id: string, processId: number, terminalId: number, cols: number, rows: number): void;
Expand Down
18 changes: 18 additions & 0 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ export interface PluginPackageContribution {
jsonValidation?: PluginJsonValidationContribution[];
resourceLabelFormatters?: ResourceLabelFormatter[];
localizations?: PluginPackageLocalization[];
terminal?: PluginPackageTerminal;
}

export interface PluginPackageTerminalProfile {
title: string,
id: string,
icon?: string
}

export interface PluginPackageTerminal {
profiles: PluginPackageTerminalProfile[]
}

export interface PluginPackageLocalization {
Expand Down Expand Up @@ -555,6 +566,13 @@ export interface PluginContribution {
problemPatterns?: ProblemPatternContribution[];
resourceLabelFormatters?: ResourceLabelFormatter[];
localizations?: Localization[];
terminalProfiles?: TerminalProfile[];
}

export interface TerminalProfile {
title: string,
id: string,
icon?: string
}

export interface Localization {
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,10 @@ export class HostedPluginSupport {
return this.activateByEvent(`onFileSystem:${event.scheme}`);
}

activateByTerminalProfile(profileId: string): Promise<void> {
return this.activateByEvent(`onTerminalProfile:${profileId}`);
}

protected ensureFileSystemActivation(event: FileSystemProviderActivationEvent): void {
event.waitUntil(this.activateByFileSystem(event));
}
Expand Down
16 changes: 15 additions & 1 deletion packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ import {
Localization,
PluginPackageTranslation,
Translation,
PluginIdentifiers
PluginIdentifiers,
TerminalProfile
} from '../../../common/plugin-protocol';
import * as fs from 'fs';
import * as path from 'path';
Expand Down Expand Up @@ -358,9 +359,22 @@ export class TheiaPluginScanner implements PluginScanner {
console.error(`Could not read '${rawPlugin.name}' contribution 'localizations'.`, rawPlugin.contributes.colors, err);
}

try {
contributions.terminalProfiles = this.readTerminals(rawPlugin);
} catch (err) {
console.error(`Could not read '${rawPlugin.name}' contribution 'terminals'.`, rawPlugin.contributes.terminal, err);
}

return contributions;
}

protected readTerminals(pck: PluginPackage): TerminalProfile[] | undefined {
if (!pck?.contributes?.terminal?.profiles) {
return undefined;
}
return pck.contributes.terminal.profiles.filter(profile => profile.id && profile.title);
}

protected readLocalizations(pck: PluginPackage): Localization[] | undefined {
if (!pck.contributes || !pck.contributes.localizations) {
return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ import { PluginIconThemeService } from './plugin-icon-theme-service';
import { ContributionProvider } from '@theia/core/lib/common';
import * as monaco from '@theia/monaco-editor-core';
import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService';
import { ContributedTerminalProfileStore, TerminalProfileStore } from '@theia/terminal/lib/browser/terminal-profile-service';
import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
import { PluginTerminalRegistry } from './plugin-terminal-registry';

@injectable()
export class PluginContributionHandler {
Expand Down Expand Up @@ -106,6 +110,15 @@ export class PluginContributionHandler {
@inject(PluginIconThemeService)
protected readonly iconThemeService: PluginIconThemeService;

@inject(TerminalService)
protected readonly terminalService: TerminalService;

@inject(PluginTerminalRegistry)
protected readonly pluginTerminalRegistry: PluginTerminalRegistry;

@inject(ContributedTerminalProfileStore)
protected readonly contributedProfileStore: TerminalProfileStore;

@inject(ContributionProvider) @named(LabelProviderContribution)
protected readonly contributionProvider: ContributionProvider<LabelProviderContribution>;

Expand Down Expand Up @@ -356,6 +369,28 @@ export class PluginContributionHandler {
}
}

const self = this;
if (contributions.terminalProfiles) {
for (const profile of contributions.terminalProfiles) {
pushContribution(`terminalProfiles.${profile.id}`, () => {
this.contributedProfileStore.registerTerminalProfile(profile.title, {
async start(): Promise<TerminalWidget> {
const terminalId = await self.pluginTerminalRegistry.start(profile.id);
const result = self.terminalService.getById(terminalId);
if (!result) {
throw new Error(`Error starting terminal from profile ${profile.id}`);
}
return result;

}
});
return Disposable.create(() => {
this.contributedProfileStore.unregisterTerminalProfile(profile.id);
});
});
}
}

return toDispose;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import { bindTreeViewDecoratorUtilities, TreeViewDecoratorService } from './view
import { CodeEditorWidgetUtil } from './menus/vscode-theia-menu-mappings';
import { PluginMenuCommandAdapter } from './menus/plugin-menu-command-adapter';
import './theme-icon-override';
import { PluginTerminalRegistry } from './plugin-terminal-registry';

export default new ContainerModule((bind, unbind, isBound, rebind) => {

Expand Down Expand Up @@ -240,4 +241,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {

bind(PluginAuthenticationServiceImpl).toSelf().inSingletonScope();
rebind(AuthenticationService).toService(PluginAuthenticationServiceImpl);

bind(PluginTerminalRegistry).toSelf().inSingletonScope();
});
27 changes: 27 additions & 0 deletions packages/plugin-ext/src/main/browser/plugin-terminal-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// *****************************************************************************
// Copyright (C) 2022 STMicroelectronics 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 { injectable } from '@theia/core/shared/inversify';

@injectable()
export class PluginTerminalRegistry {

startCallback: (id: string) => Promise<string>;

start(profileId: string): Promise<string> {
return this.startCallback(profileId);
}
}
61 changes: 35 additions & 26 deletions packages/plugin-ext/src/main/browser/terminal-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import { interfaces } from '@theia/core/shared/inversify';
import { ApplicationShell, WidgetOpenerOptions } from '@theia/core/lib/browser';
import { CancellationToken } from '@theia/core/shared/vscode-languageserver-protocol';
import { TerminalEditorLocationOptions, TerminalOptions } from '@theia/plugin';
import { TerminalLocation, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
Expand All @@ -28,13 +27,18 @@ import { ShellTerminalServerProxy } from '@theia/terminal/lib/common/shell-termi
import { TerminalLink, TerminalLinkProvider } from '@theia/terminal/lib/browser/terminal-link-provider';
import { URI } from '@theia/core/lib/common/uri';
import { getIconClass } from '../../plugin/terminal-ext';
import { PluginTerminalRegistry } from './plugin-terminal-registry';
import { CancellationToken } from '@theia/core';
import { HostedPluginSupport } from '../../hosted/browser/hosted-plugin';

/**
* Plugin api service allows working with terminal emulator.
*/
export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLinkProvider, Disposable {

private readonly terminals: TerminalService;
private readonly pluginTerminalRegistry: PluginTerminalRegistry;
private readonly hostedPluginSupport: HostedPluginSupport;
private readonly shell: ApplicationShell;
private readonly extProxy: TerminalServiceExt;
private readonly shellTerminalServer: ShellTerminalServerProxy;
Expand All @@ -44,6 +48,8 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin

constructor(rpc: RPCProtocol, container: interfaces.Container) {
this.terminals = container.get(TerminalService);
this.pluginTerminalRegistry = container.get(PluginTerminalRegistry);
this.hostedPluginSupport = container.get(HostedPluginSupport);
this.shell = container.get(ApplicationShell);
this.shellTerminalServer = container.get(ShellTerminalServerProxy);
this.extProxy = rpc.getProxy(MAIN_RPC_CONTEXT.TERMINAL_EXT);
Expand All @@ -59,9 +65,16 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
this.extProxy.$initEnvironmentVariableCollections(serializedCollections);
}

this.pluginTerminalRegistry.startCallback = id => this.startProfile(id);

container.bind(TerminalLinkProvider).toDynamicValue(() => this);
}

async startProfile(id: string): Promise<string> {
await this.hostedPluginSupport.activateByTerminalProfile(id);
return this.extProxy.$startProfile(id, CancellationToken.None);
}

$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: SerializableEnvironmentVariableCollection | undefined): void {
if (collection) {
this.shellTerminalServer.setCollection(extensionIdentifier, persistent, collection);
Expand Down Expand Up @@ -124,32 +137,28 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
}

async $createTerminal(id: string, options: TerminalOptions, parentId?: string, isPseudoTerminal?: boolean): Promise<string> {
try {
const terminal = await this.terminals.newTerminal({
id,
title: options.name,
iconClass: getIconClass(options),
shellPath: options.shellPath,
shellArgs: options.shellArgs,
cwd: options.cwd ? new URI(options.cwd) : undefined,
env: options.env,
strictEnv: options.strictEnv,
destroyTermOnClose: true,
useServerTitle: false,
attributes: options.attributes,
hideFromUser: options.hideFromUser,
location: this.getTerminalLocation(options, parentId),
isPseudoTerminal,
isTransient: options.isTransient
});
if (options.message) {
terminal.writeLine(options.message);
}
terminal.start();
return terminal.id;
} catch (error) {
throw new Error('Failed to create terminal. Cause: ' + error);
const terminal = await this.terminals.newTerminal({
id,
title: options.name,
iconClass: getIconClass(options),
shellPath: options.shellPath,
shellArgs: options.shellArgs,
cwd: options.cwd ? new URI(options.cwd) : undefined,
env: options.env,
strictEnv: options.strictEnv,
destroyTermOnClose: true,
useServerTitle: false,
attributes: options.attributes,
hideFromUser: options.hideFromUser,
location: this.getTerminalLocation(options, parentId),
isPseudoTerminal,
isTransient: options.isTransient
});
if (options.message) {
terminal.writeLine(options.message);
}
terminal.start();
return terminal.id;
}

protected getTerminalLocation(options: TerminalOptions, parentId?: string): TerminalLocation | TerminalEditorLocationOptions | { parentTerminal: string; } | undefined {
Expand Down
5 changes: 5 additions & 0 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ import {
InputBoxValidationSeverity,
TerminalLink,
TerminalLocation,
TerminalProfile,
InlayHint,
InlayHintKind,
InlayHintLabelPart,
Expand Down Expand Up @@ -548,6 +549,9 @@ export function createAPIFactory(
registerTerminalLinkProvider(provider: theia.TerminalLinkProvider): theia.Disposable {
return terminalExt.registerTerminalLinkProvider(provider);
},
registerTerminalProfileProvider(id: string, provider: theia.TerminalProfileProvider): theia.Disposable {
return terminalExt.registerTerminalProfileProvider(id, provider);
},
get activeColorTheme(): theia.ColorTheme {
return themingExt.activeColorTheme;
},
Expand Down Expand Up @@ -1252,6 +1256,7 @@ export function createAPIFactory(
SourceControlInputBoxValidationType,
FileDecoration,
TerminalLink,
TerminalProfile,
CancellationError,
ExtensionMode,
LinkedEditingRanges,
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/src/plugin/plugin-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
'workspaceContains',
'onView',
'onUri',
'onTerminalProfile',
'onWebviewPanel',
'onFileSystem',
'onCustomEditor',
Expand Down
Loading