commit
9367be9a58
|
@ -5,6 +5,7 @@
|
|||
|
||||
import {createDecorator, ServiceIdentifier} from 'vs/platform/instantiation/common/instantiation';
|
||||
import {IEventEmitter} from 'vs/base/common/eventEmitter';
|
||||
import Event from 'vs/base/common/event';
|
||||
import winjs = require('vs/base/common/winjs.base');
|
||||
|
||||
export var IConfigurationService = createDecorator<IConfigurationService>('configurationService');
|
||||
|
@ -21,7 +22,12 @@ export interface IConfigurationService extends IEventEmitter {
|
|||
/**
|
||||
* Returns iff the workspace has configuration or not.
|
||||
*/
|
||||
hasWorkspaceConfiguration():boolean;
|
||||
hasWorkspaceConfiguration(): boolean;
|
||||
|
||||
/**
|
||||
* Event that fires when the configuration changes.
|
||||
*/
|
||||
onDidUpdateConfiguration: Event<{ config: any }>
|
||||
}
|
||||
|
||||
export class ConfigurationServiceEventTypes {
|
||||
|
|
|
@ -19,6 +19,7 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
|||
import Files = require('vs/platform/files/common/files');
|
||||
import {IConfigurationRegistry, Extensions} from './configurationRegistry';
|
||||
import {Registry} from 'vs/platform/platform';
|
||||
import Event, {fromEventEmitter} from 'vs/base/common/event';
|
||||
|
||||
|
||||
// ---- service abstract implementation
|
||||
|
@ -43,6 +44,8 @@ interface ILoadConfigResult {
|
|||
export abstract class ConfigurationService extends eventEmitter.EventEmitter implements IConfigurationService, lifecycle.IDisposable {
|
||||
public serviceId = IConfigurationService;
|
||||
|
||||
public onDidUpdateConfiguration: Event<{ config: any }>;
|
||||
|
||||
protected contextService: IWorkspaceContextService;
|
||||
protected eventService: IEventService;
|
||||
protected workspaceSettingsRootFolder: string;
|
||||
|
@ -67,6 +70,8 @@ export abstract class ConfigurationService extends eventEmitter.EventEmitter imp
|
|||
unbind();
|
||||
subscription.dispose();
|
||||
}
|
||||
|
||||
this.onDidUpdateConfiguration = fromEventEmitter(this, ConfigurationServiceEventTypes.UPDATED);
|
||||
}
|
||||
|
||||
protected abstract resolveContents(resource: uri[]): winjs.TPromise<IContent[]>;
|
||||
|
@ -229,6 +234,10 @@ export class NullConfigurationService extends eventEmitter.EventEmitter implemen
|
|||
public hasWorkspaceConfiguration(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public onDidUpdateConfiguration() {
|
||||
return { dispose() { } };
|
||||
}
|
||||
}
|
||||
|
||||
export var nullService = new NullConfigurationService();
|
||||
|
|
|
@ -53,3 +53,11 @@ export interface IExtensionsService {
|
|||
uninstall(extension: IExtension): TPromise<void>;
|
||||
getInstalled(includeDuplicateVersions?: boolean): TPromise<IExtension[]>;
|
||||
}
|
||||
|
||||
export var IExtensionTipsService = createDecorator<IExtensionTipsService>('extensionTipsService');
|
||||
|
||||
export interface IExtensionTipsService {
|
||||
serviceId: ServiceIdentifier<any>;
|
||||
tips: IExtension[];
|
||||
onDidChangeTips: Event<IExtension[]>;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"jrieken.vscode-omnisharp": "{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln}",
|
||||
"msjsdiag.debugger-for-chrome": "{**/*.ts,**/*.tsx**/*.js,**/*.jsx,**/*.es6}",
|
||||
"lukehoban.Go": "**/*.go",
|
||||
"ms-vscode.PowerShell": "{**/*.ps,**/*.ps1}",
|
||||
"austin.code-gnu-global": "{**/*.c,**/*.cpp,**/*.h}",
|
||||
"Ionide.Ionide-fsharp": "{**/*.fsx,**/*.fsi,**/*.fs,**/*.ml,**/*.mli}",
|
||||
"dbaeumer.vscode-eslint": "{**/*.js,**/*.jsx,**/*.es6}",
|
||||
"eg2.tslint": "{**/*.ts,**/*.tsx}"
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/text!vs/workbench/parts/extensions/electron-browser/extensionTips.json';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {onUnexpectedError} from 'vs/base/common/errors';
|
||||
import {values, forEach} from 'vs/base/common/collections';
|
||||
import {IDisposable, disposeAll} from 'vs/base/common/lifecycle';
|
||||
import {TPromise as Promise} from 'vs/base/common/winjs.base';
|
||||
import {match} from 'vs/base/common/glob';
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
|
||||
import {IExtensionsService, IGalleryService, IExtensionTipsService, IExtension} from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import {IModelService} from 'vs/editor/common/services/modelService';
|
||||
import {EventType} from 'vs/editor/common/editorCommon';
|
||||
|
||||
interface ExtensionMap {
|
||||
[id: string]: IExtension;
|
||||
}
|
||||
|
||||
interface ExtensionData {
|
||||
[id: string]: string;
|
||||
}
|
||||
|
||||
enum ExtensionTipReasons {
|
||||
// FileExists = 1
|
||||
FileOpened = 2,
|
||||
FileEdited = 3
|
||||
}
|
||||
|
||||
class ExtensionTip {
|
||||
|
||||
private _resources: { [uri: string]: ExtensionTipReasons } = Object.create(null);
|
||||
private _touched = Date.now();
|
||||
private _score = -1;
|
||||
|
||||
constructor(public extension: IExtension) {
|
||||
//
|
||||
}
|
||||
|
||||
resource(uri: URI, reason: ExtensionTipReasons): boolean {
|
||||
if (reason !== this._resources[uri.toString()]) {
|
||||
this._touched = Date.now();
|
||||
this._resources[uri.toString()] = Math.max((this._resources[uri.toString()] || 0), reason);
|
||||
this._score = - 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
get score() {
|
||||
if (this._score === -1) {
|
||||
forEach(this._resources, entry => this._score += entry.value);
|
||||
}
|
||||
return this._score;
|
||||
}
|
||||
|
||||
compareTo(tip: ExtensionTip): number {
|
||||
if (this === tip) {
|
||||
return 0;
|
||||
}
|
||||
let result = tip._touched - this._touched;
|
||||
if (result === 0) {
|
||||
result = tip.score - this.score;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionTipsService implements IExtensionTipsService {
|
||||
|
||||
serviceId: any;
|
||||
|
||||
private _onDidChangeTips: Emitter<IExtension[]> = new Emitter<IExtension[]>();
|
||||
private _tips: { [id: string]: ExtensionTip } = Object.create(null);
|
||||
private _disposeOnUpdate: IDisposable[] = [];
|
||||
private _availableExtensions: Promise<ExtensionMap>;
|
||||
private _extensionData: Promise<ExtensionData>;
|
||||
|
||||
constructor(
|
||||
@IExtensionsService private _extensionService: IExtensionsService,
|
||||
@IGalleryService private _galleryService: IGalleryService,
|
||||
@IModelService private _modelService: IModelService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService
|
||||
) {
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._disposeOnUpdate = disposeAll(this._disposeOnUpdate);
|
||||
}
|
||||
|
||||
get onDidChangeTips(): Event<IExtension[]> {
|
||||
return this._onDidChangeTips.event;
|
||||
}
|
||||
|
||||
get tips(): IExtension[] {
|
||||
let tips = values(this._tips);
|
||||
tips.sort((a, b) => a.compareTo(b));
|
||||
return tips.map(tip => tip.extension);
|
||||
}
|
||||
|
||||
// --- internals
|
||||
|
||||
private _updateState(): void {
|
||||
|
||||
// check with configuration service and then GO
|
||||
this._disposeOnUpdate = disposeAll(this._disposeOnUpdate);
|
||||
this._tips = Object.create(null);
|
||||
this._onDidChangeTips.fire(this.tips);
|
||||
|
||||
this._configurationService.loadConfiguration('extensions').then(value => {
|
||||
if (value && value.showTips === true) {
|
||||
this._init();
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
|
||||
// listen for config changes
|
||||
this._configurationService.onDidUpdateConfiguration(this._updateState, this, this._disposeOnUpdate);
|
||||
}
|
||||
|
||||
private _init():void {
|
||||
|
||||
if (!this._galleryService.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._extensionData = new Promise((resolve, reject) => {
|
||||
require(['vs/text!vs/workbench/parts/extensions/electron-browser/extensionTips.json'],
|
||||
data => resolve(JSON.parse(data)),
|
||||
reject);
|
||||
});
|
||||
|
||||
this._availableExtensions = this._getAvailableExtensions();
|
||||
|
||||
// don't suggest what got installed
|
||||
this._disposeOnUpdate.push(this._extensionService.onDidInstallExtension(ext => {
|
||||
const id = `${ext.publisher}.${ext.name}`;
|
||||
let change = false;
|
||||
if (delete this._tips[id]) {
|
||||
change = true;
|
||||
}
|
||||
if (change) {
|
||||
this._onDidChangeTips.fire(this.tips);
|
||||
}
|
||||
this._availableExtensions = this._getAvailableExtensions();
|
||||
}));
|
||||
|
||||
// we listen for editor models being added and changed
|
||||
// when a model is added it gives 2 points, a change gives 3 points
|
||||
// such that files you type have bigger impact on the suggest
|
||||
// order than those you only look at
|
||||
const modelListener: { [uri: string]: IDisposable } = Object.create(null);
|
||||
this._disposeOnUpdate.push({ dispose() { disposeAll(values(modelListener)) } });
|
||||
|
||||
this._disposeOnUpdate.push(this._modelService.onModelAdded(model => {
|
||||
const uri = model.getAssociatedResource();
|
||||
this._suggestByResource(uri, ExtensionTipReasons.FileOpened);
|
||||
modelListener[uri.toString()] = model.addListener2(EventType.ModelContentChanged2,
|
||||
() => this._suggestByResource(uri, ExtensionTipReasons.FileEdited));
|
||||
}));
|
||||
|
||||
this._disposeOnUpdate.push(this._modelService.onModelRemoved(model => {
|
||||
const subscription = modelListener[model.getAssociatedResource().toString()];
|
||||
if (subscription) {
|
||||
subscription.dispose();
|
||||
delete modelListener[model.getAssociatedResource().toString()];
|
||||
}
|
||||
}));
|
||||
|
||||
for (let model of this._modelService.getModels()) {
|
||||
this._suggestByResource(model.getAssociatedResource(), ExtensionTipReasons.FileOpened);
|
||||
}
|
||||
}
|
||||
|
||||
private _getAvailableExtensions(): Promise<ExtensionMap> {
|
||||
return this._galleryService.query().then(extensions => {
|
||||
let map: ExtensionMap = Object.create(null);
|
||||
for (let ext of extensions) {
|
||||
map[`${ext.publisher}.${ext.name}`] = ext;
|
||||
}
|
||||
|
||||
return this._extensionService.getInstalled().then(installed => {
|
||||
for (let ext of installed) {
|
||||
delete map[`${ext.publisher}.${ext.name}`];
|
||||
}
|
||||
return map;
|
||||
});
|
||||
}, () => {
|
||||
return Object.create(null);
|
||||
});
|
||||
}
|
||||
|
||||
// --- suggest logic
|
||||
|
||||
private _suggestByResource(uri: URI, reason: ExtensionTipReasons): Promise<any> {
|
||||
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.join<any>([this._availableExtensions, this._extensionData]).then(all => {
|
||||
let extensions = <ExtensionMap>all[0];
|
||||
let data = <ExtensionData>all[1];
|
||||
|
||||
let change = false;
|
||||
forEach(data, entry => {
|
||||
let extension = extensions[entry.key];
|
||||
if (extension && match(entry.value, uri.fsPath)) {
|
||||
let value = this._tips[entry.key];
|
||||
if (!value) {
|
||||
value = this._tips[entry.key] = new ExtensionTip(extension);
|
||||
}
|
||||
if (value.resource(uri, reason)) {
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (change) {
|
||||
this._onDidChangeTips.fire(this.tips);
|
||||
}
|
||||
}, () => {
|
||||
// ignore
|
||||
});
|
||||
}
|
||||
}
|
|
@ -3,14 +3,16 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import platform = require('vs/platform/platform');
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar');
|
||||
import { ExtensionsStatusbarItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets';
|
||||
import { ExtensionsStatusbarItem, ExtensionTipsStatusbarItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets';
|
||||
import { IGalleryService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { GalleryService } from 'vs/workbench/parts/extensions/node/vsoGalleryService';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { ExtensionsWorkbenchExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension';
|
||||
import ConfigurationRegistry = require('vs/platform/configuration/common/configurationRegistry');
|
||||
|
||||
// Register Gallery Service
|
||||
registerSingleton(IGalleryService, GalleryService);
|
||||
|
@ -25,4 +27,24 @@ registerSingleton(IGalleryService, GalleryService);
|
|||
ExtensionsStatusbarItem,
|
||||
statusbar.StatusbarAlignment.LEFT,
|
||||
10 /* Low Priority */
|
||||
));
|
||||
));
|
||||
|
||||
// Register Statusbar item
|
||||
(<statusbar.IStatusbarRegistry>platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor(
|
||||
ExtensionTipsStatusbarItem,
|
||||
statusbar.StatusbarAlignment.LEFT,
|
||||
9 /* Low Priority */
|
||||
));
|
||||
|
||||
|
||||
(<ConfigurationRegistry.IConfigurationRegistry>platform.Registry.as(ConfigurationRegistry.Extensions.Configuration)).registerConfiguration({
|
||||
id: 'extensions',
|
||||
type: 'object',
|
||||
properties: {
|
||||
'extensions.showTips': {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: nls.localize('extConfig', "Suggest extensions based on changed and open files."),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -83,6 +83,29 @@ export class ListOutdatedExtensionsAction extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
export class ListSuggestedExtensionsAction extends Action {
|
||||
|
||||
static ID = 'workbench.extensions.action.listSuggestedExtensions';
|
||||
static LABEL = nls.localize('showExtensionTips', "Show Extension Tips");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IExtensionsService private extensionsService: IExtensionsService,
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService
|
||||
) {
|
||||
super(id, label, null, true);
|
||||
}
|
||||
|
||||
public run(): Promise {
|
||||
return this.quickOpenService.show('ext tips ');
|
||||
}
|
||||
|
||||
protected isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class InstallAction extends Action {
|
||||
|
||||
constructor(
|
||||
|
|
|
@ -17,7 +17,7 @@ import { since } from 'vs/base/common/dates';
|
|||
import { matchesContiguousSubString } from 'vs/base/common/filters';
|
||||
import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
|
||||
import { IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { IExtensionsService, IGalleryService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IExtensionsService, IGalleryService, IExtensionTipsService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { InstallAction, UninstallAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
@ -531,4 +531,71 @@ export class OutdatedExtensionsHandler extends QuickOpenHandler {
|
|||
getAutoFocus(searchValue: string): IAutoFocus {
|
||||
return { autoFocusFirstEntry: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SuggestedExtensionsModel implements IModel<IExtensionEntry> {
|
||||
|
||||
public dataSource = new DataSource();
|
||||
public renderer: IRenderer<IExtensionEntry>;
|
||||
public runner: IRunner<IExtensionEntry>;
|
||||
public entries: IExtensionEntry[];
|
||||
|
||||
constructor(
|
||||
private suggestedExtensions: IExtension[],
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
) {
|
||||
this.renderer = instantiationService.createInstance(Renderer);
|
||||
this.runner = instantiationService.createInstance(InstallRunner);
|
||||
this.entries = [];
|
||||
}
|
||||
|
||||
public set input(input: string) {
|
||||
this.entries = this.suggestedExtensions
|
||||
.map(extension => ({ extension, highlights: getHighlights(input, extension) }))
|
||||
.filter(({ highlights }) => !!highlights)
|
||||
.map(({ extension, highlights }: { extension: IExtension, highlights: IHighlights }) => {
|
||||
|
||||
return {
|
||||
extension,
|
||||
highlights,
|
||||
state: ExtensionState.Uninstalled
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class SuggestedExtensionHandler extends QuickOpenHandler {
|
||||
|
||||
private model: SuggestedExtensionsModel;
|
||||
|
||||
constructor(
|
||||
@IExtensionTipsService private extensionTipsService: IExtensionTipsService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
getResults(input: string): TPromise<IModel<IExtensionEntry>> {
|
||||
if (!this.model) {
|
||||
this.model = this.instantiationService.createInstance(
|
||||
SuggestedExtensionsModel,
|
||||
this.extensionTipsService.tips);
|
||||
}
|
||||
this.model.input = input;
|
||||
return TPromise.as(this.model);
|
||||
}
|
||||
|
||||
onClose(canceled: boolean): void {
|
||||
this.model = null;
|
||||
}
|
||||
|
||||
getEmptyLabel(input: string): string {
|
||||
return nls.localize('noSuggestedExtensions', "No suggested extensions");
|
||||
}
|
||||
|
||||
getAutoFocus(searchValue: string): IAutoFocus {
|
||||
return { autoFocusFirstEntry: true };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import nls = require('vs/nls');
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import {forEach} from 'vs/base/common/collections';
|
||||
import errors = require('vs/base/common/errors');
|
||||
import dom = require('vs/base/browser/dom');
|
||||
import lifecycle = require('vs/base/common/lifecycle');
|
||||
|
@ -13,9 +14,11 @@ import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar');
|
|||
import { IPluginService, IPluginStatus } from 'vs/platform/plugins/common/plugins';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { UninstallAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
|
||||
import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
|
||||
import { IExtensionsService, IGalleryService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IExtensionsService, IGalleryService, IExtension, IExtensionTipsService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
|
||||
|
||||
var $ = dom.emmet;
|
||||
|
||||
|
@ -83,3 +86,69 @@ export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionTipsStatusbarItem implements statusbar.IStatusbarItem {
|
||||
|
||||
private static _dontSuggestAgainTimeout = 1000 * 60 * 60 * 24 * 28; // 4 wks
|
||||
|
||||
private _domNode: HTMLElement;
|
||||
private _label: OcticonLabel;
|
||||
|
||||
constructor(
|
||||
@IQuickOpenService private _quickOpenService: IQuickOpenService,
|
||||
@IExtensionTipsService private _extensionTipsService: IExtensionTipsService,
|
||||
@IStorageService private _storageService: IStorageService
|
||||
) {
|
||||
|
||||
const previousTips = <{ [id: string]: number }>JSON.parse(this._storageService.get('extensionsAssistant/tips', StorageScope.GLOBAL, '{}'));
|
||||
|
||||
// forget previous tips after 28 days
|
||||
const now = Date.now();
|
||||
forEach(previousTips, (entry, rm) => {
|
||||
if (now - entry.value > ExtensionTipsStatusbarItem._dontSuggestAgainTimeout) {
|
||||
rm();
|
||||
}
|
||||
});
|
||||
|
||||
function extid(ext: IExtension): string {
|
||||
return `${ext.publisher}.${ext.name}@${ext.version}`;
|
||||
};
|
||||
|
||||
this._extensionTipsService.onDidChangeTips(tips => {
|
||||
|
||||
if (tips.length === 0) {
|
||||
dom.removeClass(this._domNode, 'active');
|
||||
return;
|
||||
}
|
||||
|
||||
// check for new tips
|
||||
let hasNewTips = false;
|
||||
for (let tip of tips) {
|
||||
const id = extid(tip);
|
||||
if (!previousTips[id]) {
|
||||
previousTips[id] = Date.now();
|
||||
hasNewTips = true;
|
||||
}
|
||||
}
|
||||
if (hasNewTips) {
|
||||
dom.addClass(this._domNode, 'active');
|
||||
this._storageService.store('extensionsAssistant/tips', JSON.stringify(previousTips), StorageScope.GLOBAL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): lifecycle.IDisposable {
|
||||
|
||||
this._domNode = document.createElement('a');
|
||||
this._domNode.className = 'extensions-suggestions';
|
||||
this._label = new OcticonLabel(this._domNode);
|
||||
this._label.text = '$(light-bulb) extension tips';
|
||||
container.appendChild(this._domNode);
|
||||
|
||||
return dom.addDisposableListener(this._domNode, 'click', event => this._onClick(event));
|
||||
}
|
||||
|
||||
private _onClick(event: MouseEvent): void {
|
||||
this._quickOpenService.show('ext tips ').then(() => dom.removeClass(this._domNode, 'active'));
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import errors = require('vs/base/common/errors');
|
|||
import platform = require('vs/platform/platform');
|
||||
import { Promise } from 'vs/base/common/winjs.base';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IExtensionsService, IGalleryService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IExtensionsService, IGalleryService, IExtensionTipsService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
@ -16,7 +16,8 @@ import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common
|
|||
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
|
||||
import wbaregistry = require('vs/workbench/common/actionRegistry');
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ListExtensionsAction, InstallExtensionAction, ListOutdatedExtensionsAction } from './extensionsActions';
|
||||
import { ListExtensionsAction, InstallExtensionAction, ListOutdatedExtensionsAction, ListSuggestedExtensionsAction } from './extensionsActions';
|
||||
import { ExtensionTipsService } from './extensionTipsService';
|
||||
import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||
import { checkForLegacyExtensionNeeds } from './extensionsAssistant';
|
||||
import {ipcRenderer as ipc} from 'electron';
|
||||
|
@ -42,6 +43,9 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
|
|||
this.install(options.extensionsToInstall).done(null, errors.onUnexpectedError);
|
||||
}
|
||||
|
||||
// add service
|
||||
instantiationService.addSingleton(IExtensionTipsService, this.instantiationService.createInstance(ExtensionTipsService));
|
||||
|
||||
const extensionsCategory = nls.localize('extensionsCategory', "Extensions");
|
||||
const actionRegistry = (<wbaregistry.IWorkbenchActionRegistry> platform.Registry.as(wbaregistry.Extensions.WorkbenchActions));
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListExtensionsAction, ListExtensionsAction.ID, ListExtensionsAction.LABEL), extensionsCategory);
|
||||
|
@ -79,6 +83,18 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
|
|||
nls.localize('outdatedExtensionsCommands', "Update Outdated Extensions")
|
||||
)
|
||||
);
|
||||
|
||||
// add extension tips services
|
||||
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListSuggestedExtensionsAction, ListSuggestedExtensionsAction.ID, ListSuggestedExtensionsAction.LABEL), extensionsCategory);
|
||||
|
||||
(<IQuickOpenRegistry>platform.Registry.as(Extensions.Quickopen)).registerQuickOpenHandler(
|
||||
new QuickOpenHandlerDescriptor(
|
||||
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
|
||||
'SuggestedExtensionHandler',
|
||||
'ext tips ',
|
||||
nls.localize('suggestedExtensionsCommands', "Show Extension Tips")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -132,3 +132,19 @@
|
|||
background-size: 14px;
|
||||
background-position: 4px 50%;
|
||||
}
|
||||
|
||||
.monaco-shell .extensions-suggestions {
|
||||
visibility: hidden;
|
||||
padding: 0 5px 0 5px;
|
||||
-webkit-transition: visibility 250ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
transition: visibility 250ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.monaco-shell .extensions-suggestions.active {
|
||||
visibility: inherit;
|
||||
background-color: rgba(76, 119, 76, 0.9);
|
||||
}
|
||||
|
||||
.monaco-shell .extensions-suggestions > .octicon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
|
@ -514,4 +514,8 @@ export class TestConfigurationService extends EventEmitter.EventEmitter implemen
|
|||
public hasWorkspaceConfiguration():boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public onDidUpdateConfiguration() {
|
||||
return { dispose() { } };
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue