Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/standalone/browser/standaloneEditor.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 { mainWindow } from '../../../base/browser/window.js';
7
import { Disposable, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
8
import { splitLines } from '../../../base/common/strings.js';
9
import { URI } from '../../../base/common/uri.js';
10
import './standalone-tokens.css';
11
import { FontMeasurements } from '../../browser/config/fontMeasurements.js';
12
import { ICodeEditor } from '../../browser/editorBrowser.js';
13
import { EditorCommand, ServicesAccessor } from '../../browser/editorExtensions.js';
14
import { ICodeEditorService } from '../../browser/services/codeEditorService.js';
15
import { IInternalWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from './standaloneWebWorker.js';
16
import { ApplyUpdateResult, ConfigurationChangedEvent, EditorOptions } from '../../common/config/editorOptions.js';
17
import { EditorZoom } from '../../common/config/editorZoom.js';
18
import { BareFontInfo, FontInfo } from '../../common/config/fontInfo.js';
19
import { IPosition } from '../../common/core/position.js';
20
import { IRange } from '../../common/core/range.js';
21
import { EditorType, IDiffEditor } from '../../common/editorCommon.js';
22
import * as languages from '../../common/languages.js';
23
import { ILanguageService } from '../../common/languages/language.js';
24
import { PLAINTEXT_LANGUAGE_ID } from '../../common/languages/modesRegistry.js';
25
import { NullState, nullTokenize } from '../../common/languages/nullTokenize.js';
26
import { FindMatch, ITextModel, TextModelResolvedOptions } from '../../common/model.js';
27
import { IModelService } from '../../common/services/model.js';
28
import * as standaloneEnums from '../../common/standalone/standaloneEnums.js';
29
import { Colorizer, IColorizerElementOptions, IColorizerOptions } from './colorizer.js';
30
import { IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor2, StandaloneEditor, createTextModel } from './standaloneCodeEditor.js';
31
import { IEditorOverrideServices, StandaloneKeybindingService, StandaloneServices } from './standaloneServices.js';
32
import { StandaloneThemeService } from './standaloneThemeService.js';
33
import { IStandaloneThemeData, IStandaloneThemeService } from '../common/standaloneTheme.js';
34
import { IMenuItem, MenuId, MenuRegistry } from '../../../platform/actions/common/actions.js';
35
import { CommandsRegistry, ICommandHandler } from '../../../platform/commands/common/commands.js';
36
import { ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js';
37
import { ITextResourceEditorInput } from '../../../platform/editor/common/editor.js';
38
import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';
39
import { IMarker, IMarkerData, IMarkerService } from '../../../platform/markers/common/markers.js';
40
import { IOpenerService } from '../../../platform/opener/common/opener.js';
41
import { MultiDiffEditorWidget } from '../../browser/widget/multiDiffEditor/multiDiffEditorWidget.js';
42
43
/**
44
* Create a new editor under `domElement`.
45
* `domElement` should be empty (not contain other dom nodes).
46
* The editor will read the size of `domElement`.
47
*/
48
export function create(domElement: HTMLElement, options?: IStandaloneEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneCodeEditor {
49
const instantiationService = StandaloneServices.initialize(override || {});
50
return instantiationService.createInstance(StandaloneEditor, domElement, options);
51
}
52
53
/**
54
* Emitted when an editor is created.
55
* Creating a diff editor might cause this listener to be invoked with the two editors.
56
* @event
57
*/
58
export function onDidCreateEditor(listener: (codeEditor: ICodeEditor) => void): IDisposable {
59
const codeEditorService = StandaloneServices.get(ICodeEditorService);
60
return codeEditorService.onCodeEditorAdd((editor) => {
61
listener(<ICodeEditor>editor);
62
});
63
}
64
65
/**
66
* Emitted when an diff editor is created.
67
* @event
68
*/
69
export function onDidCreateDiffEditor(listener: (diffEditor: IDiffEditor) => void): IDisposable {
70
const codeEditorService = StandaloneServices.get(ICodeEditorService);
71
return codeEditorService.onDiffEditorAdd((editor) => {
72
listener(<IDiffEditor>editor);
73
});
74
}
75
76
/**
77
* Get all the created editors.
78
*/
79
export function getEditors(): readonly ICodeEditor[] {
80
const codeEditorService = StandaloneServices.get(ICodeEditorService);
81
return codeEditorService.listCodeEditors();
82
}
83
84
/**
85
* Get all the created diff editors.
86
*/
87
export function getDiffEditors(): readonly IDiffEditor[] {
88
const codeEditorService = StandaloneServices.get(ICodeEditorService);
89
return codeEditorService.listDiffEditors();
90
}
91
92
/**
93
* Create a new diff editor under `domElement`.
94
* `domElement` should be empty (not contain other dom nodes).
95
* The editor will read the size of `domElement`.
96
*/
97
export function createDiffEditor(domElement: HTMLElement, options?: IStandaloneDiffEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneDiffEditor {
98
const instantiationService = StandaloneServices.initialize(override || {});
99
return instantiationService.createInstance(StandaloneDiffEditor2, domElement, options);
100
}
101
102
export function createMultiFileDiffEditor(domElement: HTMLElement, override?: IEditorOverrideServices) {
103
const instantiationService = StandaloneServices.initialize(override || {});
104
return new MultiDiffEditorWidget(domElement, {}, instantiationService);
105
}
106
107
/**
108
* Description of a command contribution
109
*/
110
export interface ICommandDescriptor {
111
/**
112
* An unique identifier of the contributed command.
113
*/
114
id: string;
115
/**
116
* Callback that will be executed when the command is triggered.
117
*/
118
run: ICommandHandler;
119
}
120
121
/**
122
* Add a command.
123
*/
124
export function addCommand(descriptor: ICommandDescriptor): IDisposable {
125
if ((typeof descriptor.id !== 'string') || (typeof descriptor.run !== 'function')) {
126
throw new Error('Invalid command descriptor, `id` and `run` are required properties!');
127
}
128
return CommandsRegistry.registerCommand(descriptor.id, descriptor.run);
129
}
130
131
/**
132
* Add an action to all editors.
133
*/
134
export function addEditorAction(descriptor: IActionDescriptor): IDisposable {
135
if ((typeof descriptor.id !== 'string') || (typeof descriptor.label !== 'string') || (typeof descriptor.run !== 'function')) {
136
throw new Error('Invalid action descriptor, `id`, `label` and `run` are required properties!');
137
}
138
139
const precondition = ContextKeyExpr.deserialize(descriptor.precondition);
140
const run = (accessor: ServicesAccessor, ...args: any[]): void | Promise<void> => {
141
return EditorCommand.runEditorCommand(accessor, args, precondition, (accessor, editor, args) => Promise.resolve(descriptor.run(editor, ...args)));
142
};
143
144
const toDispose = new DisposableStore();
145
146
// Register the command
147
toDispose.add(CommandsRegistry.registerCommand(descriptor.id, run));
148
149
// Register the context menu item
150
if (descriptor.contextMenuGroupId) {
151
const menuItem: IMenuItem = {
152
command: {
153
id: descriptor.id,
154
title: descriptor.label
155
},
156
when: precondition,
157
group: descriptor.contextMenuGroupId,
158
order: descriptor.contextMenuOrder || 0
159
};
160
toDispose.add(MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem));
161
}
162
163
// Register the keybindings
164
if (Array.isArray(descriptor.keybindings)) {
165
const keybindingService = StandaloneServices.get(IKeybindingService);
166
if (!(keybindingService instanceof StandaloneKeybindingService)) {
167
console.warn('Cannot add keybinding because the editor is configured with an unrecognized KeybindingService');
168
} else {
169
const keybindingsWhen = ContextKeyExpr.and(precondition, ContextKeyExpr.deserialize(descriptor.keybindingContext));
170
toDispose.add(keybindingService.addDynamicKeybindings(descriptor.keybindings.map((keybinding) => {
171
return {
172
keybinding,
173
command: descriptor.id,
174
when: keybindingsWhen
175
};
176
})));
177
}
178
}
179
180
return toDispose;
181
}
182
183
/**
184
* A keybinding rule.
185
*/
186
export interface IKeybindingRule {
187
keybinding: number;
188
command?: string | null;
189
commandArgs?: any;
190
when?: string | null;
191
}
192
193
/**
194
* Add a keybinding rule.
195
*/
196
export function addKeybindingRule(rule: IKeybindingRule): IDisposable {
197
return addKeybindingRules([rule]);
198
}
199
200
/**
201
* Add keybinding rules.
202
*/
203
export function addKeybindingRules(rules: IKeybindingRule[]): IDisposable {
204
const keybindingService = StandaloneServices.get(IKeybindingService);
205
if (!(keybindingService instanceof StandaloneKeybindingService)) {
206
console.warn('Cannot add keybinding because the editor is configured with an unrecognized KeybindingService');
207
return Disposable.None;
208
}
209
210
return keybindingService.addDynamicKeybindings(rules.map((rule) => {
211
return {
212
keybinding: rule.keybinding,
213
command: rule.command,
214
commandArgs: rule.commandArgs,
215
when: ContextKeyExpr.deserialize(rule.when),
216
};
217
}));
218
}
219
220
/**
221
* Create a new editor model.
222
* You can specify the language that should be set for this model or let the language be inferred from the `uri`.
223
*/
224
export function createModel(value: string, language?: string, uri?: URI): ITextModel {
225
const languageService = StandaloneServices.get(ILanguageService);
226
const languageId = languageService.getLanguageIdByMimeType(language) || language;
227
return createTextModel(
228
StandaloneServices.get(IModelService),
229
languageService,
230
value,
231
languageId,
232
uri
233
);
234
}
235
236
/**
237
* Change the language for a model.
238
*/
239
export function setModelLanguage(model: ITextModel, mimeTypeOrLanguageId: string): void {
240
const languageService = StandaloneServices.get(ILanguageService);
241
const languageId = languageService.getLanguageIdByMimeType(mimeTypeOrLanguageId) || mimeTypeOrLanguageId || PLAINTEXT_LANGUAGE_ID;
242
model.setLanguage(languageService.createById(languageId));
243
}
244
245
/**
246
* Set the markers for a model.
247
*/
248
export function setModelMarkers(model: ITextModel, owner: string, markers: IMarkerData[]): void {
249
if (model) {
250
const markerService = StandaloneServices.get(IMarkerService);
251
markerService.changeOne(owner, model.uri, markers);
252
}
253
}
254
255
/**
256
* Remove all markers of an owner.
257
*/
258
export function removeAllMarkers(owner: string) {
259
const markerService = StandaloneServices.get(IMarkerService);
260
markerService.changeAll(owner, []);
261
}
262
263
/**
264
* Get markers for owner and/or resource
265
*
266
* @returns list of markers
267
*/
268
export function getModelMarkers(filter: { owner?: string; resource?: URI; take?: number }): IMarker[] {
269
const markerService = StandaloneServices.get(IMarkerService);
270
return markerService.read(filter);
271
}
272
273
/**
274
* Emitted when markers change for a model.
275
* @event
276
*/
277
export function onDidChangeMarkers(listener: (e: readonly URI[]) => void): IDisposable {
278
const markerService = StandaloneServices.get(IMarkerService);
279
return markerService.onMarkerChanged(listener);
280
}
281
282
/**
283
* Get the model that has `uri` if it exists.
284
*/
285
export function getModel(uri: URI): ITextModel | null {
286
const modelService = StandaloneServices.get(IModelService);
287
return modelService.getModel(uri);
288
}
289
290
/**
291
* Get all the created models.
292
*/
293
export function getModels(): ITextModel[] {
294
const modelService = StandaloneServices.get(IModelService);
295
return modelService.getModels();
296
}
297
298
/**
299
* Emitted when a model is created.
300
* @event
301
*/
302
export function onDidCreateModel(listener: (model: ITextModel) => void): IDisposable {
303
const modelService = StandaloneServices.get(IModelService);
304
return modelService.onModelAdded(listener);
305
}
306
307
/**
308
* Emitted right before a model is disposed.
309
* @event
310
*/
311
export function onWillDisposeModel(listener: (model: ITextModel) => void): IDisposable {
312
const modelService = StandaloneServices.get(IModelService);
313
return modelService.onModelRemoved(listener);
314
}
315
316
/**
317
* Emitted when a different language is set to a model.
318
* @event
319
*/
320
export function onDidChangeModelLanguage(listener: (e: { readonly model: ITextModel; readonly oldLanguage: string }) => void): IDisposable {
321
const modelService = StandaloneServices.get(IModelService);
322
return modelService.onModelLanguageChanged((e) => {
323
listener({
324
model: e.model,
325
oldLanguage: e.oldLanguageId
326
});
327
});
328
}
329
330
/**
331
* Create a new web worker that has model syncing capabilities built in.
332
* Specify an AMD module to load that will `create` an object that will be proxied.
333
*/
334
export function createWebWorker<T extends object>(opts: IInternalWebWorkerOptions): MonacoWebWorker<T> {
335
return actualCreateWebWorker<T>(StandaloneServices.get(IModelService), opts);
336
}
337
338
/**
339
* Colorize the contents of `domNode` using attribute `data-lang`.
340
*/
341
export function colorizeElement(domNode: HTMLElement, options: IColorizerElementOptions): Promise<void> {
342
const languageService = StandaloneServices.get(ILanguageService);
343
const themeService = <StandaloneThemeService>StandaloneServices.get(IStandaloneThemeService);
344
return Colorizer.colorizeElement(themeService, languageService, domNode, options).then(() => {
345
themeService.registerEditorContainer(domNode);
346
});
347
}
348
349
/**
350
* Colorize `text` using language `languageId`.
351
*/
352
export function colorize(text: string, languageId: string, options: IColorizerOptions): Promise<string> {
353
const languageService = StandaloneServices.get(ILanguageService);
354
const themeService = <StandaloneThemeService>StandaloneServices.get(IStandaloneThemeService);
355
themeService.registerEditorContainer(mainWindow.document.body);
356
return Colorizer.colorize(languageService, text, languageId, options);
357
}
358
359
/**
360
* Colorize a line in a model.
361
*/
362
export function colorizeModelLine(model: ITextModel, lineNumber: number, tabSize: number = 4): string {
363
const themeService = <StandaloneThemeService>StandaloneServices.get(IStandaloneThemeService);
364
themeService.registerEditorContainer(mainWindow.document.body);
365
return Colorizer.colorizeModelLine(model, lineNumber, tabSize);
366
}
367
368
/**
369
* @internal
370
*/
371
function getSafeTokenizationSupport(language: string): Omit<languages.ITokenizationSupport, 'tokenizeEncoded'> {
372
const tokenizationSupport = languages.TokenizationRegistry.get(language);
373
if (tokenizationSupport) {
374
return tokenizationSupport;
375
}
376
return {
377
getInitialState: () => NullState,
378
tokenize: (line: string, hasEOL: boolean, state: languages.IState) => nullTokenize(language, state)
379
};
380
}
381
382
/**
383
* Tokenize `text` using language `languageId`
384
*/
385
export function tokenize(text: string, languageId: string): languages.Token[][] {
386
// Needed in order to get the mode registered for subsequent look-ups
387
languages.TokenizationRegistry.getOrCreate(languageId);
388
389
const tokenizationSupport = getSafeTokenizationSupport(languageId);
390
const lines = splitLines(text);
391
const result: languages.Token[][] = [];
392
let state = tokenizationSupport.getInitialState();
393
for (let i = 0, len = lines.length; i < len; i++) {
394
const line = lines[i];
395
const tokenizationResult = tokenizationSupport.tokenize(line, true, state);
396
397
result[i] = tokenizationResult.tokens;
398
state = tokenizationResult.endState;
399
}
400
return result;
401
}
402
403
/**
404
* Define a new theme or update an existing theme.
405
*/
406
export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void {
407
const standaloneThemeService = StandaloneServices.get(IStandaloneThemeService);
408
standaloneThemeService.defineTheme(themeName, themeData);
409
}
410
411
/**
412
* Switches to a theme.
413
*/
414
export function setTheme(themeName: string): void {
415
const standaloneThemeService = StandaloneServices.get(IStandaloneThemeService);
416
standaloneThemeService.setTheme(themeName);
417
}
418
419
/**
420
* Clears all cached font measurements and triggers re-measurement.
421
*/
422
export function remeasureFonts(): void {
423
FontMeasurements.clearAllFontInfos();
424
}
425
426
/**
427
* Register a command.
428
*/
429
export function registerCommand(id: string, handler: (accessor: any, ...args: any[]) => void): IDisposable {
430
return CommandsRegistry.registerCommand({ id, handler });
431
}
432
433
export interface ILinkOpener {
434
open(resource: URI): boolean | Promise<boolean>;
435
}
436
437
/**
438
* Registers a handler that is called when a link is opened in any editor. The handler callback should return `true` if the link was handled and `false` otherwise.
439
* The handler that was registered last will be called first when a link is opened.
440
*
441
* Returns a disposable that can unregister the opener again.
442
*/
443
export function registerLinkOpener(opener: ILinkOpener): IDisposable {
444
const openerService = StandaloneServices.get(IOpenerService);
445
return openerService.registerOpener({
446
async open(resource: string | URI) {
447
if (typeof resource === 'string') {
448
resource = URI.parse(resource);
449
}
450
return opener.open(resource);
451
}
452
});
453
}
454
455
/**
456
* Represents an object that can handle editor open operations (e.g. when "go to definition" is called
457
* with a resource other than the current model).
458
*/
459
export interface ICodeEditorOpener {
460
/**
461
* Callback that is invoked when a resource other than the current model should be opened (e.g. when "go to definition" is called).
462
* The callback should return `true` if the request was handled and `false` otherwise.
463
* @param source The code editor instance that initiated the request.
464
* @param resource The URI of the resource that should be opened.
465
* @param selectionOrPosition An optional position or selection inside the model corresponding to `resource` that can be used to set the cursor.
466
*/
467
openCodeEditor(source: ICodeEditor, resource: URI, selectionOrPosition?: IRange | IPosition): boolean | Promise<boolean>;
468
}
469
470
/**
471
* Registers a handler that is called when a resource other than the current model should be opened in the editor (e.g. "go to definition").
472
* The handler callback should return `true` if the request was handled and `false` otherwise.
473
*
474
* Returns a disposable that can unregister the opener again.
475
*
476
* If no handler is registered the default behavior is to do nothing for models other than the currently attached one.
477
*/
478
export function registerEditorOpener(opener: ICodeEditorOpener): IDisposable {
479
const codeEditorService = StandaloneServices.get(ICodeEditorService);
480
return codeEditorService.registerCodeEditorOpenHandler(async (input: ITextResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean) => {
481
if (!source) {
482
return null;
483
}
484
const selection = input.options?.selection;
485
let selectionOrPosition: IRange | IPosition | undefined;
486
if (selection && typeof selection.endLineNumber === 'number' && typeof selection.endColumn === 'number') {
487
selectionOrPosition = <IRange>selection;
488
} else if (selection) {
489
selectionOrPosition = { lineNumber: selection.startLineNumber, column: selection.startColumn };
490
}
491
if (await opener.openCodeEditor(source, input.resource, selectionOrPosition)) {
492
return source; // return source editor to indicate that this handler has successfully handled the opening
493
}
494
return null; // fallback to other registered handlers
495
});
496
}
497
498
/**
499
* @internal
500
*/
501
export function createMonacoEditorAPI(): typeof monaco.editor {
502
return {
503
// methods
504
create: <any>create,
505
getEditors: <any>getEditors,
506
getDiffEditors: <any>getDiffEditors,
507
onDidCreateEditor: <any>onDidCreateEditor,
508
onDidCreateDiffEditor: <any>onDidCreateDiffEditor,
509
createDiffEditor: <any>createDiffEditor,
510
511
addCommand: <any>addCommand,
512
addEditorAction: <any>addEditorAction,
513
addKeybindingRule: <any>addKeybindingRule,
514
addKeybindingRules: <any>addKeybindingRules,
515
516
createModel: <any>createModel,
517
setModelLanguage: <any>setModelLanguage,
518
setModelMarkers: <any>setModelMarkers,
519
getModelMarkers: <any>getModelMarkers,
520
removeAllMarkers: removeAllMarkers,
521
onDidChangeMarkers: <any>onDidChangeMarkers,
522
getModels: <any>getModels,
523
getModel: <any>getModel,
524
onDidCreateModel: <any>onDidCreateModel,
525
onWillDisposeModel: <any>onWillDisposeModel,
526
onDidChangeModelLanguage: <any>onDidChangeModelLanguage,
527
528
529
createWebWorker: <any>createWebWorker,
530
colorizeElement: <any>colorizeElement,
531
colorize: <any>colorize,
532
colorizeModelLine: <any>colorizeModelLine,
533
tokenize: <any>tokenize,
534
defineTheme: <any>defineTheme,
535
setTheme: <any>setTheme,
536
remeasureFonts: remeasureFonts,
537
registerCommand: registerCommand,
538
539
registerLinkOpener: registerLinkOpener,
540
registerEditorOpener: <any>registerEditorOpener,
541
542
// enums
543
AccessibilitySupport: standaloneEnums.AccessibilitySupport,
544
ContentWidgetPositionPreference: standaloneEnums.ContentWidgetPositionPreference,
545
CursorChangeReason: standaloneEnums.CursorChangeReason,
546
DefaultEndOfLine: standaloneEnums.DefaultEndOfLine,
547
EditorAutoIndentStrategy: standaloneEnums.EditorAutoIndentStrategy,
548
EditorOption: standaloneEnums.EditorOption,
549
EndOfLinePreference: standaloneEnums.EndOfLinePreference,
550
EndOfLineSequence: standaloneEnums.EndOfLineSequence,
551
MinimapPosition: standaloneEnums.MinimapPosition,
552
MinimapSectionHeaderStyle: standaloneEnums.MinimapSectionHeaderStyle,
553
MouseTargetType: standaloneEnums.MouseTargetType,
554
OverlayWidgetPositionPreference: standaloneEnums.OverlayWidgetPositionPreference,
555
OverviewRulerLane: standaloneEnums.OverviewRulerLane,
556
GlyphMarginLane: standaloneEnums.GlyphMarginLane,
557
RenderLineNumbersType: standaloneEnums.RenderLineNumbersType,
558
RenderMinimap: standaloneEnums.RenderMinimap,
559
ScrollbarVisibility: standaloneEnums.ScrollbarVisibility,
560
ScrollType: standaloneEnums.ScrollType,
561
TextEditorCursorBlinkingStyle: standaloneEnums.TextEditorCursorBlinkingStyle,
562
TextEditorCursorStyle: standaloneEnums.TextEditorCursorStyle,
563
TrackedRangeStickiness: standaloneEnums.TrackedRangeStickiness,
564
WrappingIndent: standaloneEnums.WrappingIndent,
565
InjectedTextCursorStops: standaloneEnums.InjectedTextCursorStops,
566
PositionAffinity: standaloneEnums.PositionAffinity,
567
ShowLightbulbIconMode: standaloneEnums.ShowLightbulbIconMode,
568
TextDirection: standaloneEnums.TextDirection,
569
570
// classes
571
ConfigurationChangedEvent: <any>ConfigurationChangedEvent,
572
BareFontInfo: <any>BareFontInfo,
573
FontInfo: <any>FontInfo,
574
TextModelResolvedOptions: <any>TextModelResolvedOptions,
575
FindMatch: <any>FindMatch,
576
ApplyUpdateResult: <any>ApplyUpdateResult,
577
EditorZoom: <any>EditorZoom,
578
579
createMultiFileDiffEditor: <any>createMultiFileDiffEditor,
580
581
// vars
582
EditorType: EditorType,
583
EditorOptions: <any>EditorOptions
584
585
};
586
}
587
588