提交 9c51f1a8 编写于 作者: J Joao Moreno

remove extension tips status bar contribution

fixes #3633
上级 9f881a08
......@@ -5,6 +5,7 @@
import 'vs/text!vs/workbench/parts/extensions/electron-browser/extensionTips.json';
import URI from 'vs/base/common/uri';
import {toObject} from 'vs/base/common/objects';
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';
......@@ -30,8 +31,8 @@ enum ExtensionTipReasons {
class ExtensionTip {
private _resources: { [uri: string]: ExtensionTipReasons } = Object.create(null);
private _touched = Date.now();
private resources: { [uri: string]: ExtensionTipReasons } = Object.create(null);
private touched = Date.now();
private _score = -1;
constructor(public extension: IExtension) {
......@@ -39,9 +40,9 @@ class ExtensionTip {
}
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);
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;
}
......@@ -49,7 +50,7 @@ class ExtensionTip {
get score() {
if (this._score === -1) {
forEach(this._resources, entry => this._score += entry.value);
forEach(this.resources, entry => this._score += entry.value);
}
return this._score;
}
......@@ -58,7 +59,7 @@ class ExtensionTip {
if (this === tip) {
return 0;
}
let result = tip._touched - this._touched;
let result = tip.touched - this.touched;
if (result === 0) {
result = tip.score - this.score;
}
......@@ -72,20 +73,20 @@ export class ExtensionTipsService implements IExtensionTipsService {
private _onDidChangeTips: Emitter<IExtension[]> = new Emitter<IExtension[]>();
private _tips: { [id: string]: ExtensionTip } = Object.create(null);
private _disposables: IDisposable[] = [];
private _availableExtensions: Promise<ExtensionMap>;
private _extensionData: Promise<ExtensionData>;
private disposables: IDisposable[] = [];
private availableExtensions: Promise<ExtensionMap>;
private extensionData: Promise<ExtensionData>;
constructor(
@IExtensionsService private _extensionService: IExtensionsService,
@IGalleryService private _galleryService: IGalleryService,
@IModelService private _modelService: IModelService
@IExtensionsService private extensionService: IExtensionsService,
@IGalleryService private galleryService: IGalleryService,
@IModelService private modelService: IModelService
) {
this._init();
this.init();
}
dispose() {
this._disposables = disposeAll(this._disposables);
this.disposables = disposeAll(this.disposables);
}
get onDidChangeTips(): Event<IExtension[]> {
......@@ -100,48 +101,35 @@ export class ExtensionTipsService implements IExtensionTipsService {
// --- internals
private _init():void {
private init():void {
if (!this._galleryService.isEnabled()) {
if (!this.galleryService.isEnabled()) {
return;
}
this._extensionData = new Promise((resolve, reject) => {
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._disposables.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();
}));
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._disposables.push({ dispose() { disposeAll(values(modelListener)); } });
this.disposables.push({ dispose() { disposeAll(values(modelListener)); } });
this._disposables.push(this._modelService.onModelAdded(model => {
this.disposables.push(this.modelService.onModelAdded(model => {
const uri = model.getAssociatedResource();
this._suggestByResource(uri, ExtensionTipReasons.FileOpened);
this.suggestByResource(uri, ExtensionTipReasons.FileOpened);
modelListener[uri.toString()] = model.addListener2(EventType.ModelContentChanged2,
() => this._suggestByResource(uri, ExtensionTipReasons.FileEdited));
() => this.suggestByResource(uri, ExtensionTipReasons.FileEdited));
}));
this._disposables.push(this._modelService.onModelRemoved(model => {
this.disposables.push(this.modelService.onModelRemoved(model => {
const subscription = modelListener[model.getAssociatedResource().toString()];
if (subscription) {
subscription.dispose();
......@@ -149,38 +137,25 @@ export class ExtensionTipsService implements IExtensionTipsService {
}
}));
for (let model of this._modelService.getModels()) {
this._suggestByResource(model.getAssociatedResource(), ExtensionTipReasons.FileOpened);
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);
});
private getAvailableExtensions(): Promise<ExtensionMap> {
return this.galleryService.query()
.then(null, () => [])
.then(extensions => toObject(extensions, ext => `${ext.publisher}.${ext.name}`));
}
// --- suggest logic
private _suggestByResource(uri: URI, reason: ExtensionTipReasons): Promise<any> {
private suggestByResource(uri: URI, reason: ExtensionTipReasons): Promise<any> {
if (!uri) {
return;
}
Promise.join<any>([this._availableExtensions, this._extensionData]).then(all => {
Promise.join<any>([this.availableExtensions, this.extensionData]).then(all => {
let extensions = <ExtensionMap>all[0];
let data = <ExtensionData>all[1];
......
......@@ -3,16 +3,14 @@
* 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, ExtensionTipsStatusbarItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets';
import { ExtensionsStatusbarItem } 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);
......@@ -28,23 +26,3 @@ registerSingleton(IGalleryService, GalleryService);
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: true,
description: nls.localize('extConfig', "Suggest extensions based on changed and open files."),
}
}
});
......@@ -86,7 +86,7 @@ export class ListOutdatedExtensionsAction extends Action {
export class ListSuggestedExtensionsAction extends Action {
static ID = 'workbench.extensions.action.listSuggestedExtensions';
static LABEL = nls.localize('showExtensionTips', "Show Extension Tips");
static LABEL = nls.localize('showExtensionRecommendations', "Show Extension Recommendations");
constructor(
id: string,
......
......@@ -18,7 +18,6 @@ import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { IExtensionsService, IGalleryService, IExtensionTipsService, IExtension } from 'vs/workbench/parts/extensions/common/extensions';
import { InstallAction, UninstallAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IMessageService } from 'vs/platform/message/common/message';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
......@@ -559,6 +558,7 @@ class SuggestedExtensionsModel implements IModel<IExtensionEntry> {
constructor(
private suggestedExtensions: IExtension[],
private localExtensions: IExtension[],
@IInstantiationService instantiationService: IInstantiationService
) {
this.renderer = instantiationService.createInstance(Renderer);
......@@ -569,9 +569,11 @@ class SuggestedExtensionsModel implements IModel<IExtensionEntry> {
public set input(input: string) {
this.entries = this.suggestedExtensions
.map(extension => ({ extension, highlights: getHighlights(input.trim(), extension) }))
.filter(({ highlights }) => !!highlights)
.filter(({ extension, highlights }) => {
const local = this.localExtensions.filter(local => extensionEquals(local, extension))[0];
return !local && !!highlights;
})
.map(({ extension, highlights }: { extension: IExtension, highlights: IHighlights }) => {
return {
extension,
highlights,
......@@ -590,30 +592,22 @@ export class SuggestedExtensionHandler extends QuickOpenHandler {
@IExtensionTipsService private extensionTipsService: IExtensionTipsService,
@IInstantiationService private instantiationService: IInstantiationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IExtensionsService extensionService: IExtensionsService,
@ILifecycleService lifecycleService:ILifecycleService
@IExtensionsService private extensionsService: IExtensionsService
) {
super();
const subscription = extensionService.onInstallExtension(manifest => {
if (this.model) { // indicates that tips currently show
this.telemetryService.publicLog('extensionGallery:tips', { installed: true });
}
});
lifecycleService.onShutdown(() => subscription.dispose());
}
getResults(input: string): TPromise<IModel<IExtensionEntry>> {
if (!this.model) {
const {tips} = this.extensionTipsService;
this.telemetryService.publicLog('extensionGallery:tips', { count: tips.length });
this.model = this.instantiationService.createInstance(
return this.extensionsService.getInstalled().then(localExtensions => {
const model = this.instantiationService.createInstance(
SuggestedExtensionsModel,
tips);
}
this.model.input = input;
return TPromise.as(this.model);
this.extensionTipsService.tips,
localExtensions
);
model.input = input;
return model;
});
}
onClose(canceled: boolean): void {
......@@ -621,7 +615,7 @@ export class SuggestedExtensionHandler extends QuickOpenHandler {
}
getEmptyLabel(input: string): string {
return nls.localize('noSuggestedExtensions', "No suggested extensions");
return nls.localize('noRecommendedExtensions', "No recommended extensions");
}
getAutoFocus(searchValue: string): IAutoFocus {
......
......@@ -5,7 +5,6 @@
import nls = require('vs/nls');
import Severity from 'vs/base/common/severity';
import {forEach} from 'vs/base/common/collections';
import dom = require('vs/base/browser/dom');
import lifecycle = require('vs/base/common/lifecycle');
import {onUnexpectedError} from 'vs/base/common/errors';
......@@ -13,14 +12,9 @@ import { Action } from 'vs/base/common/actions';
import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar');
import { IExtensionService, IExtensionsStatus } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
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, IExtension, IExtensionTipsService } from 'vs/workbench/parts/extensions/common/extensions';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { IExtensionsService } from 'vs/workbench/parts/extensions/common/extensions';
var $ = dom.emmet;
......@@ -87,91 +81,4 @@ export class ExtensionsStatusbarItem implements statusbar.IStatusbarItem {
dispose: () => lifecycle.disposeAll(this.toDispose)
};
}
}
export class ExtensionTipsStatusbarItem implements statusbar.IStatusbarItem {
private static _dontSuggestAgainTimeout = 1000 * 60 * 60 * 24 * 28; // 4 wks
private _domNode: HTMLElement;
private _label: OcticonLabel;
private _previousTips: { [id: string]: number };
constructor(
@IQuickOpenService private _quickOpenService: IQuickOpenService,
@IExtensionTipsService private _extensionTipsService: IExtensionTipsService,
@IStorageService private _storageService: IStorageService,
@IConfigurationService private _configurationService: IConfigurationService,
@ITelemetryService private _telemetryService: ITelemetryService
) {
// previously shown tips, not older than 28 days
this._previousTips = JSON.parse(this._storageService.get('extensionsAssistant/tips', StorageScope.GLOBAL, '{}'));
const now = Date.now();
forEach(this._previousTips, (entry, rm) => {
if (now - entry.value > ExtensionTipsStatusbarItem._dontSuggestAgainTimeout) {
rm();
}
});
// show/hide tips depending on configuration
let localDispose: lifecycle.Disposable[] = [];
let update = () => {
localDispose = lifecycle.disposeAll(localDispose);
this._configurationService.loadConfiguration('extensions').then(value => {
if (value && value.showTips === true) {
this._extensionTipsService.onDidChangeTips(this._onTips, this, localDispose);
this._onTips(this._extensionTipsService.tips);
} else {
this._onTips([]);
}
}, onUnexpectedError);
this._configurationService.onDidUpdateConfiguration(update, this, localDispose);
};
update();
}
private _onTips(tips: IExtension[]): void {
if (!this._domNode) {
return;
}
if (tips.length === 0) {
dom.addClass(this._domNode, 'disabled');
return;
}
function extid(ext: IExtension): string {
return `${ext.publisher}.${ext.name}@${ext.version}`;
}
// check for new tips
let hasNewTips = false;
for (let tip of tips) {
const id = extid(tip);
if (!this._previousTips[id]) {
this._previousTips[id] = Date.now();
hasNewTips = true;
}
}
if (hasNewTips) {
dom.removeClass(this._domNode, 'disabled');
this._telemetryService.publicLog('extensionGallery:tips', { hintingTips: true });
}
}
public render(container: HTMLElement): lifecycle.IDisposable {
this._domNode = document.createElement('a');
this._domNode.className = 'extensions-suggestions disabled';
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._storageService.store('extensionsAssistant/tips', JSON.stringify(this._previousTips), StorageScope.GLOBAL);
this._telemetryService.publicLog('extensionGallery:tips', { revealingTips: true });
this._quickOpenService.show('ext tips ').then(() => dom.addClass(this._domNode, 'disabled'));
}
}
\ No newline at end of file
......@@ -92,7 +92,7 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
'SuggestedExtensionHandler',
'ext tips ',
nls.localize('suggestedExtensionsCommands', "Show Extension Tips")
nls.localize('suggestedExtensionsCommands', "Show Extension Recommendations")
)
);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册