提交 02ee3562 编写于 作者: A Alex Dima

Have `workspace.applyEdit` correctly account for model version changes and...

Have `workspace.applyEdit` correctly account for model version changes and reply with false if a model was loaded and changed in the meantime
上级 012d7da5
......@@ -250,7 +250,7 @@ export class MainThreadDocumentsAndEditors {
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService);
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
const mainThreadEditors = new MainThreadEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, telemetryService);
const mainThreadEditors = new MainThreadEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, telemetryService, textModelResolverService, fileService, this._modelService);
extHostContext.set(MainContext.MainThreadEditors, mainThreadEditors);
// It is expected that the ctor of the state computer calls our `_onDelta`.
......
......@@ -8,7 +8,7 @@ import URI from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { disposed } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { ISingleEditOperation, IDecorationRenderOptions, IDecorationOptions, ILineChange } from 'vs/editor/common/editorCommon';
import { ISingleEditOperation, IDecorationRenderOptions, IDecorationOptions, ILineChange, ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
......@@ -18,9 +18,13 @@ import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOption
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { equals as objectEquals } from 'vs/base/common/objects';
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext, IWorkspaceResourceEdit } 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/common/services/bulkEdit';
import { IModelService } from 'vs/editor/common/services/modelService';
export class MainThreadEditors implements MainThreadEditorsShape {
......@@ -38,7 +42,10 @@ export class MainThreadEditors implements MainThreadEditorsShape {
@ICodeEditorService private _codeEditorService: ICodeEditorService,
@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ITelemetryService telemetryService: ITelemetryService
@ITelemetryService telemetryService: ITelemetryService,
@ITextModelService private readonly _textModelResolverService: ITextModelService,
@IFileService private readonly _fileService: IFileService,
@IModelService private readonly _modelService: IModelService,
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostEditors);
this._documentsAndEditors = documentsAndEditors;
......@@ -194,6 +201,52 @@ export class MainThreadEditors implements MainThreadEditorsShape {
return TPromise.as(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
}
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {
// 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) {
let model = this._modelService.getModel(workspaceResourceEdit.resource);
if (model && model.getVersionId() !== workspaceResourceEdit.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 = workspaceResourceEdit.resource;
const edits = workspaceResourceEdit.edits;
for (let j = 0, lenJ = edits.length; j < lenJ; j++) {
const edit = edits[j];
resourceEdits.push({
resource: <URI>uri,
newText: edit.newText,
newEol: edit.newEol,
range: edit.range
});
}
}
let codeEditor: ICommonCodeEditor;
let editor = this._workbenchEditorService.getActiveEditor();
if (editor) {
let candidate = editor.getControl();
if (isCommonCodeEditor(candidate)) {
codeEditor = candidate;
}
}
return bulkEdit(this._textModelResolverService, codeEditor, resourceEdits, this._fileService)
.then(() => true);
}
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {
if (!this._documentsAndEditors.getEditor(id)) {
return TPromise.wrapError<boolean>(disposed(`TextEditor(${id})`));
......
......@@ -8,13 +8,9 @@ import { isPromiseCanceledError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import { ISearchService, QueryType, ISearchQuery, ISearchProgressItem, ISearchComplete } from 'vs/platform/search/common/search';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
import { bulkEdit, IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { TPromise, PPromise } from 'vs/base/common/winjs.base';
import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IFileService } from 'vs/platform/files/common/files';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService';
......@@ -34,8 +30,6 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@ISearchService private readonly _searchService: ISearchService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
@ITextModelService private readonly _textModelResolverService: ITextModelService,
@IExperimentService private experimentService: IExperimentService,
@IFileService private readonly _fileService: IFileService
) {
......@@ -109,21 +103,6 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
});
}
$applyWorkspaceEdit(edits: IResourceEdit[]): TPromise<boolean> {
let codeEditor: ICommonCodeEditor;
let editor = this._editorService.getActiveEditor();
if (editor) {
let candidate = editor.getControl();
if (isCommonCodeEditor(candidate)) {
codeEditor = candidate;
}
}
return bulkEdit(this._textModelResolverService, codeEditor, edits, this._fileService)
.then(() => true);
}
// --- EXPERIMENT: workspace provider
private _idPool: number = 0;
......
......@@ -84,7 +84,7 @@ export function createApiFactory(
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)));
const extHostDocumentSaveParticipant = threadService.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadEditors)));
const extHostEditors = threadService.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(threadService, extHostDocumentsAndEditors));
const extHostCommands = threadService.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(threadService, extHostHeapService));
const extHostTreeViews = threadService.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(threadService.get(MainContext.MainThreadTreeViews), extHostCommands));
......@@ -417,7 +417,7 @@ export function createApiFactory(
return extHostWorkspace.saveAll(includeUntitled);
},
applyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
return extHostWorkspace.appyEdit(edit);
return extHostEditors.applyWorkspaceEdit(edit);
},
createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => {
return extHostFileSystemEvent.createFileSystemWatcher(pattern, ignoreCreate, ignoreChange, ignoreDelete);
......
......@@ -26,7 +26,6 @@ import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/pro
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { ITextSource } from 'vs/editor/common/model/textSource';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
......@@ -189,6 +188,16 @@ export interface ITextDocumentShowOptions {
selection?: IRange;
}
export interface IWorkspaceResourceEdit {
resource: URI;
modelVersionId?: number;
edits: {
range?: IRange;
newText: string;
newEol?: editorCommon.EndOfLineSequence;
}[];
}
export interface MainThreadEditorsShape extends IDisposable {
$tryShowTextDocument(resource: URI, options: ITextDocumentShowOptions): TPromise<string>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
......@@ -200,6 +209,7 @@ export interface MainThreadEditorsShape extends IDisposable {
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any>;
$trySetSelections(id: string, selections: ISelection[]): TPromise<any>;
$tryApplyEdits(id: string, modelVersionId: number, edits: editorCommon.ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean>;
$tryApplyWorkspaceEdit(workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): TPromise<any>;
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]>;
}
......@@ -301,7 +311,6 @@ export interface MainThreadWorkspaceShape extends IDisposable {
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<URI[]>;
$cancelSearch(requestId: number): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
$applyWorkspaceEdit(edits: IResourceEdit[]): TPromise<boolean>;
$registerFileSystemProvider(handle: number, authority: string): void;
$unregisterFileSystemProvider(handle: number): void;
......
......@@ -10,10 +10,9 @@ import URI from 'vs/base/common/uri';
import { sequence, always } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainThreadWorkspaceShape, ExtHostDocumentSaveParticipantShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentSaveParticipantShape, MainThreadEditorsShape, IWorkspaceResourceEdit } 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 { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
......@@ -21,14 +20,14 @@ import * as vscode from 'vscode';
export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSaveParticipantShape {
private _documents: ExtHostDocuments;
private _workspace: MainThreadWorkspaceShape;
private _mainThreadEditors: MainThreadEditorsShape;
private _callbacks = new CallbackList();
private _badListeners = new WeakMap<Function, number>();
private _thresholds: { timeout: number; errors: number; };
constructor(documents: ExtHostDocuments, workspace: MainThreadWorkspaceShape, thresholds: { timeout: number; errors: number; } = { timeout: 1500, errors: 3 }) {
constructor(documents: ExtHostDocuments, mainThreadEditors: MainThreadEditorsShape, thresholds: { timeout: number; errors: number; } = { timeout: 1500, errors: 3 }) {
this._documents = documents;
this._workspace = workspace;
this._mainThreadEditors = mainThreadEditors;
this._thresholds = thresholds;
}
......@@ -133,13 +132,15 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
}).then(values => {
let edits: IResourceEdit[] = [];
let workspaceResourceEdit: IWorkspaceResourceEdit = {
resource: <URI>document.uri,
edits: []
};
for (const value of values) {
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
for (const { newText, newEol, range } of value) {
edits.push({
resource: <URI>document.uri,
workspaceResourceEdit.edits.push({
range: range && fromRange(range),
newText,
newEol: EndOfLine.from(newEol)
......@@ -150,12 +151,12 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
// apply edits if any and if document
// didn't change somehow in the meantime
if (edits.length === 0) {
if (workspaceResourceEdit.edits.length === 0) {
return undefined;
}
if (version === document.version) {
return this._workspace.$applyWorkspaceEdit(edits);
return this._mainThreadEditors.$tryApplyWorkspaceEdit([workspaceResourceEdit]);
}
// TODO@joh bubble this to listener?
......
......@@ -13,7 +13,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 } from './extHost.protocol';
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext, IWorkspaceResourceEdit } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostEditors implements ExtHostEditorsShape {
......@@ -91,6 +91,40 @@ export class ExtHostEditors implements ExtHostEditorsShape {
return new TextEditorDecorationType(this._proxy, options);
}
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
let workspaceResourceEdits: IWorkspaceResourceEdit[] = [];
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>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)
});
}
workspaceResourceEdits.push(workspaceResourceEdit);
}
return this._proxy.$tryApplyWorkspaceEdit(workspaceResourceEdits);
}
// --- called from main thread
$acceptOptionsChanged(id: string, opts: IResolvedTextEditorConfiguration): void {
......
......@@ -10,9 +10,7 @@ import { normalize } from 'vs/base/common/paths';
import { delta } from 'vs/base/common/arrays';
import { relative } from 'path';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
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';
......@@ -182,27 +180,6 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return this._proxy.$saveAll(includeUntitled);
}
appyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
let resourceEdits: IResourceEdit[] = [];
let entries = edit.entries();
for (let entry of entries) {
let [uri, edits] = entry;
for (let edit of edits) {
resourceEdits.push({
resource: <URI>uri,
newText: edit.newText,
newEol: EndOfLine.from(edit.newEol),
range: edit.range && fromRange(edit.range)
});
}
}
return this._proxy.$applyWorkspaceEdit(resourceEdits);
}
// --- EXPERIMENT: workspace resolver
......
......@@ -10,10 +10,9 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { TextDocumentSaveReason, TextEdit, Position, EndOfLine } from 'vs/workbench/api/node/extHostTypes';
import { MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol';
import { MainThreadEditorsShape, IWorkspaceResourceEdit } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
import { OneGetThreadService } from './testThreadService';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
......@@ -21,7 +20,7 @@ import { mock } from 'vs/workbench/test/electron-browser/api/mock';
suite('ExtHostDocumentSaveParticipant', () => {
let resource = URI.parse('foo:bar');
let workspace = new class extends mock<MainThreadWorkspaceShape>() { };
let mainThreadEditors = new class extends mock<MainThreadEditorsShape>() { };
let documents: ExtHostDocuments;
setup(() => {
......@@ -40,12 +39,12 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('no listeners, no problem', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => assert.ok(true));
});
test('event delivery', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let event: vscode.TextDocumentWillSaveEvent;
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
......@@ -62,7 +61,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, immutable', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let event: vscode.TextDocumentWillSaveEvent;
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
......@@ -78,7 +77,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, bad listener', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
throw new Error('💀');
......@@ -93,7 +92,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, bad listener doesn\'t prevent more events', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
throw new Error('💀');
......@@ -112,7 +111,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, in subscriber order', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let counter = 0;
let sub1 = participant.onWillSaveTextDocumentEvent(function (event) {
......@@ -130,7 +129,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, ignore bad listeners', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 5, errors: 1 });
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 5, errors: 1 });
let callCount = 0;
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
......@@ -151,7 +150,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, overall timeout', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 20, errors: 5 });
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 20, errors: 5 });
let callCount = 0;
let sub1 = participant.onWillSaveTextDocumentEvent(function (event) {
......@@ -179,7 +178,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
......@@ -195,7 +194,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil must be called sync', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
......@@ -218,7 +217,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil will timeout', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 5, errors: 3 });
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 5, errors: 3 });
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
event.waitUntil(TPromise.timeout(15));
......@@ -233,7 +232,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil failure handling', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
e.waitUntil(TPromise.wrapError(new Error('dddd')));
......@@ -253,9 +252,9 @@ suite('ExtHostDocumentSaveParticipant', () => {
test('event delivery, pushEdits sync', () => {
let edits: IResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadWorkspaceShape>() {
$applyWorkspaceEdit(_edits) {
let edits: IWorkspaceResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: IWorkspaceResourceEdit[]) {
edits = _edits;
return TPromise.as(true);
}
......@@ -269,15 +268,16 @@ suite('ExtHostDocumentSaveParticipant', () => {
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
assert.equal(edits.length, 2);
assert.equal(edits.length, 1);
assert.equal(edits[0].edits.length, 2);
});
});
test('event delivery, concurrent change', () => {
let edits: IResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadWorkspaceShape>() {
$applyWorkspaceEdit(_edits) {
let edits: IWorkspaceResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: IWorkspaceResourceEdit[]) {
edits = _edits;
return TPromise.as(true);
}
......@@ -310,20 +310,23 @@ suite('ExtHostDocumentSaveParticipant', () => {
test('event delivery, two listeners -> two document states', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadWorkspaceShape>() {
$applyWorkspaceEdit(_edits: IResourceEdit[]) {
for (const { resource, newText, range } of _edits) {
documents.$acceptModelChanged(resource.toString(), {
changes: [{
range,
rangeLength: undefined,
text: newText
}],
eol: undefined,
versionId: documents.getDocumentData(resource).version + 1
}, true);
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: IWorkspaceResourceEdit[]) {
for (const { resource, edits } of _edits) {
for (const { newText, range } of edits) {
documents.$acceptModelChanged(resource.toString(), {
changes: [{
range,
rangeLength: undefined,
text: newText
}],
eol: undefined,
versionId: documents.getDocumentData(resource).version + 1
}, true);
}
}
return TPromise.as(true);
}
});
......
......@@ -64,6 +64,7 @@ suite('ExtHostTextEditorOptions', () => {
$tryRevealRange: undefined,
$trySetSelections: undefined,
$tryApplyEdits: undefined,
$tryApplyWorkspaceEdit: undefined,
$tryInsertSnippet: undefined,
$getDiffInformation: undefined
};
......
/*---------------------------------------------------------------------------------------------
* 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 assert from 'assert';
import { TPromise } from 'vs/base/common/winjs.base';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import { MainContext, MainThreadEditorsShape, IWorkspaceResourceEdit } from 'vs/workbench/api/node/extHost.protocol';
import URI from 'vs/base/common/uri';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { OneGetThreadService, TestThreadService } from 'vs/workbench/test/electron-browser/api/testThreadService';
import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors';
suite('ExtHostTextEditors.applyWorkspaceEdit', () => {
const resource = URI.parse('foo:bar');
let editors: ExtHostEditors;
let workspaceResourceEdits: IWorkspaceResourceEdit[];
setup(() => {
workspaceResourceEdits = null;
let threadService = new TestThreadService();
threadService.setTestInstance(MainContext.MainThreadEditors, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {
workspaceResourceEdits = _workspaceResourceEdits;
return TPromise.as(true);
}
});
const documentsAndEditors = new ExtHostDocumentsAndEditors(OneGetThreadService(null));
documentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
isDirty: false,
modeId: 'foo',
url: resource,
versionId: 1337,
lines: ['foo'],
EOL: '\n',
}]
});
editors = new ExtHostEditors(threadService, documentsAndEditors);
});
test('uses version id if document available', () => {
let edit = new extHostTypes.WorkspaceEdit();
edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello');
return editors.applyWorkspaceEdit(edit).then((result) => {
assert.equal(workspaceResourceEdits.length, 1);
assert.equal(workspaceResourceEdits[0].modelVersionId, 1337);
});
});
test('does not use version id if document is not available', () => {
let edit = new extHostTypes.WorkspaceEdit();
edit.replace(URI.parse('foo:bar2'), new extHostTypes.Range(0, 0, 0, 0), 'hello');
return editors.applyWorkspaceEdit(edit).then((result) => {
assert.equal(workspaceResourceEdits.length, 1);
assert.ok(typeof workspaceResourceEdits[0].modelVersionId === 'undefined');
});
});
});
/*---------------------------------------------------------------------------------------------
* 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 assert from 'assert';
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors';
import { OneGetThreadService, TestThreadService } from './testThreadService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { MockCodeEditorService } from 'vs/editor/test/common/mocks/mockCodeEditorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditorsShape, IWorkspaceResourceEdit, ExtHostContext, ExtHostDocumentsShape } from 'vs/workbench/api/node/extHost.protocol';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import Event from 'vs/base/common/event';
import { MainThreadEditors } from 'vs/workbench/api/electron-browser/mainThreadEditors';
import URI from 'vs/base/common/uri';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { IModelService } from 'vs/editor/common/services/modelService';
import { EditOperation } from 'vs/editor/common/core/editOperation';
suite('MainThreadEditors', () => {
const resource = URI.parse('foo:bar');
let modelService: IModelService;
let editors: MainThreadEditors;
setup(() => {
const configService = new TestConfigurationService();
modelService = new ModelServiceImpl(null, configService);
const codeEditorService = new MockCodeEditorService();
const textFileService = new class extends mock<ITextFileService>() {
isDirty() { return false; };
models = <any>{
onModelSaved: Event.None,
onModelReverted: Event.None,
onModelDirty: Event.None,
};
};
const workbenchEditorService = <IWorkbenchEditorService>{
getVisibleEditors() { return []; },
getActiveEditor() { return undefined; }
};
const editorGroupService = new class extends mock<IEditorGroupService>() {
onEditorsChanged = Event.None;
onEditorsMoved = Event.None;
};
const testThreadService = new TestThreadService(true);
testThreadService.setTestInstance(ExtHostContext.ExtHostDocuments, new class extends mock<ExtHostDocumentsShape>() {
$acceptModelChanged(): void {
}
});
testThreadService.setTestInstance(ExtHostContext.ExtHostDocumentsAndEditors, new class extends mock<ExtHostDocumentsAndEditorsShape>() {
$acceptDocumentsAndEditorsDelta(): void {
}
});
const documentAndEditor = new MainThreadDocumentsAndEditors(
testThreadService,
modelService,
textFileService,
workbenchEditorService,
codeEditorService,
null,
null,
null,
null,
editorGroupService,
null
);
editors = new MainThreadEditors(
documentAndEditor,
OneGetThreadService(null),
codeEditorService,
workbenchEditorService,
editorGroupService,
null,
null,
null,
modelService
);
});
test(`applyWorkspaceEdit returns false if model is changed by user`, () => {
let model = modelService.createModel('something', null, resource);
let workspaceResourceEdit: IWorkspaceResourceEdit = {
resource: resource,
modelVersionId: model.getVersionId(),
edits: [{
newText: 'asdfg',
range: new Range(1, 1, 1, 1)
}]
};
// Act as if the user edited the model
model.applyEdits([EditOperation.insert(new Position(0, 0), 'something')]);
return editors.$tryApplyWorkspaceEdit([workspaceResourceEdit]).then((result) => {
assert.equal(result, false);
});
});
});
......@@ -76,8 +76,8 @@ export abstract class AbstractTestThreadService {
}
export class TestThreadService extends AbstractTestThreadService implements IThreadService {
constructor() {
super(false);
constructor(isMainProcess: boolean = false) {
super(isMainProcess);
}
private _callCountValue: number = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册