Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/testCodeEditor.ts
5222 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 { DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
7
import { mock } from '../../../base/test/common/mock.js';
8
import { EditorConfiguration } from '../../browser/config/editorConfiguration.js';
9
import { IActiveCodeEditor, ICodeEditor } from '../../browser/editorBrowser.js';
10
import { ICodeEditorService } from '../../browser/services/codeEditorService.js';
11
import { View } from '../../browser/view.js';
12
import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../browser/widget/codeEditor/codeEditorWidget.js';
13
import * as editorOptions from '../../common/config/editorOptions.js';
14
import { IEditorContribution } from '../../common/editorCommon.js';
15
import { ILanguageService } from '../../common/languages/language.js';
16
import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js';
17
import { ITextBufferFactory, ITextModel } from '../../common/model.js';
18
import { IEditorWorkerService } from '../../common/services/editorWorker.js';
19
import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../common/services/languageFeatureDebounce.js';
20
import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js';
21
import { LanguageFeaturesService } from '../../common/services/languageFeaturesService.js';
22
import { LanguageService } from '../../common/services/languageService.js';
23
import { IModelService } from '../../common/services/model.js';
24
import { ModelService } from '../../common/services/modelService.js';
25
import { ITextResourcePropertiesService } from '../../common/services/textResourceConfiguration.js';
26
import { ViewModel } from '../../common/viewModel/viewModelImpl.js';
27
import { TestConfiguration } from './config/testConfiguration.js';
28
import { TestCodeEditorService, TestCommandService } from './editorTestServices.js';
29
import { TestLanguageConfigurationService } from '../common/modes/testLanguageConfigurationService.js';
30
import { TestEditorWorkerService } from '../common/services/testEditorWorkerService.js';
31
import { TestTextResourcePropertiesService } from '../common/services/testTextResourcePropertiesService.js';
32
import { instantiateTextModel } from '../common/testTextModel.js';
33
import { AccessibilitySupport, IAccessibilityService } from '../../../platform/accessibility/common/accessibility.js';
34
import { TestAccessibilityService } from '../../../platform/accessibility/test/common/testAccessibilityService.js';
35
import { MenuId } from '../../../platform/actions/common/actions.js';
36
import { IClipboardService } from '../../../platform/clipboard/common/clipboardService.js';
37
import { TestClipboardService } from '../../../platform/clipboard/test/common/testClipboardService.js';
38
import { ICommandService } from '../../../platform/commands/common/commands.js';
39
import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';
40
import { TestConfigurationService } from '../../../platform/configuration/test/common/testConfigurationService.js';
41
import { IContextKeyService, IContextKeyServiceTarget } from '../../../platform/contextkey/common/contextkey.js';
42
import { IDialogService } from '../../../platform/dialogs/common/dialogs.js';
43
import { TestDialogService } from '../../../platform/dialogs/test/common/testDialogService.js';
44
import { IEnvironmentService } from '../../../platform/environment/common/environment.js';
45
import { SyncDescriptor } from '../../../platform/instantiation/common/descriptors.js';
46
import { BrandedService, IInstantiationService, ServiceIdentifier, ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js';
47
import { ServiceCollection } from '../../../platform/instantiation/common/serviceCollection.js';
48
import { TestInstantiationService } from '../../../platform/instantiation/test/common/instantiationServiceMock.js';
49
import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';
50
import { MockContextKeyService, MockKeybindingService } from '../../../platform/keybinding/test/common/mockKeybindingService.js';
51
import { ILoggerService, ILogService, NullLoggerService, NullLogService } from '../../../platform/log/common/log.js';
52
import { INotificationService } from '../../../platform/notification/common/notification.js';
53
import { TestNotificationService } from '../../../platform/notification/test/common/testNotificationService.js';
54
import { IOpenerService } from '../../../platform/opener/common/opener.js';
55
import { NullOpenerService } from '../../../platform/opener/test/common/nullOpenerService.js';
56
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js';
57
import { NullTelemetryServiceShape } from '../../../platform/telemetry/common/telemetryUtils.js';
58
import { IThemeService } from '../../../platform/theme/common/themeService.js';
59
import { TestThemeService } from '../../../platform/theme/test/common/testThemeService.js';
60
import { IUndoRedoService } from '../../../platform/undoRedo/common/undoRedo.js';
61
import { UndoRedoService } from '../../../platform/undoRedo/common/undoRedoService.js';
62
import { ITreeSitterLibraryService } from '../../common/services/treeSitter/treeSitterLibraryService.js';
63
import { TestTreeSitterLibraryService } from '../common/services/testTreeSitterLibraryService.js';
64
import { IInlineCompletionsService, InlineCompletionsService } from '../../browser/services/inlineCompletionsService.js';
65
import { EditorCommand } from '../../browser/editorExtensions.js';
66
import { IDataChannelService, NullDataChannelService } from '../../../platform/dataChannel/common/dataChannel.js';
67
import { IUserInteractionService, MockUserInteractionService } from '../../../platform/userInteraction/browser/userInteractionService.js';
68
69
export interface ITestCodeEditor extends IActiveCodeEditor {
70
getViewModel(): ViewModel | undefined;
71
registerAndInstantiateContribution<T extends IEditorContribution, Services extends BrandedService[]>(id: string, ctor: new (editor: ICodeEditor, ...services: Services) => T): T;
72
registerDisposable(disposable: IDisposable): void;
73
runCommand(command: ITestEditorCommand, args?: any): void | Promise<void>;
74
runAction(action: ITestEditorAction, args?: any): void | Promise<void>;
75
}
76
77
export interface ITestEditorCommand {
78
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args?: any): void | Promise<void>;
79
}
80
81
export interface ITestEditorAction {
82
run(accessor: ServicesAccessor, editor: ICodeEditor, args?: any): void | Promise<void>;
83
}
84
85
export class TestCodeEditor extends CodeEditorWidget implements ICodeEditor {
86
87
//#region testing overrides
88
protected override _createConfiguration(isSimpleWidget: boolean, contextMenuId: MenuId, options: Readonly<TestCodeEditorCreationOptions>): EditorConfiguration {
89
return new TestConfiguration(options);
90
}
91
protected override _createView(viewModel: ViewModel): [View, boolean] {
92
// Never create a view
93
return [null! as View, false];
94
}
95
private _hasTextFocus = false;
96
public setHasTextFocus(hasTextFocus: boolean): void {
97
this._hasTextFocus = hasTextFocus;
98
}
99
public override hasTextFocus(): boolean {
100
return this._hasTextFocus;
101
}
102
//#endregion
103
104
//#region Testing utils
105
public getViewModel(): ViewModel | undefined {
106
return this._modelData ? this._modelData.viewModel : undefined;
107
}
108
public registerAndInstantiateContribution<T extends IEditorContribution>(id: string, ctor: new (editor: ICodeEditor, ...services: BrandedService[]) => T): T {
109
const r: T = this._instantiationService.createInstance(ctor, this);
110
this._contributions.set(id, r);
111
return r;
112
}
113
public registerDisposable(disposable: IDisposable): void {
114
this._register(disposable);
115
}
116
public runCommand(command: EditorCommand, args?: any): void | Promise<void> {
117
return this._instantiationService.invokeFunction((accessor) => {
118
return command.runEditorCommand(accessor, this, args);
119
});
120
}
121
public runAction(action: ITestEditorAction, args?: any): void | Promise<void> {
122
return this._instantiationService.invokeFunction((accessor) => {
123
return action.run(accessor, this, args);
124
});
125
}
126
}
127
128
class TestEditorDomElement {
129
parentElement: IContextKeyServiceTarget | null = null;
130
ownerDocument = document;
131
document = document;
132
setAttribute(attr: string, value: string): void { }
133
removeAttribute(attr: string): void { }
134
hasAttribute(attr: string): boolean { return false; }
135
getAttribute(attr: string): string | undefined { return undefined; }
136
addEventListener(event: string): void { }
137
removeEventListener(event: string): void { }
138
}
139
140
export interface TestCodeEditorCreationOptions extends editorOptions.IEditorOptions {
141
/**
142
* If the editor has text focus.
143
* Defaults to true.
144
*/
145
hasTextFocus?: boolean;
146
/**
147
* Env configuration
148
*/
149
envConfig?: ITestEnvConfiguration;
150
}
151
152
export interface TestCodeEditorInstantiationOptions extends TestCodeEditorCreationOptions {
153
/**
154
* Services to use.
155
*/
156
serviceCollection?: ServiceCollection;
157
}
158
159
export interface ITestEnvConfiguration {
160
extraEditorClassName?: string;
161
outerWidth?: number;
162
outerHeight?: number;
163
emptySelectionClipboard?: boolean;
164
pixelRatio?: number;
165
accessibilitySupport?: AccessibilitySupport;
166
}
167
168
export function withTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void {
169
return _withTestCodeEditor(text, options, callback);
170
}
171
172
export async function withAsyncTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void>): Promise<void> {
173
return _withTestCodeEditor(text, options, callback);
174
}
175
176
function isTextModel(arg: ITextModel | string | string[] | ITextBufferFactory): arg is ITextModel {
177
return Boolean(arg && (arg as ITextModel).uri);
178
}
179
180
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void;
181
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void>): Promise<void>;
182
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void> | void): Promise<void> | void {
183
const disposables = new DisposableStore();
184
const instantiationService = createCodeEditorServices(disposables, options.serviceCollection);
185
delete options.serviceCollection;
186
187
// create a model if necessary
188
let model: ITextModel;
189
if (isTextModel(arg)) {
190
model = arg;
191
} else {
192
model = disposables.add(instantiateTextModel(instantiationService, Array.isArray(arg) ? arg.join('\n') : arg));
193
}
194
195
const editor = disposables.add(instantiateTestCodeEditor(instantiationService, model, options));
196
const viewModel = editor.getViewModel()!;
197
viewModel.setHasFocus(true);
198
const result = callback(editor, editor.getViewModel()!, instantiationService);
199
if (result) {
200
return result.then(() => disposables.dispose());
201
}
202
203
disposables.dispose();
204
}
205
206
export function createCodeEditorServices(disposables: Pick<DisposableStore, 'add'>, services: ServiceCollection = new ServiceCollection()): TestInstantiationService {
207
const serviceIdentifiers: ServiceIdentifier<any>[] = [];
208
const define = <T>(id: ServiceIdentifier<T>, ctor: new (...args: any[]) => T) => {
209
if (!services.has(id)) {
210
services.set(id, new SyncDescriptor(ctor));
211
}
212
serviceIdentifiers.push(id);
213
};
214
const defineInstance = <T>(id: ServiceIdentifier<T>, instance: T) => {
215
if (!services.has(id)) {
216
services.set(id, instance);
217
}
218
serviceIdentifiers.push(id);
219
};
220
221
define(IAccessibilityService, TestAccessibilityService);
222
define(IKeybindingService, MockKeybindingService);
223
define(IClipboardService, TestClipboardService);
224
define(IEditorWorkerService, TestEditorWorkerService);
225
defineInstance(IOpenerService, NullOpenerService);
226
define(INotificationService, TestNotificationService);
227
define(IDialogService, TestDialogService);
228
define(IUndoRedoService, UndoRedoService);
229
define(ILanguageService, LanguageService);
230
define(ILanguageConfigurationService, TestLanguageConfigurationService);
231
define(IConfigurationService, TestConfigurationService);
232
define(ITextResourcePropertiesService, TestTextResourcePropertiesService);
233
define(IThemeService, TestThemeService);
234
define(ILogService, NullLogService);
235
define(IModelService, ModelService);
236
define(ICodeEditorService, TestCodeEditorService);
237
define(IContextKeyService, MockContextKeyService);
238
define(ICommandService, TestCommandService);
239
define(ITelemetryService, NullTelemetryServiceShape);
240
define(ILoggerService, NullLoggerService);
241
define(IDataChannelService, NullDataChannelService);
242
define(IEnvironmentService, class extends mock<IEnvironmentService>() {
243
declare readonly _serviceBrand: undefined;
244
override isBuilt: boolean = true;
245
override isExtensionDevelopment: boolean = false;
246
});
247
define(ILanguageFeatureDebounceService, LanguageFeatureDebounceService);
248
define(ILanguageFeaturesService, LanguageFeaturesService);
249
define(ITreeSitterLibraryService, TestTreeSitterLibraryService);
250
define(IInlineCompletionsService, InlineCompletionsService);
251
define(IUserInteractionService, MockUserInteractionService);
252
253
const instantiationService = disposables.add(new TestInstantiationService(services, true));
254
disposables.add(toDisposable(() => {
255
for (const id of serviceIdentifiers) {
256
const instanceOrDescriptor = services.get(id);
257
if (typeof instanceOrDescriptor.dispose === 'function') {
258
instanceOrDescriptor.dispose();
259
}
260
}
261
}));
262
return instantiationService;
263
}
264
265
export function createTestCodeEditor(model: ITextModel | undefined, options: TestCodeEditorInstantiationOptions = {}): ITestCodeEditor {
266
const disposables = new DisposableStore();
267
const instantiationService = createCodeEditorServices(disposables, options.serviceCollection);
268
delete options.serviceCollection;
269
270
const editor = instantiateTestCodeEditor(instantiationService, model || null, options);
271
editor.registerDisposable(disposables);
272
return editor;
273
}
274
275
export function instantiateTestCodeEditor(instantiationService: IInstantiationService, model: ITextModel | null, options: TestCodeEditorCreationOptions = {}): ITestCodeEditor {
276
const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
277
contributions: []
278
};
279
const editor = instantiationService.createInstance(
280
TestCodeEditor,
281
// eslint-disable-next-line local/code-no-any-casts
282
<HTMLElement><any>new TestEditorDomElement(),
283
options,
284
codeEditorWidgetOptions
285
);
286
if (typeof options.hasTextFocus === 'undefined') {
287
options.hasTextFocus = true;
288
}
289
editor.setHasTextFocus(options.hasTextFocus);
290
editor.setModel(model);
291
const viewModel = editor.getViewModel();
292
viewModel?.setHasFocus(options.hasTextFocus);
293
return <ITestCodeEditor>editor;
294
}
295
296