diff --git a/CHANGELOG.md b/CHANGELOG.md index edd01986089c2..98b10eb01494f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## v1.9.0 + +- [plugin-ext-vscode] added support for the command `workbench.extensions.installExtension`. [#8745](https://github.com/eclipse-theia/theia/pull/8745) + +[Breaking Changes:](#breaking_changes_1.9.0) + +- [plugin-ext] `LocalDirectoryPluginDeployerResolver` has moved from `packages/plugin-ext/src/main/node/resolvers/plugin-local-dir-resolver.ts` to `packages/plugin-ext/src/main/node/resolvers/local-file-plugin-deployer-resolver.ts` and now derives from `LocalPluginDeployerResolver`. + ## v1.8.0 - 26/11/2020 - [api-tests] fixed issue with `saveable` test suite [#8736](https://github.com/eclipse-theia/theia/pull/8736) diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index 440730fb20a6b..f818455723ff2 100755 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -53,6 +53,7 @@ import { DiffService } from '@theia/workspace/lib/browser/diff-service'; import { inject, injectable } from 'inversify'; import { Position } from '@theia/plugin-ext/lib/common/plugin-api-rpc'; import { URI } from 'vscode-uri'; +import { PluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { TerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; import { QuickOpenWorkspace } from '@theia/workspace/lib/browser/quick-open-workspace'; @@ -63,6 +64,7 @@ import { } 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 { UriComponents } from '@theia/plugin-ext/lib/common/uri-components'; export namespace VscodeCommands { export const OPEN: Command = { @@ -106,6 +108,8 @@ export class PluginVscodeCommandsContribution implements CommandContribution { protected readonly terminalService: TerminalService; @inject(CodeEditorWidgetUtil) protected readonly codeEditorWidgetUtil: CodeEditorWidgetUtil; + @inject(PluginServer) + protected readonly pluginServer: PluginServer; registerCommands(commands: CommandRegistry): void { commands.registerCommand(VscodeCommands.OPEN, { @@ -208,6 +212,15 @@ export class PluginVscodeCommandsContribution implements CommandContribution { commands.registerCommand({ id: 'workbench.action.openSettings' }, { execute: () => commands.executeCommand(CommonCommands.OPEN_PREFERENCES.id) }); + commands.registerCommand({ id: 'workbench.extensions.installExtension' }, { + execute: async (vsixUriOrExtensionId: UriComponents | string) => { + if (typeof vsixUriOrExtensionId === 'string') { + this.pluginServer.deploy(`vscode:extension/${vsixUriOrExtensionId}`); + } else { + this.pluginServer.deploy(`local-file:${URI.revive(vsixUriOrExtensionId).fsPath}`); + } + } + }); commands.registerCommand({ id: 'workbench.action.files.save', }, { execute: (uri?: monaco.Uri) => { if (uri) { diff --git a/packages/plugin-ext/src/main/node/plugin-cli-contribution.ts b/packages/plugin-ext/src/main/node/plugin-cli-contribution.ts index e989b1527b15d..614c63bc4ce54 100644 --- a/packages/plugin-ext/src/main/node/plugin-cli-contribution.ts +++ b/packages/plugin-ext/src/main/node/plugin-cli-contribution.ts @@ -17,7 +17,7 @@ import { injectable } from 'inversify'; import { Argv, Arguments } from 'yargs'; import { CliContribution } from '@theia/core/lib/node/cli'; -import { LocalDirectoryPluginDeployerResolver } from './resolvers/plugin-local-dir-resolver'; +import { LocalDirectoryPluginDeployerResolver } from './resolvers/local-directory-plugin-deployer-resolver'; @injectable() export class PluginCliContribution implements CliContribution { diff --git a/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts b/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts index 440e5f138f032..412cb3ddeffd3 100644 --- a/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts +++ b/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts @@ -24,7 +24,8 @@ import { PluginDeployerDirectoryHandler, PluginServer, pluginServerJsonRpcPath, PluginDeployerParticipant } from '../../common/plugin-protocol'; import { PluginDeployerImpl } from './plugin-deployer-impl'; -import { LocalDirectoryPluginDeployerResolver } from './resolvers/plugin-local-dir-resolver'; +import { LocalFilePluginDeployerResolver } from './resolvers/local-file-plugin-deployer-resolver'; +import { LocalDirectoryPluginDeployerResolver } from './resolvers/local-directory-plugin-deployer-resolver'; import { PluginTheiaFileHandler } from './handlers/plugin-theia-file-handler'; import { PluginTheiaDirectoryHandler } from './handlers/plugin-theia-directory-handler'; import { GithubPluginDeployerResolver } from './plugin-github-resolver'; @@ -47,6 +48,7 @@ export function bindMainBackend(bind: interfaces.Bind): void { bind(BackendApplicationContribution).toService(PluginDeployerContribution); bind(PluginDeployerResolver).to(LocalDirectoryPluginDeployerResolver).inSingletonScope(); + bind(PluginDeployerResolver).to(LocalFilePluginDeployerResolver).inSingletonScope(); bind(PluginDeployerResolver).to(GithubPluginDeployerResolver).inSingletonScope(); bind(PluginDeployerResolver).to(HttpPluginDeployerResolver).inSingletonScope(); diff --git a/packages/plugin-ext/src/main/node/resolvers/local-directory-plugin-deployer-resolver.ts b/packages/plugin-ext/src/main/node/resolvers/local-directory-plugin-deployer-resolver.ts new file mode 100644 index 0000000000000..6e0ea92da99e3 --- /dev/null +++ b/packages/plugin-ext/src/main/node/resolvers/local-directory-plugin-deployer-resolver.ts @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (C) 2018 Red Hat, Inc. 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 { PluginDeployerResolverContext } from '../../../common/plugin-protocol'; +import { injectable } from 'inversify'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import { LocalPluginDeployerResolver } from './local-plugin-deployer-resolver'; + +@injectable() +export class LocalDirectoryPluginDeployerResolver extends LocalPluginDeployerResolver { + static LOCAL_DIR = 'local-dir'; + + protected get supportedScheme(): string { + return LocalDirectoryPluginDeployerResolver.LOCAL_DIR; + } + + protected async resolveFromLocalPath(pluginResolverContext: PluginDeployerResolverContext, localPath: string): Promise { + const files = await fs.readdir(localPath); + files.forEach(file => + pluginResolverContext.addPlugin(file, path.resolve(localPath, file)) + ); + } +} diff --git a/packages/plugin-ext/src/main/node/resolvers/local-file-plugin-deployer-resolver.ts b/packages/plugin-ext/src/main/node/resolvers/local-file-plugin-deployer-resolver.ts new file mode 100644 index 0000000000000..9635024ddf0b3 --- /dev/null +++ b/packages/plugin-ext/src/main/node/resolvers/local-file-plugin-deployer-resolver.ts @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (C) 2018 Red Hat, Inc. 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 { PluginDeployerResolverContext } from '../../../common/plugin-protocol'; +import { injectable } from 'inversify'; +import * as path from 'path'; +import { LocalPluginDeployerResolver } from './local-plugin-deployer-resolver'; + +@injectable() +export class LocalFilePluginDeployerResolver extends LocalPluginDeployerResolver { + static LOCAL_FILE = 'local-file'; + + protected get supportedScheme(): string { + return LocalFilePluginDeployerResolver.LOCAL_FILE; + } + + async resolveFromLocalPath(pluginResolverContext: PluginDeployerResolverContext, localPath: string): Promise { + const fileName = path.basename(localPath); + pluginResolverContext.addPlugin(fileName, localPath); + } +} diff --git a/packages/plugin-ext/src/main/node/resolvers/local-plugin-deployer-resolver.ts b/packages/plugin-ext/src/main/node/resolvers/local-plugin-deployer-resolver.ts new file mode 100644 index 0000000000000..8eaa16688feb6 --- /dev/null +++ b/packages/plugin-ext/src/main/node/resolvers/local-plugin-deployer-resolver.ts @@ -0,0 +1,60 @@ +/******************************************************************************** + * Copyright (C) 2020 Red Hat, Inc. 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 'inversify'; +import { PluginDeployerResolver, PluginDeployerResolverContext } from '../../../common/plugin-protocol'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import { FileUri } from '@theia/core/lib/node'; +import URI from '@theia/core/lib/common/uri'; + +@injectable() +export abstract class LocalPluginDeployerResolver implements PluginDeployerResolver { + public async resolve(pluginResolverContext: PluginDeployerResolverContext): Promise { + const localPath = await this.resolveLocalPluginPath( + pluginResolverContext, + this.supportedScheme); + if (localPath) { + await this.resolveFromLocalPath(pluginResolverContext, localPath); + } + } + + public accept(pluginId: string): boolean { + return pluginId.startsWith(this.supportedScheme); + } + + protected abstract get supportedScheme(): string; + + protected abstract resolveFromLocalPath(pluginResolverContext: PluginDeployerResolverContext, localPath: string): Promise; + + private async resolveLocalPluginPath( + pluginResolverContext: PluginDeployerResolverContext, + expectedScheme: string): Promise { + const localUri = new URI(pluginResolverContext.getOriginId()); + if (localUri.scheme !== expectedScheme) { + return null; + } + let fsPath = FileUri.fsPath(localUri); + if (!path.isAbsolute(fsPath)) { + fsPath = path.resolve(process.cwd(), fsPath); + } + if (!await fs.pathExists(fsPath)) { + console.warn(`The local plugin referenced by ${pluginResolverContext.getOriginId()} does not exist.`); + return null; + } + return fsPath; + } +} diff --git a/packages/plugin-ext/src/main/node/resolvers/plugin-local-dir-resolver.ts b/packages/plugin-ext/src/main/node/resolvers/plugin-local-dir-resolver.ts deleted file mode 100644 index 7e9de4a3d11c7..0000000000000 --- a/packages/plugin-ext/src/main/node/resolvers/plugin-local-dir-resolver.ts +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2018 Red Hat, Inc. 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 - ********************************************************************************/ - -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { PluginDeployerResolver, PluginDeployerResolverContext } from '../../../common/plugin-protocol'; -import { injectable } from 'inversify'; -import * as fs from 'fs'; -import * as path from 'path'; -import { FileUri } from '@theia/core/lib/node'; -import URI from '@theia/core/lib/common/uri'; - -@injectable() -export class LocalDirectoryPluginDeployerResolver implements PluginDeployerResolver { - - static LOCAL_DIR = 'local-dir'; - - /** - * Check all files/folder from the local-dir referenced and add them as plugins. - */ - async resolve(pluginResolverContext: PluginDeployerResolverContext): Promise { - // get directory - const localDirUri = new URI(pluginResolverContext.getOriginId()); - if (localDirUri.scheme !== LocalDirectoryPluginDeployerResolver.LOCAL_DIR) { - return; - } - // get fs path - let dirPath = FileUri.fsPath(localDirUri); - if (!path.isAbsolute(dirPath)) { - dirPath = path.resolve(process.cwd(), dirPath); - } - // check directory exists - if (!fs.existsSync(dirPath)) { - console.warn(`The directory referenced by ${pluginResolverContext.getOriginId()} does not exist.`); - return; - } - // list all stuff from this directory - await new Promise((resolve: any, reject: any) => { - fs.readdir(dirPath, (err: any, files: any) => { - files.forEach((file: any) => { - pluginResolverContext.addPlugin(file, path.resolve(dirPath, file)); - }); - resolve(true); - }); - }); - } - accept(pluginId: string): boolean { - return pluginId.startsWith(LocalDirectoryPluginDeployerResolver.LOCAL_DIR); - } -}