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

@@ -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();
}
}