Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { Event } from '../../../../base/common/event.js';
8
import { DisposableStore, IReference, ImmortalReference } from '../../../../base/common/lifecycle.js';
9
import { URI } from '../../../../base/common/uri.js';
10
import { mock } from '../../../../base/test/common/mock.js';
11
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
12
import { IBulkEditService } from '../../../../editor/browser/services/bulkEditService.js';
13
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
14
import { EditOperation } from '../../../../editor/common/core/editOperation.js';
15
import { Position } from '../../../../editor/common/core/position.js';
16
import { Range } from '../../../../editor/common/core/range.js';
17
import { ITextSnapshot } from '../../../../editor/common/model.js';
18
import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js';
19
import { IModelService } from '../../../../editor/common/services/model.js';
20
import { ModelService } from '../../../../editor/common/services/modelService.js';
21
import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js';
22
import { TestCodeEditorService } from '../../../../editor/test/browser/editorTestServices.js';
23
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
24
import { TestConfigurationService } from '../../../../platform/configuration/test/common/testConfigurationService.js';
25
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
26
import { TestDialogService } from '../../../../platform/dialogs/test/common/testDialogService.js';
27
import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
28
import { IFileService } from '../../../../platform/files/common/files.js';
29
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
30
import { InstantiationService } from '../../../../platform/instantiation/common/instantiationService.js';
31
import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';
32
import { ILabelService } from '../../../../platform/label/common/label.js';
33
import { ILogService, NullLogService } from '../../../../platform/log/common/log.js';
34
import { INotificationService } from '../../../../platform/notification/common/notification.js';
35
import { TestNotificationService } from '../../../../platform/notification/test/common/testNotificationService.js';
36
import { TestThemeService } from '../../../../platform/theme/test/common/testThemeService.js';
37
import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js';
38
import { UndoRedoService } from '../../../../platform/undoRedo/common/undoRedoService.js';
39
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
40
import { UriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentityService.js';
41
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
42
import { MainThreadBulkEdits } from '../../browser/mainThreadBulkEdits.js';
43
import { IWorkspaceTextEditDto } from '../../common/extHost.protocol.js';
44
import { SingleProxyRPCProtocol } from '../common/testRPCProtocol.js';
45
import { BulkEditService } from '../../../contrib/bulkEdit/browser/bulkEditService.js';
46
import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';
47
import { IEditorService } from '../../../services/editor/common/editorService.js';
48
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
49
import { SerializableObjectWithBuffers } from '../../../services/extensions/common/proxyIdentifier.js';
50
import { LabelService } from '../../../services/label/common/labelService.js';
51
import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js';
52
import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js';
53
import { ITextFileService } from '../../../services/textfile/common/textfiles.js';
54
import { ICopyOperation, ICreateFileOperation, ICreateOperation, IDeleteOperation, IMoveOperation, IWorkingCopyFileService } from '../../../services/workingCopy/common/workingCopyFileService.js';
55
import { IWorkingCopyService } from '../../../services/workingCopy/common/workingCopyService.js';
56
import { TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestFileService, TestLifecycleService, TestWorkingCopyService } from '../../../test/browser/workbenchTestServices.js';
57
import { TestContextService, TestTextResourcePropertiesService } from '../../../test/common/workbenchTestServices.js';
58
import { ILanguageService } from '../../../../editor/common/languages/language.js';
59
import { LanguageService } from '../../../../editor/common/services/languageService.js';
60
import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js';
61
import { TestLanguageConfigurationService } from '../../../../editor/test/common/modes/testLanguageConfigurationService.js';
62
import { ITreeSitterLibraryService } from '../../../../editor/common/services/treeSitter/treeSitterLibraryService.js';
63
import { TestTreeSitterLibraryService } from '../../../../editor/test/common/services/testTreeSitterLibraryService.js';
64
65
suite('MainThreadEditors', () => {
66
67
let disposables: DisposableStore;
68
const resource = URI.parse('foo:bar');
69
70
let modelService: IModelService;
71
72
let bulkEdits: MainThreadBulkEdits;
73
74
const movedResources = new Map<URI, URI>();
75
const copiedResources = new Map<URI, URI>();
76
const createdResources = new Set<URI>();
77
const deletedResources = new Set<URI>();
78
79
setup(() => {
80
disposables = new DisposableStore();
81
82
movedResources.clear();
83
copiedResources.clear();
84
createdResources.clear();
85
deletedResources.clear();
86
87
const configService = new TestConfigurationService();
88
const dialogService = new TestDialogService();
89
const notificationService = new TestNotificationService();
90
const undoRedoService = new UndoRedoService(dialogService, notificationService);
91
const themeService = new TestThemeService();
92
93
const services = new ServiceCollection();
94
services.set(IBulkEditService, new SyncDescriptor(BulkEditService));
95
services.set(ILabelService, new SyncDescriptor(LabelService));
96
services.set(ILogService, new NullLogService());
97
services.set(IWorkspaceContextService, new TestContextService());
98
services.set(IEnvironmentService, TestEnvironmentService);
99
services.set(IWorkbenchEnvironmentService, TestEnvironmentService);
100
services.set(IConfigurationService, configService);
101
services.set(IDialogService, dialogService);
102
services.set(INotificationService, notificationService);
103
services.set(IUndoRedoService, undoRedoService);
104
services.set(IModelService, modelService);
105
services.set(ICodeEditorService, new TestCodeEditorService(themeService));
106
services.set(IFileService, new TestFileService());
107
services.set(IUriIdentityService, new SyncDescriptor(UriIdentityService));
108
services.set(ITreeSitterLibraryService, new TestTreeSitterLibraryService());
109
services.set(IEditorService, disposables.add(new TestEditorService()));
110
services.set(ILifecycleService, new TestLifecycleService());
111
services.set(IWorkingCopyService, new TestWorkingCopyService());
112
services.set(IEditorGroupsService, new TestEditorGroupsService());
113
services.set(ITextFileService, new class extends mock<ITextFileService>() {
114
override isDirty() { return false; }
115
override files = <any>{
116
onDidSave: Event.None,
117
onDidRevert: Event.None,
118
onDidChangeDirty: Event.None
119
};
120
override create(operations: { resource: URI }[]) {
121
for (const o of operations) {
122
createdResources.add(o.resource);
123
}
124
return Promise.resolve(Object.create(null));
125
}
126
override async getEncodedReadable(resource: URI, value?: string | ITextSnapshot): Promise<any> {
127
return undefined;
128
}
129
});
130
services.set(IWorkingCopyFileService, new class extends mock<IWorkingCopyFileService>() {
131
override onDidRunWorkingCopyFileOperation = Event.None;
132
override createFolder(operations: ICreateOperation[]): any {
133
this.create(operations);
134
}
135
override create(operations: ICreateFileOperation[]) {
136
for (const operation of operations) {
137
createdResources.add(operation.resource);
138
}
139
return Promise.resolve(Object.create(null));
140
}
141
override move(operations: IMoveOperation[]) {
142
const { source, target } = operations[0].file;
143
movedResources.set(source, target);
144
return Promise.resolve(Object.create(null));
145
}
146
override copy(operations: ICopyOperation[]) {
147
const { source, target } = operations[0].file;
148
copiedResources.set(source, target);
149
return Promise.resolve(Object.create(null));
150
}
151
override delete(operations: IDeleteOperation[]) {
152
for (const operation of operations) {
153
deletedResources.add(operation.resource);
154
}
155
return Promise.resolve(undefined);
156
}
157
});
158
services.set(ITextModelService, new class extends mock<ITextModelService>() {
159
override createModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
160
const textEditorModel = new class extends mock<IResolvedTextEditorModel>() {
161
override textEditorModel = modelService.getModel(resource)!;
162
};
163
textEditorModel.isReadonly = () => false;
164
return Promise.resolve(new ImmortalReference(textEditorModel));
165
}
166
});
167
services.set(IEditorWorkerService, new class extends mock<IEditorWorkerService>() {
168
169
});
170
services.set(IPaneCompositePartService, new class extends mock<IPaneCompositePartService>() implements IPaneCompositePartService {
171
override onDidPaneCompositeOpen = Event.None;
172
override onDidPaneCompositeClose = Event.None;
173
override getActivePaneComposite() {
174
return undefined;
175
}
176
});
177
178
services.set(ILanguageService, disposables.add(new LanguageService()));
179
services.set(ILanguageConfigurationService, new TestLanguageConfigurationService());
180
181
const instaService = new InstantiationService(services);
182
183
modelService = new ModelService(
184
configService,
185
new TestTextResourcePropertiesService(configService),
186
undoRedoService,
187
instaService
188
);
189
190
bulkEdits = instaService.createInstance(MainThreadBulkEdits, SingleProxyRPCProtocol(null));
191
});
192
193
teardown(() => {
194
disposables.dispose();
195
});
196
197
ensureNoDisposablesAreLeakedInTestSuite();
198
199
test(`applyWorkspaceEdit returns false if model is changed by user`, () => {
200
201
const model = disposables.add(modelService.createModel('something', null, resource));
202
203
const workspaceResourceEdit: IWorkspaceTextEditDto = {
204
resource: resource,
205
versionId: model.getVersionId(),
206
textEdit: {
207
text: 'asdfg',
208
range: new Range(1, 1, 1, 1)
209
}
210
};
211
212
// Act as if the user edited the model
213
model.applyEdits([EditOperation.insert(new Position(0, 0), 'something')]);
214
215
return bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({ edits: [workspaceResourceEdit] })).then((result) => {
216
assert.strictEqual(result, false);
217
});
218
});
219
220
test(`issue #54773: applyWorkspaceEdit checks model version in race situation`, () => {
221
222
const model = disposables.add(modelService.createModel('something', null, resource));
223
224
const workspaceResourceEdit1: IWorkspaceTextEditDto = {
225
resource: resource,
226
versionId: model.getVersionId(),
227
textEdit: {
228
text: 'asdfg',
229
range: new Range(1, 1, 1, 1)
230
}
231
};
232
const workspaceResourceEdit2: IWorkspaceTextEditDto = {
233
resource: resource,
234
versionId: model.getVersionId(),
235
textEdit: {
236
text: 'asdfg',
237
range: new Range(1, 1, 1, 1)
238
}
239
};
240
241
const p1 = bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({ edits: [workspaceResourceEdit1] })).then((result) => {
242
// first edit request succeeds
243
assert.strictEqual(result, true);
244
});
245
const p2 = bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({ edits: [workspaceResourceEdit2] })).then((result) => {
246
// second edit request fails
247
assert.strictEqual(result, false);
248
});
249
return Promise.all([p1, p2]);
250
});
251
252
test(`applyWorkspaceEdit with only resource edit`, () => {
253
return bulkEdits.$tryApplyWorkspaceEdit(new SerializableObjectWithBuffers({
254
edits: [
255
{ oldResource: resource, newResource: resource, options: undefined },
256
{ oldResource: undefined, newResource: resource, options: undefined },
257
{ oldResource: resource, newResource: undefined, options: undefined }
258
]
259
})).then((result) => {
260
assert.strictEqual(result, true);
261
assert.strictEqual(movedResources.get(resource), resource);
262
assert.strictEqual(createdResources.has(resource), true);
263
assert.strictEqual(deletedResources.has(resource), true);
264
});
265
});
266
});
267
268