Skip to content

Commit

Permalink
Support version for installExtension, support uninstall cmd (#13795, #…
Browse files Browse the repository at this point in the history
  • Loading branch information
dhuebner committed Oct 11, 2024
1 parent c7fb4f5 commit a34caac
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { Command, CommandContribution, CommandRegistry, environment, isOSX, CancellationTokenSource, MessageService } from '@theia/core';
import { CallHierarchyService, CallHierarchyServiceProvider } from '@theia/callhierarchy/lib/browser';
import { CancellationTokenSource, Command, CommandContribution, CommandRegistry, MessageService, environment, isOSX } from '@theia/core';
import {
ApplicationShell,
CommonCommands,
NavigatableWidget,
OpenerService, OpenHandler,
OpenHandler,
OpenerService,
QuickInputService,
Saveable,
TabBar,
Expand All @@ -28,60 +30,59 @@ import {
} from '@theia/core/lib/browser';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import { ApplicationShellMouseTracker } from '@theia/core/lib/browser/shell/application-shell-mouse-tracker';
import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { CommandService } from '@theia/core/lib/common/command';
import { nls } from '@theia/core/lib/common/nls';
import TheiaURI from '@theia/core/lib/common/uri';
import { EditorManager, EditorCommands } from '@theia/editor/lib/browser';
import { inject, injectable, optional } from '@theia/core/shared/inversify';
import { URI } from '@theia/core/shared/vscode-uri';
import { EditorCommands, EditorManager } from '@theia/editor/lib/browser';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import * as monaco from '@theia/monaco-editor-core';
import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages';
import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
import { FILE_NAVIGATOR_ID, FileNavigatorWidget } from '@theia/navigator/lib/browser';
import {
FILE_NAVIGATOR_TOGGLE_COMMAND_ID,
FileNavigatorCommands
} from '@theia/navigator/lib/browser/navigator-contribution';
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
import { Range } from '@theia/plugin';
import { Position } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
import {
TextDocumentShowOptions,
Location,
CallHierarchyItem,
CallHierarchyIncomingCall,
CallHierarchyItem,
CallHierarchyOutgoingCall,
TypeHierarchyItem,
DocumentHighlight,
FormattingOptions,
Hover,
Location,
TextDocumentShowOptions,
TextEdit,
FormattingOptions,
DocumentHighlight
TypeHierarchyItem
} from '@theia/plugin-ext/lib/common/plugin-api-rpc-model';
import { DocumentsMainImpl } from '@theia/plugin-ext/lib/main/browser/documents-main';
import { isUriComponents, toMergedSymbol, toPosition } from '@theia/plugin-ext/lib/plugin/type-converters';
import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl';
import { WorkspaceCommands } from '@theia/workspace/lib/browser';
import { WorkspaceService, WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service';
import { DiffService } from '@theia/workspace/lib/browser/diff-service';
import { inject, injectable, optional } from '@theia/core/shared/inversify';
import { Position } from '@theia/plugin-ext/lib/common/plugin-api-rpc';
import { URI } from '@theia/core/shared/vscode-uri';
import { PluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { TerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
import { QuickOpenWorkspace } from '@theia/workspace/lib/browser/quick-open-workspace';
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
import {
FileNavigatorCommands,
FILE_NAVIGATOR_TOGGLE_COMMAND_ID
} from '@theia/navigator/lib/browser/navigator-contribution';
import { FILE_NAVIGATOR_ID, FileNavigatorWidget } from '@theia/navigator/lib/browser';
import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection';
import { PluginDeployOptions, PluginIdentifiers, PluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { UriComponents } from '@theia/plugin-ext/lib/common/uri-components';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { CallHierarchyServiceProvider, CallHierarchyService } from '@theia/callhierarchy/lib/browser';
import { TypeHierarchyServiceProvider, TypeHierarchyService } from '@theia/typehierarchy/lib/browser';
import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
import { CustomEditorOpener } from '@theia/plugin-ext/lib/main/browser/custom-editors/custom-editor-opener';
import { DocumentsMainImpl } from '@theia/plugin-ext/lib/main/browser/documents-main';
import {
fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall,
fromCallHierarchyCallerToModelCallHierarchyIncomingCall,
fromItemHierarchyDefinition,
toItemHierarchyDefinition
} from '@theia/plugin-ext/lib/main/browser/hierarchy/hierarchy-types-converters';
import { CustomEditorOpener } from '@theia/plugin-ext/lib/main/browser/custom-editors/custom-editor-opener';
import { nls } from '@theia/core/lib/common/nls';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import * as monaco from '@theia/monaco-editor-core';
import { VSCodeExtensionUri } from '../common/plugin-vscode-uri';
import { CodeEditorWidgetUtil } from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings';
import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution';
import { Range } from '@theia/plugin';
import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages';
import { isUriComponents, toMergedSymbol, toPosition } from '@theia/plugin-ext/lib/plugin/type-converters';
import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl';
import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
import { TerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
import { TypeHierarchyService, TypeHierarchyServiceProvider } from '@theia/typehierarchy/lib/browser';
import { WorkspaceCommands } from '@theia/workspace/lib/browser';
import { DiffService } from '@theia/workspace/lib/browser/diff-service';
import { QuickOpenWorkspace } from '@theia/workspace/lib/browser/quick-open-workspace';
import { WorkspaceInput, WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { VSCodeExtensionUri } from '../common/plugin-vscode-uri';

export namespace VscodeCommands {

Expand Down Expand Up @@ -109,6 +110,10 @@ export namespace VscodeCommands {
export const INSTALL_FROM_VSIX: Command = {
id: 'workbench.extensions.installExtension'
};

export const UNINSTALL_EXTENSION: Command = {
id: 'workbench.extensions.uninstallExtension'
};
}

// https://wicg.github.io/webusb/
Expand Down Expand Up @@ -370,16 +375,36 @@ export class PluginVscodeCommandsContribution implements CommandContribution {
commands.registerCommand({ id: 'workbench.files.action.refreshFilesExplorer' }, {
execute: () => commands.executeCommand(FileNavigatorCommands.REFRESH_NAVIGATOR.id)
});
commands.registerCommand({ id: VscodeCommands.INSTALL_FROM_VSIX.id }, {
commands.registerCommand(VscodeCommands.INSTALL_FROM_VSIX, {
execute: async (vsixUriOrExtensionId: TheiaURI | UriComponents | string) => {
if (typeof vsixUriOrExtensionId === 'string') {
await this.pluginServer.deploy(VSCodeExtensionUri.fromId(vsixUriOrExtensionId).toString());
let extensionId = vsixUriOrExtensionId;
let opts: PluginDeployOptions | undefined;
if (PluginIdentifiers.isVersionedId(vsixUriOrExtensionId)) {
const idAndVersion = PluginIdentifiers.getIdAndVersion(vsixUriOrExtensionId);
extensionId = idAndVersion[0];
opts = { version: idAndVersion[1]!, ignoreOtherVersions: true };
}
await this.pluginServer.deploy(VSCodeExtensionUri.fromId(extensionId).toString(), undefined, opts);
} else {
const uriPath = isUriComponents(vsixUriOrExtensionId) ? URI.revive(vsixUriOrExtensionId).fsPath : await this.fileService.fsPath(vsixUriOrExtensionId);
await this.pluginServer.deploy(`local-file:${uriPath}`);
}
}
});
commands.registerCommand(VscodeCommands.UNINSTALL_EXTENSION, {
execute: async (id: string) => {
if (!id) {
throw new Error(nls.localizeByDefault('Extension id required.'));
}
if (!PluginIdentifiers.isVersionedId(id)) {
throw new Error(`Invalid extension id: ${id}\nExpected format: <publisher>.<name>@<version>.`);
}
const idAndVersion = PluginIdentifiers.identifiersFromVersionedId(id);
const pluginId = PluginIdentifiers.componentsToVersionedId(idAndVersion!);
await this.pluginServer.uninstall(pluginId);
}
});
commands.registerCommand({ id: 'workbench.action.files.save', }, {
execute: (uri?: monaco.Uri) => {
if (uri) {
Expand Down
3 changes: 2 additions & 1 deletion packages/plugin-ext-vscode/src/common/plugin-vscode-uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// *****************************************************************************

import URI from '@theia/core/lib/common/uri';
import { PluginIdentifiers } from '@theia/plugin-ext';

/**
* Static methods for identifying a plugin as the target of the VSCode deployment system.
Expand All @@ -32,7 +33,7 @@ export namespace VSCodeExtensionUri {
}

export function fromVersionedId(versionedId: string): URI {
const versionAndId = versionedId.split('@');
const versionAndId = PluginIdentifiers.getIdAndVersion(versionedId);
return fromId(versionAndId[0], versionAndId[1]);
}

Expand Down
27 changes: 27 additions & 0 deletions packages/plugin-ext/src/common/plugin-identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,31 @@ export namespace PluginIdentifiers {
}
return { id: probablyId.slice(0, endOfName) as UnversionedId, version: probablyId.slice(endOfName + 1) };
}

const EXTENSION_IDENTIFIER_WITH_VERSION_REGEX = /^([^.]+\..+)@((prerelease)|(\d+\.\d+\.\d+(-.*)?))$/;

/**
* Extracts the extension identifier and version from a string.
* @param id The extension identifier
* @returns A tuple of the extension identifier and the version, if present.
*/
export function getIdAndVersion(id: string): [string, string | undefined] {
const matches = EXTENSION_IDENTIFIER_WITH_VERSION_REGEX.exec(id);
if (matches && matches[1]) {
return [matches[1], matches[2]];
}
return [id, undefined];
}

/**
* Checks if the extension identifier is in the format `<publisher>.<name>@<version>`.
* @param id The extension identifier
* @returns `true` if the extension identifier is in the format `<publisher>.<name>@<version>`.
*/
export function isVersionedId(id: string): boolean {
const matches = EXTENSION_IDENTIFIER_WITH_VERSION_REGEX.exec(id);
// eslint-disable-next-line no-null/no-null
return matches !== null && matches.length > 2;
}

}
7 changes: 5 additions & 2 deletions packages/vsx-registry/src/browser/vsx-extension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { TreeElement, TreeElementNode } from '@theia/core/lib/browser/source-tre
import { OpenerService, open, OpenerOptions } from '@theia/core/lib/browser/opener-service';
import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
import { PluginServer, DeployedPlugin, PluginType, PluginIdentifiers, PluginDeployOptions } from '@theia/plugin-ext/lib/common/plugin-protocol';
import { VscodeCommands } from '@theia/plugin-ext-vscode/lib/browser/plugin-vscode-commands-contribution';
import { VSCodeExtensionUri } from '@theia/plugin-ext-vscode/lib/common/plugin-vscode-uri';
import { ProgressService } from '@theia/core/lib/common/progress-service';
import { Endpoint } from '@theia/core/lib/browser/endpoint';
Expand Down Expand Up @@ -324,8 +325,10 @@ export class VSXExtension implements VSXExtensionData, TreeElement {
if (plugin) {
await this.progressService.withProgress(
nls.localizeByDefault('Uninstalling {0}...', this.id), 'extensions',
() => this.pluginServer.uninstall(PluginIdentifiers.componentsToVersionedId(plugin.metadata.model))
);
async () => {
const versionedId = PluginIdentifiers.componentsToVersionedId(plugin.metadata.model);
await this.commandRegistry.executeCommand(VscodeCommands.UNINSTALL_EXTENSION.id, versionedId);
});
}
} finally {
this._busy--;
Expand Down

0 comments on commit a34caac

Please sign in to comment.