From e989a259d3460607e3513c55be1651e60cff9fec Mon Sep 17 00:00:00 2001 From: Maryam Archie Date: Mon, 3 Jul 2017 15:28:15 -0400 Subject: [PATCH 01/96] Show git clone progress in status bar and source control feed --- extensions/git/src/commands.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index fa93cd06bf5..85e732cbc77 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,7 +5,7 @@ 'use strict'; -import { Uri, commands, scm, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn } from 'vscode'; +import { Uri, commands, scm, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation } from 'vscode'; import { Ref, RefType, Git, GitErrorCodes, Branch } from './git'; import { Model, Resource, Status, CommitOptions, WorkingTreeGroup, IndexGroup, MergeGroup } from './model'; import { toGitUri, fromGitUri } from './uri'; @@ -253,7 +253,8 @@ export class CommandCenter { } const clonePromise = this.git.clone(url, parentPath); - window.setStatusBarMessage(localize('cloning', "Cloning git repository..."), clonePromise); + window.withProgress({location: ProgressLocation.SourceControl, title: localize('cloning', "Cloning git repository...")}, () => clonePromise); + window.withProgress({location: ProgressLocation.Window, title: localize('cloning', "Cloning git repository...")}, () => clonePromise); try { const repositoryPath = await clonePromise; From 263278aaedb9dc43e055e13705743008a41ef8b7 Mon Sep 17 00:00:00 2001 From: Maryam Archie Date: Mon, 3 Jul 2017 16:08:08 -0400 Subject: [PATCH 02/96] Resolve timeout issue --- extensions/git/src/commands.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 85e732cbc77..a78a1fd4adc 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -253,10 +253,12 @@ export class CommandCenter { } const clonePromise = this.git.clone(url, parentPath); - window.withProgress({location: ProgressLocation.SourceControl, title: localize('cloning', "Cloning git repository...")}, () => clonePromise); - window.withProgress({location: ProgressLocation.Window, title: localize('cloning', "Cloning git repository...")}, () => clonePromise); + try { + window.withProgress({ location: ProgressLocation.SourceControl, title: localize('cloning', "Cloning git repository...") }, () => clonePromise); + window.withProgress({ location: ProgressLocation.Window, title: localize('cloning', "Cloning git repository...") }, () => clonePromise); + const repositoryPath = await clonePromise; const open = localize('openrepo', "Open Repository"); From b9ed241eb3e3f1b5cd941911e4c0e9e110b7535e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 18 Jul 2017 09:42:13 +0100 Subject: [PATCH 03/96] Added oxford comma and fixed macOS spelling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a10b463101..df87a4eea0b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ a code editor with what developers need for their core edit-build-debug cycle. Code provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools. -VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily. +VS Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.

VS Code in action From 87e2e7d8a3b1a171b8ea0a3fa0b60cd622ffa77d Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 11 Aug 2017 15:37:40 +0200 Subject: [PATCH 04/96] report uninstall telemetry --- .../common/extensionManagement.ts | 5 ++++ .../node/extensionGalleryService.ts | 19 ++++++++++++- .../node/extensionManagementService.ts | 28 ++++++++++--------- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index eebeee7101a..3e241e50808 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -195,12 +195,17 @@ export interface IQueryOptions { sortOrder?: SortOrder; } +export enum StatisticType { + Uninstall = 'uninstall' +} + export interface IExtensionGalleryService { _serviceBrand: any; isEnabled(): boolean; getRequestHeaders(): TPromise<{ [key: string]: string; }>; query(options?: IQueryOptions): TPromise>; download(extension: IGalleryExtension): TPromise; + reportStatistic(publisher: string, name: string, version: string, type: StatisticType): TPromise; getReadme(extension: IGalleryExtension): TPromise; getManifest(extension: IGalleryExtension): TPromise; getChangelog(extension: IGalleryMetadata): TPromise; diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 6b5d9a67ca1..76d92ed5994 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as uuid from 'vs/base/common/uuid'; import { distinct } from 'vs/base/common/arrays'; import { getErrorMessage } from 'vs/base/common/errors'; -import { IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { assign, getOrDefault } from 'vs/base/common/objects'; import { IRequestService } from 'vs/platform/request/node/request'; @@ -385,6 +385,23 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return { galleryExtensions, total }; } + async reportStatistic(publisher: string, name: string, version: string, type: StatisticType): TPromise { + if (!this.isEnabled()) { + return; + } + + try { + const headers = await this.commonHTTPHeaders; + await this.requestService.request({ + type: 'POST', + url: this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`), + headers + }); + } catch (err) { + // noop + } + } + download(extension: IGalleryExtension): TPromise { return this.loadCompatibleVersion(extension).then(extension => { const zipPath = path.join(tmpdir(), uuid.generateUuid()); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index ebe55654b90..12a8bc796a4 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -17,7 +17,8 @@ import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IExtensionManifest, IGalleryMetadata, - InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, LocalExtensionType + InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, LocalExtensionType, + StatisticType } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest, getGalleryExtensionIdFromLocal, getIdAndVersionFromLocalExtensionId, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; @@ -223,9 +224,9 @@ export class ExtensionManagementService implements IExtensionManagementService { } private rollback(localExtension: ILocalExtension, dependecies: IGalleryExtension[]): TPromise { - return this.doUninstall(localExtension.id) + return this.doUninstall(localExtension) .then(() => this.filterOutUninstalled(dependecies)) - .then(installed => TPromise.join(installed.map((i) => this.doUninstall(i.id)))) + .then(installed => TPromise.join(installed.map((i) => this.doUninstall(i)))) .then(() => null); } @@ -304,7 +305,7 @@ export class ExtensionManagementService implements IExtensionManagementService { } private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise { - return this.preUninstallExtension(extension.id) + return this.preUninstallExtension(extension) .then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.promptAndUninstall(extension, installed, force)) .then(() => this.postUninstallExtension(extension.id), error => { @@ -370,7 +371,7 @@ export class ExtensionManagementService implements IExtensionManagementService { if (dependents.length) { return TPromise.wrapError(new Error(this.getDependentsErrorMessage(extension, dependents))); } - return TPromise.join([this.uninstallExtension(extension.id), ...dependenciesToUninstall.map(d => this.doUninstall(d.id))]).then(() => null); + return TPromise.join([this.uninstallExtension(extension.id), ...dependenciesToUninstall.map(d => this.doUninstall(d))]).then(() => null); } private getDependentsErrorMessage(extension: ILocalExtension, dependents: ILocalExtension[]): string { @@ -419,21 +420,22 @@ export class ExtensionManagementService implements IExtensionManagementService { return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.indexOf(getGalleryExtensionIdFromLocal(extension)) !== -1); } - private doUninstall(id: string): TPromise { - return this.preUninstallExtension(id) - .then(() => this.uninstallExtension(id)) - .then(() => this.postUninstallExtension(id), + private doUninstall(extension: ILocalExtension): TPromise { + return this.preUninstallExtension(extension) + .then(() => this.uninstallExtension(extension.id)) + .then(() => this.postUninstallExtension(extension.id), error => { - this.postUninstallExtension(id, error); + this.postUninstallExtension(extension.id, error); return TPromise.wrapError(error); }); } - private preUninstallExtension(id: string): TPromise { - const extensionPath = path.join(this.extensionsPath, id); + private preUninstallExtension(extension: ILocalExtension): TPromise { + const extensionPath = path.join(this.extensionsPath, extension.id); return pfs.exists(extensionPath) .then(exists => exists ? null : TPromise.wrapError(new Error(nls.localize('notExists', "Could not find extension")))) - .then(() => this._onUninstallExtension.fire(id)); + .then(() => this._onUninstallExtension.fire(extension.id)) + .then(() => this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall)); } private uninstallExtension(id: string): TPromise { From 73d8c3104cb2426521cc865460bff2ddc6526d5c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2017 12:58:02 -0700 Subject: [PATCH 05/96] Remove js indent on if/for/while block Fixes #30933 Fixes #32653 --- extensions/typescript/src/typescriptMain.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index 5d4a89d93ba..036b130adab 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -300,8 +300,7 @@ class LanguageProvider { // ^(.*\*/)?\s*\}.*$ decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/, // ^.*\{[^}"']*$ - increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/, - indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/ + increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/ }, wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, onEnterRules: [ @@ -327,12 +326,6 @@ class LanguageProvider { // e.g. *-----*/| beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/, action: { indentAction: IndentAction.None, removeText: 1 } - }, - { - // e.g. if (...) | {} - beforeText: /^\s*(for|while|if|else)\s*/, - afterText: /^\s*{/, - action: { indentAction: IndentAction.None } } ] })); From c2ee6133aa87d915f246e1ce1c33ef9b4f1b3888 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2017 13:48:48 -0700 Subject: [PATCH 06/96] Add plugins property on TSServer open requests Part of https://github.com/Microsoft/TypeScript/issues/17151 --- extensions/typescript/src/features/bufferSyncSupport.ts | 7 +++++++ extensions/typescript/src/typescriptService.ts | 3 +++ extensions/typescript/src/typescriptServiceClient.ts | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/extensions/typescript/src/features/bufferSyncSupport.ts b/extensions/typescript/src/features/bufferSyncSupport.ts index e76816e5b8f..665b973ac12 100644 --- a/extensions/typescript/src/features/bufferSyncSupport.ts +++ b/extensions/typescript/src/features/bufferSyncSupport.ts @@ -53,6 +53,13 @@ class SyncedBuffer { } } + if (this.client.apiVersion.has240Features()) { + if (this.client.plugins.length) { + (args as any).plugins = this.client.plugins.map(x => x.name); + } + } + + this.client.execute('open', args, false); } diff --git a/extensions/typescript/src/typescriptService.ts b/extensions/typescript/src/typescriptService.ts index f0d5f5e2501..1a4d1340507 100644 --- a/extensions/typescript/src/typescriptService.ts +++ b/extensions/typescript/src/typescriptService.ts @@ -7,6 +7,7 @@ import { CancellationToken, Uri, Event } from 'vscode'; import * as Proto from './protocol'; import API from './utils/api'; +import { TypeScriptServerPlugin } from "./utils/plugins"; export interface ITypescriptServiceClientHost { syntaxDiagnosticsReceived(event: Proto.DiagnosticEvent): void; @@ -34,6 +35,8 @@ export interface ITypescriptServiceClient { apiVersion: API; + plugins: TypeScriptServerPlugin[]; + execute(command: 'configure', args: Proto.ConfigureRequestArguments, token?: CancellationToken): Promise; execute(command: 'open', args: Proto.OpenRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; execute(command: 'close', args: Proto.FileRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index bf68bc742f9..e935ea84017 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -159,7 +159,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient private readonly host: ITypescriptServiceClientHost, private readonly workspaceState: Memento, private readonly versionStatus: VersionStatus, - private readonly plugins: TypeScriptServerPlugin[] + public readonly plugins: TypeScriptServerPlugin[] ) { this.pathSeparator = path.sep; this.lastStart = Date.now(); From 5055bb7ba4aa36f8b719bc18fb86a012edc534c1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2017 14:50:53 -0700 Subject: [PATCH 07/96] Don't scroll to factional line positions in the markdown preview when wordwrap is off --- extensions/markdown/media/main.js | 2 +- extensions/markdown/src/extension.ts | 4 ++-- extensions/markdown/src/previewContentProvider.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/markdown/media/main.js b/extensions/markdown/media/main.js index ddf21afbbb9..d23381de9bf 100644 --- a/extensions/markdown/media/main.js +++ b/extensions/markdown/media/main.js @@ -211,7 +211,7 @@ } else { const line = getEditorLineNumberForPageOffset(window.scrollY); if (!isNaN(line)) { - const args = [settings.source, line]; + const args = [settings.source, line, settings.wordWrap]; window.parent.postMessage({ command: 'did-click-link', data: `command:_markdown.revealLine?${encodeURIComponent(JSON.stringify(args))}` diff --git a/extensions/markdown/src/extension.ts b/extensions/markdown/src/extension.ts index 82dce8f9251..140a3ecd73c 100644 --- a/extensions/markdown/src/extension.ts +++ b/extensions/markdown/src/extension.ts @@ -102,7 +102,7 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(cspArbiter, uri, true))); context.subscriptions.push(vscode.commands.registerCommand('markdown.showSource', showSource)); - context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri, line) => { + context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri: string, line: number, wordWrap: boolean) => { const sourceUri = vscode.Uri.parse(decodeURIComponent(uri)); logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line }); @@ -112,7 +112,7 @@ export function activate(context: vscode.ExtensionContext) { const sourceLine = Math.floor(line); const fraction = line - sourceLine; const text = editor.document.lineAt(sourceLine).text; - const start = Math.floor(fraction * text.length); + const start = wordWrap ? Math.floor(fraction * text.length) : 0; editor.revealRange( new vscode.Range(sourceLine, start, sourceLine + 1, 0), vscode.TextEditorRevealType.AtTop); diff --git a/extensions/markdown/src/previewContentProvider.ts b/extensions/markdown/src/previewContentProvider.ts index 049a1c414a4..47d01be3d47 100644 --- a/extensions/markdown/src/previewContentProvider.ts +++ b/extensions/markdown/src/previewContentProvider.ts @@ -218,7 +218,8 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv line: initialLine, scrollPreviewWithEditorSelection: this.config.scrollPreviewWithEditorSelection, scrollEditorWithPreview: this.config.scrollEditorWithPreview, - doubleClickToSwitchToEditor: this.config.doubleClickToSwitchToEditor + doubleClickToSwitchToEditor: this.config.doubleClickToSwitchToEditor, + wordWrap: this.config.wordWrap }; this.logger.log('provideTextDocumentContent', initialData); From cb1797f1efb5ea8c8ad5b523061ddb5ba0843341 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Aug 2017 14:54:53 -0700 Subject: [PATCH 08/96] Revert "Don't scroll to factional line positions in the markdown preview when wordwrap is off" This reverts commit 5055bb7ba4aa36f8b719bc18fb86a012edc534c1. --- extensions/markdown/media/main.js | 2 +- extensions/markdown/src/extension.ts | 4 ++-- extensions/markdown/src/previewContentProvider.ts | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/extensions/markdown/media/main.js b/extensions/markdown/media/main.js index d23381de9bf..ddf21afbbb9 100644 --- a/extensions/markdown/media/main.js +++ b/extensions/markdown/media/main.js @@ -211,7 +211,7 @@ } else { const line = getEditorLineNumberForPageOffset(window.scrollY); if (!isNaN(line)) { - const args = [settings.source, line, settings.wordWrap]; + const args = [settings.source, line]; window.parent.postMessage({ command: 'did-click-link', data: `command:_markdown.revealLine?${encodeURIComponent(JSON.stringify(args))}` diff --git a/extensions/markdown/src/extension.ts b/extensions/markdown/src/extension.ts index 140a3ecd73c..82dce8f9251 100644 --- a/extensions/markdown/src/extension.ts +++ b/extensions/markdown/src/extension.ts @@ -102,7 +102,7 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(cspArbiter, uri, true))); context.subscriptions.push(vscode.commands.registerCommand('markdown.showSource', showSource)); - context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri: string, line: number, wordWrap: boolean) => { + context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri, line) => { const sourceUri = vscode.Uri.parse(decodeURIComponent(uri)); logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line }); @@ -112,7 +112,7 @@ export function activate(context: vscode.ExtensionContext) { const sourceLine = Math.floor(line); const fraction = line - sourceLine; const text = editor.document.lineAt(sourceLine).text; - const start = wordWrap ? Math.floor(fraction * text.length) : 0; + const start = Math.floor(fraction * text.length); editor.revealRange( new vscode.Range(sourceLine, start, sourceLine + 1, 0), vscode.TextEditorRevealType.AtTop); diff --git a/extensions/markdown/src/previewContentProvider.ts b/extensions/markdown/src/previewContentProvider.ts index 47d01be3d47..049a1c414a4 100644 --- a/extensions/markdown/src/previewContentProvider.ts +++ b/extensions/markdown/src/previewContentProvider.ts @@ -218,8 +218,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv line: initialLine, scrollPreviewWithEditorSelection: this.config.scrollPreviewWithEditorSelection, scrollEditorWithPreview: this.config.scrollEditorWithPreview, - doubleClickToSwitchToEditor: this.config.doubleClickToSwitchToEditor, - wordWrap: this.config.wordWrap + doubleClickToSwitchToEditor: this.config.doubleClickToSwitchToEditor }; this.logger.log('provideTextDocumentContent', initialData); From 0af0d4922241d676dce9f5861ae9074a8d06dbbb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 16 Aug 2017 14:58:25 -0700 Subject: [PATCH 09/96] Remove overly wordy "Smoke Test Suite", change 'context' to 'describe' --- test/smoke/src/test.ts | 2 +- test/smoke/src/tests/configuration-views.ts | 2 +- test/smoke/src/tests/css.ts | 2 +- test/smoke/src/tests/data-loss.ts | 2 +- test/smoke/src/tests/data-migration.ts | 2 +- test/smoke/src/tests/explorer.ts | 2 +- test/smoke/src/tests/extensions.ts | 2 +- test/smoke/src/tests/git.ts | 2 +- test/smoke/src/tests/integrated-terminal.ts | 2 +- test/smoke/src/tests/javascript-debug.ts | 2 +- test/smoke/src/tests/javascript.ts | 2 +- test/smoke/src/tests/localization.ts | 2 +- test/smoke/src/tests/multiroot.ts | 2 +- test/smoke/src/tests/search.ts | 2 +- test/smoke/src/tests/statusbar.ts | 2 +- test/smoke/src/tests/tasks.ts | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/smoke/src/test.ts b/test/smoke/src/test.ts index 3eb91634c19..0f1725dfa90 100644 --- a/test/smoke/src/test.ts +++ b/test/smoke/src/test.ts @@ -19,7 +19,7 @@ import { testExtensions } from "./tests/extensions"; import { testLocalization } from "./tests/localization"; import { testMultiRoot } from "./tests/multiroot"; -describe('Smoke Test Suite', () => { +describe('Smoke:', () => { testDataMigration(); testDataLoss(); testExplorer(); diff --git a/test/smoke/src/tests/configuration-views.ts b/test/smoke/src/tests/configuration-views.ts index ff810dd37d4..c5673a73416 100644 --- a/test/smoke/src/tests/configuration-views.ts +++ b/test/smoke/src/tests/configuration-views.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testConfigViews() { - context('Configuration and views', () => { + describe('Configuration and views', () => { let configView: ConfigurationView; beforeEach(async function () { diff --git a/test/smoke/src/tests/css.ts b/test/smoke/src/tests/css.ts index 52fcf60335f..c591c0fc6f2 100644 --- a/test/smoke/src/tests/css.ts +++ b/test/smoke/src/tests/css.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testCSS() { - context('CSS', () => { + describe('CSS', () => { let css: CSS; beforeEach(async function () { diff --git a/test/smoke/src/tests/data-loss.ts b/test/smoke/src/tests/data-loss.ts index c090b6f140c..cccefb9aa42 100644 --- a/test/smoke/src/tests/data-loss.ts +++ b/test/smoke/src/tests/data-loss.ts @@ -14,7 +14,7 @@ let common: CommonActions; let dl: DataLoss; export function testDataLoss() { - context('Data Loss', () => { + describe('Data Loss', () => { beforeEach(async function () { app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH], [`--user-data-dir=${USER_DIR}`]); diff --git a/test/smoke/src/tests/data-migration.ts b/test/smoke/src/tests/data-migration.ts index 4c9ac38334d..86e781bf0ec 100644 --- a/test/smoke/src/tests/data-migration.ts +++ b/test/smoke/src/tests/data-migration.ts @@ -16,7 +16,7 @@ export function testDataMigration() { return; } - context('Data Migration', () => { + describe('Data Migration', () => { afterEach(async function () { await app.stop(); diff --git a/test/smoke/src/tests/explorer.ts b/test/smoke/src/tests/explorer.ts index d1a4570f003..9edf29bb7d6 100644 --- a/test/smoke/src/tests/explorer.ts +++ b/test/smoke/src/tests/explorer.ts @@ -12,7 +12,7 @@ let app: SpectronApplication; let common: CommonActions; export function testExplorer() { - context('Explorer', () => { + describe('Explorer', () => { beforeEach(async function () { app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [WORKSPACE_PATH]); diff --git a/test/smoke/src/tests/extensions.ts b/test/smoke/src/tests/extensions.ts index 39894de6fea..d9527e1432e 100644 --- a/test/smoke/src/tests/extensions.ts +++ b/test/smoke/src/tests/extensions.ts @@ -16,7 +16,7 @@ let common: CommonActions; export function testExtensions() { - context('Extensions', () => { + describe('Extensions', () => { let extensions: Extensions; const extensionName = 'vscode-smoketest-check'; diff --git a/test/smoke/src/tests/git.ts b/test/smoke/src/tests/git.ts index 98f2c761aa5..35d61752dae 100644 --- a/test/smoke/src/tests/git.ts +++ b/test/smoke/src/tests/git.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testGit() { - context('Git', () => { + describe('Git', () => { let git: Git; beforeEach(async function () { diff --git a/test/smoke/src/tests/integrated-terminal.ts b/test/smoke/src/tests/integrated-terminal.ts index 57ec1d42d09..b9dbe88bcb6 100644 --- a/test/smoke/src/tests/integrated-terminal.ts +++ b/test/smoke/src/tests/integrated-terminal.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testIntegratedTerminal() { - context('Integrated Terminal', () => { + describe('Integrated Terminal', () => { let terminal: IntegratedTerminal; beforeEach(async function () { diff --git a/test/smoke/src/tests/javascript-debug.ts b/test/smoke/src/tests/javascript-debug.ts index 970fa6edabf..0018ae2f077 100644 --- a/test/smoke/src/tests/javascript-debug.ts +++ b/test/smoke/src/tests/javascript-debug.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testJavaScriptDebug() { - context('Debugging JavaScript', () => { + describe('Debugging JavaScript', () => { let jsDebug: JavaScriptDebug; beforeEach(async function () { diff --git a/test/smoke/src/tests/javascript.ts b/test/smoke/src/tests/javascript.ts index b522ce5c66f..60466ec5deb 100644 --- a/test/smoke/src/tests/javascript.ts +++ b/test/smoke/src/tests/javascript.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testJavaScript() { - context('JavaScript', () => { + describe('JavaScript', () => { let js: JavaScript; beforeEach(async function () { diff --git a/test/smoke/src/tests/localization.ts b/test/smoke/src/tests/localization.ts index a40f7cfc9ed..3e45fbc1e48 100644 --- a/test/smoke/src/tests/localization.ts +++ b/test/smoke/src/tests/localization.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testLocalization() { - context('Localization', () => { + describe('Localization', () => { afterEach(async function () { return await app.stop(); }); diff --git a/test/smoke/src/tests/multiroot.ts b/test/smoke/src/tests/multiroot.ts index 22aa8001452..73153097514 100644 --- a/test/smoke/src/tests/multiroot.ts +++ b/test/smoke/src/tests/multiroot.ts @@ -12,7 +12,7 @@ let app: SpectronApplication; let common: CommonActions; export function testMultiRoot() { - context('Multi Root', () => { + describe('Multi Root', () => { beforeEach(async function () { app = new SpectronApplication(LATEST_PATH, this.currentTest.fullTitle(), (this.currentTest as any).currentRetry(), [CODE_WORKSPACE_PATH]); diff --git a/test/smoke/src/tests/search.ts b/test/smoke/src/tests/search.ts index 35ea09e7e46..36818f0bcbd 100644 --- a/test/smoke/src/tests/search.ts +++ b/test/smoke/src/tests/search.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testSearch() { - context('Search', () => { + describe('Search', () => { let search: Search; beforeEach(async function () { diff --git a/test/smoke/src/tests/statusbar.ts b/test/smoke/src/tests/statusbar.ts index 9038266b334..d092105bec6 100644 --- a/test/smoke/src/tests/statusbar.ts +++ b/test/smoke/src/tests/statusbar.ts @@ -13,7 +13,7 @@ let app: SpectronApplication; let common: CommonActions; export function testStatusbar() { - context('Status Bar', () => { + describe('Status Bar', () => { let statusBar: StatusBar; beforeEach(async function () { diff --git a/test/smoke/src/tests/tasks.ts b/test/smoke/src/tests/tasks.ts index 45fc5d794bb..1aa10e3eac2 100644 --- a/test/smoke/src/tests/tasks.ts +++ b/test/smoke/src/tests/tasks.ts @@ -11,7 +11,7 @@ import { Tasks } from "../areas/tasks"; let app: SpectronApplication; export function testTasks() { - context('Tasks', () => { + describe('Tasks', () => { let tasks: Tasks; beforeEach(async function () { From 0e18b0533f026043e1b8ee5486311c90eba50a40 Mon Sep 17 00:00:00 2001 From: Simon Chan Date: Thu, 17 Aug 2017 07:43:40 +0800 Subject: [PATCH 10/96] Automatically restart ts server after disableAutomaticTypeAcquisition config changed (#32412) * Automatically restart ts server after disableAutomaticTypeAcquisition config changed * Change to LF --- extensions/typescript/package.nls.json | 2 +- extensions/typescript/src/typescriptServiceClient.ts | 2 +- extensions/typescript/src/utils/configuration.ts | 11 +++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/extensions/typescript/package.nls.json b/extensions/typescript/package.nls.json index 49d28876e2e..b25953666f6 100644 --- a/extensions/typescript/package.nls.json +++ b/extensions/typescript/package.nls.json @@ -4,7 +4,7 @@ "configuration.typescript": "TypeScript", "typescript.useCodeSnippetsOnMethodSuggest.dec": "Complete functions with their parameter signature.", "typescript.tsdk.desc": "Specifies the folder path containing the tsserver and lib*.d.ts files to use.", - "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Requires TypeScript >= 2.0.6 and a restart after changing it.", + "typescript.disableAutomaticTypeAcquisition": "Disables automatic type acquisition. Requires TypeScript >= 2.0.6.", "typescript.tsserver.log": "Enables logging of the TS server to a file. This log can be used to diagnose TS Server issues. The log may contain file paths, source code, and other potentially sensitive information from your project.", "typescript.tsserver.trace": "Enables tracing of messages sent to the TS server. This trace can be used to diagnose TS Server issues. The trace may contain file paths, source code, and other potentially sensitive information from your project.", "typescript.validate.enable": "Enable/disable TypeScript validation.", diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index e935ea84017..24e38eb8db6 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -332,7 +332,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient const args: string[] = []; if (this.apiVersion.has206Features()) { args.push('--useSingleInferredProject'); - if (workspace.getConfiguration().get('typescript.disableAutomaticTypeAcquisition', false)) { + if (this.configuration.disableAutomaticTypeAcquisition) { args.push('--disableAutomaticTypingAcquisition'); } } diff --git a/extensions/typescript/src/utils/configuration.ts b/extensions/typescript/src/utils/configuration.ts index df3a8943643..3ff9ad9cc63 100644 --- a/extensions/typescript/src/utils/configuration.ts +++ b/extensions/typescript/src/utils/configuration.ts @@ -47,6 +47,7 @@ export class TypeScriptServiceConfiguration { public readonly npmLocation: string | null; public readonly tsServerLogLevel: TsServerLogLevel = TsServerLogLevel.Off; public readonly checkJs: boolean; + public readonly disableAutomaticTypeAcquisition: boolean; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -60,6 +61,7 @@ export class TypeScriptServiceConfiguration { this.npmLocation = TypeScriptServiceConfiguration.readNpmLocation(configuration); this.tsServerLogLevel = TypeScriptServiceConfiguration.readTsServerLogLevel(configuration); this.checkJs = TypeScriptServiceConfiguration.readCheckJs(configuration); + this.disableAutomaticTypeAcquisition = TypeScriptServiceConfiguration.readDisableAutomaticTypeAcquisition(configuration); } public isEqualTo(other: TypeScriptServiceConfiguration): boolean { @@ -67,7 +69,8 @@ export class TypeScriptServiceConfiguration { && this.localTsdk === other.localTsdk && this.npmLocation === other.npmLocation && this.tsServerLogLevel === other.tsServerLogLevel - && this.checkJs === other.checkJs; + && this.checkJs === other.checkJs + && this.disableAutomaticTypeAcquisition === other.disableAutomaticTypeAcquisition; } private static extractGlobalTsdk(configuration: WorkspaceConfiguration): string | null { @@ -98,4 +101,8 @@ export class TypeScriptServiceConfiguration { private static readNpmLocation(configuration: WorkspaceConfiguration): string | null { return configuration.get('typescript.npm', null); } -} \ No newline at end of file + + private static readDisableAutomaticTypeAcquisition(configuration: WorkspaceConfiguration): boolean { + return configuration.get('typescript.disableAutomaticTypeAcquisition', false); + } +} From 7a8c2648b7781904a60b89f9da353451949d19b9 Mon Sep 17 00:00:00 2001 From: Robert Pethick Date: Thu, 17 Aug 2017 00:57:34 +0100 Subject: [PATCH 11/96] Add publishsettings to list of xml file types (#32641) --- extensions/xml/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/xml/package.json b/extensions/xml/package.json index 9ae56b31dc5..c9210274b25 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -36,6 +36,7 @@ ".proj", ".props", ".pt", + ".publishsettings", ".pubxml", ".pubxml.user", ".rdf", From 9e7441df9f609500a13e8651bd01eccf1613f386 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 16 Aug 2017 21:31:27 -0700 Subject: [PATCH 12/96] node-debug2@1.16.1 --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 20531bdc4b1..04ecea16c26 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -44,7 +44,7 @@ const nodeModules = ['electron', 'original-fs'] const builtInExtensions = [ { name: 'ms-vscode.node-debug', version: '1.16.4' }, - { name: 'ms-vscode.node-debug2', version: '1.16.0' } + { name: 'ms-vscode.node-debug2', version: '1.16.1' } ]; const excludedExtensions = [ From a1a198e730e48ce8582a72c77ac4e662281c52d3 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Wed, 16 Aug 2017 22:13:14 -0700 Subject: [PATCH 13/96] Fix parsing error in less files with loop #32354 --- extensions/emmet/npm-shrinkwrap.json | 4 ++-- extensions/emmet/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json index 68b2886e6d4..bf8d1f1095e 100644 --- a/extensions/emmet/npm-shrinkwrap.json +++ b/extensions/emmet/npm-shrinkwrap.json @@ -4,8 +4,8 @@ "dependencies": { "@emmetio/css-parser": { "version": "0.4.0", - "from": "@emmetio/css-parser@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.0.tgz" + "from": "ramya-rao-a/css-parser#vscode", + "resolved": "git://github.com/ramya-rao-a/css-parser.git#370c480ac103bd17c7bcfb34bf5d577dc40d3660" }, "@emmetio/extract-abbreviation": { "version": "0.1.3", diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index f9266ef067a..821d3ce7da6 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -210,7 +210,7 @@ "dependencies": { "@emmetio/html-matcher": "^0.3.1", - "@emmetio/css-parser": "^0.4.0", + "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/math-expression": "^0.1.1", "vscode-emmet-helper": "^1.0.16", "vscode-languageserver-types": "^3.0.3", From a573e65d70016054bbc0b423dace25414198fbf6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2017 08:41:20 +0200 Subject: [PATCH 14/96] Copy Selection Not Saved (fixes #20269) --- src/vs/workbench/parts/files/browser/views/explorerViewer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index 3282cfd22f9..3907902d276 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -353,8 +353,11 @@ export class FileRenderer implements IRenderer { this.state.actionProvider.runAction(tree, stat, editableData.action, { value: inputBox.value }); } + const restoreFocus = document.activeElement === inputBox.inputElement; // https://github.com/Microsoft/vscode/issues/20269 setTimeout(() => { - tree.DOMFocus(); + if (restoreFocus) { + tree.DOMFocus(); + } lifecycle.dispose(toDispose); container.removeChild(label.element); }, 0); From 900762c5622ee8c1f64a6ecccb6ecacc8749ea07 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Aug 2017 09:14:57 +0200 Subject: [PATCH 15/96] fixes #32610 --- .../node/extensionsWorkbenchService.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 15cca4f0517..8229a8fed86 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -813,8 +813,12 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { const extensionId = match[1]; - this.queryGallery({ names: [extensionId] }) - .then(result => { + this.queryLocal().then(local => { + if (local.some(local => local.id === extensionId)) { + return TPromise.as(null); + } + + return this.queryGallery({ names: [extensionId] }).then(result => { if (result.total < 1) { return TPromise.as(null); } @@ -826,20 +830,16 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { nls.localize('install', "Install"), nls.localize('cancel', "Cancel") ]; - return this.choiceService.choose(Severity.Info, message, options, 2, false) - .then(value => { - if (value === 0) { - const promises: TPromise[] = []; - if (this.local.every(local => local.id !== extension.id)) { - promises.push(this.install(extension)); - } - return TPromise.join(promises); - } + return this.choiceService.choose(Severity.Info, message, options, 2, false).then(value => { + if (value !== 0) { return TPromise.as(null); - }); + } + + return this.install(extension); + }); }); - }) - .done(undefined, error => this.onError(error)); + }); + }).done(undefined, error => this.onError(error)); } dispose(): void { From a9e0905dd9974670027fb50da6d72f0dadce6d6f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Aug 2017 09:50:14 +0200 Subject: [PATCH 16/96] fixes #32652 --- src/vs/base/test/common/event.test.ts | 28 +++++---------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index b07f2458744..2e26cb50b0c 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -338,34 +338,16 @@ suite('Event utils', () => { }); }); - test('should emit when done - setTimeout', () => { + test('should emit when done - setTimeout', async () => { let count = 0; - const event = fromPromise(TPromise.timeout(5)); + const promise = TPromise.timeout(5); + const event = fromPromise(promise); event(() => count++); assert.equal(count, 0); - - return TPromise.timeout(10).then(() => { - assert.equal(count, 1); - }); - }); - - test('should emit when done - setTimeout (#2)', () => { - let count = 0; - - const event = fromPromise(TPromise.timeout(30)); - event(() => count++); - - assert.equal(count, 0); - - return TPromise.timeout(0).then(() => { - assert.equal(count, 0); - - return TPromise.timeout(35).then(() => { - assert.equal(count, 1); - }); - }); + await promise; + assert.equal(count, 1); }); }); From a60a2472ca62cd58c464ac95da3692540819290b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2017 11:04:04 +0200 Subject: [PATCH 17/96] Tab labels fail to identify "~" as a root path (fixes #32701) --- src/vs/base/common/labels.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index ca5661ad764..6687e3b9e2f 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -110,6 +110,7 @@ export function tildify(path: string, userHome: string): string { */ const ellipsis = '\u2026'; const unc = '\\\\'; +const home = '~'; export function shorten(paths: string[]): string[] { const shortenedPaths: string[] = new Array(paths.length); @@ -130,7 +131,7 @@ export function shorten(paths: string[]): string[] { match = true; - // trim for now and concatenate unc path (e.g. \\network) or root path (/etc) later + // trim for now and concatenate unc path (e.g. \\network) or root path (/etc, ~/etc) later let prefix = ''; if (path.indexOf(unc) === 0) { prefix = path.substr(0, path.indexOf(unc) + unc.length); @@ -138,6 +139,9 @@ export function shorten(paths: string[]): string[] { } else if (path.indexOf(nativeSep) === 0) { prefix = path.substr(0, path.indexOf(nativeSep) + nativeSep.length); path = path.substr(path.indexOf(nativeSep) + nativeSep.length); + } else if (path.indexOf(home) === 0) { + prefix = path.substr(0, path.indexOf(home) + home.length); + path = path.substr(path.indexOf(home) + home.length); } // pick the first shortest subpath found From 2c0a55294b47d61b04840fcf00ab7bec5ba8f204 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2017 11:08:49 +0200 Subject: [PATCH 18/96] Make the compare action label easier to understand when file names are identical (fixes #30515) --- .../parts/files/browser/fileActions.ts | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 5b431213cf5..40822918de9 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -1246,26 +1246,38 @@ export class CompareResourcesAction extends Action { constructor( resource: URI, tree: ITree, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IEnvironmentService environmentService: IEnvironmentService ) { - super('workbench.files.action.compareFiles', CompareResourcesAction.computeLabel()); + super('workbench.files.action.compareFiles', CompareResourcesAction.computeLabel(resource, contextService, environmentService)); this.tree = tree; this.resource = resource; } - private static computeLabel(): string { + private static computeLabel(resource: URI, contextService: IWorkspaceContextService, environmentService: IEnvironmentService): string { if (globalResourceToCompare) { - return nls.localize('compareWith', "Compare with '{0}'", paths.basename(globalResourceToCompare.fsPath)); + let leftResourceName = paths.basename(globalResourceToCompare.fsPath); + let rightResourceName = paths.basename(resource.fsPath); + + // If the file names are identical, add more context by looking at the parent folder + if (leftResourceName === rightResourceName) { + const folderPaths = labels.shorten([ + labels.getPathLabel(paths.dirname(globalResourceToCompare.fsPath), contextService, environmentService), + labels.getPathLabel(paths.dirname(resource.fsPath), contextService, environmentService) + ]); + + leftResourceName = paths.join(folderPaths[0], leftResourceName); + rightResourceName = paths.join(folderPaths[1], rightResourceName); + } + + return nls.localize('compareWith', "Compare '{0}' with '{1}'", leftResourceName, rightResourceName); } return nls.localize('compareFiles', "Compare Files"); } - public getLabel(): string { - return CompareResourcesAction.computeLabel(); - } - _isEnabled(): boolean { // Need at least a resource to compare From f287c4551fbe92413727b918068024771165fcfe Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2017 11:38:54 +0200 Subject: [PATCH 19/96] multi root - ensure unique tab labels when root names are identical --- .../browser/parts/editor/tabsTitleControl.ts | 11 +++++++---- .../parts/files/common/editors/fileEditorInput.ts | 15 +++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 242abde192d..c0eb5a14a1b 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -44,7 +44,6 @@ import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, import { activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; interface IEditorInputLabel { - editor: IEditorInput; name: string; hasAmbiguousName?: boolean; description?: string; @@ -325,10 +324,14 @@ export class TabsTitleControl extends TitleControl { // Build labels and descriptions for each editor editors.forEach(editor => { + const name = editor.getName(); let description = editor.getDescription(); + if (mapLabelAndDescriptionToDuplicates.has(`${name}${description}`)) { + description = editor.getDescription(true); // try verbose description if name+description already exists + } + const item: IEditorInputLabel = { - editor, - name: editor.getName(), + name, description, title: editor.getTitle(Verbosity.LONG) }; @@ -351,7 +354,7 @@ export class TabsTitleControl extends TitleControl { }); if (duplicates.length > 1) { - const shortenedDescriptions = shorten(duplicates.map(duplicate => duplicate.editor.getDescription())); + const shortenedDescriptions = shorten(duplicates.map(duplicate => duplicate.description)); duplicates.forEach((duplicate, i) => { duplicate.description = shortenedDescriptions[i]; duplicate.hasAmbiguousName = true; diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index 8700ed4ecdd..b85202fdc87 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -33,6 +33,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private name: string; private description: string; + private verboseDescription: string; private shortTitle: string; private mediumTitle: string; @@ -127,12 +128,18 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { return this.decorateOrphanedFiles(this.name); } - public getDescription(): string { - if (!this.description) { - this.description = labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService); + public getDescription(verbose?: boolean): string { + if (verbose) { + if (!this.verboseDescription) { + this.verboseDescription = labels.getPathLabel(paths.dirname(this.resource.fsPath), void 0, this.environmentService); + } + } else { + if (!this.description) { + this.description = labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService); + } } - return this.description; + return verbose ? this.verboseDescription : this.description; } public getTitle(verbosity: Verbosity): string { From 4dae70b7d0ad089280094b7556a1d7f5a439b53d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2017 12:28:34 +0200 Subject: [PATCH 20/96] fix glitch with badge in multi root search result --- src/vs/workbench/parts/search/browser/media/searchviewlet.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/media/searchviewlet.css b/src/vs/workbench/parts/search/browser/media/searchviewlet.css index e568bf2982c..dbbe3915e79 100644 --- a/src/vs/workbench/parts/search/browser/media/searchviewlet.css +++ b/src/vs/workbench/parts/search/browser/media/searchviewlet.css @@ -270,10 +270,12 @@ } .search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .filematch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .foldermatch .monaco-count-badge, .search-viewlet > .results > .monaco-tree .monaco-tree-row:hover .content .linematch .monaco-count-badge, .search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .filematch .monaco-count-badge, +.search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .foldermatch .monaco-count-badge, .search-viewlet > .results > .monaco-tree.focused .monaco-tree-row.focused .content .linematch .monaco-count-badge { - display: none; + display: none; } .search-viewlet .focused .monaco-tree-row.selected:not(.highlighted) > .content.actions .action-remove, From 0705604e0d79c00aa167290f6ac55498baa91b57 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Aug 2017 14:37:15 +0200 Subject: [PATCH 21/96] ignore menu items that have no command, fixes #32673 --- .../platform/actions/electron-browser/menusExtensionPoint.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts b/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts index 401e489ee0f..7801cc6e557 100644 --- a/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts +++ b/src/vs/platform/actions/electron-browser/menusExtensionPoint.ts @@ -329,7 +329,8 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM let alt = item.alt && MenuRegistry.getCommand(item.alt); if (!command) { - collector.warn(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command)); + collector.error(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command)); + continue; } if (item.alt && !alt) { collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", item.alt)); From 59726c99fac614750e479a97bd26d8f2047282a4 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 17 Aug 2017 14:54:05 +0200 Subject: [PATCH 22/96] do not overwrite selected config name by contained name; fixes #32318 --- .../workbench/parts/debug/electron-browser/debugService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 4cdd77f497c..efee1adc6d1 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -617,7 +617,7 @@ export class DebugService implements debug.IDebugService { this.model.removeWatchExpressions(id); } - public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false): TPromise { + public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise { // make sure to save all files and that the configuration is up to date return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() => @@ -640,7 +640,8 @@ export class DebugService implements debug.IDebugService { config = configOrName; } if (launch) { - manager.selectConfiguration(launch, typeof configOrName === 'string' ? configOrName : undefined, true); + // in the drop down the name of the top most compound takes precedence over the launch config name + manager.selectConfiguration(launch, topCompoundName || (typeof configOrName === 'string' ? configOrName : undefined), true); } if (compound) { @@ -649,7 +650,7 @@ export class DebugService implements debug.IDebugService { "Compound must have \"configurations\" attribute set in order to start multiple configurations."))); } - return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name) : TPromise.as(null))); + return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null))); } if (configOrName && !config) { return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName))); From b8c9bff49e245f9e3dac7093b5d1aa3674e6d0a3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Aug 2017 15:06:15 +0200 Subject: [PATCH 23/96] remove unused telemetry --- .../performance.contribution.ts | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts index 5ac35b0980e..8b04c59fd2f 100644 --- a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts @@ -23,12 +23,10 @@ import { join } from 'path'; import { localize } from 'vs/nls'; import { toPromise, filterEvent } from 'vs/base/common/event'; import { platform, Platform } from 'vs/base/common/platform'; -import { readdir, stat } from 'vs/base/node/pfs'; +import { readdir } from 'vs/base/node/pfs'; import { release } from 'os'; import { stopProfiling } from 'vs/base/node/profiler'; import { virtualMachineHint } from 'vs/base/node/id'; -import { forEach } from 'vs/base/common/collections'; -import URI from 'vs/base/common/uri'; class ProfilingHint implements IWorkbenchContribution { @@ -215,46 +213,6 @@ class StartupProfiler implements IWorkbenchContribution { } } -class PerformanceTelemetry implements IWorkbenchContribution { - - constructor( - @ITimerService private readonly _timerService: ITimerService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IExtensionService extensionService: IExtensionService, - ) { - TPromise.join([ - TPromise.timeout(7 * 1000), - extensionService.onReady() - ]).then(() => { - this._sendWorkbenchMainSizeTelemetry(); - this._validateTimers(); - }); - } - - getId(): string { - return 'performance.PerformanceTelemetry'; - } - - private _validateTimers(): void { - const { startupMetrics } = this._timerService; - const invalidTimers: string[] = []; - forEach(startupMetrics.timers, (entry) => { - if (entry.value < 0) { - invalidTimers.push(entry.key); - } - }); - this._telemetryService.publicLog('perf:invalidTimers', { invalidTimers }); - } - - private _sendWorkbenchMainSizeTelemetry(): void { - const { fsPath } = URI.parse(require.toUrl('vs/workbench/workbench.main.js')); - stat(fsPath).then(stats => { - this._telemetryService.publicLog('perf:jsFileSize', { workbenchMain: stats.size }); - }); - } -} - const registry = Registry.as(Extensions.Workbench); registry.registerWorkbenchContribution(ProfilingHint); registry.registerWorkbenchContribution(StartupProfiler); -registry.registerWorkbenchContribution(PerformanceTelemetry); From 347422bdfa8df60efa7aa6b67da19484c6857a1e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2017 15:32:18 +0200 Subject: [PATCH 24/96] :lipstick: --- .../browser/actions/workspaceActions.ts | 113 +++++++++--------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 247cd788487..e28576043c5 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -98,6 +98,7 @@ export abstract class BaseWorkspacesAction extends Action { } const res = this.windowService.showMessageBox(opts); + return !buttons[res].canceled; } @@ -117,41 +118,6 @@ export abstract class BaseWorkspacesAction extends Action { } } -export class NewWorkspaceFromExistingAction extends BaseWorkspacesAction { - - static ID = 'workbench.action.newWorkspaceFromExisting'; - static LABEL = nls.localize('newWorkspaceFormExisting', "New Workspace From Existing..."); - - constructor( - id: string, - label: string, - @IWindowService windowService: IWindowService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IEnvironmentService environmentService: IEnvironmentService, - @IWorkspacesService protected workspacesService: IWorkspacesService, - @IWindowsService protected windowsService: IWindowsService, - ) { - super(id, label, windowService, environmentService, contextService); - } - - public run(): TPromise { - if (this.contextService.hasWorkspace()) { - let folders = this.pickFolders(mnemonicLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace")); - if (folders && folders.length) { - if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('addSupported', "To open multiple folders, window reload is required."))) { - return this.createWorkspace([this.contextService.getWorkspace().roots[0], ...folders.map(folder => URI.file(folder))]); - } - } - } - return TPromise.as(null); - } - - private createWorkspace(folders: URI[]): TPromise { - return this.workspacesService.createWorkspace(distinct(folders.map(folder => folder.toString(true /* encoding */)))) - .then(({ configPath }) => this.windowsService.openWindow([configPath])); - } -} - export class AddRootFolderAction extends BaseWorkspacesAction { static ID = 'workbench.action.addRootFolder'; @@ -190,6 +156,61 @@ export class AddRootFolderAction extends BaseWorkspacesAction { } } +class NewWorkspaceAction extends Action { + + static ID = 'workbench.action.newWorkspace'; + static LABEL = nls.localize('newWorkspace', "New Workspace..."); + + constructor( + id: string, + label: string, + @IWindowService private windowService: IWindowService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + super(id, label); + } + + public run(): TPromise { + return this.windowService.newWorkspace(); + } +} + +class NewWorkspaceFromExistingAction extends BaseWorkspacesAction { + + static ID = 'workbench.action.newWorkspaceFromExisting'; + static LABEL = nls.localize('newWorkspaceFormExisting', "New Workspace From Existing..."); + + constructor( + id: string, + label: string, + @IWindowService windowService: IWindowService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IEnvironmentService environmentService: IEnvironmentService, + @IWorkspacesService protected workspacesService: IWorkspacesService, + @IWindowsService protected windowsService: IWindowsService, + ) { + super(id, label, windowService, environmentService, contextService); + } + + public run(): TPromise { + if (this.contextService.hasFolderWorkspace()) { + let folders = this.pickFolders(mnemonicLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace")); + if (folders && folders.length) { + if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('addSupported', "To open multiple folders, window reload is required."))) { + return this.createWorkspace([this.contextService.getWorkspace().roots[0], ...folders.map(folder => URI.file(folder))]); + } + } + } + + return TPromise.as(null); + } + + private createWorkspace(folders: URI[]): TPromise { + return this.workspacesService.createWorkspace(distinct(folders.map(folder => folder.toString(true /* encoding */)))) + .then(({ configPath }) => this.windowsService.openWindow([configPath])); + } +} + export class RemoveRootFolderAction extends Action { static ID = 'workbench.action.removeRootFolder'; @@ -258,6 +279,7 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { .then(({ configPath }) => this.windowsService.openWindow([configPath])); }); } + return TPromise.as(null); } @@ -292,7 +314,6 @@ export class OpenWorkspaceAction extends Action { id: string, label: string, @IWindowService private windowService: IWindowService, - @IWorkspaceContextService private contextService: IWorkspaceContextService ) { super(id, label); } @@ -302,25 +323,6 @@ export class OpenWorkspaceAction extends Action { } } -class NewWorkspaceAction extends Action { - - static ID = 'workbench.action.newWorkspace'; - static LABEL = nls.localize('newWorkspace', "New Workspace..."); - - constructor( - id: string, - label: string, - @IWindowService private windowService: IWindowService, - @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { - super(id, label); - } - - public run(): TPromise { - return this.windowService.newWorkspace(); - } -} - export class OpenWorkspaceConfigFileAction extends Action { public static ID = 'workbench.action.openWorkspaceConfigFile'; @@ -333,6 +335,7 @@ export class OpenWorkspaceConfigFileAction extends Action { @IWorkbenchEditorService private editorService: IWorkbenchEditorService ) { super(id, label); + this.enabled = this.workspaceContextService.hasMultiFolderWorkspace(); } From e9e3baa0b1c414895d11513758c0ae79cac2424e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2017 15:42:30 +0200 Subject: [PATCH 25/96] mr - simplify workspace actions in renderer --- src/vs/code/electron-main/windows.ts | 15 -------- src/vs/platform/windows/common/windows.ts | 2 -- src/vs/platform/windows/common/windowsIpc.ts | 6 ---- .../windows/electron-browser/windowService.ts | 4 --- .../platform/windows/electron-main/windows.ts | 1 - .../windows/electron-main/windowsService.ts | 10 ------ .../browser/actions/workspaceActions.ts | 36 +++++-------------- .../workbench/test/workbenchTestServices.ts | 8 ----- 8 files changed, 8 insertions(+), 74 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 6e821e3a8c0..2aa56687041 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1171,21 +1171,6 @@ export class WindowsManager implements IWindowsMainService { }); } - public newWorkspace(window: CodeWindow = this.getLastActiveWindow()): void { - const folders = dialog.showOpenDialog(window ? window.win : void 0, { - buttonLabel: mnemonicLabel(localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), - title: localize('selectWorkspace', "Select Folders for Workspace"), - properties: ['multiSelections', 'openDirectory', 'createDirectory'], - defaultPath: this.getWorkspaceDialogDefaultPath(window ? (window.openedWorkspace || window.openedFolderPath) : void 0) - }); - - if (folders && folders.length) { - this.workspacesService.createWorkspace(folders.map(folder => URI.file(folder).toString(true /* encoding */))).then(workspace => { - this.open({ context: OpenContext.DIALOG, cli: this.environmentService.args, pathsToOpen: [workspace.configPath] }); - }); - } - } - public openWorkspace(window: CodeWindow = this.getLastActiveWindow()): void { let defaultPath: string; if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index ecc0b6643cb..0764f6c5466 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -42,7 +42,6 @@ export interface IWindowsService { toggleDevTools(windowId: number): TPromise; closeWorkspace(windowId: number): TPromise; openWorkspace(windowId: number): TPromise; - newWorkspace(windowId: number): TPromise; toggleFullScreen(windowId: number): TPromise; setRepresentedFilename(windowId: number, fileName: string): TPromise; addRecentlyOpened(files: string[]): TPromise; @@ -98,7 +97,6 @@ export interface IWindowService { toggleDevTools(): TPromise; closeWorkspace(): TPromise; openWorkspace(): TPromise; - newWorkspace(): TPromise; toggleFullScreen(): TPromise; setRepresentedFilename(fileName: string): TPromise; getRecentlyOpened(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index dd1f6f599f0..08e5fca38e6 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -23,7 +23,6 @@ export interface IWindowsChannel extends IChannel { call(command: 'toggleDevTools', arg: number): TPromise; call(command: 'closeWorkspace', arg: number): TPromise; call(command: 'openWorkspace', arg: number): TPromise; - call(command: 'newWorkspace', arg: number): TPromise; call(command: 'toggleFullScreen', arg: number): TPromise; call(command: 'setRepresentedFilename', arg: [number, string]): TPromise; call(command: 'addRecentlyOpened', arg: string[]): TPromise; @@ -79,7 +78,6 @@ export class WindowsChannel implements IWindowsChannel { case 'toggleDevTools': return this.service.toggleDevTools(arg); case 'closeWorkspace': return this.service.closeWorkspace(arg); case 'openWorkspace': return this.service.openWorkspace(arg); - case 'newWorkspace': return this.service.newWorkspace(arg); case 'toggleFullScreen': return this.service.toggleFullScreen(arg); case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]); case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg); @@ -159,10 +157,6 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('openWorkspace', windowId); } - newWorkspace(windowId: number): TPromise { - return this.channel.call('newWorkspace', windowId); - } - toggleFullScreen(windowId: number): TPromise { return this.channel.call('toggleFullScreen', windowId); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 3ebc232734c..89070eb8cdd 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -68,10 +68,6 @@ export class WindowService implements IWindowService { return this.windowsService.openWorkspace(this.windowId); } - newWorkspace(): TPromise { - return this.windowsService.newWorkspace(this.windowId); - } - closeWindow(): TPromise { return this.windowsService.closeWindow(this.windowId); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 1e4f61f7e6a..e0abfe7fad9 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -57,7 +57,6 @@ export interface IWindowsMainService { // methods ready(initialUserEnv: IProcessEnvironment): void; reload(win: ICodeWindow, cli?: ParsedArgs): void; - newWorkspace(win?: ICodeWindow): void; openWorkspace(win?: ICodeWindow): void; closeWorkspace(win: ICodeWindow): void; open(openConfig: IOpenConfiguration): ICodeWindow[]; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 73a1b67d44f..0ef83e2092d 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -124,16 +124,6 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } - newWorkspace(windowId: number): TPromise { - const codeWindow = this.windowsMainService.getWindowById(windowId); - - if (codeWindow) { - this.windowsMainService.newWorkspace(codeWindow); - } - - return TPromise.as(null); - } - toggleFullScreen(windowId: number): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index e28576043c5..943741d6f7f 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -138,11 +138,11 @@ export class AddRootFolderAction extends BaseWorkspacesAction { public run(): TPromise { if (!this.contextService.hasWorkspace()) { - return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL).run(); + return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, []).run(); } if (this.contextService.hasFolderWorkspace()) { - return this.instantiationService.createInstance(NewWorkspaceFromExistingAction, NewWorkspaceFromExistingAction.ID, NewWorkspaceFromExistingAction.LABEL).run(); + return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().roots).run(); } const folders = super.pickFolders(mnemonicLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace")); @@ -156,7 +156,7 @@ export class AddRootFolderAction extends BaseWorkspacesAction { } } -class NewWorkspaceAction extends Action { +class NewWorkspaceAction extends BaseWorkspacesAction { static ID = 'workbench.action.newWorkspace'; static LABEL = nls.localize('newWorkspace', "New Workspace..."); @@ -164,25 +164,7 @@ class NewWorkspaceAction extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService, - @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { - super(id, label); - } - - public run(): TPromise { - return this.windowService.newWorkspace(); - } -} - -class NewWorkspaceFromExistingAction extends BaseWorkspacesAction { - - static ID = 'workbench.action.newWorkspaceFromExisting'; - static LABEL = nls.localize('newWorkspaceFormExisting', "New Workspace From Existing..."); - - constructor( - id: string, - label: string, + private presetRoots: URI[], @IWindowService windowService: IWindowService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IEnvironmentService environmentService: IEnvironmentService, @@ -193,12 +175,10 @@ class NewWorkspaceFromExistingAction extends BaseWorkspacesAction { } public run(): TPromise { - if (this.contextService.hasFolderWorkspace()) { - let folders = this.pickFolders(mnemonicLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace")); - if (folders && folders.length) { - if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('addSupported', "To open multiple folders, window reload is required."))) { - return this.createWorkspace([this.contextService.getWorkspace().roots[0], ...folders.map(folder => URI.file(folder))]); - } + const folders = this.pickFolders(mnemonicLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace")); + if (folders && folders.length) { + if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('addSupported', "To open multiple folders, window reload is required."))) { + return this.createWorkspace([...this.presetRoots, ...folders.map(folder => URI.file(folder))]); } } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index a9ffb50bf78..51710dd2007 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -882,10 +882,6 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } - newWorkspace(): TPromise { - return TPromise.as(void 0); - } - toggleFullScreen(): TPromise { return TPromise.as(void 0); } @@ -1018,10 +1014,6 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - newWorkspace(windowId: number): TPromise { - return TPromise.as(void 0); - } - toggleFullScreen(windowId: number): TPromise { return TPromise.as(void 0); } From 8ddccaa938c12a96e7db37009f34b1f14a62ff96 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 17 Aug 2017 10:32:00 +0200 Subject: [PATCH 26/96] Style changes --- .../electron-browser/extensionHost.ts | 221 +++++++++--------- 1 file changed, 112 insertions(+), 109 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index a3bc92f7c0b..8b122867639 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -69,55 +69,55 @@ export class ExtensionHostProcessWorker { private lastExtensionHostError: string; private terminating: boolean; - private isExtensionDevelopmentHost: boolean; - private isExtensionDevelopmentTestFromCli: boolean; - private isExtensionDevelopmentDebug: boolean; - private isExtensionDevelopmentDebugBrk: boolean; + private readonly _isExtensionDevHost: boolean; + private readonly _isExtensionDevTestFromCli: boolean; + private readonly _isExtensionDevDebug: boolean; + private readonly _isExtensionDevDebugBrk: boolean; readonly messagingProtocol = new LazyMessagePassingProtol(); private extensionService: ExtensionService; constructor( - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IMessageService private messageService: IMessageService, - @IWindowsService private windowsService: IWindowsService, - @IWindowService private windowService: IWindowService, - @IBroadcastService private broadcastService: IBroadcastService, - @ILifecycleService lifecycleService: ILifecycleService, - @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, - @ITelemetryService private telemetryService: ITelemetryService, - @ICrashReporterService private crashReporterService: ICrashReporterService + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @IMessageService private readonly _messageService: IMessageService, + @IWindowsService private readonly _windowsService: IWindowsService, + @IWindowService private readonly _windowService: IWindowService, + @IBroadcastService private readonly _broadcastService: IBroadcastService, + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ICrashReporterService private readonly _crashReporterService: ICrashReporterService ) { // handle extension host lifecycle a bit special when we know we are developing an extension that runs inside - this.isExtensionDevelopmentHost = environmentService.isExtensionDevelopment; - this.isExtensionDevelopmentDebug = (typeof environmentService.debugExtensionHost.port === 'number'); - this.isExtensionDevelopmentDebugBrk = !!environmentService.debugExtensionHost.break; - this.isExtensionDevelopmentTestFromCli = this.isExtensionDevelopmentHost && !!environmentService.extensionTestsPath && !environmentService.debugExtensionHost.break; + this._isExtensionDevHost = this._environmentService.isExtensionDevelopment; + this._isExtensionDevDebug = (typeof this._environmentService.debugExtensionHost.port === 'number'); + this._isExtensionDevDebugBrk = !!this._environmentService.debugExtensionHost.break; + this._isExtensionDevTestFromCli = this._isExtensionDevHost && !!this._environmentService.extensionTestsPath && !this._environmentService.debugExtensionHost.break; - lifecycleService.onWillShutdown(this._onWillShutdown, this); - lifecycleService.onShutdown(reason => this.terminate()); + this._lifecycleService.onWillShutdown(this._onWillShutdown, this); + this._lifecycleService.onShutdown(reason => this.terminate()); - broadcastService.onBroadcast(b => this.onBroadcast(b)); + _broadcastService.onBroadcast(b => this.onBroadcast(b)); } private onBroadcast(broadcast: IBroadcast): void { // Close Ext Host Window Request - if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this.isExtensionDevelopmentHost) { + if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this._isExtensionDevHost) { const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this.environmentService.extensionDevelopmentPath, path, !isLinux))) { - this.windowService.closeWindow(); + if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + this._windowService.closeWindow(); } } - if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this.isExtensionDevelopmentHost) { + if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this._isExtensionDevHost) { const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this.environmentService.extensionDevelopmentPath, path, !isLinux))) { - this.windowService.reloadWindow(); + if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + this._windowService.reloadWindow(); } } } @@ -125,7 +125,7 @@ export class ExtensionHostProcessWorker { public start(extensionService: ExtensionService): TPromise { this.extensionService = extensionService; - return TPromise.join([this.tryListenOnPipe(), this.tryFindDebugPort()]).then(data => { + return TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then(data => { const [server, hook] = <[Server, string]>data[0]; const port = data[1]; @@ -134,22 +134,22 @@ export class ExtensionHostProcessWorker { AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', PIPE_LOGGING: 'true', VERBOSE_LOGGING: true, - VSCODE_WINDOW_ID: String(this.windowService.getCurrentWindowId()), + VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), VSCODE_IPC_HOOK_EXTHOST: hook, ELECTRON_NO_ASAR: '1' }), // We only detach the extension host on windows. Linux and Mac orphan by default // and detach under Linux and Mac create another process group. // We detach because we have noticed that when the renderer exits, its child processes - // (i.e. extension host) is taken down in a brutal fashion by the OS + // (i.e. extension host) are taken down in a brutal fashion by the OS detached: !!isWindows, execArgv: port - ? ['--nolazy', (this.isExtensionDevelopmentDebugBrk ? '--inspect-brk=' : '--inspect=') + port] + ? ['--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + port] : undefined, silent: true }; - const crashReporterOptions = this.crashReporterService.getChildProcessStartOptions('extensionHost'); + const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost'); if (crashReporterOptions) { opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); } @@ -185,22 +185,25 @@ export class ExtensionHostProcessWorker { // Support logging from extension host this.extensionHostProcess.on('message', msg => { if (msg && (msg).type === '__$console') { - this.logExtensionHostMessage(msg); + this._logExtensionHostMessage(msg); } }); // Lifecycle - let onExit = () => this.terminate(); - process.once('exit', onExit); - this.extensionHostProcess.on('error', (err) => this.onError(err)); - this.extensionHostProcess.on('exit', (code: any, signal: any) => this.onExit(code, signal, onExit)); + const globalExitListener = () => this.terminate(); + process.once('exit', globalExitListener); + this.extensionHostProcess.on('error', (err) => this._onError(err)); + this.extensionHostProcess.on('exit', (code: number, signal: string) => { + process.removeListener('exit', globalExitListener); + this._onExit(code, signal); + }); // Notify debugger that we are ready to attach to the process if we run a development extension - if (this.isExtensionDevelopmentHost && port) { - this.broadcastService.broadcast({ + if (this._isExtensionDevHost && port) { + this._broadcastService.broadcast({ channel: EXTENSION_ATTACH_BROADCAST_CHANNEL, payload: { - debugId: this.environmentService.debugExtensionHost.debugId, + debugId: this._environmentService.debugExtensionHost.debugId, port } }); @@ -208,25 +211,25 @@ export class ExtensionHostProcessWorker { // Help in case we fail to start it let startupTimeoutHandle: number; - if (!this.environmentService.isBuilt || this.isExtensionDevelopmentHost) { + if (!this._environmentService.isBuilt || this._isExtensionDevHost) { startupTimeoutHandle = setTimeout(() => { - const msg = this.isExtensionDevelopmentDebugBrk + const msg = this._isExtensionDevDebugBrk ? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") : nls.localize('extensionHostProcess.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); - this.messageService.show(Severity.Warning, msg); + this._messageService.show(Severity.Warning, msg); }, 10000); } // Initialize extension host process with hand shakes - return this.tryExtHostHandshake(server).then((protocol) => { + return this._tryExtHostHandshake(server).then((protocol) => { clearTimeout(startupTimeoutHandle); return protocol; }); }); } - private tryListenOnPipe(): TPromise<[Server, string]> { + private _tryListenOnPipe(): TPromise<[Server, string]> { return new TPromise<[Server, string]>((resolve, reject) => { const server = createServer(); server.on('error', reject); @@ -238,8 +241,8 @@ export class ExtensionHostProcessWorker { }); } - private tryFindDebugPort(): TPromise { - const extensionHostPort = this.environmentService.debugExtensionHost.port; + private _tryFindDebugPort(): TPromise { + const extensionHostPort = this._environmentService.debugExtensionHost.port; if (typeof extensionHostPort !== 'number') { return TPromise.wrap(void 0); } @@ -252,7 +255,7 @@ export class ExtensionHostProcessWorker { if (port !== extensionHostPort) { console.warn(`%c[Extension Host] %cProvided debugging port ${extensionHostPort} is not free, using ${port} instead.`, 'color: blue', 'color: black'); } - if (this.isExtensionDevelopmentDebugBrk) { + if (this._isExtensionDevDebugBrk) { console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color: black'); } else { console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color: black'); @@ -262,17 +265,16 @@ export class ExtensionHostProcessWorker { }); } - private tryExtHostHandshake(server: Server): TPromise { + private _tryExtHostHandshake(server: Server): TPromise { return new TPromise((resolve, reject) => { + let handle = setTimeout(() => reject('timeout'), 60 * 1000); server.on('connection', socket => { clearTimeout(handle); const protocol = new Protocol(socket); resolve(protocol); }); - // }).then(protocol => { - // return protocol; }).then(protocol => { @@ -280,7 +282,7 @@ export class ExtensionHostProcessWorker { protocol.onMessage(msg => { if (msg === 'ready') { // 1) Host is ready to receive messages, initialize it - return this.createExtHostInitData().then(data => protocol.send(stringify(data))); + return this._createExtHostInitData().then(data => protocol.send(stringify(data))); } else if (msg === 'initialized') { // 2) Host is initialized this.messagingProtocol.resolve(protocol); @@ -289,34 +291,35 @@ export class ExtensionHostProcessWorker { return undefined; }); }); + }); } - private createExtHostInitData(): TPromise { - return TPromise.join([this.telemetryService.getTelemetryInfo(), this.extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { - let r: IInitData = { + private _createExtHostInitData(): TPromise { + return TPromise.join([this._telemetryService.getTelemetryInfo(), this.extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { + const r: IInitData = { parentPid: process.pid, environment: { - isExtensionDevelopmentDebug: this.isExtensionDevelopmentDebug, - appSettingsHome: this.environmentService.appSettingsHome, - disableExtensions: this.environmentService.disableExtensions, - userExtensionsHome: this.environmentService.extensionsPath, - extensionDevelopmentPath: this.environmentService.extensionDevelopmentPath, - extensionTestsPath: this.environmentService.extensionTestsPath, + isExtensionDevelopmentDebug: this._isExtensionDevDebug, + appSettingsHome: this._environmentService.appSettingsHome, + disableExtensions: this._environmentService.disableExtensions, + userExtensionsHome: this._environmentService.extensionsPath, + extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath, + extensionTestsPath: this._environmentService.extensionTestsPath, // globally disable proposed api when built and not insiders developing extensions - enableProposedApiForAll: !this.environmentService.isBuilt || (!!this.environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0), - enableProposedApiFor: this.environmentService.args['enable-proposed-api'] || [] + enableProposedApiForAll: !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0), + enableProposedApiFor: this._environmentService.args['enable-proposed-api'] || [] }, - workspace: this.contextService.getWorkspace(), + workspace: this._contextService.getWorkspace(), extensions: extensionDescriptions, - configuration: this.configurationService.getConfigurationData(), + configuration: this._configurationService.getConfigurationData(), telemetryInfo }; return r; }); } - private logExtensionHostMessage(logEntry: ILogEntry) { + private _logExtensionHostMessage(logEntry: ILogEntry) { let args = []; try { let parsed = JSON.parse(logEntry.arguments); @@ -336,28 +339,28 @@ export class ExtensionHostProcessWorker { } // Send to local console unless we run tests from cli - if (!this.isExtensionDevelopmentTestFromCli) { + if (!this._isExtensionDevTestFromCli) { console[logEntry.severity].apply(console, consoleArgs); } // Log on main side if running tests from cli - if (this.isExtensionDevelopmentTestFromCli) { - this.windowsService.log(logEntry.severity, ...args); + if (this._isExtensionDevTestFromCli) { + this._windowsService.log(logEntry.severity, ...args); } // Broadcast to other windows if we are in development mode - else if (!this.environmentService.isBuilt || this.isExtensionDevelopmentHost) { - this.broadcastService.broadcast({ + else if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + this._broadcastService.broadcast({ channel: EXTENSION_LOG_BROADCAST_CHANNEL, payload: { logEntry, - debugId: this.environmentService.debugExtensionHost.debugId + debugId: this._environmentService.debugExtensionHost.debugId } }); } } - private onError(err: any): void { + private _onError(err: any): void { let errorMessage = toErrorMessage(err); if (errorMessage === this.lastExtensionHostError) { return; // prevent error spam @@ -365,45 +368,45 @@ export class ExtensionHostProcessWorker { this.lastExtensionHostError = errorMessage; - this.messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage)); + this._messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage)); } - private onExit(code: any, signal: any, onProcessExit: any): void { - process.removeListener('exit', onProcessExit); + private _onExit(code: number, signal: string): void { + if (this.terminating) { + // Expected termination path (we asked it to terminate) + return; + } - if (!this.terminating) { + // Unexpected termination + if (!this._isExtensionDevHost) { + const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, async (): TPromise => { + await this._windowService.openDevTools(); + return false; + }); - // Unexpected termination - if (!this.isExtensionDevelopmentHost) { - const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, async (): TPromise => { - await this.windowService.openDevTools(); - return false; - }); - - let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."); - if (code === 87) { - message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive. Please reload the window to recover."); - } - this.messageService.show(Severity.Error, { - message: message, - actions: [ - openDevTools, - this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL) - ] - }); - - console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."); + if (code === 87) { + message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive. Please reload the window to recover."); } + this._messageService.show(Severity.Error, { + message: message, + actions: [ + openDevTools, + this._instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL) + ] + }); - // Expected development extension termination: When the extension host goes down we also shutdown the window - else if (!this.isExtensionDevelopmentTestFromCli) { - this.windowService.closeWindow(); - } + console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + } - // When CLI testing make sure to exit with proper exit code - else { - ipc.send('vscode:exit', code); - } + // Expected development extension termination: When the extension host goes down we also shutdown the window + else if (!this._isExtensionDevTestFromCli) { + this._windowService.closeWindow(); + } + + // When CLI testing make sure to exit with proper exit code + else { + ipc.send('vscode:exit', code); } } @@ -420,11 +423,11 @@ export class ExtensionHostProcessWorker { // If the extension development host was started without debugger attached we need // to communicate this back to the main side to terminate the debug session - if (this.isExtensionDevelopmentHost && !this.isExtensionDevelopmentTestFromCli && !this.isExtensionDevelopmentDebug) { - this.broadcastService.broadcast({ + if (this._isExtensionDevHost && !this._isExtensionDevTestFromCli && !this._isExtensionDevDebug) { + this._broadcastService.broadcast({ channel: EXTENSION_TERMINATE_BROADCAST_CHANNEL, payload: { - debugId: this.environmentService.debugExtensionHost.debugId + debugId: this._environmentService.debugExtensionHost.debugId } }); From 47d23363bd9982c47370243d809e0b5f368dc389 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 17 Aug 2017 11:20:00 +0200 Subject: [PATCH 27/96] Lift resources to members --- .../electron-browser/extensionHost.ts | 184 ++++++++++-------- 1 file changed, 102 insertions(+), 82 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 8b122867639..301e786ecf9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -27,8 +27,8 @@ import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; -import { createServer, Server } from 'net'; -import Event, { Emitter, debounceEvent, mapEvent, any } from 'vs/base/common/event'; +import { createServer, Server, Socket } from 'net'; +import { debounceEvent, mapEvent, any } from 'vs/base/common/event'; import { fromEventEmitter } from 'vs/base/node/event'; import { IInitData, IWorkspaceData } from 'vs/workbench/api/node/extHost.protocol'; import { ExtensionService } from "vs/workbench/services/extensions/electron-browser/extensionService"; @@ -38,43 +38,23 @@ import { IBroadcastService, IBroadcast } from "vs/platform/broadcast/electron-br import { isEqual } from "vs/base/common/paths"; import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, ILogEntry, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from "vs/platform/extensions/common/extensionHost"; -export class LazyMessagePassingProtol implements IMessagePassingProtocol { - - private _delegate: IMessagePassingProtocol; - private _onMessage = new Emitter(); - private _buffer: any[] = []; - - readonly onMessage: Event = this._onMessage.event; - - send(msg: any): void { - if (this._delegate) { - this._delegate.send(msg); - } else { - this._buffer.push(msg); - } - } - - resolve(delegate: IMessagePassingProtocol): void { - this._delegate = delegate; - this._delegate.onMessage(data => this._onMessage.fire(data)); - this._buffer.forEach(this._delegate.send, this._delegate); - this._buffer = null; - } -} - export class ExtensionHostProcessWorker { - private extensionHostProcess: ChildProcess; - - private lastExtensionHostError: string; - private terminating: boolean; - private readonly _isExtensionDevHost: boolean; - private readonly _isExtensionDevTestFromCli: boolean; private readonly _isExtensionDevDebug: boolean; private readonly _isExtensionDevDebugBrk: boolean; + private readonly _isExtensionDevTestFromCli: boolean; - readonly messagingProtocol = new LazyMessagePassingProtol(); + // State + private _lastExtensionHostError: string; + private _terminating: boolean; + + // Resources, in order they get acquired/created: + private _namedPipeServer: Server; + private _namedPipeName: string; + private _extensionHostProcess: ChildProcess; + private _extensionHostConnection: Socket; + private _messagingProtocol: IMessagePassingProtocol; private extensionService: ExtensionService; @@ -98,13 +78,26 @@ export class ExtensionHostProcessWorker { this._isExtensionDevDebugBrk = !!this._environmentService.debugExtensionHost.break; this._isExtensionDevTestFromCli = this._isExtensionDevHost && !!this._environmentService.extensionTestsPath && !this._environmentService.debugExtensionHost.break; + this._lastExtensionHostError = null; + this._terminating = false; + + this._namedPipeServer = null; + this._namedPipeName = null; + this._extensionHostProcess = null; + this._messagingProtocol = null; + + + // TODO@rehost this._lifecycleService.onWillShutdown(this._onWillShutdown, this); + + // TODO@rehost this._lifecycleService.onShutdown(reason => this.terminate()); - _broadcastService.onBroadcast(b => this.onBroadcast(b)); + // TODO@rehost + _broadcastService.onBroadcast(b => this._onBroadcast(b)); } - private onBroadcast(broadcast: IBroadcast): void { + private _onBroadcast(broadcast: IBroadcast): void { // Close Ext Host Window Request if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this._isExtensionDevHost) { @@ -125,9 +118,9 @@ export class ExtensionHostProcessWorker { public start(extensionService: ExtensionService): TPromise { this.extensionService = extensionService; - return TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then(data => { - const [server, hook] = <[Server, string]>data[0]; - const port = data[1]; + return TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [void, number]) => { + // The port will be 0 if there's no need to debug or if a free port was not found + const port = data[1]; const opts = { env: objects.mixin(objects.clone(process.env), { @@ -135,7 +128,7 @@ export class ExtensionHostProcessWorker { PIPE_LOGGING: 'true', VERBOSE_LOGGING: true, VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), - VSCODE_IPC_HOOK_EXTHOST: hook, + VSCODE_IPC_HOOK_EXTHOST: this._namedPipeName, ELECTRON_NO_ASAR: '1' }), // We only detach the extension host on windows. Linux and Mac orphan by default @@ -155,14 +148,14 @@ export class ExtensionHostProcessWorker { } // Run Extension Host as fork of current process - this.extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); + this._extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); // Catch all output coming from the extension host process type Output = { data: string, format: string[] }; - this.extensionHostProcess.stdout.setEncoding('utf8'); - this.extensionHostProcess.stderr.setEncoding('utf8'); - const onStdout = fromEventEmitter(this.extensionHostProcess.stdout, 'data'); - const onStderr = fromEventEmitter(this.extensionHostProcess.stderr, 'data'); + this._extensionHostProcess.stdout.setEncoding('utf8'); + this._extensionHostProcess.stderr.setEncoding('utf8'); + const onStdout = fromEventEmitter(this._extensionHostProcess.stdout, 'data'); + const onStderr = fromEventEmitter(this._extensionHostProcess.stderr, 'data'); const onOutput = any( mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })), mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] })) @@ -183,7 +176,7 @@ export class ExtensionHostProcessWorker { }); // Support logging from extension host - this.extensionHostProcess.on('message', msg => { + this._extensionHostProcess.on('message', msg => { if (msg && (msg).type === '__$console') { this._logExtensionHostMessage(msg); } @@ -192,10 +185,10 @@ export class ExtensionHostProcessWorker { // Lifecycle const globalExitListener = () => this.terminate(); process.once('exit', globalExitListener); - this.extensionHostProcess.on('error', (err) => this._onError(err)); - this.extensionHostProcess.on('exit', (code: number, signal: string) => { + this._extensionHostProcess.on('error', (err) => this._onExtHostProcessError(err)); + this._extensionHostProcess.on('exit', (code: number, signal: string) => { process.removeListener('exit', globalExitListener); - this._onExit(code, signal); + this._onExtHostProcessExit(code, signal); }); // Notify debugger that we are ready to attach to the process if we run a development extension @@ -222,29 +215,36 @@ export class ExtensionHostProcessWorker { } // Initialize extension host process with hand shakes - return this._tryExtHostHandshake(server).then((protocol) => { + return this._tryExtHostHandshake().then((protocol) => { clearTimeout(startupTimeoutHandle); - return protocol; + return this._messagingProtocol; }); }); } - private _tryListenOnPipe(): TPromise<[Server, string]> { - return new TPromise<[Server, string]>((resolve, reject) => { - const server = createServer(); - server.on('error', reject); - const hook = generateRandomPipeName(); - server.listen(hook, () => { - server.removeListener('error', reject); - resolve([server, hook]); + /** + * Start a server (`this._namedPipeServer`) that listens on a named pipe (`this._namedPipeName`) + */ + private _tryListenOnPipe(): TPromise { + return new TPromise((resolve, reject) => { + this._namedPipeName = generateRandomPipeName(); + + this._namedPipeServer = createServer(); + this._namedPipeServer.on('error', reject); + this._namedPipeServer.listen(this._namedPipeName, () => { + this._namedPipeServer.removeListener('error', reject); + resolve(void 0); }); }); } + /** + * Find a free port if extension host debugging is enabled. + */ private _tryFindDebugPort(): TPromise { const extensionHostPort = this._environmentService.debugExtensionHost.port; if (typeof extensionHostPort !== 'number') { - return TPromise.wrap(void 0); + return TPromise.wrap(0); } return new TPromise((c, e) => { findFreePort(extensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, (port) => { @@ -265,31 +265,48 @@ export class ExtensionHostProcessWorker { }); } - private _tryExtHostHandshake(server: Server): TPromise { + private _tryExtHostHandshake(): TPromise { - return new TPromise((resolve, reject) => { + return new TPromise((resolve, reject) => { + // Wait for the extension host to connect to our named pipe + // and wrap the socket in the message passing protocol let handle = setTimeout(() => reject('timeout'), 60 * 1000); - server.on('connection', socket => { + this._namedPipeServer.on('connection', socket => { clearTimeout(handle); - const protocol = new Protocol(socket); - resolve(protocol); + this._extensionHostConnection = socket; + this._messagingProtocol = new Protocol(this._extensionHostConnection); + resolve(void 0); }); - }).then(protocol => { + }).then(() => { + + // 1) wait for the incoming `ready` event and send the initialization data. + // 2) wait for the incoming `initialized` event. + return new TPromise((resolve, reject) => { + + const disposable = this._messagingProtocol.onMessage(msg => { - return new TPromise((resolve, reject) => { - protocol.onMessage(msg => { if (msg === 'ready') { // 1) Host is ready to receive messages, initialize it - return this._createExtHostInitData().then(data => protocol.send(stringify(data))); - } else if (msg === 'initialized') { - // 2) Host is initialized - this.messagingProtocol.resolve(protocol); - resolve(protocol); + this._createExtHostInitData().then(data => this._messagingProtocol.send(stringify(data))); + return; } - return undefined; + + if (msg === 'initialized') { + // 2) Host is initialized + + // stop listening for messages here + disposable.dispose(); + + // release this promise + resolve(void 0); + return; + } + + console.error(`received unexpected message during handshake phase from the extension host: `, msg); }); + }); }); @@ -360,20 +377,20 @@ export class ExtensionHostProcessWorker { } } - private _onError(err: any): void { + private _onExtHostProcessError(err: any): void { let errorMessage = toErrorMessage(err); - if (errorMessage === this.lastExtensionHostError) { + if (errorMessage === this._lastExtensionHostError) { return; // prevent error spam } - this.lastExtensionHostError = errorMessage; + this._lastExtensionHostError = errorMessage; this._messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage)); } - private _onExit(code: number, signal: string): void { - if (this.terminating) { - // Expected termination path (we asked it to terminate) + private _onExtHostProcessExit(code: number, signal: string): void { + if (this._terminating) { + // Expected termination path (we asked the process to terminate) return; } @@ -411,11 +428,14 @@ export class ExtensionHostProcessWorker { } public terminate(): void { - this.terminating = true; - if (this.extensionHostProcess) { - this.messagingProtocol.send({ + this._terminating = true; + + if (this._messagingProtocol) { + this._messagingProtocol.send({ type: '__$terminate' }); + } else { + // TODO@rehost } } From 4270b63a3af0aaa7d3ac0a5297d91be9a2979690 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 17 Aug 2017 12:04:18 +0200 Subject: [PATCH 28/96] Towards being able to free all resources associated with the extension host process --- .../electron-browser/extensionHost.ts | 265 ++++++++++-------- 1 file changed, 147 insertions(+), 118 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 301e786ecf9..fbd8df54945 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -51,10 +51,9 @@ export class ExtensionHostProcessWorker { // Resources, in order they get acquired/created: private _namedPipeServer: Server; - private _namedPipeName: string; private _extensionHostProcess: ChildProcess; private _extensionHostConnection: Socket; - private _messagingProtocol: IMessagePassingProtocol; + private _messageProtocol: TPromise; private extensionService: ExtensionService; @@ -82,9 +81,9 @@ export class ExtensionHostProcessWorker { this._terminating = false; this._namedPipeServer = null; - this._namedPipeName = null; this._extensionHostProcess = null; - this._messagingProtocol = null; + this._extensionHostConnection = null; + this._messageProtocol = null; // TODO@rehost @@ -116,124 +115,134 @@ export class ExtensionHostProcessWorker { } public start(extensionService: ExtensionService): TPromise { - this.extensionService = extensionService; + if (this._terminating) { + // .terminate() was called + return null; + } - return TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [void, number]) => { - // The port will be 0 if there's no need to debug or if a free port was not found - const port = data[1]; + if (!this._messageProtocol) { + this.extensionService = extensionService; - const opts = { - env: objects.mixin(objects.clone(process.env), { - AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', - PIPE_LOGGING: 'true', - VERBOSE_LOGGING: true, - VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), - VSCODE_IPC_HOOK_EXTHOST: this._namedPipeName, - ELECTRON_NO_ASAR: '1' - }), - // We only detach the extension host on windows. Linux and Mac orphan by default - // and detach under Linux and Mac create another process group. - // We detach because we have noticed that when the renderer exits, its child processes - // (i.e. extension host) are taken down in a brutal fashion by the OS - detached: !!isWindows, - execArgv: port - ? ['--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + port] - : undefined, - silent: true - }; + this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [string, number]) => { + const pipeName = data[0]; + // The port will be 0 if there's no need to debug or if a free port was not found + const port = data[1]; - const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost'); - if (crashReporterOptions) { - opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); - } + const opts = { + env: objects.mixin(objects.clone(process.env), { + AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess', + PIPE_LOGGING: 'true', + VERBOSE_LOGGING: true, + VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), + VSCODE_IPC_HOOK_EXTHOST: pipeName, + ELECTRON_NO_ASAR: '1' + }), + // We only detach the extension host on windows. Linux and Mac orphan by default + // and detach under Linux and Mac create another process group. + // We detach because we have noticed that when the renderer exits, its child processes + // (i.e. extension host) are taken down in a brutal fashion by the OS + detached: !!isWindows, + execArgv: port + ? ['--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + port] + : undefined, + silent: true + }; - // Run Extension Host as fork of current process - this._extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); - - // Catch all output coming from the extension host process - type Output = { data: string, format: string[] }; - this._extensionHostProcess.stdout.setEncoding('utf8'); - this._extensionHostProcess.stderr.setEncoding('utf8'); - const onStdout = fromEventEmitter(this._extensionHostProcess.stdout, 'data'); - const onStderr = fromEventEmitter(this._extensionHostProcess.stderr, 'data'); - const onOutput = any( - mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })), - mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] })) - ); - - // Debounce all output, so we can render it in the Chrome console as a group - const onDebouncedOutput = debounceEvent(onOutput, (r, o) => { - return r - ? { data: r.data + o.data, format: [...r.format, ...o.format] } - : { data: o.data, format: o.format }; - }, 100); - - // Print out extension host output - onDebouncedOutput(data => { - console.group('Extension Host'); - console.log(data.data, ...data.format); - console.groupEnd(); - }); - - // Support logging from extension host - this._extensionHostProcess.on('message', msg => { - if (msg && (msg).type === '__$console') { - this._logExtensionHostMessage(msg); + const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost'); + if (crashReporterOptions) { + opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); } - }); - // Lifecycle - const globalExitListener = () => this.terminate(); - process.once('exit', globalExitListener); - this._extensionHostProcess.on('error', (err) => this._onExtHostProcessError(err)); - this._extensionHostProcess.on('exit', (code: number, signal: string) => { - process.removeListener('exit', globalExitListener); - this._onExtHostProcessExit(code, signal); - }); + // Run Extension Host as fork of current process + this._extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); - // Notify debugger that we are ready to attach to the process if we run a development extension - if (this._isExtensionDevHost && port) { - this._broadcastService.broadcast({ - channel: EXTENSION_ATTACH_BROADCAST_CHANNEL, - payload: { - debugId: this._environmentService.debugExtensionHost.debugId, - port + // Catch all output coming from the extension host process + type Output = { data: string, format: string[] }; + this._extensionHostProcess.stdout.setEncoding('utf8'); + this._extensionHostProcess.stderr.setEncoding('utf8'); + const onStdout = fromEventEmitter(this._extensionHostProcess.stdout, 'data'); + const onStderr = fromEventEmitter(this._extensionHostProcess.stderr, 'data'); + const onOutput = any( + mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })), + mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] })) + ); + + // Debounce all output, so we can render it in the Chrome console as a group + const onDebouncedOutput = debounceEvent(onOutput, (r, o) => { + return r + ? { data: r.data + o.data, format: [...r.format, ...o.format] } + : { data: o.data, format: o.format }; + }, 100); + + // Print out extension host output + onDebouncedOutput(data => { + console.group('Extension Host'); + console.log(data.data, ...data.format); + console.groupEnd(); + }); + + // Support logging from extension host + this._extensionHostProcess.on('message', msg => { + if (msg && (msg).type === '__$console') { + this._logExtensionHostMessage(msg); } }); - } - // Help in case we fail to start it - let startupTimeoutHandle: number; - if (!this._environmentService.isBuilt || this._isExtensionDevHost) { - startupTimeoutHandle = setTimeout(() => { - const msg = this._isExtensionDevDebugBrk - ? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") - : nls.localize('extensionHostProcess.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); + // Lifecycle + const globalExitListener = () => this.terminate(); + process.once('exit', globalExitListener); + this._extensionHostProcess.on('error', (err) => this._onExtHostProcessError(err)); + this._extensionHostProcess.on('exit', (code: number, signal: string) => { + process.removeListener('exit', globalExitListener); + this._onExtHostProcessExit(code, signal); + }); - this._messageService.show(Severity.Warning, msg); - }, 10000); - } + // Notify debugger that we are ready to attach to the process if we run a development extension + if (this._isExtensionDevHost && port) { + this._broadcastService.broadcast({ + channel: EXTENSION_ATTACH_BROADCAST_CHANNEL, + payload: { + debugId: this._environmentService.debugExtensionHost.debugId, + port + } + }); + } - // Initialize extension host process with hand shakes - return this._tryExtHostHandshake().then((protocol) => { - clearTimeout(startupTimeoutHandle); - return this._messagingProtocol; + // Help in case we fail to start it + let startupTimeoutHandle: number; + if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + startupTimeoutHandle = setTimeout(() => { + const msg = this._isExtensionDevDebugBrk + ? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") + : nls.localize('extensionHostProcess.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); + + this._messageService.show(Severity.Warning, msg); + }, 10000); + } + + // Initialize extension host process with hand shakes + return this._tryExtHostHandshake().then((protocol) => { + clearTimeout(startupTimeoutHandle); + return protocol; + }); }); - }); + } + + return this._messageProtocol; } /** - * Start a server (`this._namedPipeServer`) that listens on a named pipe (`this._namedPipeName`) + * Start a server (`this._namedPipeServer`) that listens on a named pipe and return the named pipe name. */ - private _tryListenOnPipe(): TPromise { - return new TPromise((resolve, reject) => { - this._namedPipeName = generateRandomPipeName(); + private _tryListenOnPipe(): TPromise { + return new TPromise((resolve, reject) => { + const pipeName = generateRandomPipeName(); this._namedPipeServer = createServer(); this._namedPipeServer.on('error', reject); - this._namedPipeServer.listen(this._namedPipeName, () => { + this._namedPipeServer.listen(pipeName, () => { this._namedPipeServer.removeListener('error', reject); - resolve(void 0); + resolve(pipeName); }); }); } @@ -265,42 +274,54 @@ export class ExtensionHostProcessWorker { }); } - private _tryExtHostHandshake(): TPromise { + private _tryExtHostHandshake(): TPromise { - return new TPromise((resolve, reject) => { + return new TPromise((resolve, reject) => { // Wait for the extension host to connect to our named pipe // and wrap the socket in the message passing protocol - let handle = setTimeout(() => reject('timeout'), 60 * 1000); + let handle = setTimeout(() => { + this._namedPipeServer.close(); + this._namedPipeServer = null; + reject('timeout'); + }, 60 * 1000); + this._namedPipeServer.on('connection', socket => { clearTimeout(handle); + this._namedPipeServer.close(); + this._namedPipeServer = null; this._extensionHostConnection = socket; - this._messagingProtocol = new Protocol(this._extensionHostConnection); - resolve(void 0); + resolve(new Protocol(this._extensionHostConnection)); }); - }).then(() => { + }).then((protocol) => { // 1) wait for the incoming `ready` event and send the initialization data. // 2) wait for the incoming `initialized` event. - return new TPromise((resolve, reject) => { + return new TPromise((resolve, reject) => { - const disposable = this._messagingProtocol.onMessage(msg => { + let handle = setTimeout(() => { + reject('timeout'); + }, 60 * 1000); + + const disposable = protocol.onMessage(msg => { if (msg === 'ready') { - // 1) Host is ready to receive messages, initialize it - this._createExtHostInitData().then(data => this._messagingProtocol.send(stringify(data))); + // 1) Extension Host is ready to receive messages, initialize it + this._createExtHostInitData().then(data => protocol.send(stringify(data))); return; } if (msg === 'initialized') { - // 2) Host is initialized + // 2) Extension Host is initialized + + clearTimeout(handle); // stop listening for messages here disposable.dispose(); // release this promise - resolve(void 0); + resolve(protocol); return; } @@ -428,15 +449,23 @@ export class ExtensionHostProcessWorker { } public terminate(): void { + if (this._terminating) { + return; + } this._terminating = true; - if (this._messagingProtocol) { - this._messagingProtocol.send({ + if (!this._messageProtocol) { + // .start() was not called + return; + } + + this._messageProtocol.then((protocol) => { + protocol.send({ type: '__$terminate' }); - } else { - // TODO@rehost - } + }); + + // TODO@rehost: install a timer to clean up OS resources } private _onWillShutdown(event: ShutdownEvent): void { From 7e6d5644e14ee1381935d0ff38171d5f8a2bc883 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 17 Aug 2017 12:50:54 +0200 Subject: [PATCH 29/96] Have ExtensionService handle extension host process crashing --- .../electron-browser/extensionHost.ts | 106 ++++++++++-------- .../electron-browser/extensionService.ts | 44 ++++++-- 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index fbd8df54945..a932aa054e3 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -11,7 +11,6 @@ import { stringify } from 'vs/base/common/marshalling'; import * as objects from 'vs/base/common/objects'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { Action } from 'vs/base/common/actions'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { findFreePort } from 'vs/base/node/ports'; import { IMessageService, Severity } from 'vs/platform/message/common/message'; @@ -23,23 +22,27 @@ import { ChildProcess, fork } from 'child_process'; import { ipcRenderer as ipc } from 'electron'; import product from 'vs/platform/node/product'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; import { createServer, Server, Socket } from 'net'; -import { debounceEvent, mapEvent, any } from 'vs/base/common/event'; +import Event, { Emitter, debounceEvent, mapEvent, any } from 'vs/base/common/event'; import { fromEventEmitter } from 'vs/base/node/event'; import { IInitData, IWorkspaceData } from 'vs/workbench/api/node/extHost.protocol'; -import { ExtensionService } from "vs/workbench/services/extensions/electron-browser/extensionService"; +import { IExtensionService } from "vs/platform/extensions/common/extensions"; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { ICrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService'; import { IBroadcastService, IBroadcast } from "vs/platform/broadcast/electron-browser/broadcastService"; import { isEqual } from "vs/base/common/paths"; import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, ILogEntry, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from "vs/platform/extensions/common/extensionHost"; +import { IDisposable, dispose } from "vs/base/common/lifecycle"; export class ExtensionHostProcessWorker { + private _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>(); + public readonly onCrashed: Event<[number, string]> = this._onCrashed.event; + + private readonly _toDispose: IDisposable[]; + private readonly _isExtensionDevHost: boolean; private readonly _isExtensionDevDebug: boolean; private readonly _isExtensionDevDebugBrk: boolean; @@ -49,27 +52,24 @@ export class ExtensionHostProcessWorker { private _lastExtensionHostError: string; private _terminating: boolean; - // Resources, in order they get acquired/created: + // Resources, in order they get acquired/created when .start() is called: private _namedPipeServer: Server; private _extensionHostProcess: ChildProcess; private _extensionHostConnection: Socket; private _messageProtocol: TPromise; - private extensionService: ExtensionService; - constructor( + /* intentionally not injected */private readonly _extensionService: IExtensionService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IMessageService private readonly _messageService: IMessageService, @IWindowsService private readonly _windowsService: IWindowsService, @IWindowService private readonly _windowService: IWindowService, @IBroadcastService private readonly _broadcastService: IBroadcastService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @ICrashReporterService private readonly _crashReporterService: ICrashReporterService - ) { // handle extension host lifecycle a bit special when we know we are developing an extension that runs inside this._isExtensionDevHost = this._environmentService.isExtensionDevelopment; @@ -85,15 +85,23 @@ export class ExtensionHostProcessWorker { this._extensionHostConnection = null; this._messageProtocol = null; + this._toDispose = []; + this._toDispose.push(this._onCrashed); + this._toDispose.push(this._lifecycleService.onWillShutdown((e) => this._onWillShutdown(e))); + this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate())); + this._toDispose.push(this._broadcastService.onBroadcast(b => this._onBroadcast(b))); - // TODO@rehost - this._lifecycleService.onWillShutdown(this._onWillShutdown, this); + const globalExitListener = () => this.terminate(); + process.once('exit', globalExitListener); + this._toDispose.push({ + dispose: () => { + process.removeListener('exit', globalExitListener); + } + }); + } - // TODO@rehost - this._lifecycleService.onShutdown(reason => this.terminate()); - - // TODO@rehost - _broadcastService.onBroadcast(b => this._onBroadcast(b)); + public dispose(): void { + this.terminate(); } private _onBroadcast(broadcast: IBroadcast): void { @@ -114,15 +122,13 @@ export class ExtensionHostProcessWorker { } } - public start(extensionService: ExtensionService): TPromise { + public start(): TPromise { if (this._terminating) { // .terminate() was called return null; } if (!this._messageProtocol) { - this.extensionService = extensionService; - this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [string, number]) => { const pipeName = data[0]; // The port will be 0 if there's no need to debug or if a free port was not found @@ -189,13 +195,8 @@ export class ExtensionHostProcessWorker { }); // Lifecycle - const globalExitListener = () => this.terminate(); - process.once('exit', globalExitListener); this._extensionHostProcess.on('error', (err) => this._onExtHostProcessError(err)); - this._extensionHostProcess.on('exit', (code: number, signal: string) => { - process.removeListener('exit', globalExitListener); - this._onExtHostProcessExit(code, signal); - }); + this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal)); // Notify debugger that we are ready to attach to the process if we run a development extension if (this._isExtensionDevHost && port) { @@ -334,7 +335,7 @@ export class ExtensionHostProcessWorker { } private _createExtHostInitData(): TPromise { - return TPromise.join([this._telemetryService.getTelemetryInfo(), this.extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { + return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => { const r: IInitData = { parentPid: process.pid, environment: { @@ -417,24 +418,7 @@ export class ExtensionHostProcessWorker { // Unexpected termination if (!this._isExtensionDevHost) { - const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, async (): TPromise => { - await this._windowService.openDevTools(); - return false; - }); - - let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."); - if (code === 87) { - message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive. Please reload the window to recover."); - } - this._messageService.show(Severity.Error, { - message: message, - actions: [ - openDevTools, - this._instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL) - ] - }); - - console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + this._onCrashed.fire([code, signal]); } // Expected development extension termination: When the extension host goes down we also shutdown the window @@ -454,18 +438,46 @@ export class ExtensionHostProcessWorker { } this._terminating = true; + dispose(this._toDispose); + if (!this._messageProtocol) { // .start() was not called return; } this._messageProtocol.then((protocol) => { + + // Send the extension host a request to terminate itself + // (graceful termination) protocol.send({ type: '__$terminate' }); - }); - // TODO@rehost: install a timer to clean up OS resources + // Give the extension host 60s, after which we will + // try to kill the process and release any resources + setTimeout(() => this._cleanResources(), 60 * 1000); + + }, (err) => { + + // Establishing a protocol with the extension host failed, so + // try to kill the process and release any resources. + this._cleanResources(); + }); + } + + private _cleanResources(): void { + if (this._namedPipeServer) { + this._namedPipeServer.close(); + this._namedPipeServer = null; + } + if (this._extensionHostConnection) { + this._extensionHostConnection.end(); + this._extensionHostConnection = null; + } + if (this._extensionHostProcess) { + this._extensionHostProcess.kill(); + this._extensionHostProcess = null; + } } private _onWillShutdown(event: ShutdownEvent): void { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index da1a86518b9..0bc754dbb67 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import * as nls from 'vs/nls'; import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import pkg from 'vs/platform/node/package'; -import { localize } from 'vs/nls'; import * as path from 'path'; import URI from 'vs/base/common/uri'; import { ExtensionDescriptionRegistry } from "vs/workbench/services/extensions/node/extensionDescriptionRegistry"; @@ -28,6 +28,9 @@ import { MainThreadService } from "vs/workbench/services/thread/electron-browser import { Barrier } from "vs/workbench/services/extensions/node/barrier"; import { IMessagePassingProtocol } from "vs/base/parts/ipc/common/ipc"; import { ExtHostCustomersRegistry } from "vs/workbench/api/electron-browser/extHostCustomers"; +import { IWindowService } from "vs/platform/windows/common/windows"; +import { Action } from "vs/base/common/actions"; +import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); @@ -68,6 +71,7 @@ export class ExtensionService implements IExtensionService { @ITelemetryService private readonly _telemetryService: ITelemetryService, @IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService, @IStorageService private readonly _storageService: IStorageService, + @IWindowService private readonly _windowService: IWindowService ) { this._registry = null; this._barrier = new Barrier(); @@ -75,10 +79,36 @@ export class ExtensionService implements IExtensionService { this._extensionsStatus = {}; this._alreadyActivatedEvents = Object.create(null); - const extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker); - this._proxy = extensionHostProcessWorker.start(this).then((protocol) => { - return { value: this._begin(protocol) }; + const extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this); + extensionHostProcessWorker.onCrashed(([code, signal]) => { + const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, async (): TPromise => { + await this._windowService.openDevTools(); + return false; + }); + + let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."); + if (code === 87) { + message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive. Please reload the window to recover."); + } + this._messageService.show(Severity.Error, { + message: message, + actions: [ + openDevTools, + this._instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL) + ] + }); + + console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); }); + this._proxy = extensionHostProcessWorker.start().then( + (protocol) => { + return { value: this._begin(protocol) }; + }, + (err) => { + console.error('Error received from starting extension host'); + console.error(err); + } + ); this._scanAndHandleExtensions(); } @@ -242,14 +272,14 @@ export class ExtensionService implements IExtensionService { }); userExtensions.forEach((userExtension) => { if (result.hasOwnProperty(userExtension.id)) { - log.warn(userExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath)); + log.warn(userExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath)); } result[userExtension.id] = userExtension; }); developedExtensions.forEach(developedExtension => { - log.info('', localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath)); + log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath)); if (result.hasOwnProperty(developedExtension.id)) { - log.warn(developedExtension.extensionFolderPath, localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath)); + log.warn(developedExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath)); } result[developedExtension.id] = developedExtension; }); From c2e0b36d9b75d6258c53d42716f078c22907435d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 17 Aug 2017 15:41:35 +0200 Subject: [PATCH 30/96] Implement extension host restarting --- .../electron-browser/mainThreadDocuments.ts | 4 +- .../api/node/extHostExtensionService.ts | 4 +- .../electron-browser/extensionService.ts | 149 +++++++++++++----- .../node/extensionDescriptionRegistry.ts | 4 + 4 files changed, 118 insertions(+), 43 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts index 1c5186a7144..d0fcfb8b158 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts @@ -99,10 +99,10 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { this._modelIsSynced = {}; this._toDispose = []; - this._toDispose.push(this._modelReferenceCollection); this._toDispose.push(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this))); this._toDispose.push(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this))); - modelService.onModelModeChanged(this._onModelModeChanged, this, this._toDispose); + this._toDispose.push(this._modelReferenceCollection); + this._toDispose.push(modelService.onModelModeChanged(this._onModelModeChanged, this)); this._toDispose.push(textFileService.models.onModelSaved(e => { if (this._shouldHandleFileEvent(e)) { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 0b92e69bd94..d14ff88d232 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -14,7 +14,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl'; -import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData } from './extHost.protocol'; +import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape } from './extHost.protocol'; import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule } from "vs/workbench/api/node/extHostExtensionActivator"; import { Barrier } from "vs/workbench/services/extensions/node/barrier"; import { ExtHostThreadService } from "vs/workbench/services/thread/node/extHostThreadService"; @@ -105,7 +105,7 @@ class ExtensionStoragePath { } } -export class ExtHostExtensionService { +export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _barrier: Barrier; private readonly _registry: ExtensionDescriptionRegistry; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 0bc754dbb67..340c982cd20 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -5,6 +5,7 @@ 'use strict'; import * as nls from 'vs/nls'; +import * as errors from 'vs/base/common/errors'; import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import pkg from 'vs/platform/node/package'; @@ -30,7 +31,7 @@ import { IMessagePassingProtocol } from "vs/base/parts/ipc/common/ipc"; import { ExtHostCustomersRegistry } from "vs/workbench/api/electron-browser/extHostCustomers"; import { IWindowService } from "vs/platform/windows/common/windows"; import { Action } from "vs/base/common/actions"; -import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; +import { IDisposable } from "vs/base/common/lifecycle"; const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); @@ -55,14 +56,22 @@ export class ExtensionService implements IExtensionService { private readonly _barrier: Barrier; private readonly _isDev: boolean; private readonly _extensionsStatus: { [id: string]: IExtensionsStatus }; + private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; + + + // --- Members used per extension host process + /** * A map of already activated events to speed things up if the same activation event is triggered multiple times. */ - private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; }; - - // winjs believes a proxy is a promise because it has a `then` method, - // so wrap the result in an object. - private _proxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; + private _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; }; + private _extensionHostProcessWorker: ExtensionHostProcessWorker; + private _extensionHostProcessThreadService: MainThreadService; + private _extensionHostProcessCustomers: IDisposable[]; + /** + * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. + */ + private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -77,86 +86,148 @@ export class ExtensionService implements IExtensionService { this._barrier = new Barrier(); this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; this._extensionsStatus = {}; - this._alreadyActivatedEvents = Object.create(null); + this._allRequestedActivateEvents = Object.create(null); - const extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this); - extensionHostProcessWorker.onCrashed(([code, signal]) => { - const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, async (): TPromise => { - await this._windowService.openDevTools(); - return false; - }); + this._extensionHostProcessFinishedActivateEvents = Object.create(null); + this._extensionHostProcessWorker = null; + this._extensionHostProcessThreadService = null; + this._extensionHostProcessCustomers = []; + this._extensionHostProcessProxy = null; - let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly. Please reload the window to recover."); - if (code === 87) { - message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive. Please reload the window to recover."); + this._startExtensionHostProcess([]); + this._scanAndHandleExtensions(); + } + + private _stopExtensionHostProcess(): void { + this._extensionHostProcessFinishedActivateEvents = Object.create(null); + if (this._extensionHostProcessWorker) { + this._extensionHostProcessWorker.dispose(); + this._extensionHostProcessWorker = null; + } + if (this._extensionHostProcessThreadService) { + // this._extensionHostProcessThreadService.dispose(); // TODO@rehost + this._extensionHostProcessThreadService = null; + } + for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) { + const customer = this._extensionHostProcessCustomers[i]; + try { + customer.dispose(); + } catch (err) { + errors.onUnexpectedError(err); } - this._messageService.show(Severity.Error, { - message: message, - actions: [ - openDevTools, - this._instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL) - ] - }); + } + this._extensionHostProcessCustomers = []; + this._extensionHostProcessProxy = null; + } - console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); - }); - this._proxy = extensionHostProcessWorker.start().then( + private _startExtensionHostProcess(initialActivationEvents: string[]): void { + this._stopExtensionHostProcess(); + + this._extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this); + this._extensionHostProcessWorker.onCrashed(([code, signal]) => this._onExtensionHostCrashed(code, signal)); + this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then( (protocol) => { - return { value: this._begin(protocol) }; + return { value: this._createExtensionHostCustomers(protocol) }; }, (err) => { console.error('Error received from starting extension host'); console.error(err); + return null; } ); - - this._scanAndHandleExtensions(); + this._extensionHostProcessProxy.then(() => { + initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent)); + }); } - private _begin(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { + private _onExtensionHostCrashed(code: number, signal: string): void { + const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, (): TPromise => { + return this._windowService.openDevTools().then(() => false); + }); - const threadService = this._instantiationService.createInstance(MainThreadService, protocol); - const extHostContext: IExtHostContext = threadService; + const restart = new Action('restart', nls.localize('restart', "Restart Extension Host"), '', true, (): TPromise => { + this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents)); + return TPromise.as(true); + }); + + console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + this._stopExtensionHostProcess(); + + let message = nls.localize('extensionHostProcess.crash', "Extension host terminated unexpectedly."); + if (code === 87) { + message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive."); + } + this._messageService.show(Severity.Error, { + message: message, + actions: [ + openDevTools, + restart + ] + }); + } + + private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { + + this._extensionHostProcessThreadService = this._instantiationService.createInstance(MainThreadService, protocol); + const extHostContext: IExtHostContext = this._extensionHostProcessThreadService; // Named customers const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers(); for (let i = 0, len = namedCustomers.length; i < len; i++) { const [id, ctor] = namedCustomers[i]; - threadService.set(id, this._instantiationService.createInstance(ctor, extHostContext)); + const instance = this._instantiationService.createInstance(ctor, extHostContext); + this._extensionHostProcessCustomers.push(instance); + this._extensionHostProcessThreadService.set(id, instance); } // Customers const customers = ExtHostCustomersRegistry.getCustomers(); for (let i = 0, len = customers.length; i < len; i++) { const ctor = customers[i]; - this._instantiationService.createInstance(ctor, extHostContext); + const instance = this._instantiationService.createInstance(ctor, extHostContext); + this._extensionHostProcessCustomers.push(instance); } // Check that no named customers are missing const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => MainContext[key]); - threadService.assertRegistered(expected); + this._extensionHostProcessThreadService.assertRegistered(expected); - return threadService.get(ExtHostContext.ExtHostExtensionService); + return this._extensionHostProcessThreadService.get(ExtHostContext.ExtHostExtensionService); } // ---- begin IExtensionService public activateByEvent(activationEvent: string): TPromise { if (this._barrier.isOpen) { + // Extensions have been scanned and interpreted + + if (!this._registry.containsActivationEvent(activationEvent)) { + // There is no extension that is interested in this activation event + return NO_OP_VOID_PROMISE; + } + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + return this._activateByEvent(activationEvent); } else { + // Extensions have not been scanned yet. + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + return this._barrier.wait().then(() => this._activateByEvent(activationEvent)); } } protected _activateByEvent(activationEvent: string): TPromise { - if (this._alreadyActivatedEvents[activationEvent]) { + if (this._extensionHostProcessFinishedActivateEvents[activationEvent]) { return NO_OP_VOID_PROMISE; } - return this._proxy.then((proxy) => { + return this._extensionHostProcessProxy.then((proxy) => { return proxy.value.$activateByEvent(activationEvent); }).then(() => { - this._alreadyActivatedEvents[activationEvent] = true; + this._extensionHostProcessFinishedActivateEvents[activationEvent] = true; }); } diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index 9f1e8df89b4..0a0042db484 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -40,6 +40,10 @@ export class ExtensionDescriptionRegistry { } } + public containsActivationEvent(activationEvent: string): boolean { + return hasOwnProperty.call(this._activationMap, activationEvent); + } + public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] { if (!hasOwnProperty.call(this._activationMap, activationEvent)) { return []; From dbf8ca266cc57f1388105bd9499a1754e975f991 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Aug 2017 15:45:49 +0200 Subject: [PATCH 31/96] inline PathSplitter for less code --- src/vs/base/common/map.ts | 2 +- src/vs/base/test/common/map.test.ts | 7 ++++--- .../configuration/test/common/testConfigurationService.ts | 2 +- src/vs/platform/workspace/common/workspace.ts | 4 ++-- src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/parts/search/common/searchModel.ts | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 560103a08c6..003e893e7f8 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -238,7 +238,7 @@ export class TrieMap { private readonly _splitter: (s: string) => string[]; private _root = new Node(); - constructor(splitter: (s: string) => string[]) { + constructor(splitter: (s: string) => string[] = TrieMap.PathSplitter) { this._splitter = s => splitter(s).filter(s => Boolean(s)); } diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 58c8413ba84..b26a2079943 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -5,6 +5,7 @@ 'use strict'; + import { BoundedMap, TrieMap, ResourceMap } from 'vs/base/common/map'; import * as assert from 'assert'; import URI from 'vs/base/common/uri'; @@ -313,7 +314,7 @@ suite('Map', () => { test('TrieMap - basics', function () { - const map = new TrieMap(TrieMap.PathSplitter); + const map = new TrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); @@ -331,7 +332,7 @@ suite('Map', () => { test('TrieMap - lookup', function () { - const map = new TrieMap(TrieMap.PathSplitter); + const map = new TrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); map.insert('/user/foo/flip/flop', 3); @@ -345,7 +346,7 @@ suite('Map', () => { test('TrieMap - superstr', function () { - const map = new TrieMap(TrieMap.PathSplitter); + const map = new TrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); map.insert('/user/foo/flip/flop', 3); diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index edd06713cf0..e7f948d824c 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -17,7 +17,7 @@ export class TestConfigurationService extends EventEmitter implements IConfigura private configuration = Object.create(null); - private configurationByRoot: TrieMap = new TrieMap(TrieMap.PathSplitter); + private configurationByRoot: TrieMap = new TrieMap(); public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 473099f12b8..7997a686697 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -142,7 +142,7 @@ export class LegacyWorkspace implements ILegacyWorkspace { export class Workspace implements IWorkspace { - private _rootsMap: TrieMap = new TrieMap(TrieMap.PathSplitter); + private _rootsMap: TrieMap = new TrieMap(); constructor( public readonly id: string, @@ -187,7 +187,7 @@ export class Workspace implements IWorkspace { } private updateRootsMap(): void { - this._rootsMap = new TrieMap(TrieMap.PathSplitter); + this._rootsMap = new TrieMap(); for (const root of this.roots) { this._rootsMap.insert(root.fsPath, root); } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 7234a7a1cdd..42991687a61 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -627,7 +627,7 @@ export function initializeExtensionApi(extensionService: ExtHostExtensionService function createExtensionPathIndex(extensionService: ExtHostExtensionService): TPromise> { // create trie to enable fast 'filename -> extension id' look up - const trie = new TrieMap(TrieMap.PathSplitter); + const trie = new TrieMap(); const extensions = extensionService.getAllExtensionDescriptions().map(ext => { if (!ext.main) { return undefined; diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 4e45a8e1d6b..67591e57e7d 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -494,7 +494,7 @@ export class SearchResult extends Disposable { public onChange: Event = this._onChange.event; private _folderMatches: FolderMatch[] = []; - private _folderMatchesMap: TrieMap = new TrieMap(TrieMap.PathSplitter); + private _folderMatchesMap: TrieMap = new TrieMap(); private _query: ISearchQuery = null; private _showHighlights: boolean; @@ -652,7 +652,7 @@ export class SearchResult extends Disposable { private disposeMatches(): void { this._folderMatches.forEach(folderMatch => folderMatch.dispose()); this._folderMatches = []; - this._folderMatchesMap = new TrieMap(TrieMap.PathSplitter); + this._folderMatchesMap = new TrieMap(); this._rangeHighlightDecorations.removeHighlightRange(); } From 82b7f2433b884c80c7bd74667c735709c5914672 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 17 Aug 2017 10:02:26 +0200 Subject: [PATCH 32/96] [themes] 'italic' on fontStyle marked as error --- src/vs/workbench/services/themes/common/colorThemeSchema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/themes/common/colorThemeSchema.ts b/src/vs/workbench/services/themes/common/colorThemeSchema.ts index e6a4a910d32..5c6b7609512 100644 --- a/src/vs/workbench/services/themes/common/colorThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/colorThemeSchema.ts @@ -127,7 +127,7 @@ export const tokenColorizationSettingSchema: IJSONSchema = { fontStyle: { type: 'string', description: nls.localize('schema.token.fontStyle', 'Font style of the rule: One or a combination of \'italic\', \'bold\' and \'underline\''), - pattern: '^(\\s*\\b(italics|bold|underline))*\\s*$', + pattern: '^(\\s*\\b(italic|bold|underline))*\\s*$', patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be a combination of \'italic\', \'bold\' and \'underline\''), defaultSnippets: [{ body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }] } From 64fd8f8720830ab698c35e56003f7bdebc78ef73 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 17 Aug 2017 15:22:06 +0200 Subject: [PATCH 33/96] [python] update grammar --- .../syntaxes/MagicPython.tmLanguage.json | 85 ++++++++++------- .../syntaxes/MagicRegExp.tmLanguage.json | 33 ++++++- .../python/test/colorize-results/test_py.json | 95 +++++++++++++++++-- 3 files changed, 167 insertions(+), 46 deletions(-) diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index 3a9f24cc41e..812ddf97357 100644 --- a/extensions/python/syntaxes/MagicPython.tmLanguage.json +++ b/extensions/python/syntaxes/MagicPython.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/MagicStack/MagicPython/commit/976e59dcb78cb577e79c8f2117216c06718337e0", + "version": "https://github.com/MagicStack/MagicPython/commit/b453f26ed856c9b16a053517c41207e3a72cc7d5", "name": "MagicPython", "scopeName": "source.python", "fileTypes": [ @@ -260,14 +260,6 @@ } } }, - "codetags": { - "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", - "captures": { - "1": { - "name": "keyword.codetag.notation.python" - } - } - }, "statement-keyword": { "patterns": [ { @@ -392,8 +384,13 @@ ] }, "member-access": { - "begin": "\\.\\s*(?!\\.)", + "begin": "(\\.)\\s*(?!\\.)", "end": "(?x)\n # stop when you've just read non-whitespace followed by non-word\n # i.e. when finished reading an identifier or function call\n (?<=\\S)(?=\\W) |\n # stop when seeing the start of something that's not a word,\n # i.e. when seeing a non-identifier\n (^|(?<=\\s))(?=[^\\\\\\w\\s]) |\n $\n", + "beginCaptures": { + "1": { + "name": "punctuation.separator.period.python" + } + }, "patterns": [ { "include": "#function-call" @@ -918,29 +915,6 @@ } ] }, - "fstring-formatting-braces": { - "patterns": [ - { - "comment": "empty braces are illegal", - "match": "({)(\\s*?)(})", - "captures": { - "1": { - "name": "constant.character.format.placeholder.other.python" - }, - "2": { - "name": "invalid.illegal.brace.python" - }, - "3": { - "name": "constant.character.format.placeholder.other.python" - } - } - }, - { - "name": "constant.character.escape.python", - "match": "({{|}})" - } - ] - }, "fstring-formatting-singe-brace": { "name": "invalid.illegal.brace.python", "match": "(}(?!}))" @@ -949,11 +923,14 @@ "comment": "Import statements\n", "patterns": [ { - "match": "(?x)\n \\s* \\b(from)\\b (\\s*\\.+\\s*) (import)?\n", + "match": "(?x)\n \\s* \\b(from)\\b \\s*(\\.+)\\s* (import)?\n", "captures": { "1": { "name": "keyword.control.import.python" }, + "2": { + "name": "punctuation.separator.period.python" + }, "3": { "name": "keyword.control.import.python" } @@ -1077,8 +1054,13 @@ } }, "member-access-class": { - "begin": "\\.\\s*(?!\\.)", + "begin": "(\\.)\\s*(?!\\.)", "end": "(?<=\\S)(?=\\W)|$", + "beginCaptures": { + "1": { + "name": "punctuation.separator.period.python" + } + }, "patterns": [ { "include": "#call-wrapper-inheritance" @@ -1671,7 +1653,7 @@ }, "magic-variable-names": { "comment": "magic variables which a class/module may have.", - "match": "(?x)\n \\b(\n __(?:\n all | bases | builtins | class | code | debug | defaults | dict\n | doc | file | func | kwdefaults | members\n | metaclass | methods | module | mro | name\n | qualname | self | signature | slots | subclasses\n | version | weakref | wrapped | annotations | classcell\n | spec | path | package | future\n )__\n )\\b\n", + "match": "(?x)\n \\b(\n __(?:\n all | bases | builtins | class | code | debug | defaults | dict\n | doc | file | func | kwdefaults | members\n | metaclass | methods | module | mro | name\n | qualname | self | signature | slots | subclasses\n | version | weakref | wrapped | annotations | classcell\n | spec | path | package | future | traceback\n )__\n )\\b\n", "captures": { "1": { "name": "support.variable.magic.python" @@ -1769,6 +1751,29 @@ } ] }, + "fstring-formatting-braces": { + "patterns": [ + { + "comment": "empty braces are illegal", + "match": "({)(\\s*?)(})", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + }, + "2": { + "name": "invalid.illegal.brace.python" + }, + "3": { + "name": "constant.character.format.placeholder.other.python" + } + } + }, + { + "name": "constant.character.escape.python", + "match": "({{|}})" + } + ] + }, "regexp-base-common": { "patterns": [ { @@ -1891,6 +1896,14 @@ } ] }, + "codetags": { + "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", + "captures": { + "1": { + "name": "keyword.codetag.notation.python" + } + } + }, "comments-base": { "name": "comment.line.number-sign.python", "begin": "(\\#)", diff --git a/extensions/python/syntaxes/MagicRegExp.tmLanguage.json b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json index c6c74610836..0de066f3677 100644 --- a/extensions/python/syntaxes/MagicRegExp.tmLanguage.json +++ b/extensions/python/syntaxes/MagicRegExp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/MagicStack/MagicPython/commit/df5bb18c64252f2e7b1aa87e2ed124666d314f1d", + "version": "https://github.com/MagicStack/MagicPython/commit/361a4964a559481330764a447e7bab88d4f1b01b", "name": "MagicRegExp", "scopeName": "source.regexp.python", "fileTypes": [ @@ -43,6 +43,29 @@ } ] }, + "fstring-formatting-braces": { + "patterns": [ + { + "comment": "empty braces are illegal", + "match": "({)(\\s*?)(})", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" + }, + "2": { + "name": "invalid.illegal.brace.python" + }, + "3": { + "name": "constant.character.format.placeholder.other.python" + } + } + }, + { + "name": "constant.character.escape.python", + "match": "({{|}})" + } + ] + }, "regexp-base-common": { "patterns": [ { @@ -165,6 +188,14 @@ } ] }, + "codetags": { + "match": "(?:\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\b)", + "captures": { + "1": { + "name": "keyword.codetag.notation.python" + } + } + }, "regexp-expression": { "patterns": [ { diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json index 5d58f2c7297..b456114b1ed 100644 --- a/extensions/python/test/colorize-results/test_py.json +++ b/extensions/python/test/colorize-results/test_py.json @@ -407,7 +407,29 @@ } }, { - "c": "banana.size", + "c": "banana", + "t": "source.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "size", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -2189,7 +2211,7 @@ } }, { - "c": " g.", + "c": " g", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -2199,6 +2221,17 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "next", "t": "source.python meta.function-call.python meta.function-call.generic.python", @@ -4499,7 +4532,18 @@ } }, { - "c": ".fn ", + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fn ", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -4554,7 +4598,18 @@ } }, { - "c": ".memo ", + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo ", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -4829,7 +4884,18 @@ } }, { - "c": ".memo", + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "memo", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -4874,7 +4940,7 @@ }, { "c": ".", - "t": "source.python", + "t": "source.python punctuation.separator.period.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4973,7 +5039,7 @@ }, { "c": ".", - "t": "source.python", + "t": "source.python punctuation.separator.period.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5083,7 +5149,7 @@ }, { "c": ".", - "t": "source.python", + "t": "source.python punctuation.separator.period.python", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5159,7 +5225,7 @@ } }, { - "c": " re.", + "c": " re", "t": "source.python", "r": { "dark_plus": "default: #D4D4D4", @@ -5169,6 +5235,17 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": ".", + "t": "source.python punctuation.separator.period.python", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "search", "t": "source.python meta.function-call.python meta.function-call.generic.python", From 47ec343bfab4a04d6ad40f00428b717519516ca0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 17 Aug 2017 16:22:28 +0200 Subject: [PATCH 34/96] [css] add multiroot support. Fixes #32486 --- extensions/css/client/src/cssMain.ts | 4 +- extensions/css/npm-shrinkwrap.json | 18 +++---- extensions/css/package.json | 63 +++++++++++++++++++++- extensions/css/server/npm-shrinkwrap.json | 27 ++++++---- extensions/css/server/package.json | 4 +- extensions/css/server/src/cssServerMain.ts | 38 +++++++++++-- 6 files changed, 126 insertions(+), 28 deletions(-) diff --git a/extensions/css/client/src/cssMain.ts b/extensions/css/client/src/cssMain.ts index 1080628cad4..a9da1608c47 100644 --- a/extensions/css/client/src/cssMain.ts +++ b/extensions/css/client/src/cssMain.ts @@ -9,6 +9,7 @@ import * as path from 'path'; import { languages, window, commands, workspace, ExtensionContext } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, Range, TextEdit } from 'vscode-languageclient'; import { activateColorDecorations, ColorProvider } from './colorDecorators'; +import { GetConfigurationFeature } from 'vscode-languageclient/lib/proposed'; import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); @@ -23,7 +24,7 @@ export function activate(context: ExtensionContext) { // The server is implemented in node let serverModule = context.asAbsolutePath(path.join('server', 'out', 'cssServerMain.js')); // The debug options for the server - let debugOptions = { execArgv: ['--nolazy', '--debug=6004'] }; + let debugOptions = { execArgv: ['--nolazy', '--inspect=6004'] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used @@ -44,6 +45,7 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions); + client.registerFeature(new GetConfigurationFeature(client)); let disposable = client.start(); // Push the disposable to the context's subscriptions so that the diff --git a/extensions/css/npm-shrinkwrap.json b/extensions/css/npm-shrinkwrap.json index 89c99f09cf0..b09c91da50a 100644 --- a/extensions/css/npm-shrinkwrap.json +++ b/extensions/css/npm-shrinkwrap.json @@ -13,19 +13,19 @@ "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz" }, "vscode-jsonrpc": { - "version": "3.2.0", - "from": "vscode-jsonrpc@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.2.0.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageclient": { - "version": "3.2.0", - "from": "vscode-languageclient@3.2.0", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.2.0.tgz" + "version": "3.4.0-next.4", + "from": "vscode-languageclient@next", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.4.tgz" }, "vscode-languageserver-types": { - "version": "3.2.0", - "from": "vscode-languageserver-types@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", diff --git a/extensions/css/package.json b/extensions/css/package.json index ff0bf9ac994..7327b530c1e 100644 --- a/extensions/css/package.json +++ b/extensions/css/package.json @@ -62,16 +62,19 @@ "properties": { "css.validate": { "type": "boolean", + "scope": "resource", "default": true, "description": "%css.validate.desc%" }, "css.colorDecorators.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%css.colorDecorators.enable.desc%" }, "css.lint.compatibleVendorPrefixes": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -82,6 +85,7 @@ }, "css.lint.vendorPrefix": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -92,6 +96,7 @@ }, "css.lint.duplicateProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -102,6 +107,7 @@ }, "css.lint.emptyRules": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -112,6 +118,7 @@ }, "css.lint.importStatement": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -122,6 +129,7 @@ }, "css.lint.boxModel": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -132,6 +140,7 @@ }, "css.lint.universalSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -142,6 +151,7 @@ }, "css.lint.zeroUnits": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -152,6 +162,7 @@ }, "css.lint.fontFaceProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -162,6 +173,7 @@ }, "css.lint.hexColorLength": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -172,6 +184,7 @@ }, "css.lint.argumentsInColorFunction": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -182,6 +195,7 @@ }, "css.lint.unknownProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -192,6 +206,7 @@ }, "css.lint.ieHack": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -202,6 +217,7 @@ }, "css.lint.unknownVendorSpecificProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -212,6 +228,7 @@ }, "css.lint.propertyIgnoredDueToDisplay": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -222,6 +239,7 @@ }, "css.lint.important": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -232,6 +250,7 @@ }, "css.lint.float": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -242,6 +261,7 @@ }, "css.lint.idSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -252,6 +272,7 @@ }, "css.trace.server": { "type": "string", + "scope": "window", "enum": [ "off", "messages", @@ -274,16 +295,19 @@ "properties": { "scss.validate": { "type": "boolean", + "scope": "resource", "default": true, "description": "%scss.validate.desc%" }, "scss.colorDecorators.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%scss.colorDecorators.enable.desc%" }, "scss.lint.compatibleVendorPrefixes": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -294,6 +318,7 @@ }, "scss.lint.vendorPrefix": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -304,6 +329,7 @@ }, "scss.lint.duplicateProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -314,6 +340,7 @@ }, "scss.lint.emptyRules": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -324,6 +351,7 @@ }, "scss.lint.importStatement": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -334,6 +362,7 @@ }, "scss.lint.boxModel": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -344,6 +373,7 @@ }, "scss.lint.universalSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -354,6 +384,7 @@ }, "scss.lint.zeroUnits": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -364,6 +395,7 @@ }, "scss.lint.fontFaceProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -374,6 +406,7 @@ }, "scss.lint.hexColorLength": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -384,6 +417,7 @@ }, "scss.lint.argumentsInColorFunction": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -394,6 +428,7 @@ }, "scss.lint.unknownProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -404,6 +439,7 @@ }, "scss.lint.ieHack": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -414,6 +450,7 @@ }, "scss.lint.unknownVendorSpecificProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -424,6 +461,7 @@ }, "scss.lint.propertyIgnoredDueToDisplay": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -434,6 +472,7 @@ }, "scss.lint.important": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -444,6 +483,7 @@ }, "scss.lint.float": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -454,6 +494,7 @@ }, "scss.lint.idSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -477,16 +518,19 @@ "properties": { "less.validate": { "type": "boolean", + "scope": "resource", "default": true, "description": "%less.validate.desc%" }, "less.colorDecorators.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%less.colorDecorators.enable.desc%" }, "less.lint.compatibleVendorPrefixes": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -497,6 +541,7 @@ }, "less.lint.vendorPrefix": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -507,6 +552,7 @@ }, "less.lint.duplicateProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -517,6 +563,7 @@ }, "less.lint.emptyRules": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -527,6 +574,7 @@ }, "less.lint.importStatement": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -537,6 +585,7 @@ }, "less.lint.boxModel": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -547,6 +596,7 @@ }, "less.lint.universalSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -557,6 +607,7 @@ }, "less.lint.zeroUnits": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -567,6 +618,7 @@ }, "less.lint.fontFaceProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -577,6 +629,7 @@ }, "less.lint.hexColorLength": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -587,6 +640,7 @@ }, "less.lint.argumentsInColorFunction": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -597,6 +651,7 @@ }, "less.lint.unknownProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -607,6 +662,7 @@ }, "less.lint.ieHack": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -617,6 +673,7 @@ }, "less.lint.unknownVendorSpecificProperties": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -627,6 +684,7 @@ }, "less.lint.propertyIgnoredDueToDisplay": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -637,6 +695,7 @@ }, "less.lint.important": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -647,6 +706,7 @@ }, "less.lint.float": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -657,6 +717,7 @@ }, "less.lint.idSelector": { "type": "string", + "scope": "resource", "enum": [ "ignore", "warning", @@ -674,7 +735,7 @@ }, "dependencies": { "parse-color": "^1.0.0", - "vscode-languageclient": "^3.2.0", + "vscode-languageclient": "^3.4.0-next.4", "vscode-nls": "^2.0.2" }, "devDependencies": { diff --git a/extensions/css/server/npm-shrinkwrap.json b/extensions/css/server/npm-shrinkwrap.json index 21329db6203..01251cf3191 100644 --- a/extensions/css/server/npm-shrinkwrap.json +++ b/extensions/css/server/npm-shrinkwrap.json @@ -3,29 +3,34 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.2", + "version": "2.1.3", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.2.tgz" + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.3.tgz" }, "vscode-jsonrpc": { - "version": "3.2.0", - "from": "vscode-jsonrpc@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.2.0.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageserver": { - "version": "3.2.0", - "from": "vscode-languageserver@3.2.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.2.0.tgz" + "version": "3.4.0-next.2", + "from": "vscode-languageserver@next", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.2.tgz" }, "vscode-languageserver-types": { - "version": "3.2.0", - "from": "vscode-languageserver-types@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", "from": "vscode-nls@>=2.0.1 <3.0.0", "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "vscode-uri": { + "version": "1.0.1", + "from": "vscode-uri@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.1.tgz" } } } diff --git a/extensions/css/server/package.json b/extensions/css/server/package.json index c7b93d1573f..9b96f1450c6 100644 --- a/extensions/css/server/package.json +++ b/extensions/css/server/package.json @@ -8,8 +8,8 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.2", - "vscode-languageserver": "^3.2.0" + "vscode-css-languageservice": "^2.1.3", + "vscode-languageserver": "^3.4.0-next.2" }, "devDependencies": { "@types/node": "^6.0.51" diff --git a/extensions/css/server/src/cssServerMain.ts b/extensions/css/server/src/cssServerMain.ts index 573b4b61c5f..9b86bdaac5d 100644 --- a/extensions/css/server/src/cssServerMain.ts +++ b/extensions/css/server/src/cssServerMain.ts @@ -8,6 +8,7 @@ import { createConnection, IConnection, Range, TextDocuments, TextDocument, InitializeParams, InitializeResult, RequestType } from 'vscode-languageserver'; +import { GetConfigurationRequest } from 'vscode-languageserver/lib/protocol.proposed'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -43,10 +44,20 @@ connection.onShutdown(() => { stylesheets.dispose(); }); +let scopedSettingsSupport = false; // After the server has started the client sends an initilize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { - let snippetSupport = params.capabilities && params.capabilities.textDocument && params.capabilities.textDocument.completion && params.capabilities.textDocument.completion.completionItem && params.capabilities.textDocument.completion.completionItem.snippetSupport; + function hasClientCapability(name: string) { + let keys = name.split('.'); + let c = params.capabilities; + for (let i = 0; c && i < keys.length; i++) { + c = c[keys[i]]; + } + return !!c; + } + let snippetSupport = hasClientCapability('textDocument.completion.completionItem.snippetSupport'); + scopedSettingsSupport = hasClientCapability('workspace.getConfiguration'); return { capabilities: { // Tell the client that the server works in FULL text document sync mode @@ -78,6 +89,20 @@ function getLanguageService(document: TextDocument) { return service; } +let documentSettings: { [key: string]: Thenable } = {}; +function getDocumentSettings(textDocument: TextDocument): Thenable { + if (scopedSettingsSupport) { + let promise = documentSettings[textDocument.uri]; + if (!promise) { + let configRequestParam = { scopeUris: [textDocument.uri], sections: [textDocument.languageId] }; + promise = connection.sendRequest(GetConfigurationRequest.type, configRequestParam).then(s => s[0][0]); + documentSettings[textDocument.uri] = promise; + } + return promise; + } + return void 0; +} + // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration(change => { updateConfiguration(change.settings); @@ -87,6 +112,8 @@ function updateConfiguration(settings: Settings) { for (let languageId in languageServices) { languageServices[languageId].configure(settings[languageId]); } + // reset all document settings + documentSettings = {}; // Revalidate any open text documents documents.all().forEach(triggerValidation); } @@ -123,10 +150,13 @@ function triggerValidation(textDocument: TextDocument): void { } function validateTextDocument(textDocument: TextDocument): void { + let settingsPromise = getDocumentSettings(textDocument); let stylesheet = stylesheets.get(textDocument); - let diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet); - // Send the computed diagnostics to VSCode. - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + settingsPromise.then(settings => { + let diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); + // Send the computed diagnostics to VSCode. + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + }); } connection.onCompletion(textDocumentPosition => { From cfc660f890b34988a6c37fb3a494919da4d3ea82 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 17 Aug 2017 08:28:15 -0700 Subject: [PATCH 35/96] Add null check in TerminalInstance.onExit Fixes #32570 --- .../parts/terminal/electron-browser/terminalInstance.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 19bee0cac6c..c4f15f39810 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -768,7 +768,9 @@ export class TerminalInstance implements ITerminalInstance { } public onExit(listener: (exitCode: number) => void): lifecycle.IDisposable { - this._process.on('exit', listener); + if (this._process) { + this._process.on('exit', listener); + } return { dispose: () => { if (this._process) { From cd64f95a9e85fe6e3170ca06dd10aefdc9e8ea55 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 17 Aug 2017 08:32:03 -0700 Subject: [PATCH 36/96] Fix exception when terminal args are not strings Fixes #32564 --- .../parts/terminal/electron-browser/terminalInstance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index c4f15f39810..3b2a163b332 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -667,7 +667,7 @@ export class TerminalInstance implements ITerminalInstance { args = this._shellLaunchConfig.args; } else if (this._shellLaunchConfig.args && this._shellLaunchConfig.args.length) { args = ' ' + this._shellLaunchConfig.args.map(a => { - if (a.indexOf(' ') !== -1) { + if (typeof a === 'string' && a.indexOf(' ') !== -1) { return `'${a}'`; } return a; From eee02ecda2e08c379bca21054e65a0f391544869 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 17 Aug 2017 08:52:41 -0700 Subject: [PATCH 37/96] Uplevel xterm.js This pulls many changes from the v3 branch. Fixes #32725 --- npm-shrinkwrap.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1112f9fcfdb..fd2a06ef420 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -567,9 +567,9 @@ "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz" }, "xterm": { - "version": "2.8.1", - "from": "Tyriar/xterm.js#vscode-release/1.15", - "resolved": "git+https://github.com/Tyriar/xterm.js.git#75ffea5ebd5510ad0478b017c3946cb8d504855f" + "version": "2.9.1", + "from": "Tyriar/xterm.js#vscode-release/1.16", + "resolved": "git+https://github.com/Tyriar/xterm.js.git#ec8e705ffef18ec9f90e27baff30105b0278b3bb" }, "yauzl": { "version": "2.8.0", diff --git a/package.json b/package.json index 5c0a1adc7e9..13c7d4e58a4 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "vscode-ripgrep": "0.0.25", "vscode-textmate": "^3.1.5", "winreg": "1.2.0", - "xterm": "Tyriar/xterm.js#vscode-release/1.15", + "xterm": "Tyriar/xterm.js#vscode-release/1.16", "yauzl": "2.8.0" }, "devDependencies": { From 1b547ed0ebd0c5b5c2dd391e09ecb8fc00ebec0f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 17 Aug 2017 11:14:58 -0700 Subject: [PATCH 38/96] Use the fspath when dropping file into terminal Fixes #32734 --- .../workbench/parts/terminal/electron-browser/terminalPanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 0d357f399a6..fa7c86eee17 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -246,7 +246,7 @@ export class TerminalPanel extends Panel { uri = URI.parse(uri).path; } else if (e.dataTransfer.files.length > 0) { // Check if the file was dragged from the filesystem - uri = URI.file(e.dataTransfer.files[0].path).path; + uri = URI.file(e.dataTransfer.files[0].path).fsPath; } if (!uri) { From 6263d2c288ca1fa988bde3b783d17c451004a399 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 17 Aug 2017 13:45:55 -0700 Subject: [PATCH 39/96] Update comments for getModelMarkers. --- src/vs/editor/standalone/browser/standaloneEditor.ts | 2 +- src/vs/monaco.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 3c37032c2c7..afd14619a58 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -195,7 +195,7 @@ export function setModelMarkers(model: editorCommon.IModel, owner: string, marke } /** - * Get markers for owner ant/or resource + * Get markers for owner and/or resource * @returns {IMarker[]} list of markers * @param filter */ diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8fff0f3d350..ae8c929e84e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -834,7 +834,7 @@ declare module monaco.editor { export function setModelMarkers(model: IModel, owner: string, markers: IMarkerData[]): void; /** - * Get markers for owner ant/or resource + * Get markers for owner and/or resource * @returns {IMarker[]} list of markers * @param filter */ From d8f5131aa4b9ff6e0add308734292485b4ee7b35 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Aug 2017 12:58:07 -0700 Subject: [PATCH 40/96] Trigger rename after refactor in JS/TS Fixes #32735 --- .../typescript/src/features/refactorProvider.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/extensions/typescript/src/features/refactorProvider.ts b/extensions/typescript/src/features/refactorProvider.ts index 4e930e09d80..1b9062998b5 100644 --- a/extensions/typescript/src/features/refactorProvider.ts +++ b/extensions/typescript/src/features/refactorProvider.ts @@ -5,7 +5,7 @@ 'use strict'; -import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit, window, QuickPickItem } from 'vscode'; +import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit, window, QuickPickItem, Selection, Position } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; @@ -123,6 +123,18 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { } const edit = this.toWorkspaceEdit(response.body.edits); - return workspace.applyEdit(edit); + if (!(await workspace.applyEdit(edit))) { + return false; + } + + const renameLocation = response.body.renameLocation; + if (renameLocation) { + if (window.activeTextEditor && window.activeTextEditor.document.uri.fsPath === file) { + const pos = new Position(renameLocation.line - 1, renameLocation.offset - 1); + window.activeTextEditor.selection = new Selection(pos, pos); + await commands.executeCommand('editor.action.rename'); + } + } + return true; } } \ No newline at end of file From 771ef9ede88c6845e1f30d7fff7c4a698f972ae8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Aug 2017 15:12:22 -0700 Subject: [PATCH 41/96] Show invalid version string if ts version cannot be determined --- extensions/typescript/src/utils/api.ts | 57 ++++++++++--------- .../typescript/src/utils/versionProvider.ts | 4 +- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/extensions/typescript/src/utils/api.ts b/extensions/typescript/src/utils/api.ts index 87749afc8e8..4fa9f1d5cb5 100644 --- a/extensions/typescript/src/utils/api.ts +++ b/extensions/typescript/src/utils/api.ts @@ -4,64 +4,65 @@ *--------------------------------------------------------------------------------------------*/ import * as semver from 'semver'; +import * as nls from 'vscode-nls'; +const localize = nls.loadMessageBundle(); export default class API { - public static readonly defaultVersion = new API('1.0.0'); + public static readonly defaultVersion = new API('1.0.0', '1.0.0'); - private readonly _version: string; - - constructor( - private readonly _versionString: string - ) { - this._version = semver.valid(_versionString); - if (!this._version) { - this._version = '1.0.0'; - } else { - // Cut of any prerelease tag since we sometimes consume those - // on purpose. - let index = _versionString.indexOf('-'); - if (index >= 0) { - this._version = this._version.substr(0, index); - } + public static fromVersionString(versionString: string): API { + let version = semver.valid(versionString); + if (!version) { + return new API(localize('invalidVersion', 'invalid version'), '1.0.0'); } + + // Cut of any prerelease tag since we sometimes consume those on purpose. + const index = versionString.indexOf('-'); + if (index >= 0) { + version = version.substr(0, index); + } + return new API(versionString, version); } - public get versionString(): string { - return this._versionString; - } + private constructor( + public readonly versionString: string, + private readonly version: string + ) { } + public has203Features(): boolean { - return semver.gte(this._version, '2.0.3'); + return semver.gte(this.version, '2.0.3'); } public has206Features(): boolean { - return semver.gte(this._version, '2.0.6'); + return semver.gte(this.version, '2.0.6'); } public has208Features(): boolean { - return semver.gte(this._version, '2.0.8'); + return semver.gte(this.version, '2.0.8'); } public has213Features(): boolean { - return semver.gte(this._version, '2.1.3'); + return semver.gte(this.version, '2.1.3'); } public has220Features(): boolean { - return semver.gte(this._version, '2.2.0'); + return semver.gte(this.version, '2.2.0'); } public has222Features(): boolean { - return semver.gte(this._version, '2.2.2'); + return semver.gte(this.version, '2.2.2'); } public has230Features(): boolean { - return semver.gte(this._version, '2.3.0'); + return semver.gte(this.version, '2.3.0'); } public has234Features(): boolean { - return semver.gte(this._version, '2.3.4'); + return semver.gte(this.version, '2.3.4'); } + public has240Features(): boolean { - return semver.gte(this._version, '2.4.0'); + return semver.gte(this.version, '2.4.0'); } } \ No newline at end of file diff --git a/extensions/typescript/src/utils/versionProvider.ts b/extensions/typescript/src/utils/versionProvider.ts index 57edf0d5116..a0a61db3872 100644 --- a/extensions/typescript/src/utils/versionProvider.ts +++ b/extensions/typescript/src/utils/versionProvider.ts @@ -41,7 +41,7 @@ export class TypeScriptVersion { // Allow TS developers to provide custom version const tsdkVersion = workspace.getConfiguration().get('typescript.tsdk_version', undefined); if (tsdkVersion) { - return new API(tsdkVersion); + return API.fromVersionString(tsdkVersion); } return undefined; @@ -78,7 +78,7 @@ export class TypeScriptVersion { if (!desc || !desc.version) { return undefined; } - return desc.version ? new API(desc.version) : undefined; + return desc.version ? API.fromVersionString(desc.version) : undefined; } } From 3266cff3a237530574d316f093c6fd517c988a0b Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 17 Aug 2017 17:04:44 -0700 Subject: [PATCH 42/96] Show Azure extensions link --- .../extensions/browser/extensionsActions.ts | 28 +++++++++++++ .../extensions.contribution.ts | 5 ++- .../page/electron-browser/welcomePage.ts | 40 +++++++++++-------- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index fc22516ec9e..3719581c61a 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -1166,6 +1166,34 @@ export class ShowLanguageExtensionsAction extends Action { } } +export class ShowAzureExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showAzureExtensions'; + static LABEL = localize('showAzureExtensions', "Show Azure Extensions"); + static SHORT_LABEL = localize('showAzureExtensionsShort', "Azure Extensions"); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService + ) { + super(id, label, null, true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@sort:installs azure '); + viewlet.focus(); + }); + } + + protected isEnabled(): boolean { + return true; + } +} + export class ChangeSortAction extends Action { private query: Query; 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 826cb9e9a3c..470b1613dac 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -24,7 +24,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/e import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, ConfigureWorkspaceRecommendedExtensionsAction, - EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction + EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, ShowAzureExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import { OpenExtensionsFolderAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; @@ -121,6 +121,9 @@ actionRegistry.registerWorkbenchAction(keymapRecommendationsActionDescriptor, 'P const languageExtensionsActionDescriptor = new SyncActionDescriptor(ShowLanguageExtensionsAction, ShowLanguageExtensionsAction.ID, ShowLanguageExtensionsAction.SHORT_LABEL); actionRegistry.registerWorkbenchAction(languageExtensionsActionDescriptor, 'Preferences: Language Extensions', PreferencesLabel); +const azureExtensionsActionDescriptor = new SyncActionDescriptor(ShowAzureExtensionsAction, ShowAzureExtensionsAction.ID, ShowAzureExtensionsAction.SHORT_LABEL); +actionRegistry.registerWorkbenchAction(azureExtensionsActionDescriptor, 'Preferences: Azure Extensions', PreferencesLabel); + const workspaceRecommendationsActionDescriptor = new SyncActionDescriptor(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(workspaceRecommendationsActionDescriptor, 'Extensions: Show Workspace Recommended Extensions', ExtensionsLabel); diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index 0235e8fc9c7..fd738117cd0 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -114,8 +114,10 @@ export class WelcomePageAction extends Action { interface ExtensionSuggestion { name: string; + title?: string; id: string; isKeymap?: boolean; + isCommand?: boolean; } const extensionPacks: ExtensionSuggestion[] = [ @@ -124,6 +126,7 @@ const extensionPacks: ExtensionSuggestion[] = [ { name: localize('welcomePage.python', "Python"), id: 'donjayamanne.python' }, // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, + { name: localize('welcomePage.azure', "Azure"), title: localize('welcomePage.showAzureExtensions', "Show Azure extensions"), id: 'workbench.extensions.action.showAzureExtensions', isCommand: true }, { name: localize('welcomePage.docker', "Docker"), id: 'PeterJausovec.vscode-docker' }, ]; @@ -322,23 +325,28 @@ class WelcomePage { const a = document.createElement('a'); a.innerText = extension.name; - a.title = extension.isKeymap ? localize('welcomePage.installKeymap', "Install {0} keymap", extension.name) : localize('welcomePage.installExtensionPack', "Install additional support for {0}", extension.name); - a.classList.add('installExtension'); - a.setAttribute('data-extension', extension.id); - a.href = 'javascript:void(0)'; - a.addEventListener('click', e => { - this.installExtension(extension, strings); - e.preventDefault(); - e.stopPropagation(); - }); - list.appendChild(a); + a.title = extension.title || (extension.isKeymap ? localize('welcomePage.installKeymap', "Install {0} keymap", extension.name) : localize('welcomePage.installExtensionPack', "Install additional support for {0}", extension.name)); + if (extension.isCommand) { + a.href = `command:${extension.id}`; + list.appendChild(a); + } else { + a.classList.add('installExtension'); + a.setAttribute('data-extension', extension.id); + a.href = 'javascript:void(0)'; + a.addEventListener('click', e => { + this.installExtension(extension, strings); + e.preventDefault(); + e.stopPropagation(); + }); + list.appendChild(a); - const span = document.createElement('span'); - span.innerText = extension.name; - span.title = extension.isKeymap ? localize('welcomePage.installedKeymap', "{0} keymap is already installed", extension.name) : localize('welcomePage.installedExtensionPack', "{0} support is already installed", extension.name); - span.classList.add('enabledExtension'); - span.setAttribute('data-extension', extension.id); - list.appendChild(span); + const span = document.createElement('span'); + span.innerText = extension.name; + span.title = extension.isKeymap ? localize('welcomePage.installedKeymap', "{0} keymap is already installed", extension.name) : localize('welcomePage.installedExtensionPack', "{0} support is already installed", extension.name); + span.classList.add('enabledExtension'); + span.setAttribute('data-extension', extension.id); + list.appendChild(span); + } }); } } From 799a4ea138e69f10b8f080a20bee3d3efa7a1f59 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 17 Aug 2017 17:15:17 -0700 Subject: [PATCH 43/96] Whitelist quickOpenNavigateNextInViewPicker in terminal Fixes #32347 --- .../parts/terminal/electron-browser/terminal.contribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index fe64fdb45ed..719ca5cd644 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -226,7 +226,8 @@ configurationRegistry.registerConfiguration({ NavigateRightAction.ID, NavigateLeftAction.ID, DeleteWordLeftTerminalAction.ID, - DeleteWordRightTerminalAction.ID + DeleteWordRightTerminalAction.ID, + 'workbench.action.quickOpenNavigateNextInViewPicker' ].sort() }, 'terminal.integrated.env.osx': { From aebaece67321b35f133be393fe8adcd50333d5ee Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Aug 2017 17:16:11 -0700 Subject: [PATCH 44/96] Only send TS plugins that have registered for the document's language Addresses comment on https://github.com/Microsoft/vscode/commit/c2ee6133aa87d915f246e1ce1c33ef9b4f1b3888 --- extensions/typescript/src/features/bufferSyncSupport.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/typescript/src/features/bufferSyncSupport.ts b/extensions/typescript/src/features/bufferSyncSupport.ts index 665b973ac12..975d4e9f529 100644 --- a/extensions/typescript/src/features/bufferSyncSupport.ts +++ b/extensions/typescript/src/features/bufferSyncSupport.ts @@ -54,12 +54,14 @@ class SyncedBuffer { } if (this.client.apiVersion.has240Features()) { - if (this.client.plugins.length) { - (args as any).plugins = this.client.plugins.map(x => x.name); + const tsPluginsForDocument = this.client.plugins + .filter(x => x.languages.indexOf(this.document.languageId) >= 0); + + if (tsPluginsForDocument.length) { + (args as any).plugins = tsPluginsForDocument.map(plugin => plugin.name); } } - this.client.execute('open', args, false); } From 066a2bb1416c7a5a9cf2069429721d125cf5ebb6 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 18 Aug 2017 00:02:57 -0700 Subject: [PATCH 45/96] Update Promise definition (#30268) * Update definition of Promise in winjs.base.d.ts. * Update to new Promise declaration * Fix remaining errors. --- build/monaco/monaco.d.ts.recipe | 13 +- src/vs/base/common/errors.ts | 12 +- src/vs/base/common/winjs.base.d.ts | 163 ++++++------------ .../electron-browser/sharedProcessMain.ts | 2 +- .../browser/goToDeclaration.ts | 5 +- .../browser/goToDeclarationCommands.ts | 1 - .../contrib/hover/browser/hoverOperation.ts | 2 +- .../browser/parameterHintsWidget.ts | 2 +- .../wordHighlighter/common/wordHighlighter.ts | 1 + src/vs/monaco.d.ts | 81 ++++----- .../node/extensionGalleryService.ts | 2 +- .../node/extensionManagementService.ts | 8 +- .../mainThreadDebugService.ts | 2 +- .../electron-browser/mainThreadTreeViews.ts | 10 +- .../workbench/api/node/extHostTextEditor.ts | 6 +- .../browser/parts/editor/editorPart.ts | 10 +- .../parts/quickopen/quickOpenController.ts | 1 + .../parts/backup/common/backupRestorer.ts | 2 +- .../cli/electron-browser/cli.contribution.ts | 9 +- .../debug/electron-browser/debugService.ts | 6 +- .../electron-browser/extensionsUtils.ts | 5 +- .../node/extensionsWorkbenchService.ts | 2 +- .../parts/files/browser/fileActions.ts | 5 +- .../parts/files/browser/fileCommands.ts | 4 +- .../parts/files/browser/views/explorerView.ts | 2 +- .../files/common/editors/fileEditorInput.ts | 2 +- .../preferences/browser/preferencesService.ts | 6 +- .../search/browser/openAnythingHandler.ts | 1 + .../parts/tasks/node/processRunnerDetector.ts | 6 +- .../page/electron-browser/welcomePage.ts | 2 +- .../node/configurationEditingService.ts | 10 +- .../electron-browser/extensionService.ts | 2 +- .../textfile/common/textFileService.ts | 2 +- .../electron-browser/workbenchThemeService.ts | 2 +- 34 files changed, 159 insertions(+), 230 deletions(-) diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 096f516afcf..9c2970d508b 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -5,16 +5,7 @@ declare module monaco { - interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; - } + type Thenable = PromiseLike; export interface IDisposable { dispose(): void; @@ -41,7 +32,7 @@ declare module monaco { Error = 3, } -#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, TPromise +#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, Promise #include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken #include(vs/base/common/uri): URI #include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 743e88c5946..a00d5ebd2ad 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -132,24 +132,24 @@ export function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler); } -export function onUnexpectedError(e: any): void { - +export function onUnexpectedError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedError(e); } + return undefined; } -export function onUnexpectedExternalError(e: any): void { - +export function onUnexpectedExternalError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedExternalError(e); } + return undefined; } -export function onUnexpectedPromiseError(promise: TPromise): TPromise { - return promise.then(null, onUnexpectedError); +export function onUnexpectedPromiseError(promise: TPromise): TPromise { + return promise.then(null, onUnexpectedError); } export function transformErrorForSerialization(error: any): any { diff --git a/src/vs/base/common/winjs.base.d.ts b/src/vs/base/common/winjs.base.d.ts index bb8ebf440d5..434db0a5fa8 100644 --- a/src/vs/base/common/winjs.base.d.ts +++ b/src/vs/base/common/winjs.base.d.ts @@ -4,110 +4,47 @@ *--------------------------------------------------------------------------------------------*/ /// Interfaces for WinJS -export interface ValueCallback { - (value: any): any; -} +export type ErrorCallback = (error: any) => void; +export type ProgressCallback = (progress: TProgress) => void; -export interface EventCallback { - (value: any): void; -} +export declare class Promise { + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason: any) => void, + progress: (progress: TProgress) => void) => void, + oncancel?: () => void); -export interface ErrorCallback { - (error: any): any; -} + public then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null, + onprogress?: (progress: TProgress) => void): Promise; -export interface ProgressCallback { - (progress: any): any; -} + public done( + onfulfilled?: (value: T) => void, + onrejected?: (reason: any) => void, + onprogress?: (progress: TProgress) => void): void; -export declare class Promise { - // commented out because this conflicts with the native promise - // constructor(init: (complete: ValueCallback, error: ErrorCallback, progress: ProgressCallback) => void, oncancel?: any); - - // commented out to speed up adoption of TPromise - // static as(value:any):Promise; - - // static join(promises: { [name: string]: Promise; }): Promise; - static join(promises: Promise[]): Promise; - // static any(promises: Promise[]): Promise; - - // commented out to speed up adoption of TPromise - // static timeout(delay:number):Promise; - - // static wrapError(error: Error): Promise; - // static is(value: any): value is Thenable; - // static addEventListener(type: string, fn: EventCallback): void; - - public then(success?: ValueCallback, error?: ErrorCallback, progress?: ProgressCallback): Promise; - // public then(success?: ValueCallback, error?: ErrorCallback, progress?: ProgressCallback): TPromise; - public done(success?: ValueCallback, error?: ErrorCallback, progress?: ProgressCallback): void; - public cancel(): void; -} - -/** - * The value callback to complete a promise - */ -export interface TValueCallback { - (value: T | Thenable): void; -} - - -export interface TProgressCallback { - (progress: T): void; -} - -interface IPromiseErrorDetail { - parent: TPromise; - error: any; - id: number; - handler: Function; - exception: Error; -} - -interface IPromiseError { - detail: IPromiseErrorDetail; -} - -/** - * A Promise implementation that supports progress and cancelation. - */ -export declare class TPromise { - - constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: ProgressCallback) => void, oncancel?: any); - - public then(success?: (value: V) => TPromise, error?: (err: any) => TPromise, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise, error?: (err: any) => TPromise | U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise, error?: (err: any) => U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise, error?: (err: any) => void, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => TPromise, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => TPromise | U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => TPromise | U, error?: (err: any) => void, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => TPromise, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => TPromise | U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => U, progress?: ProgressCallback): TPromise; - public then(success?: (value: V) => U, error?: (err: any) => void, progress?: ProgressCallback): TPromise; - - public done(success?: (value: V) => void, error?: (err: any) => any, progress?: ProgressCallback): void; public cancel(): void; - public static as(value: null): TPromise; - public static as(value: undefined): TPromise; - public static as(value: TPromise): TPromise; - public static as(value: Thenable): Thenable; - public static as(value: ValueType): TPromise; + public static as(value: null): Promise; + public static as(value: undefined): Promise; + public static as>(value: TPromise): TPromise; + public static as(value: T): Promise; - public static is(value: any): value is Thenable; - public static timeout(delay: number): TPromise; - public static join(promises: TPromise[]): TPromise; - public static join(promises: Thenable[]): Thenable; - public static join(promises: { [n: string]: TPromise }): TPromise<{ [n: string]: ValueType }>; - public static any(promises: TPromise[]): TPromise<{ key: string; value: TPromise; }>; + public static is(value: any): value is PromiseLike; - public static wrap(value: Thenable): TPromise; - public static wrap(value: ValueType): TPromise; + public static timeout(delay: number): Promise; - public static wrapError(error: Error): TPromise; + public static join(promises: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; + public static join(promises: (T | PromiseLike)[]): Promise; + public static join(promises: { [n: string]: T | PromiseLike }): Promise<{ [n: string]: T }>; + + public static any(promises: (T | PromiseLike)[]): Promise<{ key: string; value: Promise; }>; + + public static wrap(value: T | PromiseLike): Promise; + + public static wrapError(error: Error): Promise; /** * @internal @@ -115,25 +52,23 @@ export declare class TPromise { public static addEventListener(event: 'error', promiseErrorHandler: (e: IPromiseError) => void); } -// --- Generic promise with generic progress value -export declare class PPromise extends TPromise { +export type TValueCallback = (value: T | PromiseLike) => void; - constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: TProgressCallback

) => void, oncancel?: any); +export { + Promise as TPromise, + Promise as PPromise, + TValueCallback as ValueCallback, + ProgressCallback as TProgressCallback +}; - public then(success?: (value: C) => PPromise, error?: (err: any) => PPromise, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => PPromise, error?: (err: any) => U, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => PPromise, error?: (err: any) => void, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => U, error?: (err: any) => PPromise, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => U, error?: (err: any) => U, progress?: (value: P) => void): PPromise; - public then(success?: (value: C) => U, error?: (err: any) => void, progress?: (value: P) => void): PPromise; - - public done(success?: (value: C) => void, error?: (err: any) => any, progress?: (value: P) => void): void; - public cancel(): void; - - public static as(value: V): TPromise; - public static timeout(delay: number): PPromise; - public static join(promises: PPromise[]): PPromise; - public static join(promises: { [n: string]: PPromise }): PPromise<{ [n: string]: C }, P>; - public static any(promises: PPromise[]): PPromise<{ key: string; value: PPromise; }, P>; - public static wrapError(error: Error): TPromise; +export interface IPromiseErrorDetail { + parent: Promise; + error: any; + id: number; + handler: Function; + exception: Error; +} + +export interface IPromiseError { + detail: IPromiseErrorDetail; } diff --git a/src/vs/code/electron-browser/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcessMain.ts index 646098f8b6f..ef7a31319cf 100644 --- a/src/vs/code/electron-browser/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcessMain.ts @@ -138,7 +138,7 @@ function main(server: Server, initData: ISharedProcessInitData): void { function setupIPC(hook: string): TPromise { function setup(retry: boolean): TPromise { - return serve(hook).then(null, err => { + return serve(hook).then(null, err => { if (!retry || platform.isWindows || err.code !== 'EADDRINUSE') { return TPromise.wrapError(err); } diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts index 182404712f7..a006d5eb1da 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts @@ -41,10 +41,9 @@ function getDefinitions( const promises = provider.map((provider, idx) => { return asWinJsPromise((token) => { return provide(provider, model, position, token); - }).then(result => { - return result; - }, err => { + }).then(undefined, err => { onUnexpectedExternalError(err); + return null; }); }); return outputResults(promises); diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts index 0115099afdc..17e7a051baa 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts @@ -103,7 +103,6 @@ export class DefinitionAction extends EditorAction { }, (err) => { // report an error messageService.show(Severity.Error, err); - return false; }); } diff --git a/src/vs/editor/contrib/hover/browser/hoverOperation.ts b/src/vs/editor/contrib/hover/browser/hoverOperation.ts index 59ea0eff077..71c151f9fa1 100644 --- a/src/vs/editor/contrib/hover/browser/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/browser/hoverOperation.ts @@ -99,7 +99,7 @@ export class HoverOperation { this._asyncComputationPromise = this._computer.computeAsync().then((asyncResult: Result) => { this._asyncComputationPromiseDone = true; this._withAsyncResult(asyncResult); - }, () => this._onError); + }, (e) => this._onError(e)); } else { this._asyncComputationPromiseDone = true; } diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index d5ee521195b..fd83ce1c00e 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -91,7 +91,7 @@ export class ParameterHintsModel extends Disposable { private doTrigger(): void { provideSignatureHelp(this.editor.getModel(), this.editor.getPosition()) - .then(null, onUnexpectedError) + .then(null, onUnexpectedError) .then(result => { if (!result || !result.signatures || result.signatures.length === 0) { this.cancel(); diff --git a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts index 52b6d50d151..289dde559d2 100644 --- a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts @@ -44,6 +44,7 @@ export function getOccurrencesAtPosition(model: editorCommon.IReadOnlyModel, pos return undefined; }, err => { onUnexpectedExternalError(err); + return undefined; }); } return undefined; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ae8c929e84e..97c8a13a94d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5,16 +5,7 @@ declare module monaco { - interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; - } + type Thenable = PromiseLike; export interface IDisposable { dispose(): void; @@ -43,59 +34,49 @@ declare module monaco { - /** - * The value callback to complete a promise - */ - export interface TValueCallback { - (value: T | Thenable): void; - } + export type TValueCallback = (value: T | PromiseLike) => void; + + export type ProgressCallback = (progress: TProgress) => void; - export interface ProgressCallback { - (progress: any): any; - } + export class Promise { + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason: any) => void, + progress: (progress: TProgress) => void) => void, + oncancel?: () => void); + public then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null, + onprogress?: (progress: TProgress) => void): Promise; - /** - * A Promise implementation that supports progress and cancelation. - */ - export class Promise { + public done( + onfulfilled?: (value: T) => void, + onrejected?: (reason: any) => void, + onprogress?: (progress: TProgress) => void): void; - constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: ProgressCallback) => void, oncancel?: any); - - public then(success?: (value: V) => Promise, error?: (err: any) => Promise, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise, error?: (err: any) => Promise | U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise, error?: (err: any) => U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise, error?: (err: any) => void, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => Promise, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => Promise | U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => Promise | U, error?: (err: any) => void, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => Promise, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => Promise | U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => U, progress?: ProgressCallback): Promise; - public then(success?: (value: V) => U, error?: (err: any) => void, progress?: ProgressCallback): Promise; - - public done(success?: (value: V) => void, error?: (err: any) => any, progress?: ProgressCallback): void; public cancel(): void; public static as(value: null): Promise; public static as(value: undefined): Promise; - public static as(value: Promise): Promise; - public static as(value: Thenable): Thenable; - public static as(value: ValueType): Promise; + public static as>(value: TPromise): TPromise; + public static as(value: T): Promise; + + public static is(value: any): value is PromiseLike; - public static is(value: any): value is Thenable; public static timeout(delay: number): Promise; - public static join(promises: Promise[]): Promise; - public static join(promises: Thenable[]): Thenable; - public static join(promises: { [n: string]: Promise }): Promise<{ [n: string]: ValueType }>; - public static any(promises: Promise[]): Promise<{ key: string; value: Promise; }>; - public static wrap(value: Thenable): Promise; - public static wrap(value: ValueType): Promise; + public static join(promises: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; + public static join(promises: (T | PromiseLike)[]): Promise; + public static join(promises: { [n: string]: T | PromiseLike }): Promise<{ [n: string]: T }>; - public static wrapError(error: Error): Promise; + public static any(promises: (T | PromiseLike)[]): Promise<{ key: string; value: Promise; }>; + + public static wrap(value: T | PromiseLike): Promise; + + public static wrapError(error: Error): Promise; } export class CancellationTokenSource { diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 6b5d9a67ca1..a70e63cbb76 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -515,7 +515,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { const firstOptions = assign({}, options, { url: asset.uri }); return this.requestService.request(firstOptions) - .then(context => context.res.statusCode === 200 ? context : TPromise.wrapError(new Error('expected 200'))) + .then(context => context.res.statusCode === 200 ? context : TPromise.wrapError(new Error('expected 200'))) .then(null, err => { this.telemetryService.publicLog('galleryService:requestError', { cdn: true, message: getErrorMessage(err) }); this.telemetryService.publicLog('galleryService:cdnFallback', { url: asset.uri }); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index ebe55654b90..19142629db9 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -55,7 +55,7 @@ function readManifest(extensionPath: string): TPromise<{ manifest: IExtensionMan pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8') .then(raw => parseManifest(raw)), pfs.readFile(path.join(extensionPath, 'package.nls.json'), 'utf8') - .then(null, err => err.code !== 'ENOENT' ? TPromise.wrapError(err) : '{}') + .then(null, err => err.code !== 'ENOENT' ? TPromise.wrapError(err) : '{}') .then(raw => JSON.parse(raw)) ]; @@ -294,13 +294,13 @@ export class ExtensionManagementService implements IExtensionManagementService { uninstall(extension: ILocalExtension, force = false): TPromise { return this.removeOutdatedExtensions().then(() => { - return this.scanUserExtensions().then(installed => { + return this.scanUserExtensions().then(installed => { const promises = installed .filter(e => e.manifest.publisher === extension.manifest.publisher && e.manifest.name === extension.manifest.name) .map(e => this.checkForDependenciesAndUninstall(e, installed, force)); return TPromise.join(promises); }); - }); + }).then(() => { /* drop resolved value */ }); } private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise { @@ -575,7 +575,7 @@ export class ExtensionManagementService implements IExtensionManagementService { return this.obsoleteFileLimiter.queue(() => { let result: T = null; return pfs.readFile(this.obsoletePath, 'utf8') - .then(null, err => err.code === 'ENOENT' ? TPromise.as('{}') : TPromise.wrapError(err)) + .then(null, err => err.code === 'ENOENT' ? TPromise.as('{}') : TPromise.wrapError(err)) .then<{ [id: string]: boolean }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } }) .then(obsolete => { result = fn(obsolete); return obsolete; }) .then(obsolete => { diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index b22f7c6df8b..0a09b6598d0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -85,7 +85,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape { if (process) { return process.getId(); } - return TPromise.wrapError(new Error('cannot create debug session')); + return TPromise.wrapError(new Error('cannot create debug session')); }, err => { return TPromise.wrapError(err && err.message ? err.message : 'cannot start debug session'); }); diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index 52b1ba27df4..ed31354268d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -62,7 +62,10 @@ class TreeViewDataProvider implements ITreeViewDataProvider { .then(elements => { this.postGetElements(null, elements); return elements; - }, err => this.messageService.show(Severity.Error, err)); + }, err => { + this.messageService.show(Severity.Error, err); + return null; + }); } getChildren(treeItem: ITreeItem): TPromise { @@ -73,7 +76,10 @@ class TreeViewDataProvider implements ITreeViewDataProvider { .then(children => { this.postGetElements(treeItem.handle, children); return children; - }, err => this.messageService.show(Severity.Error, err)); + }, err => { + this.messageService.show(Severity.Error, err); + return null; + }); } refresh(treeItemHandles: number[]) { diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index c990fba30bd..763969acc96 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -542,10 +542,10 @@ export class ExtHostTextEditor implements vscode.TextEditor { return TPromise.as(undefined); } return callback().then(() => this, err => { - if (err instanceof Error && err.name === 'DISPOSED') { - return; + if (!(err instanceof Error && err.name === 'DISPOSED')) { + console.warn(err); } - console.warn(err); + return null; }); } } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 6c625a26b61..abf883ccba4 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -403,7 +403,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService return editor; - }, e => this.messageService.show(Severity.Error, types.isString(e) ? new Error(e) : e)); + }, e => { + this.messageService.show(Severity.Error, types.isString(e) ? new Error(e) : e); + return null; + }); } private doCreateEditor(group: EditorGroup, descriptor: IEditorDescriptor, monitor: ProgressMonitor): TPromise { @@ -513,7 +516,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Fullfill promise with Editor that is being used return editor; - }, e => this.doHandleSetInputError(e, group, editor, input, options, monitor)); + }, e => { + this.doHandleSetInputError(e, group, editor, input, options, monitor); + return null; + }); } private doHandleSetInputError(e: Error | IMessageWithAction, group: EditorGroup, editor: BaseEditor, input: EditorInput, options: EditorOptions, monitor: ProgressMonitor): void { diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 2670b54ac62..084c5d086f3 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -207,6 +207,7 @@ export class QuickOpenController extends Component implements IQuickOpenService }); }, err => { // ignore + return null; }); } } diff --git a/src/vs/workbench/parts/backup/common/backupRestorer.ts b/src/vs/workbench/parts/backup/common/backupRestorer.ts index 26c470d63bd..f035909c3e9 100644 --- a/src/vs/workbench/parts/backup/common/backupRestorer.ts +++ b/src/vs/workbench/parts/backup/common/backupRestorer.ts @@ -62,7 +62,7 @@ export class BackupRestorer implements IWorkbenchContribution { private doResolveOpenedBackups(backups: URI[]): TPromise { const stacks = this.groupService.getStacksModel(); - const restorePromises: TPromise[] = []; + const restorePromises: TPromise[] = []; const unresolved: URI[] = []; backups.forEach(backup => { diff --git a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts index c852b905cd1..3aa4c25c532 100644 --- a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -81,7 +81,9 @@ class InstallAction extends Action { }); } }) - .then(() => this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName))); + .then(() => { + this.messageService.show(Severity.Info, nls.localize('successIn', "Shell command '{0}' successfully installed in PATH.", product.applicationName)); + }); }); } @@ -141,8 +143,9 @@ class UninstallAction extends Action { return pfs.unlink(this.target) .then(null, ignore('ENOENT')) - .then(() => this.messageService.show(Severity.Info, nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", product.applicationName))) - .then(null); + .then(() => { + this.messageService.show(Severity.Info, nls.localize('successFrom', "Shell command '{0}' successfully uninstalled from PATH.", product.applicationName)); + }); }); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index efee1adc6d1..5089c0e4720 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -656,7 +656,7 @@ export class DebugService implements debug.IDebugService { return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName))); } - return manager.getStartSessionCommand(config ? config.type : undefined).then(commandAndType => { + return manager.getStartSessionCommand(config ? config.type : undefined).then(commandAndType => { if (noDebug && config) { config.noDebug = true; } @@ -753,13 +753,15 @@ export class DebugService implements debug.IDebugService { }); }, err => { if (!this.contextService.hasWorkspace()) { - return this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); + this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); + return undefined; } return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => { if (openend) { this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message)); } + return undefined; }); }) ); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts index c33a4ecd7d4..bfd75970a59 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -79,7 +79,7 @@ export class KeymapExtensions implements IWorkbenchContribution { localize('no', "No") ]; return this.choiceService.choose(Severity.Info, message, options, 1, false) - .then(value => { + .then(value => { const confirmed = value === 0; telemetryData['confirmed'] = confirmed; this.telemetryService.publicLog('disableOtherKeymaps', telemetryData); @@ -89,7 +89,8 @@ export class KeymapExtensions implements IWorkbenchContribution { })); } return undefined; - }, error => TPromise.wrapError(canceled())); + }, error => TPromise.wrapError(canceled())) + .then(() => { /* drop resolved value */ }); } dispose(): void { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 8229a8fed86..f6a30f32c9a 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -830,7 +830,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { nls.localize('install', "Install"), nls.localize('cancel', "Cancel") ]; - return this.choiceService.choose(Severity.Info, message, options, 2, false).then(value => { + return this.choiceService.choose(Severity.Info, message, options, 2, false).then(value => { if (value !== 0) { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 40822918de9..6016dda0910 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -1338,7 +1338,10 @@ export abstract class BaseSaveFileAction extends BaseErrorReportingAction { } public run(context?: any): TPromise { - return this.doRun(context).then(() => true, error => this.onError(error)); + return this.doRun(context).then(() => true, error => { + this.onError(error); + return null; + }); } protected abstract doRun(context?: any): TPromise; diff --git a/src/vs/workbench/parts/files/browser/fileCommands.ts b/src/vs/workbench/parts/files/browser/fileCommands.ts index e38fb596c2c..be7013d7286 100644 --- a/src/vs/workbench/parts/files/browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/browser/fileCommands.ts @@ -215,12 +215,12 @@ function withFocusedExplorerItem(accessor: ServicesAccessor): TPromise { if (res) { - return res.item; + return res.item as FileStat | OpenEditor; } return void 0; }); - }) as TPromise; // TypeScript fail + }); }; export const renameFocusedFilesExplorerViewItemCommand = (accessor: ServicesAccessor) => { diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index d28ad5904f1..679a6fae06e 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -866,7 +866,7 @@ export class ExplorerView extends CollapsibleView { // Select and Reveal return this.explorerViewer.refresh(root).then(() => this.doSelect(root.find(resource), reveal)); - }, e => this.messageService.show(Severity.Error, e)); + }, e => { this.messageService.show(Severity.Error, e); }); } private hasSelection(resource: URI): FileStat { diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index b85202fdc87..4ab1512df6a 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -228,7 +228,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } // Bubble any other error up - return TPromise.wrapError(error); + return TPromise.wrapError(error); }); } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 1ac73c1283e..046c8e24c15 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -340,15 +340,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { }); } - private createIfNotExists(resource: URI, contents: string): TPromise { + private createIfNotExists(resource: URI, contents: string): TPromise { return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => { if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { return this.fileService.updateContent(resource, contents).then(null, error => { - return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", labels.getPathLabel(resource, this.contextService, this.environmentService), error))); + return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", labels.getPathLabel(resource, this.contextService, this.environmentService), error))); }); } - return TPromise.wrapError(error); + return TPromise.wrapError(error); }); } diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 8c79b5fa0ea..3eae07844b7 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -216,6 +216,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { }, (error: Error) => { this.pendingSearch = null; this.messageService.show(Severity.Error, error); + return null; }); return this.pendingSearch; diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts index ed09f3635ba..eb29e67c023 100644 --- a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -232,7 +232,7 @@ export class ProcessRunnerDetector { let config = ProcessRunnerDetector.detectorConfig('gulp'); let process = new LineProcess('gulp', [config.arg, '--no-color'], true, { cwd: this._cwd }); return this.runDetection(process, 'gulp', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); - }, (err: any): TaskConfig.ExternalTaskRunnerConfiguration => { + }, (err: any) => { return null; }); } @@ -242,7 +242,7 @@ export class ProcessRunnerDetector { let config = ProcessRunnerDetector.detectorConfig('grunt'); let process = new LineProcess('grunt', [config.arg, '--no-color'], true, { cwd: this._cwd }); return this.runDetection(process, 'grunt', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); - }, (err: any): TaskConfig.ExternalTaskRunnerConfiguration => { + }, (err: any) => { return null; }); } @@ -258,7 +258,7 @@ export class ProcessRunnerDetector { }, (err: any) => { return this.fileService.resolveFile(this.contextService.toResource('Jakefile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return run(); - }, (err: any): TaskConfig.ExternalTaskRunnerConfiguration => { + }, (err: any) => { return null; }); }); diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index fd738117cd0..7264d548e85 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -458,7 +458,7 @@ class WelcomePage { }) ] }); - }).then(null, err => { + }).then(null, err => { this.telemetryService.publicLog(strings.installedEvent, { from: telemetryFrom, extensionId: extensionSuggestion.id, diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index 259864baf1c..39dcec2fb80 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -153,10 +153,10 @@ export class ConfigurationEditingService implements IConfigurationEditingService this.commandService.executeCommand(ConfigurationTarget.USER === target ? 'workbench.action.openGlobalSettings' : 'workbench.action.openWorkspaceSettings'); } - private wrapError(code: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): TPromise { + private wrapError(code: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): TPromise { const message = this.toErrorMessage(code, target, operation); - return TPromise.wrapError(new ConfigurationEditingError(message, code)); + return TPromise.wrapError(new ConfigurationEditingError(message, code)); } private toErrorMessage(error: ConfigurationEditingErrorCode, target: ConfigurationTarget, operation: IConfigurationEditOperation): string { @@ -271,14 +271,14 @@ export class ConfigurationEditingService implements IConfigurationEditingService const model = reference.object.textEditorModel; if (this.hasParseErrors(model, operation)) { - return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation); + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION, target, operation); } // Target cannot be dirty if not writing into buffer if (checkDirty && this.textFileService.isDirty(operation.resource)) { - return this.wrapError(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation); + return this.wrapError(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY, target, operation); } - return TPromise.wrap(reference); + return reference; }); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 340c982cd20..e33388b7775 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -356,7 +356,7 @@ export class ExtensionService implements IExtensionService { }); return Object.keys(result).map(name => result[name]); - }).then(null, err => { + }).then(null, err => { log.error('', err); return []; }); diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index d4bfaa7bf82..413d030c6ca 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -561,7 +561,7 @@ export abstract class TextFileService implements ITextFileService { modelPromise = this.untitledEditorService.loadOrCreate({ resource }); } - return modelPromise.then(model => { + return modelPromise.then(model => { // We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before) if (model) { diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 8f0acdd5f7c..10b047cc5e0 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -340,7 +340,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } - private initialize(): TPromise { + private initialize(): TPromise<[IColorTheme, IFileIconTheme]> { this.updateColorCustomizations(false); From bb55f060b8b18075e0e5f0e7fcf955a1bc1dd5e9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 18 Aug 2017 09:51:06 +0200 Subject: [PATCH 46/96] mr - migrate backups when entering workspace --- src/vs/code/electron-main/window.ts | 10 +- src/vs/code/electron-main/windows.ts | 106 ++++++++++++------ src/vs/platform/backup/common/backup.ts | 2 +- .../backup/electron-main/backupMainService.ts | 22 +++- .../electron-main/backupMainService.test.ts | 15 +++ .../lifecycle/electron-main/lifecycleMain.ts | 30 ++--- src/vs/platform/windows/common/windows.ts | 2 + src/vs/platform/windows/common/windowsIpc.ts | 6 + .../windows/electron-browser/windowService.ts | 4 + .../platform/windows/electron-main/windows.ts | 1 + .../windows/electron-main/windowsService.ts | 10 ++ .../browser/actions/workspaceActions.ts | 16 +-- .../workbench/test/workbenchTestServices.ts | 8 ++ 13 files changed, 168 insertions(+), 64 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 0d1b2c076e1..34170dc4e2d 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -511,10 +511,14 @@ export class CodeWindow implements ICodeWindow { } } - public reload(cli?: ParsedArgs): void { + public reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void { - // Inherit current properties but overwrite some - const configuration: IWindowConfiguration = objects.mixin({}, this.currentConfig); + // If config is not provided, copy our current one + if (!configuration) { + configuration = objects.mixin({}, this.currentConfig); + } + + // Delete some properties we do not want during reload delete configuration.filesToOpen; delete configuration.filesToCreate; delete configuration.filesToDiff; diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 2aa56687041..441a142d55e 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -19,7 +19,7 @@ import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/pa import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, ReadyState } from 'vs/platform/windows/common/windows'; import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderPath } from 'vs/code/node/windowsFinder'; import CommonEvent, { Emitter } from 'vs/base/common/event'; import product from 'vs/platform/node/product'; @@ -937,16 +937,16 @@ export class WindowsManager implements IWindowsMainService { configuration.backupPath = path.join(this.environmentService.backupHome, options.emptyWindowBackupFolder); } - let codeWindow: CodeWindow; + let window: CodeWindow; if (!options.forceNewWindow) { - codeWindow = options.windowToUse || this.getLastActiveWindow(); - if (codeWindow) { - codeWindow.focus(); + window = options.windowToUse || this.getLastActiveWindow(); + if (window) { + window.focus(); } } // New window - if (!codeWindow) { + if (!window) { const windowConfig = this.configurationService.getConfiguration('window'); const state = this.getNewWindowState(configuration); @@ -965,27 +965,27 @@ export class WindowsManager implements IWindowsMainService { state.mode = WindowMode.Normal; } - codeWindow = this.instantiationService.createInstance(CodeWindow, { + window = this.instantiationService.createInstance(CodeWindow, { state, extensionDevelopmentPath: configuration.extensionDevelopmentPath, isExtensionTestHost: !!configuration.extensionTestsPath }); // Add to our list of windows - WindowsManager.WINDOWS.push(codeWindow); + WindowsManager.WINDOWS.push(window); // Indicate number change via event this._onWindowsCountChanged.fire({ oldCount: WindowsManager.WINDOWS.length - 1, newCount: WindowsManager.WINDOWS.length }); // Window Events - codeWindow.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own - codeWindow.win.webContents.on('devtools-reload-page', () => this.reload(codeWindow)); - codeWindow.win.webContents.on('crashed', () => this.onWindowError(codeWindow, WindowError.CRASHED)); - codeWindow.win.on('unresponsive', () => this.onWindowError(codeWindow, WindowError.UNRESPONSIVE)); - codeWindow.win.on('closed', () => this.onWindowClosed(codeWindow)); + window.win.webContents.removeAllListeners('devtools-reload-page'); // remove built in listener so we can handle this on our own + window.win.webContents.on('devtools-reload-page', () => this.reload(window)); + window.win.webContents.on('crashed', () => this.onWindowError(window, WindowError.CRASHED)); + window.win.on('unresponsive', () => this.onWindowError(window, WindowError.UNRESPONSIVE)); + window.win.on('closed', () => this.onWindowClosed(window)); // Lifecycle - this.lifecycleService.registerWindow(codeWindow); + this.lifecycleService.registerWindow(window); } // Existing window @@ -993,7 +993,7 @@ export class WindowsManager implements IWindowsMainService { // Some configuration things get inherited if the window is being reused and we are // in extension development host mode. These options are all development related. - const currentWindowConfig = codeWindow.config; + const currentWindowConfig = window.config; if (!configuration.extensionDevelopmentPath && currentWindowConfig && !!currentWindowConfig.extensionDevelopmentPath) { configuration.extensionDevelopmentPath = currentWindowConfig.extensionDevelopmentPath; configuration.verbose = currentWindowConfig.verbose; @@ -1005,7 +1005,7 @@ export class WindowsManager implements IWindowsMainService { } // Only load when the window has not vetoed this - this.lifecycleService.unload(codeWindow, UnloadReason.LOAD).done(veto => { + this.lifecycleService.unload(window, UnloadReason.LOAD).done(veto => { if (!veto) { // Register window for backups @@ -1020,11 +1020,11 @@ export class WindowsManager implements IWindowsMainService { } // Load it - codeWindow.load(configuration); + window.load(configuration); } }); - return codeWindow; + return window; } private getNewWindowState(configuration: IWindowConfiguration): INewWindowState { @@ -1156,7 +1156,7 @@ export class WindowsManager implements IWindowsMainService { // Only reload when the window has not vetoed this this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => { if (!veto) { - win.reload(cli); + win.reload(void 0, cli); // Emit this._onWindowReload.fire(win.id); @@ -1171,6 +1171,46 @@ export class WindowsManager implements IWindowsMainService { }); } + public createAndOpenWorkspace(window: CodeWindow, folders?: string[], path?: string): TPromise { + if (!window || !window.win || window.readyState !== ReadyState.READY) { + return TPromise.as(null); // return early if the window is not ready or disposed + } + + return this.workspacesService.createWorkspace(folders).then(workspace => { + let savePromise: TPromise; + if (path) { + savePromise = this.workspacesService.saveWorkspace(workspace, path); + } else { + savePromise = TPromise.as(workspace); + } + + return savePromise.then(workspace => { + window.focus(); + + // Only open workspace when the window has not vetoed this + return this.lifecycleService.unload(window, UnloadReason.RELOAD).done(veto => { + if (!veto) { + + // Register window for backups and migrate current backups over + let backupPath: string; + if (window.config && !window.config.extensionDevelopmentPath) { + backupPath = this.backupService.registerWorkspaceBackupSync(workspace, window.config.backupPath); + } + + // Craft a new window configuration to use for the transition + const configuration: IWindowConfiguration = mixin({}, window.config); + configuration.folderPath = void 0; + configuration.workspace = workspace; + configuration.backupPath = backupPath; + + // Reload + window.reload(configuration); + } + }); + }); + }); + } + public openWorkspace(window: CodeWindow = this.getLastActiveWindow()): void { let defaultPath: string; if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) { @@ -1380,12 +1420,12 @@ export class WindowsManager implements IWindowsMainService { return WindowsManager.WINDOWS.length; } - private onWindowError(codeWindow: CodeWindow, error: WindowError): void { + private onWindowError(window: CodeWindow, error: WindowError): void { this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); // Unresponsive if (error === WindowError.UNRESPONSIVE) { - dialog.showMessageBox(codeWindow.win, { + dialog.showMessageBox(window.win, { title: product.nameLong, type: 'warning', buttons: [localize('reopen', "Reopen"), localize('wait', "Keep Waiting"), localize('close', "Close")], @@ -1393,22 +1433,22 @@ export class WindowsManager implements IWindowsMainService { detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), noLink: true }, result => { - if (!codeWindow.win) { + if (!window.win) { return; // Return early if the window has been going down already } if (result === 0) { - codeWindow.reload(); + window.reload(); } else if (result === 2) { - this.onBeforeWindowClose(codeWindow); // 'close' event will not be fired on destroy(), so run it manually - codeWindow.win.destroy(); // make sure to destroy the window as it is unresponsive + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it is unresponsive } }); } // Crashed else { - dialog.showMessageBox(codeWindow.win, { + dialog.showMessageBox(window.win, { title: product.nameLong, type: 'warning', buttons: [localize('reopen', "Reopen"), localize('close', "Close")], @@ -1416,15 +1456,15 @@ export class WindowsManager implements IWindowsMainService { detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), noLink: true }, result => { - if (!codeWindow.win) { + if (!window.win) { return; // Return early if the window has been going down already } if (result === 0) { - codeWindow.reload(); + window.reload(); } else if (result === 1) { - this.onBeforeWindowClose(codeWindow); // 'close' event will not be fired on destroy(), so run it manually - codeWindow.win.destroy(); // make sure to destroy the window as it has crashed + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it has crashed } }); } @@ -1493,9 +1533,9 @@ export class WindowsManager implements IWindowsMainService { // If the user selected to exit from an extension development host window, do not quit, but just // close the window unless this is the last window that is opened. - const codeWindow = this.getFocusedWindow(); - if (codeWindow && codeWindow.isExtensionDevelopmentHost && this.getWindowCount() > 1) { - codeWindow.win.close(); + const window = this.getFocusedWindow(); + if (window && window.isExtensionDevelopmentHost && this.getWindowCount() > 1) { + window.win.close(); } // Otherwise: normal quit diff --git a/src/vs/platform/backup/common/backup.ts b/src/vs/platform/backup/common/backup.ts index 42e3e3a529f..70287412b0a 100644 --- a/src/vs/platform/backup/common/backup.ts +++ b/src/vs/platform/backup/common/backup.ts @@ -23,7 +23,7 @@ export interface IBackupMainService { getFolderBackupPaths(): string[]; getEmptyWindowBackupPaths(): string[]; - registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier): string; + registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string; registerFolderBackupSync(folderPath: string): string; registerEmptyWindowBackupSync(backupFolder?: string): string; } \ No newline at end of file diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index fb871afcd3e..2bc6dfb3dbf 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -97,10 +97,28 @@ export class BackupMainService implements IBackupMainService { return this.backups.emptyWorkspaces.slice(0); // return a copy } - public registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier): string { + public registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string { this.pushBackupPathsSync(workspace, this.backups.rootWorkspaces); - return path.join(this.backupHome, workspace.id); + const backupPath = path.join(this.backupHome, workspace.id); + + if (migrateFrom) { + this.moveBackupFolderSync(backupPath, migrateFrom); + } + + return backupPath; + } + + private moveBackupFolderSync(backupPath: string, moveFromPath: string): void { + if (!fs.existsSync(moveFromPath)) { + return; + } + + try { + fs.renameSync(moveFromPath, backupPath); + } catch (ex) { + this.logService.error(`Backup: Could not move backup folder to new location: ${ex.toString()}`); + } } public registerFolderBackupSync(folderPath: string): string { diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index bec82b2d595..5cee24b6743 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -196,6 +196,21 @@ suite('BackupMainService', () => { done(); }); + test('service supports to migrate backup data from another location', done => { + const backupPathToMigrate = service.toBackupPath(fooFile.fsPath); + fs.mkdirSync(backupPathToMigrate); + fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data'); + service.registerFolderBackupSync(backupPathToMigrate); + + const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath), backupPathToMigrate); + + assert.ok(fs.existsSync(workspaceBackupPath)); + assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt'))); + assert.ok(!fs.existsSync(backupPathToMigrate)); + + done(); + }); + suite('loadSync', () => { test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => { assert.deepEqual(service.getFolderBackupPaths(), []); diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts index 30f10f310fa..ac9c9267168 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts @@ -59,9 +59,9 @@ export interface ILifecycleService { onBeforeWindowUnload: Event; ready(): void; - registerWindow(codeWindow: ICodeWindow): void; + registerWindow(window: ICodeWindow): void; - unload(codeWindow: ICodeWindow, reason: UnloadReason): TPromise; + unload(window: ICodeWindow, reason: UnloadReason): TPromise; relaunch(options?: { addArgs?: string[], removeArgs?: string[] }); @@ -148,11 +148,11 @@ export class LifecycleService implements ILifecycleService { }); } - public registerWindow(codeWindow: ICodeWindow): void { + public registerWindow(window: ICodeWindow): void { // Window Before Closing: Main -> Renderer - codeWindow.win.on('close', e => { - const windowId = codeWindow.id; + window.win.on('close', e => { + const windowId = window.id; this.logService.log('Lifecycle#window-before-close', windowId); // The window already acknowledged to be closed @@ -166,11 +166,11 @@ export class LifecycleService implements ILifecycleService { // Otherwise prevent unload and handle it from window e.preventDefault(); - this.unload(codeWindow, UnloadReason.CLOSE).done(veto => { + this.unload(window, UnloadReason.CLOSE).done(veto => { if (!veto) { this.windowToCloseRequest[windowId] = true; - this._onBeforeWindowClose.fire(codeWindow); - codeWindow.close(); + this._onBeforeWindowClose.fire(window); + window.close(); } else { this.quitRequested = false; delete this.windowToCloseRequest[windowId]; @@ -179,25 +179,25 @@ export class LifecycleService implements ILifecycleService { }); } - public unload(codeWindow: ICodeWindow, reason: UnloadReason): TPromise { + public unload(window: ICodeWindow, reason: UnloadReason): TPromise { // Always allow to unload a window that is not yet ready - if (codeWindow.readyState !== ReadyState.READY) { + if (window.readyState !== ReadyState.READY) { return TPromise.as(false); } - this.logService.log('Lifecycle#unload()', codeWindow.id); + this.logService.log('Lifecycle#unload()', window.id); const windowUnloadReason = this.quitRequested ? UnloadReason.QUIT : reason; // first ask the window itself if it vetos the unload - return this.doUnloadWindowInRenderer(codeWindow, windowUnloadReason).then(veto => { + return this.doUnloadWindowInRenderer(window, windowUnloadReason).then(veto => { if (veto) { return this.handleVeto(veto); } // then check for vetos in the main side - return this.doUnloadWindowInMain(codeWindow, windowUnloadReason).then(veto => this.handleVeto(veto)); + return this.doUnloadWindowInMain(window, windowUnloadReason).then(veto => this.handleVeto(veto)); }); } @@ -213,7 +213,7 @@ export class LifecycleService implements ILifecycleService { return veto; } - private doUnloadWindowInRenderer(codeWindow: ICodeWindow, reason: UnloadReason): TPromise { + private doUnloadWindowInRenderer(window: ICodeWindow, reason: UnloadReason): TPromise { return new TPromise((c) => { const oneTimeEventToken = this.oneTimeListenerTokenGenerator++; const okChannel = `vscode:ok${oneTimeEventToken}`; @@ -227,7 +227,7 @@ export class LifecycleService implements ILifecycleService { c(true); // veto }); - codeWindow.send('vscode:beforeUnload', { okChannel, cancelChannel, reason }); + window.send('vscode:beforeUnload', { okChannel, cancelChannel, reason }); }); } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 0764f6c5466..d3850792ef3 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -42,6 +42,7 @@ export interface IWindowsService { toggleDevTools(windowId: number): TPromise; closeWorkspace(windowId: number): TPromise; openWorkspace(windowId: number): TPromise; + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise; toggleFullScreen(windowId: number): TPromise; setRepresentedFilename(windowId: number, fileName: string): TPromise; addRecentlyOpened(files: string[]): TPromise; @@ -97,6 +98,7 @@ export interface IWindowService { toggleDevTools(): TPromise; closeWorkspace(): TPromise; openWorkspace(): TPromise; + createAndOpenWorkspace(folders?: string[], path?: string): TPromise; toggleFullScreen(): TPromise; setRepresentedFilename(fileName: string): TPromise; getRecentlyOpened(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 08e5fca38e6..0ea79af8707 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -23,6 +23,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'toggleDevTools', arg: number): TPromise; call(command: 'closeWorkspace', arg: number): TPromise; call(command: 'openWorkspace', arg: number): TPromise; + call(command: 'createAndOpenWorkspace', arg: [number, string[], string]): TPromise; call(command: 'toggleFullScreen', arg: number): TPromise; call(command: 'setRepresentedFilename', arg: [number, string]): TPromise; call(command: 'addRecentlyOpened', arg: string[]): TPromise; @@ -78,6 +79,7 @@ export class WindowsChannel implements IWindowsChannel { case 'toggleDevTools': return this.service.toggleDevTools(arg); case 'closeWorkspace': return this.service.closeWorkspace(arg); case 'openWorkspace': return this.service.openWorkspace(arg); + case 'createAndOpenWorkspace': return this.service.createAndOpenWorkspace(arg[0], arg[1], arg[2]); case 'toggleFullScreen': return this.service.toggleFullScreen(arg); case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]); case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg); @@ -157,6 +159,10 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('openWorkspace', windowId); } + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise { + return this.channel.call('createAndOpenWorkspace', [windowId, folders, path]); + } + toggleFullScreen(windowId: number): TPromise { return this.channel.call('toggleFullScreen', windowId); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 89070eb8cdd..cab6b6e4425 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -68,6 +68,10 @@ export class WindowService implements IWindowService { return this.windowsService.openWorkspace(this.windowId); } + createAndOpenWorkspace(folders?: string[], path?: string): TPromise { + return this.windowsService.createAndOpenWorkspace(this.windowId, folders, path); + } + closeWindow(): TPromise { return this.windowsService.closeWindow(this.windowId); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index e0abfe7fad9..13ed2583fd3 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -58,6 +58,7 @@ export interface IWindowsMainService { ready(initialUserEnv: IProcessEnvironment): void; reload(win: ICodeWindow, cli?: ParsedArgs): void; openWorkspace(win?: ICodeWindow): void; + createAndOpenWorkspace(win: ICodeWindow, folders?: string[], path?: string): void; closeWorkspace(win: ICodeWindow): void; open(openConfig: IOpenConfiguration): ICodeWindow[]; openExtensionDevelopmentHostWindow(openConfig: IOpenConfiguration): void; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 0ef83e2092d..9a698579518 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -124,6 +124,16 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise { + const codeWindow = this.windowsMainService.getWindowById(windowId); + + if (codeWindow) { + this.windowsMainService.createAndOpenWorkspace(codeWindow, folders, path); + } + + return TPromise.as(null); + } + toggleFullScreen(windowId: number): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 943741d6f7f..9eee812338d 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -186,8 +186,9 @@ class NewWorkspaceAction extends BaseWorkspacesAction { } private createWorkspace(folders: URI[]): TPromise { - return this.workspacesService.createWorkspace(distinct(folders.map(folder => folder.toString(true /* encoding */)))) - .then(({ configPath }) => this.windowsService.openWindow([configPath])); + const workspaceFolders = distinct(folders.map(folder => folder.toString(true /* encoding */))); + + return this.windowService.createAndOpenWorkspace(workspaceFolders); } } @@ -250,14 +251,9 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { private saveFolderWorkspace(configPath: string): TPromise { if (this.handleNotInMultiFolderWorkspaceCase(nls.localize('saveNotSupported', "To save workspace, window reload is required."))) { - // Create workspace first - this.workspacesService.createWorkspace(this.contextService.getWorkspace().roots.map(root => root.toString(true /* skip encoding */))) - .then(workspaceIdentifier => { - // Save the workspace in new location - return this.workspacesService.saveWorkspace(workspaceIdentifier, configPath) - // Open the saved workspace - .then(({ configPath }) => this.windowsService.openWindow([configPath])); - }); + const workspaceFolders = this.contextService.getWorkspace().roots.map(root => root.toString(true /* skip encoding */)); + + return this.windowService.createAndOpenWorkspace(workspaceFolders, configPath); } return TPromise.as(null); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 51710dd2007..5555e8bb960 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -882,6 +882,10 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } + createAndOpenWorkspace(folders?: string[], path?: string): TPromise { + return TPromise.as(void 0); + } + toggleFullScreen(): TPromise { return TPromise.as(void 0); } @@ -1014,6 +1018,10 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } + createAndOpenWorkspace(windowId: number, folders?: string[], path?: string): TPromise { + return TPromise.as(void 0); + } + toggleFullScreen(windowId: number): TPromise { return TPromise.as(void 0); } From 40145f5994fb103d07a70d32dfc7aae7ad06eefd Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 18 Aug 2017 10:06:09 +0200 Subject: [PATCH 47/96] use api version for uninstall telemetry --- .../node/extensionGalleryService.ts | 6 ++++- .../node/extensionManagementService.ts | 23 +++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 274064ada09..cd396127d1e 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -391,7 +391,11 @@ export class ExtensionGalleryService implements IExtensionGalleryService { } try { - const headers = await this.commonHTTPHeaders; + const headers = { + ...await this.commonHTTPHeaders, + Accept: '*/*;api-version=4.0-preview.1' + }; + await this.requestService.request({ type: 'POST', url: this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`), diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index a211557390b..e019353ebce 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -13,7 +13,7 @@ import { assign } from 'vs/base/common/objects'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { flatten, distinct } from 'vs/base/common/arrays'; import { extract, buffer } from 'vs/base/node/zip'; -import { Promise, TPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IExtensionManifest, IGalleryMetadata, @@ -307,9 +307,9 @@ export class ExtensionManagementService implements IExtensionManagementService { private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise { return this.preUninstallExtension(extension) .then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.promptAndUninstall(extension, installed, force)) - .then(() => this.postUninstallExtension(extension.id), + .then(() => this.postUninstallExtension(extension), error => { - this.postUninstallExtension(extension.id, error); + this.postUninstallExtension(extension, error); return TPromise.wrapError(error); }); } @@ -423,9 +423,9 @@ export class ExtensionManagementService implements IExtensionManagementService { private doUninstall(extension: ILocalExtension): TPromise { return this.preUninstallExtension(extension) .then(() => this.uninstallExtension(extension.id)) - .then(() => this.postUninstallExtension(extension.id), + .then(() => this.postUninstallExtension(extension), error => { - this.postUninstallExtension(extension.id, error); + this.postUninstallExtension(extension, error); return TPromise.wrapError(error); }); } @@ -434,8 +434,7 @@ export class ExtensionManagementService implements IExtensionManagementService { const extensionPath = path.join(this.extensionsPath, extension.id); return pfs.exists(extensionPath) .then(exists => exists ? null : TPromise.wrapError(new Error(nls.localize('notExists', "Could not find extension")))) - .then(() => this._onUninstallExtension.fire(extension.id)) - .then(() => this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall)); + .then(() => this._onUninstallExtension.fire(extension.id)); } private uninstallExtension(id: string): TPromise { @@ -445,8 +444,12 @@ export class ExtensionManagementService implements IExtensionManagementService { .then(() => this.unsetObsolete(id)); } - private postUninstallExtension(id: string, error?: any): TPromise { - return this._onDidUninstallExtension.fire({ id, error }); + private async postUninstallExtension(extension: ILocalExtension, error?: any): TPromise { + if (!error) { + await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall); + } + + this._onDidUninstallExtension.fire({ id: extension.id, error }); } getInstalled(type: LocalExtensionType = null): TPromise { @@ -478,7 +481,7 @@ export class ExtensionManagementService implements IExtensionManagementService { const limiter = new Limiter(10); return this.scanExtensionFolders(root) - .then(extensionIds => Promise.join(extensionIds.map(id => { + .then(extensionIds => TPromise.join(extensionIds.map(id => { const extensionPath = path.join(root, id); const each = () => pfs.readdir(extensionPath).then(children => { From 728507a850e8338a5e94bc47d1d39a7ee64ef73f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Aug 2017 11:16:31 +0200 Subject: [PATCH 48/96] Update to native-watchdog@0.2.0 (fixes #32620) --- npm-shrinkwrap.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1112f9fcfdb..d077e904dd9 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -296,9 +296,9 @@ "resolved": "https://registry.npmjs.org/native-keymap/-/native-keymap-1.2.4.tgz" }, "native-watchdog": { - "version": "0.1.0", - "from": "native-watchdog@0.1.0", - "resolved": "https://registry.npmjs.org/native-watchdog/-/native-watchdog-0.1.0.tgz" + "version": "0.2.0", + "from": "native-watchdog@0.2.0", + "resolved": "https://registry.npmjs.org/native-watchdog/-/native-watchdog-0.2.0.tgz" }, "node-pty": { "version": "0.6.9", diff --git a/package.json b/package.json index 5c0a1adc7e9..afbaf148927 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "keytar": "^4.0.3", "minimist": "1.2.0", "native-keymap": "1.2.4", - "native-watchdog": "0.1.0", + "native-watchdog": "0.2.0", "node-pty": "0.6.9", "nsfw": "1.0.16", "semver": "4.3.6", From 518590916f61acf658e2e1dbf470d0e37a612dbe Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Aug 2017 11:32:19 +0200 Subject: [PATCH 49/96] Move off RGBA in base/color, as it will soon move to floats --- .../browser/viewParts/minimap/minimap.ts | 8 +-- src/vs/editor/common/core/rgba.ts | 57 +++++++++++++++++++ .../editor/common/view/minimapCharRenderer.ts | 18 +++--- .../test/browser/view/minimapFontCreator.ts | 6 +- .../common/view/minimapCharRenderer.test.ts | 18 +++--- 5 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 src/vs/editor/common/core/rgba.ts diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index c60b9b2dbca..206351eb6da 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -20,7 +20,7 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IDisposable } from 'vs/base/common/lifecycle'; import { RenderedLinesCollection, ILine } from 'vs/editor/browser/view/viewLayer'; import { Range } from 'vs/editor/common/core/range'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from "vs/editor/common/core/rgba"; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; import * as platform from 'vs/base/common/platform'; @@ -386,7 +386,7 @@ class MinimapBuffers { private readonly _buffers: [ImageData, ImageData]; private _lastUsedBuffer: number; - constructor(ctx: CanvasRenderingContext2D, WIDTH: number, HEIGHT: number, background: RGBA) { + constructor(ctx: CanvasRenderingContext2D, WIDTH: number, HEIGHT: number, background: RGBA8) { this._backgroundFillData = MinimapBuffers._createBackgroundFillData(WIDTH, HEIGHT, background); this._buffers = [ ctx.createImageData(WIDTH, HEIGHT), @@ -406,7 +406,7 @@ class MinimapBuffers { return result; } - private static _createBackgroundFillData(WIDTH: number, HEIGHT: number, background: RGBA): Uint8ClampedArray { + private static _createBackgroundFillData(WIDTH: number, HEIGHT: number, background: RGBA8): Uint8ClampedArray { const backgroundR = background.r; const backgroundG = background.g; const backgroundB = background.b; @@ -830,7 +830,7 @@ export class Minimap extends ViewPart { private static _renderLine( target: ImageData, - backgroundColor: RGBA, + backgroundColor: RGBA8, useLighterFont: boolean, renderMinimap: RenderMinimap, colorTracker: MinimapTokensColorTracker, diff --git a/src/vs/editor/common/core/rgba.ts b/src/vs/editor/common/core/rgba.ts new file mode 100644 index 00000000000..ad6070ce13c --- /dev/null +++ b/src/vs/editor/common/core/rgba.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +/** + * A very VM friendly rgba datastructure. + * Please don't touch unless you take a look at the IR. + */ +export class RGBA8 { + _rgba8Brand: void; + + /** + * Red: integer in [0-255] + */ + public readonly r: number; + /** + * Green: integer in [0-255] + */ + public readonly g: number; + /** + * Blue: integer in [0-255] + */ + public readonly b: number; + /** + * Alpha: integer in [0-255] + */ + public readonly a: number; + + constructor(r: number, g: number, b: number, a: number) { + this.r = RGBA8._clampInt_0_255(r); + this.g = RGBA8._clampInt_0_255(g); + this.b = RGBA8._clampInt_0_255(b); + this.a = RGBA8._clampInt_0_255(a); + } + + public static equals(a: RGBA8, b: RGBA8): boolean { + return ( + a.r === b.r + && a.g === b.g + && a.b === b.b + && a.a === b.a + ); + } + + private static _clampInt_0_255(c: number): number { + if (c < 0) { + return 0; + } + if (c > 255) { + return 255; + } + return c | 0; + } +} diff --git a/src/vs/editor/common/view/minimapCharRenderer.ts b/src/vs/editor/common/view/minimapCharRenderer.ts index 63f98b0c351..9de30a36bf9 100644 --- a/src/vs/editor/common/view/minimapCharRenderer.ts +++ b/src/vs/editor/common/view/minimapCharRenderer.ts @@ -6,7 +6,7 @@ import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes'; import Event, { Emitter } from 'vs/base/common/event'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from "vs/editor/common/core/rgba"; export class MinimapTokensColorTracker { private static _INSTANCE: MinimapTokensColorTracker = null; @@ -17,7 +17,7 @@ export class MinimapTokensColorTracker { return this._INSTANCE; } - private _colors: RGBA[]; + private _colors: RGBA8[]; private _backgroundIsLight: boolean; private _onDidChange = new Emitter(); @@ -41,14 +41,16 @@ export class MinimapTokensColorTracker { } this._colors = [null]; for (let colorId = 1; colorId < colorMap.length; colorId++) { - this._colors[colorId] = colorMap[colorId].rgba; + const source = colorMap[colorId].rgba; + // Use a VM friendly data-type + this._colors[colorId] = new RGBA8(source.r, source.g, source.b, source.a); } let backgroundLuminosity = colorMap[ColorId.DefaultBackground].getRelativeLuminance(); this._backgroundIsLight = (backgroundLuminosity >= 0.5); this._onDidChange.fire(void 0); } - public getColor(colorId: ColorId): RGBA { + public getColor(colorId: ColorId): RGBA8 { if (colorId < 1 || colorId >= this._colors.length) { // background color (basically invisible) colorId = ColorId.DefaultBackground; @@ -121,7 +123,7 @@ export class MinimapCharRenderer { return (chCode % Constants.CHAR_COUNT); } - public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; @@ -198,7 +200,7 @@ export class MinimapCharRenderer { } } - public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; @@ -235,7 +237,7 @@ export class MinimapCharRenderer { } } - public x2BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x2BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; @@ -307,7 +309,7 @@ export class MinimapCharRenderer { } } - public x1BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA, backgroundColor: RGBA, useLighterFont: boolean): void { + public x1BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void { if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) { console.warn('bad render request outside image data'); return; diff --git a/src/vs/editor/test/browser/view/minimapFontCreator.ts b/src/vs/editor/test/browser/view/minimapFontCreator.ts index 7819e4cd8a3..66b46dd65ac 100644 --- a/src/vs/editor/test/browser/view/minimapFontCreator.ts +++ b/src/vs/editor/test/browser/view/minimapFontCreator.ts @@ -7,7 +7,7 @@ import { Constants, MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer'; import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from "vs/editor/common/core/rgba"; let canvas = document.getElementById('my-canvas'); let ctx = canvas.getContext('2d'); @@ -41,8 +41,8 @@ function createFakeImageData(width: number, height: number): ImageData { function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y: number): void { - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); { let x2 = createFakeImageData(Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x2_CHAR_HEIGHT); diff --git a/src/vs/editor/test/common/view/minimapCharRenderer.test.ts b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts index 0612103a1c9..b59fb4f7bad 100644 --- a/src/vs/editor/test/common/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/common/view/minimapCharRenderer.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { Constants } from 'vs/editor/common/view/minimapCharRenderer'; import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory'; import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; -import { RGBA } from 'vs/base/common/color'; +import { RGBA8 } from 'vs/editor/common/core/rgba'; suite('MinimapCharRenderer', () => { @@ -79,8 +79,8 @@ suite('MinimapCharRenderer', () => { setSampleData('d'.charCodeAt(0), sampleD); let renderer = MinimapCharRendererFactory.create(sampleData); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { @@ -106,8 +106,8 @@ suite('MinimapCharRenderer', () => { test('letter d @ 2x at runtime', () => { let renderer = getOrCreateMinimapCharRenderer(); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x2_CHAR_WIDTH, Constants.x2_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { @@ -135,8 +135,8 @@ suite('MinimapCharRenderer', () => { setSampleData('d'.charCodeAt(0), sampleD); let renderer = MinimapCharRendererFactory.create(sampleData); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { @@ -161,8 +161,8 @@ suite('MinimapCharRenderer', () => { test('letter d @ 1x at runtime', () => { let renderer = getOrCreateMinimapCharRenderer(); - let background = new RGBA(0, 0, 0, 255); - let color = new RGBA(255, 255, 255, 255); + let background = new RGBA8(0, 0, 0, 255); + let color = new RGBA8(255, 255, 255, 255); let imageData = createFakeImageData(Constants.x1_CHAR_WIDTH, Constants.x1_CHAR_HEIGHT); // set the background color for (let i = 0, len = imageData.data.length / 4; i < len; i++) { From 929ac6073094230dcc77b72bedbcc6706cb13335 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 18 Aug 2017 11:34:42 +0200 Subject: [PATCH 50/96] keep array in sync; fixes #32337 --- src/vs/workbench/parts/debug/browser/debugActionItems.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index c4fd3c05bc8..86c56ed37d9 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -167,6 +167,7 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { if (options.length === 0) { options.push(nls.localize('noConfigurations', "No Configurations")); + this.executeOnSelect.push(() => false); } options.push(StartDebugActionItem.SEPARATOR); this.executeOnSelect.push(undefined); From dd06daf898ef4468cfc1248b72c75af05aa892d1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 18 Aug 2017 12:18:08 +0200 Subject: [PATCH 51/96] fixes #32381 --- src/vs/base/node/request.ts | 92 +++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts index d6d05396fc6..d46e8b6e58c 100644 --- a/src/vs/base/node/request.ts +++ b/src/vs/base/node/request.ts @@ -58,59 +58,61 @@ async function getNodeRequest(options: IRequestOptions): TPromise { let req: http.ClientRequest; - return new TPromise(async (c, e) => { - const endpoint = parseUrl(options.url); - const rawRequest = options.getRawRequest - ? options.getRawRequest(options) - : await getNodeRequest(options); + const rawRequestPromise = options.getRawRequest + ? TPromise.as(options.getRawRequest(options)) + : getNodeRequest(options); - const opts: https.RequestOptions = { - hostname: endpoint.hostname, - port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), - protocol: endpoint.protocol, - path: endpoint.path, - method: options.type || 'GET', - headers: options.headers, - agent: options.agent, - rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true - }; + return rawRequestPromise.then(rawRequest => { + return new TPromise((c, e) => { + const endpoint = parseUrl(options.url); - if (options.user && options.password) { - opts.auth = options.user + ':' + options.password; - } + const opts: https.RequestOptions = { + hostname: endpoint.hostname, + port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), + protocol: endpoint.protocol, + path: endpoint.path, + method: options.type || 'GET', + headers: options.headers, + agent: options.agent, + rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true + }; - req = rawRequest(opts, (res: http.ClientResponse) => { - const followRedirects = isNumber(options.followRedirects) ? options.followRedirects : 3; - - if (res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { - request(assign({}, options, { - url: res.headers['location'], - followRedirects: followRedirects - 1 - })).done(c, e); - } else { - let stream: Stream = res; - - if (res.headers['content-encoding'] === 'gzip') { - stream = stream.pipe(createGunzip()); - } - - c({ res, stream }); + if (options.user && options.password) { + opts.auth = options.user + ':' + options.password; } - }); - req.on('error', e); + req = rawRequest(opts, (res: http.ClientResponse) => { + const followRedirects = isNumber(options.followRedirects) ? options.followRedirects : 3; - if (options.timeout) { - req.setTimeout(options.timeout); - } + if (res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { + request(assign({}, options, { + url: res.headers['location'], + followRedirects: followRedirects - 1 + })).done(c, e); + } else { + let stream: Stream = res; - if (options.data) { - req.write(options.data); - } + if (res.headers['content-encoding'] === 'gzip') { + stream = stream.pipe(createGunzip()); + } - req.end(); - }, - () => req && req.abort()); + c({ res, stream }); + } + }); + + req.on('error', e); + + if (options.timeout) { + req.setTimeout(options.timeout); + } + + if (options.data) { + req.write(options.data); + } + + req.end(); + }, () => req && req.abort()); + }); } function isSuccess(context: IRequestContext): boolean { From d30567bdd328dfc3fa274c4eac4d9cf32ec9414d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Aug 2017 11:52:56 +0200 Subject: [PATCH 52/96] don't log errors twice, #32766 --- src/bootstrap.js | 18 ++++++++++-------- .../electron-browser/extensionHost.ts | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/bootstrap.js b/src/bootstrap.js index 3f94abadd31..dad68bf5c80 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -103,13 +103,15 @@ if (!process.env['VSCODE_ALLOW_IO']) { process.__defineGetter__('stdin', function () { return writable; }); } -// Handle uncaught exceptions -process.on('uncaughtException', function (err) { - console.error('Uncaught Exception: ', err.toString()); - if (err.stack) { - console.error(err.stack); - } -}); +if (!process.env['VSCODE_HANDLES_UNCAUGHT_ERRORS']) { + // Handle uncaught exceptions + process.on('uncaughtException', function (err) { + console.error('Uncaught Exception: ', err.toString()); + if (err.stack) { + console.error(err.stack); + } + }); +} // Kill oneself if one's parent dies. Much drama. if (process.env['VSCODE_PARENT_PID']) { @@ -138,4 +140,4 @@ if (typeof crashReporterOptionsRaw === 'string') { } } -require('./bootstrap-amd').bootstrap(process.env['AMD_ENTRYPOINT']); \ No newline at end of file +require('./bootstrap-amd').bootstrap(process.env['AMD_ENTRYPOINT']); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index a932aa054e3..b107064177a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -141,6 +141,7 @@ export class ExtensionHostProcessWorker { VERBOSE_LOGGING: true, VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), VSCODE_IPC_HOOK_EXTHOST: pipeName, + VSCODE_HANDLES_UNCAUGHT_ERRORS: true, ELECTRON_NO_ASAR: '1' }), // We only detach the extension host on windows. Linux and Mac orphan by default From 69c9edebb919f129b1ee5ffe47afb7c35605a6fd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Aug 2017 12:02:26 +0200 Subject: [PATCH 53/96] prefix stack with extension id, #32766 --- src/vs/base/common/errors.ts | 18 ++++++++++++ src/vs/workbench/api/node/extHost.api.impl.ts | 26 +---------------- .../api/node/extHostExtensionService.ts | 29 ++++++++++++++++++- src/vs/workbench/node/extensionHostMain.ts | 23 +++++++++++++-- 4 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index a00d5ebd2ad..568a6baee1a 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -168,6 +168,24 @@ export function transformErrorForSerialization(error: any): any { return error; } +// see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces +export interface V8CallSite { + getThis(): any; + getTypeName(): string; + getFunction(): string; + getFunctionName(): string; + getMethodName(): string; + getFileName(): string; + getLineNumber(): number; + getColumnNumber(): number; + getEvalOrigin(): string; + isToplevel(): boolean; + isEval(): boolean; + isNative(): boolean; + isConstructor(): boolean; + toString(): string; +} + const canceledName = 'Canceled'; /** diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 42991687a61..2fae8ebfbe8 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -47,7 +47,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as vscode from 'vscode'; import * as paths from 'vs/base/common/paths'; -import { realpath } from 'fs'; import { MainContext, ExtHostContext, IInitData } from './extHost.protocol'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; @@ -621,30 +620,7 @@ class Extension implements vscode.Extension { } export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise { - return createExtensionPathIndex(extensionService).then(trie => defineAPI(apiFactory, trie)); -} - -function createExtensionPathIndex(extensionService: ExtHostExtensionService): TPromise> { - - // create trie to enable fast 'filename -> extension id' look up - const trie = new TrieMap(); - const extensions = extensionService.getAllExtensionDescriptions().map(ext => { - if (!ext.main) { - return undefined; - } - return new TPromise((resolve, reject) => { - realpath(ext.extensionFolderPath, (err, path) => { - if (err) { - reject(err); - } else { - trie.insert(path, ext); - resolve(void 0); - } - }); - }); - }); - - return TPromise.join(extensions).then(() => trie); + return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie)); } function defineAPI(factory: IExtensionApiFactory, extensionPaths: TrieMap): void { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index d14ff88d232..e2f327c3efb 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -18,6 +18,8 @@ import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironm import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule } from "vs/workbench/api/node/extHostExtensionActivator"; import { Barrier } from "vs/workbench/services/extensions/node/barrier"; import { ExtHostThreadService } from "vs/workbench/services/thread/node/extHostThreadService"; +import { realpath } from 'fs'; +import { TrieMap } from "vs/base/common/map"; class ExtensionMemento implements IExtensionMemento { @@ -115,7 +117,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _storagePath: ExtensionStoragePath; private readonly _proxy: MainThreadExtensionServiceShape; private _activator: ExtensionsActivator; - + private _extensionPathIndex: TPromise>; /** * This class is constructed manually because it is a service, so it doesn't use any ctor injection */ @@ -202,6 +204,31 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } } + // create trie to enable fast 'filename -> extension id' look up + public getExtensionPathIndex(): TPromise> { + if (!this._extensionPathIndex) { + const trie = new TrieMap(); + const extensions = this.getAllExtensionDescriptions().map(ext => { + if (!ext.main) { + return undefined; + } + return new TPromise((resolve, reject) => { + realpath(ext.extensionFolderPath, (err, path) => { + if (err) { + reject(err); + } else { + trie.insert(path, ext); + resolve(void 0); + } + }); + }); + }); + this._extensionPathIndex = TPromise.join(extensions).then(() => trie); + } + return this._extensionPathIndex; + } + + public deactivate(extensionId: string): TPromise { let result: TPromise = TPromise.as(void 0); diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 2fcb43ba60c..62476408f29 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -12,6 +12,7 @@ import { join } from 'path'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService'; +import { IExtensionDescription } from "vs/platform/extensions/common/extensions"; import { QueryType, ISearchQuery } from 'vs/platform/search/common/search'; import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; import { RemoteTelemetryService } from 'vs/workbench/api/node/extHostTelemetry'; @@ -49,9 +50,27 @@ export class ExtensionHostMain { const telemetryService = new RemoteTelemetryService('pluginHostTelemetry', threadService); this._extensionService = new ExtHostExtensionService(initData, threadService, telemetryService); - // Error forwarding + // error forwarding and stack trace scanning + this._extensionService.getExtensionPathIndex().then(map => { + (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { + let stackTraceMessage = ''; + let extension: IExtensionDescription; + for (const call of stackTrace) { + stackTraceMessage += `\n\tat ${call.toString()}`; + extension = extension || map.findSubstr(stackTrace[0].getFileName()); + } + let name = error.name || 'Error'; + if (extension) { + name = `[${extension.id}] ${name}`; + } + return `${name}: ${error.message}${stackTraceMessage}`; + }; + }); const mainThreadErrors = threadService.get(MainContext.MainThreadErrors); - errors.setUnexpectedErrorHandler(err => mainThreadErrors.$onUnexpectedExtHostError(errors.transformErrorForSerialization(err))); + errors.setUnexpectedErrorHandler(err => { + const data = errors.transformErrorForSerialization(err); + mainThreadErrors.$onUnexpectedExtHostError(data); + }); // Configure the watchdog to kill our process if the JS event loop is unresponsive for more than 10s if (!initData.environment.isExtensionDevelopmentDebug) { From dc69d8bf6d14c59ecbfcfede3a816c6eb95c57a1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Aug 2017 15:02:19 +0200 Subject: [PATCH 54/96] properly spec error data, #32766 --- src/vs/base/common/errors.ts | 9 +++++++++ .../workbench/api/electron-browser/mainThreadErrors.ts | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 568a6baee1a..18d8cad519c 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -152,6 +152,15 @@ export function onUnexpectedPromiseError(promise: TPromise): TPromise Date: Fri, 18 Aug 2017 15:25:29 +0200 Subject: [PATCH 55/96] send extension id to main thread and decide there how to show errors, #32766 --- .../api/electron-browser/mainThreadErrors.ts | 11 ++++++----- src/vs/workbench/api/node/extHost.protocol.ts | 3 ++- src/vs/workbench/node/extensionHostMain.ts | 11 +++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadErrors.ts b/src/vs/workbench/api/electron-browser/mainThreadErrors.ts index 25ee026c2b1..035895cc5d0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadErrors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadErrors.ts @@ -4,24 +4,25 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as errors from 'vs/base/common/errors'; +import { SerializedError, onUnexpectedError } from 'vs/base/common/errors'; import { MainThreadErrorsShape, MainContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; @extHostNamedCustomer(MainContext.MainThreadErrors) export class MainThreadErrors implements MainThreadErrorsShape { - public dispose(): void { + dispose(): void { + // } - public $onUnexpectedExtHostError(err: any | errors.SerializedError): void { + $onUnexpectedError(err: any | SerializedError, extensionId: string | undefined): void { if (err.$isError) { const { name, message, stack } = err; err = new Error(); + err.message = extensionId ? `[${extensionId}] ${message}` : message; err.name = name; - err.message = message; err.stack = stack; } - errors.onUnexpectedError(err); + onUnexpectedError(err); } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 395ee8ae8e9..faddb4f09e6 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -47,6 +47,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { ITreeItem } from 'vs/workbench/parts/views/common/views'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDisposable } from "vs/base/common/lifecycle"; +import { SerializedError } from "vs/base/common/errors"; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -189,7 +190,7 @@ export interface MainThreadTreeViewsShape extends IDisposable { } export interface MainThreadErrorsShape extends IDisposable { - $onUnexpectedExtHostError(err: any): void; + $onUnexpectedError(err: any | SerializedError, extensionId: string | undefined): void; } export interface MainThreadLanguageFeaturesShape extends IDisposable { diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 62476408f29..0e86283dd78 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -51,6 +51,7 @@ export class ExtensionHostMain { this._extensionService = new ExtHostExtensionService(initData, threadService, telemetryService); // error forwarding and stack trace scanning + const extensionErrors = new WeakMap(); this._extensionService.getExtensionPathIndex().then(map => { (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { let stackTraceMessage = ''; @@ -59,17 +60,15 @@ export class ExtensionHostMain { stackTraceMessage += `\n\tat ${call.toString()}`; extension = extension || map.findSubstr(stackTrace[0].getFileName()); } - let name = error.name || 'Error'; - if (extension) { - name = `[${extension.id}] ${name}`; - } - return `${name}: ${error.message}${stackTraceMessage}`; + extensionErrors.set(error, extension); + return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`; }; }); const mainThreadErrors = threadService.get(MainContext.MainThreadErrors); errors.setUnexpectedErrorHandler(err => { const data = errors.transformErrorForSerialization(err); - mainThreadErrors.$onUnexpectedExtHostError(data); + const extension = extensionErrors.get(err); + mainThreadErrors.$onUnexpectedError(data, extension && extension.id); }); // Configure the watchdog to kill our process if the JS event loop is unresponsive for more than 10s From 02f2b062189b5150b1d62bf88783e45e9cb1a38d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Aug 2017 16:05:27 +0200 Subject: [PATCH 56/96] Disconnected proxies resolve all outstanding and future promises with a canceled error --- .../electron-browser/extensionService.ts | 2 +- .../services/extensions/node/rpcProtocol.ts | 19 +++++++++++++++++++ .../thread/node/abstractThreadService.ts | 4 ++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index e33388b7775..735c3b15c0f 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -105,7 +105,7 @@ export class ExtensionService implements IExtensionService { this._extensionHostProcessWorker = null; } if (this._extensionHostProcessThreadService) { - // this._extensionHostProcessThreadService.dispose(); // TODO@rehost + this._extensionHostProcessThreadService.dispose(); this._extensionHostProcessThreadService = null; } for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) { diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts index 61d4259a78a..7f23b136747 100644 --- a/src/vs/workbench/services/extensions/node/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -16,6 +16,7 @@ export interface IDispatcher { export class RPCProtocol { + private _isDisposed: boolean; private _bigHandler: IDispatcher; private _lastMessageId: number; private readonly _invokedHandlers: { [req: string]: TPromise; }; @@ -23,6 +24,7 @@ export class RPCProtocol { private readonly _multiplexor: RPCMultiplexer; constructor(protocol: IMessagePassingProtocol) { + this._isDisposed = false; this._bigHandler = null; this._lastMessageId = 0; this._invokedHandlers = Object.create(null); @@ -30,7 +32,20 @@ export class RPCProtocol { this._multiplexor = new RPCMultiplexer(protocol, (msg) => this._receiveOneMessage(msg)); } + public dispose(): void { + this._isDisposed = true; + + // Release all outstanding promises with a canceled error + Object.keys(this._pendingRPCReplies).forEach((msgId) => { + const pending = this._pendingRPCReplies[msgId]; + pending.resolveErr(errors.canceled()); + }); + } + private _receiveOneMessage(rawmsg: string): void { + if (this._isDisposed) { + throw new Error(`disposed`); + } let msg = marshalling.parse(rawmsg); if (msg.seq) { @@ -97,6 +112,10 @@ export class RPCProtocol { } public callOnRemote(proxyId: string, methodName: string, args: any[]): TPromise { + if (this._isDisposed) { + return TPromise.wrapError(errors.canceled()); + } + let req = String(++this._lastMessageId); let result = new LazyPromise(() => { this._multiplexor.send(MessageFactory.cancel(req)); diff --git a/src/vs/workbench/services/thread/node/abstractThreadService.ts b/src/vs/workbench/services/thread/node/abstractThreadService.ts index e8a75d60cde..e6f11ed4c78 100644 --- a/src/vs/workbench/services/thread/node/abstractThreadService.ts +++ b/src/vs/workbench/services/thread/node/abstractThreadService.ts @@ -25,6 +25,10 @@ export abstract class AbstractThreadService implements IDispatcher { this._rpcProtocol.setDispatcher(this); } + public dispose(): void { + this._rpcProtocol.dispose(); + } + public invoke(proxyId: string, methodName: string, args: any[]): any { if (!this._locals[proxyId]) { throw new Error('Unknown actor ' + proxyId); From b824c8e4d9285fc50faf8667334e2881fa0f6c4c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Aug 2017 16:08:44 +0200 Subject: [PATCH 57/96] Fixes #32755: Register listeners to already instantiated editors in MainThreadDocumentsAndEditors --- .../mainThreadDocumentsAndEditors.ts | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts index 13a6cb85560..eb9246d74b4 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors.ts @@ -113,20 +113,22 @@ class MainThreadDocumentAndEditorStateComputer { private _toDispose: IDisposable[] = []; private _toDisposeOnEditorRemove = new Map(); - private _onDidChangeState = new Emitter(); private _currentState: DocumentAndEditorState; - readonly onDidChangeState: Event = this._onDidChangeState.event; - constructor( + private readonly _onDidChangeState: (delta: DocumentAndEditorStateDelta) => void, @IModelService private _modelService: IModelService, @ICodeEditorService private _codeEditorService: ICodeEditorService, @IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService ) { - this._modelService.onModelAdded(this.updateState, this, this._toDispose); - this._modelService.onModelRemoved(this.updateState, this, this._toDispose); + this._modelService.onModelAdded(this._updateState, this, this._toDispose); + this._modelService.onModelRemoved(this._updateState, this, this._toDispose); + this._codeEditorService.onCodeEditorAdd(this._onDidAddEditor, this, this._toDispose); this._codeEditorService.onCodeEditorRemove(this._onDidRemoveEditor, this, this._toDispose); + this._codeEditorService.listCodeEditors().forEach(this._onDidAddEditor, this); + + this._updateState(); } dispose(): void { @@ -134,10 +136,10 @@ class MainThreadDocumentAndEditorStateComputer { } private _onDidAddEditor(e: ICommonCodeEditor): void { - this._toDisposeOnEditorRemove.set(e.getId(), e.onDidChangeModel(() => this.updateState())); - this._toDisposeOnEditorRemove.set(e.getId(), e.onDidFocusEditor(() => this.updateState())); - this._toDisposeOnEditorRemove.set(e.getId(), e.onDidBlurEditor(() => this.updateState())); - this.updateState(); + this._toDisposeOnEditorRemove.set(e.getId(), e.onDidChangeModel(() => this._updateState())); + this._toDisposeOnEditorRemove.set(e.getId(), e.onDidFocusEditor(() => this._updateState())); + this._toDisposeOnEditorRemove.set(e.getId(), e.onDidBlurEditor(() => this._updateState())); + this._updateState(); } private _onDidRemoveEditor(e: ICommonCodeEditor): void { @@ -145,11 +147,11 @@ class MainThreadDocumentAndEditorStateComputer { if (sub) { this._toDisposeOnEditorRemove.delete(e.getId()); sub.dispose(); - this.updateState(); + this._updateState(); } } - updateState(): void { + private _updateState(): void { // models: ignore too large models const models = this._modelService.getModels(); @@ -207,7 +209,7 @@ class MainThreadDocumentAndEditorStateComputer { const delta = DocumentAndEditorState.compute(this._currentState, newState); if (!delta.isEmpty) { this._currentState = newState; - this._onDidChangeState.fire(delta); + this._onDidChangeState(delta); } } } @@ -244,7 +246,6 @@ export class MainThreadDocumentsAndEditors { @ITelemetryService telemetryService: ITelemetryService ) { this._proxy = extHostContext.get(ExtHostContext.ExtHostDocumentsAndEditors); - this._stateComputer = new MainThreadDocumentAndEditorStateComputer(_modelService, codeEditorService, _workbenchEditorService); const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService); extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments); @@ -252,18 +253,18 @@ export class MainThreadDocumentsAndEditors { const mainThreadEditors = new MainThreadEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, telemetryService); extHostContext.set(MainContext.MainThreadEditors, mainThreadEditors); + // It is expected that the ctor of the state computer calls our `_onDelta`. + this._stateComputer = new MainThreadDocumentAndEditorStateComputer(delta => this._onDelta(delta), _modelService, codeEditorService, _workbenchEditorService); + this._toDispose = [ mainThreadDocuments, mainThreadEditors, this._stateComputer, - this._stateComputer.onDidChangeState(this._onDelta, this), this._onTextEditorAdd, this._onTextEditorRemove, this._onDocumentAdd, this._onDocumentRemove, ]; - - this._stateComputer.updateState(); } dispose(): void { From 211918fbfde4d70a26b2ca8efba75f4015237604 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Aug 2017 15:35:47 +0200 Subject: [PATCH 58/96] remove unused code --- src/vs/base/common/strings.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index cd2254d3950..7af18aadcb6 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -182,18 +182,6 @@ export function endsWith(haystack: string, needle: string): boolean { } } -export function indexOfIgnoreCase(haystack: string, needle: string, position: number = 0): number { - let index = haystack.indexOf(needle, position); - if (index < 0) { - if (position > 0) { - haystack = haystack.substr(position); - } - needle = escapeRegExpCharacters(needle); - index = haystack.search(new RegExp(needle, 'i')); - } - return index; -} - export interface RegExpOptions { matchCase?: boolean; wholeWord?: boolean; From 253490c9dd011e65475695a789fd178aa0c68b16 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Aug 2017 16:29:16 +0200 Subject: [PATCH 59/96] focussed -> focused (#32521) --- src/vs/editor/common/controller/coreCommands.ts | 2 +- src/vs/editor/contrib/find/browser/findWidget.ts | 10 +++++----- src/vs/editor/contrib/find/common/findController.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/common/controller/coreCommands.ts b/src/vs/editor/common/controller/coreCommands.ts index 4f47fc04bbc..f3eaebb76c4 100644 --- a/src/vs/editor/common/controller/coreCommands.ts +++ b/src/vs/editor/common/controller/coreCommands.ts @@ -1687,7 +1687,7 @@ namespace Config { return this._runEditorHandler(focusedEditor, args); } - // Ignore this action when user is focussed on an element that allows for entering text + // Ignore this action when user is focused on an element that allows for entering text let activeElement = document.activeElement; if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) { document.execCommand(this._inputHandler); diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index dcc9bf926da..b0063b424ba 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -25,7 +25,7 @@ import { FIND_IDS, MATCHES_LIMIT } from 'vs/editor/contrib/find/common/findModel import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/common/findState'; import { Range } from 'vs/editor/common/core/range'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { CONTEXT_FIND_INPUT_FOCUSSED } from 'vs/editor/contrib/find/common/findController'; +import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/common/findController'; import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; @@ -105,7 +105,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _isReplaceVisible: boolean; private _focusTracker: dom.IFocusTracker; - private _findInputFocussed: IContextKey; + private _findInputFocused: IContextKey; private _viewZone: FindWidgetViewZone; private _viewZoneId: number; @@ -192,10 +192,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._updateToggleSelectionFindButton(); } })); - this._findInputFocussed = CONTEXT_FIND_INPUT_FOCUSSED.bindTo(contextKeyService); + this._findInputFocused = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService); this._focusTracker = this._register(dom.trackFocus(this._findInput.inputBox.inputElement)); this._focusTracker.addFocusListener(() => { - this._findInputFocussed.set(true); + this._findInputFocused.set(true); if (this._toggleSelectionFind.checked) { let selection = this._codeEditor.getSelection(); @@ -212,7 +212,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } }); this._focusTracker.addBlurListener(() => { - this._findInputFocussed.set(false); + this._findInputFocused.set(false); }); this._codeEditor.addOverlayWidget(this); diff --git a/src/vs/editor/contrib/find/common/findController.ts b/src/vs/editor/contrib/find/common/findController.ts index 78cd27c8d0f..f1ed34cc1c7 100644 --- a/src/vs/editor/contrib/find/common/findController.ts +++ b/src/vs/editor/contrib/find/common/findController.ts @@ -39,7 +39,7 @@ export interface IFindStartOptions { export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey('findWidgetVisible', false); export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated(); -export const CONTEXT_FIND_INPUT_FOCUSSED = new RawContextKey('findInputFocussed', false); +export const CONTEXT_FIND_INPUT_FOCUSED = new RawContextKey('findInputFocussed', false); export class CommonFindController extends Disposable implements editorCommon.IEditorContribution { @@ -1131,7 +1131,7 @@ export class ShowNextFindTermAction extends MatchFindAction { precondition: CONTEXT_FIND_WIDGET_VISIBLE, kbOpts: { weight: CommonEditorRegistry.commandWeight(5), - kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSSED, EditorContextKeys.focus), + kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus), primary: ShowNextFindTermKeybinding.primary, mac: ShowNextFindTermKeybinding.mac, win: ShowNextFindTermKeybinding.win, @@ -1156,7 +1156,7 @@ export class ShpwPreviousFindTermAction extends MatchFindAction { precondition: CONTEXT_FIND_WIDGET_VISIBLE, kbOpts: { weight: CommonEditorRegistry.commandWeight(5), - kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSSED, EditorContextKeys.focus), + kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus), primary: ShowPreviousFindTermKeybinding.primary, mac: ShowPreviousFindTermKeybinding.mac, win: ShowPreviousFindTermKeybinding.win, From 6dd175fd41cace81adc4f96d98091cc1d543a231 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Aug 2017 16:58:45 +0200 Subject: [PATCH 60/96] show message source in title, #21302 --- src/vs/platform/message/common/message.ts | 3 +- .../mainThreadMessageService.ts | 26 ++++++++++------- src/vs/workbench/api/node/extHost.api.impl.ts | 8 ++--- src/vs/workbench/api/node/extHost.protocol.ts | 7 ++++- .../api/node/extHostMessageService.ts | 29 ++++++++++--------- .../services/message/browser/messageList.ts | 8 ++++- .../api/extHostMessagerService.test.ts | 12 ++++---- 7 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/vs/platform/message/common/message.ts b/src/vs/platform/message/common/message.ts index 8a04042fff4..627bbe0900b 100644 --- a/src/vs/platform/message/common/message.ts +++ b/src/vs/platform/message/common/message.ts @@ -13,6 +13,7 @@ import { Action } from 'vs/base/common/actions'; export interface IMessageWithAction { message: string; actions: Action[]; + source?: string; } export interface IConfirmation { @@ -77,4 +78,4 @@ export interface IChoiceService { choose(severity: Severity, message: string, options: string[], cancelId: number, modal?: boolean): TPromise; } -export import Severity = Severity; \ No newline at end of file +export import Severity = Severity; diff --git a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts index abda4e128e8..656064fc982 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadMessageService.ts @@ -9,32 +9,35 @@ import { IMessageService, IChoiceService } from 'vs/platform/message/common/mess import Severity from 'vs/base/common/severity'; import { Action } from 'vs/base/common/actions'; import { TPromise as Promise } from 'vs/base/common/winjs.base'; -import { MainThreadMessageServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol'; -import * as vscode from 'vscode'; +import { MainThreadMessageServiceShape, MainContext, IExtHostContext, MainThreadMessageOptions } from '../node/extHost.protocol'; import { extHostNamedCustomer } from "vs/workbench/api/electron-browser/extHostCustomers"; +import { IExtensionService } from "vs/platform/extensions/common/extensions"; @extHostNamedCustomer(MainContext.MainThreadMessageService) export class MainThreadMessageService implements MainThreadMessageServiceShape { constructor( extHostContext: IExtHostContext, - @IMessageService private _messageService: IMessageService, - @IChoiceService private _choiceService: IChoiceService + @IExtensionService private readonly _extensionService: IExtensionService, + @IMessageService private readonly _messageService: IMessageService, + @IChoiceService private readonly _choiceService: IChoiceService ) { + // } - public dispose(): void { + dispose(): void { + // } - $showMessage(severity: Severity, message: string, options: vscode.MessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { + $showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { if (options.modal) { - return this.showModalMessage(severity, message, commands); + return this._showModalMessage(severity, message, commands); } else { - return this.showMessage(severity, message, commands); + return this._showMessage(severity, message, commands, options.extensionId); } } - private showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { + private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extensionId: string): Thenable { return new Promise(resolve => { @@ -68,12 +71,13 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { messageHide = this._messageService.show(severity, { message, - actions + actions, + source: extensionId }); }); } - private showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { + private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable { let cancelId: number | undefined = void 0; const options = commands.map((command, index) => { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 2fae8ebfbe8..6c71b094f8e 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -325,13 +325,13 @@ export function createApiFactory( return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables); }, showInformationMessage(message, first, ...rest) { - return extHostMessageService.showMessage(Severity.Info, message, first, rest); + return extHostMessageService.showMessage(extension.id, Severity.Info, message, first, rest); }, showWarningMessage(message, first, ...rest) { - return extHostMessageService.showMessage(Severity.Warning, message, first, rest); + return extHostMessageService.showMessage(extension.id, Severity.Warning, message, first, rest); }, showErrorMessage(message, first, ...rest) { - return extHostMessageService.showMessage(Severity.Error, message, first, rest); + return extHostMessageService.showMessage(extension.id, Severity.Error, message, first, rest); }, showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) { return extHostQuickOpen.showQuickPick(items, options, token); @@ -366,7 +366,7 @@ export function createApiFactory( }, // proposed API sampleFunction: proposedApiFunction(extension, () => { - return extHostMessageService.showMessage(Severity.Info, 'Hello Proposed Api!', {}, []); + return extHostMessageService.showMessage(extension.id, Severity.Info, 'Hello Proposed Api!', {}, []); }), }; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index faddb4f09e6..a653f9d00b2 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -222,8 +222,13 @@ export interface MainThreadLanguagesShape extends IDisposable { $getLanguages(): TPromise; } +export interface MainThreadMessageOptions { + extensionId?: string; + modal?: boolean; +} + export interface MainThreadMessageServiceShape extends IDisposable { - $showMessage(severity: Severity, message: string, options: vscode.MessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable; + $showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable; } export interface MainThreadOutputServiceShape extends IDisposable { diff --git a/src/vs/workbench/api/node/extHostMessageService.ts b/src/vs/workbench/api/node/extHostMessageService.ts index 6ae9309f3ce..3ac25ae5226 100644 --- a/src/vs/workbench/api/node/extHostMessageService.ts +++ b/src/vs/workbench/api/node/extHostMessageService.ts @@ -6,22 +6,13 @@ import Severity from 'vs/base/common/severity'; import vscode = require('vscode'); -import { MainContext, MainThreadMessageServiceShape, IMainContext } from './extHost.protocol'; +import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol'; -const emptyMessageOptions: vscode.MessageOptions = Object.create(null); function isMessageItem(item: any): item is vscode.MessageItem { return item && item.title; } -function parseMessageArguments(first: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): { options: vscode.MessageOptions; items: (string | vscode.MessageItem)[]; } { - if (typeof first === 'string' || isMessageItem(first)) { - return { options: emptyMessageOptions, items: [first, ...rest] }; - } else { - return { options: first || emptyMessageOptions, items: rest }; - } -} - export class ExtHostMessageService { private _proxy: MainThreadMessageServiceShape; @@ -30,10 +21,20 @@ export class ExtHostMessageService { this._proxy = mainContext.get(MainContext.MainThreadMessageService); } - showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable; - showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable; - showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable { - const { options, items } = parseMessageArguments(optionsOrFirstItem, rest); + showMessage(extensionId: string, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable; + showMessage(extensionId: string, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable; + showMessage(extensionId: string, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable { + + let options: MainThreadMessageOptions = { extensionId }; + let items: (string | vscode.MessageItem)[]; + + if (typeof optionsOrFirstItem === 'string' || isMessageItem(optionsOrFirstItem)) { + items = [optionsOrFirstItem, ...rest]; + } else { + options.modal = optionsOrFirstItem.modal; + items = rest; + } + const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = []; for (let handle = 0; handle < items.length; handle++) { diff --git a/src/vs/workbench/services/message/browser/messageList.ts b/src/vs/workbench/services/message/browser/messageList.ts index c9e44de2a82..fd4190c867d 100644 --- a/src/vs/workbench/services/message/browser/messageList.ts +++ b/src/vs/workbench/services/message/browser/messageList.ts @@ -35,11 +35,13 @@ export enum Severity { export interface IMessageWithAction { message: string; actions: Action[]; + source: string; } interface IMessageEntry { id: any; text: string; + source: string; severity: Severity; time: number; count?: number; @@ -199,6 +201,7 @@ export class MessageList { severity: severity, time: Date.now(), actions: (id).actions, + source: (id).source, onHide }); @@ -339,7 +342,10 @@ export class MessageList { className: 'message-left-side', }); - $(messageContentElement as HTMLElement).title(messageContentElement.textContent).appendTo(div); + // Hover title + const title = message.source ? `[${message.source}] ${messageContentElement.textContent}` : messageContentElement.textContent; + + $(messageContentElement as HTMLElement).title(title).appendTo(div); }); }); } diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index 0876cbf5b90..37c944c5ce9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -14,7 +14,7 @@ suite('ExtHostMessageService', function () { test('propagte handle on select', function () { - let service = new MainThreadMessageService(null, { + let service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { assert.equal(m.actions.length, 1); setImmediate(() => m.actions[0].run()); @@ -34,7 +34,7 @@ suite('ExtHostMessageService', function () { test('isCloseAffordance', function () { let actions: Action[]; - let service = new MainThreadMessageService(null, { + let service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { actions = m.actions; } @@ -62,7 +62,7 @@ suite('ExtHostMessageService', function () { let actions: Action[]; let c: number; - let service = new MainThreadMessageService(null, { + let service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { c = 0; actions = m.actions; @@ -85,7 +85,7 @@ suite('ExtHostMessageService', function () { suite('modal', () => { test('calls choice service', () => { - const service = new MainThreadMessageService(null, { + const service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { throw new Error('not implemented'); } @@ -105,7 +105,7 @@ suite('ExtHostMessageService', function () { }); test('returns undefined when cancelled', () => { - const service = new MainThreadMessageService(null, { + const service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { throw new Error('not implemented'); } @@ -121,7 +121,7 @@ suite('ExtHostMessageService', function () { }); test('hides Cancel button when not needed', () => { - const service = new MainThreadMessageService(null, { + const service = new MainThreadMessageService(null, null, { show(sev: number, m: { message; actions: Action[] }) { throw new Error('not implemented'); } From 83ac7b4e03b561b3010e65d9a4eae3a4aeee30dd Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 08:34:35 -0700 Subject: [PATCH 61/96] Deploy to Azure quick link experiment --- .../platform/telemetry/common/experiments.ts | 61 ++++++++++++------- src/vs/workbench/electron-browser/shell.ts | 9 ++- .../electron-browser/vs_code_welcome_page.ts | 1 + .../page/electron-browser/welcomePage.ts | 8 +++ 4 files changed, 54 insertions(+), 25 deletions(-) diff --git a/src/vs/platform/telemetry/common/experiments.ts b/src/vs/platform/telemetry/common/experiments.ts index b338b4518db..36961817a48 100644 --- a/src/vs/platform/telemetry/common/experiments.ts +++ b/src/vs/platform/telemetry/common/experiments.ts @@ -6,31 +6,46 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -export interface ITelemetryExperiments { +export interface IExperiments { + deployToAzureQuickLink: boolean; } -const defaultExperiments: ITelemetryExperiments = { -}; +export const IExperimentService = createDecorator('experimentService'); -export function loadExperiments(accessor?: ServicesAccessor): ITelemetryExperiments { +export interface IExperimentService { - // shortcut since there are currently no experiments (should introduce separate service to load only once) - if (!accessor) { - return {}; + _serviceBrand: any; + + getExperiments(): IExperiments; +} + +export class ExperimentService implements IExperimentService { + + _serviceBrand: any; + + private experiments: IExperiments; + + constructor( + @IStorageService private storageService: IStorageService, + @IConfigurationService private configurationService: IConfigurationService, + ) { } + + getExperiments() { + if (!this.experiments) { + this.experiments = loadExperiments(this.storageService, this.configurationService); + } + return this.experiments; } - - const storageService = accessor.get(IStorageService); - const configurationService = accessor.get(IConfigurationService); - - let { - } = splitExperimentsRandomness(storageService); - - return applyOverrides(defaultExperiments, configurationService); } -function applyOverrides(experiments: ITelemetryExperiments, configurationService: IConfigurationService): ITelemetryExperiments { +function loadExperiments(storageService: IStorageService, configurationService: IConfigurationService): IExperiments { + const experiments = splitExperimentsRandomness(storageService); + return applyOverrides(experiments, configurationService); +} + +function applyOverrides(experiments: IExperiments, configurationService: IConfigurationService): IExperiments { const experimentsConfig = getExperimentsOverrides(configurationService); Object.keys(experiments).forEach(key => { if (key in experimentsConfig) { @@ -40,14 +55,14 @@ function applyOverrides(experiments: ITelemetryExperiments, configurationService return experiments; } -function splitExperimentsRandomness(storageService: IStorageService): ITelemetryExperiments { +function splitExperimentsRandomness(storageService: IStorageService): IExperiments { const random1 = getExperimentsRandomness(storageService); const [random2, /* showTaskDocumentation */] = splitRandom(random1); - const [random3, /* openUntitledFile */] = splitRandom(random2); - const [random4, /* mergeQuickLinks */] = splitRandom(random3); - // tslint:disable-next-line:no-unused-variable (https://github.com/Microsoft/TypeScript/issues/16628) - const [random5, /* enableWelcomePage */] = splitRandom(random4); + const [/* random3 */, deployToAzureQuickLink] = splitRandom(random2); + // const [random4, /* mergeQuickLinks */] = splitRandom(random3); + // const [random5, /* enableWelcomePage */] = splitRandom(random4); return { + deployToAzureQuickLink }; } @@ -68,7 +83,7 @@ function splitRandom(random: number): [number, boolean] { return [scaled - i, i === 1]; } -function getExperimentsOverrides(configurationService: IConfigurationService): ITelemetryExperiments { +function getExperimentsOverrides(configurationService: IConfigurationService): IExperiments { const config: any = configurationService.getConfiguration('telemetry'); return config && config.experiments || {}; } diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 0870ae8b268..593ee95af9c 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -23,7 +23,7 @@ import { ContextViewService } from 'vs/platform/contextview/browser/contextViewS import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, configurationTelemetry, lifecycleTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; -import { loadExperiments } from 'vs/platform/telemetry/common/experiments'; +import { IExperimentService, ExperimentService } from 'vs/platform/telemetry/common/experiments'; import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { IdleMonitor, UserStatus } from 'vs/platform/telemetry/browser/idleMonitor'; @@ -117,6 +117,7 @@ export class WorkbenchShell { private configurationService: IConfigurationService; private contextService: IWorkspaceContextService; private telemetryService: ITelemetryService; + private experimentService: IExperimentService; private extensionService: ExtensionService; private broadcastService: IBroadcastService; private timerService: ITimerService; @@ -208,7 +209,7 @@ export class WorkbenchShell { customKeybindingsCount: info.customKeybindingsCount, theme: this.themeService.getColorTheme().id, language: platform.language, - experiments: loadExperiments(), + experiments: this.experimentService.getExperiments(), pinnedViewlets: info.pinnedViewlets, restoredViewlet: info.restoredViewlet, restoredEditors: info.restoredEditors.length, @@ -263,6 +264,10 @@ export class WorkbenchShell { restoreFontInfo(this.storageService); readFontInfo(BareFontInfo.createFromRawSettings(this.configurationService.getConfiguration('editor'), browser.getZoomLevel())); + // Experiments + this.experimentService = instantiationService.createInstance(ExperimentService); + serviceCollection.set(IExperimentService, this.experimentService); + // Telemetry this.sendMachineIdToMain(this.storageService); if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !!product.enableTelemetry) { diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts index 926d92f29b3..ecd1e174fd9 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts @@ -69,6 +69,7 @@ export default () => `

  • +
diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index 7264d548e85..530d3e2012b 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -23,6 +23,7 @@ import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExperimentService } from 'vs/platform/telemetry/common/experiments'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -194,6 +195,7 @@ class WelcomePage { @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @ILifecycleService lifecycleService: ILifecycleService, @IThemeService private themeService: IThemeService, + @IExperimentService private experimentService: IExperimentService, @ITelemetryService private telemetryService: ITelemetryService ) { this.disposables.push(lifecycleService.onShutdown(() => this.dispose())); @@ -313,6 +315,12 @@ class WelcomePage { } }; })); + + if (this.experimentService.getExperiments().deployToAzureQuickLink) { + container.querySelector('.showInterfaceOverview').remove(); + } else { + container.querySelector('.deployToAzure').remove(); + } } private addExtensionList(container: HTMLElement, listSelector: string, suggestions: ExtensionSuggestion[], strings: Strings) { From 9e86799468aaad33c8d0b59eba79eac718a0dda4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Aug 2017 18:03:05 +0200 Subject: [PATCH 62/96] fix #32788 --- .../mainThreadLanguageFeatures.ts | 2 +- .../api/node/extHostLanguageFeatures.ts | 42 +++++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 17a6ee5926b..1638a01d7f2 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -265,7 +265,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise { this._registrations[handle] = modes.LinkProviderRegistry.register(selector, { provideLinks: (model, token) => { - return wireCancellationToken(token, this._proxy.$provideDocumentLinks(handle, model.uri)); + return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideDocumentLinks(handle, model.uri))); }, resolveLink: (link, token) => { return wireCancellationToken(token, this._proxy.$resolveDocumentLink(handle, link)); diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index fa223101e15..8e0c521684c 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -634,10 +634,12 @@ class SignatureHelpAdapter { class LinkProviderAdapter { private _documents: ExtHostDocuments; + private _heapService: ExtHostHeapService; private _provider: vscode.DocumentLinkProvider; - constructor(documents: ExtHostDocuments, provider: vscode.DocumentLinkProvider) { + constructor(documents: ExtHostDocuments, heapService: ExtHostHeapService, provider: vscode.DocumentLinkProvider) { this._documents = documents; + this._heapService = heapService; this._provider = provider; } @@ -645,23 +647,37 @@ class LinkProviderAdapter { const doc = this._documents.getDocumentData(resource).document; return asWinJsPromise(token => this._provider.provideDocumentLinks(doc, token)).then(links => { - if (Array.isArray(links)) { - return links.map(TypeConverters.DocumentLink.from); + if (!Array.isArray(links)) { + return undefined; } - return undefined; + const result: modes.ILink[] = []; + for (const link of links) { + let data = TypeConverters.DocumentLink.from(link); + let id = this._heapService.keep(link); + ObjectIdentifier.mixin(data, id); + result.push(data); + } + return result; }); } resolveLink(link: modes.ILink): TPromise { - if (typeof this._provider.resolveDocumentLink === 'function') { - return asWinJsPromise(token => this._provider.resolveDocumentLink(TypeConverters.DocumentLink.to(link), token)).then(value => { - if (value) { - return TypeConverters.DocumentLink.from(value); - } - return undefined; - }); + if (typeof this._provider.resolveDocumentLink !== 'function') { + return undefined; } - return undefined; + + const id = ObjectIdentifier.of(link); + const item = this._heapService.get(id); + if (!item) { + return undefined; + } + + return asWinJsPromise(token => this._provider.resolveDocumentLink(item, token)).then(value => { + if (value) { + return TypeConverters.DocumentLink.from(value); + } + return undefined; + }); } } @@ -993,7 +1009,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { const handle = this._nextHandle(); - this._adapter.set(handle, new LinkProviderAdapter(this._documents, provider)); + this._adapter.set(handle, new LinkProviderAdapter(this._documents, this._heapService, provider)); this._proxy.$registerDocumentLinkProvider(handle, selector); return this._createDisposable(handle); } From a3607bc2d246258b29dc9dc647eed18796537f0f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 18 Aug 2017 18:20:58 +0200 Subject: [PATCH 63/96] [html] add multiroot support. Fixes #32489 --- extensions/html/client/src/htmlMain.ts | 6 +- extensions/html/npm-shrinkwrap.json | 20 +++--- extensions/html/package.json | 23 +++++- extensions/html/server/npm-shrinkwrap.json | 33 ++++----- extensions/html/server/package.json | 9 +-- extensions/html/server/src/htmlServerMain.ts | 72 +++++++++++++------ extensions/html/server/src/modes/cssMode.ts | 6 +- .../html/server/src/modes/formatting.ts | 10 +-- extensions/html/server/src/modes/htmlMode.ts | 14 ++-- .../html/server/src/modes/javascriptMode.ts | 12 ++-- .../html/server/src/modes/languageModes.ts | 18 +++-- .../html/server/src/test/formatting.test.ts | 2 +- .../server/src/test/javascriptMode.test.ts | 2 +- 13 files changed, 140 insertions(+), 87 deletions(-) diff --git a/extensions/html/client/src/htmlMain.ts b/extensions/html/client/src/htmlMain.ts index 7f4c9fe3440..82a1d0ffc59 100644 --- a/extensions/html/client/src/htmlMain.ts +++ b/extensions/html/client/src/htmlMain.ts @@ -12,6 +12,8 @@ import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateColorDecorations } from './colorDecorators'; import TelemetryReporter from 'vscode-extension-telemetry'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; + import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); @@ -34,7 +36,7 @@ export function activate(context: ExtensionContext) { // The server is implemented in node let serverModule = context.asAbsolutePath(path.join('server', 'out', 'htmlServerMain.js')); // The debug options for the server - let debugOptions = { execArgv: ['--nolazy', '--debug=6004'] }; + let debugOptions = { execArgv: ['--nolazy', '--inspect=6004'] }; // If the extension is launch in debug mode the debug server options are use // Otherwise the run options are used @@ -59,6 +61,8 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), serverOptions, clientOptions); + client.registerFeature(new ConfigurationFeature(client)); + let disposable = client.start(); context.subscriptions.push(disposable); client.onReady().then(() => { diff --git a/extensions/html/npm-shrinkwrap.json b/extensions/html/npm-shrinkwrap.json index 7fee667332b..9dc0cf34176 100644 --- a/extensions/html/npm-shrinkwrap.json +++ b/extensions/html/npm-shrinkwrap.json @@ -8,24 +8,24 @@ "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" }, "vscode-extension-telemetry": { - "version": "0.0.7", + "version": "0.0.8", "from": "vscode-extension-telemetry@>=0.0.8 <0.0.9", "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" }, "vscode-jsonrpc": { - "version": "3.1.0-alpha.1", - "from": "vscode-jsonrpc@>=3.1.0-alpha.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.1.0-alpha.1.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageclient": { - "version": "3.1.0-alpha.1", + "version": "3.4.0-next.10", "from": "vscode-languageclient@next", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.1.0-alpha.1.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.10.tgz" }, "vscode-languageserver-types": { - "version": "3.0.3", - "from": "vscode-languageserver-types@>=3.0.2-beta.5 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.0.3.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", @@ -38,4 +38,4 @@ "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" } } -} \ No newline at end of file +} diff --git a/extensions/html/package.json b/extensions/html/package.json index f79ac361538..a06789f08a2 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -75,11 +75,13 @@ "properties": { "html.format.enable": { "type": "boolean", + "scope": "window", "default": true, "description": "%html.format.enable.desc%" }, "html.format.wrapLineLength": { "type": "integer", + "scope": "resource", "default": 120, "description": "%html.format.wrapLineLength.desc%" }, @@ -88,6 +90,7 @@ "string", "null" ], + "scope": "resource", "default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, select, small, span, strong, sub, sup, textarea, tt, var", "description": "%html.format.unformatted.desc%" }, @@ -96,16 +99,19 @@ "string", "null" ], + "scope": "resource", "default": "pre", "description": "%html.format.contentUnformatted.desc%" }, "html.format.indentInnerHtml": { "type": "boolean", + "scope": "resource", "default": false, "description": "%html.format.indentInnerHtml.desc%" }, "html.format.preserveNewLines": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.format.preserveNewLines.desc%" }, @@ -114,16 +120,19 @@ "number", "null" ], + "scope": "resource", "default": null, "description": "%html.format.maxPreserveNewLines.desc%" }, "html.format.indentHandlebars": { "type": "boolean", + "scope": "resource", "default": false, "description": "%html.format.indentHandlebars.desc%" }, "html.format.endWithNewline": { "type": "boolean", + "scope": "resource", "default": false, "description": "%html.format.endWithNewline.desc%" }, @@ -132,11 +141,13 @@ "string", "null" ], + "scope": "resource", "default": "head, body, /html", "description": "%html.format.extraLiners.desc%" }, "html.format.wrapAttributes": { "type": "string", + "scope": "resource", "default": "auto", "enum": [ "auto", @@ -154,31 +165,37 @@ }, "html.suggest.angular1": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.suggest.angular1.desc%" }, "html.suggest.ionic": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.suggest.ionic.desc%" }, "html.suggest.html5": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.suggest.html5.desc%" }, "html.validate.scripts": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.validate.scripts%" }, "html.validate.styles": { "type": "boolean", + "scope": "resource", "default": true, "description": "%html.validate.styles%" }, "html.trace.server": { "type": "string", + "scope": "window", "enum": [ "off", "messages", @@ -192,12 +209,12 @@ }, "dependencies": { "vscode-extension-telemetry": "0.0.8", - "vscode-languageclient": "3.1.0-alpha.1", - "vscode-languageserver-types": "3.0.3", + "vscode-languageclient": "3.4.0-next.10", + "vscode-languageserver-types": "^3.3.0", "vscode-nls": "2.0.2" }, "devDependencies": { "@types/node": "^6.0.51", "@types/mocha": "^2.2.33" } -} \ No newline at end of file +} diff --git a/extensions/html/server/npm-shrinkwrap.json b/extensions/html/server/npm-shrinkwrap.json index ee83f51e71e..8853dfc48b4 100644 --- a/extensions/html/server/npm-shrinkwrap.json +++ b/extensions/html/server/npm-shrinkwrap.json @@ -3,16 +3,9 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.0", + "version": "2.1.3", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.0.tgz", - "dependencies": { - "vscode-languageserver-types": { - "version": "3.2.0", - "from": "vscode-languageserver-types@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.2.0.tgz" - } - } + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.3.tgz" }, "vscode-html-languageservice": { "version": "2.0.5", @@ -20,19 +13,19 @@ "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.5.tgz" }, "vscode-jsonrpc": { - "version": "3.1.0-alpha.1", - "from": "vscode-jsonrpc@>=3.1.0-alpha.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.1.0-alpha.1.tgz" + "version": "3.3.1", + "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageserver": { - "version": "3.1.0-alpha.1", + "version": "3.4.0-next.4", "from": "vscode-languageserver@next", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.1.0-alpha.1.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.4.tgz" }, "vscode-languageserver-types": { - "version": "3.0.3", - "from": "vscode-languageserver-types@>=3.0.3 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.0.3.tgz" + "version": "3.3.0", + "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" }, "vscode-nls": { "version": "2.0.2", @@ -40,9 +33,9 @@ "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" }, "vscode-uri": { - "version": "1.0.0", - "from": "vscode-uri@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.0.tgz" + "version": "1.0.1", + "from": "vscode-uri@latest", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.1.tgz" } } } diff --git a/extensions/html/server/package.json b/extensions/html/server/package.json index 5284d547143..3fe6263139d 100644 --- a/extensions/html/server/package.json +++ b/extensions/html/server/package.json @@ -8,11 +8,12 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.0", + "vscode-css-languageservice": "^2.1.3", "vscode-html-languageservice": "^2.0.5", - "vscode-languageserver": "^3.1.0-alpha.1", + "vscode-languageserver": "3.4.0-next.4", + "vscode-languageserver-types": "^3.3.0", "vscode-nls": "^2.0.2", - "vscode-uri": "^1.0.0" + "vscode-uri": "^1.0.1" }, "devDependencies": { "@types/node": "^6.0.51", @@ -25,6 +26,6 @@ "install-service-local": "npm install ../../../../vscode-css-languageservice -f -S && npm install ../../../../vscode-html-languageservice -f -S", "install-server-next": "npm install vscode-languageserver@next -f -S", "install-server-local": "npm install ../../../../vscode-languageserver-node/server -f -S", - "test": "mocha" + "test": "../../../node_modules/.bin/mocha" } } diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 0ea5b627424..c29012bd8b1 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -4,10 +4,12 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector } from 'vscode-languageserver'; +import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector, GetConfigurationParams } from 'vscode-languageserver'; import { DocumentContext } from 'vscode-html-languageservice'; import { TextDocument, Diagnostic, DocumentLink, Range, SymbolInformation } from 'vscode-languageserver-types'; -import { getLanguageModes, LanguageModes } from './modes/languageModes'; +import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes'; + +import { GetConfigurationRequest } from 'vscode-languageserver/lib/protocol.proposed'; import { format } from './modes/formatting'; import { pushAll } from './utils/arrays'; @@ -38,10 +40,27 @@ documents.listen(connection); let workspacePath: string; var languageModes: LanguageModes; -var settings: any = {}; let clientSnippetSupport = false; let clientDynamicRegisterSupport = false; +let scopedSettingsSupport = false; + +var globalSettings: Settings = {}; +let documentSettings: { [key: string]: Thenable } = {}; + +function getDocumentSettings(textDocument: TextDocument, needsDocumentSettings: () => boolean): Thenable { + if (scopedSettingsSupport && needsDocumentSettings()) { + let promise = documentSettings[textDocument.uri]; + if (!promise) { + let scopeUri = textDocument.uri; + let configRequestParam: GetConfigurationParams = { items: [{ scopeUri, section: 'css' }, { scopeUri, section: 'html' }, { scopeUri, section: 'javascript' }] }; + promise = connection.sendRequest(GetConfigurationRequest.type, configRequestParam).then(s => ({ css: s[0], html: s[1], javascript: s[2] })); + documentSettings[textDocument.uri] = promise; + } + return promise; + } + return Promise.resolve(void 0); +} // After the server has started the client sends an initilize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilites @@ -68,6 +87,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport'); clientDynamicRegisterSupport = hasClientCapability('workspace', 'symbol', 'dynamicRegistration'); + scopedSettingsSupport = hasClientCapability('workspace', 'configuration'); return { capabilities: { // Tell the client that the server works in FULL text document sync mode @@ -85,21 +105,13 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { }; }); -let validation = { - html: true, - css: true, - javascript: true -}; - let formatterRegistration: Thenable = null; // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { - settings = change.settings; - let validationSettings = settings && settings.html && settings.html.validate || {}; - validation.css = validationSettings.styles !== false; - validation.javascript = validationSettings.scripts !== false; + globalSettings = change.settings; + documentSettings = {}; // reset all document settings languageModes.getAllModes().forEach(m => { if (m.configure) { m.configure(change.settings); @@ -109,7 +121,7 @@ connection.onDidChangeConfiguration((change) => { // dynamically enable & disable the formatter if (clientDynamicRegisterSupport) { - let enableFormatter = settings && settings.html && settings.html.format && settings.html.format.enable; + let enableFormatter = globalSettings && globalSettings.html && globalSettings.html.format && globalSettings.html.format.enable; if (enableFormatter) { if (!formatterRegistration) { let documentSelector: DocumentSelector = [{ language: 'html' }, { language: 'handlebars' }]; // don't register razor, the formatter does more harm than good @@ -154,26 +166,37 @@ function triggerValidation(textDocument: TextDocument): void { }, validationDelayMs); } -function validateTextDocument(textDocument: TextDocument): void { +function isValidationEnabled(languageId: string, settings: Settings = globalSettings) { + let validationSettings = settings && settings.html && settings.html.validate; + if (validationSettings) { + return languageId === 'css' && validationSettings.styles !== false || languageId === 'javascript' && validationSettings.scripts !== false; + } + return true; +} + +async function validateTextDocument(textDocument: TextDocument) { let diagnostics: Diagnostic[] = []; if (textDocument.languageId === 'html') { - languageModes.getAllModesInDocument(textDocument).forEach(mode => { - if (mode.doValidation && validation[mode.getId()]) { - pushAll(diagnostics, mode.doValidation(textDocument)); + let modes = languageModes.getAllModesInDocument(textDocument); + let settings = await getDocumentSettings(textDocument, () => modes.some(m => m.doValidation && m.doValidation.length > 1)); + modes.forEach(mode => { + if (mode.doValidation && isValidationEnabled(mode.getId(), settings)) { + pushAll(diagnostics, mode.doValidation(textDocument, settings)); } }); } connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } -connection.onCompletion(textDocumentPosition => { +connection.onCompletion(async textDocumentPosition => { let document = documents.get(textDocumentPosition.textDocument.uri); let mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); if (mode && mode.doComplete) { if (mode.getId() !== 'html') { connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } }); } - return mode.doComplete(document, textDocumentPosition.position); + let settings = await getDocumentSettings(document, () => mode.doComplete.length > 2); + return mode.doComplete(document, textDocumentPosition.position, settings); } return { isIncomplete: true, items: [] }; }); @@ -235,13 +258,16 @@ connection.onSignatureHelp(signatureHelpParms => { return null; }); -connection.onDocumentRangeFormatting(formatParams => { +connection.onDocumentRangeFormatting(async formatParams => { let document = documents.get(formatParams.textDocument.uri); - + let settings = await getDocumentSettings(document, () => true); + if (!settings) { + settings = globalSettings; + } let unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || ''; let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) }; - return format(languageModes, document, formatParams.range, formatParams.options, enabledModes); + return format(languageModes, document, formatParams.range, formatParams.options, settings, enabledModes); }); connection.onDocumentLinks(documentLinkParam => { diff --git a/extensions/html/server/src/modes/cssMode.ts b/extensions/html/server/src/modes/cssMode.ts index b813c59f572..bcaa48391d9 100644 --- a/extensions/html/server/src/modes/cssMode.ts +++ b/extensions/html/server/src/modes/cssMode.ts @@ -7,7 +7,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { TextDocument, Position } from 'vscode-languageserver-types'; import { getCSSLanguageService, Stylesheet } from 'vscode-css-languageservice'; -import { LanguageMode } from './languageModes'; +import { LanguageMode, Settings } from './languageModes'; import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport'; export function getCSSMode(documentRegions: LanguageModelCache): LanguageMode { @@ -22,9 +22,9 @@ export function getCSSMode(documentRegions: LanguageModelCache(10, 60, document => htmlLanguageService.parseHTMLDocument(document)); return { getId() { return 'html'; }, configure(options: any) { - settings = options && options.html; + globalSettings = options; }, - doComplete(document: TextDocument, position: Position) { - let options = settings && settings.suggest; + doComplete(document: TextDocument, position: Position, settings: Settings = globalSettings) { + let options = settings && settings.html && settings.html.suggest; return htmlLanguageService.doComplete(document, position, htmlDocuments.get(document), options); }, doHover(document: TextDocument, position: Position) { @@ -35,8 +35,8 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService): LanguageM findDocumentSymbols(document: TextDocument) { return htmlLanguageService.findDocumentSymbols(document, htmlDocuments.get(document)); }, - format(document: TextDocument, range: Range, formatParams: FormattingOptions) { - let formatSettings = settings && settings.format; + format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings) { + let formatSettings = settings && settings.html && settings.html.format; if (!formatSettings) { formatSettings = formatParams; } else { diff --git a/extensions/html/server/src/modes/javascriptMode.ts b/extensions/html/server/src/modes/javascriptMode.ts index b9cd9075b20..128969d0140 100644 --- a/extensions/html/server/src/modes/javascriptMode.ts +++ b/extensions/html/server/src/modes/javascriptMode.ts @@ -6,7 +6,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types'; -import { LanguageMode } from './languageModes'; +import { LanguageMode, Settings } from './languageModes'; import { getWordAtText, startsWith, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; @@ -60,14 +60,14 @@ export function getJavascriptMode(documentRegions: LanguageModelCache; +} + export interface LanguageMode { getId(); - configure?: (options: any) => void; - doValidation?: (document: TextDocument) => Diagnostic[]; - doComplete?: (document: TextDocument, position: Position) => CompletionList; + configure?: (options: Settings) => void; + doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[]; + doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList; doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; doHover?: (document: TextDocument, position: Position) => Hover; doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp; @@ -29,7 +39,7 @@ export interface LanguageMode { findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => DocumentLink[]; findDefinition?: (document: TextDocument, position: Position) => Definition; findReferences?: (document: TextDocument, position: Position) => Location[]; - format?: (document: TextDocument, range: Range, options: FormattingOptions) => TextEdit[]; + format?: (document: TextDocument, range: Range, options: FormattingOptions, settings: Settings) => TextEdit[]; findColorSymbols?: (document: TextDocument) => Range[]; onDocumentRemoved(document: TextDocument): void; dispose(): void; diff --git a/extensions/html/server/src/test/formatting.test.ts b/extensions/html/server/src/test/formatting.test.ts index ea4ecf33153..713d2943ed4 100644 --- a/extensions/html/server/src/test/formatting.test.ts +++ b/extensions/html/server/src/test/formatting.test.ts @@ -38,7 +38,7 @@ suite('HTML Embedded Formatting', () => { formatOptions = FormattingOptions.create(2, true); } - let result = format(languageModes, document, range, formatOptions, { css: true, javascript: true }); + let result = format(languageModes, document, range, formatOptions, void 0, { css: true, javascript: true }); let actual = applyEdits(document, result); assert.equal(actual, expected, message); diff --git a/extensions/html/server/src/test/javascriptMode.test.ts b/extensions/html/server/src/test/javascriptMode.test.ts index 8b231644ee5..c5d44e88cae 100644 --- a/extensions/html/server/src/test/javascriptMode.test.ts +++ b/extensions/html/server/src/test/javascriptMode.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { getJavascriptMode } from '../modes/javascriptMode'; -import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types'; +import { TextDocument } from 'vscode-languageserver-types'; import { getLanguageModelCache } from '../languageModelCache'; import { getLanguageService } from 'vscode-html-languageservice'; From 1ba52e2113d3e4b8e531357879ba95d66bca229a Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 18 Aug 2017 18:28:02 +0200 Subject: [PATCH 64/96] [css] update language server & client --- extensions/css/client/src/cssMain.ts | 4 ++-- extensions/css/npm-shrinkwrap.json | 4 ++-- extensions/css/package.json | 4 ++-- extensions/css/server/npm-shrinkwrap.json | 4 ++-- extensions/css/server/package.json | 2 +- extensions/css/server/src/cssServerMain.ts | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/extensions/css/client/src/cssMain.ts b/extensions/css/client/src/cssMain.ts index a9da1608c47..a13dabac0b9 100644 --- a/extensions/css/client/src/cssMain.ts +++ b/extensions/css/client/src/cssMain.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import { languages, window, commands, workspace, ExtensionContext } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, Range, TextEdit } from 'vscode-languageclient'; import { activateColorDecorations, ColorProvider } from './colorDecorators'; -import { GetConfigurationFeature } from 'vscode-languageclient/lib/proposed'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); @@ -45,7 +45,7 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions); - client.registerFeature(new GetConfigurationFeature(client)); + client.registerFeature(new ConfigurationFeature(client)); let disposable = client.start(); // Push the disposable to the context's subscriptions so that the diff --git a/extensions/css/npm-shrinkwrap.json b/extensions/css/npm-shrinkwrap.json index b09c91da50a..6a497e0b691 100644 --- a/extensions/css/npm-shrinkwrap.json +++ b/extensions/css/npm-shrinkwrap.json @@ -18,9 +18,9 @@ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageclient": { - "version": "3.4.0-next.4", + "version": "3.4.0-next.10", "from": "vscode-languageclient@next", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.4.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.10.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/css/package.json b/extensions/css/package.json index 7327b530c1e..49b8f98ba2f 100644 --- a/extensions/css/package.json +++ b/extensions/css/package.json @@ -735,10 +735,10 @@ }, "dependencies": { "parse-color": "^1.0.0", - "vscode-languageclient": "^3.4.0-next.4", + "vscode-languageclient": "3.4.0-next.10", "vscode-nls": "^2.0.2" }, "devDependencies": { "@types/node": "^6.0.51" } -} \ No newline at end of file +} diff --git a/extensions/css/server/npm-shrinkwrap.json b/extensions/css/server/npm-shrinkwrap.json index 01251cf3191..63bed9e999b 100644 --- a/extensions/css/server/npm-shrinkwrap.json +++ b/extensions/css/server/npm-shrinkwrap.json @@ -13,9 +13,9 @@ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" }, "vscode-languageserver": { - "version": "3.4.0-next.2", + "version": "3.4.0-next.4", "from": "vscode-languageserver@next", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.2.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.4.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/css/server/package.json b/extensions/css/server/package.json index 9b96f1450c6..d8112cc044a 100644 --- a/extensions/css/server/package.json +++ b/extensions/css/server/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "vscode-css-languageservice": "^2.1.3", - "vscode-languageserver": "^3.4.0-next.2" + "vscode-languageserver": "3.4.0-next.4" }, "devDependencies": { "@types/node": "^6.0.51" diff --git a/extensions/css/server/src/cssServerMain.ts b/extensions/css/server/src/cssServerMain.ts index 9b86bdaac5d..4b5c4e4ad2c 100644 --- a/extensions/css/server/src/cssServerMain.ts +++ b/extensions/css/server/src/cssServerMain.ts @@ -57,7 +57,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { return !!c; } let snippetSupport = hasClientCapability('textDocument.completion.completionItem.snippetSupport'); - scopedSettingsSupport = hasClientCapability('workspace.getConfiguration'); + scopedSettingsSupport = hasClientCapability('workspace.configuration'); return { capabilities: { // Tell the client that the server works in FULL text document sync mode @@ -94,8 +94,8 @@ function getDocumentSettings(textDocument: TextDocument): Thenable s[0][0]); + let configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; + promise = connection.sendRequest(GetConfigurationRequest.type, configRequestParam).then(s => s[0]); documentSettings[textDocument.uri] = promise; } return promise; From ace0793755d625af8ecf83b06e60906a9f47d4fd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 18 Aug 2017 10:07:24 -0700 Subject: [PATCH 65/96] Have terminal env config apply case insensitively Fixes #32787 --- .../electron-browser/terminalInstance.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 3b2a163b332..ddbb38a3859 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -560,8 +560,23 @@ export class TerminalInstance implements ITerminalInstance { this._configHelper.mergeDefaultShellPathAndArgs(shell); } this._initialCwd = this._getCwd(this._shellLaunchConfig, this._historyService.getLastActiveWorkspaceRoot()); - const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux'; - const envFromConfig = { ...process.env, ...this._configHelper.config.env[platformKey] }; + let envFromConfig: IStringDictionary; + if (platform.isWindows) { + envFromConfig = { ...process.env }; + for (let configKey in this._configHelper.config.env['windows']) { + let actualKey = configKey; + for (let envKey in envFromConfig) { + if (configKey.toLowerCase() === envKey.toLowerCase()) { + actualKey = envKey; + break; + } + } + envFromConfig[actualKey] = this._configHelper.config.env['windows'][configKey]; + } + } else { + const platformKey = platform.isMacintosh ? 'osx' : 'linux'; + envFromConfig = { ...process.env, ...this._configHelper.config.env[platformKey] }; + } const env = TerminalInstance.createTerminalEnv(envFromConfig, shell, this._initialCwd, locale, this._cols, this._rows); this._process = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], { env, From 6ebd0fe0e97f1f4480f9a717a0419c637765cbbc Mon Sep 17 00:00:00 2001 From: t-amqi Date: Fri, 18 Aug 2017 12:55:26 -0700 Subject: [PATCH 66/96] Fix quick navigate arrow keys --- src/vs/base/parts/quickopen/browser/quickOpenWidget.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index ac08aeef4e5..63d07bf6c35 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -340,6 +340,10 @@ export class QuickOpenWidget implements IModelProvider { // Allows focus to switch to next/previous entry after tab into an actionbar item DOM.addDisposableListener(this.treeContainer.getHTMLElement(), DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); + // Only handle when not in quick navigation mode + if (this.quickNavigateConfiguration) { + return; + } if (keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { DOM.EventHelper.stop(e, true); this.navigateInTree(keyboardEvent.keyCode, keyboardEvent.shiftKey); From c0bb2f5eac63b9850953b5f36839fe8e0f632770 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 18 Aug 2017 22:45:21 +0200 Subject: [PATCH 67/96] node-debug@1.16.5 --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 04ecea16c26..2a8db450907 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -43,7 +43,7 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.16.4' }, + { name: 'ms-vscode.node-debug', version: '1.16.5' }, { name: 'ms-vscode.node-debug2', version: '1.16.1' } ]; From 26644f02fadf8dc0161d058bce158b312c82c1cb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 18 Aug 2017 13:58:40 -0700 Subject: [PATCH 68/96] Fix duplicate tsc tasks on windows Fixes #31977 --- extensions/typescript/src/features/taskProvider.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/typescript/src/features/taskProvider.ts b/extensions/typescript/src/features/taskProvider.ts index 9ece0fc2119..aea776a38fc 100644 --- a/extensions/typescript/src/features/taskProvider.ts +++ b/extensions/typescript/src/features/taskProvider.ts @@ -78,10 +78,10 @@ class TscTaskProvider implements vscode.TaskProvider { const editor = vscode.window.activeTextEditor; if (editor) { if (path.basename(editor.document.fileName).match(/^tsconfig\.(.\.)?json$/)) { - const path = editor.document.uri; + const uri = editor.document.uri; return [{ - path: path.fsPath, - workspaceFolder: vscode.workspace.getWorkspaceFolder(path) + path: uri.fsPath, + workspaceFolder: vscode.workspace.getWorkspaceFolder(uri) }]; } } @@ -103,8 +103,8 @@ class TscTaskProvider implements vscode.TaskProvider { const { configFileName } = res.body; if (configFileName && !isImplicitProjectConfigFile(configFileName)) { - const path = vscode.Uri.file(configFileName); - const folder = vscode.workspace.getWorkspaceFolder(path); + const uri = vscode.Uri.file(path.normalize(configFileName)); + const folder = vscode.workspace.getWorkspaceFolder(uri); return [{ path: configFileName, workspaceFolder: folder From a6e46b1b344c29a72795284e78915b5e6f6894cd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 18 Aug 2017 14:05:40 -0700 Subject: [PATCH 69/96] Make sure we use the normalized path in the task provider in all locations --- extensions/typescript/src/features/taskProvider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/typescript/src/features/taskProvider.ts b/extensions/typescript/src/features/taskProvider.ts index aea776a38fc..4eb9a3e4e43 100644 --- a/extensions/typescript/src/features/taskProvider.ts +++ b/extensions/typescript/src/features/taskProvider.ts @@ -103,10 +103,11 @@ class TscTaskProvider implements vscode.TaskProvider { const { configFileName } = res.body; if (configFileName && !isImplicitProjectConfigFile(configFileName)) { - const uri = vscode.Uri.file(path.normalize(configFileName)); + const normalizedConfigPath = path.normalize(configFileName); + const uri = vscode.Uri.file(normalizedConfigPath); const folder = vscode.workspace.getWorkspaceFolder(uri); return [{ - path: configFileName, + path: normalizedConfigPath, workspaceFolder: folder }]; } From 59d5ce9f31992f22132ae8362b00bdd8a0de2e1e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 18 Aug 2017 14:42:16 -0700 Subject: [PATCH 70/96] Make language identifier case insensitive for markdown code blocks Fixes #32805 --- extensions/markdown/syntaxes/gulpfile.js | 2 +- .../markdown/syntaxes/markdown.tmLanguage | 88 +++++++++---------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/extensions/markdown/syntaxes/gulpfile.js b/extensions/markdown/syntaxes/gulpfile.js index 6bd6851cb01..e04c73fe1d1 100644 --- a/extensions/markdown/syntaxes/gulpfile.js +++ b/extensions/markdown/syntaxes/gulpfile.js @@ -68,7 +68,7 @@ const fencedCodeBlockDefinition = (name, identifiers, sourceScope) => { return `fenced_code_block_${name} begin - (^|\\G)(\\s*)(\`{3,}|~{3,})\\s*((${identifiers.join('|')})(\\s+[^\`~]*)?$) + (^|\\G)(\\s*)(\`{3,}|~{3,})\\s*(?i:(${identifiers.join('|')})(\\s+[^\`~]*)?$) name markup.fenced_code.block.markdown end diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage index ede4f2a2d18..552aec21007 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -588,7 +588,7 @@ fenced_code_block_css begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((css|css.erb)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(css|css.erb)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -639,7 +639,7 @@ fenced_code_block_basic begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((html|htm|shtml|xhtml|inc|tmpl|tpl)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(html|htm|shtml|xhtml|inc|tmpl|tpl)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -690,7 +690,7 @@ fenced_code_block_ini begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((ini|conf)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(ini|conf)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -741,7 +741,7 @@ fenced_code_block_java begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((java|bsh)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(java|bsh)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -792,7 +792,7 @@ fenced_code_block_lua begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((lua)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(lua)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -843,7 +843,7 @@ fenced_code_block_makefile begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((Makefile|makefile|GNUmakefile|OCamlMakefile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(Makefile|makefile|GNUmakefile|OCamlMakefile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -894,7 +894,7 @@ fenced_code_block_perl begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((perl|pl|pm|pod|t|PL|psgi|vcl)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(perl|pl|pm|pod|t|PL|psgi|vcl)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -945,7 +945,7 @@ fenced_code_block_r begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((R|r|s|S|Rprofile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(R|r|s|S|Rprofile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -996,7 +996,7 @@ fenced_code_block_ruby begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1047,7 +1047,7 @@ fenced_code_block_php begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((php|php3|php4|php5|phpt|phtml|aw|ctp)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(php|php3|php4|php5|phpt|phtml|aw|ctp)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1102,7 +1102,7 @@ fenced_code_block_sql begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((sql|ddl|dml)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(sql|ddl|dml)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1153,7 +1153,7 @@ fenced_code_block_vs_net begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((vb)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(vb)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1204,7 +1204,7 @@ fenced_code_block_xml begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1255,7 +1255,7 @@ fenced_code_block_xsl begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((xsl|xslt)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(xsl|xslt)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1306,7 +1306,7 @@ fenced_code_block_yaml begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((yaml|yml)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(yaml|yml)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1357,7 +1357,7 @@ fenced_code_block_dosbatch begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((bat|batch)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(bat|batch)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1408,7 +1408,7 @@ fenced_code_block_clojure begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((clj|cljs|clojure)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(clj|cljs|clojure)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1459,7 +1459,7 @@ fenced_code_block_coffee begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((coffee|Cakefile|coffee.erb)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(coffee|Cakefile|coffee.erb)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1510,7 +1510,7 @@ fenced_code_block_c begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((c|h)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(c|h)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1561,7 +1561,7 @@ fenced_code_block_cpp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((cpp|c\+\+|cxx)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(cpp|c\+\+|cxx)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1612,7 +1612,7 @@ fenced_code_block_diff begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((patch|diff|rej)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(patch|diff|rej)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1663,7 +1663,7 @@ fenced_code_block_dockerfile begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((dockerfile|Dockerfile)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(dockerfile|Dockerfile)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1714,7 +1714,7 @@ fenced_code_block_git_commit begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((COMMIT_EDITMSG|MERGE_MSG)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(COMMIT_EDITMSG|MERGE_MSG)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1765,7 +1765,7 @@ fenced_code_block_git_rebase begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((git-rebase-todo)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(git-rebase-todo)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1816,7 +1816,7 @@ fenced_code_block_go begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((go|golang)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(go|golang)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1867,7 +1867,7 @@ fenced_code_block_groovy begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((groovy|gvy)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(groovy|gvy)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1918,7 +1918,7 @@ fenced_code_block_jade begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((jade|pug)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(jade|pug)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -1969,7 +1969,7 @@ fenced_code_block_js begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((js|jsx|javascript|es6|mjs)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(js|jsx|javascript|es6|mjs)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2020,7 +2020,7 @@ fenced_code_block_js_regexp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((regexp)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(regexp)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2071,7 +2071,7 @@ fenced_code_block_json begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((json|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(json|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2122,7 +2122,7 @@ fenced_code_block_less begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((less)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(less)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2173,7 +2173,7 @@ fenced_code_block_objc begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((objectivec|objective-c|mm|objc|obj-c|m|h)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(objectivec|objective-c|mm|objc|obj-c|m|h)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2224,7 +2224,7 @@ fenced_code_block_scss begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((scss)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(scss)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2275,7 +2275,7 @@ fenced_code_block_perl6 begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((perl6|p6|pl6|pm6|nqp)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(perl6|p6|pl6|pm6|nqp)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2326,7 +2326,7 @@ fenced_code_block_powershell begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((powershell|ps1|psm1|psd1)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(powershell|ps1|psm1|psd1)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2377,7 +2377,7 @@ fenced_code_block_python begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2428,7 +2428,7 @@ fenced_code_block_regexp_python begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((re)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(re)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2479,7 +2479,7 @@ fenced_code_block_rust begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((rust|rs)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(rust|rs)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2530,7 +2530,7 @@ fenced_code_block_scala begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((scala|sbt)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(scala|sbt)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2581,7 +2581,7 @@ fenced_code_block_shell begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2632,7 +2632,7 @@ fenced_code_block_ts begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((typescript|ts)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(typescript|ts)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2683,7 +2683,7 @@ fenced_code_block_tsx begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((tsx)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(tsx)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2734,7 +2734,7 @@ fenced_code_block_csharp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((cs|csharp|c#)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(cs|csharp|c#)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end @@ -2785,7 +2785,7 @@ fenced_code_block_fsharp begin - (^|\G)(\s*)(`{3,}|~{3,})\s*((fs|fsharp|f#)(\s+[^`~]*)?$) + (^|\G)(\s*)(`{3,}|~{3,})\s*(?i:(fs|fsharp|f#)(\s+[^`~]*)?$) name markup.fenced_code.block.markdown end From 74fde6b00cbf4f2aa796d98a077d52656ade4856 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 18 Aug 2017 14:55:51 -0700 Subject: [PATCH 71/96] Fix template expression coloration not being reset in tsx files Fixes #32773 --- extensions/theme-defaults/themes/dark_vs.json | 9 +++------ .../theme-defaults/themes/hc_black_defaults.json | 10 +++------- extensions/theme-defaults/themes/light_vs.json | 9 +++------ 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 02e12514b26..4dd90c71801 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -243,10 +243,8 @@ { "name": "JavaScript string interpolation ${}", "scope": [ - "punctuation.definition.template-expression.begin.js", - "punctuation.definition.template-expression.begin.ts", - "punctuation.definition.template-expression.end.ts", - "punctuation.definition.template-expression.end.js", + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", "punctuation.section.embedded.coffee" ], "settings": { @@ -256,8 +254,7 @@ { "name": "Reset JavaScript string interpolation expression", "scope": [ - "meta.template.expression.js", - "meta.template.expression.ts" + "meta.template.expression" ], "settings": { "foreground": "#d4d4d4" diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index fe0b672d312..986fa7cf615 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -32,7 +32,6 @@ "foreground": "#000080" } }, - { "scope": "comment", "settings": { @@ -230,10 +229,8 @@ { "name": "JavaScript string interpolation ${}", "scope": [ - "punctuation.definition.template-expression.begin.js", - "punctuation.definition.template-expression.begin.ts", - "punctuation.definition.template-expression.end.ts", - "punctuation.definition.template-expression.end.js", + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", "punctuation.section.embedded.coffee" ], "settings": { @@ -243,8 +240,7 @@ { "name": "Reset JavaScript string interpolation expression", "scope": [ - "meta.template.expression.js", - "meta.template.expression.ts" + "meta.template.expression" ], "settings": { "foreground": "#ffffff" diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index a1d9f797f64..162c32f6218 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -239,10 +239,8 @@ { "name": "JavaScript string interpolation ${}", "scope": [ - "punctuation.definition.template-expression.begin.js", - "punctuation.definition.template-expression.begin.ts", - "punctuation.definition.template-expression.end.ts", - "punctuation.definition.template-expression.end.js", + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", "punctuation.section.embedded.coffee" ], "settings": { @@ -252,8 +250,7 @@ { "name": "Reset JavaScript string interpolation expression", "scope": [ - "meta.template.expression.js", - "meta.template.expression.ts" + "meta.template.expression" ], "settings": { "foreground": "#000000" From b29bd1e6563b6981e5db9ba0764697be3c334078 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 15:06:10 -0700 Subject: [PATCH 72/96] Use #welcome --- .../parts/welcome/page/electron-browser/vs_code_welcome_page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts index ecd1e174fd9..0ad49659764 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts @@ -69,7 +69,7 @@ export default () => `
  • -
  • +
From abea60daf8257659acbdd890f132bbd215369099 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 15:31:18 -0700 Subject: [PATCH 73/96] Remove proposed credentials API (#31131) --- src/vs/vscode.proposed.d.ts | 34 ------------------- src/vs/workbench/api/node/extHost.api.impl.ts | 7 ++-- 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index d237dad287e..ab976f0f57b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -55,40 +55,6 @@ declare module 'vscode' { export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; } - /** - * Namespace for handling credentials. - */ - export namespace credentials { - - /** - * Read a previously stored secret from the credential store. - * - * @param service The service of the credential. - * @param account The account of the credential. - * @return A promise for the secret of the credential. - */ - export function readSecret(service: string, account: string): Thenable; - - /** - * Write a secret to the credential store. - * - * @param service The service of the credential. - * @param account The account of the credential. - * @param secret The secret of the credential to write to the credential store. - * @return A promise indicating completion of the operation. - */ - export function writeSecret(service: string, account: string, secret: string): Thenable; - - /** - * Delete a previously stored secret from the credential store. - * - * @param service The service of the credential. - * @param account The account of the credential. - * @return A promise resolving to true if there was a secret for that service and account. - */ - export function deleteSecret(service: string, account: string): Thenable; - } - /** * Represents a color in RGBA space. */ diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 6c71b094f8e..c410c99b202 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -506,7 +506,7 @@ export function createApiFactory( }; // namespace: credentials - const credentials: typeof vscode.credentials = { + const credentials = { readSecret(service: string, account: string): Thenable { return extHostCredentials.readSecret(service, account); }, @@ -530,7 +530,6 @@ export function createApiFactory( workspace, scm, debug, - credentials, // types CancellationTokenSource: CancellationTokenSource, CodeLens: extHostTypes.CodeLens, @@ -584,8 +583,8 @@ export function createApiFactory( Task: extHostTypes.Task, ConfigurationTarget: extHostTypes.ConfigurationTarget }; - if (!extension.enableProposedApi) { - delete api.credentials; // Instead of error to avoid #31854 + if (extension.enableProposedApi && extension.isBuiltin) { + api['credentials'] = credentials; } return api; }; From b445cded3489a973bdd388cc14db7cccef7e2b4a Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 14:47:45 -0700 Subject: [PATCH 74/96] Include azure-account extension --- build/npm/postinstall.js | 1 + extensions/azure-account/.vscodeignore | 4 + extensions/azure-account/package.json | 70 +++ extensions/azure-account/package.nls.json | 8 + extensions/azure-account/src/azure-account.ts | 471 ++++++++++++++++++ extensions/azure-account/src/extension.ts | 53 ++ .../src/typings/azure-account.api.d.ts | 34 ++ extensions/azure-account/src/typings/ref.d.ts | 6 + .../src/typings/vscode.rejected.d.ts | 43 ++ extensions/azure-account/tsconfig.json | 19 + 10 files changed, 709 insertions(+) create mode 100644 extensions/azure-account/.vscodeignore create mode 100644 extensions/azure-account/package.json create mode 100644 extensions/azure-account/package.nls.json create mode 100644 extensions/azure-account/src/azure-account.ts create mode 100644 extensions/azure-account/src/extension.ts create mode 100644 extensions/azure-account/src/typings/azure-account.api.d.ts create mode 100644 extensions/azure-account/src/typings/ref.d.ts create mode 100644 extensions/azure-account/src/typings/vscode.rejected.d.ts create mode 100644 extensions/azure-account/tsconfig.json diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 7b156212bec..95205aa1fe1 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -24,6 +24,7 @@ npmInstall('extensions'); // node modules shared by all extensions const extensions = [ 'vscode-api-tests', 'vscode-colorize-tests', + 'azure-account', 'json', 'configuration-editing', 'extension-editing', diff --git a/extensions/azure-account/.vscodeignore b/extensions/azure-account/.vscodeignore new file mode 100644 index 00000000000..24428a6f758 --- /dev/null +++ b/extensions/azure-account/.vscodeignore @@ -0,0 +1,4 @@ +test/** +src/** +tsconfig.json +npm-shrinkwrap.json \ No newline at end of file diff --git a/extensions/azure-account/package.json b/extensions/azure-account/package.json new file mode 100644 index 00000000000..d98aa059e74 --- /dev/null +++ b/extensions/azure-account/package.json @@ -0,0 +1,70 @@ +{ + "name": "azure-account", + "version": "0.1.0", + "publisher": "vscode", + "engines": { + "vscode": "*" + }, + "enableProposedApi": true, + "activationEvents": [ + "*" + ], + "main": "./out/extension", + "contributes": { + "commands": [ + { + "command": "azure-account.login", + "title": "%azure-account.commands.login%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.logout", + "title": "%azure-account.commands.logout%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.addFilter", + "title": "%azure-account.commands.addResourceFilter%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.removeFilter", + "title": "%azure-account.commands.removeResourceFilter%", + "category": "%azure-account.commands.azure%" + }, + { + "command": "azure-account.createAccount", + "title": "%azure-account.commands.createAccount%", + "category": "%azure-account.commands.azure%" + } + ], + "configuration": { + "type": "object", + "title": "Azure configuration", + "properties": { + "azure.resourceFilter": { + "type": "array", + "default": null, + "description": "The resource filter, each element is either a subscription id or a subscription id and a resource group name separated by a slash." + } + } + } + }, + "scripts": { + "compile": "gulp compile-extension:azure-account", + "watch": "gulp watch-extension:azure-account" + }, + "devDependencies": { + "@types/copy-paste": "^1.1.30", + "@types/node": "^6.0.40", + "@types/opn": "^3.0.28" + }, + "dependencies": { + "adal-node": "^0.1.22", + "azure-arm-resource": "^2.0.0-preview", + "copy-paste": "^1.3.0", + "ms-rest-azure": "^2.2.3", + "vscode-nls": "^2.0.2", + "opn": "^5.1.0" + } +} \ No newline at end of file diff --git a/extensions/azure-account/package.nls.json b/extensions/azure-account/package.nls.json new file mode 100644 index 00000000000..4857a376665 --- /dev/null +++ b/extensions/azure-account/package.nls.json @@ -0,0 +1,8 @@ +{ + "azure-account.commands.azure": "Azure", + "azure-account.commands.login": "Login", + "azure-account.commands.logout": "Logout", + "azure-account.commands.addResourceFilter": "Add Resource Filter", + "azure-account.commands.removeResourceFilter": "Remove Resource Filter", + "azure-account.commands.createAccount": "Create an Account" +} \ No newline at end of file diff --git a/extensions/azure-account/src/azure-account.ts b/extensions/azure-account/src/azure-account.ts new file mode 100644 index 00000000000..9d52bc3073b --- /dev/null +++ b/extensions/azure-account/src/azure-account.ts @@ -0,0 +1,471 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const adal = require('adal-node'); +const MemoryCache = adal.MemoryCache; +const AuthenticationContext = adal.AuthenticationContext; +const CacheDriver = require('adal-node/lib/cache-driver'); +const createLogContext = require('adal-node/lib/log').createLogContext; + +import { DeviceTokenCredentials, AzureEnvironment } from 'ms-rest-azure'; +import { SubscriptionClient, ResourceManagementClient, SubscriptionModels } from 'azure-arm-resource'; +import * as opn from 'opn'; +import * as copypaste from 'copy-paste'; +import * as nls from 'vscode-nls'; + +import { window, commands, credentials, EventEmitter, MessageItem, ExtensionContext, workspace, ConfigurationTarget } from 'vscode'; +import { AzureLogin, AzureSession, AzureLoginStatus, AzureResourceFilter } from './typings/azure-account.api'; + +const localize = nls.loadMessageBundle(); + +const defaultEnvironment = (AzureEnvironment).Azure; +const commonTenantId = 'common'; +const authorityHostUrl = defaultEnvironment.activeDirectoryEndpointUrl; +const clientId = '818dee8b-8777-4f45-afc3-f7cc977caae2'; +const authorityUrl = `${authorityHostUrl}${commonTenantId}`; +const resource = defaultEnvironment.activeDirectoryResourceId; + +const credentialsService = 'VSCode Public Azure'; +const credentialsAccount = 'Refresh Token'; + +interface DeviceLogin { + userCode: string; + deviceCode: string; + verificationUrl: string; + expiresIn: number; + interval: number; + message: string; +} + +interface TokenResponse { + tokenType: string; + expiresIn: number; + expiresOn: string; + resource: string; + accessToken: string; + refreshToken: string; + userId: string; + isUserIdDisplayable: boolean; + familyName: string; + givenName: string; + oid: string; + tenantId: string; + isMRRT: boolean; + _clientId: string; + _authority: string; +} + +interface AzureLoginWriteable extends AzureLogin { + status: AzureLoginStatus; +} + +class AzureLoginError extends Error { + constructor(message: string, public _reason: any) { + super(message); + } +} + +export class AzureLoginHelper { + + private onStatusChanged = new EventEmitter(); + private onSessionsChanged = new EventEmitter(); + private onFiltersChanged = new EventEmitter(); + private tokenCache = new MemoryCache(); + private oldResourceFilter: string; + + constructor(context: ExtensionContext) { + const subscriptions = context.subscriptions; + subscriptions.push(commands.registerCommand('azure-account.login', () => this.login().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.logout', () => this.logout().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.askForLogin', () => this.askForLogin().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.addFilter', () => this.addFilter().catch(console.error))); + subscriptions.push(commands.registerCommand('azure-account.removeFilter', () => this.removeFilter().catch(console.error))); + subscriptions.push(this.api.onSessionsChanged(() => this.updateFilters().catch(console.error))); + subscriptions.push(workspace.onDidChangeConfiguration(() => this.updateFilters(true).catch(console.error))); + this.initialize() + .catch(console.error); + } + + api: AzureLogin = { + status: 'Initializing', + onStatusChanged: this.onStatusChanged.event, + sessions: [], + onSessionsChanged: this.onSessionsChanged.event, + filters: [], + onFiltersChanged: this.onFiltersChanged.event + }; + + async login() { + try { + this.beginLoggingIn(); + const deviceLogin = await deviceLogin1(); + const copyAndOpen: MessageItem = { title: localize('azure-account.copyAndOpen', "Copy & Open") }; + const close: MessageItem = { title: localize('azure-account.close', "Close"), isCloseAffordance: true }; + const response = await window.showInformationMessage(deviceLogin.message, copyAndOpen, close); + if (response === copyAndOpen) { + copypaste.copy(deviceLogin.userCode); + opn(deviceLogin.verificationUrl); + } + const tokenResponse = await deviceLogin2(deviceLogin); + const refreshToken = tokenResponse.refreshToken; + const tokenResponses = await tokensFromToken(tokenResponse); + await credentials.writeSecret(credentialsService, credentialsAccount, refreshToken); + await this.updateSessions(tokenResponses); + } finally { + this.updateStatus(); + } + } + + async logout() { + await credentials.deleteSecret(credentialsService, credentialsAccount); + await this.updateSessions([]); + this.updateStatus(); + } + + private async initialize() { + try { + const refreshToken = await credentials.readSecret(credentialsService, credentialsAccount); + if (refreshToken) { + this.beginLoggingIn(); + const tokenResponse = await tokenFromRefreshToken(refreshToken); + const tokenResponses = await tokensFromToken(tokenResponse); + await this.updateSessions(tokenResponses); + } + } catch (err) { + if (!(err instanceof AzureLoginError)) { + throw err; + } + } finally { + this.updateStatus(); + } + } + + private beginLoggingIn() { + if (this.api.status !== 'LoggedIn') { + (this.api).status = 'LoggingIn'; + this.onStatusChanged.fire(this.api.status); + } + } + + private updateStatus() { + const status = this.api.sessions.length ? 'LoggedIn' : 'LoggedOut'; + if (this.api.status !== status) { + (this.api).status = status; + this.onStatusChanged.fire(this.api.status); + } + } + + private async updateSessions(tokenResponses: TokenResponse[]) { + await clearTokenCache(this.tokenCache); + for (const tokenResponse of tokenResponses) { + await addTokenToCache(this.tokenCache, tokenResponse); + } + const sessions = this.api.sessions; + sessions.splice(0, sessions.length, ...tokenResponses.map(tokenResponse => ({ + environment: defaultEnvironment, + userId: tokenResponse.userId, + tenantId: tokenResponse.tenantId, + credentials: new DeviceTokenCredentials({ username: tokenResponse.userId, clientId, tokenCache: this.tokenCache, domain: tokenResponse.tenantId }) + }))); + this.onSessionsChanged.fire(); + } + + private async askForLogin() { + if (this.api.status === 'LoggedIn') { + return; + } + const login = { title: localize('azure-account.login', "Login") }; + const cancel = { title: 'Cancel', isCloseAffordance: true }; + const result = await window.showInformationMessage(localize('azure-account.loginFirst', "Not logged in, log in first."), login, cancel); + return result === login && commands.executeCommand('azure-account.login'); + } + + private async addFilter() { + if (this.api.status !== 'LoggedIn') { + return commands.executeCommand('azure-account.askForLogin'); + } + + const azureConfig = workspace.getConfiguration('azure'); + const resourceFilter = azureConfig.get('resourceFilter') || []; + + const subscriptionItems: { session: AzureSession; subscription: SubscriptionModels.Subscription }[] = []; + for (const session of this.api.sessions) { + const credentials = session.credentials; + const client = new SubscriptionClient(credentials); + const subscriptions = await listAll(client.subscriptions, client.subscriptions.list()); + subscriptionItems.push(...subscriptions.filter(subscription => resourceFilter.indexOf(`${session.tenantId}/${subscription.subscriptionId}`) === -1) + .map(subscription => ({ + session, + subscription + }))); + } + subscriptionItems.sort((a, b) => a.subscription.displayName!.localeCompare(b.subscription.displayName!)); + const subscriptionResult = await window.showQuickPick(subscriptionItems.map(subscription => ({ + label: subscription.subscription.displayName!, + description: subscription.subscription.subscriptionId!, + subscription + }))); + if (!subscriptionResult) { + return; + } + + const { session, subscription } = subscriptionResult.subscription; + const client = new ResourceManagementClient(session.credentials, subscription.subscriptionId!); + const resourceGroups = await listAll(client.resourceGroups, client.resourceGroups.list()); + const resourceGroupFilters: AzureResourceFilter[] = [ + { + ...subscriptionResult.subscription, + allResourceGroups: true, + resourceGroups + } + ]; + resourceGroupFilters.push(...resourceGroups.filter(resourceGroup => resourceFilter.indexOf(`${session.tenantId}/${subscription.subscriptionId}/${resourceGroup.name}`) === -1) + .map(resourceGroup => ({ + session, + subscription, + allResourceGroups: false, + resourceGroups: [resourceGroup] + }))); + resourceGroupFilters.sort((a, b) => (!a.allResourceGroups ? a.resourceGroups[0].name! : '').localeCompare(!b.allResourceGroups ? b.resourceGroups[0].name! : '')); + const resourceGroupResult = await window.showQuickPick(resourceGroupFilters.map(resourceGroup => (!resourceGroup.allResourceGroups ? { + label: resourceGroup.resourceGroups[0].name!, + description: resourceGroup.resourceGroups[0].location, + resourceGroup + } : { + label: localize('azure-account.entireSubscription', "Entire Subscription"), + description: '', + resourceGroup + }))); + if (!resourceGroupResult) { + return; + } + + const resourceGroup = resourceGroupResult.resourceGroup; + if (!resourceGroup.allResourceGroups) { + resourceFilter.push(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}/${resourceGroup.resourceGroups[0].name}`); + } else { + resourceFilter.splice(0, resourceFilter.length, ...resourceFilter.filter(c => !c.startsWith(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}/`))); + resourceFilter.push(`${resourceGroup.session.tenantId}/${resourceGroup.subscription.subscriptionId}`); + } + + const resourceFilterConfig = azureConfig.inspect('resourceFilter'); + let target = ConfigurationTarget.Global; + if (resourceFilterConfig) { + if (resourceFilterConfig.workspaceFolderValue) { + target = ConfigurationTarget.WorkspaceFolder; + } else if (resourceFilterConfig.workspaceValue) { + target = ConfigurationTarget.Workspace; + } else if (resourceFilterConfig.globalValue) { + target = ConfigurationTarget.Global; + } + } + await azureConfig.update('resourceFilter', resourceFilter, target); + } + + private async removeFilter() { + if (this.api.status !== 'LoggedIn') { + return commands.executeCommand('azure-account.askForLogin'); + } + + const azureConfig = workspace.getConfiguration('azure'); + let resourceFilter = azureConfig.get('resourceFilter') || []; + + const filters = resourceFilter.length ? this.api.filters.reduce((list, filter) => { + if (filter.allResourceGroups) { + list.push(filter); + } else { + list.push(...filter.resourceGroups.map(resourceGroup => ({ + ...filter, + resourceGroups: [resourceGroup] + }))); + } + return list; + }, []) : []; + filters.sort((a, b) => (!a.allResourceGroups ? a.resourceGroups[0].name! : `/${a.subscription.displayName}`).localeCompare(!b.allResourceGroups ? b.resourceGroups[0].name! : `/${b.subscription.displayName}`)); + const filterResult = await window.showQuickPick(filters.map(filter => (!filter.allResourceGroups ? { + label: filter.resourceGroups[0].name!, + description: filter.subscription.displayName!, + filter + } : { + label: filter.subscription.displayName!, + description: filter.subscription.subscriptionId!, + filter + }))); + if (!filterResult) { + return; + } + + const filter = filterResult.filter; + const remove = !filter.allResourceGroups ? + `${filter.session.tenantId}/${filter.subscription.subscriptionId}/${filter.resourceGroups[0].name}` : + `${filter.session.tenantId}/${filter.subscription.subscriptionId}`; + resourceFilter = resourceFilter.filter(e => e !== remove); + + const resourceFilterConfig = azureConfig.inspect('resourceFilter'); + let target = ConfigurationTarget.Global; + if (resourceFilterConfig) { + if (resourceFilterConfig.workspaceFolderValue) { + target = ConfigurationTarget.WorkspaceFolder; + } else if (resourceFilterConfig.workspaceValue) { + target = ConfigurationTarget.Workspace; + } else if (resourceFilterConfig.globalValue) { + target = ConfigurationTarget.Global; + } + } + await azureConfig.update('resourceFilter', resourceFilter.length ? resourceFilter : undefined, target); + } + + private async updateFilters(configChange = false) { + const azureConfig = workspace.getConfiguration('azure'); + let resourceFilter = azureConfig.get('resourceFilter'); + if (configChange && JSON.stringify(resourceFilter) === this.oldResourceFilter) { + return; + } + this.oldResourceFilter = JSON.stringify(resourceFilter); + if (resourceFilter && !Array.isArray(resourceFilter)) { + resourceFilter = []; + } + const filters = resourceFilter && resourceFilter.map(s => typeof s === 'string' ? s.split('/') : []) + .filter(s => s.length === 2 || s.length === 3) + .map(([tenantId, subscriptionId, resourceGroup]) => ({ tenantId, subscriptionId, resourceGroup })); + const tenantIds = filters && filters.reduce | boolean>>>((result, filter) => { + const tenant = result[filter.tenantId] || (result[filter.tenantId] = {}); + const resourceGroups = tenant[filter.subscriptionId] || (tenant[filter.subscriptionId] = (filter.resourceGroup ? {} : true)); + if (typeof resourceGroups === 'object' && filter.resourceGroup) { + resourceGroups[filter.resourceGroup] = true; + } + return result; + }, {}); + + const newFilters: AzureResourceFilter[] = []; + const sessions = tenantIds ? this.api.sessions.filter(session => tenantIds[session.tenantId]) : this.api.sessions; + for (const session of sessions) { + const client = new SubscriptionClient(session.credentials); + const subscriptionIds = tenantIds && tenantIds[session.tenantId]; + const subscriptions = await listAll(client.subscriptions, client.subscriptions.list()); + const filteredSubscriptions = subscriptionIds ? subscriptions.filter(subscription => subscriptionIds[subscription.subscriptionId!]) : subscriptions; + for (const subscription of filteredSubscriptions) { + const client = new ResourceManagementClient(session.credentials, subscription.subscriptionId!); + const resourceGroupNames = subscriptionIds && subscriptionIds[subscription.subscriptionId!]; + const allResourceGroups = !(resourceGroupNames && typeof resourceGroupNames === 'object'); + const unfilteredResourceGroups = await listAll(client.resourceGroups, client.resourceGroups.list()); + const resourceGroups = allResourceGroups ? unfilteredResourceGroups : unfilteredResourceGroups.filter(resourceGroup => (>resourceGroupNames!)[resourceGroup.name!]); + newFilters.push({ session, subscription, allResourceGroups, resourceGroups }); + } + } + this.api.filters.splice(0, this.api.filters.length, ...newFilters); + this.onFiltersChanged.fire(); + } +} + +async function deviceLogin1(): Promise { + return new Promise((resolve, reject) => { + const cache = new MemoryCache(); + const context = new AuthenticationContext(authorityUrl, null, cache); + context.acquireUserCode(resource, clientId, 'en-us', function (err: any, response: any) { + if (err) { + reject(new AzureLoginError(localize('azure-account.userCodeFailed', "Aquiring user code failed"), err)); + } else { + resolve(response); + } + }); + }); +} + +async function deviceLogin2(deviceLogin: DeviceLogin) { + return new Promise((resolve, reject) => { + const tokenCache = new MemoryCache(); + const context = new AuthenticationContext(authorityUrl, null, tokenCache); + context.acquireTokenWithDeviceCode(resource, clientId, deviceLogin, function (err: any, tokenResponse: TokenResponse) { + if (err) { + reject(new AzureLoginError(localize('azure-account.tokenFailed', "Aquiring token with device code"), err)); + } else { + resolve(tokenResponse); + } + }); + }); +} + +async function tokenFromRefreshToken(refreshToken: string, tenantId = commonTenantId) { + return new Promise((resolve, reject) => { + const tokenCache = new MemoryCache(); + const context = new AuthenticationContext(`${authorityHostUrl}${tenantId}`, null, tokenCache); + context.acquireTokenWithRefreshToken(refreshToken, clientId, null, function (err: any, tokenResponse: TokenResponse) { + if (err) { + reject(new AzureLoginError(localize('azure-account.tokenFromRefreshTokenFailed', "Aquiring token with refresh token"), err)); + } else { + resolve(tokenResponse); + } + }); + }); +} + +async function tokensFromToken(firstTokenResponse: TokenResponse) { + const tokenResponses = [firstTokenResponse]; + const tokenCache = new MemoryCache(); + await addTokenToCache(tokenCache, firstTokenResponse); + const credentials = new DeviceTokenCredentials({ username: firstTokenResponse.userId, clientId, tokenCache }); + const client = new SubscriptionClient(credentials); + const tenants = await listAll(client.tenants, client.tenants.list()); + for (const tenant of tenants) { + if (tenant.tenantId !== firstTokenResponse.tenantId) { + const tokenResponse = await tokenFromRefreshToken(firstTokenResponse.refreshToken, tenant.tenantId); + tokenResponses.push(tokenResponse); + } + } + return tokenResponses; +} + +async function addTokenToCache(tokenCache: any, tokenResponse: TokenResponse) { + return new Promise((resolve, reject) => { + const driver = new CacheDriver( + { _logContext: createLogContext('') }, + `${authorityHostUrl}${tokenResponse.tenantId}`, + tokenResponse.resource, + clientId, + tokenCache, + (entry: any, resource: any, callback: (err: any, response: any) => {}) => { + callback(null, entry); + } + ); + driver.add(tokenResponse, function (err: any) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); +} + +async function clearTokenCache(tokenCache: any) { + await new Promise((resolve, reject) => { + tokenCache.find({}, (err: any, entries: any[]) => { + if (err) { + reject(err); + } else { + tokenCache.remove(entries, (err: any) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + } + }); + }); +} + +export interface PartialList extends Array { + nextLink?: string; +} + +export async function listAll(client: { listNext(nextPageLink: string): Promise>; }, first: Promise>): Promise { + const all: T[] = []; + for (let list = await first; list.length || list.nextLink; list = list.nextLink ? await client.listNext(list.nextLink) : []) { + all.push(...list); + } + return all; +} \ No newline at end of file diff --git a/extensions/azure-account/src/extension.ts b/extensions/azure-account/src/extension.ts new file mode 100644 index 00000000000..4b67cf387d2 --- /dev/null +++ b/extensions/azure-account/src/extension.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { window, ExtensionContext, commands, credentials } from 'vscode'; +import { AzureLoginHelper } from './azure-account'; +import { AzureLogin } from './typings/azure-account.api'; +import * as opn from 'opn'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export function activate(context: ExtensionContext) { + if (!credentials) { + return; // Proposed API not available. + } + const azureLogin = new AzureLoginHelper(context); + const subscriptions = context.subscriptions; + subscriptions.push(createStatusBarItem(azureLogin.api)); + subscriptions.push(commands.registerCommand('azure-account.createAccount', createAccount)); + return azureLogin.api; +} + +function createAccount() { + opn('https://azure.microsoft.com/en-us/free'); +} + +function createStatusBarItem(api: AzureLogin) { + const statusBarItem = window.createStatusBarItem(); + function updateStatusBar() { + switch (api.status) { + case 'LoggingIn': + statusBarItem.text = localize('azure-account.loggingIn', "Azure: Logging in..."); + statusBarItem.show(); + break; + case 'LoggedIn': + statusBarItem.text = localize('azure-account.loggedIn', "Azure: {0}", api.sessions[0].userId); + statusBarItem.show(); + break; + default: + statusBarItem.hide(); + break; + } + } + api.onStatusChanged(updateStatusBar); + api.onSessionsChanged(updateStatusBar); + updateStatusBar(); + return statusBarItem; +} + +export function deactivate() { +} \ No newline at end of file diff --git a/extensions/azure-account/src/typings/azure-account.api.d.ts b/extensions/azure-account/src/typings/azure-account.api.d.ts new file mode 100644 index 00000000000..b1f2c167b45 --- /dev/null +++ b/extensions/azure-account/src/typings/azure-account.api.d.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vscode'; +import { ServiceClientCredentials } from 'ms-rest'; +import { AzureEnvironment } from 'ms-rest-azure'; +import { SubscriptionModels, ResourceModels } from 'azure-arm-resource'; + +export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut'; + +export interface AzureLogin { + readonly status: AzureLoginStatus; + readonly onStatusChanged: Event; + readonly sessions: AzureSession[]; + readonly onSessionsChanged: Event; + readonly filters: AzureResourceFilter[]; + readonly onFiltersChanged: Event; +} + +export interface AzureSession { + readonly environment: AzureEnvironment; + readonly userId: string; + readonly tenantId: string; + readonly credentials: ServiceClientCredentials; +} + +export interface AzureResourceFilter { + readonly session: AzureSession; + readonly subscription: SubscriptionModels.Subscription; + readonly allResourceGroups: boolean; + readonly resourceGroups: ResourceModels.ResourceGroup[]; +} \ No newline at end of file diff --git a/extensions/azure-account/src/typings/ref.d.ts b/extensions/azure-account/src/typings/ref.d.ts new file mode 100644 index 00000000000..216911a680e --- /dev/null +++ b/extensions/azure-account/src/typings/ref.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// diff --git a/extensions/azure-account/src/typings/vscode.rejected.d.ts b/extensions/azure-account/src/typings/vscode.rejected.d.ts new file mode 100644 index 00000000000..7f6f64df560 --- /dev/null +++ b/extensions/azure-account/src/typings/vscode.rejected.d.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// This is the place for API experiments and proposal. + +declare module 'vscode' { + + /** + * Namespace for handling credentials. + */ + export namespace credentials { + + /** + * Read a previously stored secret from the credential store. + * + * @param service The service of the credential. + * @param account The account of the credential. + * @return A promise for the secret of the credential. + */ + export function readSecret(service: string, account: string): Thenable; + + /** + * Write a secret to the credential store. + * + * @param service The service of the credential. + * @param account The account of the credential. + * @param secret The secret of the credential to write to the credential store. + * @return A promise indicating completion of the operation. + */ + export function writeSecret(service: string, account: string, secret: string): Thenable; + + /** + * Delete a previously stored secret from the credential store. + * + * @param service The service of the credential. + * @param account The account of the credential. + * @return A promise resolving to true if there was a secret for that service and account. + */ + export function deleteSecret(service: string, account: string): Thenable; + } +} diff --git a/extensions/azure-account/tsconfig.json b/extensions/azure-account/tsconfig.json new file mode 100644 index 00000000000..61265d449a4 --- /dev/null +++ b/extensions/azure-account/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "strict": true, + "noUnusedLocals": true, + "outDir": "./out", + "lib": [ + "es6" + ], + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], + "include": [ + "src/**/*" + ] +} \ No newline at end of file From 16657b1c7a425b0446892c987762c6f1266775f6 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 15:31:48 -0700 Subject: [PATCH 75/96] Surface credentials --- extensions/azure-account/src/azure-account.ts | 3 ++- .../azure-account/src/typings/azure-account.api.d.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/extensions/azure-account/src/azure-account.ts b/extensions/azure-account/src/azure-account.ts index 9d52bc3073b..654f1a25aa7 100644 --- a/extensions/azure-account/src/azure-account.ts +++ b/extensions/azure-account/src/azure-account.ts @@ -94,7 +94,8 @@ export class AzureLoginHelper { sessions: [], onSessionsChanged: this.onSessionsChanged.event, filters: [], - onFiltersChanged: this.onFiltersChanged.event + onFiltersChanged: this.onFiltersChanged.event, + credentials }; async login() { diff --git a/extensions/azure-account/src/typings/azure-account.api.d.ts b/extensions/azure-account/src/typings/azure-account.api.d.ts index b1f2c167b45..09ee52c1cd4 100644 --- a/extensions/azure-account/src/typings/azure-account.api.d.ts +++ b/extensions/azure-account/src/typings/azure-account.api.d.ts @@ -17,6 +17,7 @@ export interface AzureLogin { readonly onSessionsChanged: Event; readonly filters: AzureResourceFilter[]; readonly onFiltersChanged: Event; + readonly credentials: Credentials; } export interface AzureSession { @@ -31,4 +32,10 @@ export interface AzureResourceFilter { readonly subscription: SubscriptionModels.Subscription; readonly allResourceGroups: boolean; readonly resourceGroups: ResourceModels.ResourceGroup[]; -} \ No newline at end of file +} + +export interface Credentials { + readSecret(service: string, account: string): Thenable; + writeSecret(service: string, account: string, secret: string): Thenable; + deleteSecret(service: string, account: string): Thenable; +} From 131780c80ad7608c719845e3bcd0534320222342 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 16:07:14 -0700 Subject: [PATCH 76/96] Revert custom application id --- extensions/azure-account/src/azure-account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/azure-account/src/azure-account.ts b/extensions/azure-account/src/azure-account.ts index 654f1a25aa7..6d7ebb2b99f 100644 --- a/extensions/azure-account/src/azure-account.ts +++ b/extensions/azure-account/src/azure-account.ts @@ -23,7 +23,7 @@ const localize = nls.loadMessageBundle(); const defaultEnvironment = (AzureEnvironment).Azure; const commonTenantId = 'common'; const authorityHostUrl = defaultEnvironment.activeDirectoryEndpointUrl; -const clientId = '818dee8b-8777-4f45-afc3-f7cc977caae2'; +const clientId = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'; const authorityUrl = `${authorityHostUrl}${commonTenantId}`; const resource = defaultEnvironment.activeDirectoryResourceId; From d315efe6ef42c9c21ef2ad016b0c3b19d021bc45 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 16:19:13 -0700 Subject: [PATCH 77/96] Naming --- extensions/azure-account/src/azure-account.ts | 10 +++++----- extensions/azure-account/src/extension.ts | 4 ++-- .../azure-account/src/typings/azure-account.api.d.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/azure-account/src/azure-account.ts b/extensions/azure-account/src/azure-account.ts index 6d7ebb2b99f..d5b4b3d9ed1 100644 --- a/extensions/azure-account/src/azure-account.ts +++ b/extensions/azure-account/src/azure-account.ts @@ -16,7 +16,7 @@ import * as copypaste from 'copy-paste'; import * as nls from 'vscode-nls'; import { window, commands, credentials, EventEmitter, MessageItem, ExtensionContext, workspace, ConfigurationTarget } from 'vscode'; -import { AzureLogin, AzureSession, AzureLoginStatus, AzureResourceFilter } from './typings/azure-account.api'; +import { AzureAccount, AzureSession, AzureLoginStatus, AzureResourceFilter } from './typings/azure-account.api'; const localize = nls.loadMessageBundle(); @@ -57,7 +57,7 @@ interface TokenResponse { _authority: string; } -interface AzureLoginWriteable extends AzureLogin { +interface AzureAccountWriteable extends AzureAccount { status: AzureLoginStatus; } @@ -88,7 +88,7 @@ export class AzureLoginHelper { .catch(console.error); } - api: AzureLogin = { + api: AzureAccount = { status: 'Initializing', onStatusChanged: this.onStatusChanged.event, sessions: [], @@ -145,7 +145,7 @@ export class AzureLoginHelper { private beginLoggingIn() { if (this.api.status !== 'LoggedIn') { - (this.api).status = 'LoggingIn'; + (this.api).status = 'LoggingIn'; this.onStatusChanged.fire(this.api.status); } } @@ -153,7 +153,7 @@ export class AzureLoginHelper { private updateStatus() { const status = this.api.sessions.length ? 'LoggedIn' : 'LoggedOut'; if (this.api.status !== status) { - (this.api).status = status; + (this.api).status = status; this.onStatusChanged.fire(this.api.status); } } diff --git a/extensions/azure-account/src/extension.ts b/extensions/azure-account/src/extension.ts index 4b67cf387d2..df46a761df1 100644 --- a/extensions/azure-account/src/extension.ts +++ b/extensions/azure-account/src/extension.ts @@ -5,7 +5,7 @@ import { window, ExtensionContext, commands, credentials } from 'vscode'; import { AzureLoginHelper } from './azure-account'; -import { AzureLogin } from './typings/azure-account.api'; +import { AzureAccount } from './typings/azure-account.api'; import * as opn from 'opn'; import * as nls from 'vscode-nls'; @@ -26,7 +26,7 @@ function createAccount() { opn('https://azure.microsoft.com/en-us/free'); } -function createStatusBarItem(api: AzureLogin) { +function createStatusBarItem(api: AzureAccount) { const statusBarItem = window.createStatusBarItem(); function updateStatusBar() { switch (api.status) { diff --git a/extensions/azure-account/src/typings/azure-account.api.d.ts b/extensions/azure-account/src/typings/azure-account.api.d.ts index 09ee52c1cd4..089511dda45 100644 --- a/extensions/azure-account/src/typings/azure-account.api.d.ts +++ b/extensions/azure-account/src/typings/azure-account.api.d.ts @@ -10,7 +10,7 @@ import { SubscriptionModels, ResourceModels } from 'azure-arm-resource'; export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut'; -export interface AzureLogin { +export interface AzureAccount { readonly status: AzureLoginStatus; readonly onStatusChanged: Event; readonly sessions: AzureSession[]; From 84c7d308370c47cafd716e22498d0b3afb2bc12f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 18 Aug 2017 16:31:43 -0700 Subject: [PATCH 78/96] Adapt the terminal for xterm v3 Fixes #32725 Fixes #32552 --- src/typings/xterm.d.ts | 4 +--- .../parts/terminal/electron-browser/terminalInstance.ts | 2 +- .../parts/terminal/electron-browser/windowsShellHelper.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts index 08c24dcd70c..e7e0917e2e0 100644 --- a/src/typings/xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -6,7 +6,7 @@ declare module 'xterm' { type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void; - class Terminal { + export class Terminal { cols: number; rows: number; ydisp: number; @@ -192,6 +192,4 @@ declare module 'xterm' { */ static loadAddon(addon: string): void; } - - export = Terminal; } \ No newline at end of file diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 3b2a163b332..2a7fd366e38 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -13,7 +13,7 @@ import * as dom from 'vs/base/browser/dom'; import Event, { Emitter } from 'vs/base/common/event'; import Uri from 'vs/base/common/uri'; import { WindowsShellHelper } from 'vs/workbench/parts/terminal/electron-browser/windowsShellHelper'; -import XTermTerminal = require('xterm'); +import { Terminal as XTermTerminal } from 'xterm'; import { Dimension } from 'vs/base/browser/builder'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; diff --git a/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts index 4effb4b9466..6dab973c67a 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/windowsShellHelper.ts @@ -10,7 +10,7 @@ import * as path from 'path'; import { TPromise } from 'vs/base/common/winjs.base'; import { Emitter, debounceEvent } from 'vs/base/common/event'; import { ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal'; -import XTermTerminal = require('xterm'); +import { Terminal as XTermTerminal } from 'xterm'; const SHELL_EXECUTABLES = ['cmd.exe', 'powershell.exe', 'bash.exe']; From 43199d610343fbc0577df4bfa04db163bc7edda6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 18 Aug 2017 16:34:20 -0700 Subject: [PATCH 79/96] Pull in latest xterm.js Fixes #32554 --- npm-shrinkwrap.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index fd2a06ef420..50f1b87edd3 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -569,7 +569,7 @@ "xterm": { "version": "2.9.1", "from": "Tyriar/xterm.js#vscode-release/1.16", - "resolved": "git+https://github.com/Tyriar/xterm.js.git#ec8e705ffef18ec9f90e27baff30105b0278b3bb" + "resolved": "git+https://github.com/Tyriar/xterm.js.git#74fde417c97962730a5fbbc8e8e7dcd47e1b897b" }, "yauzl": { "version": "2.8.0", From 99beb4f93ff45c05be83f167b1d67d95332f7524 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 18 Aug 2017 16:44:42 -0700 Subject: [PATCH 80/96] Add shrinkwrap.json --- extensions/azure-account/npm-shrinkwrap.json | 521 +++++++++++++++++++ 1 file changed, 521 insertions(+) create mode 100644 extensions/azure-account/npm-shrinkwrap.json diff --git a/extensions/azure-account/npm-shrinkwrap.json b/extensions/azure-account/npm-shrinkwrap.json new file mode 100644 index 00000000000..18206b8124a --- /dev/null +++ b/extensions/azure-account/npm-shrinkwrap.json @@ -0,0 +1,521 @@ +{ + "name": "azure-account", + "version": "0.1.0", + "dependencies": { + "@types/copy-paste": { + "version": "1.1.30", + "from": "@types/copy-paste@>=1.1.30 <2.0.0", + "resolved": "https://registry.npmjs.org/@types/copy-paste/-/copy-paste-1.1.30.tgz", + "dev": true + }, + "@types/form-data": { + "version": "2.2.0", + "from": "@types/form-data@*", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.0.tgz", + "dependencies": { + "@types/node": { + "version": "8.0.24", + "from": "@types/node@*", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz" + } + } + }, + "@types/node": { + "version": "6.0.87", + "from": "@types/node@>=6.0.40 <7.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.87.tgz", + "dev": true + }, + "@types/opn": { + "version": "3.0.28", + "from": "@types/opn@>=3.0.28 <4.0.0", + "resolved": "https://registry.npmjs.org/@types/opn/-/opn-3.0.28.tgz", + "dev": true + }, + "@types/request": { + "version": "0.0.45", + "from": "@types/request@>=0.0.45 <0.0.46", + "resolved": "https://registry.npmjs.org/@types/request/-/request-0.0.45.tgz", + "dependencies": { + "@types/node": { + "version": "8.0.24", + "from": "@types/node@*", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz" + } + } + }, + "@types/uuid": { + "version": "2.0.30", + "from": "@types/uuid@>=2.0.29 <3.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-2.0.30.tgz", + "dependencies": { + "@types/node": { + "version": "8.0.24", + "from": "@types/node@*", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.24.tgz" + } + } + }, + "adal-node": { + "version": "0.1.22", + "from": "adal-node@>=0.1.22 <0.2.0", + "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.22.tgz" + }, + "ajv": { + "version": "4.11.8", + "from": "ajv@>=4.9.1 <5.0.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz" + }, + "asn1": { + "version": "0.2.3", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "async": { + "version": "2.5.0", + "from": "async@>=0.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz" + }, + "asynckit": { + "version": "0.4.0", + "from": "asynckit@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + }, + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.6.0", + "from": "aws4@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + }, + "azure-arm-resource": { + "version": "2.0.0-preview", + "from": "azure-arm-resource@>=2.0.0-preview <3.0.0", + "resolved": "https://registry.npmjs.org/azure-arm-resource/-/azure-arm-resource-2.0.0-preview.tgz" + }, + "base64url": { + "version": "2.0.0", + "from": "base64url@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "optional": true + }, + "boom": { + "version": "2.10.1", + "from": "boom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "from": "buffer-equal-constant-time@1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "caseless@>=0.12.0 <0.13.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "co": { + "version": "4.6.0", + "from": "co@>=4.6.0 <5.0.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "combined-stream@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz" + }, + "copy-paste": { + "version": "1.3.0", + "from": "copy-paste@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/copy-paste/-/copy-paste-1.3.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@>=1.12.0 <2.0.0", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "date-utils": { + "version": "1.2.21", + "from": "date-utils@*", + "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz" + }, + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + }, + "duplexer": { + "version": "0.1.1", + "from": "duplexer@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "ecc-jsbn@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "optional": true + }, + "ecdsa-sig-formatter": { + "version": "1.0.9", + "from": "ecdsa-sig-formatter@1.0.9", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz" + }, + "extend": { + "version": "3.0.1", + "from": "extend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz" + }, + "extsprintf": { + "version": "1.3.0", + "from": "extsprintf@1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.4", + "from": "form-data@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz" + }, + "getpass": { + "version": "0.1.7", + "from": "getpass@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "har-schema@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + }, + "har-validator": { + "version": "4.2.1", + "from": "har-validator@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz" + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@>=3.1.3 <3.2.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" + }, + "hoek": { + "version": "2.16.3", + "from": "hoek@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" + }, + "iconv-lite": { + "version": "0.4.18", + "from": "iconv-lite@>=0.4.8 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz" + }, + "is-buffer": { + "version": "1.1.5", + "from": "is-buffer@>=1.1.5 <2.0.0", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz" + }, + "is-stream": { + "version": "1.1.0", + "from": "is-stream@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "is-wsl": { + "version": "1.1.0", + "from": "is-wsl@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "json-stable-stringify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@>=5.0.1 <5.1.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "jsonify": { + "version": "0.0.0", + "from": "jsonify@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + }, + "jsprim": { + "version": "1.4.1", + "from": "jsprim@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "jwa": { + "version": "1.1.5", + "from": "jwa@>=1.1.4 <2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz" + }, + "jws": { + "version": "3.1.4", + "from": "jws@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.14.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "mime-db": { + "version": "1.29.0", + "from": "mime-db@>=1.29.0 <1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz" + }, + "mime-types": { + "version": "2.1.16", + "from": "mime-types@>=2.1.7 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz" + }, + "moment": { + "version": "2.18.1", + "from": "moment@>=2.14.1 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz" + }, + "ms-rest": { + "version": "2.2.1", + "from": "ms-rest@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.2.1.tgz", + "dependencies": { + "@types/node": { + "version": "7.0.42", + "from": "@types/node@>=7.0.10 <8.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.42.tgz" + }, + "uuid": { + "version": "3.1.0", + "from": "uuid@^3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + } + } + }, + "ms-rest-azure": { + "version": "2.2.3", + "from": "ms-rest-azure@>=2.2.3 <3.0.0", + "resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.2.3.tgz", + "dependencies": { + "@types/node": { + "version": "7.0.42", + "from": "@types/node@^7.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.42.tgz" + }, + "async": { + "version": "0.2.7", + "from": "async@0.2.7", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.7.tgz" + }, + "uuid": { + "version": "3.1.0", + "from": "uuid@^3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + } + } + }, + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@1.4.7", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "opn": { + "version": "5.1.0", + "from": "opn@>=5.1.0 <6.0.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "performance-now@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "qs@>=6.4.0 <6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "request": { + "version": "2.81.0", + "from": "request@>=2.52.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "dependencies": { + "uuid": { + "version": "3.1.0", + "from": "uuid@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "from": "safe-buffer@>=5.0.1 <6.0.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + }, + "sshpk": { + "version": "1.13.1", + "from": "sshpk@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "stringstream": { + "version": "0.0.5", + "from": "stringstream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "sync-exec": { + "version": "0.6.2", + "from": "sync-exec@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/sync-exec/-/sync-exec-0.6.2.tgz", + "optional": true + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.4 <2.4.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz" + }, + "tunnel": { + "version": "0.0.5", + "from": "tunnel@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz" + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@>=0.14.0 <0.15.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "optional": true + }, + "underscore": { + "version": "1.8.3", + "from": "underscore@>=1.3.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" + }, + "verror": { + "version": "1.10.0", + "from": "verror@1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "vscode-nls": { + "version": "2.0.2", + "from": "vscode-nls@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz" + }, + "xmldom": { + "version": "0.1.27", + "from": "xmldom@>=0.1.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz" + }, + "xpath.js": { + "version": "1.0.7", + "from": "xpath.js@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.0.7.tgz" + } + } +} From d9cbd11d0099c6179ac56ddd09f3b727b00006b6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 18 Aug 2017 16:48:51 -0700 Subject: [PATCH 81/96] Pick up TS 2.5.1 insiders --- extensions/npm-shrinkwrap.json | 6 +++--- extensions/package.json | 2 +- .../typescript/src/features/completionItemProvider.ts | 2 +- .../typescript/src/features/documentSymbolProvider.ts | 4 ++-- extensions/typescript/src/typescriptServiceClient.ts | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/npm-shrinkwrap.json b/extensions/npm-shrinkwrap.json index 4ffcfc354c6..149c711b4e7 100644 --- a/extensions/npm-shrinkwrap.json +++ b/extensions/npm-shrinkwrap.json @@ -3,9 +3,9 @@ "version": "0.0.1", "dependencies": { "typescript": { - "version": "2.4.2", - "from": "typescript@2.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz" + "version": "2.5.1-insiders.20170818", + "from": "typescript@2.5.1-insiders.20170818", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.1-insiders.20170818.tgz" } } } diff --git a/extensions/package.json b/extensions/package.json index 834e7ca2131..102d1ef27d5 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "2.4.2" + "typescript": "2.5.1-insiders.20170818" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts index 90a3f56b30e..db06b46cbc2 100644 --- a/extensions/typescript/src/features/completionItemProvider.ts +++ b/extensions/typescript/src/features/completionItemProvider.ts @@ -278,7 +278,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP // Don't complete function calls inside of destructive assigments or imports return this.client.execute('quickinfo', args).then(infoResponse => { const info = infoResponse.body; - switch (info && info.kind) { + switch (info && info.kind as string) { case 'var': case 'let': case 'const': diff --git a/extensions/typescript/src/features/documentSymbolProvider.ts b/extensions/typescript/src/features/documentSymbolProvider.ts index 9fbaf64fc80..456776cf3a6 100644 --- a/extensions/typescript/src/features/documentSymbolProvider.ts +++ b/extensions/typescript/src/features/documentSymbolProvider.ts @@ -71,7 +71,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP let key = `${realIndent}|${item.text}`; if (realIndent !== 0 && !foldingMap[key] && TypeScriptDocumentSymbolProvider.shouldInclueEntry(item.text)) { let result = new SymbolInformation(item.text, - outlineTypeTable[item.kind] || SymbolKind.Variable, + outlineTypeTable[item.kind as string] || SymbolKind.Variable, containerLabel ? containerLabel : '', new Location(resource, textSpan2Range(item.spans[0]))); foldingMap[key] = result; @@ -86,7 +86,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP private static convertNavTree(resource: Uri, bucket: SymbolInformation[], item: Proto.NavigationTree, containerLabel?: string): void { const result = new SymbolInformation(item.text, - outlineTypeTable[item.kind] || SymbolKind.Variable, + outlineTypeTable[item.kind as string] || SymbolKind.Variable, containerLabel ? containerLabel : '', new Location(resource, textSpan2Range(item.spans[0])) ); diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index 24e38eb8db6..091abda60d1 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -501,12 +501,12 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient } const compilerOptions: Proto.ExternalProjectCompilerOptions = { - module: 'CommonJS', - target: 'ES6', + module: Proto.ModuleKind.CommonJS, + target: Proto.ScriptTarget.ES6, allowSyntheticDefaultImports: true, allowNonTsExtensions: true, allowJs: true, - jsx: 'Preserve' + jsx: Proto.JsxEmit.Preserve }; if (this.apiVersion.has230Features()) { From 575cd205bc6f9ce3c4e0b02eb4ce9a6678d0d987 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 18 Aug 2017 16:52:04 -0700 Subject: [PATCH 82/96] Fix ts/js colorizer results --- .../colorize-results/test-issue5431_ts.json | 48 +++--- .../colorize-results/test-strings_ts.json | 160 +++++++++--------- 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/extensions/typescript/test/colorize-results/test-issue5431_ts.json b/extensions/typescript/test/colorize-results/test-issue5431_ts.json index f249e06fe47..53b90902a23 100644 --- a/extensions/typescript/test/colorize-results/test-issue5431_ts.json +++ b/extensions/typescript/test/colorize-results/test-issue5431_ts.json @@ -355,11 +355,11 @@ "c": "${", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { @@ -368,8 +368,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -377,11 +377,11 @@ "c": "}", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { @@ -399,11 +399,11 @@ "c": "${", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { @@ -412,8 +412,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -421,11 +421,11 @@ "c": "}", "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { diff --git a/extensions/typescript/test/colorize-results/test-strings_ts.json b/extensions/typescript/test/colorize-results/test-strings_ts.json index cbf4e9dfb66..136549cfeba 100644 --- a/extensions/typescript/test/colorize-results/test-strings_ts.json +++ b/extensions/typescript/test/colorize-results/test-strings_ts.json @@ -91,11 +91,11 @@ "c": "${", "t": "source.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { @@ -104,8 +104,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -113,11 +113,11 @@ "c": "}", "t": "source.ts meta.var.expr.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { @@ -344,22 +344,22 @@ "c": "${", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -368,8 +368,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -377,11 +377,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -399,11 +399,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -412,8 +412,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -421,22 +421,22 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { "c": "}", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { @@ -454,22 +454,22 @@ "c": "${", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.begin.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.begin.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.begin.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.begin.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.begin.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", + "light_plus": "punctuation.definition.template-expression.begin: #0000FF", + "dark_vs": "punctuation.definition.template-expression.begin: #569CD6", + "light_vs": "punctuation.definition.template-expression.begin: #0000FF", + "hc_black": "punctuation.definition.template-expression.begin: #569CD6" } }, { "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -478,8 +478,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -487,11 +487,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -509,11 +509,11 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { @@ -522,8 +522,8 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", "hc_black": "variable: #9CDCFE" } }, @@ -531,22 +531,22 @@ "c": " ", "t": "source.ts string.template.ts meta.template.expression.ts", "r": { - "dark_plus": "meta.template.expression.ts: #D4D4D4", - "light_plus": "meta.template.expression.ts: #000000", - "dark_vs": "meta.template.expression.ts: #D4D4D4", - "light_vs": "meta.template.expression.ts: #000000", - "hc_black": "meta.template.expression.ts: #FFFFFF" + "dark_plus": "meta.template.expression: #D4D4D4", + "light_plus": "meta.template.expression: #000000", + "dark_vs": "meta.template.expression: #D4D4D4", + "light_vs": "meta.template.expression: #000000", + "hc_black": "meta.template.expression: #FFFFFF" } }, { "c": "}", "t": "source.ts string.template.ts meta.template.expression.ts punctuation.definition.template-expression.end.ts", "r": { - "dark_plus": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_plus": "punctuation.definition.template-expression.end.ts: #0000FF", - "dark_vs": "punctuation.definition.template-expression.end.ts: #569CD6", - "light_vs": "punctuation.definition.template-expression.end.ts: #0000FF", - "hc_black": "punctuation.definition.template-expression.end.ts: #569CD6" + "dark_plus": "punctuation.definition.template-expression.end: #569CD6", + "light_plus": "punctuation.definition.template-expression.end: #0000FF", + "dark_vs": "punctuation.definition.template-expression.end: #569CD6", + "light_vs": "punctuation.definition.template-expression.end: #0000FF", + "hc_black": "punctuation.definition.template-expression.end: #569CD6" } }, { From d98f5893cd150fb9db555d65f481995361f360d0 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 18 Aug 2017 18:23:48 -0700 Subject: [PATCH 83/96] node-debug2@1.16.3 --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 2a8db450907..68caf689ded 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -44,7 +44,7 @@ const nodeModules = ['electron', 'original-fs'] const builtInExtensions = [ { name: 'ms-vscode.node-debug', version: '1.16.5' }, - { name: 'ms-vscode.node-debug2', version: '1.16.1' } + { name: 'ms-vscode.node-debug2', version: '1.16.3' } ]; const excludedExtensions = [ From 2a34051d9d3b4c6cca30d68ff9497a342cc73e0e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Aug 2017 17:06:11 +0200 Subject: [PATCH 84/96] Remove unused code --- src/vs/editor/common/model/tokenIterator.ts | 182 ------------- .../test/common/model/model.modes.test.ts | 240 ------------------ 2 files changed, 422 deletions(-) delete mode 100644 src/vs/editor/common/model/tokenIterator.ts diff --git a/src/vs/editor/common/model/tokenIterator.ts b/src/vs/editor/common/model/tokenIterator.ts deleted file mode 100644 index ad19467d666..00000000000 --- a/src/vs/editor/common/model/tokenIterator.ts +++ /dev/null @@ -1,182 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LineToken } from 'vs/editor/common/core/lineTokens'; -import { Position } from 'vs/editor/common/core/position'; -import { StandardTokenType } from 'vs/editor/common/modes'; - -export interface ITokenInfo { - readonly type: StandardTokenType; - readonly lineNumber: number; - readonly startColumn: number; - readonly endColumn: number; -} - -export interface ITokenIterator { - hasNext(): boolean; - next(): ITokenInfo; - hasPrev(): boolean; - prev(): ITokenInfo; -} - -class TokenInfo implements ITokenInfo { - _tokenInfoBrand: void; - - readonly _actual: LineToken; - public readonly lineNumber: number; - public readonly startColumn: number; - public readonly endColumn: number; - public readonly type: StandardTokenType; - - constructor(actual: LineToken, lineNumber: number) { - this._actual = actual; - this.lineNumber = lineNumber; - this.startColumn = this._actual.startOffset + 1; - this.endColumn = this._actual.endOffset + 1; - this.type = this._actual.tokenType; - } -} - -function findClosestNonEmptyLine(model: editorCommon.ITokenizedModel, position: Position): Position { - const lineNumber = position.lineNumber; - if (model.getLineMaxColumn(lineNumber) !== 1) { - return position; - } - - const lineCount = model.getLineCount(); - - // we need to go up or down - let distance = 1; - while (true) { - let aboveLineNumber = lineNumber - distance; - let belowLineNumber = lineNumber + distance; - - if (aboveLineNumber < 1 && belowLineNumber > lineCount) { - // No more lines above or below - break; - } - - if (aboveLineNumber >= 1) { - let aboveMaxColumn = model.getLineMaxColumn(aboveLineNumber); - if (aboveMaxColumn !== 1) { - // bingo! - return new Position(aboveLineNumber, aboveMaxColumn); - } - } - - if (belowLineNumber <= lineCount) { - let belowMaxColumn = model.getLineMaxColumn(belowLineNumber); - if (belowMaxColumn !== 1) { - // bingo! - return new Position(belowLineNumber, 1); - } - } - - distance++; - } - return null; -} - -export class TokenIterator implements ITokenIterator { - - private _model: editorCommon.ITokenizedModel; - private _lineCount: number; - private _prev: TokenInfo; - private _next: TokenInfo; - - constructor(model: editorCommon.ITokenizedModel, position: Position) { - this._model = model; - this._lineCount = this._model.getLineCount(); - this._prev = null; - this._next = null; - - position = findClosestNonEmptyLine(model, position); - if (position) { - this._model.forceTokenization(position.lineNumber); - let lineTokens = this._model.getLineTokens(position.lineNumber); - let currentToken = lineTokens.findTokenAtOffset(position.column - 1); - if (currentToken) { - this._prev = this._next = new TokenInfo(currentToken, position.lineNumber); - } - } - } - - private _advanceNext(): void { - if (!this._next) { - return; - } - - let lineNumber = this._next.lineNumber; - let next = this._next._actual.next(); - while (!next && lineNumber < this._lineCount) { - lineNumber++; - this._model.forceTokenization(lineNumber); - let currentLineTokens = this._model.getLineTokens(lineNumber); - next = currentLineTokens.firstToken(); - } - - this._prev = this._next; - if (next) { - this._next = new TokenInfo(next, lineNumber); - } else { - this._next = null; - } - } - - private _advancePrev(): void { - if (!this._prev) { - return; - } - - let lineNumber = this._prev.lineNumber; - let prev = this._prev._actual.prev(); - while (!prev && lineNumber > 1) { - lineNumber--; - this._model.forceTokenization(lineNumber); - let currentLineTokens = this._model.getLineTokens(lineNumber); - prev = currentLineTokens.lastToken(); - } - - this._next = this._prev; - if (prev) { - this._prev = new TokenInfo(prev, lineNumber); - } else { - this._prev = null; - } - } - - public hasNext(): boolean { - return this._next !== null; - } - - public next(): ITokenInfo { - const result = this._next; - this._advanceNext(); - return result; - } - - public hasPrev(): boolean { - return this._prev !== null; - } - - public prev(): ITokenInfo { - const result = this._prev; - this._advancePrev(); - return result; - } - - public _invalidate() { - // replace all public functions with errors - var errorFn = function (): any { - throw new Error('iteration isn\'t valid anymore'); - }; - this.hasNext = errorFn; - this.next = errorFn; - this.hasPrev = errorFn; - this.prev = errorFn; - } -} diff --git a/src/vs/editor/test/common/model/model.modes.test.ts b/src/vs/editor/test/common/model/model.modes.test.ts index 474c259b0c3..5d970ea6c69 100644 --- a/src/vs/editor/test/common/model/model.modes.test.ts +++ b/src/vs/editor/test/common/model/model.modes.test.ts @@ -13,7 +13,6 @@ import { Model } from 'vs/editor/common/model/model'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { TokenIterator, ITokenInfo } from 'vs/editor/common/model/tokenIterator'; // --------- utils @@ -289,242 +288,3 @@ suite('Editor Model - Model Modes 2', () => { statesEqual(thisModel, ['', 'ne3', 'Line4', 'Line5']); }); }); - - -suite('Editor Model - Token Iterator', () => { - - const tokenizationSupport: modes.ITokenizationSupport = { - getInitialState: (): modes.IState => NULL_STATE, - tokenize: undefined, - tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { - if (line.length % 3 !== 0) { - throw new Error('Unexpected line length in ' + line); - } - let tokensCount = line.length / 3; - let tokens = new Uint32Array(tokensCount << 1); - for (let i = 0; i < tokensCount; i++) { - tokens[(i << 1)] = 3 * i; - tokens[(i << 1) + 1] = ( - i << modes.MetadataConsts.FOREGROUND_OFFSET - ) >>> 0; - } - return new TokenizationResult2(tokens, state); - } - }; - - let thisModel: Model = null; - let languageRegistration: IDisposable = null; - - setup(() => { - const TEXT = - 'foobarfoobar' + '\r\n' + - 'foobarfoobar' + '\r\n' + - 'foobarfoobar' + '\r\n'; - const LANGUAGE_ID = 'modelModeTestTokenIterator'; - languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); - thisModel = Model.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); - }); - - teardown(() => { - thisModel.dispose(); - thisModel = null; - languageRegistration.dispose(); - languageRegistration = null; - }); - - function tokenIterator(model: Model, position: Position, callback: (it: TokenIterator) => any): any { - let iter = new TokenIterator(model, model.validatePosition(position)); - let result = callback(iter); - iter._invalidate(); - return result; - } - - test('all tokens with ranges', () => { - var calls = 0; - var ranges = [ - [1, 4, 4, 7, 7, 10, 10, 13], - [1, 4, 4, 7, 7, 10, 10, 13], - [1, 4, 4, 7, 7, 10, 10, 13], - ]; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - var a: number[] = [], line = 0; - while (iter.hasNext()) { - calls++; - if (a.length === 0) { - a = ranges.shift(); - line += 1; - } - var next = iter.next(); - assert.equal(next.lineNumber, line); - assert.equal(next.startColumn, a.shift()); - assert.equal(next.endColumn, a.shift()); - } - }); - assert.equal(calls, 12, 'calls'); - }); - - test('all tokens from beginning with next', () => { - var n = 0; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - while (iter.hasNext()) { - iter.next(); - n++; - } - }); - assert.equal(n, 12); - }); - - test('all tokens from beginning with prev', () => { - var n = 0; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - while (iter.hasPrev()) { - iter.prev(); - n++; - } - }); - assert.equal(n, 1); - }); - - test('all tokens from end with prev', () => { - var n = 0; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - while (iter.hasPrev()) { - iter.prev(); - n++; - } - }); - assert.equal(n, 12); - }); - - test('all tokens from end with next', () => { - var n = 0; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - while (iter.hasNext()) { - iter.next(); - n++; - } - }); - assert.equal(n, 1); - }); - - test('prev and next are assert.equal at start', () => { - var calls = 0; - tokenIterator(thisModel, new Position(1, 2), (iter) => { - calls++; - var next = iter.next(); - var prev = iter.prev(); - assert.deepEqual(next, prev); - }); - assert.equal(calls, 1, 'calls'); - }); - - test('position variance within token', () => { - var calls = 0; - - tokenIterator(thisModel, new Position(1, 4), (iter) => { - calls++; - var next = iter.next(); - assert.equal(next.lineNumber, 1); - assert.equal(next.startColumn, 4); - assert.equal(next.endColumn, 7); - }); - - tokenIterator(thisModel, new Position(1, 5), (iter) => { - calls++; - var next = iter.next(); - assert.equal(next.lineNumber, 1); - assert.equal(next.startColumn, 4); - assert.equal(next.endColumn, 7); - }); - - tokenIterator(thisModel, new Position(1, 6), (iter) => { - calls++; - var next = iter.next(); - assert.equal(next.lineNumber, 1); - assert.equal(next.startColumn, 4); - assert.equal(next.endColumn, 7); - }); - - assert.equal(calls, 3, 'calls'); - }); - - test('iterator allows next/prev', () => { - var n = 0; - var up: ITokenInfo[] = [], down: ITokenInfo[] = []; - tokenIterator(thisModel, new Position(1, 1), (iter) => { - while (iter.hasNext()) { - var next = iter.next(); - up.push(next); - n++; - } - while (iter.hasPrev()) { - var prev = iter.prev(); - down.push(prev); - n++; - } - }); - assert.equal(n, 24); - assert.equal(up.length, 12); - assert.equal(down.length, 12); - while (up.length) { - assert.deepEqual(up.pop(), down.shift()); - } - }); - - test('iterator allows prev/next', () => { - var n = 0; - var up: ITokenInfo[] = [], down: ITokenInfo[] = []; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - while (iter.hasPrev()) { - var prev = iter.prev(); - down.push(prev); - n++; - } - while (iter.hasNext()) { - var next = iter.next(); - up.push(next); - n++; - } - }); - assert.equal(n, 24); - assert.equal(up.length, 12); - assert.equal(down.length, 12); - while (up.length) { - assert.deepEqual(up.pop(), down.shift()); - } - }); - - - test('iterator can not be used outside of callback', () => { - var illegalIterReference: TokenIterator; - tokenIterator(thisModel, new Position(3, 12), (iter) => { - illegalIterReference = iter; - }); - - - try { - illegalIterReference.hasNext(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - try { - illegalIterReference.next(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - try { - illegalIterReference.hasPrev(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - try { - illegalIterReference.prev(); - assert.ok(false); - } catch (e) { - assert.ok(true); - } - }); -}); From cc5e2fb18d6288fb70a626ea93ae150293c56fe7 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 19 Aug 2017 14:57:25 +0200 Subject: [PATCH 85/96] Handle most of the forceTokenization callers (#32508) --- src/vs/editor/common/commands/shiftCommand.ts | 40 ++++++++++--------- .../common/controller/cursorTypeOperations.ts | 15 ++++--- src/vs/editor/common/editorCommon.ts | 13 +++++- .../common/model/textModelWithTokens.ts | 15 +++++-- .../comment/common/blockCommentCommand.ts | 2 +- .../comment/common/lineCommentCommand.ts | 4 +- .../contrib/indentation/common/indentation.ts | 2 +- .../common/moveLinesCommand.ts | 1 + .../contrib/suggest/browser/suggestModel.ts | 2 +- .../test/common/controller/cursor.test.ts | 10 +++++ .../electron-browser/insertSnippet.ts | 2 +- .../electron-browser/snippetsService.ts | 2 +- 12 files changed, 70 insertions(+), 38 deletions(-) diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index 209e93d7fcb..e4074200469 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -112,28 +112,30 @@ export class ShiftCommand implements ICommand { if (contentStartVisibleColumn % tabSize !== 0) { // The current line is "miss-aligned", so let's see if this is expected... // This can only happen when it has trailing commas in the indent - let enterAction = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); - if (enterAction) { - extraSpaces = previousLineExtraSpaces; - if (enterAction.appendText) { - for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) { - if (enterAction.appendText.charCodeAt(j) === CharCode.Space) { - extraSpaces++; - } else { - break; + if (model.isCheapToTokenize(lineNumber - 1)) { + let enterAction = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); + if (enterAction) { + extraSpaces = previousLineExtraSpaces; + if (enterAction.appendText) { + for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) { + if (enterAction.appendText.charCodeAt(j) === CharCode.Space) { + extraSpaces++; + } else { + break; + } } } - } - if (enterAction.removeText) { - extraSpaces = Math.max(0, extraSpaces - enterAction.removeText); - } - - // Act as if `prefixSpaces` is not part of the indentation - for (let j = 0; j < extraSpaces; j++) { - if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== CharCode.Space) { - break; + if (enterAction.removeText) { + extraSpaces = Math.max(0, extraSpaces - enterAction.removeText); + } + + // Act as if `prefixSpaces` is not part of the indentation + for (let j = 0; j < extraSpaces; j++) { + if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== CharCode.Space) { + break; + } + indentationEndIndex--; } - indentationEndIndex--; } } } diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 40272ecf2d7..763d7731ff6 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -223,7 +223,7 @@ export class TypeOperations { let lineText = model.getLineContent(selection.startLineNumber); - if (/^\s*$/.test(lineText)) { + if (/^\s*$/.test(lineText) && model.isCheapToTokenize(selection.startLineNumber)) { let goodIndent = this._goodIndentForLine(config, model, selection.startLineNumber); goodIndent = goodIndent || '\t'; let possibleTypeText = config.normalizeIndentation(goodIndent); @@ -286,7 +286,7 @@ export class TypeOperations { } private static _enter(config: CursorConfiguration, model: ITokenizedModel, keepPosition: boolean, range: Range): ICommand { - if (model.getFirstInvalidLineNumber() < range.getStartPosition().lineNumber) { + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { let lineText = model.getLineContent(range.startLineNumber); let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); @@ -380,9 +380,8 @@ export class TypeOperations { return false; } - let firstInvalidLineNumber = model.getFirstInvalidLineNumber(); for (let i = 0, len = selections.length; i < len; i++) { - if (firstInvalidLineNumber < selections[i].getEndPosition().lineNumber) { + if (!model.isCheapToTokenize(selections[i].getEndPosition().lineNumber)) { return false; } } @@ -528,6 +527,11 @@ export class TypeOperations { } } + if (!model.isCheapToTokenize(position.lineNumber)) { + // Do not force tokenization + return false; + } + model.forceTokenization(position.lineNumber); const lineTokens = model.getLineTokens(position.lineNumber); @@ -607,8 +611,7 @@ export class TypeOperations { } private static _isTypeInterceptorElectricChar(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[]) { - let firstInvalidLineNumber = model.getFirstInvalidLineNumber(); - if (selections.length === 1 && firstInvalidLineNumber >= selections[0].getEndPosition().lineNumber) { + if (selections.length === 1 && model.isCheapToTokenize(selections[0].getEndPosition().lineNumber)) { return true; } return false; diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index cc864bcb222..b2a8bde84ba 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -820,10 +820,19 @@ export interface ITokenizedModel extends ITextModel { forceTokenization(lineNumber: number): void; /** - * Get the line number of the first line whose tokens might be inaccurate. + * If it is cheap, force tokenization information for `lineNumber` to be accurate. + * This is based on a heuristic. * @internal */ - getFirstInvalidLineNumber(): number; + tokenizeIfCheap(lineNumber: number): void; + + /** + * Check if calling `forceTokenization` for this `lineNumber` will be cheap (time-wise). + * This is based on a heuristic. + * @internal + */ + isCheapToTokenize(lineNumber: number): boolean; + /** * Get the tokens for the line `lineNumber`. * The tokens might be inaccurate. Use `forceTokenization` to ensure accurate tokens. diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts index eeeab138210..d4f31ffcc58 100644 --- a/src/vs/editor/common/model/textModelWithTokens.ts +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -169,10 +169,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke return result; } - public getFirstInvalidLineNumber(): number { - return this._invalidLineStartIndex + 1; - } - public forceTokenization(lineNumber: number): void { if (lineNumber < 1 || lineNumber > this.getLineCount()) { throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); @@ -183,6 +179,17 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke }); } + public isCheapToTokenize(lineNumber: number): boolean { + const firstInvalidLineNumber = this._invalidLineStartIndex + 1; + return (firstInvalidLineNumber >= lineNumber); + } + + public tokenizeIfCheap(lineNumber: number): void { + if (this.isCheapToTokenize(lineNumber)) { + this.forceTokenization(lineNumber); + } + } + public getLineTokens(lineNumber: number): LineTokens { if (lineNumber < 1 || lineNumber > this.getLineCount()) { throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); diff --git a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts index 4db4eff0bac..ffefd02ed8c 100644 --- a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts @@ -135,7 +135,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { var endLineNumber = this._selection.endLineNumber; var endColumn = this._selection.endColumn; - model.forceTokenization(startLineNumber); + model.tokenizeIfCheap(startLineNumber); let languageId = model.getLanguageIdAtPosition(startLineNumber, startColumn); let config = LanguageConfigurationRegistry.getComments(languageId); if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { diff --git a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts index a73a333f268..8dd881a6c40 100644 --- a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts @@ -64,7 +64,7 @@ export class LineCommentCommand implements editorCommon.ICommand { */ public static _gatherPreflightCommentStrings(model: editorCommon.ITokenizedModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] { - model.forceTokenization(startLineNumber); + model.tokenizeIfCheap(startLineNumber); const languageId = model.getLanguageIdAtPosition(startLineNumber, 1); const config = LanguageConfigurationRegistry.getComments(languageId); @@ -266,7 +266,7 @@ export class LineCommentCommand implements editorCommon.ICommand { * Given an unsuccessful analysis, delegate to the block comment command */ private _executeBlockComment(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder, s: Selection): void { - model.forceTokenization(s.startLineNumber); + model.tokenizeIfCheap(s.startLineNumber); let languageId = model.getLanguageIdAtPosition(s.startLineNumber, s.startColumn); let config = LanguageConfigurationRegistry.getComments(languageId); if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { diff --git a/src/vs/editor/contrib/indentation/common/indentation.ts b/src/vs/editor/contrib/indentation/common/indentation.ts index 9c5873fc4bc..47322484a43 100644 --- a/src/vs/editor/contrib/indentation/common/indentation.ts +++ b/src/vs/editor/contrib/indentation/common/indentation.ts @@ -423,7 +423,7 @@ export class AutoIndentOnPaste implements IEditorContribution { } const model = this.editor.getModel(); - if (model.getFirstInvalidLineNumber() < range.getStartPosition().lineNumber) { + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { return; } const { tabSize, insertSpaces } = model.getOptions(); diff --git a/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts index c829b36d87b..caf02e64b8b 100644 --- a/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts @@ -261,6 +261,7 @@ export class MoveLinesCommand implements ICommand { } let maxColumn = model.getLineMaxColumn(validPrecedingLine); + // TODO@Peng TODO@forceTokenization: getEnterAction forces tokenization let enter = LanguageConfigurationRegistry.getEnterAction(model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); if (enter) { diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index f70b0d709ab..62af9d881d1 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -293,7 +293,7 @@ export class SuggestModel implements IDisposable { } else if (quickSuggestions === true) { // all good } else { - model.forceTokenization(pos.lineNumber); + model.tokenizeIfCheap(pos.lineNumber); const { tokenType } = model .getLineTokens(pos.lineNumber) .findTokenAtOffset(pos.column - 1); diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts index c2776da191d..4b18c7cdc14 100644 --- a/src/vs/editor/test/common/controller/cursor.test.ts +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -3471,6 +3471,7 @@ suite('autoClosingPairs', () => { const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); } else { @@ -3513,6 +3514,7 @@ suite('autoClosingPairs', () => { const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); if (autoCloseColumns[column] === ColumnType.Special1) { assertType(model, cursor, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); } else if (autoCloseColumns[column] === ColumnType.Special2) { @@ -3555,42 +3557,50 @@ suite('autoClosingPairs', () => { } // First gif + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste1 = teste\' ok'); assert.equal(model.getLineContent(1), 'teste1 = teste\' ok'); cursor.setSelections('test', [new Selection(1, 1000, 1, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste2 = teste \'ok'); assert.equal(model.getLineContent(2), 'teste2 = teste \'ok\''); cursor.setSelections('test', [new Selection(2, 1000, 2, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste3 = teste" ok'); assert.equal(model.getLineContent(3), 'teste3 = teste" ok'); cursor.setSelections('test', [new Selection(3, 1000, 3, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste4 = teste "ok'); assert.equal(model.getLineContent(4), 'teste4 = teste "ok"'); // Second gif cursor.setSelections('test', [new Selection(4, 1000, 4, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste \''); assert.equal(model.getLineContent(5), 'teste \'\''); cursor.setSelections('test', [new Selection(5, 1000, 5, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste "'); assert.equal(model.getLineContent(6), 'teste ""'); cursor.setSelections('test', [new Selection(6, 1000, 6, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste\''); assert.equal(model.getLineContent(7), 'teste\''); cursor.setSelections('test', [new Selection(7, 1000, 7, 1000)]); typeCharacters(cursor, '\n'); + model.forceTokenization(model.getLineCount()); typeCharacters(cursor, 'teste"'); assert.equal(model.getLineContent(8), 'teste"'); }); diff --git a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts index 3490f5c2766..0a418364f12 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts @@ -92,7 +92,7 @@ class InsertSnippetAction extends EditorAction { if (langId) { languageId = modeService.getLanguageIdentifier(langId).id; } else { - editor.getModel().forceTokenization(lineNumber); + editor.getModel().tokenizeIfCheap(lineNumber); languageId = editor.getModel().getLanguageIdAtPosition(lineNumber, column); // validate the `languageId` to ensure this is a user diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index 7bd845976f0..0af3c024910 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -187,7 +187,7 @@ export class SnippetSuggestProvider implements ISuggestSupport { // validate the `languageId` to ensure this is a user // facing language with a name and the chance to have // snippets, else fall back to the outer language - model.forceTokenization(position.lineNumber); + model.tokenizeIfCheap(position.lineNumber); let languageId = model.getLanguageIdAtPosition(position.lineNumber, position.column); let { language } = this._modeService.getLanguageIdentifier(languageId); if (!this._modeService.getLanguageName(language)) { From 1bad719ea4d5d92bdfa8f8da3e99e1b9c525928d Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 18 Aug 2017 16:55:51 -0700 Subject: [PATCH 86/96] Float Alpha --- src/vs/base/common/color.ts | 51 ++-- src/vs/base/test/common/color.test.ts | 226 +++++++++--------- .../editor/common/view/minimapCharRenderer.ts | 2 +- .../colorPicker/browser/colorPickerWidget.ts | 4 +- .../colorPicker/common/colorFormatter.ts | 2 +- .../test/common/colorFormatter.test.ts | 4 +- .../hover/browser/modesContentHover.ts | 2 +- src/vs/platform/theme/common/colorRegistry.ts | 4 +- src/vs/workbench/api/node/extHostTypes.ts | 4 +- 9 files changed, 149 insertions(+), 150 deletions(-) diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index 37b6cc08681..217dc4b426e 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -29,15 +29,15 @@ export class RGBA { readonly b: number; /** - * Alpha: integer in [0-255] + * Alpha: float in [0-1] */ readonly a: number; - constructor(r: number, g: number, b: number, a: number = 255) { + constructor(r: number, g: number, b: number, a: number = 1) { this.r = Math.min(255, Math.max(0, r)) | 0; this.g = Math.min(255, Math.max(0, g)) | 0; this.b = Math.min(255, Math.max(0, b)) | 0; - this.a = Math.min(255, Math.max(0, a)) | 0; + this.a = roundFloat(Math.max(Math.min(1, a), 0), 3); } static equals(a: RGBA, b: RGBA): boolean { @@ -90,7 +90,7 @@ export class HSLA { const r = rgba.r / 255; const g = rgba.g / 255; const b = rgba.b / 255; - const a = rgba.a / 255; + const a = rgba.a; const max = Math.max(r, g, b); const min = Math.min(r, g, b); @@ -154,7 +154,7 @@ export class HSLA { b = HSLA._hue2rgb(p, q, h - 1 / 3); } - return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), Math.round(a * 255)); + return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a); } } @@ -214,7 +214,7 @@ export class HSVA { m = ((r - g) / delta) + 4; } - return new HSVA(m * 60, s, cmax, rgba.a / 255); + return new HSVA(m * 60, s, cmax, rgba.a); } // from http://www.rapidtables.com/convert/color/hsv-to-rgb.htm @@ -249,7 +249,7 @@ export class HSVA { g = Math.round((g + m) * 255); b = Math.round((b + m) * 255); - return new RGBA(r, g, b, Math.round(a * 255)); + return new RGBA(r, g, b, a); } } @@ -349,7 +349,7 @@ export class Color { transparent(factor: number): Color { const { r, g, b, a } = this.rgba; - return new Color(new RGBA(r, g, b, Math.round(a * factor))); + return new Color(new RGBA(r, g, b, a * factor)); } isTransparent(): boolean { @@ -357,7 +357,7 @@ export class Color { } isOpaque(): boolean { - return this.rgba.a === 255; + return this.rgba.a === 1; } opposite(): Color { @@ -368,8 +368,8 @@ export class Color { const rgba = c.rgba; // Convert to 0..1 opacity - const thisA = this.rgba.a / 255; - const colorA = rgba.a / 255; + const thisA = this.rgba.a; + const colorA = rgba.a; let a = thisA + colorA * (1 - thisA); if (a < 1.0e-6) { @@ -379,7 +379,6 @@ export class Color { const r = this.rgba.r * thisA / a + rgba.r * colorA * (1 - thisA) / a; const g = this.rgba.g * thisA / a + rgba.g * colorA * (1 - thisA) / a; const b = this.rgba.b * thisA / a + rgba.b * colorA * (1 - thisA) / a; - a *= 255; return new Color(new RGBA(r, g, b, a)); } @@ -410,13 +409,13 @@ export class Color { return of.darken(factor); } - static readonly white = new Color(new RGBA(255, 255, 255, 255)); - static readonly black = new Color(new RGBA(0, 0, 0, 255)); - static readonly red = new Color(new RGBA(255, 0, 0, 255)); - static readonly blue = new Color(new RGBA(0, 0, 255, 255)); - static readonly green = new Color(new RGBA(0, 255, 0, 255)); - static readonly cyan = new Color(new RGBA(0, 255, 255, 255)); - static readonly lightgrey = new Color(new RGBA(211, 211, 211, 255)); + static readonly white = new Color(new RGBA(255, 255, 255, 1)); + static readonly black = new Color(new RGBA(0, 0, 0, 1)); + static readonly red = new Color(new RGBA(255, 0, 0, 1)); + static readonly blue = new Color(new RGBA(0, 0, 255, 1)); + static readonly green = new Color(new RGBA(0, 255, 0, 1)); + static readonly cyan = new Color(new RGBA(0, 255, 255, 1)); + static readonly lightgrey = new Color(new RGBA(211, 211, 211, 1)); static readonly transparent = new Color(new RGBA(0, 0, 0, 0)); } @@ -425,7 +424,7 @@ export namespace Color { export namespace CSS { export function formatRGB(color: Color): string { - if (color.rgba.a === 255) { + if (color.rgba.a === 1) { return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`; } @@ -433,7 +432,7 @@ export namespace Color { } export function formatRGBA(color: Color): string { - return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a / 255).toFixed(2)})`; + return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`; } export function formatHSL(color: Color): string { @@ -465,11 +464,11 @@ export namespace Color { * If 'compact' is set, colors without transparancy will be printed as #RRGGBB */ export function formatHexA(color: Color, compact = false): string { - if (compact && color.rgba.a === 0xFF) { + if (compact && color.rgba.a === 1) { return Color.Format.CSS.formatHex(color); } - return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(color.rgba.a)}`; + return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(Math.round(color.rgba.a * 255))}`; } /** @@ -515,7 +514,7 @@ export namespace Color { const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2)); const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4)); const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6)); - return new Color(new RGBA(r, g, b, 255)); + return new Color(new RGBA(r, g, b, 1)); } if (length === 9) { @@ -524,7 +523,7 @@ export namespace Color { const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4)); const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6)); const a = 16 * _parseHexDigit(hex.charCodeAt(7)) + _parseHexDigit(hex.charCodeAt(8)); - return new Color(new RGBA(r, g, b, a)); + return new Color(new RGBA(r, g, b, a / 255)); } if (length === 4) { @@ -541,7 +540,7 @@ export namespace Color { const g = _parseHexDigit(hex.charCodeAt(2)); const b = _parseHexDigit(hex.charCodeAt(3)); const a = _parseHexDigit(hex.charCodeAt(4)); - return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b, 16 * a + a)); + return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b, (16 * a + a) / 255)); } // Invalid color diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts index bf926d4db7a..a76c98a0333 100644 --- a/src/vs/base/test/common/color.test.ts +++ b/src/vs/base/test/common/color.test.ts @@ -51,133 +51,133 @@ suite('Color', () => { }); test('luminance', function () { - assert.deepEqual(0, new Color(new RGBA(0, 0, 0, 255)).getRelativeLuminance()); - assert.deepEqual(1, new Color(new RGBA(255, 255, 255, 255)).getRelativeLuminance()); + assert.deepEqual(0, new Color(new RGBA(0, 0, 0, 1)).getRelativeLuminance()); + assert.deepEqual(1, new Color(new RGBA(255, 255, 255, 1)).getRelativeLuminance()); - assert.deepEqual(0.2126, new Color(new RGBA(255, 0, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.7152, new Color(new RGBA(0, 255, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.0722, new Color(new RGBA(0, 0, 255, 255)).getRelativeLuminance()); + assert.deepEqual(0.2126, new Color(new RGBA(255, 0, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.7152, new Color(new RGBA(0, 255, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.0722, new Color(new RGBA(0, 0, 255, 1)).getRelativeLuminance()); - assert.deepEqual(0.9278, new Color(new RGBA(255, 255, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.7874, new Color(new RGBA(0, 255, 255, 255)).getRelativeLuminance()); - assert.deepEqual(0.2848, new Color(new RGBA(255, 0, 255, 255)).getRelativeLuminance()); + assert.deepEqual(0.9278, new Color(new RGBA(255, 255, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.7874, new Color(new RGBA(0, 255, 255, 1)).getRelativeLuminance()); + assert.deepEqual(0.2848, new Color(new RGBA(255, 0, 255, 1)).getRelativeLuminance()); - assert.deepEqual(0.5271, new Color(new RGBA(192, 192, 192, 255)).getRelativeLuminance()); + assert.deepEqual(0.5271, new Color(new RGBA(192, 192, 192, 1)).getRelativeLuminance()); - assert.deepEqual(0.2159, new Color(new RGBA(128, 128, 128, 255)).getRelativeLuminance()); - assert.deepEqual(0.0459, new Color(new RGBA(128, 0, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.2003, new Color(new RGBA(128, 128, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.1544, new Color(new RGBA(0, 128, 0, 255)).getRelativeLuminance()); - assert.deepEqual(0.0615, new Color(new RGBA(128, 0, 128, 255)).getRelativeLuminance()); - assert.deepEqual(0.17, new Color(new RGBA(0, 128, 128, 255)).getRelativeLuminance()); - assert.deepEqual(0.0156, new Color(new RGBA(0, 0, 128, 255)).getRelativeLuminance()); + assert.deepEqual(0.2159, new Color(new RGBA(128, 128, 128, 1)).getRelativeLuminance()); + assert.deepEqual(0.0459, new Color(new RGBA(128, 0, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.2003, new Color(new RGBA(128, 128, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.1544, new Color(new RGBA(0, 128, 0, 1)).getRelativeLuminance()); + assert.deepEqual(0.0615, new Color(new RGBA(128, 0, 128, 1)).getRelativeLuminance()); + assert.deepEqual(0.17, new Color(new RGBA(0, 128, 128, 1)).getRelativeLuminance()); + assert.deepEqual(0.0156, new Color(new RGBA(0, 0, 128, 1)).getRelativeLuminance()); }); test('blending', function () { assert.deepEqual(new Color(new RGBA(0, 0, 0, 0)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(243, 34, 43))); assert.deepEqual(new Color(new RGBA(255, 255, 255)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(255, 255, 255))); - assert.deepEqual(new Color(new RGBA(122, 122, 122, 178.5)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(158, 95, 98))); - assert.deepEqual(new Color(new RGBA(0, 0, 0, 147.9)).blend(new Color(new RGBA(255, 255, 255, 84.15))), new Color(new RGBA(49, 49, 49, 182))); + assert.deepEqual(new Color(new RGBA(122, 122, 122, 0.7)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(158, 95, 98))); + assert.deepEqual(new Color(new RGBA(0, 0, 0, 0.58)).blend(new Color(new RGBA(255, 255, 255, 0.33))), new Color(new RGBA(49, 49, 49, 0.719))); }); suite('HSLA', () => { test('HSLA.toRGBA', function () { assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 0)), new RGBA(0, 0, 0, 0)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 1)), new RGBA(0, 0, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 1, 1)), new RGBA(255, 255, 255, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 1)), new RGBA(0, 0, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 1, 1)), new RGBA(255, 255, 255, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.5, 1)), new RGBA(255, 0, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.5, 1)), new RGBA(0, 255, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.5, 1)), new RGBA(0, 0, 255, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.5, 1)), new RGBA(255, 0, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.5, 1)), new RGBA(0, 255, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.5, 1)), new RGBA(0, 0, 255, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.5, 1)), new RGBA(255, 255, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.5, 1)), new RGBA(0, 255, 255, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.5, 1)), new RGBA(255, 0, 255, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.5, 1)), new RGBA(255, 255, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.5, 1)), new RGBA(0, 255, 255, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.5, 1)), new RGBA(255, 0, 255, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 1)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.251, 1)), new RGBA(128, 0, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.251, 1)), new RGBA(128, 128, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.251, 1)), new RGBA(0, 128, 0, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.251, 1)), new RGBA(128, 0, 128, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.251, 1)), new RGBA(0, 128, 128, 255)); - assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.251, 1)), new RGBA(0, 0, 128, 255)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.251, 1)), new RGBA(128, 0, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.251, 1)), new RGBA(128, 128, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.251, 1)), new RGBA(0, 128, 0, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.251, 1)), new RGBA(128, 0, 128, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.251, 1)), new RGBA(0, 128, 128, 1)); + assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.251, 1)), new RGBA(0, 0, 128, 1)); }); test('HSLA.fromRGBA', function () { assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 0)), new HSLA(0, 0, 0, 0)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 255)), new HSLA(0, 0, 0, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 255, 255)), new HSLA(0, 0, 1, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 1)), new HSLA(0, 0, 0, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 255, 1)), new HSLA(0, 0, 1, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 0, 255)), new HSLA(0, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 0, 255)), new HSLA(120, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 255, 255)), new HSLA(240, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 0, 1)), new HSLA(0, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 0, 1)), new HSLA(120, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 255, 1)), new HSLA(240, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 0, 255)), new HSLA(60, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 255, 255)), new HSLA(180, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 255, 255)), new HSLA(300, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 0, 1)), new HSLA(60, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 255, 1)), new HSLA(180, 1, 0.5, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 255, 1)), new HSLA(300, 1, 0.5, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(192, 192, 192, 255)), new HSLA(0, 0, 0.753, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(192, 192, 192, 1)), new HSLA(0, 0, 0.753, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 128, 255)), new HSLA(0, 0, 0.502, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 0, 255)), new HSLA(0, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 0, 255)), new HSLA(60, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 0, 255)), new HSLA(120, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 128, 255)), new HSLA(300, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 128, 255)), new HSLA(180, 1, 0.251, 1)); - assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 128, 255)), new HSLA(240, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 128, 1)), new HSLA(0, 0, 0.502, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 0, 1)), new HSLA(0, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 0, 1)), new HSLA(60, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 0, 1)), new HSLA(120, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 128, 1)), new HSLA(300, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 128, 1)), new HSLA(180, 1, 0.251, 1)); + assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 128, 1)), new HSLA(240, 1, 0.251, 1)); }); }); suite('HSVA', () => { test('HSVA.toRGBA', function () { assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 0)), new RGBA(0, 0, 0, 0)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 1)), new RGBA(0, 0, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 1, 1)), new RGBA(255, 255, 255, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 1)), new RGBA(0, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 1, 1)), new RGBA(255, 255, 255, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 1, 1)), new RGBA(255, 0, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 1, 1)), new RGBA(0, 255, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 1, 1)), new RGBA(0, 0, 255, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 1, 1)), new RGBA(255, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 1, 1)), new RGBA(0, 255, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 1, 1)), new RGBA(0, 0, 255, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 1, 1)), new RGBA(255, 255, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 1, 1)), new RGBA(0, 255, 255, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 1, 1)), new RGBA(255, 0, 255, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 1, 1)), new RGBA(255, 255, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 1, 1)), new RGBA(0, 255, 255, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 1, 1)), new RGBA(255, 0, 255, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 1)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 0.502, 1)), new RGBA(128, 0, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 0.502, 1)), new RGBA(128, 128, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 0.502, 1)), new RGBA(0, 128, 0, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 0.502, 1)), new RGBA(128, 0, 128, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 0.502, 1)), new RGBA(0, 128, 128, 255)); - assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 0.502, 1)), new RGBA(0, 0, 128, 255)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 0.502, 1)), new RGBA(128, 0, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 0.502, 1)), new RGBA(128, 128, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 0.502, 1)), new RGBA(0, 128, 0, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 0.502, 1)), new RGBA(128, 0, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 0.502, 1)), new RGBA(0, 128, 128, 1)); + assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 0.502, 1)), new RGBA(0, 0, 128, 1)); }); test('HSVA.fromRGBA', () => { assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 0)), new HSVA(0, 0, 0, 0)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 255)), new HSVA(0, 0, 0, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 255, 255)), new HSVA(0, 0, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 1)), new HSVA(0, 0, 0, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 255, 1)), new HSVA(0, 0, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 0, 255)), new HSVA(0, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 0, 255)), new HSVA(120, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 255, 255)), new HSVA(240, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 0, 1)), new HSVA(0, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 0, 1)), new HSVA(120, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 255, 1)), new HSVA(240, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 0, 255)), new HSVA(60, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 255, 255)), new HSVA(180, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 255, 255)), new HSVA(300, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 0, 1)), new HSVA(60, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 255, 1)), new HSVA(180, 1, 1, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 255, 1)), new HSVA(300, 1, 1, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(192, 192, 192, 255)), new HSVA(0, 0, 0.753, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(192, 192, 192, 1)), new HSVA(0, 0, 0.753, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 128, 255)), new HSVA(0, 0, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 0, 255)), new HSVA(0, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 0, 255)), new HSVA(60, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 0, 255)), new HSVA(120, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 128, 255)), new HSVA(300, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 128, 255)), new HSVA(180, 1, 0.502, 1)); - assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 128, 255)), new HSVA(240, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 128, 1)), new HSVA(0, 0, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 0, 1)), new HSVA(0, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 0, 1)), new HSVA(60, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 0, 1)), new HSVA(120, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 128, 1)), new HSVA(300, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 128, 1)), new HSVA(180, 1, 0.502, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 128, 1)), new HSVA(240, 1, 0.502, 1)); }); }); @@ -192,44 +192,44 @@ suite('Color', () => { assert.deepEqual(Color.Format.CSS.parseHex('#0102030'), null); // somewhat valid - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0').rgba, new RGBA(255, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0').rgba, new RGBA(255, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00').rgba, new RGBA(15, 255, 0, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0').rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0').rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00').rgba, new RGBA(15, 255, 0, 1)); // valid - assert.deepEqual(Color.Format.CSS.parseHex('#000000').rgba, new RGBA(0, 0, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF').rgba, new RGBA(255, 255, 255, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#000000').rgba, new RGBA(0, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF').rgba, new RGBA(255, 255, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FF0000').rgba, new RGBA(255, 0, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FF00').rgba, new RGBA(0, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0000FF').rgba, new RGBA(0, 0, 255, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#FF0000').rgba, new RGBA(255, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FF00').rgba, new RGBA(0, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0000FF').rgba, new RGBA(0, 0, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00').rgba, new RGBA(255, 255, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF').rgba, new RGBA(0, 255, 255, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF').rgba, new RGBA(255, 0, 255, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00').rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF').rgba, new RGBA(0, 255, 255, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF').rgba, new RGBA(255, 0, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0').rgba, new RGBA(192, 192, 192, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0').rgba, new RGBA(192, 192, 192, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#808080').rgba, new RGBA(128, 128, 128, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#800000').rgba, new RGBA(128, 0, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#808000').rgba, new RGBA(128, 128, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#008000').rgba, new RGBA(0, 128, 0, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#800080').rgba, new RGBA(128, 0, 128, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#008080').rgba, new RGBA(0, 128, 128, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#000080').rgba, new RGBA(0, 0, 128, 255)); + assert.deepEqual(Color.Format.CSS.parseHex('#808080').rgba, new RGBA(128, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800000').rgba, new RGBA(128, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#808000').rgba, new RGBA(128, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008000').rgba, new RGBA(0, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800080').rgba, new RGBA(128, 0, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008080').rgba, new RGBA(0, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#000080').rgba, new RGBA(0, 0, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#010203').rgba, new RGBA(1, 2, 3, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#040506').rgba, new RGBA(4, 5, 6, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#070809').rgba, new RGBA(7, 8, 9, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a').rgba, new RGBA(10, 10, 10, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b').rgba, new RGBA(11, 11, 11, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c').rgba, new RGBA(12, 12, 12, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d').rgba, new RGBA(13, 13, 13, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e').rgba, new RGBA(14, 14, 14, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f').rgba, new RGBA(15, 15, 15, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0').rgba, new RGBA(160, 160, 160, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA').rgba, new RGBA(204, 255, 170, 255)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA8').rgba, new RGBA(204, 255, 170, 136)); + assert.deepEqual(Color.Format.CSS.parseHex('#010203').rgba, new RGBA(1, 2, 3, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#040506').rgba, new RGBA(4, 5, 6, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#070809').rgba, new RGBA(7, 8, 9, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a').rgba, new RGBA(10, 10, 10, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b').rgba, new RGBA(11, 11, 11, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c').rgba, new RGBA(12, 12, 12, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d').rgba, new RGBA(13, 13, 13, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e').rgba, new RGBA(14, 14, 14, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f').rgba, new RGBA(15, 15, 15, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0').rgba, new RGBA(160, 160, 160, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA').rgba, new RGBA(204, 255, 170, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA8').rgba, new RGBA(204, 255, 170, 0.533)); }); }); }); diff --git a/src/vs/editor/common/view/minimapCharRenderer.ts b/src/vs/editor/common/view/minimapCharRenderer.ts index 9de30a36bf9..6a9fcdf2042 100644 --- a/src/vs/editor/common/view/minimapCharRenderer.ts +++ b/src/vs/editor/common/view/minimapCharRenderer.ts @@ -43,7 +43,7 @@ export class MinimapTokensColorTracker { for (let colorId = 1; colorId < colorMap.length; colorId++) { const source = colorMap[colorId].rgba; // Use a VM friendly data-type - this._colors[colorId] = new RGBA8(source.r, source.g, source.b, source.a); + this._colors[colorId] = new RGBA8(source.r, source.g, source.b, Math.round(source.a * 255)); } let backgroundLuminosity = colorMap[ColorId.DefaultBackground].getRelativeLuminance(); this._backgroundIsLight = (backgroundLuminosity >= 0.5); diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index c933c2d7916..2474d37d322 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -47,7 +47,7 @@ export class ColorPickerHeader extends Disposable { private onDidChangeColor(color: Color): void { this.pickedColorNode.style.backgroundColor = Color.Format.CSS.format(color); - dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 128 ? this.backgroundColor.isLighter() : color.isLighter()); + dom.toggleClass(this.pickedColorNode, 'light', color.rgba.a < 0.5 ? this.backgroundColor.isLighter() : color.isLighter()); this.onDidChangeFormatter(); } @@ -277,7 +277,7 @@ class OpacityStrip extends Strip { private onDidChangeColor(color: Color): void { const { r, g, b } = color.rgba; - const opaque = new Color(new RGBA(r, g, b, 255)); + const opaque = new Color(new RGBA(r, g, b, 1)); const transparent = new Color(new RGBA(r, g, b, 0)); this.overlay.style.background = `linear-gradient(to bottom, ${opaque} 0%, ${transparent} 100%)`; diff --git a/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts index b9cf5912404..91c6df8f07d 100644 --- a/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts +++ b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts @@ -32,7 +32,7 @@ function getPropertyValue(color: Color, variable: string): number | undefined { case 'blue': return color.rgba.b / 255; case 'alpha': - return color.rgba.a / 255; + return color.rgba.a; case 'hue': return color.hsla.h / 360; case 'saturation': diff --git a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts index b3ab48b1d17..4f9b83e119d 100644 --- a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts +++ b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts @@ -55,12 +55,12 @@ suite('ColorFormatter', () => { assert.equal(rgb.format(color), 'rgb(255, 127, 0)'); const rgba = new ColorFormatter('rgba({red:d[0-255]}, {green:d[0-255]}, {blue:d[0-255]}, {alpha})'); - assert.equal(rgba.format(color), 'rgba(255, 127, 0, 1)'); + assert.equal(rgba.format(color), 'rgba(255, 127, 0, 0)'); const hex = new ColorFormatter('#{red:X}{green:X}{blue:X}'); assert.equal(hex.format(color), '#FF7F00'); const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); - assert.equal(hsla.format(color), 'hsla(30, 100%, 50%, 1)'); + assert.equal(hsla.format(color), 'hsla(30, 100%, 50%, 0)'); }); }); \ No newline at end of file diff --git a/src/vs/editor/contrib/hover/browser/modesContentHover.ts b/src/vs/editor/contrib/hover/browser/modesContentHover.ts index 7030def9416..323f55d5fc6 100644 --- a/src/vs/editor/contrib/hover/browser/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/browser/modesContentHover.ts @@ -338,7 +338,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { }); } else { const { red, green, blue, alpha } = msg.color; - const rgba = new RGBA(red * 255, green * 255, blue * 255, alpha * 255); + const rgba = new RGBA(red * 255, green * 255, blue * 255, alpha); const color = new Color(rgba); const formatters = [...msg.formatters]; diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index cd8ce27509c..2ac3d924e5c 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -264,8 +264,8 @@ export const editorActiveLinkForeground = registerColor('editorLink.activeForegr /** * Diff Editor Colors */ -export const defaultInsertColor = new Color(new RGBA(155, 185, 85, 255 * 0.2)); -export const defaultRemoveColor = new Color(new RGBA(255, 0, 0, 255 * 0.2)); +export const defaultInsertColor = new Color(new RGBA(155, 185, 85, 0.2)); +export const defaultRemoveColor = new Color(new RGBA(255, 0, 0, 0.2)); export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: defaultInsertColor, light: defaultInsertColor, hc: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted.')); export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: defaultRemoveColor, light: defaultRemoveColor, hc: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed.')); diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 994bcb27dc5..d154a8be24d 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1029,14 +1029,14 @@ export class Color { static fromHSLA(hue: number, saturation: number, luminance: number, alpha: number): Color { const color = new BaseColor(new HSLA(hue, saturation, luminance, alpha)).rgba; - return new Color(color.r, color.g, color.b, color.a / 255); + return new Color(color.r, color.g, color.b, color.a); } static fromHex(hex: string): Color | null { let baseColor = BaseColor.Format.CSS.parseHex(hex); if (baseColor) { const rgba = baseColor.rgba; - return new Color(rgba.r, rgba.g, rgba.b, rgba.a / 255); + return new Color(rgba.r, rgba.g, rgba.b, rgba.a); } return null; } From 1c20b3ce3916b289bf027b0820ad9928bdac6aac Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 18 Aug 2017 23:07:52 -0700 Subject: [PATCH 87/96] Fix #32323. Colors lose precision --- src/vs/editor/contrib/colorPicker/common/colorFormatter.ts | 2 +- .../contrib/colorPicker/test/common/colorFormatter.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts index 91c6df8f07d..337262b8436 100644 --- a/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts +++ b/src/vs/editor/contrib/colorPicker/common/colorFormatter.ts @@ -56,7 +56,7 @@ function createPropertyNode(variable: string, fractionDigits: number, type: stri min = typeof min === 'number' ? min : 0; max = typeof max === 'number' ? max : 255; - return (normalize(value, min, max) | 0).toString(); + return (normalize(value, min, max).toFixed(0)).toString(); } else if (type === 'x' || type === 'X') { min = typeof min === 'number' ? min : 0; max = typeof max === 'number' ? max : 255; diff --git a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts index 4f9b83e119d..b3ab48b1d17 100644 --- a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts +++ b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts @@ -55,12 +55,12 @@ suite('ColorFormatter', () => { assert.equal(rgb.format(color), 'rgb(255, 127, 0)'); const rgba = new ColorFormatter('rgba({red:d[0-255]}, {green:d[0-255]}, {blue:d[0-255]}, {alpha})'); - assert.equal(rgba.format(color), 'rgba(255, 127, 0, 0)'); + assert.equal(rgba.format(color), 'rgba(255, 127, 0, 1)'); const hex = new ColorFormatter('#{red:X}{green:X}{blue:X}'); assert.equal(hex.format(color), '#FF7F00'); const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); - assert.equal(hsla.format(color), 'hsla(30, 100%, 50%, 0)'); + assert.equal(hsla.format(color), 'hsla(30, 100%, 50%, 1)'); }); }); \ No newline at end of file From f90f8aea431279a6f713f54c051cb4b73a4ccf2c Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 18 Aug 2017 23:12:47 -0700 Subject: [PATCH 88/96] #32323, test case for formatter. --- .../colorPicker/test/common/colorFormatter.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts index b3ab48b1d17..361c23262ac 100644 --- a/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts +++ b/src/vs/editor/contrib/colorPicker/test/common/colorFormatter.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { Color, RGBA } from 'vs/base/common/color'; +import { Color, RGBA, HSLA } from 'vs/base/common/color'; import { ColorFormatter } from 'vs/editor/contrib/colorPicker/common/colorFormatter'; suite('ColorFormatter', () => { @@ -63,4 +63,12 @@ suite('ColorFormatter', () => { const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); assert.equal(hsla.format(color), 'hsla(30, 100%, 50%, 1)'); }); + + test('bug#32323', () => { + const color = new Color(new HSLA(121, 0.45, 0.29, 0.61)); + const rgba = color.rgba; + const color2 = new Color(new RGBA(rgba.r, rgba.g, rgba.b, rgba.a)); + const hsla = new ColorFormatter('hsla({hue:d[0-360]}, {saturation:d[0-100]}%, {luminance:d[0-100]}%, {alpha})'); + assert.equal(hsla.format(color2), 'hsla(121, 45%, 29%, 0.61)'); + }); }); \ No newline at end of file From 791f59fbaf9d6a8e7db42ad650fa2b4772a1bef7 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sat, 19 Aug 2017 12:31:06 -0700 Subject: [PATCH 89/96] Fix #32508. Tokenize tokens when cheap --- .../linesOperations/common/moveLinesCommand.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts index caf02e64b8b..8def37367b3 100644 --- a/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/common/moveLinesCommand.ts @@ -98,7 +98,7 @@ export class MoveLinesCommand implements ICommand { let insertingText = movingLineText; - if (this.isAutoIndent(model, s)) { + if (this.shouldAutoIndent(model, s)) { let movingLineMatchResult = this.matchEnterRule(model, indentConverter, tabSize, movingLineNumber, s.startLineNumber - 1); // if s.startLineNumber - 1 matches onEnter rule, we still honor that. if (movingLineMatchResult !== null) { @@ -178,7 +178,7 @@ export class MoveLinesCommand implements ICommand { // Insert line that needs to be moved after builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + movingLineText); - if (this.isAutoIndent(model, s)) { + if (this.shouldAutoIndent(model, s)) { virtualModel.getLineContent = (lineNumber: number) => { if (lineNumber === movingLineNumber) { return model.getLineContent(s.startLineNumber); @@ -261,7 +261,6 @@ export class MoveLinesCommand implements ICommand { } let maxColumn = model.getLineMaxColumn(validPrecedingLine); - // TODO@Peng TODO@forceTokenization: getEnterAction forces tokenization let enter = LanguageConfigurationRegistry.getEnterAction(model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); if (enter) { @@ -298,10 +297,14 @@ export class MoveLinesCommand implements ICommand { return str.replace(/^\s+/, ''); } - private isAutoIndent(model: ITokenizedModel, selection: Selection) { + private shouldAutoIndent(model: ITokenizedModel, selection: Selection) { if (!this._autoIndent) { return false; } + // if it's not easy to tokenize, we stop auto indent. + if (!model.isCheapToTokenize(selection.startLineNumber)) { + return false; + } let languageAtSelectionStart = model.getLanguageIdAtPosition(selection.startLineNumber, 1); let languageAtSelectionEnd = model.getLanguageIdAtPosition(selection.endLineNumber, 1); From 40bd5d9859f9f2e33554cb5c3895978c50c6ae94 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sat, 19 Aug 2017 18:10:01 -0700 Subject: [PATCH 90/96] Continue emmet expand even if parsing doc fails --- extensions/emmet/src/abbreviationActions.ts | 5 +---- extensions/emmet/src/util.ts | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 399d3807833..624a486729c 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -95,10 +95,7 @@ export function expandEmmetAbbreviation(args): Thenable { const editor = vscode.window.activeTextEditor; - let rootNode = parseDocument(editor.document); - if (!rootNode) { - return fallbackTab(); - } + let rootNode = parseDocument(editor.document, false); // When tabbed on a non empty selection, do not treat it as an emmet abbreviation, and fallback to tab instead if (vscode.workspace.getConfiguration('emmet')['triggerExpansionOnTab'] === true && editor.selections.find(x => !x.isEmpty)) { diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 3eee4a0dba9..4446a73bea4 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -83,6 +83,10 @@ export function parseDocument(document: vscode.TextDocument, showError: boolean * @param includeNodeBoundary */ export function getNode(root: Node, position: vscode.Position, includeNodeBoundary: boolean = false) { + if (!root) { + return null; + } + let currentNode = root.firstChild; let foundNode: Node = null; From 4c0b3828c6d3a305e72fc0b8a6c5bff4e2f951ee Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sun, 20 Aug 2017 11:04:35 -0700 Subject: [PATCH 91/96] Update Interactive playground for emmet #32850 --- .../electron-browser/editor/vs_code_editor_walkthrough.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md index f154fd91f57..1edc236faf6 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/editor/vs_code_editor_walkthrough.md @@ -144,13 +144,13 @@ You can greatly accelerate your editing through the use of snippets. Simply sta ### Emmet -Emmet takes the snippets idea to a whole new level: you can type CSS-like expressions that can be dynamically parsed, and produce output depending on what you type in the abbreviation. To use Emmet simply press tab after a valid piece for Emmet syntax and the expansion will occur. Try it by pressing tab after `ul>li.item$*5` to see Emmet in action. +Emmet takes the snippets idea to a whole new level: you can type CSS-like expressions that can be dynamically parsed, and produce output depending on what you type in the abbreviation. To use Emmet simply run the command `Emmet: Expand Abbreviation` with cursor at the end of a valid Emmet abbreviation or snippet and the expansion will occur. ```html ul>li.item$*5 ``` ->**Tip:** The [Emmet cheat sheet](http://docs.emmet.io/cheat-sheet/) is a great source of Emmet syntax suggestions. Emmet can also be enabled for additional languages via the `emmet.syntaxProfiles` [setting](command:workbench.action.openGlobalSettings). +>**Tip:** The [Emmet cheat sheet](http://docs.emmet.io/cheat-sheet/) is a great source of Emmet syntax suggestions. To expand Emmet abbreviations and snippets using the `tab` key use the `emmet.triggerExpansionOnTab` [setting](command:workbench.action.openGlobalSettings). Check out the docs on [Emmet in VS Code](https://code.visualstudio.com/docs/editor/emmet) to learn more. From 0e000d784b164085e2d7d7d2a129e338048c9e09 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 20 Aug 2017 10:44:03 -0700 Subject: [PATCH 92/96] Uplevel xterm.js --- npm-shrinkwrap.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index b5f26d54ad9..32ae19faf07 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -569,7 +569,7 @@ "xterm": { "version": "2.9.1", "from": "Tyriar/xterm.js#vscode-release/1.16", - "resolved": "git+https://github.com/Tyriar/xterm.js.git#74fde417c97962730a5fbbc8e8e7dcd47e1b897b" + "resolved": "git+https://github.com/Tyriar/xterm.js.git#cdf3177fd735ab64403a34778d69daa833e539a5" }, "yauzl": { "version": "2.8.0", From 34c6e0a7ca6107002c3939e65a9ff6850f2b5caf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 20 Aug 2017 11:58:53 -0700 Subject: [PATCH 93/96] Integrate official xterm.js types --- src/typings/xterm.d.ts | 588 +++++++++++++----- .../electron-browser/terminalInstance.ts | 2 +- 2 files changed, 425 insertions(+), 165 deletions(-) diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts index e7e0917e2e0..f182fce6bdb 100644 --- a/src/typings/xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -1,195 +1,455 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ +/** + * @license MIT + * + * This contains the type declarations for the xterm.js library. Note that + * some interfaces differ between this file and the actual implementation in + * src/, that's because this file declares the *public* API which is intended + * to be stable and consumed by external programs. + */ -declare module 'xterm' { - type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void; +/** + * An object containing start up options for the terminal. + */ +interface ITerminalOptions { + /** + * A data uri of the sound to use for the bell (needs bellStyle = 'sound'). + */ + bellSound?: string; + /** + * The type of the bell notification the terminal will use. + */ + bellStyle?: 'none' | 'visual' | 'sound' | 'both'; + + /** + * The number of columns in the terminal. + */ + cols?: number; + + /** + * Whether the cursor blinks. + */ + cursorBlink?: boolean; + + /** + * The style of the cursor. + */ + cursorStyle?: 'block' | 'underline' | 'bar'; + + /** + * Whether input should be disabled. + */ + disableStdin?: boolean; + + /** + * The number of rows in the terminal. + */ + rows?: number; + + /** + * The amount of scrollback in the terminal. Scrollback is the amount of rows + * that are retained when lines are scrolled beyond the initial viewport. + */ + scrollback?: number; + + /** + * The size of tab stops in the terminal. + */ + tabStopWidth?: number; + } + + /** + * An object containing options for a link matcher. + */ + interface ILinkMatcherOptions { + /** + * The index of the link from the regex.match(text) call. This defaults to 0 + * (for regular expressions without capture groups). + */ + matchIndex?: number; + + /** + * A callback that validates an individual link, returning true if valid and + * false if invalid. + */ + validationCallback?: (uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => void; + + /** + * The priority of the link matcher, this defines the order in which the link + * matcher is evaluated relative to others, from highest to lowest. The + * default value is 0. + */ + priority?: number; + } + + declare module 'xterm' { + /** + * The class that represents an xterm.js terminal. + */ export class Terminal { - cols: number; - rows: number; - ydisp: number; - element: HTMLElement; - textarea: HTMLTextAreaElement; + /** + * The element containing the terminal. + */ + element: HTMLElement; - /** - * Creates a new `Terminal` object. - * - * @param {object} options An object containing a set of options. - */ - constructor(options?: any); + /** + * The textarea that accepts input for the terminal. + */ + textarea: HTMLTextAreaElement; - /** - * Registers an event listener. - * @param eventName The name of the event. - * @param callback The callback. - */ - on(eventName: string, callback: (data: any) => void): void; + /** + * The number of rows in the terminal's viewport. + */ + rows: number; - /** - * Resizes the terminal. - * - * @param x The number of columns to resize to. - * @param y The number of rows to resize to. - */ - resize(columns: number, rows: number): void; + /** + * The number of columns in the terminal's viewport. + */ + cols: number; - /** - * Emits an event. - * @param eventName The name of the event. - * @param data The data attached to the event. - */ - emit(eventName: string, data: any): void; + /** + * Creates a new `Terminal` object. + * + * @param options An object containing a set of options. + */ + constructor(options?: ITerminalOptions); - /** - * Writes text to the terminal, followed by a break line character (\n). - * @param data The text to write to the terminal. - */ - writeln(data: string): void; + /** + * Unfocus the terminal. + */ + blur(): void; - /** - * Opens the terminal within an element. - * @param parent The element to create the terminal within. - * @param focus Focus the terminal, after it gets instantiated in the - * DOM. - */ - open(parent: HTMLElement, focus: boolean): void; + /** + * Focus the terminal. + */ + focus(): void; - /** - * Attaches a custom key event handler which is run before keys are - * processed, giving consumers of xterm.js ultimate control as to what - * keys should be processed by the terminal and what keys should not. - * @param customKeyEventHandler The custom KeyboardEvent handler to - * attach. This is a function that takes a KeyboardEvent, allowing - * consumers to stop propogation and/or prevent the default action. The - * function returns whether the event should be processed by xterm.js. - */ - attachCustomKeyEventHandler(customKeyEventHandler: (...any) => boolean); + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'blur' | 'focus' | 'lineFeed', listener: () => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'data', listener: (data?: string) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'key', listener: (key?: string, event?: KeyboardEvent) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'keypress' | 'keydown', listener: (event?: KeyboardEvent) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'refresh', listener: (data?: {start: number, end: number}) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'resize', listener: (data?: {cols: number, rows: number}) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'scroll', listener: (ydisp?: number) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: 'title', listener: (title?: string) => void): void; + /** + * Registers an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + on(type: string, listener: (...args: any[]) => void): void; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: string): any; + /** + * Deregisters an event listener. + * @param type The type of the event. + * @param listener The listener. + */ + off(type: 'blur' | 'focus' | 'lineFeed' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; - /** - * Registers a link matcher, allowing custom link patterns to be matched and - * handled. - * @param {RegExp} regex The regular expression to search for, specifically - * this searches the textContent of the rows. You will want to use \s to match - * a space ' ' character for example. - * @param {LinkMatcherHandler} handler The callback when the link is called. - * @param {LinkMatcherOptions} [options] Options for the link matcher. - * @return {number} The ID of the new matcher, this can be used to deregister. - */ - registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler , options?: any); + /** + * Resizes the terminal. + * @param x The number of columns to resize to. + * @param y The number of rows to resize to. + */ + resize(columns: number, rows: number): void; - /** - * Deregisters a link matcher if it has been registered. - * @param matcherId The link matcher's ID (returned after register) - */ - deregisterLinkMatcher(matcherId: number): void; + /** + * Writes text to the terminal, followed by a break line character (\n). + * @param data The text to write to the terminal. + */ + writeln(data: string): void; - /** - * Gets whether the terminal has an active selection. - */ - hasSelection(): boolean; + /** + * Opens the terminal within an element. + * @param parent The element to create the terminal within. + */ + open(parent: HTMLElement): void; - /** - * Gets the terminal's current selection, this is useful for implementing copy - * behavior outside of xterm.js. - */ - getSelection(): string; + /** + * Attaches a custom key event handler which is run before keys are + * processed, giving consumers of xterm.js ultimate control as to what keys + * should be processed by the terminal and what keys should not. + * @param customKeyEventHandler The custom KeyboardEvent handler to attach. + * This is a function that takes a KeyboardEvent, allowing consumers to stop + * propogation and/or prevent the default action. The function returns + * whether the event should be processed by xterm.js. + */ + attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; - /** - * Clears the current terminal selection. - */ - clearSelection(): void; + /** + * (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to + * be matched and handled. + * @param regex The regular expression to search for, specifically this + * searches the textContent of the rows. You will want to use \s to match a + * space ' ' character for example. + * @param handler The callback when the link is called. + * @param options Options for the link matcher. + * @return The ID of the new matcher, this can be used to deregister. + */ + registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => boolean | void , options?: ILinkMatcherOptions): number; - /** - * Selects all text within the terminal. - */ - selectAll(): void; + /** + * (EXPERIMENTAL) Deregisters a link matcher if it has been registered. + * @param matcherId The link matcher's ID (returned after register) + */ + deregisterLinkMatcher(matcherId: number): void; - /** - * Focus the terminal. Delegates focus handling to the terminal's DOM element. - */ - focus(): void; + /** + * Gets whether the terminal has an active selection. + */ + hasSelection(): boolean; - /** - * Find the next instance of the term, then scroll to and select it. If it - * doesn't exist, do nothing. - * @param term Tne search term. - * @return Whether a result was found. - */ - findNext(term: string): boolean; + /** + * Gets the terminal's current selection, this is useful for implementing + * copy behavior outside of xterm.js. + */ + getSelection(): string; - /** - * Find the previous instance of the term, then scroll to and select it. If it - * doesn't exist, do nothing. - * @param term Tne search term. - * @return Whether a result was found. - */ - findPrevious(term: string): boolean; + /** + * Clears the current terminal selection. + */ + clearSelection(): void; - /** - * Destroys the terminal. - */ - destroy(): void; + /** + * Selects all text within the terminal. + */ + selectAll(): void; - /** - * Scroll the display of the terminal - * @param disp The number of lines to scroll down (negatives scroll up). - */ - scrollDisp(disp: number): void; + // /** + // * Find the next instance of the term, then scroll to and select it. If it + // * doesn't exist, do nothing. + // * @param term Tne search term. + // * @return Whether a result was found. + // */ + // findNext(term: string): boolean; - /** - * Scroll the display of the terminal by a number of pages. - * @param {number} pageCount The number of pages to scroll (negative scrolls up). - */ - scrollPages(pageCount: number): void; + // /** + // * Find the previous instance of the term, then scroll to and select it. If it + // * doesn't exist, do nothing. + // * @param term Tne search term. + // * @return Whether a result was found. + // */ + // findPrevious(term: string): boolean; - /** - * Scrolls the display of the terminal to the top. - */ - scrollToTop(): void; + /** + * Destroys the terminal and detaches it from the DOM. + */ + destroy(): void; - /** - * Scrolls the display of the terminal to the bottom. - */ - scrollToBottom(): void; + /** + * Scroll the display of the terminal + * @param amount The number of lines to scroll down (negative scroll up). + */ + scrollDisp(amount: number): void; - /** - * Clears the entire buffer, making the prompt line the new first line. - */ - clear(): void; + /** + * Scroll the display of the terminal by a number of pages. + * @param pageCount The number of pages to scroll (negative scrolls up). + */ + scrollPages(pageCount: number): void; - /** - * Writes text to the terminal. - * @param data The text to write to the terminal. - */ - write(data: string): void; + /** + * Scrolls the display of the terminal to the top. + */ + scrollToTop(): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: string, value: any): void; + /** + * Scrolls the display of the terminal to the bottom. + */ + scrollToBottom(): void; - /** - * Tells the renderer to refresh terminal content between two rows (inclusive) at the next - * opportunity. - * @param start The row to start from (between 0 and this.rows - 1). - * @param end The row to end at (between start and this.rows - 1). - */ - refresh(start: number, end: number): void; + /** + * Clear the entire buffer, making the prompt line the new first line. + */ + clear(): void; - /** - * Loads an addon, attaching it to the Terminal prototype. - * @param addon The addon to load. - */ - static loadAddon(addon: string): void; + /** + * Writes text to the terminal. + * @param data The text to write to the terminal. + */ + write(data: string): void; + + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'termName'): string; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'colors'): string[]; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'cols' | 'rows' | 'tabStopWidth' | 'scrollback'): number; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'geometry'): [number, number]; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: 'handler'): (data: string) => void; + /** + * Retrieves an option's value from the terminal. + * @param key The option key. + */ + getOption(key: string): any; + + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'termName' | 'bellSound', value: string): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'bellStyle', value: null | 'none' | 'visual' | 'sound' | 'both'): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'cursorStyle', value: null | 'block' | 'underline' | 'bar'): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'colors', value: string[]): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'cols' | 'rows' | 'tabStopWidth' | 'scrollback', value: number): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'geometry', value: [number, number]): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'handler', value: (data: string) => void): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: string, value: any): void; + + /** + * Tells the renderer to refresh terminal content between two rows + * (inclusive) at the next opportunity. + * @param start The row to start from (between 0 and this.rows - 1). + * @param end The row to end at (between start and this.rows - 1). + */ + refresh(start: number, end: number): void; + + /** + * Perform a full reset (RIS, aka '\x1bc'). + */ + reset(): void + + /** + * Loads an addon, attaching it to the Terminal prototype and making it + * available to all newly created Terminals. + * @param addon The addon to load. + */ + static loadAddon(addon: 'attach' | 'fit' | 'fullscreen' | 'search' | 'terminado'): void; + + + + + + // Moficiations to official .d.ts below + + /** + * The viewport position. + */ + ydisp: number; + + /** + * Emit an event on the terminal. + */ + emit(type: string, data: any): void; + + /** + * Find the next instance of the term, then scroll to and select it. If it + * doesn't exist, do nothing. + * @param term Tne search term. + * @return Whether a result was found. + */ + findNext(term: string): boolean; + + /** + * Find the previous instance of the term, then scroll to and select it. If it + * doesn't exist, do nothing. + * @param term Tne search term. + * @return Whether a result was found. + */ + findPrevious(term: string): boolean; } -} \ No newline at end of file + } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 0fa2acccc37..0ef23d537c1 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -280,7 +280,7 @@ export class TerminalInstance implements ITerminalInstance { dom.addClass(this._wrapperElement, 'terminal-wrapper'); this._xtermElement = document.createElement('div'); - this._xterm.open(this._xtermElement, false); + this._xterm.open(this._xtermElement); this._xterm.attachCustomKeyEventHandler((event: KeyboardEvent) => { // Disable all input if the terminal is exiting if (this._isExiting) { From 9a263229b37f0570aa08372e93d7eb81c6f899ac Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sun, 20 Aug 2017 12:18:33 -0700 Subject: [PATCH 94/96] No emmet suggestions inside script tag fixes #32732 --- extensions/emmet/src/defaultCompletionProvider.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index 28adbadfb7a..cadfd8ef380 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -72,9 +72,13 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi const currentHtmlNode = currentNode; if (currentHtmlNode && currentHtmlNode.close - && currentHtmlNode.name === 'style' && getInnerRange(currentHtmlNode).contains(position)) { - return 'css'; + if (currentHtmlNode.name === 'style') { + return 'css'; + } + if (currentHtmlNode.name === 'script') { + return; + } } } From 0c079cb7df2e7f26fbed2ee97475ec9906be8e4b Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sun, 20 Aug 2017 12:22:26 -0700 Subject: [PATCH 95/96] Support for canvas,td,tr,th in emmet suggestions --- extensions/emmet/npm-shrinkwrap.json | 6 +++--- extensions/emmet/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json index bf8d1f1095e..d4dd5915a17 100644 --- a/extensions/emmet/npm-shrinkwrap.json +++ b/extensions/emmet/npm-shrinkwrap.json @@ -38,9 +38,9 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz" }, "vscode-emmet-helper": { - "version": "1.0.16", - "from": "vscode-emmet-helper@>=1.0.16 <2.0.0", - "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.0.16.tgz" + "version": "1.0.17", + "from": "vscode-emmet-helper@>=1.0.17 <2.0.0", + "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.0.17.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 821d3ce7da6..7bf1a1213b9 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -212,7 +212,7 @@ "@emmetio/html-matcher": "^0.3.1", "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/math-expression": "^0.1.1", - "vscode-emmet-helper": "^1.0.16", + "vscode-emmet-helper": "^1.0.17", "vscode-languageserver-types": "^3.0.3", "image-size": "^0.5.2", "vscode-nls": "2.0.2" From 7e539de2f4a8872909a748cfe59f0dd1ea1ce0fc Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Sun, 20 Aug 2017 12:33:13 -0700 Subject: [PATCH 96/96] Trigger emmet for ! in xsl as it is a snippet prefix --- extensions/emmet/src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 4446a73bea4..1f6d03e2458 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -16,7 +16,7 @@ export const LANGUAGE_MODES: Object = { 'slim': ['!', '.', '}', ':', '*', '$'], 'haml': ['!', '.', '}', ':', '*', '$'], 'xml': ['.', '}', '*', '$'], - 'xsl': ['.', '}', '*', '$'], + 'xsl': ['!', '.', '}', '*', '$'], 'css': [':'], 'scss': [':'], 'sass': [':'],