mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Implement #11936
This commit is contained in:
parent
7ac6d551d8
commit
ef52244d8b
|
@ -57,6 +57,10 @@
|
|||
{
|
||||
"fileMatch": "%APP_SETTINGS_HOME%/snippets/*.json",
|
||||
"url": "vscode://schemas/snippets"
|
||||
},
|
||||
{
|
||||
"fileMatch": "/.vscode/extensions.json",
|
||||
"url": "vscode://schemas/extensions"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -202,6 +202,7 @@ export const IExtensionTipsService = createDecorator<IExtensionTipsService>('ext
|
|||
export interface IExtensionTipsService {
|
||||
_serviceBrand: any;
|
||||
getRecommendations(): string[];
|
||||
getWorkspaceRecommendations(): string[];
|
||||
}
|
||||
|
||||
export const ExtensionsLabel = nls.localize('extensions', "Extensions");
|
||||
|
|
|
@ -11,6 +11,7 @@ import {TPromise as Promise} from 'vs/base/common/winjs.base';
|
|||
import {Action} from 'vs/base/common/actions';
|
||||
import {match} from 'vs/base/common/glob';
|
||||
import {IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionsConfiguration, EXTENSIONS_CONFIGURAION_NAME } from './extensions';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage';
|
||||
import product from 'vs/platform/product';
|
||||
|
@ -18,6 +19,7 @@ import { IMessageService, CloseAction } from 'vs/platform/message/common/messag
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ShowRecommendedExtensionsAction } from './extensionsActions';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export class ExtensionTipsService implements IExtensionTipsService {
|
||||
|
||||
|
@ -35,7 +37,8 @@ export class ExtensionTipsService implements IExtensionTipsService {
|
|||
@IStorageService private storageService: IStorageService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IExtensionManagementService private extensionsService: IExtensionManagementService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
if (!this._galleryService.isEnabled()) {
|
||||
return;
|
||||
|
@ -72,6 +75,11 @@ export class ExtensionTipsService implements IExtensionTipsService {
|
|||
this._modelService.getModels().forEach(model => this._suggest(model.uri));
|
||||
}
|
||||
|
||||
getWorkspaceRecommendations(): string[] {
|
||||
let configuration = this.configurationService.getConfiguration<IExtensionsConfiguration>(EXTENSIONS_CONFIGURAION_NAME);
|
||||
return configuration.recommendations ? configuration.recommendations : [];
|
||||
}
|
||||
|
||||
getRecommendations(): string[] {
|
||||
return Object.keys(this._recommendations);
|
||||
}
|
||||
|
|
|
@ -21,13 +21,15 @@ import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/co
|
|||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { VIEWLET_ID, IExtensionsWorkbenchService } from './extensions';
|
||||
import { ExtensionsWorkbenchService } from './extensionsWorkbenchService';
|
||||
import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, UpdateAllAction, OpenExtensionsFolderAction } from './extensionsActions';
|
||||
import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, UpdateAllAction, OpenExtensionsFolderAction, ConfigureWorkspaceRecommendationsAction } from './extensionsActions';
|
||||
import { ExtensionsInput } from './extensionsInput';
|
||||
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { ExtensionEditor } from './extensionEditor';
|
||||
import { StatusUpdater } from './extensionsViewlet';
|
||||
import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
|
||||
import { Schema, SchemaId } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate';
|
||||
|
||||
// Singletons
|
||||
registerSingleton(IExtensionGalleryService, ExtensionGalleryService);
|
||||
|
@ -104,6 +106,9 @@ actionRegistry.registerWorkbenchAction(listOutdatedActionDescriptor, 'Extensions
|
|||
const recommendationsActionDescriptor = new SyncActionDescriptor(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(recommendationsActionDescriptor, 'Extensions: Show Recommended Extensions', ExtensionsLabel);
|
||||
|
||||
const workspaceRecommendationsActionDescriptor = new SyncActionDescriptor(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(workspaceRecommendationsActionDescriptor, 'Extensions: Show Workspace Recommended Extensions', ExtensionsLabel);
|
||||
|
||||
const popularActionDescriptor = new SyncActionDescriptor(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(popularActionDescriptor, 'Extensions: Show Popular Extensions', ExtensionsLabel);
|
||||
|
||||
|
@ -116,6 +121,9 @@ actionRegistry.registerWorkbenchAction(updateAllActionDescriptor, 'Extensions: U
|
|||
const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel);
|
||||
|
||||
const openExtensionsFileActionDescriptor = new SyncActionDescriptor(ConfigureWorkspaceRecommendationsAction, ConfigureWorkspaceRecommendationsAction.ID, ConfigureWorkspaceRecommendationsAction.LABEL);
|
||||
actionRegistry.registerWorkbenchAction(openExtensionsFileActionDescriptor, 'Extensions: Open Extensions File', ExtensionsLabel);
|
||||
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerConfiguration({
|
||||
id: 'extensions',
|
||||
|
@ -130,3 +138,6 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
const jsonRegistry = <jsonContributionRegistry.IJSONContributionRegistry>Registry.as(jsonContributionRegistry.Extensions.JSONContribution);
|
||||
jsonRegistry.registerSchema(SchemaId, Schema);
|
|
@ -59,8 +59,12 @@ export interface IExtensionsWorkbenchService {
|
|||
canInstall(extension: IExtension): boolean;
|
||||
install(extension: IExtension): TPromise<void>;
|
||||
uninstall(extension: IExtension): TPromise<void>;
|
||||
openExtensionsFile(sideBySide?: boolean): TPromise<void>;
|
||||
}
|
||||
|
||||
export const EXTENSIONS_CONFIGURAION_NAME = 'extensions';
|
||||
|
||||
export interface IExtensionsConfiguration {
|
||||
autoUpdate: boolean;
|
||||
recommendations: string[];
|
||||
}
|
|
@ -458,6 +458,33 @@ export class ShowRecommendedExtensionsAction extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
export class ShowWorkspaceRecommendedExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.showWorkspaceRecommendedExtensions';
|
||||
static LABEL = localize('showWorkspaceRecommendedExtensions', "Show Workspace Recommended Extensions");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IViewletService private viewletService: IViewletService
|
||||
) {
|
||||
super(id, label, null, true);
|
||||
}
|
||||
|
||||
run(): TPromise<void> {
|
||||
return this.viewletService.openViewlet(VIEWLET_ID, true)
|
||||
.then(viewlet => viewlet as IExtensionsViewlet)
|
||||
.then(viewlet => {
|
||||
viewlet.search('@recommended:workspace');
|
||||
viewlet.focus();
|
||||
});
|
||||
}
|
||||
|
||||
protected isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangeSortAction extends Action {
|
||||
|
||||
private query: Query;
|
||||
|
@ -525,4 +552,17 @@ export class OpenExtensionsFolderAction extends Action {
|
|||
protected isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigureWorkspaceRecommendationsAction extends Action {
|
||||
static ID = 'workbench.extensions.action.configureWorkspaceRecommendations';
|
||||
static LABEL = localize('configureWorkspaceRecommendations', "Configure Worksapce Recommendations");
|
||||
|
||||
constructor(id: string, label: string, @IExtensionsWorkbenchService private extensionsService: IExtensionsWorkbenchService) {
|
||||
super(id, label, null, true);
|
||||
}
|
||||
|
||||
public run(event: any): TPromise<any> {
|
||||
return this.extensionsService.openExtensionsFile();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
export const SchemaId = 'vscode://schemas/extensions';
|
||||
export const Schema: IJSONSchema = {
|
||||
id: SchemaId,
|
||||
type: 'object',
|
||||
title: localize('app.extensions.json.title', "Extensions"),
|
||||
properties: {
|
||||
recommendations: {
|
||||
type: 'array',
|
||||
description: localize('app.extensions.json.recommendations', "List of extension recommendations."),
|
||||
items: {
|
||||
'type': 'string',
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Content: string = [
|
||||
'{',
|
||||
'\t// See https://go.microsoft.com/fwlink/?LinkId=733558',
|
||||
'\t// for the documentation about the extensions.json format',
|
||||
'\t"recommendations": [',
|
||||
'\t\t',
|
||||
'\t]',
|
||||
'}'
|
||||
].join('\n');
|
|
@ -30,7 +30,7 @@ import { PagedList } from 'vs/base/browser/ui/list/listPaging';
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Delegate, Renderer } from './extensionsList';
|
||||
import { IExtensionsWorkbenchService, IExtension, IExtensionsViewlet, VIEWLET_ID, ExtensionState } from './extensions';
|
||||
import { ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction } from './extensionsActions';
|
||||
import { ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction } from './extensionsActions';
|
||||
import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, SortBy, SortOrder, IQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionsInput } from './extensionsInput';
|
||||
import { Query } from '../common/extensionQuery';
|
||||
|
@ -168,6 +168,7 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet {
|
|||
this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL),
|
||||
this.instantiationService.createInstance(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL),
|
||||
new Separator(),
|
||||
this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.install', localize('sort by installs', "Sort By: Install Count"), this.onSearchChange, 'installs', undefined),
|
||||
|
@ -230,6 +231,10 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet {
|
|||
case 'desc': options = assign(options, { sortOrder: SortOrder.Descending }); break;
|
||||
}
|
||||
|
||||
if (/@recommended:workspace/i.test(query.value)) {
|
||||
return this.queryWorkspaceRecommendations();
|
||||
}
|
||||
|
||||
if (/@recommended/i.test(query.value)) {
|
||||
const value = query.value.replace(/@recommended/g, '').trim().toLowerCase();
|
||||
|
||||
|
@ -257,6 +262,15 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet {
|
|||
.then(result => new PagedModel(result));
|
||||
}
|
||||
|
||||
private queryWorkspaceRecommendations(): TPromise<PagedModel<IExtension>> {
|
||||
let names = this.tipsService.getWorkspaceRecommendations();
|
||||
if (!names.length) {
|
||||
return TPromise.as(new PagedModel([]));
|
||||
}
|
||||
return this.extensionsWorkbenchService.queryGallery({ names, pageSize: names.length })
|
||||
.then(result => new PagedModel(result));
|
||||
}
|
||||
|
||||
private openExtension(extension: IExtension): void {
|
||||
this.editorService.openEditor(this.instantiationService.createInstance(ExtensionsInput, extension))
|
||||
.done(null, err => this.onError(err));
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
'use strict';
|
||||
|
||||
import 'vs/css!./media/extensionsViewlet';
|
||||
import { localize } from 'vs/nls';
|
||||
import paths = require('vs/base/common/paths');
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { index } from 'vs/base/common/arrays';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
|
@ -21,15 +23,18 @@ import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import * as semver from 'semver';
|
||||
import * as path from 'path';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { readFile } from 'vs/base/node/pfs';
|
||||
import { asText } from 'vs/base/node/request';
|
||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, IExtensionsConfiguration } from './extensions';
|
||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, IExtensionsConfiguration, EXTENSIONS_CONFIGURAION_NAME } from './extensions';
|
||||
import { UpdateAllAction } from './extensionsActions';
|
||||
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Content } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate';
|
||||
|
||||
interface IExtensionStateProvider {
|
||||
(extension: Extension): ExtensionState;
|
||||
|
@ -252,6 +257,9 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
|||
|
||||
constructor(
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IExtensionManagementService private extensionService: IExtensionManagementService,
|
||||
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
|
@ -338,7 +346,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
|||
}
|
||||
|
||||
return this.queryGallery({ ids, pageSize: ids.length }).then(() => {
|
||||
const config = this.configurationService.getConfiguration<IExtensionsConfiguration>('extensions');
|
||||
const config = this.configurationService.getConfiguration<IExtensionsConfiguration>(EXTENSIONS_CONFIGURAION_NAME);
|
||||
|
||||
if (!config.autoUpdate) {
|
||||
return;
|
||||
|
@ -502,6 +510,36 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
|||
this.messageService.show(Severity.Error, err);
|
||||
}
|
||||
|
||||
openExtensionsFile(sideBySide?: boolean): TPromise<any> {
|
||||
if (!this.contextService.getWorkspace()) {
|
||||
this.messageService.show(Severity.Info, localize('ConfigureWorkspaceRecommendations.noWorkspace', 'Recommendations are only available on a workspace folder.'));
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
|
||||
return this.getOrCreateExtensionsFile().then(value => {
|
||||
return this.editorService.openEditor({
|
||||
resource: value.extensionsFileResource,
|
||||
options: {
|
||||
forceOpen: true,
|
||||
pinned: value.created
|
||||
},
|
||||
}, sideBySide);
|
||||
}, (error) => {
|
||||
throw new Error(localize('OpenExtensionsFile.failed', "Unable to create 'extensions.json' file inside the '.vscode' folder ({0}).", error));
|
||||
});
|
||||
}
|
||||
|
||||
private getOrCreateExtensionsFile(): TPromise<{ created: boolean, extensionsFileResource: URI }> {
|
||||
let extensionsFileResource = URI.file(paths.join(this.contextService.getWorkspace().resource.fsPath, '/.vscode/' + EXTENSIONS_CONFIGURAION_NAME + '.json'));
|
||||
return this.fileService.resolveContent(extensionsFileResource).then(content => {
|
||||
return { created: false, extensionsFileResource };
|
||||
}, err => {
|
||||
return this.fileService.updateContent(extensionsFileResource, Content).then(() => {
|
||||
return { created: true, extensionsFileResource };
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue