Merge branch 'master' into ben/workspace-api

This commit is contained in:
Benjamin Pasero
2018-01-24 13:25:06 +01:00
115 changed files with 1881 additions and 13315 deletions

View File

@@ -46,6 +46,7 @@ import './mainThreadTask';
import './mainThreadTelemetry';
import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadLogService';
import './mainThreadWindow';
import './mainThreadWorkspace';

View File

@@ -15,17 +15,18 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { MainThreadTextEditor } from './mainThreadEditor';
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
import { equals as objectEquals } from 'vs/base/common/objects';
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext, IWorkspaceResourceEdit } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext } from '../node/extHost.protocol';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IFileService } from 'vs/platform/files/common/files';
import { bulkEdit, IResourceEdit } from 'vs/editor/browser/services/bulkEdit';
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
import { IModelService } from 'vs/editor/common/services/modelService';
import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { isResourceFileEdit } from 'vs/editor/common/modes';
export class MainThreadEditors implements MainThreadEditorsShape {
@@ -210,40 +211,22 @@ export class MainThreadEditors implements MainThreadEditorsShape {
return TPromise.as(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
}
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): TPromise<boolean> {
const { edits } = reviveWorkspaceEditDto(dto);
// First check if loaded models were not changed in the meantime
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
const workspaceResourceEdit = workspaceResourceEdits[i];
if (workspaceResourceEdit.modelVersionId) {
const uri = URI.revive(workspaceResourceEdit.resource);
let model = this._modelService.getModel(uri);
if (model && model.getVersionId() !== workspaceResourceEdit.modelVersionId) {
for (let i = 0, len = edits.length; i < len; i++) {
const edit = edits[i];
if (!isResourceFileEdit(edit) && edit.modelVersionId) {
let model = this._modelService.getModel(edit.resource);
if (model && model.getVersionId() !== edit.modelVersionId) {
// model changed in the meantime
return TPromise.as(false);
}
}
}
// Convert to shape expected by bulkEdit below
let resourceEdits: IResourceEdit[] = [];
for (let i = 0, len = workspaceResourceEdits.length; i < len; i++) {
const workspaceResourceEdit = workspaceResourceEdits[i];
const uri = URI.revive(workspaceResourceEdit.resource);
const edits = workspaceResourceEdit.edits;
for (let j = 0, lenJ = edits.length; j < lenJ; j++) {
const edit = edits[j];
resourceEdits.push({
resource: uri,
newText: edit.newText,
newEol: edit.newEol,
range: edit.range
});
}
}
let codeEditor: ICodeEditor;
let editor = this._workbenchEditorService.getActiveEditor();
if (editor) {
@@ -253,8 +236,7 @@ export class MainThreadEditors implements MainThreadEditorsShape {
}
}
return bulkEdit(this._textModelResolverService, codeEditor, resourceEdits, this._fileService)
.then(() => true);
return BulkEdit.perform(edits, this._textModelResolverService, this._fileService, codeEditor).then(() => true);
}
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {

View File

@@ -15,7 +15,7 @@ import { wireCancellationToken } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, WorkspaceEditDto, ResourceEditDto, CodeActionDto } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, CodeActionDto, reviveWorkspaceEditDto } from '../node/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IHeapService } from './mainThreadHeapService';
@@ -86,21 +86,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private static _reviveResourceEditDto(data: ResourceEditDto): modes.IResourceEdit {
data.resource = URI.revive(data.resource);
return <modes.IResourceEdit>data;
}
private static _reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceEdit {
if (data && data.edits) {
data.edits.forEach(MainThreadLanguageFeatures._reviveResourceEditDto);
}
return <modes.WorkspaceEdit>data;
}
private static _reviveCodeActionDto(data: CodeActionDto[]): modes.CodeAction[] {
if (data) {
data.forEach(code => MainThreadLanguageFeatures._reviveWorkspaceEditDto(code.edit));
data.forEach(code => reviveWorkspaceEditDto(code.edit));
}
return <modes.CodeAction[]>data;
}
@@ -266,7 +254,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): void {
this._registrations[handle] = modes.RenameProviderRegistry.register(toLanguageSelector(selector), <modes.RenameProvider>{
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(MainThreadLanguageFeatures._reviveWorkspaceEditDto);
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(reviveWorkspaceEditDto);
}
});
}

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 { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtHostContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
@extHostCustomer
export class MainThreadLogService extends Disposable {
constructor(
extHostContext: IExtHostContext,
@ILogService logService: ILogService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => extHostContext.getProxy(ExtHostContext.ExtHostLogService).$setLevel(level)));
}
}

