Skip to content

Commit

Permalink
Install vsix files from the explorer view (eclipse-theia#13269) (ecli…
Browse files Browse the repository at this point in the history
  • Loading branch information
dhuebner authored Feb 16, 2024
1 parent e53924d commit f442b7d
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 25 deletions.
1 change: 1 addition & 0 deletions packages/vsx-registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {
"@theia/core": "1.46.0",
"@theia/filesystem": "1.46.0",
"@theia/navigator": "1.46.0",
"@theia/ovsx-client": "1.46.0",
"@theia/plugin-ext": "1.46.0",
"@theia/plugin-ext-vscode": "1.46.0",
Expand Down
5 changes: 5 additions & 0 deletions packages/vsx-registry/src/browser/vsx-extension-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export namespace VSXExtensionsCommands {
label: nls.localizeByDefault('Install from VSIX') + '...',
dialogLabel: nls.localizeByDefault('Install from VSIX')
};
export const INSTALL_VSIX_FILE: Command = Command.toDefaultLocalizedCommand({
id: 'vsxExtensions.installVSIX',
label: 'Install Extension VSIX',
category: EXTENSIONS_CATEGORY,
});
export const INSTALL_ANOTHER_VERSION: Command = {
id: 'vsxExtensions.installAnotherVersion'
};
Expand Down
76 changes: 52 additions & 24 deletions packages/vsx-registry/src/browser/vsx-extensions-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,32 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { DateTime } from 'luxon';
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
import debounce = require('@theia/core/shared/lodash.debounce');
import { Command, CommandRegistry } from '@theia/core/lib/common/command';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { VSXExtensionsViewContainer } from './vsx-extensions-view-container';
import { VSXExtensionsModel } from './vsx-extensions-model';
import { CommonMenus, LabelProvider, PreferenceService, QuickInputService, QuickPickItem } from '@theia/core/lib/browser';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
import { Color } from '@theia/core/lib/common/color';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
import { MenuModelRegistry, MessageService, nls } from '@theia/core/lib/common';
import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution';
import { MenuModelRegistry, MessageService, SelectionService, nls } from '@theia/core/lib/common';
import { Color } from '@theia/core/lib/common/color';
import { Command, CommandRegistry } from '@theia/core/lib/common/command';
import URI from '@theia/core/lib/common/uri';
import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler';
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import { FileDialogService, OpenFileDialogProps } from '@theia/filesystem/lib/browser';
import { LabelProvider, PreferenceService, QuickPickItem, QuickInputService, CommonMenus } from '@theia/core/lib/browser';
import { NAVIGATOR_CONTEXT_MENU } from '@theia/navigator/lib/browser/navigator-contribution';
import { OVSXApiFilter, VSXExtensionRaw } from '@theia/ovsx-client';
import { VscodeCommands } from '@theia/plugin-ext-vscode/lib/browser/plugin-vscode-commands-contribution';
import { VSXExtensionsContextMenu, VSXExtension } from './vsx-extension';
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
import { BUILTIN_QUERY, INSTALLED_QUERY, RECOMMENDED_QUERY } from './vsx-extensions-search-model';
import { DateTime } from 'luxon';
import { OVSXClientProvider } from '../common/ovsx-client-provider';
import { IGNORE_RECOMMENDATIONS_ID } from './recommended-extensions/recommended-extensions-preference-contribution';
import { VSXExtension, VSXExtensionsContextMenu } from './vsx-extension';
import { VSXExtensionsCommands } from './vsx-extension-commands';
import { VSXExtensionRaw, OVSXApiFilter } from '@theia/ovsx-client';
import { OVSXClientProvider } from '../common/ovsx-client-provider';
import { VSXExtensionsModel } from './vsx-extensions-model';
import { BUILTIN_QUERY, INSTALLED_QUERY, RECOMMENDED_QUERY } from './vsx-extensions-search-model';
import { VSXExtensionsViewContainer } from './vsx-extensions-view-container';
import debounce = require('@theia/core/shared/lodash.debounce');

export namespace VSXCommands {
export const TOGGLE_EXTENSIONS: Command = {
Expand All @@ -57,6 +60,7 @@ export class VSXExtensionsContribution extends AbstractViewContribution<VSXExten
@inject(OVSXClientProvider) protected clientProvider: OVSXClientProvider;
@inject(OVSXApiFilter) protected vsxApiFilter: OVSXApiFilter;
@inject(QuickInputService) protected quickInput: QuickInputService;
@inject(SelectionService) protected readonly selectionService: SelectionService;

constructor() {
super({
Expand Down Expand Up @@ -95,6 +99,13 @@ export class VSXExtensionsContribution extends AbstractViewContribution<VSXExten
execute: () => this.installFromVSIX()
});

commands.registerCommand(VSXExtensionsCommands.INSTALL_VSIX_FILE,
UriAwareCommandHandler.MonoSelect(this.selectionService, {
execute: fileURI => this.installVsixFile(fileURI),
isEnabled: fileURI => fileURI.scheme === 'file' && fileURI.path.ext === '.vsix'
})
);

commands.registerCommand(VSXExtensionsCommands.INSTALL_ANOTHER_VERSION, {
// Check downloadUrl to ensure we have an idea of where to look for other versions.
isEnabled: (extension: VSXExtension) => !extension.builtin && !!extension.downloadUrl,
Expand Down Expand Up @@ -143,6 +154,11 @@ export class VSXExtensionsContribution extends AbstractViewContribution<VSXExten
commandId: VSXExtensionsCommands.INSTALL_ANOTHER_VERSION.id,
label: nls.localizeByDefault('Install Another Version...'),
});
menus.registerMenuAction(NAVIGATOR_CONTEXT_MENU, {
commandId: VSXExtensionsCommands.INSTALL_VSIX_FILE.id,
label: VSXExtensionsCommands.INSTALL_VSIX_FILE.label,
when: 'resourceScheme == file && resourceExtname == .vsix'
});
}

registerColors(colors: ColorRegistry): void {
Expand Down Expand Up @@ -199,25 +215,37 @@ export class VSXExtensionsContribution extends AbstractViewContribution<VSXExten
title: VSXExtensionsCommands.INSTALL_FROM_VSIX.dialogLabel,
openLabel: nls.localizeByDefault('Install from VSIX'),
filters: { 'VSIX Extensions (*.vsix)': ['vsix'] },
canSelectMany: false
canSelectMany: false,
canSelectFiles: true
};
const extensionUri = await this.fileDialogService.showOpenDialog(props);
if (extensionUri) {
if (extensionUri.path.ext === '.vsix') {
const extensionName = this.labelProvider.getName(extensionUri);
try {
await this.commandRegistry.executeCommand(VscodeCommands.INSTALL_FROM_VSIX.id, extensionUri);
this.messageService.info(nls.localizeByDefault('Completed installing {0} extension from VSIX.', extensionName));
} catch (e) {
this.messageService.error(nls.localize('theia/vsx-registry/failedInstallingVSIX', 'Failed to install {0} from VSIX.', extensionName));
console.warn(e);
}
await this.installVsixFile(extensionUri);
} else {
this.messageService.error(nls.localize('theia/vsx-registry/invalidVSIX', 'The selected file is not a valid "*.vsix" plugin.'));
}
}
}

/**
* Installs a local vs-code extension file.
* The implementation doesn't check if the file is a valid VSIX file, or the URI has a *.vsix extension.
* The caller should ensure the file is a valid VSIX file.
*
* @param fileURI the URI of the file to install.
*/
protected async installVsixFile(fileURI: URI): Promise<void> {
const extensionName = this.labelProvider.getName(fileURI);
try {
await this.commandRegistry.executeCommand(VscodeCommands.INSTALL_FROM_VSIX.id, fileURI);
this.messageService.info(nls.localizeByDefault('Completed installing {0} extension from VSIX.', extensionName));
} catch (e) {
this.messageService.error(nls.localize('theia/vsx-registry/failedInstallingVSIX', 'Failed to install {0} from VSIX.', extensionName));
console.warn(e);
}
}

/**
* Given an extension, displays a quick pick of other compatible versions and installs the selected version.
*
Expand Down
11 changes: 10 additions & 1 deletion packages/vsx-registry/src/browser/vsx-extensions-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// *****************************************************************************

import { injectable, interfaces, postConstruct, inject } from '@theia/core/shared/inversify';
import { TreeModel, TreeNode } from '@theia/core/lib/browser';
import { Message, TreeModel, TreeNode } from '@theia/core/lib/browser';
import { SourceTreeWidget } from '@theia/core/lib/browser/source-tree';
import { VSXExtensionsSource, VSXExtensionsSourceOptions } from './vsx-extensions-source';
import { nls } from '@theia/core/lib/common/nls';
Expand Down Expand Up @@ -153,4 +153,13 @@ export class VSXExtensionsWidget extends SourceTreeWidget implements BadgeWidget
}
return super.renderTree(model);
}

protected override onAfterShow(msg: Message): void {
super.onAfterShow(msg);
if (this.options.id === VSXExtensionsSourceOptions.INSTALLED) {
// This is needed when an Extension was installed outside of the extension view.
// E.g. using explorer context menu.
this.doUpdateRows();
}
}
}
3 changes: 3 additions & 0 deletions packages/vsx-registry/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
{
"path": "../filesystem"
},
{
"path": "../navigator"
},
{
"path": "../plugin-ext"
},
Expand Down

0 comments on commit f442b7d

Please sign in to comment.