Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/testCodeEditor.ts
3294 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
68
export interface ITestCodeEditor extends IActiveCodeEditor {
69
getViewModel(): ViewModel | undefined;
70
registerAndInstantiateContribution<T extends IEditorContribution, Services extends BrandedService[]>(id: string, ctor: new (editor: ICodeEditor, ...services: Services) => T): T;
71
registerDisposable(disposable: IDisposable): void;
72
runCommand(command: ITestEditorCommand, args?: any): void | Promise<void>;
73
runAction(action: ITestEditorAction, args?: any): void | Promise<void>;
74
}
75
76
export interface ITestEditorCommand {
77
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args?: any): void | Promise<void>;
78
}
79
80
export interface ITestEditorAction {
81
run(accessor: ServicesAccessor, editor: ICodeEditor, args?: any): void | Promise<void>;
82
}
83
84
export class TestCodeEditor extends CodeEditorWidget implements ICodeEditor {
85
86
//#region testing overrides
87
protected override _createConfiguration(isSimpleWidget: boolean, contextMenuId: MenuId, options: Readonly<TestCodeEditorCreationOptions>): EditorConfiguration {
88
return new TestConfiguration(options);
89
}
90
protected override _createView(viewModel: ViewModel): [View, boolean] {
91
// Never create a view
92
return [null! as View, false];
93
}
94
private _hasTextFocus = false;
95
public setHasTextFocus(hasTextFocus: boolean): void {
96
this._hasTextFocus = hasTextFocus;
97
}
98
public override hasTextFocus(): boolean {
99
return this._hasTextFocus;
100
}
101
//#endregion
102
103
//#region Testing utils
104
public getViewModel(): ViewModel | undefined {
105
return this._modelData ? this._modelData.viewModel : undefined;
106
}
107
public registerAndInstantiateContribution<T extends IEditorContribution>(id: string, ctor: new (editor: ICodeEditor, ...services: BrandedService[]) => T): T {
108
const r: T = this._instantiationService.createInstance(ctor, this);
109
this._contributions.set(id, r);
110
return r;
111
}
112
public registerDisposable(disposable: IDisposable): void {
113
this._register(disposable);
114
}
115
public runCommand(command: EditorCommand, args?: any): void | Promise<void> {
116
return this._instantiationService.invokeFunction((accessor) => {
117
return command.runEditorCommand(accessor, this, args);
118
});
119
}
120
public runAction(action: ITestEditorAction, args?: any): void | Promise<void> {
121
return this._instantiationService.invokeFunction((accessor) => {
122
return action.run(accessor, this, args);
123
});
124
}
125
}
126
127
class TestEditorDomElement {
128
parentElement: IContextKeyServiceTarget | null = null;
129
ownerDocument = document;
130
document = document;
131
setAttribute(attr: string, value: string): void { }
132
removeAttribute(attr: string): void { }
133
hasAttribute(attr: string): boolean { return false; }
134
getAttribute(attr: string): string | undefined { return undefined; }
135
addEventListener(event: string): void { }
136
removeEventListener(event: string): void { }
137
}
138
139
export interface TestCodeEditorCreationOptions extends editorOptions.IEditorOptions {
140
/**
141
* If the editor has text focus.
142
* Defaults to true.
143
*/
144
hasTextFocus?: boolean;
145
/**
146
* Env configuration
147
*/
148
envConfig?: ITestEnvConfiguration;
149
}
150
151
export interface TestCodeEditorInstantiationOptions extends TestCodeEditorCreationOptions {
152
/**
153
* Services to use.
154
*/
155
serviceCollection?: ServiceCollection;
156
}
157
158
export interface ITestEnvConfiguration {
159
extraEditorClassName?: string;
160
outerWidth?: number;
161
outerHeight?: number;
162
emptySelectionClipboard?: boolean;
163
pixelRatio?: number;
164
accessibilitySupport?: AccessibilitySupport;
165
}
166
167
export function withTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void {
168
return _withTestCodeEditor(text, options, callback);
169
}
170
171
export async function withAsyncTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void>): Promise<void> {
172
return _withTestCodeEditor(text, options, callback);
173
}
174
175
function isTextModel(arg: ITextModel | string | string[] | ITextBufferFactory): arg is ITextModel {
176
return Boolean(arg && (arg as ITextModel).uri);
177
}
178
179
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void;
180
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void>): Promise<void>;
181
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void> | void): Promise<void> | void {
182
const disposables = new DisposableStore();
183
const instantiationService = createCodeEditorServices(disposables, options.serviceCollection);
184
delete options.serviceCollection;
185
186
// create a model if necessary
187
let model: ITextModel;
188
if (isTextModel(arg)) {
189
model = arg;
190
} else {
191
model = disposables.add(instantiateTextModel(instantiationService, Array.isArray(arg) ? arg.join('\n') : arg));
192
}
193
194
const editor = disposables.add(instantiateTestCodeEditor(instantiationService, model, options));
195
const viewModel = editor.getViewModel()!;
196
viewModel.setHasFocus(true);
197
const result = callback(<ITestCodeEditor>editor, editor.getViewModel()!, instantiationService);
198
if (result) {
199
return result.then(() => disposables.dispose());
200
}
201
202
disposables.dispose();
203
}
204
205
export function createCodeEditorServices(disposables: Pick<DisposableStore, 'add'>, services: ServiceCollection = new ServiceCollection()): TestInstantiationService {
206
const serviceIdentifiers: ServiceIdentifier<any>[] = [];
207
const define = <T>(id: ServiceIdentifier<T>, ctor: new (...args: any[]) => T) => {
208
if (!services.has(id)) {
209
services.set(id, new SyncDescriptor(ctor));
210
}
211
serviceIdentifiers.push(id);
212
};
213
const defineInstance = <T>(id: ServiceIdentifier<T>, instance: T) => {
214
if (!services.has(id)) {
215
services.set(id, instance);
216
}
217
serviceIdentifiers.push(id);
218
};
219
220
define(IAccessibilityService, TestAccessibilityService);
221
define(IKeybindingService, MockKeybindingService);
222
define(IClipboardService, TestClipboardService);
223
define(IEditorWorkerService, TestEditorWorkerService);
224
defineInstance(IOpenerService, NullOpenerService);
225
define(INotificationService, TestNotificationService);
226
define(IDialogService, TestDialogService);
227
define(IUndoRedoService, UndoRedoService);
228
define(ILanguageService, LanguageService);
229
define(ILanguageConfigurationService, TestLanguageConfigurationService);
230
define(IConfigurationService, TestConfigurationService);
231
define(ITextResourcePropertiesService, TestTextResourcePropertiesService);
232
define(IThemeService, TestThemeService);
233
define(ILogService, NullLogService);
234
define(IModelService, ModelService);
235
define(ICodeEditorService, TestCodeEditorService);
236
define(IContextKeyService, MockContextKeyService);
237
define(ICommandService, TestCommandService);
238
define(ITelemetryService, NullTelemetryServiceShape);
239
define(ILoggerService, NullLoggerService);
240
define(IDataChannelService, NullDataChannelService);
241
define(IEnvironmentService, class extends mock<IEnvironmentService>() {
242
declare readonly _serviceBrand: undefined;
243
override isBuilt: boolean = true;
244
override isExtensionDevelopment: boolean = false;
245
});
246
define(ILanguageFeatureDebounceService, LanguageFeatureDebounceService);
247
define(ILanguageFeaturesService, LanguageFeaturesService);
248
define(ITreeSitterLibraryService, TestTreeSitterLibraryService);
249
define(IInlineCompletionsService, InlineCompletionsService);
250
251
const instantiationService = disposables.add(new TestInstantiationService(services, true));
252
disposables.add(toDisposable(() => {
253
for (const id of serviceIdentifiers) {
254
const instanceOrDescriptor = services.get(id);
255
if (typeof instanceOrDescriptor.dispose === 'function') {
256
instanceOrDescriptor.dispose();
257
}
258
}
259
}));
260
return instantiationService;
261
}
262
263
export function createTestCodeEditor(model: ITextModel | undefined, options: TestCodeEditorInstantiationOptions = {}): ITestCodeEditor {
264
const disposables = new DisposableStore();
265
const instantiationService = createCodeEditorServices(disposables, options.serviceCollection);
266
delete options.serviceCollection;
267
268
const editor = instantiateTestCodeEditor(instantiationService, model || null, options);
269
editor.registerDisposable(disposables);
270
return editor;
271
}
272
273
export function instantiateTestCodeEditor(instantiationService: IInstantiationService, model: ITextModel | null, options: TestCodeEditorCreationOptions = {}): ITestCodeEditor {
274
const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
275
contributions: []
276
};
277
const editor = instantiationService.createInstance(
278
TestCodeEditor,
279
<HTMLElement><any>new TestEditorDomElement(),
280
options,
281
codeEditorWidgetOptions
282
);
283
if (typeof options.hasTextFocus === 'undefined') {
284
options.hasTextFocus = true;
285
}
286
editor.setHasTextFocus(options.hasTextFocus);
287
editor.setModel(model);
288
const viewModel = editor.getViewModel();
289
viewModel?.setHasFocus(options.hasTextFocus);
290
return <ITestCodeEditor>editor;
291
}
292
293