View File

@@ -27,6 +27,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
import { localize } from 'vs/nls';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
export interface ISaveParticipantParticipant extends ISaveParticipant {
// progressMessage: string;
@@ -279,8 +280,9 @@ export class SaveParticipant implements ISaveParticipant {
constructor(
extHostContext: IExtHostContext,
@IInstantiationService instantiationService: IInstantiationService,
@IProgressService2 private _progressService: IProgressService2,
@IInstantiationService instantiationService: IInstantiationService
@ILogService private _logService: ILogService
) {
this._saveParticipants = [
instantiationService.createInstance(TrimWhitespaceParticipant),
@@ -303,7 +305,7 @@ export class SaveParticipant implements ISaveParticipant {
const promiseFactory = this._saveParticipants.map(p => () => {
return Promise.resolve(p.participate(model, env));
});
return sequence(promiseFactory).then(() => { });
return sequence(promiseFactory).then(() => { }, err => this._logService.error(err));
});
}
}

View File

@@ -55,8 +55,8 @@ import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
import { toGlobPattern, toLanguageSelector } from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
import { OverviewRulerLane } from 'vs/editor/common/model';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
@@ -89,18 +89,19 @@ export function createApiFactory(
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
logService: ILogService
extHostLogService: ExtHostLogService
): IExtensionApiFactory {
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol));
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(logService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadEditors)));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadEditors)));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, logService));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands));
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace));
@@ -111,7 +112,7 @@ export function createApiFactory(
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, logService));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace));
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);

View File

@@ -14,7 +14,7 @@ import {
import * as vscode from 'vscode';
import { UriComponents } from 'vs/base/common/uri';
import URI, { UriComponents } from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -52,8 +52,9 @@ import { IStat, FileChangeType } from 'vs/platform/files/common/files';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { ILineMatch, IPatternInfo } from 'vs/platform/search/common/search';
import { LogLevel } from 'vs/platform/log/common/log';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -71,6 +72,7 @@ export interface IWorkspaceData {
id: string;
name: string;
folders: { uri: UriComponents, name: string, index: number }[];
configuration?: UriComponents;
}
export interface IInitData {
@@ -83,6 +85,7 @@ export interface IInitData {
windowId: number;
args: ParsedArgs;
execPath: string;
logLevel: LogLevel;
}
export interface IConfigurationInitData extends IConfigurationData {
@@ -192,8 +195,6 @@ export interface IApplyEditsOptions extends IUndoStopOptions {
setEndOfLine: EndOfLine;
}
export interface ITextDocumentShowOptions {
position?: EditorPosition;
preserveFocus?: boolean;
@@ -201,16 +202,6 @@ export interface ITextDocumentShowOptions {
selection?: IRange;
}
export interface IWorkspaceResourceEdit {
resource: UriComponents;
modelVersionId?: number;
edits: {
range?: IRange;
newText: string;
newEol?: EndOfLineSequence;
}[];
}
export interface MainThreadEditorsShape extends IDisposable {
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): TPromise<string>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
@@ -223,7 +214,7 @@ export interface MainThreadEditorsShape extends IDisposable {
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<void>;
$trySetSelections(id: string, selections: ISelection[]): TPromise<void>;
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean>;
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): TPromise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): TPromise<boolean>;
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]>;
}
@@ -624,23 +615,44 @@ export interface WorkspaceSymbolsDto extends IdObject {
symbols: SymbolInformationDto[];
}
export interface ResourceEditDto {
export interface ResourceFileEditDto {
oldUri: UriComponents;
newUri: UriComponents;
}
export interface ResourceTextEditDto {
resource: UriComponents;
range: IRange;
newText: string;
modelVersionId?: number;
edits: modes.TextEdit[];
}
export interface WorkspaceEditDto {
edits: ResourceEditDto[];
edits: (ResourceFileEditDto | ResourceTextEditDto)[];
// todo@joh reject should go into rename
rejectReason?: string;
}
export function reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceEdit {
if (data && data.edits) {
for (const edit of data.edits) {
if (typeof (<ResourceTextEditDto>edit).resource === 'object') {
(<ResourceTextEditDto>edit).resource = URI.revive((<ResourceTextEditDto>edit).resource);
} else {
(<ResourceFileEditDto>edit).newUri = URI.revive((<ResourceFileEditDto>edit).newUri);
(<ResourceFileEditDto>edit).oldUri = URI.revive((<ResourceFileEditDto>edit).oldUri);
}
}
}
return <modes.WorkspaceEdit>data;
}
export interface CodeActionDto {
title: string;
edit?: WorkspaceEditDto;
diagnostics?: IMarkerData[];
command?: modes.Command;
scope?: string;
kind?: string;
}
export interface ExtHostLanguageFeaturesShape {
@@ -745,6 +757,10 @@ export interface ExtHostWindowShape {
$onDidChangeWindowFocus(value: boolean): void;
}
export interface ExtHostLogServiceShape {
$setLevel(level: LogLevel);
}
// --- proxy identifiers
export const MainContext = {
@@ -795,7 +811,7 @@ export const ExtHostContext = {
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
// ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM'),
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask', ProxyType.CustomMarshaller),

View File

@@ -353,11 +353,7 @@ export class ExtHostApiCommands {
if (value.rejectReason) {
return TPromise.wrapError<types.WorkspaceEdit>(new Error(value.rejectReason));
}
let workspaceEdit = new types.WorkspaceEdit();
for (let edit of value.edits) {
workspaceEdit.replace(edit.resource, typeConverters.toRange(edit.range), edit.newText);
}
return workspaceEdit;
return typeConverters.WorkspaceEdit.to(value);
});
}

View File

@@ -8,7 +8,7 @@ import Event from 'vs/base/common/event';
import URI, { UriComponents } from 'vs/base/common/uri';
import { sequence, always } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';
import { ExtHostDocumentSaveParticipantShape, MainThreadEditorsShape, IWorkspaceResourceEdit } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentSaveParticipantShape, MainThreadEditorsShape, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { TextEdit } from 'vs/workbench/api/node/extHostTypes';
import { fromRange, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
@@ -142,7 +142,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
}).then(values => {
let workspaceResourceEdit: IWorkspaceResourceEdit = {
const resourceEdit: ResourceTextEditDto = {
resource: document.uri,
edits: []
};
@@ -150,10 +150,10 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
for (const value of values) {
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
for (const { newText, newEol, range } of value) {
workspaceResourceEdit.edits.push({
resourceEdit.edits.push({
range: range && fromRange(range),
newText,
newEol: EndOfLine.from(newEol)
text: newText,
eol: EndOfLine.from(newEol)
});
}
}
@@ -161,12 +161,12 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
// apply edits if any and if document
// didn't change somehow in the meantime
if (workspaceResourceEdit.edits.length === 0) {
if (resourceEdit.edits.length === 0) {
return undefined;
}
if (version === document.version) {
return this._mainThreadEditors.$tryApplyWorkspaceEdit([workspaceResourceEdit]);
return this._mainThreadEditors.$tryApplyWorkspaceEdit({ edits: [resourceEdit] });
}
// TODO@joh bubble this to listener?

View File

@@ -22,6 +22,7 @@ import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import URI from 'vs/base/common/uri';
class ExtensionMemento implements IExtensionMemento {
@@ -108,6 +109,7 @@ class ExtensionStoragePath {
join(storagePath, 'meta.json'),
JSON.stringify({
id: this._workspace.id,
configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
name: this._workspace.name
}, undefined, 2)
);
@@ -128,7 +130,6 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _proxy: MainThreadExtensionServiceShape;
private readonly _logService: ILogService;
private readonly _extHostLogService: ExtHostLogService;
private _activator: ExtensionsActivator;
private _extensionPathIndex: TPromise<TernarySearchTree<IExtensionDescription>>;
@@ -139,21 +140,20 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
extHostContext: IExtHostContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
logService: ILogService,
extHostLogService: ExtHostLogService,
environmentService: IEnvironmentService
) {
this._barrier = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._logService = logService;
this._extHostLogService = extHostLogService;
this._mainThreadTelemetry = extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._storage = new ExtHostStorage(extHostContext);
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._activator = null;
this._extHostLogService = new ExtHostLogService(environmentService);
// initialize API first (i.e. do not release barrier until the API is initialized)
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, logService);
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService);
initializeExtensionApi(this, apiFactory).then(() => {
@@ -314,14 +314,14 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
}
this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return TPromise.join<any>([
loadCommonJSModule(this._logService, extensionDescription.main, activationTimesBuilder),
loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(this._logService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
}, (errors: any[]) => {
// Avoid failing with an array of errors, fail with a single error
if (errors[0]) {
@@ -339,7 +339,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
return TPromise.join([
globalState.whenReady,
workspaceState.whenReady,

View File

@@ -17,7 +17,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics, DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto } 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';
@@ -255,7 +255,7 @@ class ReferenceAdapter {
}
}
export interface CustomCodeAction extends modes.CodeAction {
export interface CustomCodeAction extends CodeActionDto {
_isSynthetic?: boolean;
}
@@ -273,7 +273,8 @@ class CodeActionAdapter {
this._provider = provider;
}
provideCodeActions(resource: URI, range: IRange, context: modes.CodeActionContext): TPromise<modes.CodeAction[]> {
provideCodeActions(resource: URI, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
const doc = this._documents.getDocumentData(resource).document;
const ran = <vscode.Range>TypeConverters.toRange(range);
@@ -948,7 +949,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<modes.CodeAction[]> {
$provideCodeActions(handle: number, resource: UriComponents, range: IRange, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), range, context));
}

View File

@@ -8,47 +8,61 @@ import * as path from 'path';
import * as vscode from 'vscode';
import { TPromise } from 'vs/base/common/winjs.base';
import { mkdirp, dirExists } from 'vs/base/node/pfs';
import Event, { Emitter } from 'vs/base/common/event';
import Event from 'vs/base/common/event';
import { LogLevel } from 'vs/workbench/api/node/extHostTypes';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { memoize } from 'vs/base/common/decorators';
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
export class ExtHostLogService {
private _loggers: Map<string, ExtHostLogger> = new Map();
constructor(private _environmentService: IEnvironmentService) {
constructor(
windowId: number,
logLevel: LogLevel,
private _environmentService: IEnvironmentService
) {
super(createSpdLogService(`exthost${windowId}`, logLevel, _environmentService.logsPath));
}
$setLevel(level: LogLevel): void {
this.setLevel(level);
}
getExtLogger(extensionID: string): ExtHostLogger {
if (!this._loggers.has(extensionID)) {
const logService = createSpdLogService(extensionID, this._environmentService, extensionID);
const logsDirPath = path.join(this._environmentService.logsPath, extensionID);
this._loggers.set(extensionID, new ExtHostLogger(logService, logsDirPath));
let logger = this._loggers.get(extensionID);
if (!logger) {
logger = this.createLogger(extensionID);
this._loggers.set(extensionID, logger);
}
return logger;
}
return this._loggers.get(extensionID);
private createLogger(extensionID: string): ExtHostLogger {
const logService = createSpdLogService(extensionID, this.getLevel(), this._environmentService.logsPath, extensionID);
const logsDirPath = path.join(this._environmentService.logsPath, extensionID);
this._register(this.onDidChangeLogLevel(level => logService.setLevel(level)));
return new ExtHostLogger(logService, logsDirPath);
}
}
export class ExtHostLogger implements vscode.Logger {
private _currentLevel: LogLevel;
private _onDidChangeLogLevel: Emitter<LogLevel>;
constructor(
private readonly _logService: ILogService,
private readonly _logDirectory: string
) {
this._currentLevel = this._logService.getLevel();
this._onDidChangeLogLevel = new Emitter<LogLevel>();
this.onDidChangeLogLevel = this._onDidChangeLogLevel.event;
}
// TODO
readonly onDidChangeLogLevel: Event<LogLevel>;
get onDidChangeLogLevel(): Event<LogLevel> {
return this._logService.onDidChangeLogLevel;
}
get currentLevel(): LogLevel { return this._currentLevel; }
get currentLevel(): LogLevel { return this._logService.getLevel(); }
@memoize
get logDirectory(): TPromise<string> {

View File

@@ -12,7 +12,7 @@ import * as TypeConverters from './extHostTypeConverters';
import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext, IWorkspaceResourceEdit } from './extHost.protocol';
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext, WorkspaceEditDto } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostEditors implements ExtHostEditorsShape {
@@ -92,36 +92,23 @@ export class ExtHostEditors implements ExtHostEditorsShape {
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
let workspaceResourceEdits: IWorkspaceResourceEdit[] = [];
const dto: WorkspaceEditDto = { edits: [] };
let entries = edit.entries();
for (let entry of entries) {
let [uri, edits] = entry;
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
let docVersion: number = undefined;
if (doc) {
docVersion = doc.version;
}
let workspaceResourceEdit: IWorkspaceResourceEdit = {
resource: uri,
modelVersionId: docVersion,
edits: []
};
for (let edit of edits) {
workspaceResourceEdit.edits.push({
newText: edit.newText,
newEol: TypeConverters.EndOfLine.from(edit.newEol),
range: edit.range && TypeConverters.fromRange(edit.range)
for (let entry of edit.allEntries()) {
let [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
dto.edits.push({
resource: uri,
modelVersionId: doc && doc.version,
edits: uriOrEdits.map(TypeConverters.TextEdit.from)
});
} else {
dto.edits.push({ oldUri: uri, newUri: uriOrEdits });
}
workspaceResourceEdits.push(workspaceResourceEdit);
}
return this._proxy.$tryApplyWorkspaceEdit(workspaceResourceEdits);
return this._proxy.$tryApplyWorkspaceEdit(dto);
}
// --- called from main thread

View File

@@ -20,6 +20,7 @@ import { ISelection } from 'vs/editor/common/core/selection';
import * as htmlContent from 'vs/base/common/htmlContent';
import { IRelativePattern } from 'vs/base/common/glob';
import { LanguageSelector, LanguageFilter } from 'vs/editor/common/modes/languageSelector';
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/node/extHost.protocol';
export interface PositionLike {
line: number;
@@ -228,24 +229,36 @@ export const TextEdit = {
export namespace WorkspaceEdit {
export function from(value: vscode.WorkspaceEdit): modes.WorkspaceEdit {
const result: modes.WorkspaceEdit = { edits: [] };
for (let entry of value.entries()) {
let [uri, textEdits] = entry;
for (let textEdit of textEdits) {
result.edits.push({
resource: uri,
newText: textEdit.newText,
range: fromRange(textEdit.range)
});
const result: modes.WorkspaceEdit = {
edits: []
};
for (const entry of value.allEntries()) {
const [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
// text edits
result.edits.push({ resource: uri, edits: uriOrEdits.map(TextEdit.from) });
} else {
// resource edits
result.edits.push({ oldUri: uri, newUri: uriOrEdits });
}
}
return result;
}
export function to(value: modes.WorkspaceEdit) {
export function to(value: WorkspaceEditDto) {
const result = new types.WorkspaceEdit();
for (const edit of value.edits) {
result.replace(edit.resource, toRange(edit.range), edit.newText);
if (Array.isArray((<ResourceTextEditDto>edit).edits)) {
result.set(
URI.revive((<ResourceTextEditDto>edit).resource),
<types.TextEdit[]>(<ResourceTextEditDto>edit).edits.map(TextEdit.to)
);
} else {
result.renameResource(
URI.revive((<ResourceFileEditDto>edit).oldUri),
URI.revive((<ResourceFileEditDto>edit).newUri)
);
}
}
return result;
}

View File

@@ -492,10 +492,28 @@ export class TextEdit {
}
}
export class WorkspaceEdit {
export class WorkspaceEdit implements vscode.WorkspaceEdit {
private _values: [URI, TextEdit[]][] = [];
private _index = new Map<string, number>();
private _seqPool: number = 0;
private _resourceEdits: { seq: number, from: URI, to: URI }[] = [];
private _textEdits = new Map<string, { seq: number, uri: URI, edits: TextEdit[] }>();
createResource(uri: vscode.Uri): void {
this.renameResource(undefined, uri);
}
deleteResource(uri: vscode.Uri): void {
this.renameResource(uri, undefined);
}
renameResource(from: vscode.Uri, to: vscode.Uri): void {
this._resourceEdits.push({ seq: this._seqPool++, from, to });
}
resourceEdits(): [vscode.Uri, vscode.Uri][] {
return this._resourceEdits.map(({ from, to }) => (<[vscode.Uri, vscode.Uri]>[from, to]));
}
replace(uri: URI, range: Range, newText: string): void {
let edit = new TextEdit(range, newText);
@@ -503,8 +521,9 @@ export class WorkspaceEdit {
if (array) {
array.push(edit);
} else {
this.set(uri, [edit]);
array = [edit];
}
this.set(uri, array);
}
insert(resource: URI, position: Position, newText: string): void {
@@ -516,34 +535,58 @@ export class WorkspaceEdit {
}
has(uri: URI): boolean {
return this._index.has(uri.toString());
return this._textEdits.has(uri.toString());
}
set(uri: URI, edits: TextEdit[]): void {
const idx = this._index.get(uri.toString());
if (typeof idx === 'undefined') {
let newLen = this._values.push([uri, edits]);
this._index.set(uri.toString(), newLen - 1);
let data = this._textEdits.get(uri.toString());
if (!data) {
data = { seq: this._seqPool++, uri, edits: [] };
this._textEdits.set(uri.toString(), data);
}
if (!edits) {
data.edits = undefined;
} else {
this._values[idx][1] = edits;
data.edits = edits.slice(0);
}
}
get(uri: URI): TextEdit[] {
let idx = this._index.get(uri.toString());
return typeof idx !== 'undefined' && this._values[idx][1];
if (!this._textEdits.has(uri.toString())) {
return undefined;
}
const { edits } = this._textEdits.get(uri.toString());
return edits ? edits.slice() : undefined;
}
entries(): [URI, TextEdit[]][] {
return this._values;
const res: [URI, TextEdit[]][] = [];
this._textEdits.forEach(value => res.push([value.uri, value.edits]));
return res.slice();
}
allEntries(): ([URI, TextEdit[]] | [URI, URI])[] {
// use the 'seq' the we have assigned when inserting
// the operation and use that order in the resulting
// array
const res: ([URI, TextEdit[]] | [URI, URI])[] = [];
this._textEdits.forEach(value => {
const { seq, uri, edits } = value;
res[seq] = [uri, edits];
});
this._resourceEdits.forEach(value => {
const { seq, from, to } = value;
res[seq] = [from, to];
});
return res;
}
get size(): number {
return this._values.length;
return this._textEdits.size + this._resourceEdits.length;
}
toJSON(): any {
return this._values;
return this.entries();
}
}