Merge branch 'master' into joh/searchp

This commit is contained in:
Johannes Rieken
2017-08-28 16:25:28 +02:00
555 changed files with 14783 additions and 7994 deletions

View File

@@ -36,6 +36,7 @@ import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostCredentials } from 'vs/workbench/api/node/extHostCredentials';
import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
@@ -43,16 +44,16 @@ import EditorCommon = require('vs/editor/common/editorCommon');
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { TPromise } from 'vs/base/common/winjs.base';
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';
import { ExtHostThreadService } from "vs/workbench/services/thread/node/extHostThreadService";
import { ProxyIdentifier } from "vs/workbench/services/thread/common/threadService";
import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService';
import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
import { MarkdownString } from 'vs/base/common/htmlContent';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
@@ -74,13 +75,14 @@ function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
export function createApiFactory(
initData: IInitData,
threadService: ExtHostThreadService,
extensionService: ExtHostExtensionService,
telemetryService: ITelemetryService
extensionService: ExtHostExtensionService
): IExtensionApiFactory {
const mainThreadTelemetry = threadService.get(MainContext.MainThreadTelemetry);
// Addressable instances
const extHostHeapService = threadService.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
const extHostDocumentsAndEditors = threadService.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(threadService));
const extHostDocumentsAndEditors = threadService.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(threadService, extensionService));
const extHostDocuments = threadService.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(threadService, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = threadService.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(threadService, extHostDocumentsAndEditors));
const extHostDocumentSaveParticipant = threadService.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace)));
@@ -98,6 +100,7 @@ export function createApiFactory(
const extHostSCM = threadService.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(threadService, extHostCommands));
const extHostTask = threadService.set(ExtHostContext.ExtHostTask, new ExtHostTask(threadService));
const extHostCredentials = threadService.set(ExtHostContext.ExtHostCredentials, new ExtHostCredentials(threadService));
const extHostWindow = threadService.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(threadService));
threadService.set(ExtHostContext.ExtHostExtensionService, extensionService);
// Check that no named customers are missing
@@ -106,6 +109,7 @@ export function createApiFactory(
// Other instances
const extHostMessageService = new ExtHostMessageService(threadService);
const extHostDialogs = new ExtHostDialogs(threadService);
const extHostStatusBar = new ExtHostStatusBar(threadService);
const extHostProgress = new ExtHostProgress(threadService.get(MainContext.MainThreadProgress));
const extHostOutputService = new ExtHostOutputService(threadService);
@@ -139,7 +143,7 @@ export function createApiFactory(
return undefined;
}
this._seen.add(apiName);
return telemetryService.publicLog('apiUsage', {
return mainThreadTelemetry.$publicLog('apiUsage', {
name: apiName,
extension: extension.id
});
@@ -241,7 +245,7 @@ export function createApiFactory(
return languageFeatures.registerTypeDefinitionProvider(selector, provider);
},
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
return languageFeatures.registerHoverProvider(selector, provider);
return languageFeatures.registerHoverProvider(selector, provider, extension.id);
},
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
return languageFeatures.registerDocumentHighlightProvider(selector, provider);
@@ -325,14 +329,20 @@ export function createApiFactory(
onDidCloseTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables);
},
get state() {
return extHostWindow.state;
},
onDidChangeWindowState: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostWindow.onDidChangeWindowState(listener, thisArg, disposables);
}),
showInformationMessage(message, first, ...rest) {
return extHostMessageService.showMessage(Severity.Info, message, first, rest);
return extHostMessageService.showMessage(extension, Severity.Info, message, first, rest);
},
showWarningMessage(message, first, ...rest) {
return extHostMessageService.showMessage(Severity.Warning, message, first, rest);
return extHostMessageService.showMessage(extension, Severity.Warning, message, first, rest);
},
showErrorMessage(message, first, ...rest) {
return extHostMessageService.showMessage(Severity.Error, message, first, rest);
return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
},
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) {
return extHostQuickOpen.showQuickPick(items, options, token);
@@ -367,8 +377,11 @@ export function createApiFactory(
},
// proposed API
sampleFunction: proposedApiFunction(extension, () => {
return extHostMessageService.showMessage(Severity.Info, 'Hello Proposed Api!', {}, []);
return extHostMessageService.showMessage(extension, Severity.Info, 'Hello Proposed Api!', {}, []);
}),
showOpenDialog: proposedApiFunction(extension, options => {
return extHostDialogs.showOpenDialog(options);
})
};
// namespace: workspace
@@ -468,16 +481,16 @@ export function createApiFactory(
// namespace: scm
const scm: typeof vscode.scm = {
get inputBox() {
return extHostSCM.inputBox;
return extHostSCM.getLastInputBox(extension);
},
createSourceControl(id: string, label: string) {
telemetryService.publicLog('registerSCMProvider', {
mainThreadTelemetry.$publicLog('registerSCMProvider', {
extensionId: extension.id,
providerId: id,
providerLabel: label
});
return extHostSCM.createSourceControl(id, label);
return extHostSCM.createSourceControl(extension, id, label);
}
};
@@ -507,7 +520,7 @@ export function createApiFactory(
};
// namespace: credentials
const credentials: typeof vscode.credentials = {
const credentials = {
readSecret(service: string, account: string): Thenable<string | undefined> {
return extHostCredentials.readSecret(service, account);
},
@@ -531,7 +544,6 @@ export function createApiFactory(
workspace,
scm,
debug,
credentials,
// types
CancellationTokenSource: CancellationTokenSource,
CodeLens: extHostTypes.CodeLens,
@@ -551,6 +563,7 @@ export function createApiFactory(
Hover: extHostTypes.Hover,
IndentAction: languageConfiguration.IndentAction,
Location: extHostTypes.Location,
MarkdownString: MarkdownString,
OverviewRulerLane: EditorCommon.OverviewRulerLane,
ParameterInformation: extHostTypes.ParameterInformation,
Position: extHostTypes.Position,
@@ -585,8 +598,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;
};
@@ -616,35 +629,12 @@ class Extension<T> implements vscode.Extension<T> {
}
activate(): Thenable<T> {
return this._extensionService.activateById(this.id).then(() => this.exports);
return this._extensionService.activateById(this.id, false).then(() => this.exports);
}
}
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise<void> {
return createExtensionPathIndex(extensionService).then(trie => defineAPI(apiFactory, trie));
}
function createExtensionPathIndex(extensionService: ExtHostExtensionService): TPromise<TrieMap<IExtensionDescription>> {
// create trie to enable fast 'filename -> extension id' look up
const trie = new TrieMap<IExtensionDescription>(TrieMap.PathSplitter);
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<IExtensionDescription>): void {

View File

@@ -46,7 +46,8 @@ 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 { IDisposable } from 'vs/base/common/lifecycle';
import { SerializedError } from 'vs/base/common/errors';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -112,6 +113,18 @@ export interface MainThreadDiagnosticsShape extends IDisposable {
$clear(owner: string): TPromise<any>;
}
export interface MainThreadDialogOptions {
uri?: URI;
openLabel?: string;
openFiles?: boolean;
openFolders?: boolean;
openMany?: boolean;
}
export interface MainThreadDiaglogsShape extends IDisposable {
$showOpenDialog(options: MainThreadDialogOptions): TPromise<string[]>;
}
export interface MainThreadDocumentContentProvidersShape extends IDisposable {
$registerTextContentProvider(handle: number, scheme: string): void;
$unregisterTextContentProvider(handle: number): void;
@@ -189,7 +202,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 {
@@ -221,8 +234,13 @@ export interface MainThreadLanguagesShape extends IDisposable {
$getLanguages(): TPromise<string[]>;
}
export interface MainThreadMessageOptions {
extension?: IExtensionDescription;
modal?: boolean;
}
export interface MainThreadMessageServiceShape extends IDisposable {
$showMessage(severity: Severity, message: string, options: vscode.MessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number>;
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number>;
}
export interface MainThreadOutputServiceShape extends IDisposable {
@@ -270,7 +288,6 @@ export interface MainThreadStorageShape extends IDisposable {
export interface MainThreadTelemetryShape extends IDisposable {
$publicLog(eventName: string, data?: any): void;
$getTelemetryInfo(): TPromise<ITelemetryInfo>;
}
export interface MainThreadWorkspaceShape extends IDisposable {
@@ -293,7 +310,7 @@ export interface MainThreadTaskShape extends IDisposable {
export interface MainThreadExtensionServiceShape extends IDisposable {
$localShowMessage(severity: Severity, msg: string): void;
$onExtensionActivated(extensionId: string): void;
$onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number): void;
$onExtensionActivationFailed(extensionId: string): void;
}
@@ -330,7 +347,7 @@ export interface MainThreadSCMShape extends IDisposable {
$updateGroupResourceStates(sourceControlHandle: number, groupHandle: number, resources: SCMRawResource[]): void;
$unregisterGroup(sourceControlHandle: number, handle: number): void;
$setInputBoxValue(value: string): void;
$setInputBoxValue(sourceControlHandle: number, value: string): void;
}
export type DebugSessionUUID = string;
@@ -349,6 +366,10 @@ export interface MainThreadCredentialsShape extends IDisposable {
$deleteSecret(service: string, account: string): Thenable<boolean>;
}
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): TPromise<boolean>;
}
// -- extension host
export interface ExtHostCommandsShape {
@@ -505,9 +526,7 @@ export interface ExtHostTerminalServiceShape {
export interface ExtHostSCMShape {
$provideOriginalResource(sourceControlHandle: number, uri: URI): TPromise<URI>;
$onActiveSourceControlChange(sourceControlHandle: number): TPromise<void>;
$onInputBoxValueChange(value: string): TPromise<void>;
$onInputBoxAcceptChanges(): TPromise<void>;
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void>;
}
export interface ExtHostTaskShape {
@@ -526,6 +545,10 @@ export interface ExtHostDebugServiceShape {
export interface ExtHostCredentialsShape {
}
export interface ExtHostWindowShape {
$onDidChangeWindowFocus(value: boolean): void;
}
// --- proxy identifiers
export const MainContext = {
@@ -533,6 +556,7 @@ export const MainContext = {
MainThreadConfiguration: createMainId<MainThreadConfigurationShape>('MainThreadConfiguration'),
MainThreadDebugService: createMainId<MainThreadDebugServiceShape>('MainThreadDebugService'),
MainThreadDiagnostics: createMainId<MainThreadDiagnosticsShape>('MainThreadDiagnostics'),
MainThreadDialogs: createMainId<MainThreadDiaglogsShape>('MainThreadDiaglogs'),
MainThreadDocuments: createMainId<MainThreadDocumentsShape>('MainThreadDocuments'),
MainThreadDocumentContentProviders: createMainId<MainThreadDocumentContentProvidersShape>('MainThreadDocumentContentProviders'),
MainThreadEditors: createMainId<MainThreadEditorsShape>('MainThreadEditors'),
@@ -553,6 +577,7 @@ export const MainContext = {
MainThreadSCM: createMainId<MainThreadSCMShape>('MainThreadSCM'),
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
MainThreadCredentials: createMainId<MainThreadCredentialsShape>('MainThreadCredentials'),
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
};
export const ExtHostContext = {
@@ -576,4 +601,5 @@ export const ExtHostContext = {
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask'),
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostCredentials: createExtId<ExtHostCredentialsShape>('ExtHostCredentials'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
};

View File

@@ -56,7 +56,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
if (this._commands.has(id)) {
throw new Error('command with id already exists');
throw new Error(`command '${id}' already exists`);
}
this._commands.set(id, { callback, thisArg, description });

View File

@@ -62,7 +62,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget {
if (arg === void 0 || arg === null) {
return ConfigurationTarget.WORKSPACE;
return null;
}
if (typeof arg === 'boolean') {
return arg ? ConfigurationTarget.USER : ConfigurationTarget.WORKSPACE;

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { MainContext, MainThreadDiaglogsShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
export class ExtHostDialogs {
private readonly _proxy: MainThreadDiaglogsShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadDialogs);
}
showOpenDialog(options: vscode.OpenDialogOptions): Thenable<URI[]> {
return this._proxy.$showOpenDialog(<any>options).then(filepaths => {
return filepaths && filepaths.map(URI.file);
});
}
}

View File

@@ -8,9 +8,10 @@ import Event, { Emitter } from 'vs/base/common/event';
import { dispose } from 'vs/base/common/lifecycle';
import { MainContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext } from './extHost.protocol';
import { ExtHostDocumentData } from './extHostDocumentData';
import { ExtHostTextEditor } from './extHostTextEditor';
import { ExtHostTextEditor, ExtHostTextEditor2 } from './extHostTextEditor';
import * as assert from 'assert';
import * as typeConverters from './extHostTypeConverters';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {
@@ -29,7 +30,8 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
readonly onDidChangeActiveTextEditor: Event<ExtHostTextEditor> = this._onDidChangeActiveTextEditor.event;
constructor(
private readonly _mainContext: IMainContext
private readonly _mainContext: IMainContext,
private readonly _extHostExtensions?: ExtHostExtensionService
) {
}
@@ -79,7 +81,9 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`);
const documentData = this._documents.get(data.document.toString());
const editor = new ExtHostTextEditor(
const editor = new ExtHostTextEditor2(
this._extHostExtensions,
this._mainContext.get(MainContext.MainThreadTelemetry),
this._mainContext.get(MainContext.MainThreadEditors),
data.id,
documentData,

View File

@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from "vs/workbench/services/extensions/node/extensionDescriptionRegistry";
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
const hasOwnProperty = Object.hasOwnProperty;
@@ -43,15 +43,101 @@ export interface IExtensionAPI {
// _extensionAPIBrand: any;
}
export class ExtensionActivationTimes {
public static NONE = new ExtensionActivationTimes(false, -1, -1, -1);
public readonly startup: boolean;
public readonly codeLoadingTime: number;
public readonly activateCallTime: number;
public readonly activateResolvedTime: number;
constructor(startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number) {
this.startup = startup;
this.codeLoadingTime = codeLoadingTime;
this.activateCallTime = activateCallTime;
this.activateResolvedTime = activateResolvedTime;
}
}
export class ExtensionActivationTimesBuilder {
private readonly _startup: boolean;
private _codeLoadingStart: number;
private _codeLoadingStop: number;
private _activateCallStart: number;
private _activateCallStop: number;
private _activateResolveStart: number;
private _activateResolveStop: number;
constructor(startup: boolean) {
this._startup = startup;
this._codeLoadingStart = -1;
this._codeLoadingStop = -1;
this._activateCallStart = -1;
this._activateCallStop = -1;
this._activateResolveStart = -1;
this._activateResolveStop = -1;
}
private _delta(start: number, stop: number): number {
if (start === -1 || stop === -1) {
return -1;
}
return stop - start;
}
public build(): ExtensionActivationTimes {
return new ExtensionActivationTimes(
this._startup,
this._delta(this._codeLoadingStart, this._codeLoadingStop),
this._delta(this._activateCallStart, this._activateCallStop),
this._delta(this._activateResolveStart, this._activateResolveStop)
);
}
public codeLoadingStart(): void {
this._codeLoadingStart = Date.now();
}
public codeLoadingStop(): void {
this._codeLoadingStop = Date.now();
}
public activateCallStart(): void {
this._activateCallStart = Date.now();
}
public activateCallStop(): void {
this._activateCallStop = Date.now();
}
public activateResolveStart(): void {
this._activateResolveStart = Date.now();
}
public activateResolveStop(): void {
this._activateResolveStop = Date.now();
}
}
export class ActivatedExtension {
activationFailed: boolean;
module: IExtensionModule;
exports: IExtensionAPI;
subscriptions: IDisposable[];
public readonly activationFailed: boolean;
public readonly activationTimes: ExtensionActivationTimes;
public readonly module: IExtensionModule;
public readonly exports: IExtensionAPI;
public readonly subscriptions: IDisposable[];
constructor(activationFailed: boolean, module: IExtensionModule, exports: IExtensionAPI, subscriptions: IDisposable[]) {
constructor(
activationFailed: boolean,
activationTimes: ExtensionActivationTimes,
module: IExtensionModule,
exports: IExtensionAPI,
subscriptions: IDisposable[]
) {
this.activationFailed = activationFailed;
this.activationTimes = activationTimes;
this.module = module;
this.exports = exports;
this.subscriptions = subscriptions;
@@ -59,21 +145,21 @@ export class ActivatedExtension {
}
export class EmptyExtension extends ActivatedExtension {
constructor() {
super(false, { activate: undefined, deactivate: undefined }, undefined, []);
constructor(activationTimes: ExtensionActivationTimes) {
super(false, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
}
}
export class FailedExtension extends ActivatedExtension {
constructor() {
super(true, { activate: undefined, deactivate: undefined }, undefined, []);
constructor(activationTimes: ExtensionActivationTimes) {
super(true, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
}
}
export interface IExtensionsActivatorHost {
showMessage(severity: Severity, message: string): void;
actualActivateExtension(extensionDescription: IExtensionDescription): TPromise<ActivatedExtension>;
actualActivateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension>;
}
export class ExtensionsActivator {
@@ -106,23 +192,23 @@ export class ExtensionsActivator {
return this._activatedExtensions[extensionId];
}
public activateByEvent(activationEvent: string): TPromise<void> {
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
if (this._alreadyActivatedEvents[activationEvent]) {
return NO_OP_VOID_PROMISE;
}
let activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
return this._activateExtensions(activateExtensions, 0).then(() => {
return this._activateExtensions(activateExtensions, startup, 0).then(() => {
this._alreadyActivatedEvents[activationEvent] = true;
});
}
public activateById(extensionId: string): TPromise<void> {
public activateById(extensionId: string, startup: boolean): TPromise<void> {
let desc = this._registry.getExtensionDescription(extensionId);
if (!desc) {
throw new Error('Extension `' + extensionId + '` is not known');
}
return this._activateExtensions([desc], 0);
return this._activateExtensions([desc], startup, 0);
}
/**
@@ -140,7 +226,7 @@ export class ExtensionsActivator {
if (!depDesc) {
// Error condition 1: unknown dependency
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Extension `{1}` failed to activate. Reason: unknown dependency `{0}`.", depId, currentExtension.id));
this._activatedExtensions[currentExtension.id] = new FailedExtension();
this._activatedExtensions[currentExtension.id] = new FailedExtension(ExtensionActivationTimes.NONE);
return;
}
@@ -149,7 +235,7 @@ export class ExtensionsActivator {
if (dep.activationFailed) {
// Error condition 2: a dependency has already failed activation
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Extension `{1}` failed to activate. Reason: dependency `{0}` failed to activate.", depId, currentExtension.id));
this._activatedExtensions[currentExtension.id] = new FailedExtension();
this._activatedExtensions[currentExtension.id] = new FailedExtension(ExtensionActivationTimes.NONE);
return;
}
} else {
@@ -166,7 +252,7 @@ export class ExtensionsActivator {
}
}
private _activateExtensions(extensionDescriptions: IExtensionDescription[], recursionLevel: number): TPromise<void> {
private _activateExtensions(extensionDescriptions: IExtensionDescription[], startup: boolean, recursionLevel: number): TPromise<void> {
// console.log(recursionLevel, '_activateExtensions: ', extensionDescriptions.map(p => p.id));
if (extensionDescriptions.length === 0) {
return TPromise.as(void 0);
@@ -182,7 +268,7 @@ export class ExtensionsActivator {
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
// Error condition 3: dependency loop
this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension `{0}` failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].id));
this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension();
this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(ExtensionActivationTimes.NONE);
}
return TPromise.as(void 0);
}
@@ -208,15 +294,15 @@ export class ExtensionsActivator {
if (red.length === 0) {
// Finally reached only leafs!
return TPromise.join(green.map((p) => this._activateExtension(p))).then(_ => void 0);
return TPromise.join(green.map((p) => this._activateExtension(p, startup))).then(_ => void 0);
}
return this._activateExtensions(green, recursionLevel + 1).then(_ => {
return this._activateExtensions(red, recursionLevel + 1);
return this._activateExtensions(green, startup, recursionLevel + 1).then(_ => {
return this._activateExtensions(red, startup, recursionLevel + 1);
});
}
private _activateExtension(extensionDescription: IExtensionDescription): TPromise<void> {
private _activateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<void> {
if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) {
return TPromise.as(void 0);
}
@@ -225,12 +311,12 @@ export class ExtensionsActivator {
return this._activatingExtensions[extensionDescription.id];
}
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription).then(null, (err) => {
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription, startup).then(null, (err) => {
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension `{0}` failed: {1}.", extensionDescription.id, err.message));
console.error('Activating extension `' + extensionDescription.id + '` failed: ', err.message);
console.log('Here is the error stack: ', err.stack);
// Treat the extension as being empty
return new FailedExtension();
return new FailedExtension(ExtensionActivationTimes.NONE);
}).then((x: ActivatedExtension) => {
this._activatedExtensions[extensionDescription.id] = x;
delete this._activatingExtensions[extensionDescription.id];

View File

@@ -9,15 +9,16 @@ import { join } from 'path';
import { mkdirp, dirExists } from 'vs/base/node/pfs';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from "vs/workbench/services/extensions/node/extensionDescriptionRegistry";
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
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 { 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 { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape } from './extHost.protocol';
import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes } 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 {
@@ -105,32 +106,32 @@ class ExtensionStoragePath {
}
}
export class ExtHostExtensionService {
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private readonly _barrier: Barrier;
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _threadService: ExtHostThreadService;
private readonly _telemetryService: ITelemetryService;
private readonly _mainThreadTelemetry: MainThreadTelemetryShape;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _proxy: MainThreadExtensionServiceShape;
private _activator: ExtensionsActivator;
private _extensionPathIndex: TPromise<TrieMap<IExtensionDescription>>;
/**
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
*/
constructor(initData: IInitData, threadService: ExtHostThreadService, telemetryService: ITelemetryService) {
constructor(initData: IInitData, threadService: ExtHostThreadService) {
this._barrier = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._threadService = threadService;
this._telemetryService = telemetryService;
this._mainThreadTelemetry = threadService.get(MainContext.MainThreadTelemetry);
this._storage = new ExtHostStorage(threadService);
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._proxy = this._threadService.get(MainContext.MainThreadExtensionService);
this._activator = null;
// initialize API first (i.e. do not release barrier until the API is initialized)
const apiFactory = createApiFactory(initData, threadService, this, this._telemetryService);
const apiFactory = createApiFactory(initData, threadService, this);
initializeExtensionApi(this, apiFactory).then(() => {
@@ -150,8 +151,8 @@ export class ExtHostExtensionService {
}
},
actualActivateExtension: (extensionDescription: IExtensionDescription): TPromise<ActivatedExtension> => {
return this._activateExtension(extensionDescription);
actualActivateExtension: (extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> => {
return this._activateExtension(extensionDescription, startup);
}
});
@@ -170,19 +171,19 @@ export class ExtHostExtensionService {
return false;
}
public activateByEvent(activationEvent: string): TPromise<void> {
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
if (this._barrier.isOpen()) {
return this._activator.activateByEvent(activationEvent);
return this._activator.activateByEvent(activationEvent, startup);
} else {
return this._barrier.wait().then(() => this._activator.activateByEvent(activationEvent));
return this._barrier.wait().then(() => this._activator.activateByEvent(activationEvent, startup));
}
}
public activateById(extensionId: string): TPromise<void> {
public activateById(extensionId: string, startup: boolean): TPromise<void> {
if (this._barrier.isOpen()) {
return this._activator.activateById(extensionId);
return this._activator.activateById(extensionId, startup);
} else {
return this._barrier.wait().then(() => this._activator.activateById(extensionId));
return this._barrier.wait().then(() => this._activator.activateById(extensionId, startup));
}
}
@@ -202,6 +203,31 @@ export class ExtHostExtensionService {
}
}
// create trie to enable fast 'filename -> extension id' look up
public getExtensionPathIndex(): TPromise<TrieMap<IExtensionDescription>> {
if (!this._extensionPathIndex) {
const trie = new TrieMap<IExtensionDescription>();
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<void> {
let result: TPromise<void> = TPromise.as(void 0);
@@ -242,9 +268,10 @@ export class ExtHostExtensionService {
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription): TPromise<ActivatedExtension> {
return this._doActivateExtension(extensionDescription).then((activatedExtension) => {
this._proxy.$onExtensionActivated(extensionDescription.id);
private _activateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> {
return this._doActivateExtension(extensionDescription, startup).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
this._proxy.$onExtensionActivated(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime);
return activatedExtension;
}, (err) => {
this._proxy.$onExtensionActivationFailed(extensionDescription.id);
@@ -252,18 +279,20 @@ export class ExtHostExtensionService {
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription): TPromise<ActivatedExtension> {
private _doActivateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> {
let event = getTelemetryActivationEvent(extensionDescription);
this._telemetryService.publicLog('activatePlugin', event);
this._mainThreadTelemetry.$publicLog('activatePlugin', event);
if (!extensionDescription.main) {
// Treat the extension as being empty => NOT AN ERROR CASE
return TPromise.as(new EmptyExtension());
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
}
const activationTimesBuilder = new ExtensionActivationTimesBuilder(startup);
return TPromise.join<any>([
loadCommonJSModule(extensionDescription.main),
loadCommonJSModule(extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(<IExtensionModule>values[0], <IExtensionContext>values[1]);
return ExtHostExtensionService._callActivate(<IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
}, (errors: any[]) => {
// Avoid failing with an array of errors, fail with a single error
if (errors[0]) {
@@ -297,22 +326,30 @@ export class ExtHostExtensionService {
});
}
private static _callActivate(extensionModule: IExtensionModule, context: IExtensionContext): TPromise<ActivatedExtension> {
private static _callActivate(extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<ActivatedExtension> {
// Make sure the extension's surface is not undefined
extensionModule = extensionModule || {
activate: undefined,
deactivate: undefined
};
return this._callActivateOptional(extensionModule, context).then((extensionExports) => {
return new ActivatedExtension(false, extensionModule, extensionExports, context.subscriptions);
return this._callActivateOptional(extensionModule, context, activationTimesBuilder).then((extensionExports) => {
return new ActivatedExtension(false, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
});
}
private static _callActivateOptional(extensionModule: IExtensionModule, context: IExtensionContext): TPromise<IExtensionAPI> {
private static _callActivateOptional(extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<IExtensionAPI> {
if (typeof extensionModule.activate === 'function') {
try {
return TPromise.as(extensionModule.activate.apply(global, [context]));
activationTimesBuilder.activateCallStart();
const activateResult = extensionModule.activate.apply(global, [context]);
activationTimesBuilder.activateCallStop();
activationTimesBuilder.activateResolveStart();
return TPromise.as(activateResult).then((value) => {
activationTimesBuilder.activateResolveStop();
return value;
});
} catch (err) {
return TPromise.wrapError(err);
}
@@ -325,16 +362,19 @@ export class ExtHostExtensionService {
// -- called by main thread
public $activateByEvent(activationEvent: string): TPromise<void> {
return this.activateByEvent(activationEvent);
return this.activateByEvent(activationEvent, false);
}
}
function loadCommonJSModule<T>(modulePath: string): TPromise<T> {
function loadCommonJSModule<T>(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<T> {
let r: T = null;
activationTimesBuilder.codeLoadingStart();
try {
r = require.__$__nodeRequire<T>(modulePath);
} catch (e) {
return TPromise.wrapError<T>(e);
} finally {
activationTimesBuilder.codeLoadingStop();
}
return TPromise.as(r);
}

View File

@@ -18,10 +18,13 @@ import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHos
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IRawColorFormatMap, IMainContext } from './extHost.protocol';
import { MainContext, MainThreadTelemetryShape, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IRawColorFormatMap, IMainContext } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { containsCommandLink } from 'vs/base/common/htmlContent';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { once } from 'vs/base/common/functional';
// --- adapter
@@ -173,12 +176,12 @@ class TypeDefinitionAdapter {
class HoverAdapter {
private _documents: ExtHostDocuments;
private _provider: vscode.HoverProvider;
constructor(documents: ExtHostDocuments, provider: vscode.HoverProvider) {
this._documents = documents;
this._provider = provider;
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.HoverProvider,
private readonly _telemetryLog: (name: string, data: object) => void,
) {
//
}
public provideHover(resource: URI, position: IPosition): TPromise<modes.Hover> {
@@ -187,7 +190,7 @@ class HoverAdapter {
let pos = TypeConverters.toPosition(position);
return asWinJsPromise(token => this._provider.provideHover(doc, pos, token)).then(value => {
if (!value) {
if (!value || isFalsyOrEmpty(value.contents)) {
return undefined;
}
if (!value.range) {
@@ -197,7 +200,14 @@ class HoverAdapter {
value.range = new Range(pos, pos);
}
return TypeConverters.fromHover(value);
const result = TypeConverters.fromHover(value);
// we wanna know which extension uses command links
// because that is a potential trick-attack on users
if (result.contents.some(h => containsCommandLink(h.value))) {
this._telemetryLog('usesCommandLink', { from: 'hover' });
}
return result;
});
}
}
@@ -634,10 +644,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 +657,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<modes.ILink> {
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<vscode.DocumentLink>(id);
if (!item) {
return undefined;
}
return asWinJsPromise(token => this._provider.resolveDocumentLink(item, token)).then(value => {
if (value) {
return TypeConverters.DocumentLink.from(value);
}
return undefined;
});
}
}
@@ -728,6 +754,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private static _handlePool: number = 0;
private _proxy: MainThreadLanguageFeaturesShape;
private _telemetry: MainThreadTelemetryShape;
private _documents: ExtHostDocuments;
private _commands: ExtHostCommands;
private _heapService: ExtHostHeapService;
@@ -743,6 +770,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
diagnostics: ExtHostDiagnostics
) {
this._proxy = mainContext.get(MainContext.MainThreadLanguageFeatures);
this._telemetry = mainContext.get(MainContext.MainThreadTelemetry);
this._documents = documents;
this._commands = commands;
this._heapService = heapMonitor;
@@ -844,9 +872,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- extra info
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable {
const handle = this._nextHandle();
this._adapter.set(handle, new HoverAdapter(this._documents, provider));
this._adapter.set(handle, new HoverAdapter(this._documents, provider, once((name, data) => {
data['extension'] = extensionId;
this._telemetry.$publicLog(name, data);
})));
this._proxy.$registerHoverProvider(handle, selector);
return this._createDisposable(handle);
}
@@ -993,7 +1024,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);
}

View File

@@ -6,22 +6,14 @@
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';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
const emptyMessageOptions: vscode.MessageOptions = Object.create(null);
function isMessageItem<T>(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 +22,20 @@ export class ExtHostMessageService {
this._proxy = mainContext.get(MainContext.MainThreadMessageService);
}
showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable<string | undefined>;
showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable<vscode.MessageItem | undefined>;
showMessage(severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable<string | vscode.MessageItem | undefined> {
const { options, items } = parseMessageArguments(optionsOrFirstItem, rest);
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable<string | undefined>;
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable<vscode.MessageItem | undefined>;
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable<string | vscode.MessageItem | undefined> {
let options: MainThreadMessageOptions = { extension };
let items: (string | vscode.MessageItem)[];
if (typeof optionsOrFirstItem === 'string' || isMessageItem(optionsOrFirstItem)) {
items = [optionsOrFirstItem, ...rest];
} else {
options.modal = optionsOrFirstItem && optionsOrFirstItem.modal;
items = rest;
}
const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = [];
for (let handle = 0; handle < items.length; handle++) {

View File

@@ -8,6 +8,7 @@ import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { asWinJsPromise } from 'vs/base/common/async';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
@@ -32,7 +33,7 @@ export class ExtHostSCMInputBox {
}
set value(value: string) {
this._proxy.$setInputBoxValue(value);
this._proxy.$setInputBoxValue(this._sourceControlHandle, value);
this.updateValue(value);
}
@@ -42,13 +43,7 @@ export class ExtHostSCMInputBox {
return this._onDidChange.event;
}
private _onDidAccept = new Emitter<string>();
get onDidAccept(): Event<string> {
return this._onDidAccept.event;
}
constructor(private _proxy: MainThreadSCMShape) {
constructor(private _proxy: MainThreadSCMShape, private _sourceControlHandle: number) {
// noop
}
@@ -56,10 +51,6 @@ export class ExtHostSCMInputBox {
this.updateValue(value);
}
$onInputBoxAcceptChanges(): void {
this._onDidAccept.fire(this._value);
}
private updateValue(value: string): void {
this._value = value;
this._onDidChange.fire(value);
@@ -171,6 +162,9 @@ class ExtHostSourceControl implements vscode.SourceControl {
return this._label;
}
private _inputBox: ExtHostSCMInputBox;
get inputBox(): ExtHostSCMInputBox { return this._inputBox; }
private _count: number | undefined = undefined;
get count(): number | undefined {
@@ -238,6 +232,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
private _id: string,
private _label: string,
) {
this._inputBox = new ExtHostSCMInputBox(this._proxy, this._handle);
this._proxy.$registerSourceControl(this._handle, _id, _label);
}
@@ -266,22 +261,16 @@ export class ExtHostSCM {
private _proxy: MainThreadSCMShape;
private _sourceControls: Map<ProviderHandle, ExtHostSourceControl> = new Map<ProviderHandle, ExtHostSourceControl>();
private _sourceControlsByExtension: Map<string, ExtHostSourceControl[]> = new Map<string, ExtHostSourceControl[]>();
private _onDidChangeActiveProvider = new Emitter<vscode.SourceControl>();
get onDidChangeActiveProvider(): Event<vscode.SourceControl> { return this._onDidChangeActiveProvider.event; }
private _activeProvider: vscode.SourceControl | undefined;
get activeProvider(): vscode.SourceControl | undefined { return this._activeProvider; }
private _inputBox: ExtHostSCMInputBox;
get inputBox(): ExtHostSCMInputBox { return this._inputBox; }
constructor(
mainContext: IMainContext,
private _commands: ExtHostCommands
) {
this._proxy = mainContext.get(MainContext.MainThreadSCM);
this._inputBox = new ExtHostSCMInputBox(this._proxy);
_commands.registerArgumentProcessor({
processArgument: arg => {
@@ -307,6 +296,14 @@ export class ExtHostSCM {
}
return sourceControl.getResourceGroup(arg.groupHandle);
} else if (arg && arg.$mid === 5) {
const sourceControl = this._sourceControls.get(arg.handle);
if (!sourceControl) {
return arg;
}
return sourceControl;
}
return arg;
@@ -314,14 +311,27 @@ export class ExtHostSCM {
});
}
createSourceControl(id: string, label: string): vscode.SourceControl {
createSourceControl(extension: IExtensionDescription, id: string, label: string): vscode.SourceControl {
const handle = ExtHostSCM._handlePool++;
const sourceControl = new ExtHostSourceControl(this._proxy, this._commands.converter, id, label);
this._sourceControls.set(handle, sourceControl);
const sourceControls = this._sourceControlsByExtension.get(extension.id) || [];
sourceControls.push(sourceControl);
this._sourceControlsByExtension.set(extension.id, sourceControls);
return sourceControl;
}
// Deprecated
getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox {
const sourceControls = this._sourceControlsByExtension.get(extension.id);
const sourceControl = sourceControls && sourceControls[sourceControls.length - 1];
const inputBox = sourceControl && sourceControl.inputBox;
return inputBox;
}
$provideOriginalResource(sourceControlHandle: number, uri: URI): TPromise<URI> {
const sourceControl = this._sourceControls.get(sourceControlHandle);
@@ -335,18 +345,14 @@ export class ExtHostSCM {
});
}
$onActiveSourceControlChange(handle: number): TPromise<void> {
this._activeProvider = this._sourceControls.get(handle);
return TPromise.as(null);
}
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void> {
const sourceControl = this._sourceControls.get(sourceControlHandle);
$onInputBoxValueChange(value: string): TPromise<void> {
this._inputBox.$onInputBoxValueChange(value);
return TPromise.as(null);
}
if (!sourceControl || !sourceControl.quickDiffProvider) {
return TPromise.as(null);
}
$onInputBoxAcceptChanges(): TPromise<void> {
this._inputBox.$onInputBoxAcceptChanges();
sourceControl.inputBox.$onInputBoxValueChange(value);
return TPromise.as(null);
}
}

View File

@@ -329,7 +329,7 @@ namespace Tasks {
let source = {
kind: TaskSystem.TaskSourceKind.Extension,
label: typeof task.source === 'string' ? task.source : extension.name,
detail: extension.id
extension: extension.id
};
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let key = (task as types.Task).definitionKey;

View File

@@ -1,46 +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 { notImplemented } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { MainContext, MainThreadTelemetryShape, IMainContext } from './extHost.protocol';
export class RemoteTelemetryService implements ITelemetryService {
_serviceBrand: any;
private _name: string;
private _proxy: MainThreadTelemetryShape;
constructor(name: string, mainContext: IMainContext) {
this._name = name;
this._proxy = mainContext.get(MainContext.MainThreadTelemetry);
}
get isOptedIn(): boolean {
throw notImplemented();
}
getTelemetryInfo(): TPromise<ITelemetryInfo> {
return this._proxy.$getTelemetryInfo();
}
publicLog(eventName: string, data?: any): TPromise<void> {
data = data || Object.create(null);
data[this._name] = true;
this._proxy.$publicLog(eventName, data);
return TPromise.as(null);
}
timedPublicLog(): any {
throw notImplemented();
}
addTelemetryAppender(): any {
throw notImplemented();
}
}

View File

@@ -6,17 +6,19 @@
import { ok } from 'vs/base/common/assert';
import { readonly, illegalArgument } from 'vs/base/common/errors';
import { readonly, illegalArgument, V8CallSite } from 'vs/base/common/errors';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
import { Selection, Range, Position, EndOfLine, TextEditorRevealType, TextEditorLineNumbersStyle, SnippetString } from './extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/editorCommon';
import * as TypeConverters from './extHostTypeConverters';
import { MainThreadEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from './extHost.protocol';
import { MainThreadEditorsShape, MainThreadTelemetryShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from './extHost.protocol';
import * as vscode from 'vscode';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { IRange } from 'vs/editor/common/core/range';
import { containsCommandLink } from 'vs/base/common/htmlContent';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
export class TextEditorDecorationType implements vscode.TextEditorDecorationType {
@@ -542,14 +544,78 @@ 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;
});
}
}
export class ExtHostTextEditor2 extends ExtHostTextEditor {
constructor(
private readonly _extHostExtensions: ExtHostExtensionService,
private readonly _mainThreadTelemetry: MainThreadTelemetryShape,
proxy: MainThreadEditorsShape,
id: string,
document: ExtHostDocumentData,
selections: Selection[],
options: IResolvedTextEditorConfiguration,
viewColumn: vscode.ViewColumn
) {
super(proxy, id, document, selections, options, viewColumn);
}
setDecorations(decorationType: vscode.TextEditorDecorationType, rangesOrOptions: Range[] | vscode.DecorationOptions[]): void {
// (1) find out if this decoration is important for us
let usesCommandLink = false;
outer: for (const rangeOrOption of rangesOrOptions) {
if (Range.isRange(rangeOrOption)) {
break;
}
if (typeof rangeOrOption.hoverMessage === 'string' && containsCommandLink(rangeOrOption.hoverMessage)) {
usesCommandLink = true;
break;
} else if (Array.isArray(rangeOrOption.hoverMessage)) {
for (const message of rangeOrOption.hoverMessage) {
if (typeof message === 'string' && containsCommandLink(message)) {
usesCommandLink = true;
break outer;
}
}
}
}
// (2) send event for important decorations
if (usesCommandLink) {
let tag = new Error();
this._extHostExtensions.getExtensionPathIndex().then(index => {
const oldHandler = (<any>Error).prepareStackTrace;
(<any>Error).prepareStackTrace = (error: Error, stackTrace: V8CallSite[]) => {
for (const call of stackTrace) {
const extension = index.findSubstr(call.getFileName());
if (extension) {
this._mainThreadTelemetry.$publicLog('usesCommandLink', {
extension: extension.id,
from: 'decoration',
});
return;
}
}
};
// it all happens here...
// tslint:disable-next-line:no-unused-expression
tag.stack;
(<any>Error).prepareStackTrace = oldHandler;
});
}
// (3) do it
super.setDecorations(decorationType, rangesOrOptions);
}
}
function warnOnError(promise: TPromise<any>): void {
promise.then(null, (err) => {
console.warn(err);

View File

@@ -16,6 +16,7 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import * as htmlContent from 'vs/base/common/htmlContent';
export interface PositionLike {
line: number;
@@ -143,12 +144,35 @@ function isDecorationOptionsArr(something: vscode.Range[] | vscode.DecorationOpt
return isDecorationOptions(something[0]) ? true : false;
}
export namespace MarkdownString {
export function fromMany(markup: (vscode.MarkdownString | vscode.MarkedString)[]): htmlContent.IMarkdownString[] {
return markup.map(MarkdownString.from);
}
export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString {
if (htmlContent.isMarkdownString(markup)) {
return markup;
} else if (typeof markup === 'string' || !markup) {
return { value: <string>markup || '', isTrusted: true };
} else {
const { language, value } = markup;
return { value: '```' + language + '\n' + value + '\n```' };
}
}
export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString {
const ret = new htmlContent.MarkdownString(value.value);
ret.isTrusted = value.isTrusted;
return ret;
}
}
export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.DecorationOptions[]): IDecorationOptions[] {
if (isDecorationOptionsArr(ranges)) {
return ranges.map((r): IDecorationOptions => {
return ranges.map(r => {
return {
range: fromRange(r.range),
hoverMessage: r.hoverMessage,
hoverMessage: Array.isArray(r.hoverMessage) ? MarkdownString.fromMany(r.hoverMessage) : r.hoverMessage && MarkdownString.from(r.hoverMessage),
renderOptions: <any> /* URI vs Uri */r.renderOptions
};
});
@@ -256,12 +280,12 @@ export const location = {
export function fromHover(hover: vscode.Hover): modes.Hover {
return <modes.Hover>{
range: fromRange(hover.range),
contents: hover.contents
contents: MarkdownString.fromMany(hover.contents)
};
}
export function toHover(info: modes.Hover): types.Hover {
return new types.Hover(info.contents, toRange(info.range));
return new types.Hover(info.contents.map(MarkdownString.to), toRange(info.range));
}
export function toDocumentHighlight(occurrence: modes.DocumentHighlight): types.DocumentHighlight {

View File

@@ -10,6 +10,7 @@ import URI from 'vs/base/common/uri';
import { Color as BaseColor, HSLA } from 'vs/base/common/color';
import { illegalArgument } from 'vs/base/common/errors';
import * as vscode from 'vscode';
import { isMarkdownString } from 'vs/base/common/htmlContent';
export class Disposable {
@@ -698,16 +699,20 @@ export class Diagnostic {
export class Hover {
public contents: vscode.MarkedString[];
public contents: vscode.MarkdownString[] | vscode.MarkedString[];
public range: Range;
constructor(contents: vscode.MarkedString | vscode.MarkedString[], range?: Range) {
constructor(
contents: vscode.MarkdownString | vscode.MarkedString | vscode.MarkdownString[] | vscode.MarkedString[],
range?: Range
) {
if (!contents) {
throw new Error('Illegal argument, contents must be defined');
}
if (Array.isArray(contents)) {
this.contents = contents;
this.contents = <vscode.MarkdownString[] | vscode.MarkedString[]>contents;
} else if (isMarkdownString(contents)) {
this.contents = [contents];
} else {
this.contents = [contents];
}
@@ -1029,14 +1034,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;
}
@@ -1377,4 +1382,4 @@ export enum ConfigurationTarget {
Workspace = 2,
WorkspaceFolder = 3
}
}

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* 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 Event, { Emitter } from 'vs/base/common/event';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostWindowShape, MainContext, MainThreadWindowShape } from './extHost.protocol';
import { WindowState } from 'vscode';
export class ExtHostWindow implements ExtHostWindowShape {
private static InitialState: WindowState = {
focused: true
};
private _proxy: MainThreadWindowShape;
private _onDidChangeWindowState = new Emitter<WindowState>();
readonly onDidChangeWindowState: Event<WindowState> = this._onDidChangeWindowState.event;
private _state = ExtHostWindow.InitialState;
get state(): WindowState { return this._state; }
constructor(threadService: IThreadService) {
this._proxy = threadService.get(MainContext.MainThreadWindow);
this._proxy.$getWindowVisibility().then(isFocused => this.$onDidChangeWindowFocus(isFocused));
}
$onDidChangeWindowFocus(focused: boolean): void {
if (focused === this._state.focused) {
return;
}
this._state = { ...this._state, focused };
this._onDidChangeWindowState.fire(this._state);
}
}

View File

@@ -15,7 +15,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { fromRange, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import { compare } from "vs/base/common/strings";
import { compare } from 'vs/base/common/strings';
import { asWinJsPromise } from 'vs/base/common/async';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { TrieMap } from 'vs/base/common/map';