mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-07 17:44:05 -05:00
Merge branch 'master' into ben/associations
This commit is contained in:
@@ -121,6 +121,7 @@ gulp.task('tslint', function () {
|
||||
});
|
||||
|
||||
var hygiene = exports.hygiene = function (some, options) {
|
||||
options = options || {};
|
||||
var errorCount = 0;
|
||||
|
||||
var eol = es.through(function (file) {
|
||||
|
||||
@@ -168,9 +168,7 @@ function packageTask(platform, arch, opts) {
|
||||
'!extensions/json/server/src/**',
|
||||
'!extensions/json/server/out/**/test/**',
|
||||
'!extensions/json/server/test/**',
|
||||
'!extensions/json/server/typings/**',
|
||||
'!extensions/json/server/node_modules/typescript/**',
|
||||
'!extensions/json/server/node_modules/mocha/**'
|
||||
'!extensions/json/server/typings/**'
|
||||
], { base: '.' });
|
||||
|
||||
var sources = es.merge(src, extensions)
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
],
|
||||
"main": "./client/out/jsonMain",
|
||||
"scripts": {
|
||||
"compile": "gulp compile-extension:json-client && gulp compile-extension:json-server"
|
||||
"compile": "gulp compile-extension:json-client && gulp compile-extension:json-server",
|
||||
"postinstall": "npm --prefix server install server"
|
||||
},
|
||||
"contributes": {
|
||||
"languages": [
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
"vscode-languageserver": "^1.3.0",
|
||||
"vscode-nls": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^2.2.5",
|
||||
"typescript": "^1.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "gulp compile-extension:json-server",
|
||||
"watch": "gulp watch-extension:json-server"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Code",
|
||||
"version": "0.10.11",
|
||||
"version": "0.10.12",
|
||||
"electronVersion": "0.35.6",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
||||
@@ -269,6 +269,14 @@ export class MouseTargetFactory {
|
||||
} else if (hitTestResult.hitTarget) {
|
||||
t = hitTestResult.hitTarget;
|
||||
path = this.getClassNamePathTo(t, this.viewHelper.viewDomNode);
|
||||
|
||||
// TODO@Alex: try again with this different target, but guard against recursion.
|
||||
// Is it a cursor ?
|
||||
var lineNumberAttribute = t.hasAttribute && t.hasAttribute('lineNumber') ? t.getAttribute('lineNumber') : null;
|
||||
var columnAttribute = t.hasAttribute && t.hasAttribute('column') ? t.getAttribute('column') : null;
|
||||
if (lineNumberAttribute && columnAttribute) {
|
||||
return this.createMouseTargetFromViewCursor(t, parseInt(lineNumberAttribute, 10), parseInt(columnAttribute, 10), mouseColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +384,7 @@ export class MouseTargetFactory {
|
||||
|
||||
// Chrome always hits a TEXT_NODE, while Edge sometimes hits a token span
|
||||
let startContainer = range.startContainer;
|
||||
let hitTarget: HTMLElement;
|
||||
|
||||
if (startContainer.nodeType === startContainer.TEXT_NODE) {
|
||||
// startContainer is expected to be the token text
|
||||
@@ -389,6 +398,8 @@ export class MouseTargetFactory {
|
||||
position: this.viewHelper.getPositionFromDOMInfo(<HTMLElement>parent1, range.startOffset),
|
||||
hitTarget: null
|
||||
};
|
||||
} else {
|
||||
hitTarget = <HTMLElement>startContainer.parentNode;
|
||||
}
|
||||
} else if (startContainer.nodeType === startContainer.ELEMENT_NODE) {
|
||||
// startContainer is expected to be the token span
|
||||
@@ -401,12 +412,14 @@ export class MouseTargetFactory {
|
||||
position: this.viewHelper.getPositionFromDOMInfo(<HTMLElement>startContainer, (<HTMLElement>startContainer).textContent.length),
|
||||
hitTarget: null
|
||||
};
|
||||
} else {
|
||||
hitTarget = <HTMLElement>startContainer;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
position: null,
|
||||
hitTarget: <HTMLElement>range.startContainer.parentNode
|
||||
hitTarget: hitTarget
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user