diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index ff2b745d94c..634efcc741d 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -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) { diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 70b52cccbe6..21627b6da67 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -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) diff --git a/extensions/json/package.json b/extensions/json/package.json index 563ea6c7a09..23065673ed2 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -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": [ diff --git a/extensions/json/server/package.json b/extensions/json/server/package.json index d9fb8aa6814..c86c575b8ff 100644 --- a/extensions/json/server/package.json +++ b/extensions/json/server/package.json @@ -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" diff --git a/package.json b/package.json index 4d7edf1eaf7..91974352420 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Code", - "version": "0.10.11", + "version": "0.10.12", "electronVersion": "0.35.6", "author": { "name": "Microsoft Corporation" diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index e9684bccb0e..78d085ac1e7 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -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(parent1, range.startOffset), hitTarget: null }; + } else { + hitTarget = 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(startContainer, (startContainer).textContent.length), hitTarget: null }; + } else { + hitTarget = startContainer; } } return { position: null, - hitTarget: range.startContainer.parentNode + hitTarget: hitTarget }; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 5a5dab91250..1715dd4be49 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -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 = new Emitter(); private _tips: { [id: string]: ExtensionTip } = Object.create(null); - private _disposables: IDisposable[] = []; - private _availableExtensions: Promise; - private _extensionData: Promise; + private disposables: IDisposable[] = []; + private availableExtensions: Promise; + private extensionData: Promise; 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 { @@ -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 { - 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 { + return this.galleryService.query() + .then(null, () => []) + .then(extensions => toObject(extensions, ext => `${ext.publisher}.${ext.name}`)); } // --- suggest logic - private _suggestByResource(uri: URI, reason: ExtensionTipReasons): Promise { - + private suggestByResource(uri: URI, reason: ExtensionTipReasons): Promise { if (!uri) { return; } - Promise.join([this._availableExtensions, this._extensionData]).then(all => { + Promise.join([this.availableExtensions, this.extensionData]).then(all => { let extensions = all[0]; let data = all[1]; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index c42156773d1..fcffb70e5ba 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -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 -(platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( - ExtensionTipsStatusbarItem, - statusbar.StatusbarAlignment.LEFT, - 9 /* Low Priority */ -)); - - -(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."), - } - } -}); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 5586f48eb9b..951d79b161d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -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, diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen.ts index e56cf424a22..5a20c572bf7 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen.ts @@ -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 { constructor( private suggestedExtensions: IExtension[], + private localExtensions: IExtension[], @IInstantiationService instantiationService: IInstantiationService ) { this.renderer = instantiationService.createInstance(Renderer); @@ -569,9 +569,11 @@ class SuggestedExtensionsModel implements IModel { 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> { - 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 { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts index 222a7d5a558..cee13bf01cf 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -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 diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts index e42d5724ad3..e026be3b704 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts @@ -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") ) ); }