Atlas › Test

preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1

Exact test identity: mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1

Package
mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common
Suite / test hierarchy
preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1
Test
preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1
Introduced at
preferencesModels.test|title=DefaultSettings separates groups with same id but different titles|occurrence=1, preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1 Frontier kind: Test frontier
Covered ranges
1434
Covered lines
12460
Covered files
52

Co-introduced tests

1 other test enter at the same concept.

Covered source

Expand a file to inspect source; the > gutter marks covered lines.

src/vs/workbench/common/editor.ts 1397 covered LOC · 36 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editor.ts
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 { localize } from '../../nls.js';
7 > import { Event } from '../../base/common/event.js';
8 > import { DeepRequiredNonNullable, assertReturnsDefined } from '../../base/common/types.js';
9 > import { URI } from '../../base/common/uri.js';
10 > import { Disposable, IDisposable, toDisposable } from '../../base/common/lifecycle.js';
11 > import { ICodeEditorViewState, IDiffEditor, IDiffEditorViewState, IEditor, IEditorViewState } from '../../editor/common/editorCommon.js';
12 > import { IEditorOptions, IResourceEditorInput, ITextResourceEditorInput, IBaseTextResourceEditorInput, IBaseUntypedEditorInput, ITextEditorOptions } from '../../platform/editor/common/editor.js';
13 > import type { EditorInput } from './editor/editorInput.js';
14 > import { IInstantiationService, IConstructorSignature, ServicesAccessor, BrandedService } from '../../platform/instantiation/common/instantiation.js';
15 > import { IContextKeyService } from '../../platform/contextkey/common/contextkey.js';
16 > import { Registry } from '../../platform/registry/common/platform.js';
17 > import { IEncodingSupport, ILanguageSupport } from '../services/textfile/common/textfiles.js';
18 > import { IEditorGroup } from '../services/editor/common/editorGroupsService.js';
19 > import { ICompositeControl, IComposite } from './composite.js';
20 > import { FileType, IFileReadLimits, IFileService } from '../../platform/files/common/files.js';
21 > import { IPathData } from '../../platform/window/common/window.js';
22 > import { IExtUri } from '../../base/common/resources.js';
23 > import { Schemas } from '../../base/common/network.js';
24 > import { IEditorService } from '../services/editor/common/editorService.js';
25 > import { ILogService } from '../../platform/log/common/log.js';
26 > import { IErrorWithActions, createErrorWithActions, isErrorWithActions } from '../../base/common/errorMessage.js';
27 > import { IAction, toAction } from '../../base/common/actions.js';
28 > import Severity from '../../base/common/severity.js';
29 > import { IPreferencesService } from '../services/preferences/common/preferences.js';
30 > import { IReadonlyEditorGroupModel } from './editor/editorGroupModel.js';
31 >
32 > // Static values for editor contributions
33 > export const EditorExtensions = {
34 > EditorPane: 'workbench.contributions.editors',
35 > EditorFactory: 'workbench.contributions.editor.inputFactories'
36 > };
37 >
38 > // Static information regarding the text editor
39 > export const DEFAULT_EDITOR_ASSOCIATION = {
40 > id: 'default',
41 > displayName: localize('promptOpenWith.defaultEditor.displayName', "Text Editor"),
42 > providerDisplayName: localize('builtinProviderDisplayName', "Built-in")
43 > };
44 >
45 > /**
46 > * Side by side editor id.
47 > */
48 > export const SIDE_BY_SIDE_EDITOR_ID = 'workbench.editor.sidebysideEditor';
49 >
50 > /**
51 > * Text diff editor id.
52 > */
53 > export const TEXT_DIFF_EDITOR_ID = 'workbench.editors.textDiffEditor';
54 >
55 > /**
56 > * Binary diff editor id.
57 > */
58 > export const BINARY_DIFF_EDITOR_ID = 'workbench.editors.binaryResourceDiffEditor';
59 >
60 > export interface IEditorDescriptor<T extends IEditorPane> {
61 >
62 > /**
63 > * The unique type identifier of the editor. All instances
64 > * of the same `IEditorPane` should have the same type
65 > * identifier.
66 > */
67 > readonly typeId: string;
68 >
69 > /**
70 > * The display name of the editor.
71 > */
72 > readonly name: string;
73 >
74 > /**
75 > * Instantiates the editor pane using the provided services.
76 > */
77 > instantiate(instantiationService: IInstantiationService, group: IEditorGroup): T;
78 >
79 > /**
80 > * Whether the descriptor is for the provided editor pane.
81 > */
82 > describes(editorPane: T): boolean;
83 > }
84 >
85 > /**
86 > * Declares that an editor hosts the full-width group header (rendered by the
87 > * editor group below the tab bar, using the group's configured header menus).
88 > */
89 > export interface IEditorHeaderActions {
90 > /** Editor-scoped instantiation service so the header toolbars' `when` clauses see the editor's context. */
91 > readonly instantiationService: IInstantiationService;
92 > }
93 >
94 > /**
95 > * The editor pane is the container for workbench editors.
96 > */
97 > export interface IEditorPane extends IComposite {
98 >
99 > /**
100 > * An event to notify when the `IEditorControl` in this
101 > * editor pane changes.
102 > *
103 > * This can be used for editor panes that are a compound
104 > * of multiple editor controls to signal that the active
105 > * editor control has changed when the user clicks around.
106 > */
107 > readonly onDidChangeControl: Event<void>;
108 >
109 > /**
110 > * An optional event to notify when the selection inside the editor
111 > * pane changed in case the editor has a selection concept.
112 > *
113 > * For example, in a text editor pane, the selection changes whenever
114 > * the cursor is set to a new location.
115 > */
116 > readonly onDidChangeSelection?: Event<IEditorPaneSelectionChangeEvent>;
117 >
118 > /**
119 > * An optional event to notify when the editor inside the pane scrolled
120 > */
121 > readonly onDidChangeScroll?: Event<void>;
122 >
123 > /**
124 > * The assigned input of this editor.
125 > */
126 > readonly input: EditorInput | undefined;
127 >
128 > /**
129 > * The assigned options of the editor.
130 > */
131 > readonly options: IEditorOptions | undefined;
132 >
133 > /**
134 > * The assigned group this editor is showing in.
135 > */
136 > readonly group: IEditorGroup;
137 >
138 > /**
139 > * The minimum width of this editor.
140 > */
141 > readonly minimumWidth: number;
142 >
143 > /**
144 > * The maximum width of this editor.
145 > */
146 > readonly maximumWidth: number;
147 >
148 > /**
149 > * The minimum height of this editor.
150 > */
151 > readonly minimumHeight: number;
152 >
153 > /**
154 > * The maximum height of this editor.
155 > */
156 > readonly maximumHeight: number;
157 >
158 > /**
159 > * An event to notify whenever minimum/maximum width/height changes.
160 > */
161 > readonly onDidChangeSizeConstraints: Event<{ width: number; height: number } | undefined>;
162 >
163 > /**
164 > * The context key service for this editor. Should be overridden by
165 > * editors that have their own ScopedContextKeyService
166 > */
167 > readonly scopedContextKeyService: IContextKeyService | undefined;
168 >
169 > /**
170 > * Returns the underlying control of this editor. Callers need to cast
171 > * the control to a specific instance as needed, e.g. by using the
172 > * `isCodeEditor` helper method to access the text code editor.
173 > *
174 > * Use the `onDidChangeControl` event to track whenever the control
175 > * changes.
176 > */
177 > getControl(): IEditorControl | undefined;
178 >
179 > /**
180 > * Returns the current view state of the editor if any.
181 > *
182 > * This method is optional to override for the editor pane
183 > * and should only be overridden when the pane can deal with
184 > * `IEditorOptions.viewState` to be applied when opening.
185 > */
186 > getViewState(): object | undefined;
187 >
188 > /**
189 > * An optional method to declare that this editor hosts the full-width group
190 > * header (rendered by the editor group below the tab bar using the group's
191 > * configured header menus), providing the editor-scoped instantiation service
192 > * so the header actions' `when` clauses evaluate in the editor's context.
193 > * Return `undefined` for no header (the default).
194 > */
195 > getHeaderActions?(): IEditorHeaderActions | undefined;
196 >
197 > /**
198 > * An optional method to return the current selection in
199 > * the editor pane in case the editor pane has a selection
200 > * concept.
201 > *
202 > * Clients of this method will typically react to the
203 > * `onDidChangeSelection` event to receive the current
204 > * selection as needed.
205 > */
206 > getSelection?(): IEditorPaneSelection | undefined;
207 >
208 > /**
209 > * An optional method to return the current scroll position
210 > * of an editor inside the pane.
211 > *
212 > * Clients of this method will typically react to the
213 > * `onDidChangeScroll` event to receive the current
214 > * scroll position as needed.
215 > */
216 > getScrollPosition?(): IEditorPaneScrollPosition;
217 >
218 > /**
219 > * An optional method to set the current scroll position
220 > * of an editor inside the pane.
221 > */
222 > setScrollPosition?(scrollPosition: IEditorPaneScrollPosition): void;
223 >
224 > /**
225 > * Finds out if this editor is visible or not.
226 > */
227 > isVisible(): boolean;
228 > }
229 >
230 > export interface IEditorPaneSelectionChangeEvent {
231 >
232 > /**
233 > * More details for how the selection was made.
234 > */
235 > reason: EditorPaneSelectionChangeReason;
236 > }
237 >
238 > export const enum EditorPaneSelectionChangeReason {
239 >
240 > /**
241 > * The selection was changed as a result of a programmatic
242 > * method invocation.
243 > *
244 > * For a text editor pane, this for example can be a selection
245 > * being restored from previous view state automatically.
246 > */
247 > PROGRAMMATIC = 1,
248 >
249 > /**
250 > * The selection was changed by the user.
251 > *
252 > * This typically means the user changed the selection
253 > * with mouse or keyboard.
254 > */
255 > USER,
256 >
257 > /**
258 > * The selection was changed as a result of editing in
259 > * the editor pane.
260 > *
261 > * For a text editor pane, this for example can be typing
262 > * in the text of the editor pane.
263 > */
264 > EDIT,
265 >
266 > /**
267 > * The selection was changed as a result of a navigation
268 > * action.
269 > *
270 > * For a text editor pane, this for example can be a result
271 > * of selecting an entry from a text outline view.
272 > */
273 > NAVIGATION,
274 >
275 > /**
276 > * The selection was changed as a result of a jump action
277 > * from within the editor pane.
278 > *
279 > * For a text editor pane, this for example can be a result
280 > * of invoking "Go to definition" from a symbol.
281 > */
282 > JUMP
283 > }
284 >
285 > export interface IEditorPaneSelection {
286 >
287 > /**
288 > * Asks to compare this selection to another selection.
289 > */
290 > compare(otherSelection: IEditorPaneSelection): EditorPaneSelectionCompareResult;
291 >
292 > /**
293 > * Asks to massage the provided `options` in a way
294 > * that the selection can be restored when the editor
295 > * is opened again.
296 > *
297 > * For a text editor this means to apply the selected
298 > * line and column as text editor options.
299 > */
300 > restore(options: IEditorOptions): IEditorOptions;
301 >
302 > /**
303 > * Only used for logging to print more info about the selection.
304 > */
305 > log?(): string;
306 > }
307 >
308 > export const enum EditorPaneSelectionCompareResult {
309 >
310 > /**
311 > * The selections are identical.
312 > */
313 > IDENTICAL = 1,
314 >
315 > /**
316 > * The selections are similar.
317 > *
318 > * For a text editor this can mean that the one
319 > * selection is in close proximity to the other
320 > * selection.
321 > *
322 > * Upstream clients may decide in this case to
323 > * not treat the selection different from the
324 > * previous one because it is not distinct enough.
325 > */
326 > SIMILAR = 2,
327 >
328 > /**
329 > * The selections are entirely different.
330 > */
331 > DIFFERENT = 3
332 > }
333 >
334 > export interface IEditorPaneWithSelection extends IEditorPane {
335 >
336 > readonly onDidChangeSelection: Event<IEditorPaneSelectionChangeEvent>;
337 >
338 > getSelection(): IEditorPaneSelection | undefined;
339 > }
340 >
341 > export function isEditorPaneWithSelection(editorPane: IEditorPane | undefined): editorPane is IEditorPaneWithSelection {
342 const candidate = editorPane as IEditorPaneWithSelection | undefined;
343
344 return !!candidate && typeof candidate.getSelection === 'function' && !!candidate.onDidChangeSelection;
345 }
346 > editor.ts
347 > export interface IEditorPaneWithScrolling extends IEditorPane {
348 >
349 > readonly onDidChangeScroll: Event<void>;
350 >
351 > getScrollPosition(): IEditorPaneScrollPosition;
352 >
353 > setScrollPosition(position: IEditorPaneScrollPosition): void;
354 > }
355 >
356 > export function isEditorPaneWithScrolling(editorPane: IEditorPane | undefined): editorPane is IEditorPaneWithScrolling {
357 const candidate = editorPane as IEditorPaneWithScrolling | undefined;
358
359 return !!candidate && typeof candidate.getScrollPosition === 'function' && typeof candidate.setScrollPosition === 'function' && !!candidate.onDidChangeScroll;
360 }
361 > editor.ts
362 > /**
363 > * Scroll position of a pane
364 > */
365 > export interface IEditorPaneScrollPosition {
366 > readonly scrollTop: number;
367 > readonly scrollLeft?: number;
368 > }
369 >
370 > /**
371 > * Try to retrieve the view state for the editor pane that
372 > * has the provided editor input opened, if at all.
373 > *
374 > * This method will return `undefined` if the editor input
375 > * is not visible in any of the opened editor panes.
376 > */
377 > export function findViewStateForEditor(input: EditorInput, group: GroupIdentifier, editorService: IEditorService): object | undefined {
378 for (const editorPane of editorService.visibleEditorPanes) {
379 if (editorPane.group.id === group && input.matches(editorPane.input)) {
384 return undefined;
385 }
386 > editor.ts
387 > /**
388 > * Overrides `IEditorPane` where `input` and `group` are known to be set.
389 > */
390 > export interface IVisibleEditorPane extends IEditorPane {
391 > readonly input: EditorInput;
392 > }
393 >
394 > /**
395 > * The text editor pane is the container for workbench text editors.
396 > */
397 > export interface ITextEditorPane extends IEditorPane {
398 >
399 > /**
400 > * Returns the underlying text editor widget of this editor.
401 > */
402 > getControl(): IEditor | undefined;
403 > }
404 >
405 > /**
406 > * The text editor pane is the container for workbench text diff editors.
407 > */
408 > export interface ITextDiffEditorPane extends IEditorPane {
409 >
410 > /**
411 > * Returns the underlying text diff editor widget of this editor.
412 > */
413 > getControl(): IDiffEditor | undefined;
414 > }
415 >
416 > /**
417 > * Marker interface for the control inside an editor pane. Callers
418 > * have to cast the control to work with it, e.g. via methods
419 > * such as `isCodeEditor(control)`.
420 > */
421 > export interface IEditorControl extends ICompositeControl { }
422 >
423 > export interface IFileEditorFactory {
424 >
425 > /**
426 > * The type identifier of the file editor.
427 > */
428 > typeId: string;
429 >
430 > /**
431 > * Creates new editor capable of showing files.
432 > */
433 > createFileEditor(resource: URI, preferredResource: URI | undefined, preferredName: string | undefined, preferredDescription: string | undefined, preferredEncoding: string | undefined, preferredLanguageId: string | undefined, preferredContents: string | undefined, instantiationService: IInstantiationService): IFileEditorInput;
434 >
435 > /**
436 > * Check if the provided object is a file editor.
437 > */
438 > isFileEditor(obj: unknown): obj is IFileEditorInput;
439 > }
440 >
441 > export interface IEditorFactoryRegistry {
442 >
443 > /**
444 > * Registers the file editor factory to use for file editors.
445 > */
446 > registerFileEditorFactory(factory: IFileEditorFactory): void;
447 >
448 > /**
449 > * Returns the file editor factory to use for file editors.
450 > */
451 > getFileEditorFactory(): IFileEditorFactory;
452 >
453 > /**
454 > * Registers a editor serializer for the given editor to the registry.
455 > * An editor serializer is capable of serializing and deserializing editor
456 > * from string data.
457 > *
458 > * @param editorTypeId the type identifier of the editor
459 > * @param serializer the editor serializer for serialization/deserialization
460 > */
461 > registerEditorSerializer<Services extends BrandedService[]>(editorTypeId: string, ctor: { new(...Services: Services): IEditorSerializer }): IDisposable;
462 >
463 > /**
464 > * Returns the editor serializer for the given editor.
465 > */
466 > getEditorSerializer(editor: EditorInput): IEditorSerializer | undefined;
467 > getEditorSerializer(editorTypeId: string): IEditorSerializer | undefined;
468 >
469 > /**
470 > * Starts the registry by providing the required services.
471 > */
472 > start(accessor: ServicesAccessor): void;
473 > }
474 >
475 > export interface IEditorSerializer {
476 >
477 > /**
478 > * Determines whether the given editor can be serialized by the serializer.
479 > */
480 > canSerialize(editor: EditorInput): boolean;
481 >
482 > /**
483 > * Returns a string representation of the provided editor that contains enough information
484 > * to deserialize back to the original editor from the deserialize() method.
485 > */
486 > serialize(editor: EditorInput): string | undefined;
487 >
488 > /**
489 > * Returns an editor from the provided serialized form of the editor. This form matches
490 > * the value returned from the serialize() method.
491 > */
492 > deserialize(instantiationService: IInstantiationService, serializedEditor: string): EditorInput | undefined;
493 > }
494 >
495 > export interface IUntitledTextResourceEditorInput extends IBaseTextResourceEditorInput {
496 >
497 > /**
498 > * Optional resource for the untitled editor. Depending on the value, the editor:
499 > * - should get a unique name if `undefined` (for example `Untitled-1`)
500 > * - should use the resource directly if the scheme is `untitled:`
501 > * - should change the scheme to `untitled:` otherwise and assume an associated path
502 > *
503 > * Untitled editors with associated path behave slightly different from other untitled
504 > * editors:
505 > * - they are dirty right when opening
506 > * - they will not ask for a file path when saving but use the associated path
507 > */
508 > readonly resource: URI | undefined;
509 > }
510 >
511 > /**
512 > * A resource side by side editor input shows 2 editors side by side but
513 > * without highlighting any differences.
514 > *
515 > * Note: both sides will be resolved as editor individually. As such, it is
516 > * possible to show 2 different editors side by side.
517 > *
518 > * @see {@link IResourceDiffEditorInput} for a variant that compares 2 editors.
519 > */
520 > export interface IResourceSideBySideEditorInput extends IBaseUntypedEditorInput {
521 >
522 > /**
523 > * The right hand side editor to open inside a side-by-side editor.
524 > */
525 > readonly primary: Omit<IResourceEditorInput, 'options'> | Omit<ITextResourceEditorInput, 'options'> | Omit<IUntitledTextResourceEditorInput, 'options'>;
526 >
527 > /**
528 > * The left hand side editor to open inside a side-by-side editor.
529 > */
530 > readonly secondary: Omit<IResourceEditorInput, 'options'> | Omit<ITextResourceEditorInput, 'options'> | Omit<IUntitledTextResourceEditorInput, 'options'>;
531 > }
532 >
533 > /**
534 > * A resource diff editor input compares 2 editors side by side
535 > * highlighting the differences.
536 > *
537 > * Note: both sides must be resolvable to the same editor, or
538 > * a text based presentation will be used as fallback.
539 > */
540 > export interface IResourceDiffEditorInput extends IBaseUntypedEditorInput {
541 >
542 > /**
543 > * The left hand side editor to open inside a diff editor.
544 > */
545 > readonly original: Omit<IResourceEditorInput, 'options'> | Omit<ITextResourceEditorInput, 'options'> | Omit<IUntitledTextResourceEditorInput, 'options'>;
546 >
547 > /**
548 > * The right hand side editor to open inside a diff editor.
549 > */
550 > readonly modified: Omit<IResourceEditorInput, 'options'> | Omit<ITextResourceEditorInput, 'options'> | Omit<IUntitledTextResourceEditorInput, 'options'>;
551 > }
552 >
553 > export interface ITextResourceDiffEditorInput extends IBaseTextResourceEditorInput {
554 >
555 > /**
556 > * The left hand side text editor to open inside a diff editor.
557 > */
558 > readonly original: Omit<ITextResourceEditorInput, 'options'> | Omit<IUntitledTextResourceEditorInput, 'options'>;
559 >
560 > /**
561 > * The right hand side text editor to open inside a diff editor.
562 > */
563 > readonly modified: Omit<ITextResourceEditorInput, 'options'> | Omit<IUntitledTextResourceEditorInput, 'options'>;
564 > }
565 >
566 > /**
567 > * A resource list diff editor input compares multiple resources side by side
568 > * highlighting the differences.
569 > */
570 > export interface IResourceMultiDiffEditorInput extends IBaseUntypedEditorInput {
571 > /**
572 > * A unique identifier of this multi diff editor input.
573 > * If a second multi diff editor with the same uri is opened, the existing one is revealed instead (even if the resources list is different!).
574 > */
575 > readonly multiDiffSource?: URI;
576 >
577 > /**
578 > * The list of resources to compare.
579 > * If not set, the resources are dynamically derived from the {@link multiDiffSource}.
580 > */
581 > readonly resources?: IMultiDiffEditorResource[];
582 >
583 > /**
584 > * Whether the editor should be serialized and stored for subsequent sessions.
585 > */
586 > readonly isTransient?: boolean;
587 > }
588 >
589 > export interface IMultiDiffEditorResource extends IResourceDiffEditorInput {
590 > readonly goToFileResource?: URI;
591 > }
592 > export type IResourceMergeEditorInputSide = (Omit<IResourceEditorInput, 'options'> | Omit<ITextResourceEditorInput, 'options'>) & { detail?: string };
593 >
594 > /**
595 > * A resource merge editor input compares multiple editors
596 > * highlighting the differences for merging.
597 > *
598 > * Note: all sides must be resolvable to the same editor, or
599 > * a text based presentation will be used as fallback.
600 > */
601 > export interface IResourceMergeEditorInput extends IBaseUntypedEditorInput {
602 >
603 > /**
604 > * The one changed version of the file.
605 > */
606 > readonly input1: IResourceMergeEditorInputSide;
607 >
608 > /**
609 > * The second changed version of the file.
610 > */
611 > readonly input2: IResourceMergeEditorInputSide;
612 >
613 > /**
614 > * The base common ancestor of the file to merge.
615 > */
616 > readonly base: Omit<IResourceEditorInput, 'options'> | Omit<ITextResourceEditorInput, 'options'>;
617 >
618 > /**
619 > * The resulting output of the merge.
620 > */
621 > readonly result: Omit<IResourceEditorInput, 'options'> | Omit<ITextResourceEditorInput, 'options'>;
622 > }
623 >
624 > export function isResourceEditorInput(editor: unknown): editor is IResourceEditorInput {
625 if (isEditorInput(editor)) {
626 return false; // make sure to not accidentally match on typed editor inputs
631 return URI.isUri(candidate?.resource);
632 }
633 > editor.ts
634 > export function isResourceDiffEditorInput(editor: unknown): editor is IResourceDiffEditorInput {
635 if (isEditorInput(editor)) {
636 return false; // make sure to not accidentally match on typed editor inputs
641 return candidate?.original !== undefined && candidate.modified !== undefined;
642 }
643 > editor.ts
644 > export function isResourceMultiDiffEditorInput(editor: unknown): editor is IResourceMultiDiffEditorInput {
645 if (isEditorInput(editor)) {
646 return false; // make sure to not accidentally match on typed editor inputs
657 return !!candidate.resources || !!candidate.multiDiffSource;
658 }
659 > editor.ts
660 > export function isResourceSideBySideEditorInput(editor: unknown): editor is IResourceSideBySideEditorInput {
661 if (isEditorInput(editor)) {
662 return false; // make sure to not accidentally match on typed editor inputs
671 return candidate?.primary !== undefined && candidate.secondary !== undefined;
672 }
673 > editor.ts
674 > export function isUntitledResourceEditorInput(editor: unknown): editor is IUntitledTextResourceEditorInput {
675 if (isEditorInput(editor)) {
676 return false; // make sure to not accidentally match on typed editor inputs
684 return candidate.resource === undefined || candidate.resource.scheme === Schemas.untitled || candidate.forceUntitled === true;
685 }
686 > editor.ts
687 > export function isResourceMergeEditorInput(editor: unknown): editor is IResourceMergeEditorInput {
688 if (isEditorInput(editor)) {
689 return false; // make sure to not accidentally match on typed editor inputs
694 return URI.isUri(candidate?.base?.resource) && URI.isUri(candidate?.input1?.resource) && URI.isUri(candidate?.input2?.resource) && URI.isUri(candidate?.result?.resource);
695 }
696 > editor.ts
697 > export const enum Verbosity {
698 > SHORT,
699 > MEDIUM,
700 > LONG
701 > }
702 >
703 > export const enum SaveReason {
704 >
705 > /**
706 > * Explicit user gesture.
707 > */
708 > EXPLICIT = 1,
709 >
710 > /**
711 > * Auto save after a timeout.
712 > */
713 > AUTO = 2,
714 >
715 > /**
716 > * Auto save after editor focus change.
717 > */
718 > FOCUS_CHANGE = 3,
719 >
720 > /**
721 > * Auto save after window change.
722 > */
723 > WINDOW_CHANGE = 4
724 > }
725 >
726 > export type SaveSource = string;
727 >
728 > interface ISaveSourceDescriptor {
729 > source: SaveSource;
730 > label: string;
731 > }
732 >
733 > class SaveSourceFactory {
734 >
735 > private readonly mapIdToSaveSource = new Map<SaveSource, ISaveSourceDescriptor>();
736 >
737 > /**
738 > * Registers a `SaveSource` with an identifier and label
739 > * to the registry so that it can be used in save operations.
740 > */
741 > registerSource(id: string, label: string): SaveSource {
742 let sourceDescriptor = this.mapIdToSaveSource.get(id);
743 if (!sourceDescriptor) {
748 return sourceDescriptor.source;
749 }
750 > editor.ts
751 > getSourceLabel(source: SaveSource): string {
752 return this.mapIdToSaveSource.get(source)?.label ?? source;
753 }
754 > } editor.ts
755 >
756 > export const SaveSourceRegistry = new SaveSourceFactory();
757 >
758 > export interface ISaveOptions {
759 >
760 > /**
761 > * An indicator how the save operation was triggered.
762 > */
763 > reason?: SaveReason;
764 >
765 > /**
766 > * An indicator about the source of the save operation.
767 > *
768 > * Must use `SaveSourceRegistry.registerSource()` to obtain.
769 > */
770 > readonly source?: SaveSource;
771 >
772 > /**
773 > * Forces to save the contents of the working copy
774 > * again even if the working copy is not dirty.
775 > */
776 > readonly force?: boolean;
777 >
778 > /**
779 > * Instructs the save operation to skip any save participants.
780 > */
781 > readonly skipSaveParticipants?: boolean;
782 >
783 > /**
784 > * A hint as to which file systems should be available for saving.
785 > */
786 > readonly availableFileSystems?: string[];
787 > }
788 >
789 > export interface IRevertOptions {
790 >
791 > /**
792 > * Forces to load the contents of the working copy
793 > * again even if the working copy is not dirty.
794 > */
795 > readonly force?: boolean;
796 >
797 > /**
798 > * A soft revert will clear dirty state of a working copy
799 > * but will not attempt to load it from its persisted state.
800 > *
801 > * This option may be used in scenarios where an editor is
802 > * closed and where we do not require to load the contents.
803 > */
804 > readonly soft?: boolean;
805 > }
806 >
807 > export interface IMoveResult {
808 > editor: EditorInput | IUntypedEditorInput;
809 > options?: IEditorOptions;
810 > }
811 >
812 > export const enum EditorInputCapabilities {
813 >
814 > /**
815 > * Signals no specific capability for the input.
816 > */
817 > None = 0,
818 >
819 > /**
820 > * Signals that the input is readonly.
821 > */
822 > Readonly = 1 << 1,
823 >
824 > /**
825 > * Signals that the input is untitled.
826 > */
827 > Untitled = 1 << 2,
828 >
829 > /**
830 > * Signals that the input can only be shown in one group
831 > * and not be split into multiple groups.
832 > */
833 > Singleton = 1 << 3,
834 >
835 > /**
836 > * Signals that the input requires workspace trust.
837 > */
838 > RequiresTrust = 1 << 4,
839 >
840 > /**
841 > * Signals that the editor can split into 2 in the same
842 > * editor group.
843 > */
844 > CanSplitInGroup = 1 << 5,
845 >
846 > /**
847 > * Signals that the editor wants its description to be
848 > * visible when presented to the user. By default, a UI
849 > * component may decide to hide the description portion
850 > * for brevity.
851 > */
852 > ForceDescription = 1 << 6,
853 >
854 > /**
855 > * Signals that the editor supports dropping into the
856 > * editor by holding shift.
857 > */
858 > CanDropIntoEditor = 1 << 7,
859 >
860 > /**
861 > * Signals that the editor is composed of multiple editors
862 > * within.
863 > */
864 > MultipleEditors = 1 << 8,
865 >
866 > /**
867 > * Signals that the editor cannot be in a dirty state
868 > * and may still have unsaved changes
869 > */
870 > Scratchpad = 1 << 9,
871 >
872 > /**
873 > * Signals that the editor should be revealed when being
874 > * opened if it is already opened in any editor group.
875 > */
876 > ForceReveal = 1 << 10,
877 >
878 > /**
879 > * Signals that the editor must be opened in a modal editor
880 > * part. This is honored unless the user has explicitly opted
881 > * out of modal editors via `workbench.editor.useModal: 'off'`.
882 > */
883 > RequiresModal = 1 << 11,
884 >
885 > /**
886 > * Signals that the editor is exempt from the opened editors
887 > * limit (`workbench.editor.limit`): it never counts towards the
888 > * limit and is never auto-closed to satisfy it.
889 > */
890 > ExcludeFromEditorLimit = 1 << 12
891 > }
892 >
893 > export type IUntypedEditorInput = IResourceEditorInput | ITextResourceEditorInput | IUntitledTextResourceEditorInput | IResourceDiffEditorInput | IResourceMultiDiffEditorInput | IResourceSideBySideEditorInput | IResourceMergeEditorInput;
894 >
895 > export abstract class AbstractEditorInput extends Disposable {
896 > // Marker class for implementing `isEditorInput`
897 > }
898 >
899 > export function isEditorInput(editor: unknown): editor is EditorInput {
900 return editor instanceof AbstractEditorInput;
901 }
902 > editor.ts
903 > export interface EditorInputWithPreferredResource {
904 >
905 > /**
906 > * An editor may provide an additional preferred resource alongside
907 > * the `resource` property. While the `resource` property serves as
908 > * unique identifier of the editor that should be used whenever we
909 > * compare to other editors, the `preferredResource` should be used
910 > * in places where e.g. the resource is shown to the user.
911 > *
912 > * For example: on Windows and macOS, the same URI with different
913 > * casing may point to the same file. The editor may chose to
914 > * "normalize" the URIs so that only one editor opens for different
915 > * URIs. But when displaying the editor label to the user, the
916 > * preferred URI should be used.
917 > *
918 > * Not all editors have a `preferredResource`. The `EditorResourceAccessor`
919 > * utility can be used to always get the right resource without having
920 > * to do instanceof checks.
921 > */
922 > readonly preferredResource: URI;
923 > }
924 >
925 function isEditorInputWithPreferredResource(editor: unknown): editor is EditorInputWithPreferredResource {
926 const candidate = editor as EditorInputWithPreferredResource | undefined;
928 return URI.isUri(candidate?.preferredResource);
929 }
930 > editor.ts
931 > export interface ISideBySideEditorInput extends EditorInput {
932 >
933 > /**
934 > * The primary editor input is shown on the right hand side.
935 > */
936 > primary: EditorInput;
937 >
938 > /**
939 > * The secondary editor input is shown on the left hand side.
940 > */
941 > secondary: EditorInput;
942 > }
943 >
944 > export function isSideBySideEditorInput(editor: unknown): editor is ISideBySideEditorInput {
945 const candidate = editor as ISideBySideEditorInput | undefined;
946
947 return isEditorInput(candidate?.primary) && isEditorInput(candidate?.secondary);
948 }
949 > editor.ts
950 > export interface IDiffEditorInput extends EditorInput {
951 >
952 > /**
953 > * The modified (primary) editor input is shown on the right hand side.
954 > */
955 > modified: EditorInput;
956 >
957 > /**
958 > * The original (secondary) editor input is shown on the left hand side.
959 > */
960 > original: EditorInput;
961 > }
962 >
963 > export function isDiffEditorInput(editor: unknown): editor is IDiffEditorInput {
964 const candidate = editor as IDiffEditorInput | undefined;
965
966 return isEditorInput(candidate?.modified) && isEditorInput(candidate?.original);
967 }
968 > editor.ts
969 > export interface IUntypedFileEditorInput extends ITextResourceEditorInput {
970 >
971 > /**
972 > * A marker to create a `IFileEditorInput` from this untyped input.
973 > */
974 > forceFile: true;
975 > }
976 >
977 > /**
978 > * This is a tagging interface to declare an editor input being capable of dealing with files. It is only used in the editor registry
979 > * to register this kind of input to the platform.
980 > */
981 > export interface IFileEditorInput extends EditorInput, IEncodingSupport, ILanguageSupport, EditorInputWithPreferredResource {
982 >
983 > /**
984 > * Gets the resource this file input is about. This will always be the
985 > * canonical form of the resource, so it may differ from the original
986 > * resource that was provided to create the input. Use `preferredResource`
987 > * for the form as it was created.
988 > */
989 > readonly resource: URI;
990 >
991 > /**
992 > * Sets the preferred resource to use for this file input.
993 > */
994 > setPreferredResource(preferredResource: URI): void;
995 >
996 > /**
997 > * Sets the preferred name to use for this file input.
998 > *
999 > * Note: for certain file schemes the input may decide to ignore this
1000 > * name and use our standard naming. Specifically for schemes we own,
1001 > * we do not let others override the name.
1002 > */
1003 > setPreferredName(name: string): void;
1004 >
1005 > /**
1006 > * Sets the preferred description to use for this file input.
1007 > *
1008 > * Note: for certain file schemes the input may decide to ignore this
1009 > * description and use our standard naming. Specifically for schemes we own,
1010 > * we do not let others override the description.
1011 > */
1012 > setPreferredDescription(description: string): void;
1013 >
1014 > /**
1015 > * Sets the preferred encoding to use for this file input.
1016 > */
1017 > setPreferredEncoding(encoding: string): void;
1018 >
1019 > /**
1020 > * Sets the preferred language id to use for this file input.
1021 > */
1022 > setPreferredLanguageId(languageId: string): void;
1023 >
1024 > /**
1025 > * Sets the preferred contents to use for this file input.
1026 > */
1027 > setPreferredContents(contents: string): void;
1028 >
1029 > /**
1030 > * Forces this file input to open as binary instead of text.
1031 > */
1032 > setForceOpenAsBinary(): void;
1033 >
1034 > /**
1035 > * Figure out if the file input has been resolved or not.
1036 > */
1037 > isResolved(): boolean;
1038 > }
1039 >
1040 > export interface IFileLimitedEditorInputOptions extends IEditorOptions {
1041 >
1042 > /**
1043 > * If provided, the size of the file will be checked against the limits
1044 > * and an error will be thrown if any limit is exceeded.
1045 > */
1046 > readonly limits?: IFileReadLimits;
1047 > }
1048 >
1049 > export interface IFileEditorInputOptions extends ITextEditorOptions, IFileLimitedEditorInputOptions { }
1050 >
1051 > export function createTooLargeFileError(group: IEditorGroup, input: EditorInput, options: IEditorOptions | undefined, message: string, preferencesService: IPreferencesService): Error {
1052 return createEditorOpenError(message, [
1053 toAction({
1073 });
1074 }
1075 > editor.ts
1076 > export interface EditorInputWithOptions {
1077 > editor: EditorInput;
1078 > options?: IEditorOptions;
1079 > }
1080 >
1081 > export interface EditorInputWithOptionsAndGroup extends EditorInputWithOptions {
1082 > group: IEditorGroup;
1083 > }
1084 >
1085 > export function isEditorInputWithOptions(editor: unknown): editor is EditorInputWithOptions {
1086 const candidate = editor as EditorInputWithOptions | undefined;
1087
1088 return isEditorInput(candidate?.editor);
1089 }
1090 > editor.ts
1091 > export function isEditorInputWithOptionsAndGroup(editor: unknown): editor is EditorInputWithOptionsAndGroup {
1092 const candidate = editor as EditorInputWithOptionsAndGroup | undefined;
1093
1094 return isEditorInputWithOptions(editor) && candidate?.group !== undefined;
1095 }
1096 > editor.ts
1097 > /**
1098 > * Context passed into `EditorPane#setInput` to give additional
1099 > * context information around why the editor was opened.
1100 > */
1101 > export interface IEditorOpenContext {
1102 >
1103 > /**
1104 > * An indicator if the editor input is new for the group the editor is in.
1105 > * An editor is new for a group if it was not part of the group before and
1106 > * otherwise was already opened in the group and just became the active editor.
1107 > *
1108 > * This hint can e.g. be used to decide whether to restore view state or not.
1109 > */
1110 > newInGroup?: boolean;
1111 > }
1112 >
1113 > export interface IEditorIdentifier {
1114 > groupId: GroupIdentifier;
1115 > editor: EditorInput;
1116 > }
1117 >
1118 > export function isEditorIdentifier(identifier: unknown): identifier is IEditorIdentifier {
1119 const candidate = identifier as IEditorIdentifier | undefined;
1120
1121 return typeof candidate?.groupId === 'number' && isEditorInput(candidate.editor);
1122 }
1123 > editor.ts
1124 > /**
1125 > * The editor commands context is used for editor commands (e.g. in the editor title)
1126 > * and we must ensure that the context is serializable because it potentially travels
1127 > * to the extension host!
1128 > */
1129 > export interface IEditorCommandsContext {
1130 > groupId: GroupIdentifier;
1131 > editorIndex?: number;
1132 >
1133 > preserveFocus?: boolean;
1134 > }
1135 >
1136 > export function isEditorCommandsContext(context: unknown): context is IEditorCommandsContext {
1137 const candidate = context as IEditorCommandsContext | undefined;
1138
1139 return typeof candidate?.groupId === 'number';
1140 }
1141 > editor.ts
1142 > /**
1143 > * More information around why an editor was closed in the model.
1144 > */
1145 > export enum EditorCloseContext {
1146 >
1147 > /**
1148 > * No specific context for closing (e.g. explicit user gesture).
1149 > */
1150 > UNKNOWN,
1151 >
1152 > /**
1153 > * The editor closed because it was replaced with another editor.
1154 > * This can either happen via explicit replace call or when an
1155 > * editor is in preview mode and another editor opens.
1156 > */
1157 > REPLACE,
1158 >
1159 > /**
1160 > * The editor closed as a result of moving it to another group.
1161 > */
1162 > MOVE,
1163 >
1164 > /**
1165 > * The editor closed because another editor turned into preview
1166 > * and this used to be the preview editor before.
1167 > */
1168 > UNPIN
1169 > }
1170 >
1171 > export interface IEditorCloseEvent extends IEditorIdentifier {
1172 >
1173 > /**
1174 > * More information around why the editor was closed.
1175 > */
1176 > readonly context: EditorCloseContext;
1177 >
1178 > /**
1179 > * The index of the editor before closing.
1180 > */
1181 > readonly index: number;
1182 >
1183 > /**
1184 > * Whether the editor was sticky or not.
1185 > */
1186 > readonly sticky: boolean;
1187 > }
1188 >
1189 > export interface IActiveEditorChangeEvent {
1190 >
1191 > /**
1192 > * The new active editor or `undefined` if the group is empty.
1193 > */
1194 > editor: EditorInput | undefined;
1195 >
1196 > /**
1197 > * Indicates whether the editor change is the result of an explicit
1198 > * user action (`true`) or happened automatically as a side effect
1199 > * (e.g. the chat agent opening files it has edited).
1200 > *
1201 > * When omitted, callers should treat the change as explicit.
1202 > */
1203 > isExplicit?: boolean;
1204 > }
1205 >
1206 > export interface IEditorWillMoveEvent extends IEditorIdentifier {
1207 >
1208 > /**
1209 > * The target group of the move operation.
1210 > */
1211 > readonly target: GroupIdentifier;
1212 > }
1213 >
1214 > export interface IEditorWillOpenEvent extends IEditorIdentifier { }
1215 >
1216 > export interface IWillInstantiateEditorPaneEvent {
1217 >
1218 > /**
1219 > * @see {@link IEditorDescriptor.typeId}
1220 > */
1221 > readonly typeId: string;
1222 > }
1223 >
1224 > export type GroupIdentifier = number;
1225 >
1226 > export const enum GroupModelChangeKind {
1227 >
1228 > /* Group Changes */
1229 > GROUP_ACTIVE,
1230 > GROUP_INDEX,
1231 > GROUP_LABEL,
1232 > GROUP_LOCKED,
1233 >
1234 > /* Editors Change */
1235 > EDITORS_SELECTION,
1236 >
1237 > /* Editor Changes */
1238 > EDITOR_OPEN,
1239 > EDITOR_CLOSE,
1240 > EDITOR_MOVE,
1241 > EDITOR_ACTIVE,
1242 > EDITOR_LABEL,
1243 > EDITOR_CAPABILITIES,
1244 > EDITOR_PIN,
1245 > EDITOR_TRANSIENT,
1246 > EDITOR_STICKY,
1247 > EDITOR_DIRTY,
1248 > EDITOR_WILL_DISPOSE
1249 > }
1250 >
1251 > export interface IWorkbenchEditorConfiguration {
1252 > workbench?: {
1253 > editor?: IEditorPartConfiguration;
1254 > iconTheme?: string;
1255 > };
1256 > }
1257 >
1258 > interface IEditorPartLimitConfiguration {
1259 > enabled?: boolean;
1260 > excludeDirty?: boolean;
1261 > value?: number;
1262 > perEditorGroup?: boolean;
1263 > }
1264 >
1265 > export interface IEditorPartLimitOptions extends Required<IEditorPartLimitConfiguration> { }
1266 >
1267 > interface IEditorPartDecorationsConfiguration {
1268 > badges?: boolean;
1269 > colors?: boolean;
1270 > }
1271 >
1272 > export interface IEditorPartDecorationOptions extends Required<IEditorPartDecorationsConfiguration> { }
1273 >
1274 > interface IEditorPartConfiguration {
1275 > showTabs?: 'multiple' | 'single' | 'none';
1276 > wrapTabs?: boolean;
1277 > scrollToSwitchTabs?: boolean;
1278 > highlightModifiedTabs?: boolean;
1279 > tabActionLocation?: 'left' | 'right';
1280 > tabActionCloseVisibility?: boolean;
1281 > tabActionUnpinVisibility?: boolean;
1282 > showTabIndex?: boolean;
1283 > alwaysShowEditorActions?: boolean;
1284 > tabSizing?: 'fit' | 'shrink' | 'fixed';
1285 > tabSizingFixedMinWidth?: number;
1286 > tabSizingFixedMaxWidth?: number;
1287 > pinnedTabSizing?: 'normal' | 'compact' | 'shrink';
1288 > pinnedTabsOnSeparateRow?: boolean;
1289 > tabHeight?: 'default' | 'compact';
1290 > preventPinnedEditorClose?: PreventPinnedEditorClose;
1291 > titleScrollbarSizing?: 'default' | 'large';
1292 > titleScrollbarVisibility?: 'auto' | 'visible' | 'hidden';
1293 > focusRecentEditorAfterClose?: boolean;
1294 > showIcons?: boolean;
1295 > enablePreview?: boolean;
1296 > enablePreviewFromQuickOpen?: boolean;
1297 > enablePreviewFromCodeNavigation?: boolean;
1298 > closeOnFileDelete?: boolean;
1299 > openPositioning?: 'left' | 'right' | 'first' | 'last';
1300 > openSideBySideDirection?: 'right' | 'down';
1301 > closeEmptyGroups?: boolean;
1302 > autoLockGroups?: Set<string>;
1303 > revealIfOpen?: boolean;
1304 > swipeToNavigate?: boolean;
1305 > mouseBackForwardToNavigate?: boolean;
1306 > labelFormat?: 'default' | 'short' | 'medium' | 'long';
1307 > restoreViewState?: boolean;
1308 > splitInGroupLayout?: 'vertical' | 'horizontal';
1309 > splitSizing?: 'auto' | 'split' | 'distribute';
1310 > splitOnDragAndDrop?: boolean;
1311 > allowDropIntoGroup?: boolean;
1312 > dragToOpenWindow?: boolean;
1313 > centeredLayoutFixedWidth?: boolean;
1314 > doubleClickTabToToggleEditorGroupSizes?: 'maximize' | 'expand' | 'off';
1315 > editorActionsLocation?: 'default' | 'titleBar' | 'hidden';
1316 > limit?: IEditorPartLimitConfiguration;
1317 > decorations?: IEditorPartDecorationsConfiguration;
1318 > }
1319 >
1320 > export interface IEditorPartOptions extends DeepRequiredNonNullable<IEditorPartConfiguration> {
1321 > hasIcons: boolean;
1322 > }
1323 >
1324 > export interface IEditorPartOptionsChangeEvent {
1325 > oldPartOptions: IEditorPartOptions;
1326 > newPartOptions: IEditorPartOptions;
1327 > }
1328 >
1329 > export enum SideBySideEditor {
1330 > PRIMARY = 1,
1331 > SECONDARY = 2,
1332 > BOTH = 3,
1333 > ANY = 4
1334 > }
1335 >
1336 > export interface IFindEditorOptions {
1337 >
1338 > /**
1339 > * Whether to consider any or both side by side editor as matching.
1340 > * By default, side by side editors will not be considered
1341 > * as matching, even if the editor is opened in one of the sides.
1342 > */
1343 > supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY | SideBySideEditor.ANY;
1344 >
1345 > /**
1346 > * The order in which to consider editors for finding.
1347 > */
1348 > order?: EditorsOrder;
1349 > }
1350 >
1351 > export interface IMatchEditorOptions {
1352 >
1353 > /**
1354 > * Whether to consider a side by side editor as matching.
1355 > * By default, side by side editors will not be considered
1356 > * as matching, even if the editor is opened in one of the sides.
1357 > */
1358 > supportSideBySide?: SideBySideEditor.ANY | SideBySideEditor.BOTH;
1359 >
1360 > /**
1361 > * Only consider an editor to match when the
1362 > * `candidate === editor` but not when
1363 > * `candidate.matches(editor)`.
1364 > */
1365 > strictEquals?: boolean;
1366 > }
1367 >
1368 > export interface IEditorResourceAccessorOptions {
1369 >
1370 > /**
1371 > * Allows to access the `resource(s)` of side by side editors. If not
1372 > * specified, a `resource` for a side by side editor will always be
1373 > * `undefined`.
1374 > */
1375 > supportSideBySide?: SideBySideEditor;
1376 >
1377 > /**
1378 > * Allows to filter the scheme to consider. A resource scheme that does
1379 > * not match a filter will not be considered.
1380 > */
1381 > filterByScheme?: string | string[];
1382 > }
1383 >
1384 > class EditorResourceAccessorImpl {
1385 >
1386 > /**
1387 > * The original URI of an editor is the URI that was used originally to open
1388 > * the editor and should be used whenever the URI is presented to the user,
1389 > * e.g. as a label together with utility methods such as `ResourceLabel` or
1390 > * `ILabelService` that can turn this original URI into the best form for
1391 > * presenting.
1392 > *
1393 > * In contrast, the canonical URI (#getCanonicalUri) may be different and should
1394 > * be used whenever the URI is used to e.g. compare with other editors or when
1395 > * caching certain data based on the URI.
1396 > *
1397 > * For example: on Windows and macOS, the same file URI with different casing may
1398 > * point to the same file. The editor may chose to "normalize" the URI into a canonical
1399 > * form so that only one editor opens for same file URIs with different casing. As
1400 > * such, the original URI and the canonical URI can be different.
1401 > */
1402 > getOriginalUri(editor: EditorInput | IUntypedEditorInput | undefined | null): URI | undefined;
1403 > getOriginalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options: IEditorResourceAccessorOptions & { supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY | SideBySideEditor.ANY }): URI | undefined;
1404 > getOriginalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options: IEditorResourceAccessorOptions & { supportSideBySide: SideBySideEditor.BOTH }): URI | { primary?: URI; secondary?: URI } | undefined;
1405 > getOriginalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options?: IEditorResourceAccessorOptions): URI | { primary?: URI; secondary?: URI } | undefined;
1406 > getOriginalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options?: IEditorResourceAccessorOptions): URI | { primary?: URI; secondary?: URI } | undefined {
1407 if (!editor) {
1408 return undefined;
1443 return this.filterUri(originalResource, options.filterByScheme);
1444 }
1445 > editor.ts
1446 > private getSideEditors(editor: EditorInput | IUntypedEditorInput): { primary: EditorInput | IUntypedEditorInput | undefined; secondary: EditorInput | IUntypedEditorInput | undefined } {
1447 if (isSideBySideEditorInput(editor) || isResourceSideBySideEditorInput(editor)) {
1448 return { primary: editor.primary, secondary: editor.secondary };
1455 return { primary: undefined, secondary: undefined };
1456 }
1457 > editor.ts
1458 > /**
1459 > * The canonical URI of an editor is the true unique identifier of the editor
1460 > * and should be used whenever the URI is used e.g. to compare with other
1461 > * editors or when caching certain data based on the URI.
1462 > *
1463 > * In contrast, the original URI (#getOriginalUri) may be different and should
1464 > * be used whenever the URI is presented to the user, e.g. as a label.
1465 > *
1466 > * For example: on Windows and macOS, the same file URI with different casing may
1467 > * point to the same file. The editor may chose to "normalize" the URI into a canonical
1468 > * form so that only one editor opens for same file URIs with different casing. As
1469 > * such, the original URI and the canonical URI can be different.
1470 > */
1471 > getCanonicalUri(editor: EditorInput | IUntypedEditorInput | undefined | null): URI | undefined;
1472 > getCanonicalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options: IEditorResourceAccessorOptions & { supportSideBySide?: SideBySideEditor.PRIMARY | SideBySideEditor.SECONDARY | SideBySideEditor.ANY }): URI | undefined;
1473 > getCanonicalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options: IEditorResourceAccessorOptions & { supportSideBySide: SideBySideEditor.BOTH }): URI | { primary?: URI; secondary?: URI } | undefined;
1474 > getCanonicalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options?: IEditorResourceAccessorOptions): URI | { primary?: URI; secondary?: URI } | undefined;
1475 > getCanonicalUri(editor: EditorInput | IUntypedEditorInput | undefined | null, options?: IEditorResourceAccessorOptions): URI | { primary?: URI; secondary?: URI } | undefined {
1476 if (!editor) {
1477 return undefined;
1512 return this.filterUri(canonicalResource, options.filterByScheme);
1513 }
1514 > editor.ts
1515 > private filterUri(resource: URI, filter: string | string[]): URI | undefined {
1516
1517 // Multiple scheme filter
1531 return undefined;
1532 }
1533 > } editor.ts
1534 >
1535 > export type PreventPinnedEditorClose = 'keyboardAndMouse' | 'keyboard' | 'mouse' | 'never' | undefined;
1536 >
1537 > export enum EditorCloseMethod {
1538 > UNKNOWN,
1539 > KEYBOARD,
1540 > MOUSE
1541 > }
1542 >
1543 > export function preventEditorClose(group: IEditorGroup | IReadonlyEditorGroupModel, editor: EditorInput, method: EditorCloseMethod, configuration: IEditorPartConfiguration): boolean {
1544 if (!group.isSticky(editor)) {
1545 return false; // only interested in sticky editors
1554 return false;
1555 }
1556 > editor.ts
1557 > export const EditorResourceAccessor = new EditorResourceAccessorImpl();
1558 >
1559 > export const enum CloseDirection {
1560 > LEFT,
1561 > RIGHT
1562 > }
1563 >
1564 > export interface IEditorMemento<T> {
1565 >
1566 > saveEditorState(group: IEditorGroup, resource: URI, state: T): void;
1567 > saveEditorState(group: IEditorGroup, editor: EditorInput, state: T): void;
1568 >
1569 > loadEditorState(group: IEditorGroup, resource: URI): T | undefined;
1570 > loadEditorState(group: IEditorGroup, editor: EditorInput): T | undefined;
1571 >
1572 > clearEditorState(resource: URI, group?: IEditorGroup): void;
1573 > clearEditorState(editor: EditorInput, group?: IEditorGroup): void;
1574 >
1575 > clearEditorStateOnDispose(resource: URI, editor: EditorInput): void;
1576 >
1577 > moveEditorState(source: URI, target: URI, comparer: IExtUri): void;
1578 > }
1579 >
1580 > class EditorFactoryRegistry implements IEditorFactoryRegistry {
1581 > private instantiationService: IInstantiationService | undefined;
1582 >
1583 > private fileEditorFactory: IFileEditorFactory | undefined;
1584 >
1585 > private readonly editorSerializerConstructors = new Map<string /* Type ID */, IConstructorSignature<IEditorSerializer>>();
1586 > private readonly editorSerializerInstances = new Map<string /* Type ID */, IEditorSerializer>();
1587 >
1588 > start(accessor: ServicesAccessor): void {
1589 const instantiationService = this.instantiationService = accessor.get(IInstantiationService);
1590
1595 this.editorSerializerConstructors.clear();
1596 }
1597 > editor.ts
1598 > private createEditorSerializer(editorTypeId: string, ctor: IConstructorSignature<IEditorSerializer>, instantiationService: IInstantiationService): void {
1599 const instance = instantiationService.createInstance(ctor);
1600 this.editorSerializerInstances.set(editorTypeId, instance);
1601 }
1602 > editor.ts
1603 > registerFileEditorFactory(factory: IFileEditorFactory): void {
1604 if (this.fileEditorFactory) {
1605 throw new Error('Can only register one file editor factory.');
1608 this.fileEditorFactory = factory;
1609 }
1610 > editor.ts
1611 > getFileEditorFactory(): IFileEditorFactory {
1612 return assertReturnsDefined(this.fileEditorFactory);
1613 }
1614 > editor.ts
1615 > registerEditorSerializer(editorTypeId: string, ctor: IConstructorSignature<IEditorSerializer>): IDisposable {
1616 if (this.editorSerializerConstructors.has(editorTypeId) || this.editorSerializerInstances.has(editorTypeId)) {
1617 throw new Error(`A editor serializer with type ID '${editorTypeId}' was already registered.`);
1629 });
1630 }
1631 > editor.ts
1632 > getEditorSerializer(editor: EditorInput): IEditorSerializer | undefined;
1633 > getEditorSerializer(editorTypeId: string): IEditorSerializer | undefined;
1634 > getEditorSerializer(arg1: string | EditorInput): IEditorSerializer | undefined {
1635 return this.editorSerializerInstances.get(typeof arg1 === 'string' ? arg1 : arg1.typeId);
1636 }
1637 > } editor.ts
1638 >
1639 > Registry.add(EditorExtensions.EditorFactory, new EditorFactoryRegistry());
1640 >
1641 export async function pathsToEditors(paths: IPathData[] | undefined, fileService: IFileService, logService: ILogService): Promise<ReadonlyArray<IResourceEditorInput | IUntitledTextResourceEditorInput | undefined>> {
1642 if (!paths?.length) {
1691 }));
1692 }
1693 > editor.ts
1694 > export const enum EditorsOrder {
1695 >
1696 > /**
1697 > * Editors sorted by most recent activity (most recent active first)
1698 > */
1699 > MOST_RECENTLY_ACTIVE,
1700 >
1701 > /**
1702 > * Editors sorted by sequential order
1703 > */
1704 > SEQUENTIAL
1705 > }
1706 >
1707 > export function isTextEditorViewState(candidate: unknown): candidate is IEditorViewState {
1708 const viewState = candidate as IEditorViewState | undefined;
1709 if (!viewState) {
1720 return !!(codeEditorViewState.contributionsState && codeEditorViewState.viewState && Array.isArray(codeEditorViewState.cursorState));
1721 }
1722 > editor.ts
1723 > export interface IEditorOpenErrorOptions {
1724 >
1725 > /**
1726 > * If set to true, the message will be taken
1727 > * from the error message entirely and not be
1728 > * composed with more text.
1729 > */
1730 > forceMessage?: boolean;
1731 >
1732 > /**
1733 > * If set, will override the severity of the error.
1734 > */
1735 > forceSeverity?: Severity;
1736 >
1737 > /**
1738 > * If set to true, the error may be shown in a dialog
1739 > * to the user if the editor opening was triggered by
1740 > * user action. Otherwise and by default, the error will
1741 > * be shown as place holder in the editor area.
1742 > */
1743 > allowDialog?: boolean;
1744 > }
1745 >
1746 > export interface IEditorOpenError extends IErrorWithActions, IEditorOpenErrorOptions { }
1747 >
1748 > export function isEditorOpenError(obj: unknown): obj is IEditorOpenError {
1749 return isErrorWithActions(obj);
1750 }
1751 > editor.ts
1752 > export function createEditorOpenError(messageOrError: string | Error, actions: IAction[], options?: IEditorOpenErrorOptions): IEditorOpenError {
1753 const error: IEditorOpenError = createErrorWithActions(messageOrError, actions);
1754
1759 return error;
1760 }
1761 > editor.ts
1762 > export interface IToolbarActions {
1763 > readonly primary: IAction[];
1764 > readonly secondary: IAction[];
1765 > }
src/vs/platform/files/common/files.ts 1360 covered LOC · 44 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- files.ts
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 { VSBuffer, VSBufferReadable, VSBufferReadableStream } from '../../../base/common/buffer.js';
7 > import { CancellationToken } from '../../../base/common/cancellation.js';
8 > import { Event } from '../../../base/common/event.js';
9 > import { IExpression, IRelativePattern } from '../../../base/common/glob.js';
10 > import { IDisposable } from '../../../base/common/lifecycle.js';
11 > import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';
12 > import { sep } from '../../../base/common/path.js';
13 > import { ReadableStreamEvents } from '../../../base/common/stream.js';
14 > import { startsWithIgnoreCase } from '../../../base/common/strings.js';
15 > import { isNumber } from '../../../base/common/types.js';
16 > import { URI } from '../../../base/common/uri.js';
17 > import { localize } from '../../../nls.js';
18 > import { createDecorator } from '../../instantiation/common/instantiation.js';
19 > import { isWeb } from '../../../base/common/platform.js';
20 > import { Schemas } from '../../../base/common/network.js';
21 > import { IMarkdownString } from '../../../base/common/htmlContent.js';
22 > import { Lazy } from '../../../base/common/lazy.js';
23 >
24 > //#region file service & providers
25 >
26 > export const IFileService = createDecorator<IFileService>('fileService');
27 >
28 > export interface IFileService {
29 >
30 > readonly _serviceBrand: undefined;
31 >
32 > /**
33 > * An event that is fired when a file system provider is added or removed
34 > */
35 > readonly onDidChangeFileSystemProviderRegistrations: Event<IFileSystemProviderRegistrationEvent>;
36 >
37 > /**
38 > * An event that is fired when a registered file system provider changes its capabilities.
39 > */
40 > readonly onDidChangeFileSystemProviderCapabilities: Event<IFileSystemProviderCapabilitiesChangeEvent>;
41 >
42 > /**
43 > * An event that is fired when a file system provider is about to be activated. Listeners
44 > * can join this event with a long running promise to help in the activation process.
45 > */
46 > readonly onWillActivateFileSystemProvider: Event<IFileSystemProviderActivationEvent>;
47 >
48 > /**
49 > * Registers a file system provider for a certain scheme.
50 > */
51 > registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable;
52 >
53 > /**
54 > * Returns a file system provider for a certain scheme.
55 > */
56 > getProvider(scheme: string): IFileSystemProvider | undefined;
57 >
58 > /**
59 > * Tries to activate a provider with the given scheme.
60 > */
61 > activateProvider(scheme: string): Promise<void>;
62 >
63 > /**
64 > * Checks if this file service can handle the given resource by
65 > * first activating any extension that wants to be activated
66 > * on the provided resource scheme to include extensions that
67 > * contribute file system providers for the given resource.
68 > */
69 > canHandleResource(resource: URI): Promise<boolean>;
70 >
71 > /**
72 > * Checks if the file service has a registered provider for the
73 > * provided resource.
74 > *
75 > * Note: this does NOT account for contributed providers from
76 > * extensions that have not been activated yet. To include those,
77 > * consider to call `await fileService.canHandleResource(resource)`.
78 > */
79 > hasProvider(resource: URI): boolean;
80 >
81 > /**
82 > * Checks if the provider for the provided resource has the provided file system capability.
83 > */
84 > hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean;
85 >
86 > /**
87 > * List the schemes and capabilities for registered file system providers
88 > */
89 > listCapabilities(): Iterable<{ scheme: string; capabilities: FileSystemProviderCapabilities }>;
90 >
91 > /**
92 > * Allows to listen for file changes. The event will fire for every file within the opened workspace
93 > * (if any) as well as all files that have been watched explicitly using the #watch() API.
94 > */
95 > readonly onDidFilesChange: Event<FileChangesEvent>;
96 >
97 > /**
98 > * An event that is fired upon successful completion of a certain file operation.
99 > */
100 > readonly onDidRunOperation: Event<FileOperationEvent>;
101 >
102 > /**
103 > * Resolve the properties of a file/folder identified by the resource. For a folder, children
104 > * information is resolved as well depending on the provided options. Use `stat()` method if
105 > * you do not need children information.
106 > *
107 > * If the optional parameter "resolveTo" is specified in options, the stat service is asked
108 > * to provide a stat object that should contain the full graph of folders up to all of the
109 > * target resources.
110 > *
111 > * If the optional parameter "resolveSingleChildDescendants" is specified in options,
112 > * the stat service is asked to automatically resolve child folders that only
113 > * contain a single element.
114 > *
115 > * If the optional parameter "resolveMetadata" is specified in options,
116 > * the stat will contain metadata information such as size, mtime and etag.
117 > */
118 > resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
119 > resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
120 >
121 > /**
122 > * Same as `resolve()` but supports resolving multiple resources in parallel.
123 > *
124 > * If one of the resolve targets fails to resolve returns a fake `IFileStat` instead of
125 > * making the whole call fail.
126 > */
127 > resolveAll(toResolve: { resource: URI; options: IResolveMetadataFileOptions }[]): Promise<IFileStatResult[]>;
128 > resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]>;
129 >
130 > /**
131 > * Same as `resolve()` but without resolving the children of a folder if the
132 > * resource is pointing to a folder.
133 > */
134 > stat(resource: URI): Promise<IFileStatWithPartialMetadata>;
135 >
136 > /**
137 > * Attempts to resolve the real path of the provided resource. The real path can be
138 > * different from the resource path for example when it is a symlink.
139 > *
140 > * Will return `undefined` if the real path cannot be resolved.
141 > */
142 > realpath(resource: URI): Promise<URI | undefined>;
143 >
144 > /**
145 > * Finds out if a file/folder identified by the resource exists.
146 > */
147 > exists(resource: URI): Promise<boolean>;
148 >
149 > /**
150 > * Read the contents of the provided resource unbuffered.
151 > */
152 > readFile(resource: URI, options?: IReadFileOptions, token?: CancellationToken): Promise<IFileContent>;
153 >
154 > /**
155 > * Read the contents of the provided resource buffered as stream.
156 > */
157 > readFileStream(resource: URI, options?: IReadFileStreamOptions, token?: CancellationToken): Promise<IFileStreamContent>;
158 >
159 > /**
160 > * Updates the content replacing its previous value.
161 > * If `options.append` is true, appends content to the end of the file instead.
162 > *
163 > * Emits a `FileOperation.WRITE` file operation event when successful.
164 > */
165 > writeFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: IWriteFileOptions): Promise<IFileStatWithMetadata>;
166 >
167 > /**
168 > * Moves the file/folder to a new path identified by the resource.
169 > *
170 > * The optional parameter overwrite can be set to replace an existing file at the location.
171 > *
172 > * Emits a `FileOperation.MOVE` file operation event when successful.
173 > */
174 > move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
175 >
176 > /**
177 > * Find out if a move operation is possible given the arguments. No changes on disk will
178 > * be performed. Returns an Error if the operation cannot be done.
179 > */
180 > canMove(source: URI, target: URI, overwrite?: boolean): Promise<Error | true>;
181 >
182 > /**
183 > * Copies the file/folder to a path identified by the resource. A folder is copied
184 > * recursively.
185 > *
186 > * Emits a `FileOperation.COPY` file operation event when successful.
187 > */
188 > copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
189 >
190 > /**
191 > * Find out if a copy operation is possible given the arguments. No changes on disk will
192 > * be performed. Returns an Error if the operation cannot be done.
193 > */
194 > canCopy(source: URI, target: URI, overwrite?: boolean): Promise<Error | true>;
195 >
196 > /**
197 > * Clones a file to a path identified by the resource. Folders are not supported.
198 > *
199 > * If the target path exists, it will be overwritten.
200 > */
201 > cloneFile(source: URI, target: URI): Promise<void>;
202 >
203 > /**
204 > * Creates a new file with the given path and optional contents. The returned promise
205 > * will have the stat model object as a result.
206 > *
207 > * The optional parameter content can be used as value to fill into the new file.
208 > *
209 > * Emits a `FileOperation.CREATE` file operation event when successful.
210 > */
211 > createFile(resource: URI, bufferOrReadableOrStream?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
212 >
213 > /**
214 > * Find out if a file create operation is possible given the arguments. No changes on disk will
215 > * be performed. Returns an Error if the operation cannot be done.
216 > */
217 > canCreateFile(resource: URI, options?: ICreateFileOptions): Promise<Error | true>;
218 >
219 > /**
220 > * Creates a new folder with the given path. The returned promise
221 > * will have the stat model object as a result.
222 > *
223 > * Emits a `FileOperation.CREATE` file operation event when successful.
224 > */
225 > createFolder(resource: URI): Promise<IFileStatWithMetadata>;
226 >
227 > /**
228 > * Deletes the provided file. The optional useTrash parameter allows to
229 > * move the file to trash. The optional recursive parameter allows to delete
230 > * non-empty folders recursively.
231 > *
232 > * Emits a `FileOperation.DELETE` file operation event when successful.
233 > */
234 > del(resource: URI, options?: Partial<IFileDeleteOptions>): Promise<void>;
235 >
236 > /**
237 > * Find out if a delete operation is possible given the arguments. No changes on disk will
238 > * be performed. Returns an Error if the operation cannot be done.
239 > */
240 > canDelete(resource: URI, options?: Partial<IFileDeleteOptions>): Promise<Error | true>;
241 >
242 > /**
243 > * An event that signals an error when watching for file changes.
244 > */
245 > readonly onDidWatchError: Event<Error>;
246 >
247 > /**
248 > * Allows to start a watcher that reports file/folder change events on the provided resource.
249 > *
250 > * The watcher runs correlated and thus, file events will be reported on the returned
251 > * `IFileSystemWatcher` and not on the generic `IFileService.onDidFilesChange` event.
252 > *
253 > * Note: only non-recursive file watching supports event correlation for now.
254 > */
255 > createWatcher(resource: URI, options: IWatchOptionsWithoutCorrelation & { recursive: false }): IFileSystemWatcher;
256 >
257 > /**
258 > * Allows to start a watcher that reports file/folder change events on the provided resource.
259 > *
260 > * The watcher runs uncorrelated and thus will report all events from `IFileService.onDidFilesChange`.
261 > * This means, most listeners in the application will receive your events. It is encouraged to
262 > * use correlated watchers (via `IWatchOptionsWithCorrelation`) to limit events to your listener.
263 > */
264 > watch(resource: URI, options?: IWatchOptionsWithoutCorrelation): IDisposable;
265 >
266 > /**
267 > * Frees up any resources occupied by this service.
268 > */
269 > dispose(): void;
270 > }
271 >
272 > export interface IFileOverwriteOptions {
273 >
274 > /**
275 > * Set to `true` to overwrite a file if it exists. Will
276 > * throw an error otherwise if the file does exist.
277 > */
278 > readonly overwrite: boolean;
279 > }
280 >
281 > export interface IFileUnlockOptions {
282 >
283 > /**
284 > * Set to `true` to try to remove any write locks the file might
285 > * have. A file that is write locked will throw an error for any
286 > * attempt to write to unless `unlock: true` is provided.
287 > */
288 > readonly unlock: boolean;
289 > }
290 >
291 > export interface IFileAtomicReadOptions {
292 >
293 > /**
294 > * The optional `atomic` flag can be used to make sure
295 > * the `readFile` method is not running in parallel with
296 > * any `write` operations in the same process.
297 > *
298 > * Typically you should not need to use this flag but if
299 > * for example you are quickly reading a file right after
300 > * a file event occurred and the file changes a lot, there
301 > * is a chance that a read returns an empty or partial file
302 > * because a pending write has not finished yet.
303 > *
304 > * Note: this does not prevent the file from being written
305 > * to from a different process. If you need such atomic
306 > * operations, you better use a real database as storage.
307 > */
308 > readonly atomic: boolean;
309 > }
310 >
311 > export interface IFileAtomicOptions {
312 >
313 > /**
314 > * The postfix is used to create a temporary file based
315 > * on the original resource. The resulting temporary
316 > * file will be in the same folder as the resource and
317 > * have `postfix` appended to the resource name.
318 > *
319 > * Example: given a file resource `file:///some/path/foo.txt`
320 > * and a postfix `.vsctmp`, the temporary file will be
321 > * created as `file:///some/path/foo.txt.vsctmp`.
322 > */
323 > readonly postfix: string;
324 > }
325 >
326 > export interface IFileAtomicWriteOptions {
327 >
328 > /**
329 > * The optional `atomic` flag can be used to make sure
330 > * the `writeFile` method updates the target file atomically
331 > * by first writing to a temporary file in the same folder
332 > * and then renaming it over the target.
333 > */
334 > readonly atomic: IFileAtomicOptions | false;
335 > }
336 >
337 > export interface IFileAtomicDeleteOptions {
338 >
339 > /**
340 > * The optional `atomic` flag can be used to make sure
341 > * the `delete` method deletes the target atomically by
342 > * first renaming it to a temporary resource in the same
343 > * folder and then deleting it.
344 > */
345 > readonly atomic: IFileAtomicOptions | false;
346 > }
347 >
348 > export interface IFileReadLimits {
349 >
350 > /**
351 > * If the file exceeds the given size, an error of kind
352 > * `FILE_TOO_LARGE` will be thrown.
353 > */
354 > size?: number;
355 > }
356 >
357 > export interface IFileReadStreamOptions {
358 >
359 > /**
360 > * Is an integer specifying where to begin reading from in the file. If position is undefined,
361 > * data will be read from the current file position.
362 > */
363 > readonly position?: number;
364 >
365 > /**
366 > * Is an integer specifying how many bytes to read from the file. By default, all bytes
367 > * will be read.
368 > */
369 > readonly length?: number;
370 >
371 > /**
372 > * If provided, the size of the file will be checked against the limits
373 > * and an error will be thrown if any limit is exceeded.
374 > */
375 > readonly limits?: IFileReadLimits;
376 > }
377 >
378 > export interface IFileWriteOptions extends IFileOverwriteOptions, IFileUnlockOptions, IFileAtomicWriteOptions {
379 >
380 > /**
381 > * Set to `true` to create a file when it does not exist. Will
382 > * throw an error otherwise if the file does not exist.
383 > */
384 > readonly create: boolean;
385 >
386 > /**
387 > * Set to `true` to append content to the end of the file. Implies `create: true`,
388 > * and set only when the corresponding `FileAppend` capability is defined.
389 > */
390 > readonly append?: boolean;
391 > }
392 >
393 > export type IFileOpenOptions = IFileOpenForReadOptions | IFileOpenForWriteOptions;
394 >
395 > export function isFileOpenForWriteOptions(options: IFileOpenOptions): options is IFileOpenForWriteOptions {
396 return options.create === true;
397 }
398 > files.ts
399 > export interface IFileOpenForReadOptions {
400 >
401 > /**
402 > * A hint that the file should be opened for reading only.
403 > */
404 > readonly create: false;
405 > }
406 >
407 > export interface IFileOpenForWriteOptions extends IFileUnlockOptions {
408 >
409 > /**
410 > * A hint that the file should be opened for reading and writing.
411 > */
412 > readonly create: true;
413 >
414 > /**
415 > * Open the file in append mode. This will write data to the
416 > * end of the file.
417 > */
418 > readonly append?: boolean;
419 > }
420 >
421 > export interface IFileDeleteOptions {
422 >
423 > /**
424 > * Set to `true` to recursively delete any children of the file. This
425 > * only applies to folders and can lead to an error unless provided
426 > * if the folder is not empty.
427 > */
428 > readonly recursive: boolean;
429 >
430 > /**
431 > * Set to `true` to attempt to move the file to trash
432 > * instead of deleting it permanently from disk.
433 > *
434 > * This option maybe not be supported on all providers.
435 > */
436 > readonly useTrash: boolean;
437 >
438 > /**
439 > * The optional `atomic` flag can be used to make sure
440 > * the `delete` method deletes the target atomically by
441 > * first renaming it to a temporary resource in the same
442 > * folder and then deleting it.
443 > *
444 > * This option maybe not be supported on all providers.
445 > */
446 > readonly atomic: IFileAtomicOptions | false;
447 > }
448 >
449 > export enum FileType {
450 >
451 > /**
452 > * File is unknown (neither file, directory nor symbolic link).
453 > */
454 > Unknown = 0,
455 >
456 > /**
457 > * File is a normal file.
458 > */
459 > File = 1,
460 >
461 > /**
462 > * File is a directory.
463 > */
464 > Directory = 2,
465 >
466 > /**
467 > * File is a symbolic link.
468 > *
469 > * Note: even when the file is a symbolic link, you can test for
470 > * `FileType.File` and `FileType.Directory` to know the type of
471 > * the target the link points to.
472 > */
473 > SymbolicLink = 64
474 > }
475 >
476 > export enum FilePermission {
477 >
478 > /**
479 > * File is readonly. Components like editors should not
480 > * offer to edit the contents.
481 > */
482 > Readonly = 1,
483 >
484 > /**
485 > * File is locked. Components like editors should offer
486 > * to edit the contents and ask the user upon saving to
487 > * remove the lock.
488 > */
489 > Locked = 2,
490 >
491 > /**
492 > * File is executable. Relevant for Unix-like systems where
493 > * the executable bit determines if a file can be run.
494 > */
495 > Executable = 4
496 > }
497 >
498 > export interface IStat {
499 >
500 > /**
501 > * The file type.
502 > */
503 > readonly type: FileType;
504 >
505 > /**
506 > * The last modification date represented as millis from unix epoch.
507 > */
508 > readonly mtime: number;
509 >
510 > /**
511 > * The creation date represented as millis from unix epoch.
512 > */
513 > readonly ctime: number;
514 >
515 > /**
516 > * The size of the file in bytes.
517 > */
518 > readonly size: number;
519 >
520 > /**
521 > * The file permissions.
522 > */
523 > readonly permissions?: FilePermission;
524 > }
525 >
526 > export interface IWatchOptionsWithoutCorrelation {
527 >
528 > /**
529 > * Set to `true` to watch for changes recursively in a folder
530 > * and all of its children.
531 > */
532 > recursive: boolean;
533 >
534 > /**
535 > * A set of glob patterns or paths to exclude from watching.
536 > * Paths can be relative or absolute and when relative are
537 > * resolved against the watched folder. Glob patterns are
538 > * always matched relative to the watched folder.
539 > */
540 > excludes: string[];
541 >
542 > /**
543 > * An optional set of glob patterns or paths to include for
544 > * watching. If not provided, all paths are considered for
545 > * events.
546 > * Paths can be relative or absolute and when relative are
547 > * resolved against the watched folder. Glob patterns are
548 > * always matched relative to the watched folder.
549 > */
550 > includes?: Array<string | IRelativePattern>;
551 >
552 > /**
553 > * If provided, allows to filter the events that the watcher should consider
554 > * for emitting. If not provided, all events are emitted.
555 > *
556 > * For example, to emit added and updated events, set to:
557 > * `FileChangeFilter.ADDED | FileChangeFilter.UPDATED`.
558 > */
559 > filter?: FileChangeFilter;
560 > }
561 >
562 > export interface IWatchOptions extends IWatchOptionsWithoutCorrelation {
563 >
564 > /**
565 > * If provided, file change events from the watcher that
566 > * are a result of this watch request will carry the same
567 > * id.
568 > */
569 > readonly correlationId?: number;
570 > }
571 >
572 > export const enum FileChangeFilter {
573 > UPDATED = 1 << 1,
574 > ADDED = 1 << 2,
575 > DELETED = 1 << 3
576 > }
577 >
578 > export interface IWatchOptionsWithCorrelation extends IWatchOptions {
579 > readonly correlationId: number;
580 > }
581 >
582 > export interface IFileSystemWatcher extends IDisposable {
583 >
584 > /**
585 > * An event which fires on file/folder change only for changes
586 > * that correlate to the watch request with matching correlation
587 > * identifier.
588 > */
589 > readonly onDidChange: Event<FileChangesEvent>;
590 > }
591 >
592 > export function isFileSystemWatcher(thing: unknown): thing is IFileSystemWatcher {
593 const candidate = thing as IFileSystemWatcher | undefined;
594
595 return !!candidate && typeof candidate.onDidChange === 'function';
596 }
597 > files.ts
598 > export const enum FileSystemProviderCapabilities {
599 >
600 > /**
601 > * No capabilities.
602 > */
603 > None = 0,
604 >
605 > /**
606 > * Provider supports unbuffered read/write.
607 > */
608 > FileReadWrite = 1 << 1,
609 >
610 > /**
611 > * Provider supports open/read/write/close low level file operations.
612 > */
613 > FileOpenReadWriteClose = 1 << 2,
614 >
615 > /**
616 > * Provider supports stream based reading.
617 > */
618 > FileReadStream = 1 << 4,
619 >
620 > /**
621 > * Provider supports copy operation.
622 > */
623 > FileFolderCopy = 1 << 3,
624 >
625 > /**
626 > * Provider is path case sensitive.
627 > */
628 > PathCaseSensitive = 1 << 10,
629 >
630 > /**
631 > * All files of the provider are readonly.
632 > */
633 > Readonly = 1 << 11,
634 >
635 > /**
636 > * Provider supports to delete via trash.
637 > */
638 > Trash = 1 << 12,
639 >
640 > /**
641 > * Provider support to unlock files for writing.
642 > */
643 > FileWriteUnlock = 1 << 13,
644 >
645 > /**
646 > * Provider support to read files atomically. This implies the
647 > * provider provides the `FileReadWrite` capability too.
648 > */
649 > FileAtomicRead = 1 << 14,
650 >
651 > /**
652 > * Provider support to write files atomically. This implies the
653 > * provider provides the `FileReadWrite` capability too.
654 > */
655 > FileAtomicWrite = 1 << 15,
656 >
657 > /**
658 > * Provider support to delete atomically.
659 > */
660 > FileAtomicDelete = 1 << 16,
661 >
662 > /**
663 > * Provider support to clone files atomically.
664 > */
665 > FileClone = 1 << 17,
666 >
667 > /**
668 > * Provider support to resolve real paths.
669 > */
670 > FileRealpath = 1 << 18,
671 >
672 > /**
673 > * Provider support to append to files.
674 > */
675 > FileAppend = 1 << 19
676 > }
677 >
678 > export interface IFileSystemProvider {
679 >
680 > readonly capabilities: FileSystemProviderCapabilities;
681 > readonly onDidChangeCapabilities: Event<void>;
682 >
683 > readonly onDidChangeFile: Event<readonly IFileChange[]>;
684 > readonly onDidWatchError?: Event<string>;
685 > watch(resource: URI, opts: IWatchOptions): IDisposable;
686 >
687 > stat(resource: URI): Promise<IStat>;
688 > mkdir(resource: URI): Promise<void>;
689 > readdir(resource: URI): Promise<[string, FileType][]>;
690 > delete(resource: URI, opts: IFileDeleteOptions): Promise<void>;
691 >
692 > rename(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void>;
693 > copy?(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void>;
694 >
695 > readFile?(resource: URI): Promise<Uint8Array>;
696 > writeFile?(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise<void>;
697 >
698 > readFileStream?(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array>;
699 >
700 > open?(resource: URI, opts: IFileOpenOptions): Promise<number>;
701 > close?(fd: number): Promise<void>;
702 > read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
703 > write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
704 >
705 > cloneFile?(from: URI, to: URI): Promise<void>;
706 > }
707 >
708 > export interface IFileSystemProviderWithFileReadWriteCapability extends IFileSystemProvider {
709 > readFile(resource: URI): Promise<Uint8Array>;
710 > writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise<void>;
711 > }
712 >
713 > export function hasReadWriteCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileReadWriteCapability {
714 return !!(provider.capabilities & FileSystemProviderCapabilities.FileReadWrite);
715 }
716 > files.ts
717 > export function hasFileAppendCapability(provider: IFileSystemProvider): boolean {
718 return !!(provider.capabilities & FileSystemProviderCapabilities.FileAppend);
719 }
720 > files.ts
721 > export interface IFileSystemProviderWithFileFolderCopyCapability extends IFileSystemProvider {
722 > copy(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void>;
723 > }
724 >
725 > export function hasFileFolderCopyCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileFolderCopyCapability {
726 return !!(provider.capabilities & FileSystemProviderCapabilities.FileFolderCopy);
727 }
728 > files.ts
729 > export interface IFileSystemProviderWithFileCloneCapability extends IFileSystemProvider {
730 > cloneFile(from: URI, to: URI): Promise<void>;
731 > }
732 >
733 > export function hasFileCloneCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileCloneCapability {
734 return !!(provider.capabilities & FileSystemProviderCapabilities.FileClone);
735 }
736 > files.ts
737 > export interface IFileSystemProviderWithFileRealpathCapability extends IFileSystemProvider {
738 > realpath(resource: URI): Promise<string>;
739 > }
740 >
741 > export function hasFileRealpathCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileRealpathCapability {
742 return !!(provider.capabilities & FileSystemProviderCapabilities.FileRealpath);
743 }
744 > files.ts
745 > export interface IFileSystemProviderWithOpenReadWriteCloseCapability extends IFileSystemProvider {
746 > open(resource: URI, opts: IFileOpenOptions): Promise<number>;
747 > close(fd: number): Promise<void>;
748 > read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
749 > write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
750 > }
751 >
752 > export function hasOpenReadWriteCloseCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithOpenReadWriteCloseCapability {
753 return !!(provider.capabilities & FileSystemProviderCapabilities.FileOpenReadWriteClose);
754 }
755 > files.ts
756 > export interface IFileSystemProviderWithFileReadStreamCapability extends IFileSystemProvider {
757 > readFileStream(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array>;
758 > }
759 >
760 > export function hasFileReadStreamCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileReadStreamCapability {
761 return !!(provider.capabilities & FileSystemProviderCapabilities.FileReadStream);
762 }
763 > files.ts
764 > export interface IFileSystemProviderWithFileAtomicReadCapability extends IFileSystemProvider {
765 > readFile(resource: URI, opts?: IFileAtomicReadOptions): Promise<Uint8Array>;
766 > enforceAtomicReadFile?(resource: URI): boolean;
767 > }
768 >
769 > export function hasFileAtomicReadCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileAtomicReadCapability {
770 if (!hasReadWriteCapability(provider)) {
771 return false; // we require the `FileReadWrite` capability too
774 return !!(provider.capabilities & FileSystemProviderCapabilities.FileAtomicRead);
775 }
776 > files.ts
777 > export interface IFileSystemProviderWithFileAtomicWriteCapability extends IFileSystemProvider {
778 > writeFile(resource: URI, contents: Uint8Array, opts?: IFileAtomicWriteOptions): Promise<void>;
779 > enforceAtomicWriteFile?(resource: URI): IFileAtomicOptions | false;
780 > }
781 >
782 > export function hasFileAtomicWriteCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileAtomicWriteCapability {
783 if (!hasReadWriteCapability(provider)) {
784 return false; // we require the `FileReadWrite` capability too
787 return !!(provider.capabilities & FileSystemProviderCapabilities.FileAtomicWrite);
788 }
789 > files.ts
790 > export interface IFileSystemProviderWithFileAtomicDeleteCapability extends IFileSystemProvider {
791 > delete(resource: URI, opts: IFileAtomicDeleteOptions): Promise<void>;
792 > enforceAtomicDelete?(resource: URI): IFileAtomicOptions | false;
793 > }
794 >
795 > export function hasFileAtomicDeleteCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileAtomicDeleteCapability {
796 return !!(provider.capabilities & FileSystemProviderCapabilities.FileAtomicDelete);
797 }
798 > files.ts
799 > export interface IFileSystemProviderWithReadonlyCapability extends IFileSystemProvider {
800 >
801 > readonly capabilities: FileSystemProviderCapabilities.Readonly & FileSystemProviderCapabilities;
802 >
803 > /**
804 > * An optional message to show in the UI to explain why the file system is readonly.
805 > */
806 > readonly readOnlyMessage?: IMarkdownString;
807 > }
808 >
809 > export function hasReadonlyCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithReadonlyCapability {
810 return !!(provider.capabilities & FileSystemProviderCapabilities.Readonly);
811 }
812 > files.ts
813 > export enum FileSystemProviderErrorCode {
814 > FileExists = 'EntryExists',
815 > FileNotFound = 'EntryNotFound',
816 > FileNotADirectory = 'EntryNotADirectory',
817 > FileIsADirectory = 'EntryIsADirectory',
818 > FileExceedsStorageQuota = 'EntryExceedsStorageQuota',
819 > FileTooLarge = 'EntryTooLarge',
820 > FileWriteLocked = 'EntryWriteLocked',
821 > NoPermissions = 'NoPermissions',
822 > Unavailable = 'Unavailable',
823 > Unknown = 'Unknown'
824 > }
825 >
826 > export interface IFileSystemProviderError extends Error {
827 > readonly name: string;
828 > readonly code: FileSystemProviderErrorCode;
829 > }
830 >
831 > export class FileSystemProviderError extends Error implements IFileSystemProviderError {
832 >
833 > static create(error: Error | string, code: FileSystemProviderErrorCode): FileSystemProviderError {
834 > const providerError = new FileSystemProviderError(error.toString(), code);
835 > markAsFileSystemProviderError(providerError, code);
836 >
837 > return providerError;
838 > }
839 >
840 > private constructor(message: string, readonly code: FileSystemProviderErrorCode) {
841 super(message);
842 }
843 > } files.ts
844 >
845 > export function createFileSystemProviderError(error: Error | string, code: FileSystemProviderErrorCode): FileSystemProviderError {
846 return FileSystemProviderError.create(error, code);
847 }
848 > files.ts
849 > export function ensureFileSystemProviderError(error?: Error): Error {
850 if (!error) {
851 return createFileSystemProviderError(localize('unknownError', "Unknown Error"), FileSystemProviderErrorCode.Unknown); // https://github.com/microsoft/vscode/issues/72798
854 return error;
855 }
856 > files.ts
857 > export function markAsFileSystemProviderError(error: Error, code: FileSystemProviderErrorCode): Error {
858 error.name = code ? `${code} (FileSystemError)` : `FileSystemError`;
859
860 return error;
861 }
862 > files.ts
863 > export function toFileSystemProviderErrorCode(error: Error | undefined | null): FileSystemProviderErrorCode {
864
865 // Guard against abuse
893 return FileSystemProviderErrorCode.Unknown;
894 }
895 > files.ts
896 > export function toFileOperationResult(error: Error): FileOperationResult {
897
898 // FileSystemProviderError comes with the result already
921 }
922 }
923 > files.ts
924 > export interface IFileSystemProviderRegistrationEvent {
925 > readonly added: boolean;
926 > readonly scheme: string;
927 > readonly provider?: IFileSystemProvider;
928 > }
929 >
930 > export interface IFileSystemProviderCapabilitiesChangeEvent {
931 > readonly provider: IFileSystemProvider;
932 > readonly scheme: string;
933 > }
934 >
935 > export interface IFileSystemProviderActivationEvent {
936 > readonly scheme: string;
937 > join(promise: Promise<void>): void;
938 > }
939 >
940 > export const enum FileOperation {
941 > CREATE,
942 > DELETE,
943 > MOVE,
944 > COPY,
945 > WRITE
946 > }
947 >
948 > export interface IFileOperationEvent {
949 >
950 > readonly resource: URI;
951 > readonly operation: FileOperation;
952 >
953 > isOperation(operation: FileOperation.DELETE | FileOperation.WRITE): boolean;
954 > isOperation(operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY): this is IFileOperationEventWithMetadata;
955 > }
956 >
957 > export interface IFileOperationEventWithMetadata extends IFileOperationEvent {
958 > readonly target: IFileStatWithMetadata;
959 > }
960 >
961 > export class FileOperationEvent implements IFileOperationEvent {
962 >
963 > constructor(resource: URI, operation: FileOperation.DELETE | FileOperation.WRITE);
964 > constructor(resource: URI, operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY, target: IFileStatWithMetadata);
965 > constructor(readonly resource: URI, readonly operation: FileOperation, readonly target?: IFileStatWithMetadata) { }
966 >
967 > isOperation(operation: FileOperation.DELETE | FileOperation.WRITE): boolean;
968 > isOperation(operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY): this is IFileOperationEventWithMetadata;
969 > isOperation(operation: FileOperation): boolean {
970 return this.operation === operation;
971 }
972 > } files.ts
973 >
974 > /**
975 > * Possible changes that can occur to a file.
976 > */
977 > export const enum FileChangeType {
978 > UPDATED,
979 > ADDED,
980 > DELETED
981 > }
982 >
983 > /**
984 > * Identifies a single change in a file.
985 > */
986 > export interface IFileChange {
987 >
988 > /**
989 > * The type of change that occurred to the file.
990 > */
991 > type: FileChangeType;
992 >
993 > /**
994 > * The unified resource identifier of the file that changed.
995 > */
996 > readonly resource: URI;
997 >
998 > /**
999 > * If provided when starting the file watcher, the correlation
1000 > * identifier will match the original file watching request as
1001 > * a way to identify the original component that is interested
1002 > * in the change.
1003 > */
1004 > readonly cId?: number;
1005 > }
1006 >
1007 > export class FileChangesEvent {
1008 >
1009 > private static readonly MIXED_CORRELATION = null;
1010 >
1011 > private readonly correlationId: number | undefined | typeof FileChangesEvent.MIXED_CORRELATION = undefined;
1012 >
1013 > constructor(changes: readonly IFileChange[], private readonly ignorePathCasing: boolean) {
1014 for (const change of changes) {
1015
1043 }
1044 }
1045 > files.ts
1046 > private readonly added = new Lazy(() => {
1047 > const added = TernarySearchTree.forUris<boolean>(() => this.ignorePathCasing); files.ts
1048 > added.fill(this.rawAdded.map(resource => [resource, true]));
1049 >
1050 > return added;
1051 > }); files.ts
1052 >
1053 > private readonly updated = new Lazy(() => {
1054 > const updated = TernarySearchTree.forUris<boolean>(() => this.ignorePathCasing); files.ts
1055 > updated.fill(this.rawUpdated.map(resource => [resource, true]));
1056 >
1057 > return updated;
1058 > }); files.ts
1059 >
1060 > private readonly deleted = new Lazy(() => {
1061 > const deleted = TernarySearchTree.forUris<boolean>(() => this.ignorePathCasing); files.ts
1062 > deleted.fill(this.rawDeleted.map(resource => [resource, true]));
1063 >
1064 > return deleted;
1065 > }); files.ts
1066 >
1067 > /**
1068 > * Find out if the file change events match the provided resource.
1069 > *
1070 > * Note: when passing `FileChangeType.DELETED`, we consider a match
1071 > * also when the parent of the resource got deleted.
1072 > */
1073 > contains(resource: URI, ...types: FileChangeType[]): boolean {
1074 return this.doContains(resource, { includeChildren: false }, ...types);
1075 }
1076 > files.ts
1077 > /**
1078 > * Find out if the file change events either match the provided
1079 > * resource, or contain a child of this resource.
1080 > */
1081 > affects(resource: URI, ...types: FileChangeType[]): boolean {
1082 return this.doContains(resource, { includeChildren: true }, ...types);
1083 }
1084 > files.ts
1085 > private doContains(resource: URI, options: { includeChildren: boolean }, ...types: FileChangeType[]): boolean {
1086 if (!resource) {
1087 return false;
1125 return false;
1126 }
1127 > files.ts
1128 > /**
1129 > * Returns if this event contains added files.
1130 > */
1131 > gotAdded(): boolean {
1132 return this.rawAdded.length > 0;
1133 }
1134 > files.ts
1135 > /**
1136 > * Returns if this event contains deleted files.
1137 > */
1138 > gotDeleted(): boolean {
1139 return this.rawDeleted.length > 0;
1140 }
1141 > files.ts
1142 > /**
1143 > * Returns if this event contains updated files.
1144 > */
1145 > gotUpdated(): boolean {
1146 return this.rawUpdated.length > 0;
1147 }
1148 > files.ts
1149 > /**
1150 > * Returns if this event contains changes that correlate to the
1151 > * provided `correlationId`.
1152 > *
1153 > * File change event correlation is an advanced watch feature that
1154 > * allows to identify from which watch request the events originate
1155 > * from. This correlation allows to route events specifically
1156 > * only to the requestor and not emit them to all listeners.
1157 > */
1158 > correlates(correlationId: number): boolean {
1159 return this.correlationId === correlationId;
1160 }
1161 > files.ts
1162 > /**
1163 > * Figure out if the event contains changes that correlate to one
1164 > * correlation identifier.
1165 > *
1166 > * File change event correlation is an advanced watch feature that
1167 > * allows to identify from which watch request the events originate
1168 > * from. This correlation allows to route events specifically
1169 > * only to the requestor and not emit them to all listeners.
1170 > */
1171 > hasCorrelation(): boolean {
1172 return typeof this.correlationId === 'number';
1173 }
1174 > files.ts
1175 > /**
1176 > * @deprecated use the `contains` or `affects` method to efficiently find
1177 > * out if the event relates to a given resource. these methods ensure:
1178 > * - that there is no expensive lookup needed (by using a `TernarySearchTree`)
1179 > * - correctly handles `FileChangeType.DELETED` events
1180 > */
1181 > readonly rawAdded: URI[] = [];
1182 >
1183 > /**
1184 > * @deprecated use the `contains` or `affects` method to efficiently find
1185 > * out if the event relates to a given resource. these methods ensure:
1186 > * - that there is no expensive lookup needed (by using a `TernarySearchTree`)
1187 > * - correctly handles `FileChangeType.DELETED` events
1188 > */
1189 > readonly rawUpdated: URI[] = [];
1190 >
1191 > /**
1192 > * @deprecated use the `contains` or `affects` method to efficiently find
1193 > * out if the event relates to a given resource. these methods ensure:
1194 > * - that there is no expensive lookup needed (by using a `TernarySearchTree`)
1195 > * - correctly handles `FileChangeType.DELETED` events
1196 > */
1197 > readonly rawDeleted: URI[] = [];
1198 > }
1199 >
1200 > export function isParent(path: string, candidate: string, ignoreCase?: boolean): boolean {
1201 if (!path || !candidate || path === candidate) {
1202 return false;
1217 return path.indexOf(candidate) === 0;
1218 }
1219 > files.ts
1220 > export interface IBaseFileStat {
1221 >
1222 > /**
1223 > * The unified resource identifier of this file or folder.
1224 > */
1225 > readonly resource: URI;
1226 >
1227 > /**
1228 > * The name which is the last segment
1229 > * of the {{path}}.
1230 > */
1231 > readonly name: string;
1232 >
1233 > /**
1234 > * The size of the file.
1235 > *
1236 > * The value may or may not be resolved as
1237 > * it is optional.
1238 > */
1239 > readonly size?: number;
1240 >
1241 > /**
1242 > * The last modification date represented as millis from unix epoch.
1243 > *
1244 > * The value may or may not be resolved as
1245 > * it is optional.
1246 > */
1247 > readonly mtime?: number;
1248 >
1249 > /**
1250 > * The creation date represented as millis from unix epoch.
1251 > *
1252 > * The value may or may not be resolved as
1253 > * it is optional.
1254 > */
1255 > readonly ctime?: number;
1256 >
1257 > /**
1258 > * A unique identifier that represents the
1259 > * current state of the file or directory.
1260 > *
1261 > * The value may or may not be resolved as
1262 > * it is optional.
1263 > */
1264 > readonly etag?: string;
1265 >
1266 > /**
1267 > * File is readonly. Components like editors should not
1268 > * offer to edit the contents.
1269 > */
1270 > readonly readonly?: boolean;
1271 >
1272 > /**
1273 > * File is locked. Components like editors should offer
1274 > * to edit the contents and ask the user upon saving to
1275 > * remove the lock.
1276 > */
1277 > readonly locked?: boolean;
1278 >
1279 > /**
1280 > * File is executable. Relevant for Unix-like systems where
1281 > * the executable bit determines if a file can be run.
1282 > */
1283 > readonly executable?: boolean;
1284 > }
1285 >
1286 > export interface IBaseFileStatWithMetadata extends Required<IBaseFileStat> { }
1287 >
1288 > /**
1289 > * A file resource with meta information and resolved children if any.
1290 > */
1291 > export interface IFileStat extends IBaseFileStat {
1292 >
1293 > /**
1294 > * The resource is a file.
1295 > */
1296 > readonly isFile: boolean;
1297 >
1298 > /**
1299 > * The resource is a directory.
1300 > */
1301 > readonly isDirectory: boolean;
1302 >
1303 > /**
1304 > * The resource is a symbolic link. Note: even when the
1305 > * file is a symbolic link, you can test for `FileType.File`
1306 > * and `FileType.Directory` to know the type of the target
1307 > * the link points to.
1308 > */
1309 > readonly isSymbolicLink: boolean;
1310 >
1311 > /**
1312 > * The children of the file stat or undefined if none.
1313 > */
1314 > children: IFileStat[] | undefined;
1315 > }
1316 >
1317 > export interface IFileStatWithMetadata extends IFileStat, IBaseFileStatWithMetadata {
1318 > readonly mtime: number;
1319 > readonly ctime: number;
1320 > readonly etag: string;
1321 > readonly size: number;
1322 > readonly readonly: boolean;
1323 > readonly locked: boolean;
1324 > readonly executable: boolean;
1325 > readonly children: IFileStatWithMetadata[] | undefined;
1326 > }
1327 >
1328 > export interface IFileStatResult {
1329 > readonly stat?: IFileStat;
1330 > readonly success: boolean;
1331 > }
1332 >
1333 > export interface IFileStatResultWithMetadata extends IFileStatResult {
1334 > readonly stat?: IFileStatWithMetadata;
1335 > }
1336 >
1337 > export interface IFileStatWithPartialMetadata extends Omit<IFileStatWithMetadata, 'children'> { }
1338 >
1339 > export interface IFileContent extends IBaseFileStatWithMetadata {
1340 >
1341 > /**
1342 > * The content of a file as buffer.
1343 > */
1344 > readonly value: VSBuffer;
1345 > }
1346 >
1347 > export interface IFileStreamContent extends IBaseFileStatWithMetadata {
1348 >
1349 > /**
1350 > * The content of a file as stream.
1351 > */
1352 > readonly value: VSBufferReadableStream;
1353 > }
1354 >
1355 > export interface IBaseReadFileOptions extends IFileReadStreamOptions {
1356 >
1357 > /**
1358 > * The optional etag parameter allows to return early from resolving the resource if
1359 > * the contents on disk match the etag. This prevents accumulated reading of resources
1360 > * that have been read already with the same etag.
1361 > * It is the task of the caller to makes sure to handle this error case from the promise.
1362 > */
1363 > readonly etag?: string;
1364 > }
1365 >
1366 > export interface IReadFileStreamOptions extends IBaseReadFileOptions { }
1367 >
1368 > export interface IReadFileOptions extends IBaseReadFileOptions {
1369 >
1370 > /**
1371 > * The optional `atomic` flag can be used to make sure
1372 > * the `readFile` method is not running in parallel with
1373 > * any `write` operations in the same process.
1374 > *
1375 > * Typically you should not need to use this flag but if
1376 > * for example you are quickly reading a file right after
1377 > * a file event occurred and the file changes a lot, there
1378 > * is a chance that a read returns an empty or partial file
1379 > * because a pending write has not finished yet.
1380 > *
1381 > * Note: this does not prevent the file from being written
1382 > * to from a different process. If you need such atomic
1383 > * operations, you better use a real database as storage.
1384 > */
1385 > readonly atomic?: boolean;
1386 > }
1387 >
1388 > export interface IWriteFileOptions {
1389 >
1390 > /**
1391 > * The last known modification time of the file. This can be used to prevent dirty writes.
1392 > */
1393 > readonly mtime?: number;
1394 >
1395 > /**
1396 > * The etag of the file. This can be used to prevent dirty writes.
1397 > */
1398 > readonly etag?: string;
1399 >
1400 > /**
1401 > * Whether to attempt to unlock a file before writing.
1402 > */
1403 > readonly unlock?: boolean;
1404 >
1405 > /**
1406 > * The optional `atomic` flag can be used to make sure
1407 > * the `writeFile` method updates the target file atomically
1408 > * by first writing to a temporary file in the same folder
1409 > * and then renaming it over the target.
1410 > */
1411 > readonly atomic?: IFileAtomicOptions | false;
1412 >
1413 > /**
1414 > * If set to true, will append to the end of the file instead of
1415 > * replacing its contents. Will create the file if it doesn't exist.
1416 > */
1417 > readonly append?: boolean;
1418 > }
1419 >
1420 > export interface IResolveFileOptions {
1421 >
1422 > /**
1423 > * Automatically continue resolving children of a directory until the provided resources
1424 > * are found.
1425 > */
1426 > readonly resolveTo?: readonly URI[];
1427 >
1428 > /**
1429 > * Automatically continue resolving children of a directory if the number of children is 1.
1430 > */
1431 > readonly resolveSingleChildDescendants?: boolean;
1432 >
1433 > /**
1434 > * Will resolve mtime, ctime, size and etag of files if enabled. This can have a negative impact
1435 > * on performance and thus should only be used when these values are required.
1436 > */
1437 > readonly resolveMetadata?: boolean;
1438 > }
1439 >
1440 > export interface IResolveMetadataFileOptions extends IResolveFileOptions {
1441 > readonly resolveMetadata: true;
1442 > }
1443 >
1444 > export interface ICreateFileOptions {
1445 >
1446 > /**
1447 > * Overwrite the file to create if it already exists on disk. Otherwise
1448 > * an error will be thrown (FILE_MODIFIED_SINCE).
1449 > */
1450 > readonly overwrite?: boolean;
1451 > }
1452 >
1453 > export class FileOperationError extends Error {
1454 > constructor(
1455 message: string,
1456 readonly fileOperationResult: FileOperationResult,
1459 super(message);
1460 }
1461 > } files.ts
1462 >
1463 > export class TooLargeFileOperationError extends FileOperationError {
1464 > constructor(
1465 message: string,
1466 override readonly fileOperationResult: FileOperationResult.FILE_TOO_LARGE,
1470 super(message, fileOperationResult, options);
1471 }
1472 > } files.ts
1473 >
1474 > export class NotModifiedSinceFileOperationError extends FileOperationError {
1475 >
1476 > constructor(
1477 message: string,
1478 readonly stat: IFileStatWithMetadata,
1481 super(message, FileOperationResult.FILE_NOT_MODIFIED_SINCE, options);
1482 }
1483 > } files.ts
1484 >
1485 > export const enum FileOperationResult {
1486 > FILE_IS_DIRECTORY,
1487 > FILE_NOT_FOUND,
1488 > FILE_NOT_MODIFIED_SINCE,
1489 > FILE_MODIFIED_SINCE,
1490 > FILE_MOVE_CONFLICT,
1491 > FILE_WRITE_LOCKED,
1492 > FILE_PERMISSION_DENIED,
1493 > FILE_TOO_LARGE,
1494 > FILE_INVALID_PATH,
1495 > FILE_NOT_DIRECTORY,
1496 > FILE_OTHER_ERROR
1497 > }
1498 >
1499 > //#endregion
1500 >
1501 > //#region Settings
1502 >
1503 > export const AutoSaveConfiguration = {
1504 > OFF: 'off',
1505 > AFTER_DELAY: 'afterDelay',
1506 > ON_FOCUS_CHANGE: 'onFocusChange',
1507 > ON_WINDOW_CHANGE: 'onWindowChange'
1508 > };
1509 >
1510 > export const HotExitConfiguration = {
1511 > OFF: 'off',
1512 > ON_EXIT: 'onExit',
1513 > ON_EXIT_AND_WINDOW_CLOSE: 'onExitAndWindowClose'
1514 > };
1515 >
1516 > export const FILES_ASSOCIATIONS_CONFIG = 'files.associations';
1517 > export const FILES_EXCLUDE_CONFIG = 'files.exclude';
1518 > export const FILES_READONLY_INCLUDE_CONFIG = 'files.readonlyInclude';
1519 > export const FILES_READONLY_EXCLUDE_CONFIG = 'files.readonlyExclude';
1520 > export const FILES_READONLY_FROM_PERMISSIONS_CONFIG = 'files.readonlyFromPermissions';
1521 >
1522 > export interface IGlobPatterns {
1523 > [filepattern: string]: boolean;
1524 > }
1525 >
1526 > export interface IFilesConfiguration {
1527 > files?: IFilesConfigurationNode;
1528 > }
1529 >
1530 > export interface IFilesConfigurationNode {
1531 > associations: { [filepattern: string]: string };
1532 > exclude: IExpression;
1533 > watcherExclude: IGlobPatterns;
1534 > watcherInclude: string[];
1535 > encoding: string;
1536 > autoGuessEncoding: boolean;
1537 > candidateGuessEncodings: string[];
1538 > defaultLanguage: string;
1539 > trimTrailingWhitespace: boolean;
1540 > autoSave: string;
1541 > autoSaveDelay: number;
1542 > autoSaveWorkspaceFilesOnly: boolean;
1543 > autoSaveWhenNoErrors: boolean;
1544 > eol: string;
1545 > enableTrash: boolean;
1546 > hotExit: string;
1547 > saveConflictResolution: 'askUser' | 'overwriteFileOnDisk';
1548 > readonlyInclude: IGlobPatterns;
1549 > readonlyExclude: IGlobPatterns;
1550 > readonlyFromPermissions: boolean;
1551 > }
1552 >
1553 > //#endregion
1554 >
1555 > //#region Utilities
1556 >
1557 > export enum FileKind {
1558 > FILE,
1559 > FOLDER,
1560 > ROOT_FOLDER
1561 > }
1562 >
1563 > /**
1564 > * A hint to disable etag checking for reading/writing.
1565 > */
1566 > export const ETAG_DISABLED = '';
1567 >
1568 > export function etag(stat: { mtime: number; size: number }): string;
1569 > export function etag(stat: { mtime: number | undefined; size: number | undefined }): string | undefined;
1570 > export function etag(stat: { mtime: number | undefined; size: number | undefined }): string | undefined {
1571 if (typeof stat.size !== 'number' || typeof stat.mtime !== 'number') {
1572 return undefined;
1575 return stat.mtime.toString(29) + stat.size.toString(31);
1576 }
1577 > files.ts
1578 export async function whenProviderRegistered(file: URI, fileService: IFileService): Promise<void> {
1579 if (fileService.hasProvider(URI.from({ scheme: file.scheme }))) {
1590 });
1591 }
1592 > files.ts
1593 > /**
1594 > * Helper to format a raw byte size into a human readable label.
1595 > */
1596 > export class ByteSize {
1597 >
1598 > static readonly KB = 1024;
1599 > static readonly MB = ByteSize.KB * ByteSize.KB;
1600 > static readonly GB = ByteSize.MB * ByteSize.KB;
1601 > static readonly TB = ByteSize.GB * ByteSize.KB;
1602 >
1603 > static formatSize(size: number): string {
1604 if (!isNumber(size)) {
1605 size = 0;
1624 return localize('sizeTB', "{0}TB", (size / ByteSize.TB).toFixed(2));
1625 }
1626 > } files.ts
1627 >
1628 > // File limits
1629 >
1630 > export function getLargeFileConfirmationLimit(remoteAuthority?: string): number;
1631 > export function getLargeFileConfirmationLimit(uri?: URI): number;
1632 > export function getLargeFileConfirmationLimit(arg?: string | URI): number {
1633 const isRemote = typeof arg === 'string' || arg?.scheme === Schemas.vscodeRemote;
1634 const isLocal = typeof arg !== 'string' && arg?.scheme === Schemas.file;
1655 return 1024 * ByteSize.MB;
1656 }
1657 > files.ts
1658 > //#endregion
src/vs/base/common/event.ts 925 covered LOC · 121 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- event.ts
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 { CancelablePromise } from './async.js';
7 > import { CancellationToken } from './cancellation.js';
8 > import { diffSets } from './collections.js';
9 > import { onUnexpectedError } from './errors.js';
10 > import { createSingleCallFunction } from './functional.js';
11 > import { combinedDisposable, Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from './lifecycle.js';
12 > import { LinkedList } from './linkedList.js';
13 > import { IObservable, IObservableWithChange, IObserver } from './observable.js';
14 > import { env } from './process.js';
15 > import { StopWatch } from './stopwatch.js';
16 > import { MicrotaskDelay } from './symbols.js';
17 >
18 >
19 > // -----------------------------------------------------------------------------------------------------------------------
20 > // Uncomment the next line to print warnings whenever an emitter with listeners is disposed. That is a sign of code smell.
21 > // -----------------------------------------------------------------------------------------------------------------------
22 > const _enableDisposeWithListenerWarning = false
23 > // || Boolean("TRUE") // causes a linter warning so that it cannot be pushed
24 > ;
25 >
26 >
27 > // -----------------------------------------------------------------------------------------------------------------------
28 > // Uncomment the next line to print warnings whenever a snapshotted event is used repeatedly without cleanup.
29 > // See https://github.com/microsoft/vscode/issues/142851
30 > // -----------------------------------------------------------------------------------------------------------------------
31 > const _enableSnapshotPotentialLeakWarning = false
32 > // || Boolean("TRUE") // causes a linter warning so that it cannot be pushed
33 > ;
34 >
35 >
36 > const _bufferLeakWarnCountThreshold = 100;
37 > const _bufferLeakWarnTimeThreshold = 60_000; // 1 minute
38 >
39 function _isBufferLeakWarningEnabled(): boolean {
40 return !!env['VSCODE_DEV'];
41 }
42 > event.ts
43 > /**
44 > * An event with zero or one parameters that can be subscribed to. The event is a function itself.
45 > */
46 > export interface Event<T> {
47 > (listener: (e: T) => unknown, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
48 > }
49 >
50 > export namespace Event {
51 > export const None: Event<any> = () => Disposable.None;
52 >
53 > function _addLeakageTraceLogic(options: EmitterOptions) {
54 if (_enableSnapshotPotentialLeakWarning) {
55 const { onDidAddListener: origListenerDidAdd } = options;
65 }
66 }
67 > event.ts
68 > /**
69 > * Given an event, returns another event which debounces calls and defers the listeners to a later task via a shared
70 > * `setTimeout`. The event is converted into a signal (`Event<void>`) to avoid additional object creation as a
71 > * result of merging events and to try prevent race conditions that could arise when using related deferred and
72 > * non-deferred events.
73 > *
74 > * This is useful for deferring non-critical work (eg. general UI updates) to ensure it does not block critical work
75 > * (eg. latency of keypress to text rendered).
76 > *
77 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
78 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
79 > * returned event causes this utility to leak a listener on the original event.
80 > *
81 > * @param event The event source for the new event.
82 > * @param flushOnListenerRemove Whether to fire all debounced events when a listener is removed. If this is not
83 > * specified, some events could go missing. Use this if it's important that all events are processed, even if the
84 > * listener gets disposed before the debounced event fires.
85 > * @param disposable A disposable store to add the new EventEmitter to.
86 > */
87 > export function defer(event: Event<unknown>, flushOnListenerRemove?: boolean, disposable?: DisposableStore): Event<void> {
88 return debounce<unknown, void>(event, () => void 0, 0, undefined, flushOnListenerRemove ?? true, undefined, disposable);
89 }
90 > event.ts
91 > /**
92 > * Given an event, returns another event which only fires once.
93 > *
94 > * @param event The event source for the new event.
95 > */
96 > export function once<T>(event: Event<T>): Event<T> {
97 return (listener, thisArgs = null, disposables?) => {
98 // we need this, in case the event fires during the listener call
118 };
119 }
120 > event.ts
121 > /**
122 > * Given an event, returns another event which only fires once, and only when the condition is met.
123 > *
124 > * @param event The event source for the new event.
125 > */
126 > export function onceIf<T>(event: Event<T>, condition: (e: T) => boolean): Event<T> {
127 return Event.once(Event.filter(event, condition));
128 }
129 > event.ts
130 > /**
131 > * Maps an event of one type into an event of another type using a mapping function, similar to how
132 > * `Array.prototype.map` works.
133 > *
134 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
135 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
136 > * returned event causes this utility to leak a listener on the original event.
137 > *
138 > * @param event The event source for the new event.
139 > * @param map The mapping function.
140 > * @param disposable A disposable store to add the new EventEmitter to.
141 > */
142 > export function map<I, O>(event: Event<I>, map: (i: I) => O, disposable?: DisposableStore): Event<O> {
143 return snapshot((listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables), disposable);
144 }
145 > event.ts
146 > /**
147 > * Wraps an event in another event that performs some function on the event object before firing.
148 > *
149 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
150 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
151 > * returned event causes this utility to leak a listener on the original event.
152 > *
153 > * @param event The event source for the new event.
154 > * @param each The function to perform on the event object.
155 > * @param disposable A disposable store to add the new EventEmitter to.
156 > */
157 > export function forEach<I>(event: Event<I>, each: (i: I) => void, disposable?: DisposableStore): Event<I> {
158 return snapshot((listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables), disposable);
159 }
160 > event.ts
161 > /**
162 > * Wraps an event in another event that fires only when some condition is met.
163 > *
164 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
165 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
166 > * returned event causes this utility to leak a listener on the original event.
167 > *
168 > * @param event The event source for the new event.
169 > * @param filter The filter function that defines the condition. The event will fire for the object if this function
170 > * returns true.
171 > * @param disposable A disposable store to add the new EventEmitter to.
172 > */
173 > export function filter<T, U>(event: Event<T | U>, filter: (e: T | U) => e is T, disposable?: DisposableStore): Event<T>;
174 > export function filter<T>(event: Event<T>, filter: (e: T) => boolean, disposable?: DisposableStore): Event<T>;
175 > export function filter<T, R>(event: Event<T | R>, filter: (e: T | R) => e is R, disposable?: DisposableStore): Event<R>;
176 > export function filter<T>(event: Event<T>, filter: (e: T) => boolean, disposable?: DisposableStore): Event<T> {
177 return snapshot((listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables), disposable);
178 }
179 > event.ts
180 > /**
181 > * Given an event, returns the same event but typed as `Event<void>`.
182 > */
183 > export function signal<T>(event: Event<T>): Event<void> {
184 return event as Event<any> as Event<void>;
185 }
186 > event.ts
187 > /**
188 > * Given a collection of events, returns a single event which emits whenever any of the provided events emit.
189 > */
190 > export function any<T>(...events: Event<T>[]): Event<T>;
191 > export function any(...events: Event<any>[]): Event<void>;
192 > export function any<T>(...events: Event<T>[]): Event<T> {
193 return (listener, thisArgs = null, disposables?) => {
194 const disposable = combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e))));
196 };
197 }
198 > event.ts
199 > /**
200 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
201 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
202 > * returned event causes this utility to leak a listener on the original event.
203 > */
204 > export function reduce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, initial?: O, disposable?: DisposableStore): Event<O> {
205 let output: O | undefined = initial;
206
210 }, disposable);
211 }
212 > event.ts
213 > function snapshot<T>(event: Event<T>, disposable: DisposableStore | undefined): Event<T> {
214 let listener: IDisposable | undefined;
215
233 return emitter.event;
234 }
235 > event.ts
236 > /**
237 > * Adds the IDisposable to the store if it's set, and returns it. Useful to
238 > * Event function implementation.
239 > */
240 > function addAndReturnDisposable<T extends IDisposable>(d: T, store: DisposableStore | IDisposable[] | undefined): T {
241 if (store instanceof Array) {
242 store.push(d);
246 return d;
247 }
248 > event.ts
249 > /**
250 > * Given an event, creates a new emitter that event that will debounce events based on {@link delay} and give an
251 > * array event object of all events that fired.
252 > *
253 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
254 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
255 > * returned event causes this utility to leak a listener on the original event.
256 > *
257 > * @param event The original event to debounce.
258 > * @param merge A function that reduces all events into a single event.
259 > * @param delay The number of milliseconds to debounce.
260 > * @param leading Whether to fire a leading event without debouncing.
261 > * @param flushOnListenerRemove Whether to fire all debounced events when a listener is removed. If this is not
262 > * specified, some events could go missing. Use this if it's important that all events are processed, even if the
263 > * listener gets disposed before the debounced event fires.
264 > * @param leakWarningThreshold See {@link EmitterOptions.leakWarningThreshold}.
265 > * @param disposable A disposable store to register the debounce emitter to.
266 > */
267 > export function debounce<T>(event: Event<T>, merge: (last: T | undefined, event: T) => T, delay?: number | typeof MicrotaskDelay, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<T>;
268 > export function debounce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay?: number | typeof MicrotaskDelay, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O>;
269 > export function debounce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay: number | typeof MicrotaskDelay = 100, leading = false, flushOnListenerRemove = false, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O> {
270 let subscription: IDisposable;
271 let output: O | undefined = undefined;
330 return emitter.event;
331 }
332 > event.ts
333 > /**
334 > * Debounces an event, firing after some delay (default=0) with an array of all event original objects.
335 > *
336 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
337 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
338 > * returned event causes this utility to leak a listener on the original event.
339 > *
340 > * @param event The event source for the new event.
341 > * @param delay The number of milliseconds to debounce.
342 > * @param flushOnListenerRemove Whether to fire all debounced events when a listener is removed. If this is not
343 > * specified, some events could go missing. Use this if it's important that all events are processed, even if the
344 > * listener gets disposed before the debounced event fires.
345 > * @param disposable A disposable store to add the new EventEmitter to.
346 > */
347 > export function accumulate<T>(event: Event<T>, delay: number | typeof MicrotaskDelay = 0, flushOnListenerRemove?: boolean, disposable?: DisposableStore): Event<T[]> {
348 return Event.debounce<T, T[]>(event, (last, e) => {
349 if (!last) {
354 }, delay, undefined, flushOnListenerRemove ?? true, undefined, disposable);
355 }
356 > event.ts
357 > /**
358 > * Throttles an event, ensuring the event is fired at most once during the specified delay period.
359 > * Unlike debounce, throttle will fire immediately on the leading edge and/or after the delay on the trailing edge.
360 > *
361 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
362 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
363 > * returned event causes this utility to leak a listener on the original event.
364 > *
365 > * @param event The event source for the new event.
366 > * @param merge An accumulator function that merges events if multiple occur during the throttle period.
367 > * @param delay The number of milliseconds to throttle.
368 > * @param leading Whether to fire on the leading edge (immediately on first event).
369 > * @param trailing Whether to fire on the trailing edge (after delay with the last value).
370 > * @param leakWarningThreshold See {@link EmitterOptions.leakWarningThreshold}.
371 > * @param disposable A disposable store to register the throttle emitter to.
372 > */
373 > export function throttle<T>(event: Event<T>, merge: (last: T | undefined, event: T) => T, delay?: number | typeof MicrotaskDelay, leading?: boolean, trailing?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<T>;
374 > export function throttle<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay?: number | typeof MicrotaskDelay, leading?: boolean, trailing?: boolean, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O>;
375 > export function throttle<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay: number | typeof MicrotaskDelay = 100, leading = true, trailing = true, leakWarningThreshold?: number, disposable?: DisposableStore): Event<O> {
376 let subscription: IDisposable;
377 let output: O | undefined = undefined;
437 return emitter.event;
438 }
439 > event.ts
440 > /**
441 > * Filters an event such that some condition is _not_ met more than once in a row, effectively ensuring duplicate
442 > * event objects from different sources do not fire the same event object.
443 > *
444 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
445 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
446 > * returned event causes this utility to leak a listener on the original event.
447 > *
448 > * @param event The event source for the new event.
449 > * @param equals The equality condition.
450 > * @param disposable A disposable store to add the new EventEmitter to.
451 > *
452 > * @example
453 > * ```
454 > * // Fire only one time when a single window is opened or focused
455 > * Event.latch(Event.any(onDidOpenWindow, onDidFocusWindow))
456 > * ```
457 > */
458 > export function latch<T>(event: Event<T>, equals: (a: T, b: T) => boolean = (a, b) => a === b, disposable?: DisposableStore): Event<T> {
459 let firstCall = true;
460 let cache: T;
467 }, disposable);
468 }
469 > event.ts
470 > /**
471 > * Splits an event whose parameter is a union type into 2 separate events for each type in the union.
472 > *
473 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
474 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
475 > * returned event causes this utility to leak a listener on the original event.
476 > *
477 > * @example
478 > * ```
479 > * const event = new EventEmitter<number | undefined>().event;
480 > * const [numberEvent, undefinedEvent] = Event.split(event, isUndefined);
481 > * ```
482 > *
483 > * @param event The event source for the new event.
484 > * @param isT A function that determines what event is of the first type.
485 > * @param disposable A disposable store to add the new EventEmitter to.
486 > */
487 > export function split<T, U>(event: Event<T | U>, isT: (e: T | U) => e is T, disposable?: DisposableStore): [Event<T>, Event<U>] {
488 return [
489 Event.filter(event, isT, disposable),
491 ];
492 }
493 > event.ts
494 > /**
495 > * Buffers an event until it has a listener attached.
496 > *
497 > * *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
498 > * event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
499 > * returned event causes this utility to leak a listener on the original event.
500 > *
501 > * @param event The event source for the new event.
502 > * @param debugName A name for this buffer, used in leak detection warnings.
503 > * @param flushAfterTimeout Determines whether to flush the buffer after a timeout immediately or after a
504 > * `setTimeout` when the first event listener is added.
505 > * @param _buffer Internal: A source event array used for tests.
506 > *
507 > * @example
508 > * ```
509 > * // Start accumulating events, when the first listener is attached, flush
510 > * // the event after a timeout such that multiple listeners attached before
511 > * // the timeout would receive the event
512 > * this.onInstallExtension = Event.buffer(service.onInstallExtension, 'onInstallExtension', true);
513 > * ```
514 > */
515 > export function buffer<T>(event: Event<T>, debugName: string, flushAfterTimeout = false, _buffer: T[] = [], disposable?: DisposableStore): Event<T> {
516 let buffer: T[] | null = _buffer.slice();
517
600 return emitter.event;
601 }
602 > /** event.ts
603 > * Wraps the event in an {@link IChainableEvent}, allowing a more functional programming style.
604 > *
605 > * @example
606 > * ```
607 > * // Normal
608 > * const onEnterPressNormal = Event.filter(
609 > * Event.map(onKeyPress.event, e => new StandardKeyboardEvent(e)),
610 > * e.keyCode === KeyCode.Enter
611 > * ).event;
612 > *
613 > * // Using chain
614 > * const onEnterPressChain = Event.chain(onKeyPress.event, $ => $
615 > * .map(e => new StandardKeyboardEvent(e))
616 > * .filter(e => e.keyCode === KeyCode.Enter)
617 > * );
618 > * ```
619 > */
620 > export function chain<T, R>(event: Event<T>, sythensize: ($: IChainableSythensis<T>) => IChainableSythensis<R>): Event<R> {
621 const fn: Event<R> = (listener, thisArgs, disposables) => {
622 const cs = sythensize(new ChainableSynthesis()) as ChainableSynthesis;
631 return fn;
632 }
633 > event.ts
634 > const HaltChainable = Symbol('HaltChainable');
635 >
636 > class ChainableSynthesis implements IChainableSythensis<any> {
637 private readonly steps: ((input: any) => unknown)[] = [];
638 > event.ts
639 > map<O>(fn: (i: any) => O): this {
640 this.steps.push(fn);
641 return this;
642 }
643 > event.ts
644 > forEach(fn: (i: any) => void): this {
645 this.steps.push(v => {
646 fn(v);
649 return this;
650 }
651 > event.ts
652 > filter(fn: (e: any) => boolean): this {
653 this.steps.push(v => fn(v) ? v : HaltChainable);
654 return this;
655 }
656 > event.ts
657 > reduce<R>(merge: (last: R | undefined, event: any) => R, initial?: R | undefined): this {
658 let last = initial;
659 this.steps.push(v => {
663 return this;
664 }
665 > event.ts
666 > latch(equals: (a: any, b: any) => boolean = (a, b) => a === b): ChainableSynthesis {
667 let firstCall = true;
668 let cache: any;
676 return this;
677 }
678 > event.ts
679 > public evaluate(value: any) {
680 for (const step of this.steps) {
681 value = step(value);
687 return value;
688 }
689 > } event.ts
690 >
691 > export interface IChainableSythensis<T> {
692 > map<O>(fn: (i: T) => O): IChainableSythensis<O>;
693 > forEach(fn: (i: T) => void): IChainableSythensis<T>;
694 > filter<R extends T>(fn: (e: T) => e is R): IChainableSythensis<R>;
695 > filter(fn: (e: T) => boolean): IChainableSythensis<T>;
696 > reduce<R>(merge: (last: R, event: T) => R, initial: R): IChainableSythensis<R>;
697 > reduce<R>(merge: (last: R | undefined, event: T) => R): IChainableSythensis<R>;
698 > latch(equals?: (a: T, b: T) => boolean): IChainableSythensis<T>;
699 > }
700 >
701 > export interface NodeEventEmitter {
702 > on(event: string | symbol, listener: Function): unknown;
703 > removeListener(event: string | symbol, listener: Function): unknown;
704 > }
705 >
706 > /**
707 > * Creates an {@link Event} from a node event emitter.
708 > */
709 > export function fromNodeEventEmitter<T>(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
710 const fn = (...args: unknown[]) => result.fire(map(...args));
711 const onFirstListenerAdd = () => emitter.on(eventName, fn);
715 return result.event;
716 }
717 > event.ts
718 > export interface DOMEventEmitter {
719 > addEventListener(event: string | symbol, listener: Function): void;
720 > removeEventListener(event: string | symbol, listener: Function): void;
721 > }
722 >
723 > /**
724 > * Creates an {@link Event} from a DOM event emitter.
725 > */
726 > export function fromDOMEventEmitter<T>(emitter: DOMEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
727 const fn = (...args: unknown[]) => result.fire(map(...args));
728 const onFirstListenerAdd = () => emitter.addEventListener(eventName, fn);
732 return result.event;
733 }
734 > event.ts
735 > /**
736 > * Creates a promise out of an event, using the {@link Event.once} helper.
737 > */
738 > export function toPromise<T>(event: Event<T>, disposables?: IDisposable[] | DisposableStore): CancelablePromise<T> {
739 let cancelRef: () => void;
740 let listener: IDisposable;
756 return promise;
757 }
758 > event.ts
759 > /**
760 > * A convenience function for forwarding an event to another emitter which
761 > * improves readability.
762 > *
763 > * This is similar to {@link Relay} but allows instantiating and forwarding
764 > * on a single line and also allows for multiple source events.
765 > * @param from The event to forward.
766 > * @param to The emitter to forward the event to.
767 > * @example
768 > * Event.forward(event, emitter);
769 > * // equivalent to
770 > * event(e => emitter.fire(e));
771 > * // equivalent to
772 > * event(emitter.fire, emitter);
773 > */
774 > export function forward<T>(from: Event<T>, to: Emitter<T>): IDisposable {
775 return from(e => to.fire(e));
776 }
777 > event.ts
778 > /**
779 > * Adds a listener to an event and calls the listener immediately with undefined as the event object.
780 > *
781 > * @example
782 > * ```
783 > * // Initialize the UI and update it when dataChangeEvent fires
784 > * runAndSubscribe(dataChangeEvent, () => this._updateUI());
785 > * ```
786 > */
787 > export function runAndSubscribe<T>(event: Event<T>, handler: (e: T) => unknown, initial: T): IDisposable;
788 > export function runAndSubscribe<T>(event: Event<T>, handler: (e: T | undefined) => unknown): IDisposable;
789 > export function runAndSubscribe<T>(event: Event<T>, handler: (e: T | undefined) => unknown, initial?: T): IDisposable {
790 handler(initial);
791 return event(e => handler(e));
792 }
793 > event.ts
794 > class EmitterObserver<T> implements IObserver {
795 >
796 > readonly emitter: Emitter<T>;
797 >
798 > private _counter = 0;
799 > private _hasChanged = false;
800 >
801 > constructor(readonly _observable: IObservable<T>, store: DisposableStore | undefined) {
802 const options: EmitterOptions = {
803 onWillAddFirstListener: () => {
819 }
820 }
821 > event.ts
822 > beginUpdate<T>(_observable: IObservable<T>): void {
823 // assert(_observable === this.obs);
824 this._counter++;
825 }
826 > event.ts
827 > handlePossibleChange<T>(_observable: IObservable<T>): void {
828 // assert(_observable === this.obs);
829 }
830 > event.ts
831 > handleChange<T, TChange>(_observable: IObservableWithChange<T, TChange>, _change: TChange): void {
832 // assert(_observable === this.obs);
833 this._hasChanged = true;
834 }
835 > event.ts
836 > endUpdate<T>(_observable: IObservable<T>): void {
837 // assert(_observable === this.obs);
838 this._counter--;
845 }
846 }
847 > } event.ts
848 >
849 > /**
850 > * Creates an event emitter that is fired when the observable changes.
851 > * Each listeners subscribes to the emitter.
852 > */
853 > export function fromObservable<T>(obs: IObservable<T>, store?: DisposableStore): Event<T> {
854 const observer = new EmitterObserver(obs, store);
855 return observer.emitter.event;
856 }
857 > event.ts
858 > /**
859 > * Each listener is attached to the observable directly.
860 > */
861 > export function fromObservableLight(observable: IObservable<unknown>): Event<void> {
862 return (listener, thisArgs, disposables) => {
863 let count = 0;
897 };
898 }
899 > } event.ts
900 >
901 > export interface EmitterOptions {
902 > /**
903 > * Optional function that's called *before* the very first listener is added
904 > */
905 > onWillAddFirstListener?: Function;
906 > /**
907 > * Optional function that's called *after* the very first listener is added
908 > */
909 > onDidAddFirstListener?: Function;
910 > /**
911 > * Optional function that's called after a listener is added
912 > */
913 > onDidAddListener?: Function;
914 > /**
915 > * Optional function that's called *after* remove the very last listener
916 > */
917 > onDidRemoveLastListener?: Function;
918 > /**
919 > * Optional function that's called *before* a listener is removed
920 > */
921 > onWillRemoveListener?: Function;
922 > /**
923 > * Optional function that's called when a listener throws an error. Defaults to
924 > * {@link onUnexpectedError}
925 > */
926 > onListenerError?: (e: any) => void;
927 > /**
928 > * Number of listeners that are allowed before assuming a leak. Default to
929 > * a globally configured value
930 > *
931 > * @see setGlobalLeakWarningThreshold
932 > */
933 > leakWarningThreshold?: number;
934 > /**
935 > * Human-readable name for the emitter, included in leak warning error
936 > * messages to help identify which emitter is leaking in telemetry.
937 > */
938 > leakWarningName?: string;
939 > /**
940 > * Pass in a delivery queue, which is useful for ensuring
941 > * in order event delivery across multiple emitters.
942 > */
943 > deliveryQueue?: EventDeliveryQueue;
944 >
945 > /** ONLY enable this during development */
946 > _profName?: string;
947 > }
948 >
949 >
950 > export class EventProfiling {
951 >
952 > static readonly all = new Set<EventProfiling>();
953 >
954 > private static _idPool = 0;
955 >
956 > readonly name: string;
957 > public listenerCount: number = 0;
958 > public invocationCount = 0;
959 > public elapsedOverall = 0;
960 > public durations: number[] = [];
961 >
962 > private _stopWatch?: StopWatch;
963 >
964 > constructor(name: string) {
965 this.name = `${name}_${EventProfiling._idPool++}`;
966 EventProfiling.all.add(this);
967 }
968 > event.ts
969 > start(listenerCount: number): void {
970 this._stopWatch = new StopWatch();
971 this.listenerCount = listenerCount;
972 }
973 > event.ts
974 > stop(): void {
975 if (this._stopWatch) {
976 const elapsed = this._stopWatch.elapsed();
981 }
982 }
983 > } event.ts
984 >
985 > let _globalLeakWarningThreshold = -1;
986 > export function setGlobalLeakWarningThreshold(n: number): IDisposable {
987 const oldValue = _globalLeakWarningThreshold;
988 _globalLeakWarningThreshold = n;
993 };
994 }
995 > event.ts
996 > class LeakageMonitor {
997 >
998 > private static _idPool = 1;
999 >
1000 > private _stacks: Map<string, number> | undefined;
1001 > private _warnCountdown: number = 0;
1002 >
1003 > constructor(
1004 private readonly _errorHandler: (err: Error) => void,
1005 readonly threshold: number,
1006 readonly name: string = (LeakageMonitor._idPool++).toString(16).padStart(3, '0')
1007 ) { }
1008 > event.ts
1009 > dispose(): void {
1010 this._stacks?.clear();
1011 }
1012 > event.ts
1013 > check(stack: Stacktrace, listenerCount: number): undefined | (() => void) {
1014
1015 const threshold = this.threshold;
1046 };
1047 }
1048 > event.ts
1049 > getMostFrequentStack(): [string, number] | undefined {
1050 if (!this._stacks) {
1051 return undefined;
1061 return topStack;
1062 }
1063 > } event.ts
1064 >
1065 > class Stacktrace {
1066 >
1067 > static create() {
1068 > const err = new Error();
1069 > return new Stacktrace(err.stack ?? '');
1070 > }
1071 >
1072 > private constructor(readonly value: string) { }
1073 >
1074 > print() {
1075 console.warn(this.value.split('\n').slice(2).join('\n'));
1076 }
1077 > } event.ts
1078 >
1079 > // error that is logged when going over the configured listener threshold
1080 > export class ListenerLeakError extends Error {
1081 > readonly kind: string;
1082 > readonly listenerCount: number;
1083 > /**
1084 > * The detailed message including listener count and most frequent stack.
1085 > * Available locally for debugging but intentionally not used as the error
1086 > * `message`. When `emitterName` is provided, errors group by emitter name
1087 > * and kind in telemetry; otherwise they group by kind alone.
1088 > */
1089 > readonly details: string;
1090 > constructor(kind: 'dominated' | 'popular', details: string, stack: string, listenerCount: number, emitterName?: string) {
1091 super(emitterName
1092 ? `[${emitterName}] potential listener LEAK detected, ${kind}`
1098 this.stack = stack;
1099 }
1100 > event.ts
1101 > static is(err: unknown): err is ListenerLeakError {
1102 return err instanceof ListenerLeakError
1103 || (err instanceof Error && typeof (err as Error & { kind: unknown; listenerCount: unknown }).kind === 'string' && typeof (err as Error & { kind: unknown; listenerCount: unknown }).listenerCount === 'number');
1104 }
1105 > } event.ts
1106 >
1107 > // SEVERE error that is logged when having gone way over the configured listener
1108 > // threshold so that the emitter refuses to accept more listeners
1109 > export class ListenerRefusalError extends ListenerLeakError {
1110 > constructor(kind: 'dominated' | 'popular', details: string, stack: string, listenerCount: number, emitterName?: string) {
1111 super(kind, details, stack, listenerCount, emitterName);
1112 this.name = 'ListenerRefusalError';
1113 }
1114 > } event.ts
1115 >
1116 > let id = 0;
1117 > class UniqueContainer<T> {
1118 > stack?: Stacktrace;
1119 > public id = id++;
1120 > constructor(public readonly value: T) { }
1121 > }
1122 > const compactionThreshold = 2;
1123 >
1124 > type ListenerContainer<T> = UniqueContainer<(data: T) => void>;
1125 > type ListenerOrListeners<T> = (ListenerContainer<T> | undefined)[] | ListenerContainer<T>;
1126 >
1127 > const forEachListener = <T>(listeners: ListenerOrListeners<T>, fn: (c: ListenerContainer<T>) => void) => {
1128 if (listeners instanceof UniqueContainer) {
1129 fn(listeners);
1137 }
1138 };
1139 > event.ts
1140 > /**
1141 > * The Emitter can be used to expose an Event to the public
1142 > * to fire it from the insides.
1143 > * Sample:
1144 > class Document {
1145 >
1146 > private readonly _onDidChange = new Emitter<(value:string)=>any>();
1147 >
1148 > public onDidChange = this._onDidChange.event;
1149 >
1150 > // getter-style
1151 > // get onDidChange(): Event<(value:string)=>any> {
1152 > // return this._onDidChange.event;
1153 > // }
1154 >
1155 > private _doIt() {
1156 > //...
1157 > this._onDidChange.fire(value);
1158 > }
1159 > }
1160 > */
1161 > export class Emitter<T> {
1162 >
1163 > private readonly _options?: EmitterOptions;
1164 > private readonly _leakageMon?: LeakageMonitor;
1165 > private readonly _perfMon?: EventProfiling;
1166 > private _disposed?: true;
1167 > private _event?: Event<T>;
1168 >
1169 > /**
1170 > * A listener, or list of listeners. A single listener is the most common
1171 > * for event emitters (#185789), so we optimize that special case to avoid
1172 > * wrapping it in an array (just like Node.js itself.)
1173 > *
1174 > * A list of listeners never 'downgrades' back to a plain function if
1175 > * listeners are removed, for two reasons:
1176 > *
1177 > * 1. That's complicated (especially with the deliveryQueue)
1178 > * 2. A listener with >1 listener is likely to have >1 listener again at
1179 > * some point, and swapping between arrays and functions may[citation needed]
1180 > * introduce unnecessary work and garbage.
1181 > *
1182 > * The array listeners can be 'sparse', to avoid reallocating the array
1183 > * whenever any listener is added or removed. If more than `1 / compactionThreshold`
1184 > * of the array is empty, only then is it resized.
1185 > */
1186 > protected _listeners?: ListenerOrListeners<T>;
1187 >
1188 > /**
1189 > * Always to be defined if _listeners is an array. It's no longer a true
1190 > * queue, but holds the dispatching 'state'. If `fire()` is called on an
1191 > * emitter, any work left in the _deliveryQueue is finished first.
1192 > */
1193 > private _deliveryQueue?: EventDeliveryQueuePrivate;
1194 > protected _size = 0;
1195 >
1196 > constructor(options?: EmitterOptions) {
1197 > this._options = options; event.ts
1198 > this._leakageMon = (_globalLeakWarningThreshold > 0 || this._options?.leakWarningThreshold)
1199 ? new LeakageMonitor(options?.onListenerError ?? onUnexpectedError, this._options?.leakWarningThreshold ?? _globalLeakWarningThreshold, this._options?.leakWarningName) :
1200 > undefined; event.ts
1201 > this._perfMon = this._options?._profName ? new EventProfiling(this._options._profName) : undefined;
1202 > this._deliveryQueue = this._options?.deliveryQueue as EventDeliveryQueuePrivate | undefined;
1203 > }
1204 > event.ts
1205 > dispose() {
1206 > if (!this._disposed) { event.ts
1207 > this._disposed = true;
1208 >
1209 > // It is bad to have listeners at the time of disposing an emitter, it is worst to have listeners keep the emitter
1210 > // alive via the reference that's embedded in their disposables. Therefore we loop over all remaining listeners and
1211 > // unset their subscriptions/disposables. Looping and blaming remaining listeners is done on next tick because the
1212 > // the following programming pattern is very popular:
1213 > //
1214 > // const someModel = this._disposables.add(new ModelObject()); // (1) create and register model
1215 > // this._disposables.add(someModel.onDidChange(() => { ... }); // (2) subscribe and register model-event listener
1216 > // ...later...
1217 > // this._disposables.dispose(); disposes (1) then (2): don't warn after (1) but after the "overall dispose" is done
1218 >
1219 > if (this._deliveryQueue?.current === this) {
1220 this._deliveryQueue.reset();
1221 }
1222 > if (this._listeners) { event.ts
1223 if (_enableDisposeWithListenerWarning) {
1224 const listeners = this._listeners;
1231 this._size = 0;
1232 }
1233 > this._options?.onDidRemoveLastListener?.(); event.ts
1234 > this._leakageMon?.dispose();
1235 > }
1236 > }
1237 > event.ts
1238 > /**
1239 > * For the public to allow to subscribe
1240 > * to events from this Emitter
1241 > */
1242 > get event(): Event<T> {
1243 > this._event ??= (callback: (e: T) => unknown, thisArgs?: any, disposables?: IDisposable[] | DisposableStore) => { event.ts
1244 > if (this._leakageMon && this._size > this._leakageMon.threshold ** 2) { event.ts
1245 const message = `[${this._leakageMon.name}] REFUSES to accept new listeners because it exceeded its threshold by far (${this._size} vs ${this._leakageMon.threshold})`;
1246 console.warn(message);
1254 return Disposable.None;
1255 }
1256 > event.ts
1257 > if (this._disposed) {
1258 // todo: should we warn if a listener is added to a disposed emitter? This happens often
1259 return Disposable.None;
1260 }
1261 > event.ts
1262 > if (thisArgs) {
1263 callback = callback.bind(thisArgs);
1264 }
1265 > event.ts
1266 > const contained = new UniqueContainer(callback);
1267 >
1268 > let removeMonitor: Function | undefined;
1269 > let stack: Stacktrace | undefined;
1270 > if (this._leakageMon && this._size >= Math.ceil(this._leakageMon.threshold * 0.2)) {
1271 // check and record this emitter for potential leakage
1272 contained.stack = Stacktrace.create();
1273 removeMonitor = this._leakageMon.check(contained.stack, this._size + 1);
1274 }
1275 > event.ts
1276 > if (_enableDisposeWithListenerWarning) {
1277 contained.stack = stack ?? Stacktrace.create();
1278 }
1279 > event.ts
1280 > if (!this._listeners) {
1281 > this._options?.onWillAddFirstListener?.(this);
1282 > this._listeners = contained;
1283 > this._options?.onDidAddFirstListener?.(this);
1284 > } else if (this._listeners instanceof UniqueContainer) {
1285 this._deliveryQueue ??= new EventDeliveryQueuePrivate();
1286 this._listeners = [this._listeners, contained];
1288 this._listeners.push(contained);
1289 }
1290 > this._options?.onDidAddListener?.(this); event.ts
1291 >
1292 > this._size++;
1293 >
1294 >
1295 > const result = toDisposable(() => {
1296 > removeMonitor?.(); event.ts
1297 > this._removeListener(contained);
1298 > }); event.ts
1299 > addToDisposables(result, disposables);
1300 >
1301 > return result;
1302 > };
1303 > event.ts
1304 > return this._event;
1305 > }
1306 > event.ts
1307 > private _removeListener(listener: ListenerContainer<T>) {
1308 > this._options?.onWillRemoveListener?.(this); event.ts
1309 >
1310 > if (!this._listeners) {
1311 return; // expected if a listener gets disposed
1312 }
1313 > event.ts
1314 > if (this._size === 1) {
1315 > this._listeners = undefined; event.ts
1316 > this._options?.onDidRemoveLastListener?.(this);
1317 > this._size = 0;
1318 > return;
1319 > }
1320
1321 // size > 1 which requires that listeners be a list:
1348 listeners.length = n;
1349 }
1350 > } event.ts
1351 > event.ts
1352 > private _deliver(listener: undefined | UniqueContainer<(value: T) => void>, value: T) {
1353 if (!listener) {
1354 return;
1367 }
1368 }
1369 > event.ts
1370 > /** Delivers items in the queue. Assumes the queue is ready to go. */
1371 > private _deliverQueue(dq: EventDeliveryQueuePrivate) {
1372 const listeners = dq.current!._listeners! as (ListenerContainer<T> | undefined)[];
1373 while (dq.i < dq.end) {
1377 dq.reset();
1378 }
1379 > event.ts
1380 > /**
1381 > * To be kept private to fire an event to
1382 > * subscribers
1383 > */
1384 > fire(event: T): void {
1385 > if (this._deliveryQueue?.current) { event.ts
1386 this._deliverQueue(this._deliveryQueue);
1387 this._perfMon?.stop(); // last fire() will have starting perfmon, stop it before starting the next dispatch
1388 }
1389 > event.ts
1390 > this._perfMon?.start(this._size);
1391 >
1392 > if (!this._listeners) {
1393 > // no-op event.ts
1394 > } else if (this._listeners instanceof UniqueContainer) { event.ts
1395 this._deliver(this._listeners, event);
1396 } else {
1399 this._deliverQueue(dq);
1400 }
1401 > event.ts
1402 > this._perfMon?.stop();
1403 > }
1404 > event.ts
1405 > hasListeners(): boolean {
1406 return this._size > 0;
1407 }
1408 > } event.ts
1409 >
1410 > export interface EventDeliveryQueue {
1411 > _isEventDeliveryQueue: true;
1412 > }
1413 >
1414 > export const createEventDeliveryQueue = (): EventDeliveryQueue => new EventDeliveryQueuePrivate();
1415 >
1416 class EventDeliveryQueuePrivate implements EventDeliveryQueue {
1417 declare _isEventDeliveryQueue: true;
1426 */
1427 public end = 0;
1428 > event.ts
1429 > /**
1430 > * Emitter currently being dispatched on. Emitter._listeners is always an array.
1431 > */
1432 > public current?: Emitter<any>;
1433 > /**
1434 > * Currently emitting value. Defined whenever `current` is.
1435 > */
1436 > public value?: unknown;
1437 >
1438 > public enqueue<T>(emitter: Emitter<T>, value: T, end: number) {
1439 this.i = 0;
1440 this.end = end;
1442 this.value = value;
1443 }
1444 > event.ts
1445 > public reset() {
1446 this.i = this.end; // force any current emission loop to stop, mainly for during dispose
1447 this.current = undefined;
1448 this.value = undefined;
1449 }
1450 > } event.ts
1451 >
1452 > export interface IWaitUntil {
1453 > token: CancellationToken;
1454 > waitUntil(thenable: Promise<unknown>): void;
1455 > }
1456 >
1457 > export type IWaitUntilData<T> = Omit<Omit<T, 'waitUntil'>, 'token'>;
1458 >
1459 > export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
1460 >
1461 > private _asyncDeliveryQueue?: LinkedList<[(ev: T) => void, IWaitUntilData<T>]>;
1462 >
1463 > async fireAsync(data: IWaitUntilData<T>, token: CancellationToken, promiseJoin?: (p: Promise<unknown>, listener: Function) => Promise<unknown>): Promise<void> {
1464 if (!this._listeners) {
1465 return;
1512 }
1513 }
1514 > } event.ts
1515 >
1516 >
1517 > export class PauseableEmitter<T> extends Emitter<T> {
1518 >
1519 > private _isPaused = 0;
1520 > protected _eventQueue = new LinkedList<T>();
1521 > private _mergeFn?: (input: T[]) => T;
1522 >
1523 > public get isPaused(): boolean {
1524 > return this._isPaused !== 0;
1525 > }
1526 >
1527 > constructor(options?: EmitterOptions & { merge?: (input: T[]) => T }) {
1528 super(options);
1529 this._mergeFn = options?.merge;
1530 }
1531 > event.ts
1532 > pause(): void {
1533 this._isPaused++;
1534 }
1535 > event.ts
1536 > resume(): void {
1537 if (this._isPaused !== 0 && --this._isPaused === 0) {
1538 if (this._mergeFn) {
1554 }
1555 }
1556 > event.ts
1557 > override fire(event: T): void {
1558 if (this._size) {
1559 if (this._isPaused !== 0) {
1564 }
1565 }
1566 > } event.ts
1567 >
1568 > export class DebounceEmitter<T> extends PauseableEmitter<T> {
1569 >
1570 > private readonly _delay: number;
1571 > private _handle: Timeout | undefined;
1572 >
1573 > constructor(options: EmitterOptions & { merge: (input: T[]) => T; delay?: number }) {
1574 super(options);
1575 this._delay = options.delay ?? 100;
1576 }
1577 > event.ts
1578 > override fire(event: T): void {
1579 if (!this._handle) {
1580 this.pause();
1586 super.fire(event);
1587 }
1588 > } event.ts
1589 >
1590 > /**
1591 > * An emitter which queue all events and then process them at the
1592 > * end of the event loop.
1593 > */
1594 > export class MicrotaskEmitter<T> extends Emitter<T> {
1595 > private _queuedEvents: T[] = [];
1596 > private _mergeFn?: (input: T[]) => T;
1597 >
1598 > constructor(options?: EmitterOptions & { merge?: (input: T[]) => T }) {
1599 super(options);
1600 this._mergeFn = options?.merge;
1601 }
1602 > override fire(event: T): void { event.ts
1603
1604 if (!this.hasListeners()) {
1618 }
1619 }
1620 > } event.ts
1621 >
1622 > /**
1623 > * An event emitter that multiplexes many events into a single event.
1624 > *
1625 > * @example Listen to the `onData` event of all `Thing`s, dynamically adding and removing `Thing`s
1626 > * to the multiplexer as needed.
1627 > *
1628 > * ```typescript
1629 > * const anythingDataMultiplexer = new EventMultiplexer<{ data: string }>();
1630 > *
1631 > * const thingListeners = DisposableMap<Thing, IDisposable>();
1632 > *
1633 > * thingService.onDidAddThing(thing => {
1634 > * thingListeners.set(thing, anythingDataMultiplexer.add(thing.onData);
1635 > * });
1636 > * thingService.onDidRemoveThing(thing => {
1637 > * thingListeners.deleteAndDispose(thing);
1638 > * });
1639 > *
1640 > * anythingDataMultiplexer.event(e => {
1641 > * console.log('Something fired data ' + e.data)
1642 > * });
1643 > * ```
1644 > */
1645 > export class EventMultiplexer<T> implements IDisposable {
1646 >
1647 > private readonly emitter: Emitter<T>;
1648 > private hasListeners = false;
1649 > private events: { event: Event<T>; listener: IDisposable | null }[] = [];
1650 >
1651 > constructor() {
1652 this.emitter = new Emitter<T>({
1653 onWillAddFirstListener: () => this.onFirstListenerAdd(),
1655 });
1656 }
1657 > event.ts
1658 > get event(): Event<T> {
1659 return this.emitter.event;
1660 }
1661 > event.ts
1662 > add(event: Event<T>): IDisposable {
1663 const e = { event: event, listener: null };
1664 this.events.push(e);
1679 return toDisposable(createSingleCallFunction(dispose));
1680 }
1681 > event.ts
1682 > private onFirstListenerAdd(): void {
1683 this.hasListeners = true;
1684 this.events.forEach(e => this.hook(e));
1685 }
1686 > event.ts
1687 > private onLastListenerRemove(): void {
1688 this.hasListeners = false;
1689 this.events.forEach(e => this.unhook(e));
1690 }
1691 > event.ts
1692 > private hook(e: { event: Event<T>; listener: IDisposable | null }): void {
1693 e.listener = e.event(r => this.emitter.fire(r));
1694 }
1695 > event.ts
1696 > private unhook(e: { event: Event<T>; listener: IDisposable | null }): void {
1697 e.listener?.dispose();
1698 e.listener = null;
1699 }
1700 > event.ts
1701 > dispose(): void {
1702 this.emitter.dispose();
1703
1707 this.events = [];
1708 }
1709 > } event.ts
1710 >
1711 > export interface IDynamicListEventMultiplexer<TEventType> extends IDisposable {
1712 > readonly event: Event<TEventType>;
1713 > }
1714 > export class DynamicListEventMultiplexer<TItem, TEventType> implements IDynamicListEventMultiplexer<TEventType> {
1715 > private readonly _store = new DisposableStore();
1716 >
1717 > readonly event: Event<TEventType>;
1718 >
1719 > constructor(
1720 items: TItem[],
1721 onAddItem: Event<TItem>,
1747 this.event = multiplexer.event;
1748 }
1749 > event.ts
1750 > dispose() {
1751 this._store.dispose();
1752 }
1753 > } event.ts
1754 >
1755 > /**
1756 > * The EventBufferer is useful in situations in which you want
1757 > * to delay firing your events during some code.
1758 > * You can wrap that code and be sure that the event will not
1759 > * be fired during that wrap.
1760 > *
1761 > * ```
1762 > * const emitter: Emitter;
1763 > * const delayer = new EventDelayer();
1764 > * const delayedEvent = delayer.wrapEvent(emitter.event);
1765 > *
1766 > * delayedEvent(console.log);
1767 > *
1768 > * delayer.bufferEvents(() => {
1769 > * emitter.fire(); // event will not be fired yet
1770 > * });
1771 > *
1772 > * // event will only be fired at this point
1773 > * ```
1774 > */
1775 > export class EventBufferer {
1776
1777 private data: { buffers: Function[] }[] = [];
1778 > event.ts
1779 > wrapEvent<T>(event: Event<T>): Event<T>;
1780 > wrapEvent<T>(event: Event<T>, reduce: (last: T | undefined, event: T) => T): Event<T>;
1781 > wrapEvent<T, O>(event: Event<T>, reduce: (last: O | undefined, event: T) => O, initial: O): Event<O>;
1782 > wrapEvent<T, O>(event: Event<T>, reduce?: (last: T | O | undefined, event: T) => T | O, initial?: O): Event<O | T> {
1783 return (listener, thisArgs?, disposables?) => {
1784 return event(i => {
1832 };
1833 }
1834 > event.ts
1835 > bufferEvents<R = void>(fn: () => R): R {
1836 const data = { buffers: new Array<Function>() };
1837 this.data.push(data);
1841 return r;
1842 }
1843 > } event.ts
1844 >
1845 > /**
1846 > * A Relay is an event forwarder which functions as a replugabble event pipe.
1847 > * Once created, you can connect an input event to it and it will simply forward
1848 > * events from that input event through its own `event` property. The `input`
1849 > * can be changed at any point in time.
1850 > */
1851 > export class Relay<T> implements IDisposable {
1852
1853 private listening = false;
1867
1868 readonly event: Event<T> = this.emitter.event;
1869 > event.ts
1870 > set input(event: Event<T>) {
1871 this.inputEvent = event;
1872
1876 }
1877 }
1878 > event.ts
1879 > dispose() {
1880 this.inputEventListener.dispose();
1881 this.emitter.dispose();
1882 }
1883 > } event.ts
1884 >
1885 > export interface IValueWithChangeEvent<T> {
1886 > readonly onDidChange: Event<void>;
1887 > get value(): T;
1888 > }
1889 >
1890 > export class ValueWithChangeEvent<T> implements IValueWithChangeEvent<T> {
1891 > public static const<T>(value: T): IValueWithChangeEvent<T> {
1892 > return new ConstValueWithChangeEvent(value);
1893 > }
1894 >
1895 > private readonly _onDidChange = new Emitter<void>();
1896 > readonly onDidChange: Event<void> = this._onDidChange.event;
1897 >
1898 > constructor(private _value: T) { }
1899 >
1900 > get value(): T {
1901 return this._value;
1902 }
1903 > event.ts
1904 > set value(value: T) {
1905 if (value !== this._value) {
1906 this._value = value;
1908 }
1909 }
1910 > } event.ts
1911 >
1912 > class ConstValueWithChangeEvent<T> implements IValueWithChangeEvent<T> {
1913 > public readonly onDidChange: Event<void> = Event.None;
1914 >
1915 > constructor(readonly value: T) { }
1916 > }
1917 >
1918 > /**
1919 > * @param handleItem Is called for each item in the set (but only the first time the item is seen in the set).
1920 > * The returned disposable is disposed if the item is no longer in the set.
1921 > */
1922 > export function trackSetChanges<T>(getData: () => ReadonlySet<T>, onDidChangeData: Event<unknown>, handleItem: (d: T) => IDisposable): IDisposable {
1923 const map = new DisposableMap<T, IDisposable>();
1924 let oldData = new Set(getData());
1942 return store;
1943 }
1944 > event.ts
1945 >
1946 > function addToDisposables(result: IDisposable, disposables: DisposableStore | IDisposable[] | undefined) { event.ts
1947 > if (disposables instanceof DisposableStore) {
1948 disposables.add(result);
1949 > } else if (Array.isArray(disposables)) { event.ts
1950 disposables.push(result);
1951 }
1952 > } event.ts
1953 > event.ts
1954 function disposeAndRemove(result: IDisposable, disposables: DisposableStore | IDisposable[] | undefined) {
1955 if (disposables instanceof DisposableStore) {
src/vs/platform/configuration/common/configurationRegistry.ts 645 covered LOC · 97 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- configurationRegistry.ts
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 { distinct } from '../../../base/common/arrays.js';
7 > import { IStringDictionary } from '../../../base/common/collections.js';
8 > import { Emitter, Event } from '../../../base/common/event.js';
9 > import { IJSONSchema } from '../../../base/common/jsonSchema.js';
10 > import * as types from '../../../base/common/types.js';
11 > import * as nls from '../../../nls.js';
12 > import { getLanguageTagSettingPlainKey } from './configuration.js';
13 > import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../jsonschemas/common/jsonContributionRegistry.js';
14 > import { Registry } from '../../registry/common/platform.js';
15 > import { IPolicy, IPolicyReference, PolicyName } from '../../../base/common/policy.js';
16 > import { Disposable } from '../../../base/common/lifecycle.js';
17 > import product from '../../product/common/product.js';
18 >
19 > export enum EditPresentationTypes {
20 > Multiline = 'multilineText',
21 > Singleline = 'singlelineText'
22 > }
23 >
24 > export const Extensions = {
25 > Configuration: 'base.contributions.configuration'
26 > };
27 >
28 > export interface IConfigurationDelta {
29 > removedDefaults?: IConfigurationDefaults[];
30 > removedConfigurations?: IConfigurationNode[];
31 > addedDefaults?: IConfigurationDefaults[];
32 > addedConfigurations?: IConfigurationNode[];
33 > }
34 >
35 > export interface IConfigurationRegistry {
36 >
37 > /**
38 > * Register a configuration to the registry.
39 > */
40 > registerConfiguration(configuration: IConfigurationNode): IConfigurationNode;
41 >
42 > /**
43 > * Register multiple configurations to the registry.
44 > */
45 > registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void;
46 >
47 > /**
48 > * Deregister multiple configurations from the registry.
49 > */
50 > deregisterConfigurations(configurations: IConfigurationNode[]): void;
51 >
52 > /**
53 > * update the configuration registry by
54 > * - registering the configurations to add
55 > * - dereigstering the configurations to remove
56 > */
57 > updateConfigurations(configurations: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void;
58 >
59 > /**
60 > * Register multiple default configurations to the registry.
61 > */
62 > registerDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;
63 >
64 > /**
65 > * Deregister multiple default configurations from the registry.
66 > */
67 > deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;
68 >
69 > /**
70 > * Bulk update of the configuration registry (default and configurations, remove and add)
71 > * @param delta
72 > */
73 > deltaConfiguration(delta: IConfigurationDelta): void;
74 >
75 > /**
76 > * Return the registered default configurations
77 > */
78 > getRegisteredDefaultConfigurations(): IConfigurationDefaults[];
79 >
80 > /**
81 > * Return the registered configuration defaults overrides
82 > */
83 > getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverrideValue>;
84 >
85 > /**
86 > * Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values.
87 > * Property or default value changes are not allowed.
88 > */
89 > notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]): void;
90 >
91 > /**
92 > * Event that fires whenever a configuration has been
93 > * registered.
94 > */
95 > readonly onDidSchemaChange: Event<void>;
96 >
97 > /**
98 > * Event that fires whenever a configuration has been
99 > * registered.
100 > */
101 > readonly onDidUpdateConfiguration: Event<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>;
102 >
103 > /**
104 > * Returns all configuration nodes contributed to this registry.
105 > */
106 > getConfigurations(): IConfigurationNode[];
107 >
108 > /**
109 > * Returns all configurations settings of all configuration nodes contributed to this registry.
110 > */
111 > getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;
112 >
113 > /**
114 > * Returns the owning setting key per policy name (at most one owner per name).
115 > */
116 > getPolicyConfigurations(): Map<PolicyName, string>;
117 >
118 > /**
119 > * Returns the referencing setting keys per policy name.
120 > */
121 > getPolicyReferenceConfigurations(): Map<PolicyName, Set<string>>;
122 >
123 > /**
124 > * Returns all excluded configurations settings of all configuration nodes contributed to this registry.
125 > */
126 > getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;
127 >
128 > /**
129 > * Register the identifiers for editor configurations
130 > */
131 > registerOverrideIdentifiers(identifiers: string[]): void;
132 > }
133 >
134 > export const enum ConfigurationScope {
135 > /**
136 > * Application specific configuration, which can be configured only in default profile user settings.
137 > */
138 > APPLICATION = 1,
139 > /**
140 > * Machine specific configuration, which can be configured only in local and remote user settings.
141 > */
142 > MACHINE,
143 > /**
144 > * An application machine specific configuration, which can be configured only in default profile user settings and remote user settings.
145 > */
146 > APPLICATION_MACHINE,
147 > /**
148 > * Window specific configuration, which can be configured in the user or workspace settings.
149 > */
150 > WINDOW,
151 > /**
152 > * Resource specific configuration, which can be configured in the user, workspace or folder settings.
153 > */
154 > RESOURCE,
155 > /**
156 > * Resource specific configuration that can be configured in language specific settings
157 > */
158 > LANGUAGE_OVERRIDABLE,
159 > /**
160 > * Machine specific configuration that can also be configured in workspace or folder settings.
161 > */
162 > MACHINE_OVERRIDABLE,
163 > }
164 >
165 >
166 > export interface IConfigurationPropertySchema extends IJSONSchema {
167 >
168 > scope?: ConfigurationScope;
169 >
170 > /**
171 > * When restricted, value of this configuration will be read only from trusted sources.
172 > * For eg., If the workspace is not trusted, then the value of this configuration is not read from workspace settings file.
173 > */
174 > restricted?: boolean;
175 >
176 > /**
177 > * When `false` this property is excluded from the registry. Default is to include.
178 > */
179 > included?: boolean;
180 >
181 > /**
182 > * List of tags associated to the property.
183 > * - A tag can be used for filtering
184 > * - Use `experimental` tag for marking the setting as experimental.
185 > */
186 > tags?: string[];
187 >
188 > /**
189 > * When enabled this setting is ignored during sync and user can override this.
190 > */
191 > ignoreSync?: boolean;
192 >
193 > /**
194 > * When enabled this setting is ignored during sync and user cannot override this.
195 > */
196 > disallowSyncIgnore?: boolean;
197 >
198 > /**
199 > * Disallow extensions to contribute configuration default value for this setting.
200 > */
201 > disallowConfigurationDefault?: boolean;
202 >
203 > /**
204 > * Labels for enumeration items
205 > */
206 > enumItemLabels?: string[];
207 >
208 > /**
209 > * Optional keywords used for search purposes.
210 > */
211 > keywords?: string[];
212 >
213 > /**
214 > * When specified, controls the presentation format of string settings.
215 > * Otherwise, the presentation format defaults to `singleline`.
216 > */
217 > editPresentation?: EditPresentationTypes;
218 >
219 > /**
220 > * When specified, gives an order number for the setting
221 > * within the settings editor. Otherwise, the setting is placed at the end.
222 > */
223 > order?: number;
224 >
225 > /**
226 > * When specified, this setting's value can always be overwritten by
227 > * a system-wide policy. Exactly one setting may *own* a given policy name.
228 > */
229 > policy?: IPolicy;
230 >
231 > /**
232 > * When specified, this setting is governed by a policy owned by another setting.
233 > * A setting must not declare both `policy` and `policyReference`.
234 > * The type must match the owning setting (enforced when exporting the policy catalog).
235 > */
236 > policyReference?: IPolicyReference;
237 >
238 > /**
239 > * When specified, this setting's default value can always be overwritten by
240 > * an experiment.
241 > */
242 > experiment?: {
243 > /**
244 > * The mode of the experiment.
245 > * - `startup`: The setting value is updated to the experiment value only on startup.
246 > * - `auto`: The setting value is updated to the experiment value automatically (whenever the experiment value changes).
247 > */
248 > mode: 'startup' | 'auto';
249 >
250 > /**
251 > * The name of the experiment. By default, this is `config.${settingId}`
252 > */
253 > name?: string;
254 > };
255 >
256 > /**
257 > * When specified, provides configuration overrides for the Agents window.
258 > */
259 > agentsWindow?: {
260 > /**
261 > * Override default value for this setting in the Agents window.
262 > */
263 > default?: unknown;
264 >
265 > /**
266 > * When `true`, this setting is read-only in the Agents window
267 > * and cannot be changed by the user.
268 > */
269 > readOnly?: boolean;
270 > };
271 > }
272 >
273 > export interface IExtensionInfo {
274 > id: string;
275 > displayName?: string;
276 > }
277 >
278 > export interface IConfigurationNode {
279 > id?: string;
280 > order?: number;
281 > type?: string | string[];
282 > title?: string;
283 > description?: string;
284 > properties?: IStringDictionary<IConfigurationPropertySchema>;
285 > allOf?: IConfigurationNode[];
286 > scope?: ConfigurationScope;
287 > extensionInfo?: IExtensionInfo;
288 > restrictedProperties?: string[];
289 > }
290 >
291 > export type ConfigurationDefaultSource = IExtensionInfo | string;
292 >
293 > export function isConfigurationDefaultSourceEquals(a: ConfigurationDefaultSource | undefined, b: ConfigurationDefaultSource | undefined): boolean {
294 if (a === b) {
295 return true;
303 return a.id === b.id;
304 }
306 > export type ConfigurationDefaultValueSource = ConfigurationDefaultSource | Map<string, ConfigurationDefaultSource>;
307 >
308 > export interface IConfigurationDefaults {
309 > overrides: IStringDictionary<unknown>;
310 > source?: ConfigurationDefaultSource;
311 > donotCache?: boolean;
312 > preventExperimentOverride?: boolean;
313 > }
314 >
315 > export type IRegisteredConfigurationPropertySchema = IConfigurationPropertySchema & {
316 > section?: {
317 > id?: string;
318 > title?: string;
319 > order?: number;
320 > extensionInfo?: IExtensionInfo;
321 > };
322 > defaultDefaultValue?: unknown;
323 > source?: ConfigurationDefaultSource; // Source of the Property
324 > defaultValueSource?: ConfigurationDefaultValueSource; // Source of the Default Value
325 > };
326 >
327 > export interface IConfigurationDefaultOverride {
328 > readonly value: unknown;
329 > readonly source?: ConfigurationDefaultSource; // Source of the default override
330 > }
331 >
332 > export interface IConfigurationDefaultOverrideValue {
333 > readonly value: unknown;
334 > readonly source?: ConfigurationDefaultValueSource;
335 > }
336 >
337 > export const allSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
338 > export const applicationSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
339 > export const applicationMachineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
340 > export const machineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
341 > export const machineOverridableSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
342 > export const windowSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
343 > export const resourceSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
344 >
345 > export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage';
346 > export const configurationDefaultsSchemaId = 'vscode://schemas/settings/configurationDefaults';
347 >
348 > const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
349 >
350 > class ConfigurationRegistry extends Disposable implements IConfigurationRegistry {
351 >
352 > private readonly registeredConfigurationDefaults: IConfigurationDefaults[] = [];
353 > private readonly configurationDefaultsOverrides: Map<string, { configurationDefaultOverrides: IConfigurationDefaultOverride[]; configurationDefaultOverrideValue?: IConfigurationDefaultOverrideValue }>;
354 > private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode;
355 > private readonly configurationContributors: IConfigurationNode[];
356 > private readonly configurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;
357 > private readonly policyConfigurations: Map<PolicyName, string>;
358 > private readonly policyReferenceConfigurations: Map<PolicyName, Set<string>>;
359 > private readonly excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;
360 > private readonly resourceLanguageSettingsSchema: IJSONSchema;
361 > private readonly overrideIdentifiers = new Set<string>();
362 >
363 > private readonly _onDidSchemaChange = this._register(new Emitter<void>());
364 > readonly onDidSchemaChange: Event<void> = this._onDidSchemaChange.event;
365 >
366 > private readonly _onDidUpdateConfiguration = this._register(new Emitter<{ properties: ReadonlySet<string>; defaultsOverrides?: boolean }>());
367 > readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event;
368 >
369 > constructor() {
370 > super();
371 > this.configurationDefaultsOverrides = new Map();
372 > this.defaultLanguageConfigurationOverridesNode = {
373 > id: 'defaultOverrides',
374 > title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"),
375 > properties: {}
376 > };
377 > this.configurationContributors = [this.defaultLanguageConfigurationOverridesNode];
378 > this.resourceLanguageSettingsSchema = {
379 > properties: {},
380 > patternProperties: {},
381 > additionalProperties: true,
382 > allowTrailingCommas: true,
383 > allowComments: true
384 > };
385 > this.configurationProperties = {};
386 > this.policyConfigurations = new Map<PolicyName, string>();
387 > this.policyReferenceConfigurations = new Map<PolicyName, Set<string>>();
388 > this.excludedConfigurationProperties = {};
389 >
390 > contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
391 > this.registerOverridePropertyPatternKey();
392 > }
393 >
394 > public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): IConfigurationNode {
395 > this.registerConfigurations([configuration], validate); configurationRegistry.ts
396 > return configuration;
397 > }
399 > public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void {
400 > const properties = new Set<string>(); configurationRegistry.ts
401 > this.doRegisterConfigurations(configurations, validate, properties);
402 >
403 > contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
404 > this._onDidSchemaChange.fire();
405 > this._onDidUpdateConfiguration.fire({ properties });
406 > }
408 > public deregisterConfigurations(configurations: IConfigurationNode[]): void {
409 > const properties = new Set<string>(); configurationRegistry.ts
410 > this.doDeregisterConfigurations(configurations, properties);
411 >
412 > contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
413 > this._onDidSchemaChange.fire();
414 > this._onDidUpdateConfiguration.fire({ properties });
415 > }
417 > public updateConfigurations({ add, remove }: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void {
418 const properties = new Set<string>();
419 this.doDeregisterConfigurations(remove, properties);
424 this._onDidUpdateConfiguration.fire({ properties });
425 }
427 > public registerDefaultConfigurations(configurationDefaults: IConfigurationDefaults[]): void {
428 const properties = new Set<string>();
429 this.doRegisterDefaultConfigurations(configurationDefaults, properties);
431 this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
432 }
434 > private doRegisterDefaultConfigurations(configurationDefaults: IConfigurationDefaults[], bucket: Set<string>) {
435
436 this.registeredConfigurationDefaults.push(...configurationDefaults);
480 this.doRegisterOverrideIdentifiers(overrideIdentifiers);
481 }
483 > public deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void {
484 const properties = new Set<string>();
485 this.doDeregisterDefaultConfigurations(defaultConfigurations, properties);
487 this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
488 }
490 > private doDeregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[], bucket: Set<string>): void {
491 for (const defaultConfiguration of defaultConfigurations) {
492 const index = this.registeredConfigurationDefaults.indexOf(defaultConfiguration);
544 this.updateOverridePropertyPatternKey();
545 }
547 > private updateDefaultOverrideProperty(key: string, newDefaultOverride: IConfigurationDefaultOverrideValue, source: ConfigurationDefaultSource | undefined): void {
548 const property: IRegisteredConfigurationPropertySchema = {
549 section: {
564 this.defaultLanguageConfigurationOverridesNode.properties![key] = property;
565 }
567 > private mergeDefaultConfigurationsForOverrideIdentifier(overrideIdentifier: string, configurationValueObject: IStringDictionary<unknown>, valueSource: ConfigurationDefaultSource | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined {
568 const defaultValue = existingDefaultOverride?.value || {};
569 const source = existingDefaultOverride?.source ?? new Map<string, ConfigurationDefaultSource>();
605 return { value: defaultValue, source };
606 }
608 > private mergeDefaultConfigurationsForConfigurationProperty(propertyKey: string, value: unknown, valuesSource: ConfigurationDefaultSource | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined {
609 const property = this.configurationProperties[propertyKey];
610 const existingDefaultValue = existingDefaultOverride?.value ?? property?.defaultDefaultValue;
637 return { value, source };
638 }
640 > public deltaConfiguration(delta: IConfigurationDelta): void {
641 // defaults: remove
642 let defaultsOverrides = false;
662 this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides });
663 }
665 > public notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]) {
666 this._onDidSchemaChange.fire();
667 }
669 > public registerOverrideIdentifiers(overrideIdentifiers: string[]): void {
670 this.doRegisterOverrideIdentifiers(overrideIdentifiers);
671 this._onDidSchemaChange.fire();
672 }
674 > private doRegisterOverrideIdentifiers(overrideIdentifiers: string[]) {
675 for (const overrideIdentifier of overrideIdentifiers) {
676 this.overrideIdentifiers.add(overrideIdentifier);
678 this.updateOverridePropertyPatternKey();
679 }
681 > private doRegisterConfigurations(configurations: IConfigurationNode[], validate: boolean, bucket: Set<string>): void {
683 > configurations.forEach(configuration => {
684 >
685 > this.validateAndRegisterProperties(configuration, validate, configuration.extensionInfo, configuration.restrictedProperties, undefined, bucket);
686 >
687 > this.configurationContributors.push(configuration);
688 > this.registerJSONConfiguration(configuration);
689 > });
690 > }
692 > private doDeregisterConfigurations(configurations: IConfigurationNode[], bucket: Set<string>): void {
694 > const deregisterConfiguration = (configuration: IConfigurationNode) => {
695 > if (configuration.properties) { configurationRegistry.ts
696 > for (const key in configuration.properties) {
697 > bucket.add(key); configurationRegistry.ts
698 > const property = this.configurationProperties[key];
699 > if (property?.policy?.name) {
700 this.policyConfigurations.delete(property.policy.name);
701 }
702 > if (property?.policyReference?.name) { configurationRegistry.ts
703 const refs = this.policyReferenceConfigurations.get(property.policyReference.name);
704 if (refs) {
709 }
710 }
711 > delete this.configurationProperties[key]; configurationRegistry.ts
712 > this.removeFromSchema(key, configuration.properties[key]);
713 > }
715 > configuration.allOf?.forEach(node => deregisterConfiguration(node));
716 > };
717 > for (const configuration of configurations) { configurationRegistry.ts
718 > deregisterConfiguration(configuration); configurationRegistry.ts
719 > const index = this.configurationContributors.indexOf(configuration);
720 > if (index !== -1) {
721 > this.configurationContributors.splice(index, 1);
722 > }
723 > }
726 > private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, extensionInfo: IExtensionInfo | undefined, restrictedProperties: string[] | undefined, scope: ConfigurationScope = ConfigurationScope.WINDOW, bucket: Set<string>): void {
727 > scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope; configurationRegistry.ts
728 > const properties = configuration.properties;
729 > if (properties) {
730 > for (const key in properties) {
731 > const property: IRegisteredConfigurationPropertySchema = properties[key];
732 > property.section = {
733 > id: configuration.id,
734 > title: configuration.title,
735 > order: configuration.order,
736 > extensionInfo: configuration.extensionInfo
737 > };
738 > if (validate && validateProperty(key, property, extensionInfo?.id)) {
739 delete properties[key];
740 continue;
741 }
743 > property.source = extensionInfo;
744 >
745 > // update default value
746 > property.defaultDefaultValue = properties[key].default;
747 > this.updatePropertyDefaultValue(key, property);
748 >
749 > // update scope
750 > if (OVERRIDE_PROPERTY_REGEX.test(key)) {
751 property.scope = undefined; // No scope for overridable properties `[${identifier}]`
752 > } else { configurationRegistry.ts
753 > property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope;
754 > property.restricted = types.isUndefinedOrNull(property.restricted) ? !!restrictedProperties?.includes(key) : property.restricted;
755 > }
756 >
757 > if (property.experiment) {
758 if (!property.tags?.some(tag => tag.toLowerCase() === 'onexp')) {
759 property.tags = property.tags ?? [];
760 property.tags.push('onExP');
761 }
762 > } else if (property.tags?.some(tag => tag.toLowerCase() === 'onexp')) { configurationRegistry.ts
763 console.error(`Invalid tag 'onExP' found for property '${key}'. Please use 'experiment' property instead.`);
764 property.experiment = { mode: 'startup' };
765 }
767 > const excluded = properties[key].hasOwnProperty('included') && !properties[key].included;
768 > const policyName = properties[key].policy?.name; configurationRegistry.ts
769 > const policyReferenceName = properties[key].policyReference?.name;
770 >
771 > if (excluded) {
772 this.excludedConfigurationProperties[key] = properties[key];
773 if (policyName) {
780 }
781 delete properties[key];
782 > } else { configurationRegistry.ts
783 > bucket.add(key);
784 > if (policyName) {
785 this.policyConfigurations.set(policyName, key);
786 }
787 > if (policyReferenceName) { configurationRegistry.ts
788 this.addPolicyReferenceConfiguration(policyReferenceName, key);
789 }
790 > this.configurationProperties[key] = properties[key]; configurationRegistry.ts
791 > if (!properties[key].deprecationMessage && properties[key].markdownDeprecationMessage) {
792 // If not set, default deprecationMessage to the markdown source
793 properties[key].deprecationMessage = properties[key].markdownDeprecationMessage;
794 }
797 >
798 > }
799 > }
800 > const subNodes = configuration.allOf;
801 > if (subNodes) {
802 for (const node of subNodes) {
803 this.validateAndRegisterProperties(node, validate, extensionInfo, restrictedProperties, scope, bucket);
804 }
805 }
808 > private addPolicyReferenceConfiguration(policyName: PolicyName, key: string): void {
809 let keys = this.policyReferenceConfigurations.get(policyName);
810 if (!keys) {
814 keys.add(key);
815 }
817 > // Only for tests
818 > getConfigurations(): IConfigurationNode[] {
819 return this.configurationContributors;
820 }
822 > getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {
823 > return this.configurationProperties; configurationRegistry.ts
824 > }
826 > getPolicyConfigurations(): Map<PolicyName, string> {
827 return this.policyConfigurations;
828 }
830 > getPolicyReferenceConfigurations(): Map<PolicyName, Set<string>> {
831 return this.policyReferenceConfigurations;
832 }
834 > getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {
835 > return this.excludedConfigurationProperties; configurationRegistry.ts
836 > }
838 > getRegisteredDefaultConfigurations(): IConfigurationDefaults[] {
839 return [...this.registeredConfigurationDefaults];
840 }
842 > getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverrideValue> {
843 const configurationDefaultsOverrides = new Map<string, IConfigurationDefaultOverrideValue>();
844 for (const [key, value] of this.configurationDefaultsOverrides) {
849 return configurationDefaultsOverrides;
850 }
852 > private registerJSONConfiguration(configuration: IConfigurationNode) {
853 > const register = (configuration: IConfigurationNode) => { configurationRegistry.ts
854 > const properties = configuration.properties;
855 > if (properties) {
856 > for (const key in properties) {
857 > this.updateSchema(key, properties[key]); configurationRegistry.ts
858 > }
860 > const subNodes = configuration.allOf;
861 > subNodes?.forEach(register);
862 > };
863 > register(configuration);
864 > }
866 > private updateSchema(key: string, property: IConfigurationPropertySchema): void {
867 > allSettings.properties[key] = property; configurationRegistry.ts
868 > switch (property.scope) {
869 > case ConfigurationScope.APPLICATION:
870 applicationSettings.properties[key] = property;
871 break;
872 > case ConfigurationScope.MACHINE: configurationRegistry.ts
873 machineSettings.properties[key] = property;
874 break;
875 > case ConfigurationScope.APPLICATION_MACHINE: configurationRegistry.ts
876 applicationMachineSettings.properties[key] = property;
877 break;
878 > case ConfigurationScope.MACHINE_OVERRIDABLE: configurationRegistry.ts
879 machineOverridableSettings.properties[key] = property;
880 break;
881 > case ConfigurationScope.WINDOW: configurationRegistry.ts
882 > windowSettings.properties[key] = property; configurationRegistry.ts
883 > break;
884 > case ConfigurationScope.RESOURCE: configurationRegistry.ts
885 resourceSettings.properties[key] = property;
886 break;
887 > case ConfigurationScope.LANGUAGE_OVERRIDABLE: configurationRegistry.ts
888 resourceSettings.properties[key] = property;
889 this.resourceLanguageSettingsSchema.properties![key] = property;
890 break;
892 > }
894 > private removeFromSchema(key: string, property: IConfigurationPropertySchema): void {
895 > delete allSettings.properties[key]; configurationRegistry.ts
896 > switch (property.scope) {
897 > case ConfigurationScope.APPLICATION:
898 delete applicationSettings.properties[key];
899 break;
900 > case ConfigurationScope.MACHINE: configurationRegistry.ts
901 delete machineSettings.properties[key];
902 break;
903 > case ConfigurationScope.APPLICATION_MACHINE: configurationRegistry.ts
904 delete applicationMachineSettings.properties[key];
905 break;
906 > case ConfigurationScope.MACHINE_OVERRIDABLE: configurationRegistry.ts
907 delete machineOverridableSettings.properties[key];
908 break;
909 > case ConfigurationScope.WINDOW: configurationRegistry.ts
910 > delete windowSettings.properties[key];
911 > break;
912 > case ConfigurationScope.RESOURCE:
913 > case ConfigurationScope.LANGUAGE_OVERRIDABLE:
914 delete resourceSettings.properties[key];
915 delete this.resourceLanguageSettingsSchema.properties![key];
916 break;
918 > }
920 > private updateOverridePropertyPatternKey(): void {
921 for (const overrideIdentifier of this.overrideIdentifiers.values()) {
922 const overrideIdentifierProperty = `[${overrideIdentifier}]`;
937 }
938 }
940 > private registerOverridePropertyPatternKey(): void {
941 > const resourceLanguagePropertiesSchema: IJSONSchema = {
942 > type: 'object',
943 > description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),
944 > errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),
945 > $ref: resourceLanguageSettingsSchemaId,
946 > };
947 > allSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
948 > applicationSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
949 > applicationMachineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
950 > machineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
951 > machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
952 > windowSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
953 > resourceSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
954 > this._onDidSchemaChange.fire();
955 > }
956 >
957 > private updatePropertyDefaultValue(key: string, property: IRegisteredConfigurationPropertySchema): void {
958 > const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key)?.configurationDefaultOverrideValue; configurationRegistry.ts
959 > let defaultValue = undefined;
960 > let defaultSource = undefined;
961 > if (configurationdefaultOverride
962 && (!property.disallowConfigurationDefault || !configurationdefaultOverride.source) // Prevent overriding the default value if the property is disallowed to be overridden by configuration defaults from extensions
964 defaultValue = configurationdefaultOverride.value;
965 defaultSource = configurationdefaultOverride.source;
966 }
967 > if (types.isUndefined(defaultValue)) { configurationRegistry.ts
968 > defaultValue = property.defaultDefaultValue; configurationRegistry.ts
969 > defaultSource = undefined;
970 > }
971 > if (types.isUndefined(defaultValue)) { configurationRegistry.ts
972 defaultValue = getDefaultValue(property.type);
973 }
974 > property.default = defaultValue; configurationRegistry.ts
975 > property.defaultValueSource = defaultSource;
976 > }
978 >
979 > const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`;
980 > const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g');
981 > export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`;
982 > export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN);
983 >
984 > export function overrideIdentifiersFromKey(key: string): string[] {
985 const identifiers: string[] = [];
986 if (OVERRIDE_PROPERTY_REGEX.test(key)) {
996 return distinct(identifiers);
997 }
999 > export function keyFromOverrideIdentifiers(overrideIdentifiers: string[]): string {
1000 return overrideIdentifiers.reduce((result, overrideIdentifier) => `${result}[${overrideIdentifier}]`, '');
1001 }
1003 > export function getDefaultValue(type: string | string[] | undefined) {
1004 const t = Array.isArray(type) ? type[0] : <string>type;
1005 switch (t) {
1019 }
1020 }
1022 > const configurationRegistry = new ConfigurationRegistry();
1023 > Registry.add(Extensions.Configuration, configurationRegistry);
1024 >
1025 > export function validateProperty(property: string, schema: IRegisteredConfigurationPropertySchema, extensionId?: string): string | null {
1026 > if (!property.trim()) { configurationRegistry.ts
1027 return nls.localize('config.property.empty', "Cannot register an empty property");
1028 }
1029 > if (OVERRIDE_PROPERTY_REGEX.test(property)) { configurationRegistry.ts
1030 return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property);
1031 }
1032 > if (configurationRegistry.getConfigurationProperties()[property] !== undefined && (!extensionId || !EXTENSION_UNIFICATION_EXTENSION_IDS.has(extensionId.toLowerCase()))) { configurationRegistry.ts
1033 return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property);
1034 }
1035 > if (schema.policy && schema.policyReference) { configurationRegistry.ts
1036 return nls.localize('config.policy.bothPolicyAndReference', "Cannot register '{0}'. A setting must not declare both 'policy' and 'policyReference'.", property);
1037 }
1038 > if (schema.policy?.name && configurationRegistry.getPolicyConfigurations().get(schema.policy?.name) !== undefined) { configurationRegistry.ts
1039 return nls.localize('config.policy.duplicate', "Cannot register '{0}'. The associated policy {1} is already registered with {2}. To attach another setting to the same policy, use 'policyReference'.", property, schema.policy?.name, configurationRegistry.getPolicyConfigurations().get(schema.policy?.name));
1040 }
1041 > return null; configurationRegistry.ts
1042 > }
1044 > export function getScopes(): [string, ConfigurationScope | undefined][] {
1045 const scopes: [string, ConfigurationScope | undefined][] = [];
1046 const configurationProperties = configurationRegistry.getConfigurationProperties();
1052 return scopes;
1053 }
1055 > export function getAllConfigurationProperties(configurationNode: IConfigurationNode[]): IStringDictionary<IRegisteredConfigurationPropertySchema> {
1056 const result: IStringDictionary<IRegisteredConfigurationPropertySchema> = {};
1057 for (const configuration of configurationNode) {
1068 return result;
1069 }
1071 > export function parseScope(scope: string): ConfigurationScope {
1072 switch (scope) {
1073 case 'application':
1085 }
1086 }
1088 > // Used for extension unification. Should be removed when complete.
1089 > export const EXTENSION_UNIFICATION_EXTENSION_IDS: Set<string> = new Set(product.defaultChatAgent ? [product.defaultChatAgent.extensionId, product.defaultChatAgent.chatExtensionId].map(id => id.toLowerCase()) : []);
src/vs/base/common/lifecycle.ts 552 covered LOC · 126 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lifecycle.ts
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 { compareBy, numberComparator } from './arrays.js';
7 > import { groupBy } from './collections.js';
8 > import { SetMap, ResourceMap } from './map.js';
9 > import { URI } from './uri.js';
10 > import { createSingleCallFunction } from './functional.js';
11 > import { Iterable } from './iterator.js';
12 > import { BugIndicatingError, onUnexpectedError } from './errors.js';
13 >
14 > // #region Disposable Tracking
15 >
16 > /**
17 > * Enables logging of potentially leaked disposables.
18 > *
19 > * A disposable is considered leaked if it is not disposed or not registered as the child of
20 > * another disposable. This tracking is very simple an only works for classes that either
21 > * extend Disposable or use a DisposableStore. This means there are a lot of false positives.
22 > */
23 > const TRACK_DISPOSABLES = false;
24 > let disposableTracker: IDisposableTracker | null = null;
25 >
26 > export interface IDisposableTracker {
27 > /**
28 > * Is called on construction of a disposable.
29 > */
30 > trackDisposable(disposable: IDisposable): void;
31 >
32 > /**
33 > * Is called when a disposable is registered as child of another disposable (e.g. {@link DisposableStore}).
34 > * If parent is `null`, the disposable is removed from its former parent.
35 > */
36 > setParent(child: IDisposable, parent: IDisposable | null): void;
37 >
38 > /**
39 > * Is called after a disposable is disposed.
40 > */
41 > markAsDisposed(disposable: IDisposable): void;
42 >
43 > /**
44 > * Indicates that the given object is a singleton which does not need to be disposed.
45 > */
46 > markAsSingleton(disposable: IDisposable): void;
47 > }
48 >
49 > export class GCBasedDisposableTracker implements IDisposableTracker {
50
51 private readonly _registry = new FinalizationRegistry<string>(heldValue => {
52 console.warn(`[LEAKED DISPOSABLE] ${heldValue}`);
53 });
55 > trackDisposable(disposable: IDisposable): void {
56 const stack = new Error('CREATED via:').stack!;
57 this._registry.register(disposable, stack, disposable);
58 }
60 > setParent(child: IDisposable, parent: IDisposable | null): void {
61 if (parent) {
62 this._registry.unregister(child);
65 }
66 }
68 > markAsDisposed(disposable: IDisposable): void {
69 this._registry.unregister(disposable);
70 }
72 > markAsSingleton(disposable: IDisposable): void {
73 this._registry.unregister(disposable);
74 }
75 > } lifecycle.ts
76 >
77 > export interface DisposableInfo {
78 > value: IDisposable;
79 > source: string | null;
80 > parent: IDisposable | null;
81 > isSingleton: boolean;
82 > idx: number;
83 > }
84 >
85 > export class DisposableTracker implements IDisposableTracker {
86 > private static idx = 0; lifecycle.ts
87 >
88 > private readonly livingDisposables = new Map<IDisposable, DisposableInfo>();
90 > private getDisposableData(d: IDisposable): DisposableInfo {
91 > let val = this.livingDisposables.get(d); lifecycle.ts
92 > if (!val) {
93 > val = { parent: null, source: null, isSingleton: false, value: d, idx: DisposableTracker.idx++ };
94 > this.livingDisposables.set(d, val);
95 > }
96 > return val;
97 > }
99 > trackDisposable(d: IDisposable): void {
100 > const data = this.getDisposableData(d); lifecycle.ts
101 > if (!data.source) {
102 > data.source =
103 > new Error().stack!;
104 > }
105 > }
106 > lifecycle.ts
107 > setParent(child: IDisposable, parent: IDisposable | null): void {
108 > const data = this.getDisposableData(child); lifecycle.ts
109 > data.parent = parent;
110 > }
111 > lifecycle.ts
112 > markAsDisposed(x: IDisposable): void {
113 > this.livingDisposables.delete(x); lifecycle.ts
114 > }
115 > lifecycle.ts
116 > markAsSingleton(disposable: IDisposable): void {
117 this.getDisposableData(disposable).isSingleton = true;
118 }
119 > lifecycle.ts
120 > private getRootParent(data: DisposableInfo, cache: Map<DisposableInfo, DisposableInfo>): DisposableInfo {
121 const cacheValue = cache.get(data);
122 if (cacheValue) {
128 return result;
129 }
130 > lifecycle.ts
131 > getTrackedDisposables(): IDisposable[] {
132 const rootParentCache = new Map<DisposableInfo, DisposableInfo>();
133
138 return leaking;
139 }
140 > lifecycle.ts
141 > computeLeakingDisposables(maxReported = 10, preComputedLeaks?: DisposableInfo[]): { leaks: DisposableInfo[]; details: string } | undefined {
142 > let uncoveredLeakingObjs: DisposableInfo[] | undefined; lifecycle.ts
143 > if (preComputedLeaks) {
144 uncoveredLeakingObjs = preComputedLeaks;
145 > } else { lifecycle.ts
146 > const rootParentCache = new Map<DisposableInfo, DisposableInfo>();
147 >
148 > const leakingObjects = [...this.livingDisposables.values()]
149 > .filter((info) => info.source !== null && !this.getRootParent(info, rootParentCache).isSingleton);
150 >
151 > if (leakingObjects.length === 0) {
152 > return; lifecycle.ts
153 > }
154 const leakingObjsSet = new Set(leakingObjects.map(o => o.value));
155
162 throw new Error('There are cyclic diposable chains!');
163 }
164 > } lifecycle.ts
165
166 if (!uncoveredLeakingObjs) {
224
225 return { leaks: uncoveredLeakingObjs, details: message };
226 > } lifecycle.ts
227 > } lifecycle.ts
228 >
229 > export function setDisposableTracker(tracker: IDisposableTracker | null): void {
230 > disposableTracker = tracker; lifecycle.ts
231 > }
232 > lifecycle.ts
233 > if (TRACK_DISPOSABLES) {
234 const __is_disposable_tracked__ = '__is_disposable_tracked__';
235 setDisposableTracker(new class implements IDisposableTracker {
268 });
269 }
270 > lifecycle.ts
271 > export function trackDisposable<T extends IDisposable>(x: T): T {
272 > disposableTracker?.trackDisposable(x); lifecycle.ts
273 > return x;
274 > }
275 > lifecycle.ts
276 > export function markAsDisposed(disposable: IDisposable): void {
277 > disposableTracker?.markAsDisposed(disposable); lifecycle.ts
278 > }
279 > lifecycle.ts
280 > function setParentOfDisposable(child: IDisposable, parent: IDisposable | null): void { lifecycle.ts
281 > disposableTracker?.setParent(child, parent);
282 > }
283 > lifecycle.ts
284 function setParentOfDisposables(children: IDisposable[], parent: IDisposable | null): void {
285 if (!disposableTracker) {
290 }
291 }
292 > lifecycle.ts
293 > /**
294 > * Indicates that the given object is a singleton which does not need to be disposed.
295 > */
296 > export function markAsSingleton<T extends IDisposable>(singleton: T): T {
297 disposableTracker?.markAsSingleton(singleton);
298 return singleton;
299 }
300 > lifecycle.ts
301 > // #endregion
302 >
303 > /**
304 > * An object that performs a cleanup operation when `.dispose()` is called.
305 > *
306 > * Some examples of how disposables are used:
307 > *
308 > * - An event listener that removes itself when `.dispose()` is called.
309 > * - A resource such as a file system watcher that cleans up the resource when `.dispose()` is called.
310 > * - The return value from registering a provider. When `.dispose()` is called, the provider is unregistered.
311 > */
312 > export interface IDisposable {
313 > dispose(): void;
314 > }
315 >
316 > /**
317 > * Check if `thing` is {@link IDisposable disposable}.
318 > */
319 > export function isDisposable<E>(thing: E): thing is E & IDisposable {
320 // eslint-disable-next-line local/code-no-any-casts
321 return typeof thing === 'object' && thing !== null && typeof (<IDisposable><any>thing).dispose === 'function' && (<IDisposable><any>thing).dispose.length === 0;
322 }
323 > lifecycle.ts
324 > /**
325 > * Disposes of the value(s) passed in.
326 > */
327 > export function dispose<T extends IDisposable>(disposable: T): T;
328 > export function dispose<T extends IDisposable>(disposable: T | undefined): T | undefined;
329 > export function dispose<T extends IDisposable, A extends Iterable<T> = Iterable<T>>(disposables: A): A;
330 > export function dispose<T extends IDisposable>(disposables: Array<T>): Array<T>;
331 > export function dispose<T extends IDisposable>(disposables: ReadonlyArray<T>): ReadonlyArray<T>;
332 > export function dispose<T extends IDisposable>(arg: T | Iterable<T> | undefined): any {
333 > if (Iterable.is(arg)) { lifecycle.ts
334 > const errors: any[] = []; lifecycle.ts
335 >
336 > for (const d of arg) {
337 > if (d) { lifecycle.ts
338 > try {
339 > d.dispose();
340 > } catch (e) {
341 errors.push(e);
342 }
343 > } lifecycle.ts
344 > }
345 > lifecycle.ts
346 > if (errors.length === 1) {
347 throw errors[0];
348 > } else if (errors.length > 1) { lifecycle.ts
349 throw new AggregateError(errors, 'Encountered errors while disposing of store');
350 }
351 > lifecycle.ts
352 > return Array.isArray(arg) ? [] : arg; lifecycle.ts
353 > } else if (arg) { lifecycle.ts
354 arg.dispose();
355 return arg;
356 }
357 > } lifecycle.ts
358 > lifecycle.ts
359 > export function disposeIfDisposable<T extends IDisposable | object>(disposables: Array<T>): Array<T> {
360 for (const d of disposables) {
361 if (isDisposable(d)) {
365 return [];
366 }
367 > lifecycle.ts
368 > /**
369 > * Combine multiple disposable values into a single {@link IDisposable}.
370 > */
371 > export function combinedDisposable(...disposables: IDisposable[]): IDisposable {
372 const parent = toDisposable(() => dispose(disposables));
373 setParentOfDisposables(disposables, parent);
374 return parent;
375 }
376 > lifecycle.ts
377 > class FunctionDisposable implements IDisposable {
378 > private _isDisposed: boolean;
379 > private readonly _fn: () => void;
380 >
381 > constructor(fn: () => void) {
382 > this._isDisposed = false; lifecycle.ts
383 > this._fn = fn;
384 > trackDisposable(this);
385 > }
386 > lifecycle.ts
387 > dispose() {
388 > if (this._isDisposed) { lifecycle.ts
389 return;
390 }
391 > if (!this._fn) { lifecycle.ts
392 throw new Error(`Unbound disposable context: Need to use an arrow function to preserve the value of this`);
393 }
394 > this._isDisposed = true; lifecycle.ts
395 > markAsDisposed(this);
396 > this._fn();
397 > }
398 > } lifecycle.ts
399 >
400 > /**
401 > * Turn a function that implements dispose into an {@link IDisposable}.
402 > *
403 > * @param fn Clean up function, guaranteed to be called only **once**.
404 > */
405 > export function toDisposable(fn: () => void): IDisposable {
406 > return new FunctionDisposable(fn); lifecycle.ts
407 > }
408 > lifecycle.ts
409 > /**
410 > * Manages a collection of disposable values.
411 > *
412 > * This is the preferred way to manage multiple disposables. A `DisposableStore` is safer to work with than an
413 > * `IDisposable[]` as it considers edge cases, such as registering the same value multiple times or adding an item to a
414 > * store that has already been disposed of.
415 > */
416 > export class DisposableStore implements IDisposable {
417 >
418 > static DISABLE_DISPOSED_WARNING = false;
419 >
420 > private readonly _toDispose = new Set<IDisposable>();
421 > private _isDisposed = false;
422 >
423 > constructor() {
424 > trackDisposable(this); lifecycle.ts
425 > }
426 > lifecycle.ts
427 > /**
428 > * Dispose of all registered disposables and mark this object as disposed.
429 > *
430 > * Any future disposables added to this object will be disposed of on `add`.
431 > */
432 > public dispose(): void {
433 > if (this._isDisposed) { lifecycle.ts
434 return;
435 }
436 > lifecycle.ts
437 > markAsDisposed(this);
438 > this._isDisposed = true;
439 > this.clear();
440 > }
441 > lifecycle.ts
442 > /**
443 > * @return `true` if this object has been disposed of.
444 > */
445 > public get isDisposed(): boolean {
446 return this._isDisposed;
447 }
448 > lifecycle.ts
449 > /**
450 > * Dispose of all registered disposables but do not mark this object as disposed.
451 > */
452 > public clear(): void {
453 > if (this._toDispose.size === 0) { lifecycle.ts
454 return;
455 }
456 > lifecycle.ts
457 > try {
458 > dispose(this._toDispose);
459 > } finally {
460 > this._toDispose.clear();
461 > }
462 > } lifecycle.ts
463 > lifecycle.ts
464 > /**
465 > * Add a new {@link IDisposable disposable} to the collection.
466 > */
467 > public add<T extends IDisposable>(o: T): T {
468 > if (!o || o === Disposable.None) { lifecycle.ts
469 return o;
470 }
471 > if ((o as unknown as DisposableStore) === this) { lifecycle.ts
472 throw new Error('Cannot register a disposable on itself!');
473 }
474 > lifecycle.ts
475 > setParentOfDisposable(o, this);
476 > if (this._isDisposed) {
477 if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
478 console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
479 }
480 > } else { lifecycle.ts
481 > this._toDispose.add(o);
482 > }
483 >
484 > return o;
485 > } lifecycle.ts
486 > lifecycle.ts
487 > /**
488 > * Deletes a disposable from store and disposes of it. This will not throw or warn and proceed to dispose the
489 > * disposable even when the disposable is not part in the store.
490 > */
491 > public delete<T extends IDisposable>(o: T): void {
492 if (!o) {
493 return;
499 o.dispose();
500 }
501 > lifecycle.ts
502 > /**
503 > * Deletes the value from the store, but does not dispose it.
504 > */
505 > public deleteAndLeak<T extends IDisposable>(o: T): void {
506 if (!o) {
507 return;
511 }
512 }
513 > lifecycle.ts
514 > public assertNotDisposed(): void {
515 if (this._isDisposed) {
516 onUnexpectedError(new BugIndicatingError('Object disposed'));
517 }
518 }
519 > } lifecycle.ts
520 >
521 > /**
522 > * Abstract base class for a {@link IDisposable disposable} object.
523 > *
524 > * Subclasses can {@linkcode _register} disposables that will be automatically cleaned up when this object is disposed of.
525 > */
526 > export abstract class Disposable implements IDisposable {
527 >
528 > /**
529 > * A disposable that does nothing when it is disposed of.
530 > *
531 > * TODO: This should not be a static property.
532 > */
533 > static readonly None = Object.freeze<IDisposable>({ dispose() { } });
534 >
535 > protected readonly _store = new DisposableStore();
536 >
537 > constructor() {
538 > trackDisposable(this); lifecycle.ts
539 > setParentOfDisposable(this._store, this);
540 > }
541 > lifecycle.ts
542 > public dispose(): void {
543 > markAsDisposed(this); lifecycle.ts
544 >
545 > this._store.dispose();
546 > }
547 > lifecycle.ts
548 > /**
549 > * Adds `o` to the collection of disposables managed by this object.
550 > */
551 > protected _register<T extends IDisposable>(o: T): T {
552 > if ((o as unknown as Disposable) === this) { lifecycle.ts
553 throw new Error('Cannot register a disposable on itself!');
554 }
555 > return this._store.add(o); lifecycle.ts
556 > }
557 > } lifecycle.ts
558 >
559 > /**
560 > * Manages the lifecycle of a disposable value that may be changed.
561 > *
562 > * This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can
563 > * also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up.
564 > */
565 > export class MutableDisposable<T extends IDisposable> implements IDisposable {
566 > private _value?: T;
567 > private _isDisposed = false;
568 >
569 > constructor() {
570 trackDisposable(this);
571 }
572 > lifecycle.ts
573 > /**
574 > * Get the currently held disposable value, or `undefined` if this MutableDisposable has been disposed
575 > */
576 > get value(): T | undefined {
577 return this._isDisposed ? undefined : this._value;
578 }
579 > lifecycle.ts
580 > /**
581 > * Set a new disposable value.
582 > *
583 > * Behaviour:
584 > * - If the MutableDisposable has been disposed, the setter is a no-op.
585 > * - If the new value is strictly equal to the current value, the setter is a no-op.
586 > * - Otherwise the previous value (if any) is disposed and the new value is stored.
587 > *
588 > * Related helpers:
589 > * - clear() resets the value to `undefined` (and disposes the previous value).
590 > * - clearAndLeak() returns the old value without disposing it and removes its parent.
591 > */
592 > set value(value: T | undefined) {
593 if (this._isDisposed || value === this._value) {
594 return;
601 this._value = value;
602 }
603 > lifecycle.ts
604 > /**
605 > * Resets the stored value and disposed of the previously stored value.
606 > */
607 > clear(): void {
608 this.value = undefined;
609 }
610 > lifecycle.ts
611 > dispose(): void {
612 this._isDisposed = true;
613 markAsDisposed(this);
615 this._value = undefined;
616 }
617 > lifecycle.ts
618 > /**
619 > * Clears the value, but does not dispose it.
620 > * The old value is returned.
621 > */
622 > clearAndLeak(): T | undefined {
623 const oldValue = this._value;
624 this._value = undefined;
628 return oldValue;
629 }
630 > } lifecycle.ts
631 >
632 > /**
633 > * Manages the lifecycle of a disposable value that may be changed like {@link MutableDisposable}, but the value must
634 > * exist and cannot be undefined.
635 > */
636 > export class MandatoryMutableDisposable<T extends IDisposable> implements IDisposable {
637 > private readonly _disposable = new MutableDisposable<T>();
638 > private _isDisposed = false;
639 >
640 > constructor(initialValue: T) {
641 this._disposable.value = initialValue;
642 }
643 > lifecycle.ts
644 > get value(): T {
645 return this._disposable.value!;
646 }
647 > lifecycle.ts
648 > set value(value: T) {
649 if (this._isDisposed || value === this._disposable.value) {
650 return;
652 this._disposable.value = value;
653 }
654 > lifecycle.ts
655 > dispose() {
656 this._isDisposed = true;
657 this._disposable.dispose();
658 }
659 > } lifecycle.ts
660 >
661 > export class RefCountedDisposable {
662 >
663 > private _counter: number = 1;
664 >
665 > constructor(
666 private readonly _disposable: IDisposable,
667 ) { }
668 > lifecycle.ts
669 > acquire() {
670 this._counter++;
671 return this;
672 }
673 > lifecycle.ts
674 > release() {
675 if (--this._counter === 0) {
676 this._disposable.dispose();
678 return this;
679 }
680 > } lifecycle.ts
681 >
682 > export interface IReference<T> extends IDisposable {
683 > readonly object: T;
684 > }
685 >
686 > export abstract class ReferenceCollection<T> {
687
688 private readonly references: Map<string, { readonly object: T; counter: number }> = new Map();
689 > lifecycle.ts
690 > acquire(key: string, ...args: unknown[]): IReference<T> {
691 let reference = this.references.get(key);
692
708 return { object, dispose };
709 }
710 > lifecycle.ts
711 > protected abstract createReferencedObject(key: string, ...args: unknown[]): T;
712 > protected abstract destroyReferencedObject(key: string, object: T): void;
713 > }
714 >
715 > /**
716 > * Unwraps a reference collection of promised values. Makes sure
717 > * references are disposed whenever promises get rejected.
718 > */
719 > export class AsyncReferenceCollection<T> {
720 >
721 > constructor(private referenceCollection: ReferenceCollection<Promise<T>>) { }
722 >
723 > async acquire(key: string, ...args: unknown[]): Promise<IReference<T>> {
724 const ref = this.referenceCollection.acquire(key, ...args);
725
736 }
737 }
738 > } lifecycle.ts
739 >
740 > export class ImmortalReference<T> implements IReference<T> {
741 > constructor(public object: T) { }
742 > dispose(): void { /* noop */ }
743 > }
744 >
745 > export function disposeOnReturn(fn: (store: DisposableStore) => void): void {
746 const store = new DisposableStore();
747 try {
751 }
752 }
753 > lifecycle.ts
754 > /**
755 > * A map the manages the lifecycle of the values that it stores.
756 > */
757 > export class DisposableMap<K, V extends IDisposable = IDisposable> implements IDisposable {
758 >
759 > private readonly _store: Map<K, V>;
760 > private _isDisposed = false;
761 >
762 > constructor(store: Map<K, V> = new Map<K, V>()) {
763 this._store = store;
764 trackDisposable(this);
765 }
766 > lifecycle.ts
767 > /**
768 > * Disposes of all stored values and mark this object as disposed.
769 > *
770 > * Trying to use this object after it has been disposed of is an error.
771 > */
772 > dispose(): void {
773 markAsDisposed(this);
774 this._isDisposed = true;
775 this.clearAndDisposeAll();
776 }
777 > lifecycle.ts
778 > /**
779 > * Disposes of all stored values and clear the map, but DO NOT mark this object as disposed.
780 > */
781 > clearAndDisposeAll(): void {
782 if (!this._store.size) {
783 return;
790 }
791 }
792 > lifecycle.ts
793 > has(key: K): boolean {
794 return this._store.has(key);
795 }
796 > lifecycle.ts
797 > get size(): number {
798 return this._store.size;
799 }
800 > lifecycle.ts
801 > get(key: K): V | undefined {
802 return this._store.get(key);
803 }
804 > lifecycle.ts
805 > set(key: K, value: V, skipDisposeOnOverwrite = false): void {
806 if (this._isDisposed) {
807 console.warn(new Error('Trying to add a disposable to a DisposableMap that has already been disposed of. The added object will be leaked!').stack);
815 setParentOfDisposable(value, this);
816 }
817 > lifecycle.ts
818 > /**
819 > * Delete the value stored for `key` from this map and also dispose of it.
820 > */
821 > deleteAndDispose(key: K): void {
822 this._store.get(key)?.dispose();
823 this._store.delete(key);
824 }
825 > lifecycle.ts
826 > /**
827 > * Delete the value stored for `key` from this map but return it. The caller is
828 > * responsible for disposing of the value.
829 > */
830 > deleteAndLeak(key: K): V | undefined {
831 const value = this._store.get(key);
832 if (value) {
836 return value;
837 }
838 > lifecycle.ts
839 > keys(): IterableIterator<K> {
840 return this._store.keys();
841 }
842 > lifecycle.ts
843 > values(): IterableIterator<V> {
844 return this._store.values();
845 }
846 > lifecycle.ts
847 > [Symbol.iterator](): IterableIterator<[K, V]> {
848 return this._store[Symbol.iterator]();
849 }
850 > } lifecycle.ts
851 >
852 > /**
853 > * A set that manages the lifecycle of the values that it stores.
854 > */
855 > export class DisposableSet<V extends IDisposable = IDisposable> implements IDisposable {
856 >
857 > private readonly _store: Set<V>;
858 > private _isDisposed = false;
859 >
860 > constructor(store: Set<V> = new Set<V>()) {
861 this._store = store;
862 trackDisposable(this);
863 }
864 > lifecycle.ts
865 > /**
866 > * Disposes of all stored values and mark this object as disposed.
867 > *
868 > * Trying to use this object after it has been disposed of is an error.
869 > */
870 > dispose(): void {
871 markAsDisposed(this);
872 this._isDisposed = true;
873 this.clearAndDisposeAll();
874 }
875 > lifecycle.ts
876 > /**
877 > * Disposes of all stored values and clear the set, but DO NOT mark this object as disposed.
878 > */
879 > clearAndDisposeAll(): void {
880 if (!this._store.size) {
881 return;
888 }
889 }
890 > lifecycle.ts
891 > has(value: V): boolean {
892 return this._store.has(value);
893 }
894 > lifecycle.ts
895 > get size(): number {
896 return this._store.size;
897 }
898 > lifecycle.ts
899 > add(value: V): void {
900 if (this._isDisposed) {
901 console.warn(new Error('Trying to add a disposable to a DisposableSet that has already been disposed of. The added object will be leaked!').stack);
905 setParentOfDisposable(value, this);
906 }
907 > lifecycle.ts
908 > /**
909 > * Delete the value from this set and also dispose of it.
910 > */
911 > deleteAndDispose(value: V): void {
912 if (this._store.delete(value)) {
913 value.dispose();
914 }
915 }
916 > lifecycle.ts
917 > /**
918 > * Delete the value from this set but return it. The caller is
919 > * responsible for disposing of the value.
920 > */
921 > deleteAndLeak(value: V): V | undefined {
922 if (this._store.delete(value)) {
923 setParentOfDisposable(value, null);
926 return undefined;
927 }
928 > lifecycle.ts
929 > values(): IterableIterator<V> {
930 return this._store.values();
931 }
932 > lifecycle.ts
933 > [Symbol.iterator](): IterableIterator<V> {
934 return this._store[Symbol.iterator]();
935 }
936 > } lifecycle.ts
937 >
938 > /**
939 > * Call `then` on a Promise, unless the returned disposable is disposed.
940 > */
941 > export function thenIfNotDisposed<T>(promise: Promise<T>, then: (result: T) => void): IDisposable {
942 let disposed = false;
943 promise.then(result => {
951 });
952 }
953 > lifecycle.ts
954 > /**
955 > * Call `then` on a promise that resolves to a {@link IDisposable}, then either register the
956 > * disposable or register it to the {@link DisposableStore}, depending on whether the store is
957 > * disposed or not.
958 > */
959 > export function thenRegisterOrDispose<T extends IDisposable>(promise: Promise<T>, store: DisposableStore): Promise<T> {
960 return promise.then(disposable => {
961 if (store.isDisposed) {
967 });
968 }
969 > lifecycle.ts
970 > export class DisposableResourceMap<V extends IDisposable = IDisposable> extends DisposableMap<URI, V> {
971 > constructor() {
972 super(new ResourceMap());
973 }
974 > } lifecycle.ts
src/vs/base/common/strings.ts 454 covered LOC · 101 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- strings.ts
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 { LRUCachedFunction } from './cache.js';
7 > import { CharCode } from './charCode.js';
8 > import { Lazy } from './lazy.js';
9 > import { Constants } from './uint.js';
10 >
11 > export function isFalsyOrWhitespace(str: string | undefined): boolean {
12 if (!str || typeof str !== 'string') {
13 return true;
15 return str.trim().length === 0;
16 }
17 > strings.ts
18 > const _formatRegexp = /{(\d+)}/g;
19 >
20 > /**
21 > * Helper to produce a string with a variable number of arguments. Insert variable segments
22 > * into the string using the {n} notation where N is the index of the argument following the string.
23 > * @param value string to which formatting is applied
24 > * @param args replacements for {n}-entries
25 > */
26 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
27 > export function format(value: string, ...args: any[]): string {
28 if (args.length === 0) {
29 return value;
36 });
37 }
38 > strings.ts
39 > const _format2Regexp = /{([^}]+)}/g;
40 >
41 > /**
42 > * Helper to create a string from a template and a string record.
43 > * Similar to `format` but with objects instead of positional arguments.
44 > */
45 > export function format2(template: string, values: Record<string, unknown>): string {
46 if (Object.keys(values).length === 0) {
47 return template;
49 return template.replace(_format2Regexp, (match, group) => (values[group] ?? match) as string);
50 }
51 > strings.ts
52 > /**
53 > * Encodes the given value so that it can be used as literal value in html attributes.
54 > *
55 > * In other words, computes `$val`, such that `attr` in `<div attr="$val" />` has the runtime value `value`.
56 > * This prevents XSS injection.
57 > */
58 > export function htmlAttributeEncodeValue(value: string): string {
59 return value.replace(/[<>"'&]/g, ch => {
60 switch (ch) {
68 });
69 }
70 > strings.ts
71 > /**
72 > * Converts HTML characters inside the string to use entities instead. Makes the string safe from
73 > * being used e.g. in HTMLElement.innerHTML.
74 > */
75 > export function escape(html: string): string {
76 return html.replace(/[<>&]/g, function (match) {
77 switch (match) {
83 });
84 }
85 > strings.ts
86 > /**
87 > * Escapes regular expression characters in a given string
88 > */
89 > export function escapeRegExpCharacters(value: string): string {
90 return value.replace(/[\\\{\}\*\+\?\|\^\$\.\[\]\(\)]/g, '\\$&');
91 }
92 > strings.ts
93 > /**
94 > * Counts how often `substr` occurs inside `value`.
95 > */
96 > export function count(value: string, substr: string): number {
97 let result = 0;
98 let index = value.indexOf(substr);
103 return result;
104 }
105 > strings.ts
106 > export function truncate(value: string, maxLength: number, suffix = Ellipsis): string {
107 if (value.length <= maxLength) {
108 return value;
111 return `${value.substr(0, maxLength)}${suffix}`;
112 }
113 > strings.ts
114 > export function truncateMiddle(value: string, maxLength: number, suffix = Ellipsis): string {
115 if (value.length <= maxLength) {
116 return value;
122 return `${value.substr(0, prefixLength)}${suffix}${value.substr(value.length - suffixLength)}`;
123 }
124 > strings.ts
125 > /**
126 > * Removes all occurrences of needle from the beginning and end of haystack.
127 > * @param haystack string to trim
128 > * @param needle the thing to trim (default is a blank)
129 > */
130 > export function trim(haystack: string, needle: string = ' '): string {
131 const trimmed = ltrim(haystack, needle);
132 return rtrim(trimmed, needle);
133 }
134 > strings.ts
135 > /**
136 > * Removes all occurrences of needle from the beginning of haystack.
137 > * @param haystack string to trim
138 > * @param needle the thing to trim
139 > */
140 > export function ltrim(haystack: string, needle: string): string {
141 if (!haystack || !needle) {
142 return haystack;
157 return haystack.substring(offset);
158 }
159 > strings.ts
160 > /**
161 > * Removes all occurrences of needle from the end of haystack.
162 > * @param haystack string to trim
163 > * @param needle the thing to trim
164 > */
165 > export function rtrim(haystack: string, needle: string): string {
166 if (!haystack || !needle) {
167 return haystack;
187 return haystack.substring(0, offset);
188 }
189 > strings.ts
190 > export function convertSimple2RegExpPattern(pattern: string): string {
191 return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
192 }
193 > strings.ts
194 > export interface RegExpOptions {
195 > matchCase?: boolean;
196 > wholeWord?: boolean;
197 > multiline?: boolean;
198 > global?: boolean;
199 > unicode?: boolean;
200 > }
201 >
202 > export function createRegExp(searchString: string, isRegex: boolean, options: RegExpOptions = {}): RegExp {
203 if (!searchString) {
204 throw new Error('Cannot create regex from empty string');
231 return new RegExp(searchString, modifiers);
232 }
233 > strings.ts
234 > export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
235 // Exit early if it's one of these special cases which are meant to match
236 // against an empty string
244 return !!(match && regexp.lastIndex === 0);
245 }
246 > strings.ts
247 > export function joinStrings(items: (string | undefined | null | false)[], separator: string): string {
248 return items.filter(item => item !== undefined && item !== null && item !== false).join(separator);
249 }
250 > strings.ts
251 > export function splitLines(str: string): string[] {
252 return str.split(/\r\n|\r|\n/);
253 }
254 > strings.ts
255 > export function splitLinesIncludeSeparators(str: string): string[] {
256 const linesWithSeparators: string[] = [];
257 const splitLinesAndSeparators = str.split(/(\r\n|\r|\n)/);
261 return linesWithSeparators;
262 }
263 > strings.ts
264 > export function indexOfPattern(str: string, re: RegExp) {
265 const match = re.exec(str);
266 if (match) {
269 return -1;
270 }
271 > strings.ts
272 > /**
273 > * Returns first index of the string that is not whitespace.
274 > * If string is empty or contains only whitespaces, returns -1
275 > */
276 > export function firstNonWhitespaceIndex(str: string): number {
277 for (let i = 0, len = str.length; i < len; i++) {
278 const chCode = str.charCodeAt(i);
283 return -1;
284 }
285 > strings.ts
286 > /**
287 > * Returns the leading whitespace of the string.
288 > * If the string contains only whitespaces, returns entire string
289 > */
290 > export function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string {
291 for (let i = start; i < end; i++) {
292 const chCode = str.charCodeAt(i);
297 return str.substring(start, end);
298 }
299 > strings.ts
300 > /**
301 > * Returns last index of the string that is not whitespace.
302 > * If string is empty or contains only whitespaces, returns -1
303 > */
304 > export function lastNonWhitespaceIndex(str: string, startIndex: number = str.length - 1): number {
305 for (let i = startIndex; i >= 0; i--) {
306 const chCode = str.charCodeAt(i);
311 return -1;
312 }
313 > strings.ts
314 > export function getIndentationLength(str: string): number {
315 const idx = firstNonWhitespaceIndex(str);
316 if (idx === -1) { return str.length; }
317 return idx;
318 }
319 > strings.ts
320 > /**
321 > * Function that works identically to String.prototype.replace, except, the
322 > * replace function is allowed to be async and return a Promise.
323 > */
324 > export function replaceAsync(str: string, search: RegExp, replacer: (match: string, ...args: unknown[]) => Promise<string>): Promise<string> {
325 const parts: (string | Promise<string>)[] = [];
326
340 return Promise.all(parts).then(p => p.join(''));
341 }
342 > strings.ts
343 > export function compare(a: string, b: string): number {
344 if (a < b) {
345 return -1;
350 }
351 }
352 > strings.ts
353 > export function compareSubstring(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number {
354 for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
355 const codeA = a.charCodeAt(aStart);
370 return 0;
371 }
372 > strings.ts
373 > export function compareIgnoreCase(a: string, b: string): number {
374 return compareSubstringIgnoreCase(a, b, 0, a.length, 0, b.length);
375 }
376 > strings.ts
377 > export function compareSubstringIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number {
378
379 for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
421 return 0;
422 }
423 > strings.ts
424 > export function isAsciiDigit(code: number): boolean {
425 return code >= CharCode.Digit0 && code <= CharCode.Digit9;
426 }
427 > strings.ts
428 > export function isLowerAsciiLetter(code: number): boolean {
429 return code >= CharCode.a && code <= CharCode.z;
430 }
431 > strings.ts
432 > export function isUpperAsciiLetter(code: number): boolean {
433 return code >= CharCode.A && code <= CharCode.Z;
434 }
435 > strings.ts
436 > export function equalsIgnoreCase(a: string, b: string): boolean {
437 return a.length === b.length && compareSubstringIgnoreCase(a, b) === 0;
438 }
439 > strings.ts
440 > export function equals(a: string | undefined, b: string | undefined, ignoreCase?: boolean): boolean {
441 return a === b || (!!ignoreCase && a !== undefined && b !== undefined && equalsIgnoreCase(a, b));
442 }
443 > strings.ts
444 > export function startsWithIgnoreCase(str: string, candidate: string): boolean {
445 const len = candidate.length;
446 return len <= str.length && compareSubstringIgnoreCase(str, candidate, 0, len) === 0;
447 }
448 > strings.ts
449 > export function endsWithIgnoreCase(str: string, candidate: string): boolean {
450 const len = str.length;
451 const start = len - candidate.length;
452 return start >= 0 && compareSubstringIgnoreCase(str, candidate, start, len) === 0;
453 }
454 > strings.ts
455 > /**
456 > * @returns the length of the common prefix of the two strings.
457 > */
458 > export function commonPrefixLength(a: string, b: string): number {
459
460 const len = Math.min(a.length, b.length);
469 return len;
470 }
471 > strings.ts
472 > /**
473 > * @returns the length of the common suffix of the two strings.
474 > */
475 > export function commonSuffixLength(a: string, b: string): number {
476
477 const len = Math.min(a.length, b.length);
489 return len;
490 }
491 > strings.ts
492 > /**
493 > * See http://en.wikipedia.org/wiki/Surrogate_pair
494 > */
495 > export function isHighSurrogate(charCode: number): boolean {
496 return (0xD800 <= charCode && charCode <= 0xDBFF);
497 }
498 > strings.ts
499 > /**
500 > * See http://en.wikipedia.org/wiki/Surrogate_pair
501 > */
502 > export function isLowSurrogate(charCode: number): boolean {
503 return (0xDC00 <= charCode && charCode <= 0xDFFF);
504 }
505 > strings.ts
506 > /**
507 > * See http://en.wikipedia.org/wiki/Surrogate_pair
508 > */
509 > export function computeCodePoint(highSurrogate: number, lowSurrogate: number): number {
510 return ((highSurrogate - 0xD800) << 10) + (lowSurrogate - 0xDC00) + 0x10000;
511 }
512 > strings.ts
513 > /**
514 > * get the code point that begins at offset `offset`
515 > */
516 > export function getNextCodePoint(str: string, len: number, offset: number): number {
517 const charCode = str.charCodeAt(offset);
518 if (isHighSurrogate(charCode) && offset + 1 < len) {
524 return charCode;
525 }
526 > strings.ts
527 > /**
528 > * get the code point that ends right before offset `offset`
529 > */
530 function getPrevCodePoint(str: string, offset: number): number {
531 const charCode = str.charCodeAt(offset - 1);
538 return charCode;
539 }
540 > strings.ts
541 > export class CodePointIterator {
542 >
543 > private readonly _str: string;
544 > private readonly _len: number;
545 > private _offset: number;
546 >
547 > public get offset(): number {
548 return this._offset;
549 }
550 > strings.ts
551 > constructor(str: string, offset: number = 0) {
552 this._str = str;
553 this._len = str.length;
554 this._offset = offset;
555 }
556 > strings.ts
557 > public setOffset(offset: number): void {
558 this._offset = offset;
559 }
560 > strings.ts
561 > public prevCodePoint(): number {
562 const codePoint = getPrevCodePoint(this._str, this._offset);
563 this._offset -= (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
564 return codePoint;
565 }
566 > strings.ts
567 > public nextCodePoint(): number {
568 const codePoint = getNextCodePoint(this._str, this._len, this._offset);
569 this._offset += (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
570 return codePoint;
571 }
572 > strings.ts
573 > public eol(): boolean {
574 return (this._offset >= this._len);
575 }
576 > } strings.ts
577 >
578 > export class GraphemeIterator {
579 >
580 > private readonly _iterator: CodePointIterator;
581 >
582 > public get offset(): number {
583 return this._iterator.offset;
584 }
585 > strings.ts
586 > constructor(str: string, offset: number = 0) {
587 this._iterator = new CodePointIterator(str, offset);
588 }
589 > strings.ts
590 > public nextGraphemeLength(): number {
591 const graphemeBreakTree = GraphemeBreakTree.getInstance();
592 const iterator = this._iterator;
606 return (iterator.offset - initialOffset);
607 }
608 > strings.ts
609 > public prevGraphemeLength(): number {
610 const graphemeBreakTree = GraphemeBreakTree.getInstance();
611 const iterator = this._iterator;
625 return (initialOffset - iterator.offset);
626 }
627 > strings.ts
628 > public eol(): boolean {
629 return this._iterator.eol();
630 }
631 > } strings.ts
632 >
633 > export function nextCharLength(str: string, initialOffset: number): number {
634 const iterator = new GraphemeIterator(str, initialOffset);
635 return iterator.nextGraphemeLength();
636 }
637 > strings.ts
638 > export function prevCharLength(str: string, initialOffset: number): number {
639 const iterator = new GraphemeIterator(str, initialOffset);
640 return iterator.prevGraphemeLength();
641 }
642 > strings.ts
643 > export function getCharContainingOffset(str: string, offset: number): [number, number] {
644 if (offset > 0 && isLowSurrogate(str.charCodeAt(offset))) {
645 offset--;
649 return [startOffset, endOffset];
650 }
651 > strings.ts
652 > export function charCount(str: string): number {
653 const iterator = new GraphemeIterator(str);
654 let length = 0;
659 return length;
660 }
661 > strings.ts
662 > let CONTAINS_RTL: RegExp | undefined = undefined;
663 >
664 function makeContainsRtl() {
665 // Generated using https://github.com/alexdima/unicode-utils/blob/main/rtl-test.js
666 return /(?:[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05F4\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u0710\u0712-\u072F\u074D-\u07A5\u07B1-\u07EA\u07F4\u07F5\u07FA\u07FE-\u0815\u081A\u0824\u0828\u0830-\u0858\u085E-\u088E\u08A0-\u08C9\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFD3D\uFD50-\uFDC7\uFDF0-\uFDFC\uFE70-\uFEFC]|\uD802[\uDC00-\uDD1B\uDD20-\uDE00\uDE10-\uDE35\uDE40-\uDEE4\uDEEB-\uDF35\uDF40-\uDFFF]|\uD803[\uDC00-\uDD23\uDE80-\uDEA9\uDEAD-\uDF45\uDF51-\uDF81\uDF86-\uDFF6]|\uD83A[\uDC00-\uDCCF\uDD00-\uDD43\uDD4B-\uDFFF]|\uD83B[\uDC00-\uDEBB])/;
667 }
668 > strings.ts
669 > /**
670 > * Returns true if `str` contains any Unicode character that is classified as "R" or "AL".
671 > */
672 > export function containsRTL(str: string): boolean {
673 if (!CONTAINS_RTL) {
674 CONTAINS_RTL = makeContainsRtl();
677 return CONTAINS_RTL.test(str);
678 }
679 > strings.ts
680 > const IS_BASIC_ASCII = /^[\t\n\r\x20-\x7E]*$/;
681 > /**
682 > * Returns true if `str` contains only basic ASCII characters in the range 32 - 126 (including 32 and 126) or \n, \r, \t
683 > */
684 > export function isBasicASCII(str: string): boolean {
685 return IS_BASIC_ASCII.test(str);
686 }
687 > strings.ts
688 > export const UNUSUAL_LINE_TERMINATORS = /[\u2028\u2029]/; // LINE SEPARATOR (LS) or PARAGRAPH SEPARATOR (PS)
689 > /**
690 > * Returns true if `str` contains unusual line terminators, like LS or PS
691 > */
692 > export function containsUnusualLineTerminators(str: string): boolean {
693 return UNUSUAL_LINE_TERMINATORS.test(str);
694 }
695 > strings.ts
696 > export function isFullWidthCharacter(charCode: number): boolean {
697 // Do a cheap trick to better support wrapping of wide characters, treat them as 2 columns
698 // http://jrgraphix.net/research/unicode_blocks.php
741 );
742 }
743 > strings.ts
744 > /**
745 > * A fast function (therefore imprecise) to check if code points are emojis.
746 > * Generated using https://github.com/alexdima/unicode-utils/blob/main/emoji-test.js
747 > */
748 > export function isEmojiImprecise(x: number): boolean {
749 return (
750 (x >= 0x1F1E6 && x <= 0x1F1FF) || (x === 8986) || (x === 8987) || (x === 9200)
755 );
756 }
757 > strings.ts
758 > /**
759 > * Given a string and a max length returns a shorted version. Shorting
760 > * happens at favorable positions - such as whitespace or punctuation characters.
761 > * The return value can be longer than the given value of `n`. Leading whitespace is always trimmed.
762 > */
763 > export function lcut(text: string, n: number, prefix = ''): string {
764 const trimmed = text.trimStart();
765
785 return prefix + trimmed.substring(i).trimStart();
786 }
787 > strings.ts
788 > /**
789 > * Given a string and a max length returns a shortened version keeping the beginning.
790 > * Shortening happens at favorable positions - such as whitespace or punctuation characters.
791 > * Trailing whitespace is always trimmed.
792 > */
793 > export function rcut(text: string, n: number, suffix = ''): string {
794 const trimmed = text.trimEnd();
795
832 return result + suffix;
833 }
834 > strings.ts
835 > // Defacto standard: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
836 > const CSI_SEQUENCE = /(?:\x1b\[|\x9b)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~]/;
837 > const OSC_SEQUENCE = /(?:\x1b\]|\x9d).*?(?:\x1b\\|\x07|\x9c)/;
838 > const ESC_SEQUENCE = /\x1b(?:[ #%\(\)\*\+\-\.\/]?[a-zA-Z0-9\|}~@])/;
839 > const CONTROL_SEQUENCES = new RegExp('(?:' + [
840 > CSI_SEQUENCE.source,
841 > OSC_SEQUENCE.source,
842 > ESC_SEQUENCE.source,
843 > ].join('|') + ')', 'g');
844 >
845 > /** Iterates over parts of a string with CSI sequences */
846 > export function* forAnsiStringParts(str: string) {
847 let last = 0;
848 for (const match of str.matchAll(CONTROL_SEQUENCES)) {
859 }
860 }
861 > strings.ts
862 > /**
863 > * Strips ANSI escape sequences from a string.
864 > * @param str The dastringa stringo strip the ANSI escape sequences from.
865 > *
866 > * @example
867 > * removeAnsiEscapeCodes('\u001b[31mHello, World!\u001b[0m');
868 > * // 'Hello, World!'
869 > */
870 > export function removeAnsiEscapeCodes(str: string): string {
871 if (str) {
872 str = str.replace(CONTROL_SEQUENCES, '');
875 return str;
876 }
877 > strings.ts
878 > const PROMPT_NON_PRINTABLE = /\\\[.*?\\\]/g;
879 >
880 > /**
881 > * Strips ANSI escape sequences from a UNIX-style prompt string (eg. `$PS1`).
882 > * @param str The string to strip the ANSI escape sequences from.
883 > *
884 > * @example
885 > * removeAnsiEscapeCodesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]');
886 > * // '\n\\w\n> '
887 > */
888 > export function removeAnsiEscapeCodesFromPrompt(str: string): string {
889 return removeAnsiEscapeCodes(str).replace(PROMPT_NON_PRINTABLE, '');
890 }
891 > strings.ts
892 >
893 > // -- UTF-8 BOM
894 >
895 > export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM);
896 >
897 > export function startsWithUTF8BOM(str: string): boolean {
898 return !!(str && str.length > 0 && str.charCodeAt(0) === CharCode.UTF8_BOM);
899 }
900 > strings.ts
901 > export function stripUTF8BOM(str: string): string {
902 return startsWithUTF8BOM(str) ? str.substr(1) : str;
903 }
904 > strings.ts
905 > /**
906 > * Checks if the characters of the provided query string are included in the
907 > * target string. The characters do not have to be contiguous within the string.
908 > */
909 > export function fuzzyContains(target: string, query: string): boolean {
910 if (!target || !query) {
911 return false; // return early if target or query are undefined
936 return true;
937 }
938 > strings.ts
939 > export function containsUppercaseCharacter(target: string, ignoreEscapedChars = false): boolean {
940 if (!target) {
941 return false;
948 return target.toLowerCase() !== target;
949 }
950 > strings.ts
951 > export function uppercaseFirstLetter(str: string): string {
952 return str.charAt(0).toUpperCase() + str.slice(1);
953 }
954 > strings.ts
955 > export function getNLines(str: string, n = 1): string {
956 if (n === 0) {
957 return '';
974 return str.substr(0, idx);
975 }
976 > strings.ts
977 > /**
978 > * Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc.
979 > */
980 > export function singleLetterHash(n: number): string {
981 const LETTERS_CNT = (CharCode.Z - CharCode.A + 1);
982
989 return String.fromCharCode(CharCode.A + n - LETTERS_CNT);
990 }
991 > strings.ts
992 > //#region Unicode Grapheme Break
993 >
994 > export function getGraphemeBreakType(codePoint: number): GraphemeBreakType {
995 const graphemeBreakTree = GraphemeBreakTree.getInstance();
996 return graphemeBreakTree.getGraphemeBreakType(codePoint);
997 }
998 > strings.ts
999 function breakBetweenGraphemeBreakType(breakTypeA: GraphemeBreakType, breakTypeB: GraphemeBreakType): boolean {
1000 // http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
1076 return true;
1077 }
1078 > strings.ts
1079 > export const enum GraphemeBreakType {
1080 > Other = 0,
1081 > Prepend = 1,
1082 > CR = 2,
1083 > LF = 3,
1084 > Control = 4,
1085 > Extend = 5,
1086 > Regional_Indicator = 6,
1087 > SpacingMark = 7,
1088 > L = 8,
1089 > V = 9,
1090 > T = 10,
1091 > LV = 11,
1092 > LVT = 12,
1093 > ZWJ = 13,
1094 > Extended_Pictographic = 14
1095 > }
1096 >
1097 > class GraphemeBreakTree {
1098 >
1099 > private static _INSTANCE: GraphemeBreakTree | null = null;
1100 > public static getInstance(): GraphemeBreakTree {
1101 if (!GraphemeBreakTree._INSTANCE) {
1102 GraphemeBreakTree._INSTANCE = new GraphemeBreakTree();
1104 return GraphemeBreakTree._INSTANCE;
1105 }
1106 > strings.ts
1107 > private readonly _data: number[];
1108 >
1109 > constructor() {
1110 this._data = getGraphemeBreakRawData();
1111 }
1112 > strings.ts
1113 > public getGraphemeBreakType(codePoint: number): GraphemeBreakType {
1114 // !!! Let's make 7bit ASCII a bit faster: 0..31
1115 if (codePoint < 32) {
1145 return GraphemeBreakType.Other;
1146 }
1147 > } strings.ts
1148 >
1149 function getGraphemeBreakRawData(): number[] {
1150 // generated using https://github.com/alexdima/unicode-utils/blob/main/grapheme-break.js
1151 return JSON.parse('[0,0,0,51229,51255,12,44061,44087,12,127462,127487,6,7083,7085,5,47645,47671,12,54813,54839,12,128678,128678,14,3270,3270,5,9919,9923,14,45853,45879,12,49437,49463,12,53021,53047,12,71216,71218,7,128398,128399,14,129360,129374,14,2519,2519,5,4448,4519,9,9742,9742,14,12336,12336,14,44957,44983,12,46749,46775,12,48541,48567,12,50333,50359,12,52125,52151,12,53917,53943,12,69888,69890,5,73018,73018,5,127990,127990,14,128558,128559,14,128759,128760,14,129653,129655,14,2027,2035,5,2891,2892,7,3761,3761,5,6683,6683,5,8293,8293,4,9825,9826,14,9999,9999,14,43452,43453,5,44509,44535,12,45405,45431,12,46301,46327,12,47197,47223,12,48093,48119,12,48989,49015,12,49885,49911,12,50781,50807,12,51677,51703,12,52573,52599,12,53469,53495,12,54365,54391,12,65279,65279,4,70471,70472,7,72145,72147,7,119173,119179,5,127799,127818,14,128240,128244,14,128512,128512,14,128652,128652,14,128721,128722,14,129292,129292,14,129445,129450,14,129734,129743,14,1476,1477,5,2366,2368,7,2750,2752,7,3076,3076,5,3415,3415,5,4141,4144,5,6109,6109,5,6964,6964,5,7394,7400,5,9197,9198,14,9770,9770,14,9877,9877,14,9968,9969,14,10084,10084,14,43052,43052,5,43713,43713,5,44285,44311,12,44733,44759,12,45181,45207,12,45629,45655,12,46077,46103,12,46525,46551,12,46973,46999,12,47421,47447,12,47869,47895,12,48317,48343,12,48765,48791,12,49213,49239,12,49661,49687,12,50109,50135,12,50557,50583,12,51005,51031,12,51453,51479,12,51901,51927,12,52349,52375,12,52797,52823,12,53245,53271,12,53693,53719,12,54141,54167,12,54589,54615,12,55037,55063,12,69506,69509,5,70191,70193,5,70841,70841,7,71463,71467,5,72330,72342,5,94031,94031,5,123628,123631,5,127763,127765,14,127941,127941,14,128043,128062,14,128302,128317,14,128465,128467,14,128539,128539,14,128640,128640,14,128662,128662,14,128703,128703,14,128745,128745,14,129004,129007,14,129329,129330,14,129402,129402,14,129483,129483,14,129686,129704,14,130048,131069,14,173,173,4,1757,1757,1,2200,2207,5,2434,2435,7,2631,2632,5,2817,2817,5,3008,3008,5,3201,3201,5,3387,3388,5,3542,3542,5,3902,3903,7,4190,4192,5,6002,6003,5,6439,6440,5,6765,6770,7,7019,7027,5,7154,7155,7,8205,8205,13,8505,8505,14,9654,9654,14,9757,9757,14,9792,9792,14,9852,9853,14,9890,9894,14,9937,9937,14,9981,9981,14,10035,10036,14,11035,11036,14,42654,42655,5,43346,43347,7,43587,43587,5,44006,44007,7,44173,44199,12,44397,44423,12,44621,44647,12,44845,44871,12,45069,45095,12,45293,45319,12,45517,45543,12,45741,45767,12,45965,45991,12,46189,46215,12,46413,46439,12,46637,46663,12,46861,46887,12,47085,47111,12,47309,47335,12,47533,47559,12,47757,47783,12,47981,48007,12,48205,48231,12,48429,48455,12,48653,48679,12,48877,48903,12,49101,49127,12,49325,49351,12,49549,49575,12,49773,49799,12,49997,50023,12,50221,50247,12,50445,50471,12,50669,50695,12,50893,50919,12,51117,51143,12,51341,51367,12,51565,51591,12,51789,51815,12,52013,52039,12,52237,52263,12,52461,52487,12,52685,52711,12,52909,52935,12,53133,53159,12,53357,53383,12,53581,53607,12,53805,53831,12,54029,54055,12,54253,54279,12,54477,54503,12,54701,54727,12,54925,54951,12,55149,55175,12,68101,68102,5,69762,69762,7,70067,70069,7,70371,70378,5,70720,70721,7,71087,71087,5,71341,71341,5,71995,71996,5,72249,72249,7,72850,72871,5,73109,73109,5,118576,118598,5,121505,121519,5,127245,127247,14,127568,127569,14,127777,127777,14,127872,127891,14,127956,127967,14,128015,128016,14,128110,128172,14,128259,128259,14,128367,128368,14,128424,128424,14,128488,128488,14,128530,128532,14,128550,128551,14,128566,128566,14,128647,128647,14,128656,128656,14,128667,128673,14,128691,128693,14,128715,128715,14,128728,128732,14,128752,128752,14,128765,128767,14,129096,129103,14,129311,129311,14,129344,129349,14,129394,129394,14,129413,129425,14,129466,129471,14,129511,129535,14,129664,129666,14,129719,129722,14,129760,129767,14,917536,917631,5,13,13,2,1160,1161,5,1564,1564,4,1807,1807,1,2085,2087,5,2307,2307,7,2382,2383,7,2497,2500,5,2563,2563,7,2677,2677,5,2763,2764,7,2879,2879,5,2914,2915,5,3021,3021,5,3142,3144,5,3263,3263,5,3285,3286,5,3398,3400,7,3530,3530,5,3633,3633,5,3864,3865,5,3974,3975,5,4155,4156,7,4229,4230,5,5909,5909,7,6078,6085,7,6277,6278,5,6451,6456,7,6744,6750,5,6846,6846,5,6972,6972,5,7074,7077,5,7146,7148,7,7222,7223,5,7416,7417,5,8234,8238,4,8417,8417,5,9000,9000,14,9203,9203,14,9730,9731,14,9748,9749,14,9762,9763,14,9776,9783,14,9800,9811,14,9831,9831,14,9872,9873,14,9882,9882,14,9900,9903,14,9929,9933,14,9941,9960,14,9974,9974,14,9989,9989,14,10006,10006,14,10062,10062,14,10160,10160,14,11647,11647,5,12953,12953,14,43019,43019,5,43232,43249,5,43443,43443,5,43567,43568,7,43696,43696,5,43765,43765,7,44013,44013,5,44117,44143,12,44229,44255,12,44341,44367,12,44453,44479,12,44565,44591,12,44677,44703,12,44789,44815,12,44901,44927,12,45013,45039,12,45125,45151,12,45237,45263,12,45349,45375,12,45461,45487,12,45573,45599,12,45685,45711,12,45797,45823,12,45909,45935,12,46021,46047,12,46133,46159,12,46245,46271,12,46357,46383,12,46469,46495,12,46581,46607,12,46693,46719,12,46805,46831,12,46917,46943,12,47029,47055,12,47141,47167,12,47253,47279,12,47365,47391,12,47477,47503,12,47589,47615,12,47701,47727,12,47813,47839,12,47925,47951,12,48037,48063,12,48149,48175,12,48261,48287,12,48373,48399,12,48485,48511,12,48597,48623,12,48709,48735,12,48821,48847,12,48933,48959,12,49045,49071,12,49157,49183,12,49269,49295,12,49381,49407,12,49493,49519,12,49605,49631,12,49717,49743,12,49829,49855,12,49941,49967,12,50053,50079,12,50165,50191,12,50277,50303,12,50389,50415,12,50501,50527,12,50613,50639,12,50725,50751,12,50837,50863,12,50949,50975,12,51061,51087,12,51173,51199,12,51285,51311,12,51397,51423,12,51509,51535,12,51621,51647,12,51733,51759,12,51845,51871,12,51957,51983,12,52069,52095,12,52181,52207,12,52293,52319,12,52405,52431,12,52517,52543,12,52629,52655,12,52741,52767,12,52853,52879,12,52965,52991,12,53077,53103,12,53189,53215,12,53301,53327,12,53413,53439,12,53525,53551,12,53637,53663,12,53749,53775,12,53861,53887,12,53973,53999,12,54085,54111,12,54197,54223,12,54309,54335,12,54421,54447,12,54533,54559,12,54645,54671,12,54757,54783,12,54869,54895,12,54981,55007,12,55093,55119,12,55243,55291,10,66045,66045,5,68325,68326,5,69688,69702,5,69817,69818,5,69957,69958,7,70089,70092,5,70198,70199,5,70462,70462,5,70502,70508,5,70750,70750,5,70846,70846,7,71100,71101,5,71230,71230,7,71351,71351,5,71737,71738,5,72000,72000,7,72160,72160,5,72273,72278,5,72752,72758,5,72882,72883,5,73031,73031,5,73461,73462,7,94192,94193,7,119149,119149,7,121403,121452,5,122915,122916,5,126980,126980,14,127358,127359,14,127535,127535,14,127759,127759,14,127771,127771,14,127792,127793,14,127825,127867,14,127897,127899,14,127945,127945,14,127985,127986,14,128000,128007,14,128021,128021,14,128066,128100,14,128184,128235,14,128249,128252,14,128266,128276,14,128335,128335,14,128379,128390,14,128407,128419,14,128444,128444,14,128481,128481,14,128499,128499,14,128526,128526,14,128536,128536,14,128543,128543,14,128556,128556,14,128564,128564,14,128577,128580,14,128643,128645,14,128649,128649,14,128654,128654,14,128660,128660,14,128664,128664,14,128675,128675,14,128686,128689,14,128695,128696,14,128705,128709,14,128717,128719,14,128725,128725,14,128736,128741,14,128747,128748,14,128755,128755,14,128762,128762,14,128981,128991,14,129009,129023,14,129160,129167,14,129296,129304,14,129320,129327,14,129340,129342,14,129356,129356,14,129388,129392,14,129399,129400,14,129404,129407,14,129432,129442,14,129454,129455,14,129473,129474,14,129485,129487,14,129648,129651,14,129659,129660,14,129671,129679,14,129709,129711,14,129728,129730,14,129751,129753,14,129776,129782,14,917505,917505,4,917760,917999,5,10,10,3,127,159,4,768,879,5,1471,1471,5,1536,1541,1,1648,1648,5,1767,1768,5,1840,1866,5,2070,2073,5,2137,2139,5,2274,2274,1,2363,2363,7,2377,2380,7,2402,2403,5,2494,2494,5,2507,2508,7,2558,2558,5,2622,2624,7,2641,2641,5,2691,2691,7,2759,2760,5,2786,2787,5,2876,2876,5,2881,2884,5,2901,2902,5,3006,3006,5,3014,3016,7,3072,3072,5,3134,3136,5,3157,3158,5,3260,3260,5,3266,3266,5,3274,3275,7,3328,3329,5,3391,3392,7,3405,3405,5,3457,3457,5,3536,3537,7,3551,3551,5,3636,3642,5,3764,3772,5,3895,3895,5,3967,3967,7,3993,4028,5,4146,4151,5,4182,4183,7,4226,4226,5,4253,4253,5,4957,4959,5,5940,5940,7,6070,6070,7,6087,6088,7,6158,6158,4,6432,6434,5,6448,6449,7,6679,6680,5,6742,6742,5,6754,6754,5,6783,6783,5,6912,6915,5,6966,6970,5,6978,6978,5,7042,7042,7,7080,7081,5,7143,7143,7,7150,7150,7,7212,7219,5,7380,7392,5,7412,7412,5,8203,8203,4,8232,8232,4,8265,8265,14,8400,8412,5,8421,8432,5,8617,8618,14,9167,9167,14,9200,9200,14,9410,9410,14,9723,9726,14,9733,9733,14,9745,9745,14,9752,9752,14,9760,9760,14,9766,9766,14,9774,9774,14,9786,9786,14,9794,9794,14,9823,9823,14,9828,9828,14,9833,9850,14,9855,9855,14,9875,9875,14,9880,9880,14,9885,9887,14,9896,9897,14,9906,9916,14,9926,9927,14,9935,9935,14,9939,9939,14,9962,9962,14,9972,9972,14,9978,9978,14,9986,9986,14,9997,9997,14,10002,10002,14,10017,10017,14,10055,10055,14,10071,10071,14,10133,10135,14,10548,10549,14,11093,11093,14,12330,12333,5,12441,12442,5,42608,42610,5,43010,43010,5,43045,43046,5,43188,43203,7,43302,43309,5,43392,43394,5,43446,43449,5,43493,43493,5,43571,43572,7,43597,43597,7,43703,43704,5,43756,43757,5,44003,44004,7,44009,44010,7,44033,44059,12,44089,44115,12,44145,44171,12,44201,44227,12,44257,44283,12,44313,44339,12,44369,44395,12,44425,44451,12,44481,44507,12,44537,44563,12,44593,44619,12,44649,44675,12,44705,44731,12,44761,44787,12,44817,44843,12,44873,44899,12,44929,44955,12,44985,45011,12,45041,45067,12,45097,45123,12,45153,45179,12,45209,45235,12,45265,45291,12,45321,45347,12,45377,45403,12,45433,45459,12,45489,45515,12,45545,45571,12,45601,45627,12,45657,45683,12,45713,45739,12,45769,45795,12,45825,45851,12,45881,45907,12,45937,45963,12,45993,46019,12,46049,46075,12,46105,46131,12,46161,46187,12,46217,46243,12,46273,46299,12,46329,46355,12,46385,46411,12,46441,46467,12,46497,46523,12,46553,46579,12,46609,46635,12,46665,46691,12,46721,46747,12,46777,46803,12,46833,46859,12,46889,46915,12,46945,46971,12,47001,47027,12,47057,47083,12,47113,47139,12,47169,47195,12,47225,47251,12,47281,47307,12,47337,47363,12,47393,47419,12,47449,47475,12,47505,47531,12,47561,47587,12,47617,47643,12,47673,47699,12,47729,47755,12,47785,47811,12,47841,47867,12,47897,47923,12,47953,47979,12,48009,48035,12,48065,48091,12,48121,48147,12,48177,48203,12,48233,48259,12,48289,48315,12,48345,48371,12,48401,48427,12,48457,48483,12,48513,48539,12,48569,48595,12,48625,48651,12,48681,48707,12,48737,48763,12,48793,48819,12,48849,48875,12,48905,48931,12,48961,48987,12,49017,49043,12,49073,49099,12,49129,49155,12,49185,49211,12,49241,49267,12,49297,49323,12,49353,49379,12,49409,49435,12,49465,49491,12,49521,49547,12,49577,49603,12,49633,49659,12,49689,49715,12,49745,49771,12,49801,49827,12,49857,49883,12,49913,49939,12,49969,49995,12,50025,50051,12,50081,50107,12,50137,50163,12,50193,50219,12,50249,50275,12,50305,50331,12,50361,50387,12,50417,50443,12,50473,50499,12,50529,50555,12,50585,50611,12,50641,50667,12,50697,50723,12,50753,50779,12,50809,50835,12,50865,50891,12,50921,50947,12,50977,51003,12,51033,51059,12,51089,51115,12,51145,51171,12,51201,51227,12,51257,51283,12,51313,51339,12,51369,51395,12,51425,51451,12,51481,51507,12,51537,51563,12,51593,51619,12,51649,51675,12,51705,51731,12,51761,51787,12,51817,51843,12,51873,51899,12,51929,51955,12,51985,52011,12,52041,52067,12,52097,52123,12,52153,52179,12,52209,52235,12,52265,52291,12,52321,52347,12,52377,52403,12,52433,52459,12,52489,52515,12,52545,52571,12,52601,52627,12,52657,52683,12,52713,52739,12,52769,52795,12,52825,52851,12,52881,52907,12,52937,52963,12,52993,53019,12,53049,53075,12,53105,53131,12,53161,53187,12,53217,53243,12,53273,53299,12,53329,53355,12,53385,53411,12,53441,53467,12,53497,53523,12,53553,53579,12,53609,53635,12,53665,53691,12,53721,53747,12,53777,53803,12,53833,53859,12,53889,53915,12,53945,53971,12,54001,54027,12,54057,54083,12,54113,54139,12,54169,54195,12,54225,54251,12,54281,54307,12,54337,54363,12,54393,54419,12,54449,54475,12,54505,54531,12,54561,54587,12,54617,54643,12,54673,54699,12,54729,54755,12,54785,54811,12,54841,54867,12,54897,54923,12,54953,54979,12,55009,55035,12,55065,55091,12,55121,55147,12,55177,55203,12,65024,65039,5,65520,65528,4,66422,66426,5,68152,68154,5,69291,69292,5,69633,69633,5,69747,69748,5,69811,69814,5,69826,69826,5,69932,69932,7,70016,70017,5,70079,70080,7,70095,70095,5,70196,70196,5,70367,70367,5,70402,70403,7,70464,70464,5,70487,70487,5,70709,70711,7,70725,70725,7,70833,70834,7,70843,70844,7,70849,70849,7,71090,71093,5,71103,71104,5,71227,71228,7,71339,71339,5,71344,71349,5,71458,71461,5,71727,71735,5,71985,71989,7,71998,71998,5,72002,72002,7,72154,72155,5,72193,72202,5,72251,72254,5,72281,72283,5,72344,72345,5,72766,72766,7,72874,72880,5,72885,72886,5,73023,73029,5,73104,73105,5,73111,73111,5,92912,92916,5,94095,94098,5,113824,113827,4,119142,119142,7,119155,119162,4,119362,119364,5,121476,121476,5,122888,122904,5,123184,123190,5,125252,125258,5,127183,127183,14,127340,127343,14,127377,127386,14,127491,127503,14,127548,127551,14,127744,127756,14,127761,127761,14,127769,127769,14,127773,127774,14,127780,127788,14,127796,127797,14,127820,127823,14,127869,127869,14,127894,127895,14,127902,127903,14,127943,127943,14,127947,127950,14,127972,127972,14,127988,127988,14,127992,127994,14,128009,128011,14,128019,128019,14,128023,128041,14,128064,128064,14,128102,128107,14,128174,128181,14,128238,128238,14,128246,128247,14,128254,128254,14,128264,128264,14,128278,128299,14,128329,128330,14,128348,128359,14,128371,128377,14,128392,128393,14,128401,128404,14,128421,128421,14,128433,128434,14,128450,128452,14,128476,128478,14,128483,128483,14,128495,128495,14,128506,128506,14,128519,128520,14,128528,128528,14,128534,128534,14,128538,128538,14,128540,128542,14,128544,128549,14,128552,128555,14,128557,128557,14,128560,128563,14,128565,128565,14,128567,128576,14,128581,128591,14,128641,128642,14,128646,128646,14,128648,128648,14,128650,128651,14,128653,128653,14,128655,128655,14,128657,128659,14,128661,128661,14,128663,128663,14,128665,128666,14,128674,128674,14,128676,128677,14,128679,128685,14,128690,128690,14,128694,128694,14,128697,128702,14,128704,128704,14,128710,128714,14,128716,128716,14,128720,128720,14,128723,128724,14,128726,128727,14,128733,128735,14,128742,128744,14,128746,128746,14,128749,128751,14,128753,128754,14,128756,128758,14,128761,128761,14,128763,128764,14,128884,128895,14,128992,129003,14,129008,129008,14,129036,129039,14,129114,129119,14,129198,129279,14,129293,129295,14,129305,129310,14,129312,129319,14,129328,129328,14,129331,129338,14,129343,129343,14,129351,129355,14,129357,129359,14,129375,129387,14,129393,129393,14,129395,129398,14,129401,129401,14,129403,129403,14,129408,129412,14,129426,129431,14,129443,129444,14,129451,129453,14,129456,129465,14,129472,129472,14,129475,129482,14,129484,129484,14,129488,129510,14,129536,129647,14,129652,129652,14,129656,129658,14,129661,129663,14,129667,129670,14,129680,129685,14,129705,129708,14,129712,129718,14,129723,129727,14,129731,129733,14,129744,129750,14,129754,129759,14,129768,129775,14,129783,129791,14,917504,917504,4,917506,917535,4,917632,917759,4,918000,921599,4,0,9,4,11,12,4,14,31,4,169,169,14,174,174,14,1155,1159,5,1425,1469,5,1473,1474,5,1479,1479,5,1552,1562,5,1611,1631,5,1750,1756,5,1759,1764,5,1770,1773,5,1809,1809,5,1958,1968,5,2045,2045,5,2075,2083,5,2089,2093,5,2192,2193,1,2250,2273,5,2275,2306,5,2362,2362,5,2364,2364,5,2369,2376,5,2381,2381,5,2385,2391,5,2433,2433,5,2492,2492,5,2495,2496,7,2503,2504,7,2509,2509,5,2530,2531,5,2561,2562,5,2620,2620,5,2625,2626,5,2635,2637,5,2672,2673,5,2689,2690,5,2748,2748,5,2753,2757,5,2761,2761,7,2765,2765,5,2810,2815,5,2818,2819,7,2878,2878,5,2880,2880,7,2887,2888,7,2893,2893,5,2903,2903,5,2946,2946,5,3007,3007,7,3009,3010,7,3018,3020,7,3031,3031,5,3073,3075,7,3132,3132,5,3137,3140,7,3146,3149,5,3170,3171,5,3202,3203,7,3262,3262,7,3264,3265,7,3267,3268,7,3271,3272,7,3276,3277,5,3298,3299,5,3330,3331,7,3390,3390,5,3393,3396,5,3402,3404,7,3406,3406,1,3426,3427,5,3458,3459,7,3535,3535,5,3538,3540,5,3544,3550,7,3570,3571,7,3635,3635,7,3655,3662,5,3763,3763,7,3784,3789,5,3893,3893,5,3897,3897,5,3953,3966,5,3968,3972,5,3981,3991,5,4038,4038,5,4145,4145,7,4153,4154,5,4157,4158,5,4184,4185,5,4209,4212,5,4228,4228,7,4237,4237,5,4352,4447,8,4520,4607,10,5906,5908,5,5938,5939,5,5970,5971,5,6068,6069,5,6071,6077,5,6086,6086,5,6089,6099,5,6155,6157,5,6159,6159,5,6313,6313,5,6435,6438,7,6441,6443,7,6450,6450,5,6457,6459,5,6681,6682,7,6741,6741,7,6743,6743,7,6752,6752,5,6757,6764,5,6771,6780,5,6832,6845,5,6847,6862,5,6916,6916,7,6965,6965,5,6971,6971,7,6973,6977,7,6979,6980,7,7040,7041,5,7073,7073,7,7078,7079,7,7082,7082,7,7142,7142,5,7144,7145,5,7149,7149,5,7151,7153,5,7204,7211,7,7220,7221,7,7376,7378,5,7393,7393,7,7405,7405,5,7415,7415,7,7616,7679,5,8204,8204,5,8206,8207,4,8233,8233,4,8252,8252,14,8288,8292,4,8294,8303,4,8413,8416,5,8418,8420,5,8482,8482,14,8596,8601,14,8986,8987,14,9096,9096,14,9193,9196,14,9199,9199,14,9201,9202,14,9208,9210,14,9642,9643,14,9664,9664,14,9728,9729,14,9732,9732,14,9735,9741,14,9743,9744,14,9746,9746,14,9750,9751,14,9753,9756,14,9758,9759,14,9761,9761,14,9764,9765,14,9767,9769,14,9771,9773,14,9775,9775,14,9784,9785,14,9787,9791,14,9793,9793,14,9795,9799,14,9812,9822,14,9824,9824,14,9827,9827,14,9829,9830,14,9832,9832,14,9851,9851,14,9854,9854,14,9856,9861,14,9874,9874,14,9876,9876,14,9878,9879,14,9881,9881,14,9883,9884,14,9888,9889,14,9895,9895,14,9898,9899,14,9904,9905,14,9917,9918,14,9924,9925,14,9928,9928,14,9934,9934,14,9936,9936,14,9938,9938,14,9940,9940,14,9961,9961,14,9963,9967,14,9970,9971,14,9973,9973,14,9975,9977,14,9979,9980,14,9982,9985,14,9987,9988,14,9992,9996,14,9998,9998,14,10000,10001,14,10004,10004,14,10013,10013,14,10024,10024,14,10052,10052,14,10060,10060,14,10067,10069,14,10083,10083,14,10085,10087,14,10145,10145,14,10175,10175,14,11013,11015,14,11088,11088,14,11503,11505,5,11744,11775,5,12334,12335,5,12349,12349,14,12951,12951,14,42607,42607,5,42612,42621,5,42736,42737,5,43014,43014,5,43043,43044,7,43047,43047,7,43136,43137,7,43204,43205,5,43263,43263,5,43335,43345,5,43360,43388,8,43395,43395,7,43444,43445,7,43450,43451,7,43454,43456,7,43561,43566,5,43569,43570,5,43573,43574,5,43596,43596,5,43644,43644,5,43698,43700,5,43710,43711,5,43755,43755,7,43758,43759,7,43766,43766,5,44005,44005,5,44008,44008,5,44012,44012,7,44032,44032,11,44060,44060,11,44088,44088,11,44116,44116,11,44144,44144,11,44172,44172,11,44200,44200,11,44228,44228,11,44256,44256,11,44284,44284,11,44312,44312,11,44340,44340,11,44368,44368,11,44396,44396,11,44424,44424,11,44452,44452,11,44480,44480,11,44508,44508,11,44536,44536,11,44564,44564,11,44592,44592,11,44620,44620,11,44648,44648,11,44676,44676,11,44704,44704,11,44732,44732,11,44760,44760,11,44788,44788,11,44816,44816,11,44844,44844,11,44872,44872,11,44900,44900,11,44928,44928,11,44956,44956,11,44984,44984,11,45012,45012,11,45040,45040,11,45068,45068,11,45096,45096,11,45124,45124,11,45152,45152,11,45180,45180,11,45208,45208,11,45236,45236,11,45264,45264,11,45292,45292,11,45320,45320,11,45348,45348,11,45376,45376,11,45404,45404,11,45432,45432,11,45460,45460,11,45488,45488,11,45516,45516,11,45544,45544,11,45572,45572,11,45600,45600,11,45628,45628,11,45656,45656,11,45684,45684,11,45712,45712,11,45740,45740,11,45768,45768,11,45796,45796,11,45824,45824,11,45852,45852,11,45880,45880,11,45908,45908,11,45936,45936,11,45964,45964,11,45992,45992,11,46020,46020,11,46048,46048,11,46076,46076,11,46104,46104,11,46132,46132,11,46160,46160,11,46188,46188,11,46216,46216,11,46244,46244,11,46272,46272,11,46300,46300,11,46328,46328,11,46356,46356,11,46384,46384,11,46412,46412,11,46440,46440,11,46468,46468,11,46496,46496,11,46524,46524,11,46552,46552,11,46580,46580,11,46608,46608,11,46636,46636,11,46664,46664,11,46692,46692,11,46720,46720,11,46748,46748,11,46776,46776,11,46804,46804,11,46832,46832,11,46860,46860,11,46888,46888,11,46916,46916,11,46944,46944,11,46972,46972,11,47000,47000,11,47028,47028,11,47056,47056,11,47084,47084,11,47112,47112,11,47140,47140,11,47168,47168,11,47196,47196,11,47224,47224,11,47252,47252,11,47280,47280,11,47308,47308,11,47336,47336,11,47364,47364,11,47392,47392,11,47420,47420,11,47448,47448,11,47476,47476,11,47504,47504,11,47532,47532,11,47560,47560,11,47588,47588,11,47616,47616,11,47644,47644,11,47672,47672,11,47700,47700,11,47728,47728,11,47756,47756,11,47784,47784,11,47812,47812,11,47840,47840,11,47868,47868,11,47896,47896,11,47924,47924,11,47952,47952,11,47980,47980,11,48008,48008,11,48036,48036,11,48064,48064,11,48092,48092,11,48120,48120,11,48148,48148,11,48176,48176,11,48204,48204,11,48232,48232,11,48260,48260,11,48288,48288,11,48316,48316,11,48344,48344,11,48372,48372,11,48400,48400,11,48428,48428,11,48456,48456,11,48484,48484,11,48512,48512,11,48540,48540,11,48568,48568,11,48596,48596,11,48624,48624,11,48652,48652,11,48680,48680,11,48708,48708,11,48736,48736,11,48764,48764,11,48792,48792,11,48820,48820,11,48848,48848,11,48876,48876,11,48904,48904,11,48932,48932,11,48960,48960,11,48988,48988,11,49016,49016,11,49044,49044,11,49072,49072,11,49100,49100,11,49128,49128,11,49156,49156,11,49184,49184,11,49212,49212,11,49240,49240,11,49268,49268,11,49296,49296,11,49324,49324,11,49352,49352,11,49380,49380,11,49408,49408,11,49436,49436,11,49464,49464,11,49492,49492,11,49520,49520,11,49548,49548,11,49576,49576,11,49604,49604,11,49632,49632,11,49660,49660,11,49688,49688,11,49716,49716,11,49744,49744,11,49772,49772,11,49800,49800,11,49828,49828,11,49856,49856,11,49884,49884,11,49912,49912,11,49940,49940,11,49968,49968,11,49996,49996,11,50024,50024,11,50052,50052,11,50080,50080,11,50108,50108,11,50136,50136,11,50164,50164,11,50192,50192,11,50220,50220,11,50248,50248,11,50276,50276,11,50304,50304,11,50332,50332,11,50360,50360,11,50388,50388,11,50416,50416,11,50444,50444,11,50472,50472,11,50500,50500,11,50528,50528,11,50556,50556,11,50584,50584,11,50612,50612,11,50640,50640,11,50668,50668,11,50696,50696,11,50724,50724,11,50752,50752,11,50780,50780,11,50808,50808,11,50836,50836,11,50864,50864,11,50892,50892,11,50920,50920,11,50948,50948,11,50976,50976,11,51004,51004,11,51032,51032,11,51060,51060,11,51088,51088,11,51116,51116,11,51144,51144,11,51172,51172,11,51200,51200,11,51228,51228,11,51256,51256,11,51284,51284,11,51312,51312,11,51340,51340,11,51368,51368,11,51396,51396,11,51424,51424,11,51452,51452,11,51480,51480,11,51508,51508,11,51536,51536,11,51564,51564,11,51592,51592,11,51620,51620,11,51648,51648,11,51676,51676,11,51704,51704,11,51732,51732,11,51760,51760,11,51788,51788,11,51816,51816,11,51844,51844,11,51872,51872,11,51900,51900,11,51928,51928,11,51956,51956,11,51984,51984,11,52012,52012,11,52040,52040,11,52068,52068,11,52096,52096,11,52124,52124,11,52152,52152,11,52180,52180,11,52208,52208,11,52236,52236,11,52264,52264,11,52292,52292,11,52320,52320,11,52348,52348,11,52376,52376,11,52404,52404,11,52432,52432,11,52460,52460,11,52488,52488,11,52516,52516,11,52544,52544,11,52572,52572,11,52600,52600,11,52628,52628,11,52656,52656,11,52684,52684,11,52712,52712,11,52740,52740,11,52768,52768,11,52796,52796,11,52824,52824,11,52852,52852,11,52880,52880,11,52908,52908,11,52936,52936,11,52964,52964,11,52992,52992,11,53020,53020,11,53048,53048,11,53076,53076,11,53104,53104,11,53132,53132,11,53160,53160,11,53188,53188,11,53216,53216,11,53244,53244,11,53272,53272,11,53300,53300,11,53328,53328,11,53356,53356,11,53384,53384,11,53412,53412,11,53440,53440,11,53468,53468,11,53496,53496,11,53524,53524,11,53552,53552,11,53580,53580,11,53608,53608,11,53636,53636,11,53664,53664,11,53692,53692,11,53720,53720,11,53748,53748,11,53776,53776,11,53804,53804,11,53832,53832,11,53860,53860,11,53888,53888,11,53916,53916,11,53944,53944,11,53972,53972,11,54000,54000,11,54028,54028,11,54056,54056,11,54084,54084,11,54112,54112,11,54140,54140,11,54168,54168,11,54196,54196,11,54224,54224,11,54252,54252,11,54280,54280,11,54308,54308,11,54336,54336,11,54364,54364,11,54392,54392,11,54420,54420,11,54448,54448,11,54476,54476,11,54504,54504,11,54532,54532,11,54560,54560,11,54588,54588,11,54616,54616,11,54644,54644,11,54672,54672,11,54700,54700,11,54728,54728,11,54756,54756,11,54784,54784,11,54812,54812,11,54840,54840,11,54868,54868,11,54896,54896,11,54924,54924,11,54952,54952,11,54980,54980,11,55008,55008,11,55036,55036,11,55064,55064,11,55092,55092,11,55120,55120,11,55148,55148,11,55176,55176,11,55216,55238,9,64286,64286,5,65056,65071,5,65438,65439,5,65529,65531,4,66272,66272,5,68097,68099,5,68108,68111,5,68159,68159,5,68900,68903,5,69446,69456,5,69632,69632,7,69634,69634,7,69744,69744,5,69759,69761,5,69808,69810,7,69815,69816,7,69821,69821,1,69837,69837,1,69927,69931,5,69933,69940,5,70003,70003,5,70018,70018,7,70070,70078,5,70082,70083,1,70094,70094,7,70188,70190,7,70194,70195,7,70197,70197,7,70206,70206,5,70368,70370,7,70400,70401,5,70459,70460,5,70463,70463,7,70465,70468,7,70475,70477,7,70498,70499,7,70512,70516,5,70712,70719,5,70722,70724,5,70726,70726,5,70832,70832,5,70835,70840,5,70842,70842,5,70845,70845,5,70847,70848,5,70850,70851,5,71088,71089,7,71096,71099,7,71102,71102,7,71132,71133,5,71219,71226,5,71229,71229,5,71231,71232,5,71340,71340,7,71342,71343,7,71350,71350,7,71453,71455,5,71462,71462,7,71724,71726,7,71736,71736,7,71984,71984,5,71991,71992,7,71997,71997,7,71999,71999,1,72001,72001,1,72003,72003,5,72148,72151,5,72156,72159,7,72164,72164,7,72243,72248,5,72250,72250,1,72263,72263,5,72279,72280,7,72324,72329,1,72343,72343,7,72751,72751,7,72760,72765,5,72767,72767,5,72873,72873,7,72881,72881,7,72884,72884,7,73009,73014,5,73020,73021,5,73030,73030,1,73098,73102,7,73107,73108,7,73110,73110,7,73459,73460,5,78896,78904,4,92976,92982,5,94033,94087,7,94180,94180,5,113821,113822,5,118528,118573,5,119141,119141,5,119143,119145,5,119150,119154,5,119163,119170,5,119210,119213,5,121344,121398,5,121461,121461,5,121499,121503,5,122880,122886,5,122907,122913,5,122918,122922,5,123566,123566,5,125136,125142,5,126976,126979,14,126981,127182,14,127184,127231,14,127279,127279,14,127344,127345,14,127374,127374,14,127405,127461,14,127489,127490,14,127514,127514,14,127538,127546,14,127561,127567,14,127570,127743,14,127757,127758,14,127760,127760,14,127762,127762,14,127766,127768,14,127770,127770,14,127772,127772,14,127775,127776,14,127778,127779,14,127789,127791,14,127794,127795,14,127798,127798,14,127819,127819,14,127824,127824,14,127868,127868,14,127870,127871,14,127892,127893,14,127896,127896,14,127900,127901,14,127904,127940,14,127942,127942,14,127944,127944,14,127946,127946,14,127951,127955,14,127968,127971,14,127973,127984,14,127987,127987,14,127989,127989,14,127991,127991,14,127995,127999,5,128008,128008,14,128012,128014,14,128017,128018,14,128020,128020,14,128022,128022,14,128042,128042,14,128063,128063,14,128065,128065,14,128101,128101,14,128108,128109,14,128173,128173,14,128182,128183,14,128236,128237,14,128239,128239,14,128245,128245,14,128248,128248,14,128253,128253,14,128255,128258,14,128260,128263,14,128265,128265,14,128277,128277,14,128300,128301,14,128326,128328,14,128331,128334,14,128336,128347,14,128360,128366,14,128369,128370,14,128378,128378,14,128391,128391,14,128394,128397,14,128400,128400,14,128405,128406,14,128420,128420,14,128422,128423,14,128425,128432,14,128435,128443,14,128445,128449,14,128453,128464,14,128468,128475,14,128479,128480,14,128482,128482,14,128484,128487,14,128489,128494,14,128496,128498,14,128500,128505,14,128507,128511,14,128513,128518,14,128521,128525,14,128527,128527,14,128529,128529,14,128533,128533,14,128535,128535,14,128537,128537,14]');
1152 }
1153 > strings.ts
1154 > //#endregion
1155 >
1156 > /**
1157 > * Computes the offset after performing a left delete on the given string,
1158 > * while considering unicode grapheme/emoji rules.
1159 > */
1160 > export function getLeftDeleteOffset(offset: number, str: string): number {
1161 if (offset === 0) {
1162 return 0;
1174 return iterator.offset;
1175 }
1176 > strings.ts
1177 function getOffsetBeforeLastEmojiComponent(initialOffset: number, str: string): number | undefined {
1178 // See https://www.unicode.org/reports/tr51/tr51-14.html#EBNF_and_Regex for the
1210 return resultOffset;
1211 }
1212 > strings.ts
1213 function isEmojiModifier(codePoint: number): boolean {
1214 return 0x1F3FB <= codePoint && codePoint <= 0x1F3FF;
1215 }
1216 > strings.ts
1217 > const enum CodePoint {
1218 > zwj = 0x200D,
1219 >
1220 > /**
1221 > * Variation Selector-16 (VS16)
1222 > */
1223 > emojiVariantSelector = 0xFE0F,
1224 >
1225 > /**
1226 > * Combining Enclosing Keycap
1227 > */
1228 > enclosingKeyCap = 0x20E3,
1229 >
1230 > space = 0x0020,
1231 > }
1232 >
1233 > export const noBreakWhitespace = '\xa0';
1234 >
1235 > export class AmbiguousCharacters {
1236 > private static readonly ambiguousCharacterData = new Lazy<
1237 > Record<
1238 > string | '_common' | '_default',
1239 > /* code point -> ascii code point */ number[]
1240 > >
1241 > >(() => {
1242 // Generated using https://github.com/hediet/vscode-unicode-data
1243 // Stored as key1, value1, key2, value2, ...
1245 '{\"_common\":[8232,32,8233,32,5760,32,8192,32,8193,32,8194,32,8195,32,8196,32,8197,32,8198,32,8200,32,8201,32,8202,32,8287,32,8199,32,8239,32,2042,95,65101,95,65102,95,65103,95,8208,45,8209,45,8210,45,65112,45,1748,45,8259,45,727,45,8722,45,10134,45,11450,45,1549,44,1643,44,184,44,42233,44,894,59,2307,58,2691,58,1417,58,1795,58,1796,58,5868,58,65072,58,6147,58,6153,58,8282,58,1475,58,760,58,42889,58,8758,58,720,58,42237,58,451,33,11601,33,660,63,577,63,2429,63,5038,63,42731,63,119149,46,8228,46,1793,46,1794,46,42510,46,68176,46,1632,46,1776,46,42232,46,1373,96,65287,96,8219,96,1523,96,8242,96,1370,96,8175,96,65344,96,900,96,8189,96,8125,96,8127,96,8190,96,697,96,884,96,712,96,714,96,715,96,756,96,699,96,701,96,700,96,702,96,42892,96,1497,96,2036,96,2037,96,5194,96,5836,96,94033,96,94034,96,65339,91,10088,40,10098,40,12308,40,64830,40,65341,93,10089,41,10099,41,12309,41,64831,41,10100,123,119060,123,10101,125,65342,94,8270,42,1645,42,8727,42,66335,42,5941,47,8257,47,8725,47,8260,47,9585,47,10187,47,10744,47,119354,47,12755,47,12339,47,11462,47,20031,47,12035,47,65340,92,65128,92,8726,92,10189,92,10741,92,10745,92,119311,92,119355,92,12756,92,20022,92,12034,92,42872,38,708,94,710,94,5869,43,10133,43,66203,43,8249,60,10094,60,706,60,119350,60,5176,60,5810,60,5120,61,11840,61,12448,61,42239,61,8250,62,10095,62,707,62,119351,62,5171,62,94015,62,8275,126,732,126,8128,126,8764,126,65372,124,65293,45,118002,50,120784,50,120794,50,120804,50,120814,50,120824,50,130034,50,42842,50,423,50,1000,50,42564,50,5311,50,42735,50,119302,51,118003,51,120785,51,120795,51,120805,51,120815,51,120825,51,130035,51,42923,51,540,51,439,51,42858,51,11468,51,1248,51,94011,51,71882,51,118004,52,120786,52,120796,52,120806,52,120816,52,120826,52,130036,52,5070,52,71855,52,118005,53,120787,53,120797,53,120807,53,120817,53,120827,53,130037,53,444,53,71867,53,118006,54,120788,54,120798,54,120808,54,120818,54,120828,54,130038,54,11474,54,5102,54,71893,54,119314,55,118007,55,120789,55,120799,55,120809,55,120819,55,120829,55,130039,55,66770,55,71878,55,2819,56,2538,56,2666,56,125131,56,118008,56,120790,56,120800,56,120810,56,120820,56,120830,56,130040,56,547,56,546,56,66330,56,2663,57,2920,57,2541,57,3437,57,118009,57,120791,57,120801,57,120811,57,120821,57,120831,57,130041,57,42862,57,11466,57,71884,57,71852,57,71894,57,9082,97,65345,97,119834,97,119886,97,119938,97,119990,97,120042,97,120094,97,120146,97,120198,97,120250,97,120302,97,120354,97,120406,97,120458,97,593,97,945,97,120514,97,120572,97,120630,97,120688,97,120746,97,65313,65,117974,65,119808,65,119860,65,119912,65,119964,65,120016,65,120068,65,120120,65,120172,65,120224,65,120276,65,120328,65,120380,65,120432,65,913,65,120488,65,120546,65,120604,65,120662,65,120720,65,5034,65,5573,65,42222,65,94016,65,66208,65,119835,98,119887,98,119939,98,119991,98,120043,98,120095,98,120147,98,120199,98,120251,98,120303,98,120355,98,120407,98,120459,98,388,98,5071,98,5234,98,5551,98,65314,66,8492,66,117975,66,119809,66,119861,66,119913,66,120017,66,120069,66,120121,66,120173,66,120225,66,120277,66,120329,66,120381,66,120433,66,42932,66,914,66,120489,66,120547,66,120605,66,120663,66,120721,66,5108,66,5623,66,42192,66,66178,66,66209,66,66305,66,65347,99,8573,99,119836,99,119888,99,119940,99,119992,99,120044,99,120096,99,120148,99,120200,99,120252,99,120304,99,120356,99,120408,99,120460,99,7428,99,1010,99,11429,99,43951,99,66621,99,128844,67,71913,67,71922,67,65315,67,8557,67,8450,67,8493,67,117976,67,119810,67,119862,67,119914,67,119966,67,120018,67,120174,67,120226,67,120278,67,120330,67,120382,67,120434,67,1017,67,11428,67,5087,67,42202,67,66210,67,66306,67,66581,67,66844,67,8574,100,8518,100,119837,100,119889,100,119941,100,119993,100,120045,100,120097,100,120149,100,120201,100,120253,100,120305,100,120357,100,120409,100,120461,100,1281,100,5095,100,5231,100,42194,100,8558,68,8517,68,117977,68,119811,68,119863,68,119915,68,119967,68,120019,68,120071,68,120123,68,120175,68,120227,68,120279,68,120331,68,120383,68,120435,68,5024,68,5598,68,5610,68,42195,68,8494,101,65349,101,8495,101,8519,101,119838,101,119890,101,119942,101,120046,101,120098,101,120150,101,120202,101,120254,101,120306,101,120358,101,120410,101,120462,101,43826,101,1213,101,8959,69,65317,69,8496,69,117978,69,119812,69,119864,69,119916,69,120020,69,120072,69,120124,69,120176,69,120228,69,120280,69,120332,69,120384,69,120436,69,917,69,120492,69,120550,69,120608,69,120666,69,120724,69,11577,69,5036,69,42224,69,71846,69,71854,69,66182,69,119839,102,119891,102,119943,102,119995,102,120047,102,120099,102,120151,102,120203,102,120255,102,120307,102,120359,102,120411,102,120463,102,43829,102,42905,102,383,102,7837,102,1412,102,119315,70,8497,70,117979,70,119813,70,119865,70,119917,70,120021,70,120073,70,120125,70,120177,70,120229,70,120281,70,120333,70,120385,70,120437,70,42904,70,988,70,120778,70,5556,70,42205,70,71874,70,71842,70,66183,70,66213,70,66853,70,65351,103,8458,103,119840,103,119892,103,119944,103,120048,103,120100,103,120152,103,120204,103,120256,103,120308,103,120360,103,120412,103,120464,103,609,103,7555,103,397,103,1409,103,117980,71,119814,71,119866,71,119918,71,119970,71,120022,71,120074,71,120126,71,120178,71,120230,71,120282,71,120334,71,120386,71,120438,71,1292,71,5056,71,5107,71,42198,71,65352,104,8462,104,119841,104,119945,104,119997,104,120049,104,120101,104,120153,104,120205,104,120257,104,120309,104,120361,104,120413,104,120465,104,1211,104,1392,104,5058,104,65320,72,8459,72,8460,72,8461,72,117981,72,119815,72,119867,72,119919,72,120023,72,120179,72,120231,72,120283,72,120335,72,120387,72,120439,72,919,72,120494,72,120552,72,120610,72,120668,72,120726,72,11406,72,5051,72,5500,72,42215,72,66255,72,731,105,9075,105,65353,105,8560,105,8505,105,8520,105,119842,105,119894,105,119946,105,119998,105,120050,105,120102,105,120154,105,120206,105,120258,105,120310,105,120362,105,120414,105,120466,105,120484,105,618,105,617,105,953,105,8126,105,890,105,120522,105,120580,105,120638,105,120696,105,120754,105,1110,105,42567,105,1231,105,43893,105,5029,105,71875,105,65354,106,8521,106,119843,106,119895,106,119947,106,119999,106,120051,106,120103,106,120155,106,120207,106,120259,106,120311,106,120363,106,120415,106,120467,106,1011,106,1112,106,65322,74,117983,74,119817,74,119869,74,119921,74,119973,74,120025,74,120077,74,120129,74,120181,74,120233,74,120285,74,120337,74,120389,74,120441,74,42930,74,895,74,1032,74,5035,74,5261,74,42201,74,119844,107,119896,107,119948,107,120000,107,120052,107,120104,107,120156,107,120208,107,120260,107,120312,107,120364,107,120416,107,120468,107,8490,75,65323,75,117984,75,119818,75,119870,75,119922,75,119974,75,120026,75,120078,75,120130,75,120182,75,120234,75,120286,75,120338,75,120390,75,120442,75,922,75,120497,75,120555,75,120613,75,120671,75,120729,75,11412,75,5094,75,5845,75,42199,75,66840,75,1472,108,8739,73,9213,73,65512,73,1633,108,1777,73,66336,108,125127,108,118001,108,120783,73,120793,73,120803,73,120813,73,120823,73,130033,73,65321,73,8544,73,8464,73,8465,73,117982,108,119816,73,119868,73,119920,73,120024,73,120128,73,120180,73,120232,73,120284,73,120336,73,120388,73,120440,73,65356,108,8572,73,8467,108,119845,108,119897,108,119949,108,120001,108,120053,108,120105,73,120157,73,120209,73,120261,73,120313,73,120365,73,120417,73,120469,73,448,73,120496,73,120554,73,120612,73,120670,73,120728,73,11410,73,1030,73,1216,73,1493,108,1503,108,1575,108,126464,108,126592,108,65166,108,65165,108,1994,108,11599,73,5825,73,42226,73,93992,73,66186,124,66313,124,119338,76,8556,76,8466,76,117985,76,119819,76,119871,76,119923,76,120027,76,120079,76,120131,76,120183,76,120235,76,120287,76,120339,76,120391,76,120443,76,11472,76,5086,76,5290,76,42209,76,93974,76,71843,76,71858,76,66587,76,66854,76,65325,77,8559,77,8499,77,117986,77,119820,77,119872,77,119924,77,120028,77,120080,77,120132,77,120184,77,120236,77,120288,77,120340,77,120392,77,120444,77,924,77,120499,77,120557,77,120615,77,120673,77,120731,77,1018,77,11416,77,5047,77,5616,77,5846,77,42207,77,66224,77,66321,77,119847,110,119899,110,119951,110,120003,110,120055,110,120107,110,120159,110,120211,110,120263,110,120315,110,120367,110,120419,110,120471,110,1400,110,1404,110,65326,78,8469,78,117987,78,119821,78,119873,78,119925,78,119977,78,120029,78,120081,78,120185,78,120237,78,120289,78,120341,78,120393,78,120445,78,925,78,120500,78,120558,78,120616,78,120674,78,120732,78,11418,78,42208,78,66835,78,3074,111,3202,111,3330,111,3458,111,2406,111,2662,111,2790,111,3046,111,3174,111,3302,111,3430,111,3664,111,3792,111,4160,111,1637,111,1781,111,65359,111,8500,111,119848,111,119900,111,119952,111,120056,111,120108,111,120160,111,120212,111,120264,111,120316,111,120368,111,120420,111,120472,111,7439,111,7441,111,43837,111,959,111,120528,111,120586,111,120644,111,120702,111,120760,111,963,111,120532,111,120590,111,120648,111,120706,111,120764,111,11423,111,4351,111,1413,111,1505,111,1607,111,126500,111,126564,111,126596,111,65259,111,65260,111,65258,111,65257,111,1726,111,64428,111,64429,111,64427,111,64426,111,1729,111,64424,111,64425,111,64423,111,64422,111,1749,111,3360,111,4125,111,66794,111,71880,111,71895,111,66604,111,1984,79,2534,79,2918,79,12295,79,70864,79,71904,79,118000,79,120782,79,120792,79,120802,79,120812,79,120822,79,130032,79,65327,79,117988,79,119822,79,119874,79,119926,79,119978,79,120030,79,120082,79,120134,79,120186,79,120238,79,120290,79,120342,79,120394,79,120446,79,927,79,120502,79,120560,79,120618,79,120676,79,120734,79,11422,79,1365,79,11604,79,4816,79,2848,79,66754,79,42227,79,71861,79,66194,79,66219,79,66564,79,66838,79,9076,112,65360,112,119849,112,119901,112,119953,112,120005,112,120057,112,120109,112,120161,112,120213,112,120265,112,120317,112,120369,112,120421,112,120473,112,961,112,120530,112,120544,112,120588,112,120602,112,120646,112,120660,112,120704,112,120718,112,120762,112,120776,112,11427,112,65328,80,8473,80,117989,80,119823,80,119875,80,119927,80,119979,80,120031,80,120083,80,120187,80,120239,80,120291,80,120343,80,120395,80,120447,80,929,80,120504,80,120562,80,120620,80,120678,80,120736,80,11426,80,5090,80,5229,80,42193,80,66197,80,119850,113,119902,113,119954,113,120006,113,120058,113,120110,113,120162,113,120214,113,120266,113,120318,113,120370,113,120422,113,120474,113,1307,113,1379,113,1382,113,8474,81,117990,81,119824,81,119876,81,119928,81,119980,81,120032,81,120084,81,120188,81,120240,81,120292,81,120344,81,120396,81,120448,81,11605,81,119851,114,119903,114,119955,114,120007,114,120059,114,120111,114,120163,114,120215,114,120267,114,120319,114,120371,114,120423,114,120475,114,43847,114,43848,114,7462,114,11397,114,43905,114,119318,82,8475,82,8476,82,8477,82,117991,82,119825,82,119877,82,119929,82,120033,82,120189,82,120241,82,120293,82,120345,82,120397,82,120449,82,422,82,5025,82,5074,82,66740,82,5511,82,42211,82,94005,82,65363,115,119852,115,119904,115,119956,115,120008,115,120060,115,120112,115,120164,115,120216,115,120268,115,120320,115,120372,115,120424,115,120476,115,42801,115,445,115,1109,115,43946,115,71873,115,66632,115,65331,83,117992,83,119826,83,119878,83,119930,83,119982,83,120034,83,120086,83,120138,83,120190,83,120242,83,120294,83,120346,83,120398,83,120450,83,1029,83,1359,83,5077,83,5082,83,42210,83,94010,83,66198,83,66592,83,119853,116,119905,116,119957,116,120009,116,120061,116,120113,116,120165,116,120217,116,120269,116,120321,116,120373,116,120425,116,120477,116,8868,84,10201,84,128872,84,65332,84,117993,84,119827,84,119879,84,119931,84,119983,84,120035,84,120087,84,120139,84,120191,84,120243,84,120295,84,120347,84,120399,84,120451,84,932,84,120507,84,120565,84,120623,84,120681,84,120739,84,11430,84,5026,84,42196,84,93962,84,71868,84,66199,84,66225,84,66325,84,119854,117,119906,117,119958,117,120010,117,120062,117,120114,117,120166,117,120218,117,120270,117,120322,117,120374,117,120426,117,120478,117,42911,117,7452,117,43854,117,43858,117,651,117,965,117,120534,117,120592,117,120650,117,120708,117,120766,117,1405,117,66806,117,71896,117,8746,85,8899,85,117994,85,119828,85,119880,85,119932,85,119984,85,120036,85,120088,85,120140,85,120192,85,120244,85,120296,85,120348,85,120400,85,120452,85,1357,85,4608,85,66766,85,5196,85,42228,85,94018,85,71864,85,8744,118,8897,118,65366,118,8564,118,119855,118,119907,118,119959,118,120011,118,120063,118,120115,118,120167,118,120219,118,120271,118,120323,118,120375,118,120427,118,120479,118,7456,118,957,118,120526,118,120584,118,120642,118,120700,118,120758,118,1141,118,1496,118,71430,118,43945,118,71872,118,119309,86,1639,86,1783,86,8548,86,117995,86,119829,86,119881,86,119933,86,119985,86,120037,86,120089,86,120141,86,120193,86,120245,86,120297,86,120349,86,120401,86,120453,86,1140,86,11576,86,5081,86,5167,86,42719,86,42214,86,93960,86,71840,86,66845,86,623,119,119856,119,119908,119,119960,119,120012,119,120064,119,120116,119,120168,119,120220,119,120272,119,120324,119,120376,119,120428,119,120480,119,7457,119,1121,119,1309,119,1377,119,71434,119,71438,119,71439,119,43907,119,71910,87,71919,87,117996,87,119830,87,119882,87,119934,87,119986,87,120038,87,120090,87,120142,87,120194,87,120246,87,120298,87,120350,87,120402,87,120454,87,1308,87,5043,87,5076,87,42218,87,5742,120,10539,120,10540,120,10799,120,65368,120,8569,120,119857,120,119909,120,119961,120,120013,120,120065,120,120117,120,120169,120,120221,120,120273,120,120325,120,120377,120,120429,120,120481,120,5441,120,5501,120,5741,88,9587,88,66338,88,71916,88,65336,88,8553,88,117997,88,119831,88,119883,88,119935,88,119987,88,120039,88,120091,88,120143,88,120195,88,120247,88,120299,88,120351,88,120403,88,120455,88,42931,88,935,88,120510,88,120568,88,120626,88,120684,88,120742,88,11436,88,11613,88,5815,88,42219,88,66192,88,66228,88,66327,88,66855,88,611,121,7564,121,65369,121,119858,121,119910,121,119962,121,120014,121,120066,121,120118,121,120170,121,120222,121,120274,121,120326,121,120378,121,120430,121,120482,121,655,121,7935,121,43866,121,947,121,8509,121,120516,121,120574,121,120632,121,120690,121,120748,121,1199,121,4327,121,71900,121,65337,89,117998,89,119832,89,119884,89,119936,89,119988,89,120040,89,120092,89,120144,89,120196,89,120248,89,120300,89,120352,89,120404,89,120456,89,933,89,978,89,120508,89,120566,89,120624,89,120682,89,120740,89,11432,89,1198,89,5033,89,5053,89,42220,89,94019,89,71844,89,66226,89,119859,122,119911,122,119963,122,120015,122,120067,122,120119,122,120171,122,120223,122,120275,122,120327,122,120379,122,120431,122,120483,122,7458,122,43923,122,71876,122,71909,90,66293,90,65338,90,8484,90,8488,90,117999,90,119833,90,119885,90,119937,90,119989,90,120041,90,120197,90,120249,90,120301,90,120353,90,120405,90,120457,90,918,90,120493,90,120551,90,120609,90,120667,90,120725,90,5059,90,42204,90,71849,90,65282,34,65283,35,65284,36,65285,37,65286,38,65290,42,65291,43,65294,46,65295,47,65296,48,65298,50,65299,51,65300,52,65301,53,65302,54,65303,55,65304,56,65305,57,65308,60,65309,61,65310,62,65312,64,65316,68,65318,70,65319,71,65324,76,65329,81,65330,82,65333,85,65334,86,65335,87,65343,95,65346,98,65348,100,65350,102,65355,107,65357,109,65358,110,65361,113,65362,114,65364,116,65365,117,65367,119,65370,122,65371,123,65373,125,119846,109],\"_default\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"cs\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"de\":[65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"es\":[8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"fr\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"it\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"ja\":[8211,45,8218,44,65281,33,8216,96,8245,96,180,96,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65292,44,65297,49,65307,59],\"ko\":[8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"pl\":[65374,126,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"pt-BR\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"qps-ploc\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"ru\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,305,105,921,73,1009,112,215,120,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"tr\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"zh-hans\":[160,32,65374,126,8218,44,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65297,49],\"zh-hant\":[8211,45,65374,126,8218,44,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89]}'
1246 );
1247 > }); strings.ts
1248 >
1249 > private static readonly cache = new LRUCachedFunction<string, AmbiguousCharacters>((localesStr) => {
1250 const locales = localesStr.split(',');
1251
1304
1305 return new AmbiguousCharacters(map);
1306 > }); strings.ts
1307 >
1308 > public static getInstance(locales: Iterable<string>): AmbiguousCharacters {
1309 return AmbiguousCharacters.cache.get(Array.from(locales).join(','));
1310 }
1311 > strings.ts
1312 > private static _locales = new Lazy<string[]>(() =>
1313 Object.keys(AmbiguousCharacters.ambiguousCharacterData.value).filter(
1314 (k) => !k.startsWith('_')
1315 )
1316 > ); strings.ts
1317 > public static getLocales(): string[] {
1318 return AmbiguousCharacters._locales.value;
1319 }
1320 > strings.ts
1321 > private constructor(
1322 private readonly confusableDictionary: Map<number, number>
1323 ) { }
1324 > strings.ts
1325 > public isAmbiguous(codePoint: number): boolean {
1326 return this.confusableDictionary.has(codePoint);
1327 }
1328 > strings.ts
1329 > public containsAmbiguousCharacter(str: string): boolean {
1330 for (let i = 0; i < str.length; i++) {
1331 const codePoint = str.codePointAt(i);
1336 return false;
1337 }
1338 > strings.ts
1339 > /**
1340 > * Returns the non basic ASCII code point that the given code point can be confused,
1341 > * or undefined if such code point does note exist.
1342 > */
1343 > public getPrimaryConfusable(codePoint: number): number | undefined {
1344 return this.confusableDictionary.get(codePoint);
1345 }
1346 > strings.ts
1347 > public getConfusableCodePoints(): ReadonlySet<number> {
1348 return new Set(this.confusableDictionary.keys());
1349 }
1350 > } strings.ts
1351 >
1352 > export class InvisibleCharacters {
1353 > private static getRawData(): Record<string | '_common', number[]> {
1354 // Generated using https://github.com/hediet/vscode-unicode-data
1355 return JSON.parse('{\"_common\":[11,12,13,127,847,1564,4447,4448,6068,6069,6155,6156,6157,6158,7355,7356,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8204,8205,8206,8207,8234,8235,8236,8237,8238,8239,8287,8288,8289,8290,8291,8292,8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,10240,12644,65024,65025,65026,65027,65028,65029,65030,65031,65032,65033,65034,65035,65036,65037,65038,65039,65279,65440,65520,65521,65522,65523,65524,65525,65526,65527,65528,65532,78844,119155,119156,119157,119158,119159,119160,119161,119162,917504,917505,917506,917507,917508,917509,917510,917511,917512,917513,917514,917515,917516,917517,917518,917519,917520,917521,917522,917523,917524,917525,917526,917527,917528,917529,917530,917531,917532,917533,917534,917535,917536,917537,917538,917539,917540,917541,917542,917543,917544,917545,917546,917547,917548,917549,917550,917551,917552,917553,917554,917555,917556,917557,917558,917559,917560,917561,917562,917563,917564,917565,917566,917567,917568,917569,917570,917571,917572,917573,917574,917575,917576,917577,917578,917579,917580,917581,917582,917583,917584,917585,917586,917587,917588,917589,917590,917591,917592,917593,917594,917595,917596,917597,917598,917599,917600,917601,917602,917603,917604,917605,917606,917607,917608,917609,917610,917611,917612,917613,917614,917615,917616,917617,917618,917619,917620,917621,917622,917623,917624,917625,917626,917627,917628,917629,917630,917631,917760,917761,917762,917763,917764,917765,917766,917767,917768,917769,917770,917771,917772,917773,917774,917775,917776,917777,917778,917779,917780,917781,917782,917783,917784,917785,917786,917787,917788,917789,917790,917791,917792,917793,917794,917795,917796,917797,917798,917799,917800,917801,917802,917803,917804,917805,917806,917807,917808,917809,917810,917811,917812,917813,917814,917815,917816,917817,917818,917819,917820,917821,917822,917823,917824,917825,917826,917827,917828,917829,917830,917831,917832,917833,917834,917835,917836,917837,917838,917839,917840,917841,917842,917843,917844,917845,917846,917847,917848,917849,917850,917851,917852,917853,917854,917855,917856,917857,917858,917859,917860,917861,917862,917863,917864,917865,917866,917867,917868,917869,917870,917871,917872,917873,917874,917875,917876,917877,917878,917879,917880,917881,917882,917883,917884,917885,917886,917887,917888,917889,917890,917891,917892,917893,917894,917895,917896,917897,917898,917899,917900,917901,917902,917903,917904,917905,917906,917907,917908,917909,917910,917911,917912,917913,917914,917915,917916,917917,917918,917919,917920,917921,917922,917923,917924,917925,917926,917927,917928,917929,917930,917931,917932,917933,917934,917935,917936,917937,917938,917939,917940,917941,917942,917943,917944,917945,917946,917947,917948,917949,917950,917951,917952,917953,917954,917955,917956,917957,917958,917959,917960,917961,917962,917963,917964,917965,917966,917967,917968,917969,917970,917971,917972,917973,917974,917975,917976,917977,917978,917979,917980,917981,917982,917983,917984,917985,917986,917987,917988,917989,917990,917991,917992,917993,917994,917995,917996,917997,917998,917999],\"cs\":[173,8203,12288],\"de\":[173,8203,12288],\"es\":[8203,12288],\"fr\":[173,8203,12288],\"it\":[160,173,12288],\"ja\":[173],\"ko\":[173,12288],\"pl\":[173,8203,12288],\"pt-BR\":[173,8203,12288],\"qps-ploc\":[160,173,8203,12288],\"ru\":[173,12288],\"tr\":[160,173,8203,12288],\"zh-hans\":[160,173,8203,12288],\"zh-hant\":[173,12288]}');
1356 }
1357 > strings.ts
1358 > private static _data: Set<number> | undefined = undefined;
1359 >
1360 > private static getData() {
1361 if (!this._data) {
1362 this._data = new Set([...Object.values(InvisibleCharacters.getRawData())].flat());
1364 return this._data;
1365 }
1366 > strings.ts
1367 > public static isInvisibleCharacter(codePoint: number): boolean {
1368 return InvisibleCharacters.getData().has(codePoint);
1369 }
1370 > strings.ts
1371 > public static containsInvisibleCharacter(str: string): boolean {
1372 for (let i = 0; i < str.length; i++) {
1373 const codePoint = str.codePointAt(i);
1378 return false;
1379 }
1380 > strings.ts
1381 > public static get codePoints(): ReadonlySet<number> {
1382 return InvisibleCharacters.getData();
1383 }
1384 > } strings.ts
1385 >
1386 > export const Ellipsis = '\u2026';
1387 >
1388 > /**
1389 > * Convert a Unicode string to a string in which each 16-bit unit occupies only one byte
1390 > *
1391 > * From https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa
1392 > */
1393 function toBinary(str: string): string {
1394 const codeUnits = new Uint16Array(str.length);
1403 return binary;
1404 }
1405 > strings.ts
1406 > /**
1407 > * Version of the global `btoa` function that handles multi-byte characters instead
1408 > * of throwing an exception.
1409 > */
1410 >
1411 > export function multibyteAwareBtoa(str: string): string {
1412 return btoa(toBinary(str));
1413 }
src/vs/base/common/charCode.ts 450 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- charCode.ts
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 > // Names from https://blog.codinghorror.com/ascii-pronunciation-rules-for-programmers/
7 >
8 > /**
9 > * An inlined enum containing useful character codes (to be used with String.charCodeAt).
10 > * Please leave the const keyword such that it gets inlined when compiled to JavaScript!
11 > */
12 > export const enum CharCode {
13 > Null = 0,
14 > /**
15 > * The `\b` character.
16 > */
17 > Backspace = 8,
18 > /**
19 > * The `\t` character.
20 > */
21 > Tab = 9,
22 > /**
23 > * The `\n` character.
24 > */
25 > LineFeed = 10,
26 > /**
27 > * The `\r` character.
28 > */
29 > CarriageReturn = 13,
30 > Space = 32,
31 > /**
32 > * The `!` character.
33 > */
34 > ExclamationMark = 33,
35 > /**
36 > * The `"` character.
37 > */
38 > DoubleQuote = 34,
39 > /**
40 > * The `#` character.
41 > */
42 > Hash = 35,
43 > /**
44 > * The `$` character.
45 > */
46 > DollarSign = 36,
47 > /**
48 > * The `%` character.
49 > */
50 > PercentSign = 37,
51 > /**
52 > * The `&` character.
53 > */
54 > Ampersand = 38,
55 > /**
56 > * The `'` character.
57 > */
58 > SingleQuote = 39,
59 > /**
60 > * The `(` character.
61 > */
62 > OpenParen = 40,
63 > /**
64 > * The `)` character.
65 > */
66 > CloseParen = 41,
67 > /**
68 > * The `*` character.
69 > */
70 > Asterisk = 42,
71 > /**
72 > * The `+` character.
73 > */
74 > Plus = 43,
75 > /**
76 > * The `,` character.
77 > */
78 > Comma = 44,
79 > /**
80 > * The `-` character.
81 > */
82 > Dash = 45,
83 > /**
84 > * The `.` character.
85 > */
86 > Period = 46,
87 > /**
88 > * The `/` character.
89 > */
90 > Slash = 47,
91 >
92 > Digit0 = 48,
93 > Digit1 = 49,
94 > Digit2 = 50,
95 > Digit3 = 51,
96 > Digit4 = 52,
97 > Digit5 = 53,
98 > Digit6 = 54,
99 > Digit7 = 55,
100 > Digit8 = 56,
101 > Digit9 = 57,
102 >
103 > /**
104 > * The `:` character.
105 > */
106 > Colon = 58,
107 > /**
108 > * The `;` character.
109 > */
110 > Semicolon = 59,
111 > /**
112 > * The `<` character.
113 > */
114 > LessThan = 60,
115 > /**
116 > * The `=` character.
117 > */
118 > Equals = 61,
119 > /**
120 > * The `>` character.
121 > */
122 > GreaterThan = 62,
123 > /**
124 > * The `?` character.
125 > */
126 > QuestionMark = 63,
127 > /**
128 > * The `@` character.
129 > */
130 > AtSign = 64,
131 >
132 > A = 65,
133 > B = 66,
134 > C = 67,
135 > D = 68,
136 > E = 69,
137 > F = 70,
138 > G = 71,
139 > H = 72,
140 > I = 73,
141 > J = 74,
142 > K = 75,
143 > L = 76,
144 > M = 77,
145 > N = 78,
146 > O = 79,
147 > P = 80,
148 > Q = 81,
149 > R = 82,
150 > S = 83,
151 > T = 84,
152 > U = 85,
153 > V = 86,
154 > W = 87,
155 > X = 88,
156 > Y = 89,
157 > Z = 90,
158 >
159 > /**
160 > * The `[` character.
161 > */
162 > OpenSquareBracket = 91,
163 > /**
164 > * The `\` character.
165 > */
166 > Backslash = 92,
167 > /**
168 > * The `]` character.
169 > */
170 > CloseSquareBracket = 93,
171 > /**
172 > * The `^` character.
173 > */
174 > Caret = 94,
175 > /**
176 > * The `_` character.
177 > */
178 > Underline = 95,
179 > /**
180 > * The ``(`)`` character.
181 > */
182 > BackTick = 96,
183 >
184 > a = 97,
185 > b = 98,
186 > c = 99,
187 > d = 100,
188 > e = 101,
189 > f = 102,
190 > g = 103,
191 > h = 104,
192 > i = 105,
193 > j = 106,
194 > k = 107,
195 > l = 108,
196 > m = 109,
197 > n = 110,
198 > o = 111,
199 > p = 112,
200 > q = 113,
201 > r = 114,
202 > s = 115,
203 > t = 116,
204 > u = 117,
205 > v = 118,
206 > w = 119,
207 > x = 120,
208 > y = 121,
209 > z = 122,
210 >
211 > /**
212 > * The `{` character.
213 > */
214 > OpenCurlyBrace = 123,
215 > /**
216 > * The `|` character.
217 > */
218 > Pipe = 124,
219 > /**
220 > * The `}` character.
221 > */
222 > CloseCurlyBrace = 125,
223 > /**
224 > * The `~` character.
225 > */
226 > Tilde = 126,
227 >
228 > /**
229 > * The &nbsp; (no-break space) character.
230 > * Unicode Character 'NO-BREAK SPACE' (U+00A0)
231 > */
232 > NoBreakSpace = 160,
233 >
234 > U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent
235 > U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent
236 > U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent
237 > U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde
238 > U_Combining_Macron = 0x0304, // U+0304 Combining Macron
239 > U_Combining_Overline = 0x0305, // U+0305 Combining Overline
240 > U_Combining_Breve = 0x0306, // U+0306 Combining Breve
241 > U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above
242 > U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis
243 > U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above
244 > U_Combining_Ring_Above = 0x030A, // U+030A Combining Ring Above
245 > U_Combining_Double_Acute_Accent = 0x030B, // U+030B Combining Double Acute Accent
246 > U_Combining_Caron = 0x030C, // U+030C Combining Caron
247 > U_Combining_Vertical_Line_Above = 0x030D, // U+030D Combining Vertical Line Above
248 > U_Combining_Double_Vertical_Line_Above = 0x030E, // U+030E Combining Double Vertical Line Above
249 > U_Combining_Double_Grave_Accent = 0x030F, // U+030F Combining Double Grave Accent
250 > U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu
251 > U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve
252 > U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above
253 > U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above
254 > U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above
255 > U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right
256 > U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below
257 > U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below
258 > U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below
259 > U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below
260 > U_Combining_Left_Angle_Above = 0x031A, // U+031A Combining Left Angle Above
261 > U_Combining_Horn = 0x031B, // U+031B Combining Horn
262 > U_Combining_Left_Half_Ring_Below = 0x031C, // U+031C Combining Left Half Ring Below
263 > U_Combining_Up_Tack_Below = 0x031D, // U+031D Combining Up Tack Below
264 > U_Combining_Down_Tack_Below = 0x031E, // U+031E Combining Down Tack Below
265 > U_Combining_Plus_Sign_Below = 0x031F, // U+031F Combining Plus Sign Below
266 > U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below
267 > U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below
268 > U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below
269 > U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below
270 > U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below
271 > U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below
272 > U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below
273 > U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla
274 > U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek
275 > U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below
276 > U_Combining_Bridge_Below = 0x032A, // U+032A Combining Bridge Below
277 > U_Combining_Inverted_Double_Arch_Below = 0x032B, // U+032B Combining Inverted Double Arch Below
278 > U_Combining_Caron_Below = 0x032C, // U+032C Combining Caron Below
279 > U_Combining_Circumflex_Accent_Below = 0x032D, // U+032D Combining Circumflex Accent Below
280 > U_Combining_Breve_Below = 0x032E, // U+032E Combining Breve Below
281 > U_Combining_Inverted_Breve_Below = 0x032F, // U+032F Combining Inverted Breve Below
282 > U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below
283 > U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below
284 > U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line
285 > U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line
286 > U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay
287 > U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay
288 > U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay
289 > U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay
290 > U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay
291 > U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below
292 > U_Combining_Inverted_Bridge_Below = 0x033A, // U+033A Combining Inverted Bridge Below
293 > U_Combining_Square_Below = 0x033B, // U+033B Combining Square Below
294 > U_Combining_Seagull_Below = 0x033C, // U+033C Combining Seagull Below
295 > U_Combining_X_Above = 0x033D, // U+033D Combining X Above
296 > U_Combining_Vertical_Tilde = 0x033E, // U+033E Combining Vertical Tilde
297 > U_Combining_Double_Overline = 0x033F, // U+033F Combining Double Overline
298 > U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark
299 > U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark
300 > U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni
301 > U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis
302 > U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos
303 > U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni
304 > U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above
305 > U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below
306 > U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below
307 > U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below
308 > U_Combining_Not_Tilde_Above = 0x034A, // U+034A Combining Not Tilde Above
309 > U_Combining_Homothetic_Above = 0x034B, // U+034B Combining Homothetic Above
310 > U_Combining_Almost_Equal_To_Above = 0x034C, // U+034C Combining Almost Equal To Above
311 > U_Combining_Left_Right_Arrow_Below = 0x034D, // U+034D Combining Left Right Arrow Below
312 > U_Combining_Upwards_Arrow_Below = 0x034E, // U+034E Combining Upwards Arrow Below
313 > U_Combining_Grapheme_Joiner = 0x034F, // U+034F Combining Grapheme Joiner
314 > U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above
315 > U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above
316 > U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata
317 > U_Combining_X_Below = 0x0353, // U+0353 Combining X Below
318 > U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below
319 > U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below
320 > U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below
321 > U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above
322 > U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right
323 > U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below
324 > U_Combining_Double_Ring_Below = 0x035A, // U+035A Combining Double Ring Below
325 > U_Combining_Zigzag_Above = 0x035B, // U+035B Combining Zigzag Above
326 > U_Combining_Double_Breve_Below = 0x035C, // U+035C Combining Double Breve Below
327 > U_Combining_Double_Breve = 0x035D, // U+035D Combining Double Breve
328 > U_Combining_Double_Macron = 0x035E, // U+035E Combining Double Macron
329 > U_Combining_Double_Macron_Below = 0x035F, // U+035F Combining Double Macron Below
330 > U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde
331 > U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve
332 > U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below
333 > U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A
334 > U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E
335 > U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I
336 > U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O
337 > U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U
338 > U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C
339 > U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D
340 > U_Combining_Latin_Small_Letter_H = 0x036A, // U+036A Combining Latin Small Letter H
341 > U_Combining_Latin_Small_Letter_M = 0x036B, // U+036B Combining Latin Small Letter M
342 > U_Combining_Latin_Small_Letter_R = 0x036C, // U+036C Combining Latin Small Letter R
343 > U_Combining_Latin_Small_Letter_T = 0x036D, // U+036D Combining Latin Small Letter T
344 > U_Combining_Latin_Small_Letter_V = 0x036E, // U+036E Combining Latin Small Letter V
345 > U_Combining_Latin_Small_Letter_X = 0x036F, // U+036F Combining Latin Small Letter X
346 >
347 > /**
348 > * Unicode Character 'LINE SEPARATOR' (U+2028)
349 > * http://www.fileformat.info/info/unicode/char/2028/index.htm
350 > */
351 > LINE_SEPARATOR = 0x2028,
352 > /**
353 > * Unicode Character 'PARAGRAPH SEPARATOR' (U+2029)
354 > * http://www.fileformat.info/info/unicode/char/2029/index.htm
355 > */
356 > PARAGRAPH_SEPARATOR = 0x2029,
357 > /**
358 > * Unicode Character 'NEXT LINE' (U+0085)
359 > * http://www.fileformat.info/info/unicode/char/0085/index.htm
360 > */
361 > NEXT_LINE = 0x0085,
362 >
363 > // http://www.fileformat.info/info/unicode/category/Sk/list.htm
364 > U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX
365 > U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT
366 > U_DIAERESIS = 0x00A8, // U+00A8 DIAERESIS
367 > U_MACRON = 0x00AF, // U+00AF MACRON
368 > U_ACUTE_ACCENT = 0x00B4, // U+00B4 ACUTE ACCENT
369 > U_CEDILLA = 0x00B8, // U+00B8 CEDILLA
370 > U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD
371 > U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD
372 > U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, // U+02C4 MODIFIER LETTER UP ARROWHEAD
373 > U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD
374 > U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING
375 > U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING
376 > U_MODIFIER_LETTER_UP_TACK = 0x02D4, // U+02D4 MODIFIER LETTER UP TACK
377 > U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, // U+02D5 MODIFIER LETTER DOWN TACK
378 > U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, // U+02D6 MODIFIER LETTER PLUS SIGN
379 > U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, // U+02D7 MODIFIER LETTER MINUS SIGN
380 > U_BREVE = 0x02D8, // U+02D8 BREVE
381 > U_DOT_ABOVE = 0x02D9, // U+02D9 DOT ABOVE
382 > U_RING_ABOVE = 0x02DA, // U+02DA RING ABOVE
383 > U_OGONEK = 0x02DB, // U+02DB OGONEK
384 > U_SMALL_TILDE = 0x02DC, // U+02DC SMALL TILDE
385 > U_DOUBLE_ACUTE_ACCENT = 0x02DD, // U+02DD DOUBLE ACUTE ACCENT
386 > U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, // U+02DE MODIFIER LETTER RHOTIC HOOK
387 > U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, // U+02DF MODIFIER LETTER CROSS ACCENT
388 > U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR
389 > U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, // U+02E6 MODIFIER LETTER HIGH TONE BAR
390 > U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, // U+02E7 MODIFIER LETTER MID TONE BAR
391 > U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, // U+02E8 MODIFIER LETTER LOW TONE BAR
392 > U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR
393 > U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK
394 > U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK
395 > U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, // U+02ED MODIFIER LETTER UNASPIRATED
396 > U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD
397 > U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD
398 > U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD
399 > U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD
400 > U_MODIFIER_LETTER_LOW_RING = 0x02F3, // U+02F3 MODIFIER LETTER LOW RING
401 > U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT
402 > U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT
403 > U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT
404 > U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, // U+02F7 MODIFIER LETTER LOW TILDE
405 > U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, // U+02F8 MODIFIER LETTER RAISED COLON
406 > U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE
407 > U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, // U+02FA MODIFIER LETTER END HIGH TONE
408 > U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, // U+02FB MODIFIER LETTER BEGIN LOW TONE
409 > U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, // U+02FC MODIFIER LETTER END LOW TONE
410 > U_MODIFIER_LETTER_SHELF = 0x02FD, // U+02FD MODIFIER LETTER SHELF
411 > U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, // U+02FE MODIFIER LETTER OPEN SHELF
412 > U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, // U+02FF MODIFIER LETTER LOW LEFT ARROW
413 > U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN
414 > U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS
415 > U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS
416 > U_GREEK_KORONIS = 0x1FBD, // U+1FBD GREEK KORONIS
417 > U_GREEK_PSILI = 0x1FBF, // U+1FBF GREEK PSILI
418 > U_GREEK_PERISPOMENI = 0x1FC0, // U+1FC0 GREEK PERISPOMENI
419 > U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI
420 > U_GREEK_PSILI_AND_VARIA = 0x1FCD, // U+1FCD GREEK PSILI AND VARIA
421 > U_GREEK_PSILI_AND_OXIA = 0x1FCE, // U+1FCE GREEK PSILI AND OXIA
422 > U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, // U+1FCF GREEK PSILI AND PERISPOMENI
423 > U_GREEK_DASIA_AND_VARIA = 0x1FDD, // U+1FDD GREEK DASIA AND VARIA
424 > U_GREEK_DASIA_AND_OXIA = 0x1FDE, // U+1FDE GREEK DASIA AND OXIA
425 > U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, // U+1FDF GREEK DASIA AND PERISPOMENI
426 > U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, // U+1FED GREEK DIALYTIKA AND VARIA
427 > U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, // U+1FEE GREEK DIALYTIKA AND OXIA
428 > U_GREEK_VARIA = 0x1FEF, // U+1FEF GREEK VARIA
429 > U_GREEK_OXIA = 0x1FFD, // U+1FFD GREEK OXIA
430 > U_GREEK_DASIA = 0x1FFE, // U+1FFE GREEK DASIA
431 >
432 > U_IDEOGRAPHIC_FULL_STOP = 0x3002, // U+3002 IDEOGRAPHIC FULL STOP
433 > U_LEFT_CORNER_BRACKET = 0x300C, // U+300C LEFT CORNER BRACKET
434 > U_RIGHT_CORNER_BRACKET = 0x300D, // U+300D RIGHT CORNER BRACKET
435 > U_LEFT_BLACK_LENTICULAR_BRACKET = 0x3010, // U+3010 LEFT BLACK LENTICULAR BRACKET
436 > U_RIGHT_BLACK_LENTICULAR_BRACKET = 0x3011, // U+3011 RIGHT BLACK LENTICULAR BRACKET
437 >
438 >
439 > U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE'
440 >
441 > /**
442 > * UTF-8 BOM
443 > * Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
444 > * http://www.fileformat.info/info/unicode/char/feff/index.htm
445 > */
446 > UTF8_BOM = 65279,
447 >
448 > U_FULLWIDTH_SEMICOLON = 0xFF1B, // U+FF1B FULLWIDTH SEMICOLON
449 > U_FULLWIDTH_COMMA = 0xFF0C, // U+FF0C FULLWIDTH COMMA
450 > }
src/vs/workbench/services/preferences/common/preferencesModels.ts 424 covered LOC · 100 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- preferencesModels.ts
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 { coalesce } from '../../../../base/common/arrays.js';
7 > import { IStringDictionary } from '../../../../base/common/collections.js';
8 > import { Emitter, Event } from '../../../../base/common/event.js';
9 > import { JSONVisitor, visit } from '../../../../base/common/json.js';
10 > import { Disposable, IReference } from '../../../../base/common/lifecycle.js';
11 > import { URI } from '../../../../base/common/uri.js';
12 > import { IRange, Range } from '../../../../editor/common/core/range.js';
13 > import { Selection } from '../../../../editor/common/core/selection.js';
14 > import { ITextModel } from '../../../../editor/common/model.js';
15 > import { ISingleEditOperation } from '../../../../editor/common/core/editOperation.js';
16 > import { ITextEditorModel } from '../../../../editor/common/services/resolverService.js';
17 > import * as nls from '../../../../nls.js';
18 > import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
19 > import { ConfigurationDefaultValueSource, ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry, IRegisteredConfigurationPropertySchema, OVERRIDE_PROPERTY_REGEX } from '../../../../platform/configuration/common/configurationRegistry.js';
20 > import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
21 > import { Registry } from '../../../../platform/registry/common/platform.js';
22 > import { EditorModel } from '../../../common/editor/editorModel.js';
23 > import { IFilterMetadata, IFilterResult, IGroupFilter, IKeybindingsEditorModel, ISearchResultGroup, ISetting, ISettingMatch, ISettingMatcher, ISettingsEditorModel, ISettingsGroup, SettingMatchType } from './preferences.js';
24 > import { FOLDER_SCOPES, WORKSPACE_SCOPES } from '../../configuration/common/configuration.js';
25 > import { createValidator } from './preferencesValidation.js';
26 > import { isString } from '../../../../base/common/types.js';
27 >
28 > export const nullRange: IRange = { startLineNumber: -1, startColumn: -1, endLineNumber: -1, endColumn: -1 };
29 function isNullRange(range: IRange): boolean { return range.startLineNumber === -1 && range.startColumn === -1 && range.endLineNumber === -1 && range.endColumn === -1; }
31 > /**
32 > * Strips VS Code's custom `#settingId#` link syntax from a markdown string so the setting key
33 > * remains as inline code (e.g. `` `settingId` ``). Useful for contexts that don't render markdown links.
34 > */
35 > export function fixSettingLinks(text: string): string {
36 return text.replace(/`#([^#`]*)#`/g, (_, settingName) => `\`${settingName}\``);
37 }
39 abstract class AbstractSettingsModel extends EditorModel {
40
41 protected _currentResultGroups = new Map<string, ISearchResultGroup>();
43 > updateResultGroup(id: string, resultGroup: ISearchResultGroup | undefined): IFilterResult | undefined {
44 if (resultGroup) {
45 this._currentResultGroups.set(id, resultGroup);
51 return this.update();
52 }
54 > /**
55 > * Remove duplicates between result groups, preferring results in earlier groups
56 > */
57 > private removeDuplicateResults(): void {
58 const settingKeys = new Set<string>();
59 [...this._currentResultGroups.keys()]
65 });
66 }
68 > filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): ISettingMatch[] {
69 const allGroups = this.filterGroups;
70
91 return filterMatches;
92 }
94 > getPreference(key: string): ISetting | undefined {
95 for (const group of this.settingsGroups) {
96 for (const section of group.sections) {
105 return undefined;
106 }
108 > protected collectMetadata(groups: ISearchResultGroup[]): IStringDictionary<IFilterMetadata> | null {
109 const metadata = Object.create(null);
110 let hasMetadata = false;
118 return hasMetadata ? metadata : null;
119 }
121 >
122 > protected get filterGroups(): ISettingsGroup[] {
123 return this.settingsGroups;
124 }
126 > abstract settingsGroups: ISettingsGroup[];
127 >
128 > protected abstract update(): IFilterResult | undefined;
129 > }
130 >
131 > export class SettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
132 >
133 > private _settingsGroups: ISettingsGroup[] | undefined;
134 > protected settingsModel: ITextModel;
135 >
136 > private readonly _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
137 > readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
138 >
139 > constructor(reference: IReference<ITextEditorModel>, private _configurationTarget: ConfigurationTarget) {
140 super();
141 this.settingsModel = reference.object.textEditorModel!;
146 }));
147 }
149 > get uri(): URI {
150 return this.settingsModel.uri;
151 }
153 > get configurationTarget(): ConfigurationTarget {
154 return this._configurationTarget;
155 }
157 > get settingsGroups(): ISettingsGroup[] {
158 if (!this._settingsGroups) {
159 this.parse();
161 return this._settingsGroups!;
162 }
164 > get content(): string {
165 return this.settingsModel.getValue();
166 }
168 > protected isSettingsProperty(property: string, previousParents: string[]): boolean {
169 return previousParents.length === 0; // Settings is root
170 }
172 > protected parse(): void {
173 this._settingsGroups = parse(this.settingsModel, (property: string, previousParents: string[]): boolean => this.isSettingsProperty(property, previousParents));
174 }
176 > protected update(): IFilterResult | undefined {
177 const resultGroups = [...this._currentResultGroups.values()];
178 if (!resultGroups.length) {
216 };
217 }
219 >
220 > export class Settings2EditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
221 > private readonly _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
222 > readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
223 >
224 > private additionalGroups: ISettingsGroup[] = [];
225 > private dirty = false;
226 >
227 > constructor(
228 private _defaultSettings: DefaultSettings,
229 @IConfigurationService configurationService: IConfigurationService,
242 }));
243 }
245 > /** Doesn't include the "Commonly Used" group */
246 > protected override get filterGroups(): ISettingsGroup[] {
247 return this.settingsGroups.slice(1);
248 }
250 > get settingsGroups(): ISettingsGroup[] {
251 const groups = this._defaultSettings.getSettingsGroups(this.dirty);
252 this.dirty = false;
253 return [...groups, ...this.additionalGroups];
254 }
256 > /** For programmatically added groups outside of registered configurations */
257 > setAdditionalGroups(groups: ISettingsGroup[]) {
258 this.additionalGroups = groups;
259 }
261 > protected update(): IFilterResult {
262 throw new Error('Not supported');
263 }
265 >
266 function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, previousParents: string[]) => boolean): ISettingsGroup[] {
267 const settings: ISetting[] = [];
434 } satisfies ISettingsGroup] : [];
435 }
437 > export class WorkspaceConfigurationEditorModel extends SettingsEditorModel {
438
439 private _configurationGroups: ISettingsGroup[] = [];
441 > get configurationGroups(): ISettingsGroup[] {
442 return this._configurationGroups;
443 }
445 > protected override parse(): void {
446 super.parse();
447 this._configurationGroups = parse(this.settingsModel, (property: string, previousParents: string[]): boolean => previousParents.length === 0);
448 }
450 > protected override isSettingsProperty(property: string, previousParents: string[]): boolean {
451 return property === 'settings' && previousParents.length === 1;
452 }
454 > }
455 >
456 > export class DefaultSettings extends Disposable {
457 >
458 > private _allSettingsGroups: ISettingsGroup[] | undefined;
459 > private _content: string | undefined;
460 > private _contentWithoutMostCommonlyUsed: string | undefined;
461 > private _settingsByName = new Map<string, ISetting>();
462 >
463 > private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
464 > readonly onDidChange: Event<void> = this._onDidChange.event;
465 >
466 > constructor(
467 > private _mostCommonlyUsedSettingsKeys: string[],
468 > readonly target: ConfigurationTarget,
469 > readonly configurationService: IConfigurationService
470 > ) {
471 > super();
472 > this._register(configurationService.onDidChangeConfiguration(e => {
473 if (e.source === ConfigurationTarget.DEFAULT) {
474 this.reset();
475 this._onDidChange.fire();
476 }
478 > }
479 >
480 > getContent(forceUpdate = false): string {
481 if (!this._content || forceUpdate) {
482 this.initialize();
485 return this._content!;
486 }
488 > getContentWithoutMostCommonlyUsed(forceUpdate = false): string {
489 if (!this._contentWithoutMostCommonlyUsed || forceUpdate) {
490 this.initialize();
493 return this._contentWithoutMostCommonlyUsed!;
494 }
496 > getSettingsGroups(forceUpdate = false): ISettingsGroup[] {
497 if (!this._allSettingsGroups || forceUpdate) {
498 this.initialize();
501 return this._allSettingsGroups!;
502 }
504 > private initialize(): void {
505 this._allSettingsGroups = this.parse();
506 this._content = this.toContent(this._allSettingsGroups, 0);
507 this._contentWithoutMostCommonlyUsed = this.toContent(this._allSettingsGroups, 1);
508 }
510 > private reset(): void {
511 this._content = undefined;
512 this._contentWithoutMostCommonlyUsed = undefined;
513 this._allSettingsGroups = undefined;
514 }
516 > private parse(): ISettingsGroup[] {
517 const settingsGroups = this.getRegisteredGroups();
518 this.initAllSettingsMap(settingsGroups);
520 return [mostCommonlyUsed, ...settingsGroups];
521 }
523 > getRegisteredGroups(): ISettingsGroup[] {
524 > const registry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
525 > const allConfigurations: IStringDictionary<IRegisteredConfigurationPropertySchema> = { ...registry.getConfigurationProperties() };
526 > const excludedConfigurations = registry.getExcludedConfigurationProperties();
527 >
528 > for (const policyKey of this.configurationService.keys().policy ?? []) {
529 const policyConfiguration = excludedConfigurations[policyKey];
530 if (policyConfiguration) {
532 }
533 }
535 > const groups = this.removeEmptySettingsGroups(this.parseProperties(allConfigurations).sort(this.compareGroups));
536 > return this.sortGroups(groups);
537 > }
538 >
539 > private sortGroups(groups: ISettingsGroup[]): ISettingsGroup[] {
540 > groups.forEach(group => {
541 > group.sections.forEach(section => {
542 > section.settings.sort((a, b) => a.key.localeCompare(b.key));
543 > });
544 > });
545 >
546 > return groups;
547 > }
548 >
549 > private initAllSettingsMap(allSettingsGroups: ISettingsGroup[]): void {
550 this._settingsByName = new Map<string, ISetting>();
551 for (const group of allSettingsGroups) {
557 }
558 }
560 > private getMostCommonlyUsedSettings(): ISettingsGroup {
561 const settings = coalesce(this._mostCommonlyUsedSettingsKeys.map(key => {
562 const setting = this._settingsByName.get(key);
592 } satisfies ISettingsGroup;
593 }
595 > private parseProperties(properties: IStringDictionary<IRegisteredConfigurationPropertySchema>): ISettingsGroup[] {
596 > const result: ISettingsGroup[] = [];
597 > const byTitle = new Map<string, ISettingsGroup[]>();
598 > const byId = new Map<string, ISettingsGroup[]>();
599 > for (const [key, property] of Object.entries(properties)) {
600 > if (!property.section) {
601 continue;
602 }
604 > let settingsGroup: ISettingsGroup | undefined;
605 >
606 > if (property.section.title) {
607 > const groups = byTitle.get(property.section.title); preferencesModels.ts
608 > if (groups) {
609 const extensionId = property.section.extensionInfo?.id;
610 settingsGroup = groups.find(g => g.extensionInfo?.id === extensionId);
611 }
614 > if (!settingsGroup && property.section.id) {
615 > const groups = byId.get(property.section.id);
616 > if (groups) {
617 > const extensionId = property.section.extensionInfo?.id; preferencesModels.ts
618 > settingsGroup = groups.find(g => g.extensionInfo?.id === extensionId && !g.title);
619 > }
620 > if (settingsGroup && !settingsGroup?.title && property.section.title) { preferencesModels.ts
621 settingsGroup.title = property.section.title;
622 const byTitleGroups = byTitle.get(property.section.title);
627 }
628 }
630 >
631 > if (!settingsGroup) {
632 > settingsGroup = { sections: [{ title: property.section.title, settings: [] }], id: property.section.id || '', title: property.section.title ?? '', titleRange: nullRange, order: property.section.order, range: nullRange, extensionInfo: isString(property.source) ? undefined : property.source };
633 > result.push(settingsGroup);
634 > if (property.section.title) {
635 > const byTitleGroups = byTitle.get(property.section.title); preferencesModels.ts
636 > if (byTitleGroups) {
637 byTitleGroups.push(settingsGroup);
638 > } else { preferencesModels.ts
639 > byTitle.set(property.section.title, [settingsGroup]);
640 > }
641 > }
642 > if (property.section.id) { preferencesModels.ts
643 > const byIdGroups = byId.get(property.section.id);
644 > if (byIdGroups) {
645 > byIdGroups.push(settingsGroup); preferencesModels.ts
646 > } else { preferencesModels.ts
647 > byId.set(property.section.id, [settingsGroup]);
648 > }
649 > }
650 > }
651 >
652 > const setting = this.parseSetting(key, property);
653 > if (setting) {
654 > settingsGroup.sections[0].settings.push(setting);
655 > }
656 > }
657 > return result;
658 > }
659 >
660 > private removeEmptySettingsGroups(settingsGroups: ISettingsGroup[]): ISettingsGroup[] {
661 > const result: ISettingsGroup[] = [];
662 > for (const settingsGroup of settingsGroups) {
663 > settingsGroup.sections = settingsGroup.sections.filter(section => section.settings.length > 0);
664 > if (settingsGroup.sections.length) {
665 > result.push(settingsGroup);
666 > }
667 > }
668 > return result;
669 > }
670 >
671 > private parseSetting(key: string, prop: IRegisteredConfigurationPropertySchema): ISetting | undefined {
672 > if (!this.matchesScope(prop)) {
673 return undefined;
674 }
676 > const value = prop.default;
677 > let description = (prop.markdownDescription || prop.description || '');
678 > if (typeof description !== 'string') {
679 description = '';
680 }
681 > const descriptionLines = description.split('\n'); preferencesModels.ts
682 > const overrides = OVERRIDE_PROPERTY_REGEX.test(key) ? this.parseOverrideSettings(prop.default) : [];
683 > let listItemType: string | undefined;
684 > if (prop.type === 'array' && prop.items && !Array.isArray(prop.items) && prop.items.type) {
685 if (prop.items.enum) {
686 listItemType = 'enum';
689 }
690 }
692 > const objectProperties = prop.type === 'object' ? prop.properties : undefined;
693 > const objectPatternProperties = prop.type === 'object' ? prop.patternProperties : undefined;
694 > const objectAdditionalProperties = prop.type === 'object' ? prop.additionalProperties : undefined;
695 > const propertyNames = prop.type === 'object' ? prop.propertyNames : undefined;
696 >
697 > let enumToUse = prop.enum;
698 > let enumDescriptions = prop.markdownEnumDescriptions ?? prop.enumDescriptions;
699 > let enumDescriptionsAreMarkdown = !!prop.markdownEnumDescriptions;
700 > if (listItemType === 'enum' && !Array.isArray(prop.items)) {
701 enumToUse = prop.items!.enum;
702 enumDescriptions = prop.items!.markdownEnumDescriptions ?? prop.items!.enumDescriptions;
703 enumDescriptionsAreMarkdown = !!prop.items!.markdownEnumDescriptions;
704 }
706 > let allKeysAreBoolean = false;
707 > if (prop.type === 'object' && !prop.additionalProperties && prop.properties && Object.keys(prop.properties).length) {
708 allKeysAreBoolean = Object.keys(prop.properties).every(key => {
709 return prop.properties![key].type === 'boolean';
710 });
711 }
713 > let isLanguageTagSetting = false;
714 > if (OVERRIDE_PROPERTY_REGEX.test(key)) {
715 isLanguageTagSetting = true;
716 }
718 > let defaultValueSource: ConfigurationDefaultValueSource | undefined;
719 > if (!isLanguageTagSetting) {
720 > const registeredConfigurationProp = prop as IRegisteredConfigurationPropertySchema;
721 > if (registeredConfigurationProp && registeredConfigurationProp.defaultValueSource) {
722 defaultValueSource = registeredConfigurationProp.defaultValueSource;
723 }
725 >
726 > if (!enumToUse && (prop.enumItemLabels || enumDescriptions || enumDescriptionsAreMarkdown)) {
727 console.error(`The setting ${key} has enum-related fields, but doesn't have an enum field. This setting may render improperly in the Settings editor.`);
728 }
730 > return {
731 > key,
732 > value,
733 > description: descriptionLines,
734 > descriptionIsMarkdown: !!prop.markdownDescription,
735 > keywords: prop.keywords,
736 > range: nullRange,
737 > keyRange: nullRange,
738 > valueRange: nullRange,
739 > descriptionRanges: [],
740 > overrides,
741 > scope: prop.scope,
742 > type: prop.type,
743 > arrayItemType: listItemType,
744 > objectProperties,
745 > objectPatternProperties,
746 > objectAdditionalProperties,
747 > propertyNames,
748 > enum: enumToUse,
749 > enumDescriptions: enumDescriptions,
750 > enumDescriptionsAreMarkdown: enumDescriptionsAreMarkdown,
751 > enumItemLabels: prop.enumItemLabels,
752 > uniqueItems: prop.uniqueItems,
753 > tags: prop.tags,
754 > disallowSyncIgnore: prop.disallowSyncIgnore,
755 > restricted: prop.restricted,
756 > extensionInfo: isString(prop.source) ? undefined : prop.source,
757 > deprecationMessage: prop.markdownDeprecationMessage || prop.deprecationMessage,
758 > deprecationMessageIsMarkdown: !!prop.markdownDeprecationMessage,
759 > validator: createValidator(prop),
760 > allKeysAreBoolean,
761 > editPresentation: prop.editPresentation,
762 > order: prop.order,
763 > nonLanguageSpecificDefaultValueSource: defaultValueSource,
764 > isLanguageTagSetting,
765 > categoryLabel: (isString(prop.source) ? undefined : prop.source?.id) === prop.section?.id ? prop.title : prop.section?.id
766 > };
767 > }
768 >
769 > private parseOverrideSettings(overrideSettings: any): ISetting[] {
770 return Object.keys(overrideSettings).map((key) => ({
771 key,
780 }));
781 }
783 > private matchesScope(property: IConfigurationNode): boolean {
784 > if (!property.scope) {
785 return true;
786 }
787 > if (this.target === ConfigurationTarget.WORKSPACE_FOLDER) { preferencesModels.ts
788 return FOLDER_SCOPES.indexOf(property.scope) !== -1;
789 }
790 > if (this.target === ConfigurationTarget.WORKSPACE) { preferencesModels.ts
791 return WORKSPACE_SCOPES.indexOf(property.scope) !== -1;
792 }
793 > return true; preferencesModels.ts
794 > }
795 >
796 > private compareGroups(c1: ISettingsGroup, c2: ISettingsGroup): number {
797 > if (typeof c1?.order !== 'number') { preferencesModels.ts
798 > return 1; preferencesModels.ts
799 > }
800 > if (typeof c2?.order !== 'number') { preferencesModels.ts
801 return -1;
802 }
807 }
808 return c1.order - c2.order;
811 > private toContent(settingsGroups: ISettingsGroup[], startIndex: number): string {
812 const builder = new SettingsContentBuilder();
813 for (let i = startIndex; i < settingsGroups.length; i++) {
816 return builder.getContent();
817 }
819 > }
820 >
821 > export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
822 >
823 > private _model: ITextModel;
824 >
825 > private readonly _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
826 > readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
827 >
828 > constructor(
829 private _uri: URI,
830 reference: IReference<ITextEditorModel>,
837 this._register(this.onWillDispose(() => reference.dispose()));
838 }
840 > get uri(): URI {
841 return this._uri;
842 }
844 > get target(): ConfigurationTarget {
845 return this.defaultSettings.target;
846 }
848 > get settingsGroups(): ISettingsGroup[] {
849 return this.defaultSettings.getSettingsGroups();
850 }
852 > protected override get filterGroups(): ISettingsGroup[] {
853 // Don't look at "commonly used" for filter
854 return this.settingsGroups.slice(1);
855 }
857 > protected update(): IFilterResult | undefined {
858 if (this._model.isDisposed()) {
859 return undefined;
878 undefined;
879 }
881 > /**
882 > * Translate the ISearchResultGroups to text, and write it to the editor model
883 > */
884 > private writeResultGroups(groups: ISearchResultGroup[], startLine: number): { matches: IRange[]; settingsGroups: ISettingsGroup[] } {
885 const contentBuilderOffset = startLine - 1;
886 const builder = new SettingsContentBuilder(contentBuilderOffset);
915 return { matches, settingsGroups };
916 }
918 > private writeSettingsGroupToBuilder(builder: SettingsContentBuilder, settingsGroup: ISettingsGroup, filterMatches: ISettingMatch[]): IRange[] {
919 filterMatches = filterMatches
920 .map(filteredMatch => {
953 return fixedMatches;
954 }
956 > private copySetting(setting: ISetting): ISetting {
957 return {
958 description: setting.description,
974 };
975 }
977 > override getPreference(key: string): ISetting | undefined {
978 for (const group of this.settingsGroups) {
979 for (const section of group.sections) {
987 return undefined;
988 }
990 > private getGroup(resultGroup: ISearchResultGroup): ISettingsGroup {
991 return {
992 id: resultGroup.id,
1001 };
1002 }
1004 >
1005 > class SettingsContentBuilder {
1006 > private _contentByLines: string[];
1007 >
1008 > private get lineCountWithOffset(): number {
1009 > return this._contentByLines.length + this._rangeOffset;
1010 > }
1011 >
1012 > private get lastLine(): string {
1013 return this._contentByLines[this._contentByLines.length - 1] || '';
1014 }
1016 > constructor(private _rangeOffset = 0) {
1017 this._contentByLines = [];
1018 }
1020 > pushLine(...lineText: string[]): void {
1021 this._contentByLines.push(...lineText);
1022 }
1024 > pushGroup(settingsGroups: ISettingsGroup, isFirst?: boolean, isLast?: boolean): void {
1025 this._contentByLines.push(isFirst ? '[{' : '{');
1026 const lastSetting = this._pushGroup(settingsGroups, ' ');
1035 this._contentByLines.push(isLast ? '}]' : '},');
1036 }
1038 > protected _pushGroup(group: ISettingsGroup, indent: string): ISetting | null {
1039 let lastSetting: ISetting | null = null;
1040 const groupStart = this.lineCountWithOffset + 1;
1055 return lastSetting;
1056 }
1058 > getContent(): string {
1059 return this._contentByLines.join('\n');
1060 }
1062 > private pushSetting(setting: ISetting, indent: string): void {
1063 const settingStart = this.lineCountWithOffset + 1;
1064
1079 setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length };
1080 }
1082 > private pushSettingDescription(setting: ISetting, indent: string): void {
1083 setting.descriptionRanges = [];
1084 const descriptionPreValue = indent + '// ';
1106 }
1107 }
1109 > private pushValue(setting: ISetting, preValueConent: string, indent: string): void {
1110 const valueString = JSON.stringify(setting.value, null, indent);
1111 if (valueString && (typeof setting.value === 'object')) {
1131 }
1132 }
1134 > private addDescription(description: string[], indent: string, result: string[]) {
1135 for (const line of description) {
1136 result.push(indent + '// ' + line);
1137 }
1138 }
1140 >
1141 > class RawSettingsContentBuilder extends SettingsContentBuilder {
1142 >
1143 > constructor(private indent: string = '\t') {
1144 super(0);
1145 }
1147 > override pushGroup(settingsGroups: ISettingsGroup): void {
1148 this._pushGroup(settingsGroups, this.indent);
1149 }
1151 > }
1152 >
1153 > export class DefaultRawSettingsEditorModel extends Disposable {
1154 >
1155 > private _content: string | null = null;
1156 >
1157 > private readonly _onDidContentChanged = this._register(new Emitter<void>());
1158 > readonly onDidContentChanged = this._onDidContentChanged.event;
1159 >
1160 > constructor(private defaultSettings: DefaultSettings) {
1161 super();
1162 this._register(defaultSettings.onDidChange(() => {
1165 }));
1166 }
1168 > get content(): string {
1169 if (this._content === null) {
1170 const builder = new RawSettingsContentBuilder();
1178 return this._content;
1179 }
1181 >
1182 function escapeInvisibleChars(enumValue: string): string {
1183 return enumValue && enumValue
1185 .replace(/\r/g, '\\r');
1186 }
1188 > export function defaultKeybindingsContents(keybindingService: IKeybindingService): string {
1189 const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Override key bindings by placing them into your key bindings file.");
1190 return defaultsHeader + '\n' + keybindingService.getDefaultKeybindingsContent();
1191 }
1193 > export class DefaultKeybindingsEditorModel implements IKeybindingsEditorModel<any> {
1194 >
1195 > private _content: string | undefined;
1196 >
1197 > constructor(private _uri: URI,
1198 @IKeybindingService private readonly keybindingService: IKeybindingService) {
1199 }
1201 > get uri(): URI {
1202 return this._uri;
1203 }
1205 > get content(): string {
1206 if (!this._content) {
1207 this._content = defaultKeybindingsContents(this.keybindingService);
src/vs/base/common/arrays.ts 404 covered LOC · 72 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- arrays.ts
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 { findFirstIdxMonotonousOrArrLen } from './arraysFind.js';
7 > import { CancellationToken } from './cancellation.js';
8 > import { CancellationError } from './errors.js';
9 > import { ISplice } from './sequence.js';
10 >
11 > /**
12 > * Returns the last entry and the initial N-1 entries of the array, as a tuple of [rest, last].
13 > *
14 > * The array must have at least one element.
15 > *
16 > * @param arr The input array
17 > * @returns A tuple of [rest, last] where rest is all but the last element and last is the last element
18 > * @throws Error if the array is empty
19 > */
20 > export function tail<T>(arr: T[]): [T[], T] {
21 if (arr.length === 0) {
22 throw new Error('Invalid tail call');
25 return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
26 }
27 > arrays.ts
28 > export function equals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
29 if (one === other) {
30 return true;
47 return true;
48 }
49 > arrays.ts
50 > /**
51 > * Remove the element at `index` by replacing it with the last element. This is faster than `splice`
52 > * but changes the order of the array
53 > */
54 > export function removeFastWithoutKeepingOrder<T>(array: T[], index: number) {
55 const last = array.length - 1;
56 if (index < last) {
59 array.pop();
60 }
61 > arrays.ts
62 > /**
63 > * Performs a binary search algorithm over a sorted array.
64 > *
65 > * @param array The array being searched.
66 > * @param key The value we search for.
67 > * @param comparator A function that takes two array elements and returns zero
68 > * if they are equal, a negative number if the first element precedes the
69 > * second one in the sorting order, or a positive number if the second element
70 > * precedes the first one.
71 > * @return See {@link binarySearch2}
72 > */
73 > export function binarySearch<T>(array: ReadonlyArray<T>, key: T, comparator: (op1: T, op2: T) => number): number {
74 return binarySearch2(array.length, i => comparator(array[i], key));
75 }
76 > arrays.ts
77 > /**
78 > * Performs a binary search algorithm over a sorted collection. Useful for cases
79 > * when we need to perform a binary search over something that isn't actually an
80 > * array, and converting data to an array would defeat the use of binary search
81 > * in the first place.
82 > *
83 > * @param length The collection length.
84 > * @param compareToKey A function that takes an index of an element in the
85 > * collection and returns zero if the value at this index is equal to the
86 > * search key, a negative number if the value precedes the search key in the
87 > * sorting order, or a positive number if the search key precedes the value.
88 > * @return A non-negative index of an element, if found. If not found, the
89 > * result is -(n+1) (or ~n, using bitwise notation), where n is the index
90 > * where the key should be inserted to maintain the sorting order.
91 > */
92 > export function binarySearch2(length: number, compareToKey: (index: number) => number): number {
93 let low = 0,
94 high = length - 1;
107 return -(low + 1);
108 }
109 > arrays.ts
110 > type Compare<T> = (a: T, b: T) => number;
111 >
112 > /**
113 > * Finds the nth smallest element in the array using quickselect algorithm.
114 > * The data does not need to be sorted.
115 > *
116 > * @param nth The zero-based index of the element to find (0 = smallest, 1 = second smallest, etc.)
117 > * @param data The unsorted array
118 > * @param compare A comparator function that defines the sort order
119 > * @returns The nth smallest element
120 > * @throws TypeError if nth is >= data.length
121 > */
122 > export function quickSelect<T>(nth: number, data: T[], compare: Compare<T>): T {
123
124 nth = nth | 0;
152 }
153 }
154 > arrays.ts
155 > export function groupBy<T>(data: ReadonlyArray<T>, compare: (a: T, b: T) => number): T[][] {
156 const result: T[][] = [];
157 let currentGroup: T[] | undefined = undefined;
166 return result;
167 }
168 > arrays.ts
169 > /**
170 > * Splits the given items into a list of (non-empty) groups.
171 > * `shouldBeGrouped` is used to decide if two consecutive items should be in the same group.
172 > * The order of the items is preserved.
173 > */
174 > export function* groupAdjacentBy<T>(items: Iterable<T>, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable<T[]> {
175 let currentGroup: T[] | undefined;
176 let last: T | undefined;
190 }
191 }
192 > arrays.ts
193 > export function forEachAdjacent<T>(arr: T[], f: (item1: T | undefined, item2: T | undefined) => void): void {
194 for (let i = 0; i <= arr.length; i++) {
195 f(i === 0 ? undefined : arr[i - 1], i === arr.length ? undefined : arr[i]);
196 }
197 }
198 > arrays.ts
199 > export function forEachWithNeighbors<T>(arr: T[], f: (before: T | undefined, element: T, after: T | undefined) => void): void {
200 for (let i = 0; i < arr.length; i++) {
201 f(i === 0 ? undefined : arr[i - 1], arr[i], i + 1 === arr.length ? undefined : arr[i + 1]);
202 }
203 }
204 > arrays.ts
205 > export function concatArrays<T extends any[]>(...arrays: T): T[number][number][] {
206 return [].concat(...arrays);
207 }
208 > arrays.ts
209 > interface IMutableSplice<T> extends ISplice<T> {
210 > readonly toInsert: T[];
211 > deleteCount: number;
212 > }
213 >
214 > /**
215 > * Diffs two *sorted* arrays and computes the splices which apply the diff.
216 > */
217 > export function sortedDiff<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, compare: (a: T, b: T) => number): ISplice<T>[] {
218 const result: IMutableSplice<T>[] = [];
219
266 return result;
267 }
268 > arrays.ts
269 > /**
270 > * Takes two *sorted* arrays and computes their delta (removed, added elements).
271 > * Finishes in `Math.min(before.length, after.length)` steps.
272 > */
273 > export function delta<T>(before: ReadonlyArray<T>, after: ReadonlyArray<T>, compare: (a: T, b: T) => number): { removed: T[]; added: T[] } {
274 const splices = sortedDiff(before, after, compare);
275 const removed: T[] = [];
283 return { removed, added };
284 }
285 > arrays.ts
286 > /**
287 > * Returns the top N elements from the array.
288 > *
289 > * Faster than sorting the entire array when the array is a lot larger than N.
290 > *
291 > * @param array The unsorted array.
292 > * @param compare A sort function for the elements.
293 > * @param n The number of elements to return.
294 > * @return The first n elements from array when sorted with compare.
295 > */
296 > export function top<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, n: number): T[] {
297 if (n === 0) {
298 return [];
302 return result;
303 }
304 > arrays.ts
305 > /**
306 > * Asynchronous variant of `top()` allowing for splitting up work in batches between which the event loop can run.
307 > *
308 > * Returns the top N elements from the array.
309 > *
310 > * Faster than sorting the entire array when the array is a lot larger than N.
311 > *
312 > * @param array The unsorted array.
313 > * @param compare A sort function for the elements.
314 > * @param n The number of elements to return.
315 > * @param batch The number of elements to examine before yielding to the event loop.
316 > * @return The first n elements from array when sorted with compare.
317 > */
318 > export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): Promise<T[]> {
319 if (n === 0) {
320 return Promise.resolve([]);
339 });
340 }
341 > arrays.ts
342 function topStep<T>(array: ReadonlyArray<T>, compare: (a: T, b: T) => number, result: T[], i: number, m: number): void {
343 for (const n = result.length; i < m; i++) {
350 }
351 }
352 > arrays.ts
353 > /**
354 > * @returns New array with all falsy values removed. The original array IS NOT modified.
355 > */
356 > export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
357 return array.filter((e): e is T => !!e);
358 }
359 > arrays.ts
360 > /**
361 > * Remove all falsy values from `array`. The original array IS modified.
362 > */
363 > export function coalesceInPlace<T>(array: Array<T | undefined | null>): asserts array is Array<T> {
364 let to = 0;
365 for (let i = 0; i < array.length; i++) {
371 array.length = to;
372 }
373 > arrays.ts
374 > /**
375 > * @deprecated Use `Array.copyWithin` instead
376 > */
377 > export function move(array: unknown[], from: number, to: number): void {
378 array.splice(to, 0, array.splice(from, 1)[0]);
379 }
380 > arrays.ts
381 > /**
382 > * @returns false if the provided object is an array and not empty.
383 > */
384 > export function isFalsyOrEmpty(obj: unknown): boolean {
385 return !Array.isArray(obj) || obj.length === 0;
386 }
387 > arrays.ts
388 > /**
389 > * @returns True if the provided object is an array and has at least one element.
390 > */
391 > export function isNonEmptyArray<T>(obj: T[] | undefined | null): obj is T[];
392 > export function isNonEmptyArray<T>(obj: readonly T[] | undefined | null): obj is readonly T[];
393 > export function isNonEmptyArray<T>(obj: T[] | readonly T[] | undefined | null): obj is T[] | readonly T[] {
394 return Array.isArray(obj) && obj.length > 0;
395 }
396 > arrays.ts
397 > /**
398 > * Removes duplicates from the given array. The optional keyFn allows to specify
399 > * how elements are checked for equality by returning an alternate value for each.
400 > */
401 > export function distinct<T>(array: ReadonlyArray<T>, keyFn: (value: T) => unknown = value => value): T[] {
402 const seen = new Set<any>();
403
411 });
412 }
413 > arrays.ts
414 > export function uniqueFilter<T, R>(keyFn: (t: T) => R): (t: T) => boolean {
415 const seen = new Set<R>();
416
426 };
427 }
428 > arrays.ts
429 > export function commonPrefixLength<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, equals: (a: T, b: T) => boolean = (a, b) => a === b): number {
430 let result = 0;
431
436 return result;
437 }
438 > arrays.ts
439 > export function range(to: number): number[];
440 > export function range(from: number, to: number): number[];
441 > export function range(arg: number, to?: number): number[] {
442 let from = typeof to === 'number' ? arg : 0;
443
463 return result;
464 }
465 > arrays.ts
466 > export function index<T>(array: ReadonlyArray<T>, indexer: (t: T) => string): { [key: string]: T };
467 > export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, mapper: (t: T) => R): { [key: string]: R };
468 > export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, mapper?: (t: T) => R): { [key: string]: R } {
469 return array.reduce((r, t) => {
470 r[indexer(t)] = mapper ? mapper(t) : t;
472 }, Object.create(null));
473 }
474 > arrays.ts
475 > /**
476 > * Inserts an element into an array. Returns a function which, when
477 > * called, will remove that element from the array.
478 > *
479 > * @deprecated In almost all cases, use a `Set<T>` instead.
480 > */
481 > export function insert<T>(array: T[], element: T): () => void {
482 array.push(element);
483
484 return () => remove(array, element);
485 }
486 > arrays.ts
487 > /**
488 > * Removes an element from an array if it can be found.
489 > *
490 > * @deprecated In almost all cases, use a `Set<T>` instead.
491 > */
492 > export function remove<T>(array: T[], element: T): T | undefined {
493 const index = array.indexOf(element);
494 if (index > -1) {
500 return undefined;
501 }
502 > arrays.ts
503 > /**
504 > * Insert `insertArr` inside `target` at `insertIndex`.
505 > * Please don't touch unless you understand https://jsperf.com/inserting-an-array-within-an-array
506 > */
507 > export function arrayInsert<T>(target: T[], insertIndex: number, insertArr: T[]): T[] {
508 const before = target.slice(0, insertIndex);
509 const after = target.slice(insertIndex);
510 return before.concat(insertArr, after);
511 }
512 > arrays.ts
513 > /**
514 > * Uses Fisher-Yates shuffle to shuffle the given array
515 > */
516 > export function shuffle<T>(array: T[], _seed?: number): void {
517 let rand: () => number;
518
536 }
537 }
538 > arrays.ts
539 > /**
540 > * Pushes an element to the start of the array, if found.
541 > */
542 > export function pushToStart<T>(arr: T[], value: T): void {
543 const index = arr.indexOf(value);
544
548 }
549 }
550 > arrays.ts
551 > /**
552 > * Pushes an element to the end of the array, if found.
553 > */
554 > export function pushToEnd<T>(arr: T[], value: T): void {
555 const index = arr.indexOf(value);
556
560 }
561 }
562 > arrays.ts
563 > export function pushMany<T>(arr: T[], items: ReadonlyArray<T>): void {
564 for (const item of items) {
565 arr.push(item);
566 }
567 }
568 > arrays.ts
569 > export function mapArrayOrNot<T, U>(items: T | T[], fn: (_: T) => U): U | U[] {
570 return Array.isArray(items) ?
571 items.map(fn) :
572 fn(items);
573 }
574 > arrays.ts
575 > export function mapFilter<T, U>(array: ReadonlyArray<T>, fn: (t: T) => U | undefined): U[] {
576 const result: U[] = [];
577 for (const item of array) {
583 return result;
584 }
585 > arrays.ts
586 > export function withoutDuplicates<T>(array: ReadonlyArray<T>): T[] {
587 const s = new Set(array);
588 return Array.from(s);
589 }
590 > arrays.ts
591 > export function asArray<T>(x: T | T[]): T[];
592 > export function asArray<T>(x: T | readonly T[]): readonly T[];
593 > export function asArray<T>(x: T | T[]): T[] {
594 return Array.isArray(x) ? x : [x];
595 }
596 > arrays.ts
597 > export function getRandomElement<T>(arr: T[]): T | undefined {
598 return arr[Math.floor(Math.random() * arr.length)];
599 }
600 > arrays.ts
601 > /**
602 > * Insert the new items in the array.
603 > * @param array The original array.
604 > * @param start The zero-based location in the array from which to start inserting elements.
605 > * @param newItems The items to be inserted
606 > */
607 > export function insertInto<T>(array: T[], start: number, newItems: T[]): void {
608 const startIdx = getActualStartIndex(array, start);
609 const originalLength = array.length;
619 }
620 }
621 > arrays.ts
622 > /**
623 > * Removes elements from an array and inserts new elements in their place, returning the deleted elements. Alternative to the native Array.splice method, it
624 > * can only support limited number of items due to the maximum call stack size limit.
625 > * @param array The original array.
626 > * @param start The zero-based location in the array from which to start removing elements.
627 > * @param deleteCount The number of elements to remove.
628 > * @returns An array containing the elements that were deleted.
629 > */
630 > export function splice<T>(array: T[], start: number, deleteCount: number, newItems: T[]): T[] {
631 const index = getActualStartIndex(array, start);
632 let result = array.splice(index, deleteCount);
638 return result;
639 }
640 > arrays.ts
641 > /**
642 > * Determine the actual start index (same logic as the native splice() or slice())
643 > * If greater than the length of the array, start will be set to the length of the array. In this case, no element will be deleted but the method will behave as an adding function, adding as many element as item[n*] provided.
644 > * If negative, it will begin that many elements from the end of the array. (In this case, the origin -1, meaning -n is the index of the nth last element, and is therefore equivalent to the index of array.length - n.) If array.length + start is less than 0, it will begin from index 0.
645 > * @param array The target array.
646 > * @param start The operation index.
647 > */
648 function getActualStartIndex<T>(array: T[], start: number): number {
649 return start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length);
650 }
651 > arrays.ts
652 >
653 >
654 > /**
655 > * When comparing two values,
656 > * a negative number indicates that the first value is less than the second,
657 > * a positive number indicates that the first value is greater than the second,
658 > * and zero indicates that neither is the case.
659 > */
660 > export type CompareResult = number;
661 >
662 > export namespace CompareResult {
663 > export function isLessThan(result: CompareResult): boolean {
664 return result < 0;
665 }
666 > arrays.ts
667 > export function isLessThanOrEqual(result: CompareResult): boolean {
668 return result <= 0;
669 }
670 > arrays.ts
671 > export function isGreaterThan(result: CompareResult): boolean {
672 return result > 0;
673 }
674 > arrays.ts
675 > export function isNeitherLessOrGreaterThan(result: CompareResult): boolean {
676 return result === 0;
677 }
678 > arrays.ts
679 > export const greaterThan = 1;
680 > export const lessThan = -1;
681 > export const neitherLessOrGreaterThan = 0;
682 > }
683 >
684 > /**
685 > * A comparator `c` defines a total order `<=` on `T` as following:
686 > * `c(a, b) <= 0` iff `a` <= `b`.
687 > * We also have `c(a, b) == 0` iff `c(b, a) == 0`.
688 > */
689 > export type Comparator<T> = (a: T, b: T) => CompareResult;
690 >
691 > export function compareBy<TItem, TCompareBy>(selector: (item: TItem) => TCompareBy, comparator: Comparator<TCompareBy>): Comparator<TItem> {
692 return (a, b) => comparator(selector(a), selector(b));
693 }
694 > arrays.ts
695 > export function tieBreakComparators<TItem>(...comparators: Comparator<TItem>[]): Comparator<TItem> {
696 return (item1, item2) => {
697 for (const comparator of comparators) {
704 };
705 }
706 > arrays.ts
707 > /**
708 > * The natural order on numbers.
709 > */
710 > export const numberComparator: Comparator<number> = (a, b) => a - b;
711 >
712 > export const booleanComparator: Comparator<boolean> = (a, b) => numberComparator(a ? 1 : 0, b ? 1 : 0);
713 >
714 > export function reverseOrder<TItem>(comparator: Comparator<TItem>): Comparator<TItem> {
715 return (a, b) => -comparator(a, b);
716 }
717 > arrays.ts
718 > /**
719 > * Returns a new comparator that treats `undefined` as the smallest value.
720 > * All other values are compared using the given comparator.
721 > */
722 > export function compareUndefinedSmallest<T>(comparator: Comparator<T>): Comparator<T | undefined> {
723 return (a, b) => {
724 if (a === undefined) {
731 };
732 }
733 > arrays.ts
734 > export class ArrayQueue<T> {
735 > private readonly items: readonly T[];
736 > private firstIdx = 0;
737 > private lastIdx: number;
738 >
739 > /**
740 > * Constructs a queue that is backed by the given array. Runtime is O(1).
741 > */
742 > constructor(items: readonly T[]) {
743 this.items = items;
744 this.lastIdx = this.items.length - 1;
745 }
746 > arrays.ts
747 > get length(): number {
748 return this.lastIdx - this.firstIdx + 1;
749 }
750 > arrays.ts
751 > /**
752 > * Consumes elements from the beginning of the queue as long as the predicate returns true.
753 > * If no elements were consumed, `null` is returned. Has a runtime of O(result.length).
754 > */
755 > takeWhile(predicate: (value: T) => boolean): T[] | null {
756 // P(k) := k <= this.lastIdx && predicate(this.items[k])
757 // Find s := min { k | k >= this.firstIdx && !P(k) } and return this.data[this.firstIdx...s)
765 return result;
766 }
767 > arrays.ts
768 > /**
769 > * Consumes elements from the end of the queue as long as the predicate returns true.
770 > * If no elements were consumed, `null` is returned.
771 > * The result has the same order as the underlying array!
772 > */
773 > takeFromEndWhile(predicate: (value: T) => boolean): T[] | null {
774 // P(k) := this.firstIdx >= k && predicate(this.items[k])
775 // Find s := max { k | k <= this.lastIdx && !P(k) } and return this.data(s...this.lastIdx]
783 return result;
784 }
785 > arrays.ts
786 > peek(): T | undefined {
787 if (this.length === 0) {
788 return undefined;
790 return this.items[this.firstIdx];
791 }
792 > arrays.ts
793 > peekLast(): T | undefined {
794 if (this.length === 0) {
795 return undefined;
797 return this.items[this.lastIdx];
798 }
799 > arrays.ts
800 > dequeue(): T | undefined {
801 const result = this.items[this.firstIdx];
802 this.firstIdx++;
803 return result;
804 }
805 > arrays.ts
806 > removeLast(): T | undefined {
807 const result = this.items[this.lastIdx];
808 this.lastIdx--;
809 return result;
810 }
811 > arrays.ts
812 > takeCount(count: number): T[] {
813 const result = this.items.slice(this.firstIdx, this.firstIdx + count);
814 this.firstIdx += count;
815 return result;
816 }
817 > } arrays.ts
818 >
819 > /**
820 > * This class is faster than an iterator and array for lazy computed data.
821 > */
822 > export class CallbackIterable<T> {
823 > public static readonly empty = new CallbackIterable<never>(_callback => { });
824 >
825 > constructor(
826 > /**
827 > * Calls the callback for every item.
828 > * Stops when the callback returns false.
829 > */
830 > public readonly iterate: (callback: (item: T) => boolean) => void
831 > ) {
832 > }
833 >
834 > forEach(handler: (item: T) => void) {
835 this.iterate(item => { handler(item); return true; });
836 }
837 > arrays.ts
838 > toArray(): T[] {
839 const result: T[] = [];
840 this.iterate(item => { result.push(item); return true; });
841 return result;
842 }
843 > arrays.ts
844 > filter(predicate: (item: T) => boolean): CallbackIterable<T> {
845 return new CallbackIterable(cb => this.iterate(item => predicate(item) ? cb(item) : true));
846 }
847 > arrays.ts
848 > map<TResult>(mapFn: (item: T) => TResult): CallbackIterable<TResult> {
849 return new CallbackIterable<TResult>(cb => this.iterate(item => cb(mapFn(item))));
850 }
851 > arrays.ts
852 > some(predicate: (item: T) => boolean): boolean {
853 let result = false;
854 this.iterate(item => { result = predicate(item); return !result; });
855 return result;
856 }
857 > arrays.ts
858 > findFirst(predicate: (item: T) => boolean): T | undefined {
859 let result: T | undefined;
860 this.iterate(item => {
867 return result;
868 }
869 > arrays.ts
870 > findLast(predicate: (item: T) => boolean): T | undefined {
871 let result: T | undefined;
872 this.iterate(item => {
878 return result;
879 }
880 > arrays.ts
881 > findLastMaxBy(comparator: Comparator<T>): T | undefined {
882 let result: T | undefined;
883 let first = true;
891 return result;
892 }
893 > } arrays.ts
894 >
895 > /**
896 > * Represents a re-arrangement of items in an array.
897 > */
898 > export class Permutation {
899 > constructor(private readonly _indexMap: readonly number[]) { }
900 >
901 > /**
902 > * Returns a permutation that sorts the given array according to the given compare function.
903 > */
904 > public static createSortPermutation<T>(arr: readonly T[], compareFn: (a: T, b: T) => number): Permutation {
905 const sortIndices = Array.from(arr.keys()).sort((index1, index2) => compareFn(arr[index1], arr[index2]));
906 return new Permutation(sortIndices);
907 }
908 > arrays.ts
909 > /**
910 > * Returns a new array with the elements of the given array re-arranged according to this permutation.
911 > */
912 > apply<T>(arr: readonly T[]): T[] {
913 return arr.map((_, index) => arr[this._indexMap[index]]);
914 }
915 > arrays.ts
916 > /**
917 > * Returns a new permutation that undoes the re-arrangement of this permutation.
918 > */
919 > inverse(): Permutation {
920 const inverseIndexMap = this._indexMap.slice();
921 for (let i = 0; i < this._indexMap.length; i++) {
924 return new Permutation(inverseIndexMap);
925 }
926 > } arrays.ts
927 >
928 > /**
929 > * Asynchronous variant of `Array.find()`, returning the first element in
930 > * the array for which the predicate returns true.
931 > *
932 > * This implementation does not bail early and waits for all promises to
933 > * resolve before returning.
934 > */
935 export async function findAsync<T>(array: readonly T[], predicate: (element: T, index: number) => Promise<boolean>): Promise<T | undefined> {
936 const results = await Promise.all(array.map(
940 return results.find(r => r.ok)?.element;
941 }
942 > arrays.ts
943 > export function sum(array: readonly number[]): number {
944 return array.reduce((acc, value) => acc + value, 0);
945 }
946 > arrays.ts
947 > export function sumBy<T>(array: readonly T[], selector: (value: T) => number): number {
948 return array.reduce((acc, value) => acc + selector(value), 0);
949 }
src/vs/base/common/json.ts 399 covered LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- json.ts
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 > export const enum ScanError {
7 > None = 0,
8 > UnexpectedEndOfComment = 1,
9 > UnexpectedEndOfString = 2,
10 > UnexpectedEndOfNumber = 3,
11 > InvalidUnicode = 4,
12 > InvalidEscapeCharacter = 5,
13 > InvalidCharacter = 6
14 > }
15 >
16 > export const enum SyntaxKind {
17 > OpenBraceToken = 1,
18 > CloseBraceToken = 2,
19 > OpenBracketToken = 3,
20 > CloseBracketToken = 4,
21 > CommaToken = 5,
22 > ColonToken = 6,
23 > NullKeyword = 7,
24 > TrueKeyword = 8,
25 > FalseKeyword = 9,
26 > StringLiteral = 10,
27 > NumericLiteral = 11,
28 > LineCommentTrivia = 12,
29 > BlockCommentTrivia = 13,
30 > LineBreakTrivia = 14,
31 > Trivia = 15,
32 > Unknown = 16,
33 > EOF = 17
34 > }
35 >
36 > /**
37 > * The scanner object, representing a JSON scanner at a position in the input string.
38 > */
39 > export interface JSONScanner {
40 > /**
41 > * Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
42 > */
43 > setPosition(pos: number): void;
44 > /**
45 > * Read the next token. Returns the token code.
46 > */
47 > scan(): SyntaxKind;
48 > /**
49 > * Returns the current scan position, which is after the last read token.
50 > */
51 > getPosition(): number;
52 > /**
53 > * Returns the last read token.
54 > */
55 > getToken(): SyntaxKind;
56 > /**
57 > * Returns the last read token value. The value for strings is the decoded string content. For numbers its of type number, for boolean it's true or false.
58 > */
59 > getTokenValue(): string;
60 > /**
61 > * The start offset of the last read token.
62 > */
63 > getTokenOffset(): number;
64 > /**
65 > * The length of the last read token.
66 > */
67 > getTokenLength(): number;
68 > /**
69 > * An error code of the last scan.
70 > */
71 > getTokenError(): ScanError;
72 > }
73 >
74 >
75 >
76 > export interface ParseError {
77 > error: ParseErrorCode;
78 > offset: number;
79 > length: number;
80 > }
81 >
82 > export const enum ParseErrorCode {
83 > InvalidSymbol = 1,
84 > InvalidNumberFormat = 2,
85 > PropertyNameExpected = 3,
86 > ValueExpected = 4,
87 > ColonExpected = 5,
88 > CommaExpected = 6,
89 > CloseBraceExpected = 7,
90 > CloseBracketExpected = 8,
91 > EndOfFileExpected = 9,
92 > InvalidCommentToken = 10,
93 > UnexpectedEndOfComment = 11,
94 > UnexpectedEndOfString = 12,
95 > UnexpectedEndOfNumber = 13,
96 > InvalidUnicode = 14,
97 > InvalidEscapeCharacter = 15,
98 > InvalidCharacter = 16
99 > }
100 >
101 > export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
102 >
103 > export interface Node {
104 > readonly type: NodeType;
105 > readonly value?: any;
106 > readonly offset: number;
107 > readonly length: number;
108 > readonly colonOffset?: number;
109 > readonly parent?: Node;
110 > readonly children?: Node[];
111 > }
112 >
113 > export type Segment = string | number;
114 > export type JSONPath = Segment[];
115 >
116 > export interface Location {
117 > /**
118 > * The previous property key or literal value (string, number, boolean or null) or undefined.
119 > */
120 > previousNode?: Node;
121 > /**
122 > * The path describing the location in the JSON document. The path consists of a sequence strings
123 > * representing an object property or numbers for array indices.
124 > */
125 > path: JSONPath;
126 > /**
127 > * Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
128 > * '*' will match a single segment, of any property name or index.
129 > * '**' will match a sequence of segments or no segment, of any property name or index.
130 > */
131 > matches: (patterns: JSONPath) => boolean;
132 > /**
133 > * If set, the location's offset is at a property key.
134 > */
135 > isAtPropertyKey: boolean;
136 > }
137 >
138 > export interface ParseOptions {
139 > disallowComments?: boolean;
140 > allowTrailingComma?: boolean;
141 > allowEmptyContent?: boolean;
142 > }
143 >
144 > export namespace ParseOptions {
145 > export const DEFAULT = {
146 > allowTrailingComma: true
147 > };
148 > }
149 >
150 > export interface JSONVisitor {
151 > /**
152 > * Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
153 > */
154 > onObjectBegin?: (offset: number, length: number) => void;
155 >
156 > /**
157 > * Invoked when a property is encountered. The offset and length represent the location of the property name.
158 > */
159 > onObjectProperty?: (property: string, offset: number, length: number) => void;
160 >
161 > /**
162 > * Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
163 > */
164 > onObjectEnd?: (offset: number, length: number) => void;
165 >
166 > /**
167 > * Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
168 > */
169 > onArrayBegin?: (offset: number, length: number) => void;
170 >
171 > /**
172 > * Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
173 > */
174 > onArrayEnd?: (offset: number, length: number) => void;
175 >
176 > /**
177 > * Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
178 > */
179 > onLiteralValue?: (value: any, offset: number, length: number) => void;
180 >
181 > /**
182 > * Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
183 > */
184 > onSeparator?: (character: string, offset: number, length: number) => void;
185 >
186 > /**
187 > * When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
188 > */
189 > onComment?: (offset: number, length: number) => void;
190 >
191 > /**
192 > * Invoked on an error.
193 > */
194 > onError?: (error: ParseErrorCode, offset: number, length: number) => void;
195 > }
196 >
197 > /**
198 > * Creates a JSON scanner on the given text.
199 > * If ignoreTrivia is set, whitespaces or comments are ignored.
200 > */
201 > export function createScanner(text: string, ignoreTrivia: boolean = false): JSONScanner {
202
203 let pos = 0;
558 };
559 }
560 > json.ts
561 function isWhitespace(ch: number): boolean {
562 return ch === CharacterCodes.space || ch === CharacterCodes.tab || ch === CharacterCodes.verticalTab || ch === CharacterCodes.formFeed ||
564 ch === CharacterCodes.narrowNoBreakSpace || ch === CharacterCodes.mathematicalSpace || ch === CharacterCodes.ideographicSpace || ch === CharacterCodes.byteOrderMark;
565 }
566 > json.ts
567 function isLineBreak(ch: number): boolean {
568 return ch === CharacterCodes.lineFeed || ch === CharacterCodes.carriageReturn || ch === CharacterCodes.lineSeparator || ch === CharacterCodes.paragraphSeparator;
569 }
570 > json.ts
571 function isDigit(ch: number): boolean {
572 return ch >= CharacterCodes._0 && ch <= CharacterCodes._9;
573 }
574 > json.ts
575 > const enum CharacterCodes {
576 > nullCharacter = 0,
577 > maxAsciiCharacter = 0x7F,
578 >
579 > lineFeed = 0x0A, // \n
580 > carriageReturn = 0x0D, // \r
581 > lineSeparator = 0x2028,
582 > paragraphSeparator = 0x2029,
583 >
584 > // REVIEW: do we need to support this? The scanner doesn't, but our IText does. This seems
585 > // like an odd disparity? (Or maybe it's completely fine for them to be different).
586 > nextLine = 0x0085,
587 >
588 > // Unicode 3.0 space characters
589 > space = 0x0020, // " "
590 > nonBreakingSpace = 0x00A0, //
591 > enQuad = 0x2000,
592 > emQuad = 0x2001,
593 > enSpace = 0x2002,
594 > emSpace = 0x2003,
595 > threePerEmSpace = 0x2004,
596 > fourPerEmSpace = 0x2005,
597 > sixPerEmSpace = 0x2006,
598 > figureSpace = 0x2007,
599 > punctuationSpace = 0x2008,
600 > thinSpace = 0x2009,
601 > hairSpace = 0x200A,
602 > zeroWidthSpace = 0x200B,
603 > narrowNoBreakSpace = 0x202F,
604 > ideographicSpace = 0x3000,
605 > mathematicalSpace = 0x205F,
606 > ogham = 0x1680,
607 >
608 > _ = 0x5F,
609 > $ = 0x24,
610 >
611 > _0 = 0x30,
612 > _1 = 0x31,
613 > _2 = 0x32,
614 > _3 = 0x33,
615 > _4 = 0x34,
616 > _5 = 0x35,
617 > _6 = 0x36,
618 > _7 = 0x37,
619 > _8 = 0x38,
620 > _9 = 0x39,
621 >
622 > a = 0x61,
623 > b = 0x62,
624 > c = 0x63,
625 > d = 0x64,
626 > e = 0x65,
627 > f = 0x66,
628 > g = 0x67,
629 > h = 0x68,
630 > i = 0x69,
631 > j = 0x6A,
632 > k = 0x6B,
633 > l = 0x6C,
634 > m = 0x6D,
635 > n = 0x6E,
636 > o = 0x6F,
637 > p = 0x70,
638 > q = 0x71,
639 > r = 0x72,
640 > s = 0x73,
641 > t = 0x74,
642 > u = 0x75,
643 > v = 0x76,
644 > w = 0x77,
645 > x = 0x78,
646 > y = 0x79,
647 > z = 0x7A,
648 >
649 > A = 0x41,
650 > B = 0x42,
651 > C = 0x43,
652 > D = 0x44,
653 > E = 0x45,
654 > F = 0x46,
655 > G = 0x47,
656 > H = 0x48,
657 > I = 0x49,
658 > J = 0x4A,
659 > K = 0x4B,
660 > L = 0x4C,
661 > M = 0x4D,
662 > N = 0x4E,
663 > O = 0x4F,
664 > P = 0x50,
665 > Q = 0x51,
666 > R = 0x52,
667 > S = 0x53,
668 > T = 0x54,
669 > U = 0x55,
670 > V = 0x56,
671 > W = 0x57,
672 > X = 0x58,
673 > Y = 0x59,
674 > Z = 0x5A,
675 >
676 > ampersand = 0x26, // &
677 > asterisk = 0x2A, // *
678 > at = 0x40, // @
679 > backslash = 0x5C, // \
680 > bar = 0x7C, // |
681 > caret = 0x5E, // ^
682 > closeBrace = 0x7D, // }
683 > closeBracket = 0x5D, // ]
684 > closeParen = 0x29, // )
685 > colon = 0x3A, // :
686 > comma = 0x2C, // ,
687 > dot = 0x2E, // .
688 > doubleQuote = 0x22, // "
689 > equals = 0x3D, // =
690 > exclamation = 0x21, // !
691 > greaterThan = 0x3E, // >
692 > lessThan = 0x3C, // <
693 > minus = 0x2D, // -
694 > openBrace = 0x7B, // {
695 > openBracket = 0x5B, // [
696 > openParen = 0x28, // (
697 > percent = 0x25, // %
698 > plus = 0x2B, // +
699 > question = 0x3F, // ?
700 > semicolon = 0x3B, // ;
701 > singleQuote = 0x27, // '
702 > slash = 0x2F, // /
703 > tilde = 0x7E, // ~
704 >
705 > backspace = 0x08, // \b
706 > formFeed = 0x0C, // \f
707 > byteOrderMark = 0xFEFF,
708 > tab = 0x09, // \t
709 > verticalTab = 0x0B, // \v
710 > }
711 >
712 > interface NodeImpl extends Node {
713 > type: NodeType;
714 > value?: any;
715 > offset: number;
716 > length: number;
717 > colonOffset?: number;
718 > parent?: NodeImpl;
719 > children?: NodeImpl[];
720 > }
721 >
722 > /**
723 > * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
724 > */
725 > export function getLocation(text: string, position: number): Location {
726 const segments: Segment[] = []; // strings or numbers
727 const earlyReturnException = new Object();
838 };
839 }
840 > json.ts
841 >
842 > /**
843 > * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
844 > * Therefore always check the errors list to find out if the input was valid.
845 > */
846 > export function parse(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): any {
847 let currentProperty: string | null = null;
848 let currentParent: any = [];
889 return currentParent[0];
890 }
891 > json.ts
892 >
893 > /**
894 > * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
895 > */
896 > export function parseTree(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): Node {
897 let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root
898
955 return result;
956 }
957 > json.ts
958 > /**
959 > * Finds the node at the given path in a JSON DOM.
960 > */
961 > export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined {
962 if (!root) {
963 return undefined;
990 return node;
991 }
992 > json.ts
993 > /**
994 > * Gets the JSON path of the given JSON DOM node
995 > */
996 > export function getNodePath(node: Node): JSONPath {
997 if (!node.parent || !node.parent.children) {
998 return [];
1010 return path;
1011 }
1012 > json.ts
1013 > /**
1014 > * Evaluates the JavaScript object of the given JSON DOM node
1015 > */
1016 > export function getNodeValue(node: Node): any {
1017 switch (node.type) {
1018 case 'array':
1038
1039 }
1040 > json.ts
1041 > export function contains(node: Node, offset: number, includeRightBound = false): boolean {
1042 return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));
1043 }
1044 > json.ts
1045 > /**
1046 > * Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
1047 > */
1048 > export function findNodeAtOffset(node: Node, offset: number, includeRightBound = false): Node | undefined {
1049 if (contains(node, offset, includeRightBound)) {
1050 const children = node.children;
1062 return undefined;
1063 }
1064 > json.ts
1065 >
1066 > /**
1067 > * Parses the given text and invokes the visitor functions for each object, array and literal reached.
1068 > */
1069 > export function visit(text: string, visitor: JSONVisitor, options: ParseOptions = ParseOptions.DEFAULT): any {
1070
1071 const _scanner = createScanner(text, false);
1308 return true;
1309 }
1310 > json.ts
1311 > export function getNodeType(value: unknown): NodeType {
1312 switch (typeof value) {
1313 case 'boolean': return 'boolean';
src/vs/workbench/services/preferences/common/preferences.ts 346 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- preferences.ts
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 { IStringDictionary } from '../../../../base/common/collections.js';
7 > import { Event } from '../../../../base/common/event.js';
8 > import { IMatch } from '../../../../base/common/filters.js';
9 > import { IJSONSchema, IJSONSchemaMap } from '../../../../base/common/jsonSchema.js';
10 > import { ResolvedKeybinding } from '../../../../base/common/keybindings.js';
11 > import { URI } from '../../../../base/common/uri.js';
12 > import { IRange } from '../../../../editor/common/core/range.js';
13 > import { IEditorContribution } from '../../../../editor/common/editorCommon.js';
14 > import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js';
15 > import { ConfigurationDefaultValueSource, ConfigurationScope, EditPresentationTypes, IExtensionInfo } from '../../../../platform/configuration/common/configurationRegistry.js';
16 > import { IEditorOptions } from '../../../../platform/editor/common/editor.js';
17 > import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
18 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
19 > import { ResolvedKeybindingItem } from '../../../../platform/keybinding/common/resolvedKeybindingItem.js';
20 > import { DEFAULT_EDITOR_ASSOCIATION, IEditorPane } from '../../../common/editor.js';
21 > import { EditorInput } from '../../../common/editor/editorInput.js';
22 > import { Settings2EditorModel } from './preferencesModels.js';
23 >
24 > export enum SettingValueType {
25 > Null = 'null',
26 > Enum = 'enum',
27 > String = 'string',
28 > MultilineString = 'multiline-string',
29 > Integer = 'integer',
30 > Number = 'number',
31 > Boolean = 'boolean',
32 > Array = 'array',
33 > Exclude = 'exclude',
34 > Include = 'include',
35 > Complex = 'complex',
36 > NullableInteger = 'nullable-integer',
37 > NullableNumber = 'nullable-number',
38 > Object = 'object',
39 > BooleanObject = 'boolean-object',
40 > LanguageTag = 'language-tag',
41 > ExtensionToggle = 'extension-toggle',
42 > ComplexObject = 'complex-object',
43 > }
44 >
45 > export interface ISettingsGroup {
46 > id: string;
47 > range: IRange;
48 > title: string;
49 > titleRange: IRange;
50 > sections: ISettingsSection[];
51 > order?: number;
52 > extensionInfo?: IExtensionInfo;
53 > }
54 >
55 > export interface ISettingsSection {
56 > title?: string;
57 > settings: ISetting[];
58 > }
59 >
60 > export interface ISetting {
61 > range: IRange;
62 > key: string;
63 > keyRange: IRange;
64 > value: any;
65 > valueRange: IRange;
66 > description: string[];
67 > keywords?: string[];
68 > descriptionIsMarkdown?: boolean;
69 > descriptionRanges: IRange[];
70 > overrides?: ISetting[];
71 > overrideOf?: ISetting;
72 > deprecationMessage?: string;
73 > deprecationMessageIsMarkdown?: boolean;
74 >
75 > scope?: ConfigurationScope;
76 > type?: string | string[];
77 > order?: number;
78 > arrayItemType?: string;
79 > objectProperties?: IJSONSchemaMap;
80 > objectPatternProperties?: IJSONSchemaMap;
81 > objectAdditionalProperties?: boolean | IJSONSchema;
82 > propertyNames?: IJSONSchema;
83 > enum?: string[];
84 > enumDescriptions?: string[];
85 > enumDescriptionsAreMarkdown?: boolean;
86 > uniqueItems?: boolean;
87 > tags?: string[];
88 > disallowSyncIgnore?: boolean;
89 > restricted?: boolean;
90 > extensionInfo?: IExtensionInfo;
91 > validator?: (value: any) => string | null;
92 > enumItemLabels?: string[];
93 > editPresentation?: EditPresentationTypes;
94 > nonLanguageSpecificDefaultValueSource?: ConfigurationDefaultValueSource;
95 > isLanguageTagSetting?: boolean;
96 > categoryLabel?: string;
97 >
98 > // Internal properties
99 > allKeysAreBoolean?: boolean;
100 > displayExtensionId?: string;
101 > title?: string;
102 > extensionGroupTitle?: string;
103 > internalOrder?: number;
104 > }
105 >
106 > export interface IExtensionSetting extends ISetting {
107 > extensionName?: string;
108 > extensionPublisher?: string;
109 > }
110 >
111 > export interface ISearchResult {
112 > filterMatches: ISettingMatch[];
113 > exactMatch: boolean;
114 > metadata?: IFilterMetadata;
115 > }
116 >
117 > export interface ISearchResultGroup {
118 > id: string;
119 > label: string;
120 > result: ISearchResult;
121 > order: number;
122 > }
123 >
124 > export interface IFilterResult {
125 > query?: string;
126 > filteredGroups: ISettingsGroup[];
127 > allGroups: ISettingsGroup[];
128 > matches: IRange[];
129 > metadata?: IStringDictionary<IFilterMetadata>;
130 > exactMatch?: boolean;
131 > }
132 >
133 > /**
134 > * The ways a setting could match a query,
135 > * sorted in increasing order of relevance.
136 > */
137 > export enum SettingMatchType {
138 > None = 0,
139 > LanguageTagSettingMatch = 1 << 0,
140 > RemoteMatch = 1 << 1,
141 > NonContiguousQueryInSettingId = 1 << 2,
142 > DescriptionOrValueMatch = 1 << 3,
143 > NonContiguousWordsInSettingsLabel = 1 << 4,
144 > ContiguousWordsInSettingsLabel = 1 << 5,
145 > ContiguousQueryInSettingId = 1 << 6,
146 > AllWordsInSettingsLabel = 1 << 7,
147 > ExactMatch = 1 << 8,
148 > }
149 > export const SettingKeyMatchTypes = (SettingMatchType.AllWordsInSettingsLabel
150 > | SettingMatchType.ContiguousWordsInSettingsLabel
151 > | SettingMatchType.NonContiguousWordsInSettingsLabel
152 > | SettingMatchType.NonContiguousQueryInSettingId
153 > | SettingMatchType.ContiguousQueryInSettingId);
154 >
155 > export interface ISettingMatch {
156 > setting: ISetting;
157 > matches: IRange[] | null;
158 > matchType: SettingMatchType;
159 > keyMatchScore: number;
160 > score: number;
161 > providerName?: string;
162 > }
163 >
164 > export interface IScoredResults {
165 > [key: string]: IRemoteSetting;
166 > }
167 >
168 > export interface IRemoteSetting {
169 > score: number;
170 > key: string;
171 > id: string;
172 > defaultValue: string;
173 > description: string;
174 > packageId: string;
175 > extensionName?: string;
176 > extensionPublisher?: string;
177 > }
178 >
179 > export interface IFilterMetadata {
180 > requestUrl: string;
181 > requestBody: string;
182 > timestamp: number;
183 > duration: number;
184 > scoredResults: IScoredResults;
185 >
186 > /** The number of requests made, since requests are split by number of filters */
187 > requestCount?: number;
188 >
189 > /** The name of the server that actually served the request */
190 > context: string;
191 > }
192 >
193 > export interface IPreferencesEditorModel<T> {
194 > uri?: URI;
195 > getPreference(key: string): T | undefined;
196 > dispose(): void;
197 > }
198 >
199 > export type IGroupFilter = (group: ISettingsGroup) => boolean | null;
200 > export type ISettingMatcher = (setting: ISetting, group: ISettingsGroup) => { matches: IRange[]; matchType: SettingMatchType; keyMatchScore: number; score: number } | null;
201 >
202 > export interface ISettingsEditorModel extends IPreferencesEditorModel<ISetting> {
203 > readonly onDidChangeGroups: Event<void>;
204 > settingsGroups: ISettingsGroup[];
205 > filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): ISettingMatch[];
206 > updateResultGroup(id: string, resultGroup: ISearchResultGroup | undefined): IFilterResult | undefined;
207 > }
208 >
209 > export interface ISettingsEditorOptions extends IEditorOptions {
210 > target?: ConfigurationTarget;
211 > folderUri?: URI;
212 > query?: string;
213 > /**
214 > * Only works when opening the json settings file. Use `query` for settings editor.
215 > */
216 > revealSetting?: {
217 > key: string;
218 > edit?: boolean;
219 > };
220 > focusSearch?: boolean;
221 > }
222 >
223 > export interface IOpenSettingsOptions extends ISettingsEditorOptions {
224 > jsonEditor?: boolean;
225 > openToSide?: boolean;
226 > groupId?: number;
227 > }
228 >
229 > export function validateSettingsEditorOptions(options: ISettingsEditorOptions): ISettingsEditorOptions {
230 return {
231 // Inherit provided options
237 };
238 }
240 > export interface IKeybindingsEditorModel<T> extends IPreferencesEditorModel<T> {
241 > }
242 >
243 > export interface IKeybindingsEditorOptions extends IEditorOptions {
244 > query?: string;
245 > }
246 >
247 > export interface IOpenKeybindingsEditorOptions extends IKeybindingsEditorOptions {
248 > groupId?: number;
249 > }
250 >
251 > export const IPreferencesService = createDecorator<IPreferencesService>('preferencesService');
252 >
253 > export interface IPreferencesService {
254 > readonly _serviceBrand: undefined;
255 >
256 > readonly onDidDefaultSettingsContentChanged: Event<URI>;
257 >
258 > userSettingsResource: URI;
259 > workspaceSettingsResource: URI | null;
260 > getFolderSettingsResource(resource: URI): URI | null;
261 >
262 > createPreferencesEditorModel(uri: URI): Promise<IPreferencesEditorModel<ISetting> | null>;
263 > getDefaultSettingsContent(uri: URI): string | undefined;
264 > hasDefaultSettingsContent(uri: URI): boolean;
265 > createSettings2EditorModel(): Settings2EditorModel; // TODO
266 >
267 > openPreferences(): Promise<void>;
268 >
269 > openRawDefaultSettings(): Promise<IEditorPane | undefined>;
270 > openSettings(options?: IOpenSettingsOptions): Promise<IEditorPane | undefined>;
271 > openApplicationSettings(options?: IOpenSettingsOptions): Promise<IEditorPane | undefined>;
272 > openUserSettings(options?: IOpenSettingsOptions): Promise<IEditorPane | undefined>;
273 > openRemoteSettings(options?: IOpenSettingsOptions): Promise<IEditorPane | undefined>;
274 > openWorkspaceSettings(options?: IOpenSettingsOptions): Promise<IEditorPane | undefined>;
275 > openFolderSettings(options: IOpenSettingsOptions & { folderUri: IOpenSettingsOptions['folderUri'] }): Promise<IEditorPane | undefined>;
276 > openGlobalKeybindingSettings(textual: boolean, options?: IOpenKeybindingsEditorOptions): Promise<void>;
277 > openDefaultKeybindingsFile(): Promise<IEditorPane | undefined>;
278 > openLanguageSpecificSettings(languageId: string, options?: IOpenSettingsOptions): Promise<IEditorPane | undefined>;
279 > getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): Promise<URI | null>;
280 > getSetting(settingId: string): ISetting | undefined;
281 >
282 > createSplitJsonEditorInput(configurationTarget: ConfigurationTarget, resource: URI): EditorInput;
283 > }
284 >
285 > export interface KeybindingMatch {
286 > ctrlKey?: boolean;
287 > shiftKey?: boolean;
288 > altKey?: boolean;
289 > metaKey?: boolean;
290 > keyCode?: boolean;
291 > }
292 >
293 > export interface KeybindingMatches {
294 > firstPart: KeybindingMatch;
295 > chordPart: KeybindingMatch;
296 > }
297 >
298 > export interface IKeybindingItemEntry {
299 > id: string;
300 > templateId: string;
301 > keybindingItem: IKeybindingItem;
302 > commandIdMatches?: IMatch[];
303 > commandLabelMatches?: IMatch[];
304 > commandDefaultLabelMatches?: IMatch[];
305 > sourceMatches?: IMatch[];
306 > extensionIdMatches?: IMatch[];
307 > extensionLabelMatches?: IMatch[];
308 > whenMatches?: IMatch[];
309 > keybindingMatches?: KeybindingMatches;
310 > }
311 >
312 > export interface IKeybindingItem {
313 > keybinding: ResolvedKeybinding;
314 > keybindingItem: ResolvedKeybindingItem;
315 > commandLabel: string;
316 > commandDefaultLabel: string;
317 > command: string;
318 > source: string | IExtensionDescription;
319 > when: string;
320 > }
321 >
322 > export interface IKeybindingsEditorPane extends IEditorPane {
323 >
324 > readonly activeKeybindingEntry: IKeybindingItemEntry | null;
325 > readonly onDefineWhenExpression: Event<IKeybindingItemEntry>;
326 > readonly onLayout: Event<void>;
327 >
328 > search(filter: string): void;
329 > focusSearch(): void;
330 > clearSearchResults(): void;
331 > focusKeybindings(): void;
332 > recordSearchKeys(): void;
333 > toggleSortByPrecedence(): void;
334 > selectKeybinding(keybindingEntry: IKeybindingItemEntry): void;
335 > defineKeybinding(keybindingEntry: IKeybindingItemEntry, add: boolean): Promise<void>;
336 > defineWhenExpression(keybindingEntry: IKeybindingItemEntry): void;
337 > updateKeybinding(keybindingEntry: IKeybindingItemEntry, key: string, when: string | undefined): Promise<any>;
338 > removeKeybinding(keybindingEntry: IKeybindingItemEntry): Promise<any>;
339 > resetKeybinding(keybindingEntry: IKeybindingItemEntry): Promise<any>;
340 > copyKeybinding(keybindingEntry: IKeybindingItemEntry): Promise<void>;
341 > copyKeybindingCommand(keybindingEntry: IKeybindingItemEntry): Promise<void>;
342 > showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): void;
343 > }
344 >
345 > export const DEFINE_KEYBINDING_EDITOR_CONTRIB_ID = 'editor.contrib.defineKeybinding';
346 > export interface IDefineKeybindingEditorContribution extends IEditorContribution {
347 > showDefineKeybindingWidget(): void;
348 > }
349 >
350 > export const FOLDER_SETTINGS_PATH = '.vscode/settings.json';
351 > export const DEFAULT_SETTINGS_EDITOR_SETTING = 'workbench.settings.openDefaultSettings';
352 > export const USE_SPLIT_JSON_SETTING = 'workbench.settings.useSplitJSON';
353 > export const ALWAYS_SHOW_ADVANCED_SETTINGS_SETTING = 'workbench.settings.alwaysShowAdvancedSettings';
354 >
355 > export const SETTINGS_AUTHORITY = 'settings';
src/vs/base/common/map.ts 313 covered LOC · 97 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- map.ts
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 { URI } from './uri.js';
7 >
8 > export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
9 let result = map.get(key);
10 if (result === undefined) {
15 return result;
16 }
17 > map.ts
18 > export function mapToString<K, V>(map: Map<K, V>): string {
19 const entries: string[] = [];
20 map.forEach((value, key) => {
24 return `Map(${map.size}) {${entries.join(', ')}}`;
25 }
26 > map.ts
27 > export function setToString<K>(set: Set<K>): string {
28 const entries: K[] = [];
29 set.forEach(value => {
33 return `Set(${set.size}) {${entries.join(', ')}}`;
34 }
35 > map.ts
36 > interface ResourceMapKeyFn {
37 > (resource: URI): string;
38 > }
39 >
40 > class ResourceMapEntry<T> {
41 > constructor(readonly uri: URI, readonly value: T) { }
42 > }
43 >
44 function isEntries<T>(arg: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[] | undefined): arg is readonly (readonly [URI, T])[] {
45 return Array.isArray(arg);
46 }
47 > map.ts
48 > export class ResourceMap<T> implements Map<URI, T> {
49 >
50 > private static readonly defaultToKey = (resource: URI) => resource.toString();
51 >
52 > readonly [Symbol.toStringTag] = 'ResourceMap';
53 >
54 > private readonly map: Map<string, ResourceMapEntry<T>>;
55 > private readonly toKey: ResourceMapKeyFn;
56 >
57 > /**
58 > *
59 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
60 > */
61 > constructor(toKey?: ResourceMapKeyFn);
62 >
63 > /**
64 > *
65 > * @param other Another resource which this maps is created from
66 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
67 > */
68 > constructor(other?: ResourceMap<T>, toKey?: ResourceMapKeyFn);
69 >
70 > /**
71 > *
72 > * @param other Another resource which this maps is created from
73 > * @param toKey Custom uri identity function, e.g use an existing `IExtUri#getComparison`-util
74 > */
75 > constructor(entries?: readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn);
76 >
77 > constructor(arg?: ResourceMap<T> | ResourceMapKeyFn | readonly (readonly [URI, T])[], toKey?: ResourceMapKeyFn) {
78 if (arg instanceof ResourceMap) {
79 this.map = new Map(arg.map);
91 }
92 }
93 > map.ts
94 > set(resource: URI, value: T): this {
95 this.map.set(this.toKey(resource), new ResourceMapEntry(resource, value));
96 return this;
97 }
98 > map.ts
99 > get(resource: URI): T | undefined {
100 return this.map.get(this.toKey(resource))?.value;
101 }
102 > map.ts
103 > has(resource: URI): boolean {
104 return this.map.has(this.toKey(resource));
105 }
106 > map.ts
107 > get size(): number {
108 return this.map.size;
109 }
110 > map.ts
111 > clear(): void {
112 this.map.clear();
113 }
114 > map.ts
115 > delete(resource: URI): boolean {
116 return this.map.delete(this.toKey(resource));
117 }
118 > map.ts
119 > forEach(clb: (value: T, key: URI, map: Map<URI, T>) => void, thisArg?: object): void {
120 if (typeof thisArg !== 'undefined') {
121 clb = clb.bind(thisArg);
125 }
126 }
127 > map.ts
128 > *values(): MapIterator<T> {
129 for (const entry of this.map.values()) {
130 yield entry.value;
131 }
132 }
133 > map.ts
134 > *keys(): MapIterator<URI> {
135 for (const entry of this.map.values()) {
136 yield entry.uri;
137 }
138 }
139 > map.ts
140 > *entries(): MapIterator<[URI, T]> {
141 for (const entry of this.map.values()) {
142 yield [entry.uri, entry.value];
143 }
144 }
145 > map.ts
146 > *[Symbol.iterator](): MapIterator<[URI, T]> {
147 for (const [, entry] of this.map) {
148 yield [entry.uri, entry.value];
149 }
150 }
151 > } map.ts
152 >
153 > export class ResourceSet implements Set<URI> {
154 >
155 > readonly [Symbol.toStringTag]: string = 'ResourceSet';
156 >
157 > private readonly _map: ResourceMap<URI>;
158 >
159 > constructor(toKey?: ResourceMapKeyFn);
160 > constructor(entries: readonly URI[], toKey?: ResourceMapKeyFn);
161 > constructor(entriesOrKey?: readonly URI[] | ResourceMapKeyFn, toKey?: ResourceMapKeyFn) {
162 if (!entriesOrKey || typeof entriesOrKey === 'function') {
163 this._map = new ResourceMap(entriesOrKey);
167 }
168 }
169 > map.ts
170 >
171 > get size(): number {
172 return this._map.size;
173 }
174 > map.ts
175 > add(value: URI): this {
176 this._map.set(value, value);
177 return this;
178 }
179 > map.ts
180 > clear(): void {
181 this._map.clear();
182 }
183 > map.ts
184 > delete(value: URI): boolean {
185 return this._map.delete(value);
186 }
187 > map.ts
188 > forEach(callbackfn: (value: URI, value2: URI, set: Set<URI>) => void, thisArg?: unknown): void {
189 this._map.forEach((_value, key) => callbackfn.call(thisArg, key, key, this));
190 }
191 > map.ts
192 > has(value: URI): boolean {
193 return this._map.has(value);
194 }
195 > map.ts
196 > entries(): SetIterator<[URI, URI]> {
197 return this._map.entries() as unknown as SetIterator<[URI, URI]>;
198 }
199 > map.ts
200 > keys(): SetIterator<URI> {
201 return this._map.keys() as unknown as SetIterator<URI>;
202 }
203 > map.ts
204 > values(): SetIterator<URI> {
205 return this._map.keys() as unknown as SetIterator<URI>;
206 }
207 > map.ts
208 > [Symbol.iterator](): SetIterator<URI> {
209 return this.keys();
210 }
211 > } map.ts
212 >
213 >
214 > interface Item<K, V> {
215 > previous: Item<K, V> | undefined;
216 > next: Item<K, V> | undefined;
217 > key: K;
218 > value: V;
219 > }
220 >
221 > export const enum Touch {
222 > None = 0,
223 > AsOld = 1,
224 > AsNew = 2
225 > }
226 >
227 > export class LinkedMap<K, V> implements Map<K, V> {
228 >
229 > readonly [Symbol.toStringTag] = 'LinkedMap';
230 >
231 > private _map: Map<K, Item<K, V>>;
232 > private _head: Item<K, V> | undefined;
233 > private _tail: Item<K, V> | undefined;
234 > private _size: number;
235 >
236 > private _state: number;
237 >
238 > constructor() {
239 this._map = new Map<K, Item<K, V>>();
240 this._head = undefined;
243 this._state = 0;
244 }
245 > map.ts
246 > clear(): void {
247 this._map.clear();
248 this._head = undefined;
251 this._state++;
252 }
253 > map.ts
254 > isEmpty(): boolean {
255 return !this._head && !this._tail;
256 }
257 > map.ts
258 > get size(): number {
259 return this._size;
260 }
261 > map.ts
262 > get first(): V | undefined {
263 return this._head?.value;
264 }
265 > map.ts
266 > get last(): V | undefined {
267 return this._tail?.value;
268 }
269 > map.ts
270 > has(key: K): boolean {
271 return this._map.has(key);
272 }
273 > map.ts
274 > get(key: K, touch: Touch = Touch.None): V | undefined {
275 const item = this._map.get(key);
276 if (!item) {
282 return item.value;
283 }
284 > map.ts
285 > set(key: K, value: V, touch: Touch = Touch.None): this {
286 let item = this._map.get(key);
287 if (item) {
311 return this;
312 }
313 > map.ts
314 > delete(key: K): boolean {
315 return !!this.remove(key);
316 }
317 > map.ts
318 > remove(key: K): V | undefined {
319 const item = this._map.get(key);
320 if (!item) {
326 return item.value;
327 }
328 > map.ts
329 > shift(): V | undefined {
330 if (!this._head && !this._tail) {
331 return undefined;
340 return item.value;
341 }
342 > map.ts
343 > forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void {
344 const state = this._state;
345 let current = this._head;
356 }
357 }
358 > map.ts
359 > keys(): MapIterator<K> {
360 const map = this;
361 const state = this._state;
381 return iterator;
382 }
383 > map.ts
384 > values(): MapIterator<V> {
385 const map = this;
386 const state = this._state;
406 return iterator;
407 }
408 > map.ts
409 > entries(): MapIterator<[K, V]> {
410 const map = this;
411 const state = this._state;
431 return iterator;
432 }
433 > map.ts
434 > [Symbol.iterator](): MapIterator<[K, V]> {
435 return this.entries();
436 }
437 > map.ts
438 > protected trimOld(newSize: number) {
439 if (newSize >= this.size) {
440 return;
458 this._state++;
459 }
460 > map.ts
461 > protected trimNew(newSize: number) {
462 if (newSize >= this.size) {
463 return;
481 this._state++;
482 }
483 > map.ts
484 > private addItemFirst(item: Item<K, V>): void {
485 // First time Insert
486 if (!this._head && !this._tail) {
495 this._state++;
496 }
497 > map.ts
498 > private addItemLast(item: Item<K, V>): void {
499 // First time Insert
500 if (!this._head && !this._tail) {
509 this._state++;
510 }
511 > map.ts
512 > private removeItem(item: Item<K, V>): void {
513 if (item === this._head && item === this._tail) {
514 this._head = undefined;
546 this._state++;
547 }
548 > map.ts
549 > private touch(item: Item<K, V>, touch: Touch): void {
550 if (!this._head || !this._tail) {
551 throw new Error('Invalid list');
608 }
609 }
610 > map.ts
611 > toJSON(): [K, V][] {
612 const data: [K, V][] = [];
613
618 return data;
619 }
620 > map.ts
621 > fromJSON(data: [K, V][]): void {
622 this.clear();
623
626 }
627 }
628 > } map.ts
629 >
630 > abstract class Cache<K, V> extends LinkedMap<K, V> {
631 >
632 > protected _limit: number;
633 > protected _ratio: number;
634 >
635 > constructor(limit: number, ratio: number = 1) {
636 super();
637 this._limit = limit;
638 this._ratio = Math.min(Math.max(0, ratio), 1);
639 }
640 > map.ts
641 > get limit(): number {
642 return this._limit;
643 }
644 > map.ts
645 > set limit(limit: number) {
646 this._limit = limit;
647 this.checkTrim();
648 }
649 > map.ts
650 > get ratio(): number {
651 return this._ratio;
652 }
653 > map.ts
654 > set ratio(ratio: number) {
655 this._ratio = Math.min(Math.max(0, ratio), 1);
656 this.checkTrim();
657 }
658 > map.ts
659 > override get(key: K, touch: Touch = Touch.AsNew): V | undefined {
660 return super.get(key, touch);
661 }
662 > map.ts
663 > peek(key: K): V | undefined {
664 return super.get(key, Touch.None);
665 }
666 > map.ts
667 > override set(key: K, value: V): this {
668 super.set(key, value, Touch.AsNew);
669 return this;
670 }
671 > map.ts
672 > protected checkTrim() {
673 if (this.size > this._limit) {
674 this.trim(Math.round(this._limit * this._ratio));
675 }
676 }
677 > map.ts
678 > protected abstract trim(newSize: number): void;
679 > }
680 >
681 > export class LRUCache<K, V> extends Cache<K, V> {
682 >
683 > constructor(limit: number, ratio: number = 1) {
684 super(limit, ratio);
685 }
686 > map.ts
687 > protected override trim(newSize: number) {
688 this.trimOld(newSize);
689 }
690 > map.ts
691 > override set(key: K, value: V): this {
692 super.set(key, value);
693 this.checkTrim();
694 return this;
695 }
696 > } map.ts
697 >
698 > export class MRUCache<K, V> extends Cache<K, V> {
699 >
700 > constructor(limit: number, ratio: number = 1) {
701 super(limit, ratio);
702 }
703 > map.ts
704 > protected override trim(newSize: number) {
705 this.trimNew(newSize);
706 }
707 > map.ts
708 > override set(key: K, value: V): this {
709 if (this._limit <= this.size && !this.has(key)) {
710 this.trim(Math.round(this._limit * this._ratio) - 1);
714 return this;
715 }
716 > } map.ts
717 >
718 > export class CounterSet<T> {
719
720 private map = new Map<T, number>();
721 > map.ts
722 > add(value: T): CounterSet<T> {
723 this.map.set(value, (this.map.get(value) || 0) + 1);
724 return this;
725 }
726 > map.ts
727 > delete(value: T): boolean {
728 let counter = this.map.get(value) || 0;
729
742 return true;
743 }
744 > map.ts
745 > has(value: T): boolean {
746 return this.map.has(value);
747 }
748 > } map.ts
749 >
750 > /**
751 > * A map that allows access both by keys and values.
752 > * **NOTE**: values need to be unique.
753 > */
754 > export class BidirectionalMap<K, V> {
755 >
756 > private readonly _m1 = new Map<K, V>();
757 > private readonly _m2 = new Map<V, K>();
758 >
759 > constructor(entries?: readonly (readonly [K, V])[]) {
760 if (entries) {
761 for (const [key, value] of entries) {
764 }
765 }
766 > map.ts
767 > clear(): void {
768 this._m1.clear();
769 this._m2.clear();
770 }
771 > map.ts
772 > set(key: K, value: V): void {
773 this._m1.set(key, value);
774 this._m2.set(value, key);
775 }
776 > map.ts
777 > get(key: K): V | undefined {
778 return this._m1.get(key);
779 }
780 > map.ts
781 > getKey(value: V): K | undefined {
782 return this._m2.get(value);
783 }
784 > map.ts
785 > delete(key: K): boolean {
786 const value = this._m1.get(key);
787 if (value === undefined) {
792 return true;
793 }
794 > map.ts
795 > forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: unknown): void {
796 this._m1.forEach((value, key) => {
797 callbackfn.call(thisArg, value, key, this);
798 });
799 }
800 > map.ts
801 > keys(): IterableIterator<K> {
802 return this._m1.keys();
803 }
804 > map.ts
805 > values(): IterableIterator<V> {
806 return this._m1.values();
807 }
808 > } map.ts
809 >
810 > export class SetMap<K, V> {
811
812 private map = new Map<K, Set<V>>();
813 > map.ts
814 > add(key: K, value: V): void {
815 let values = this.map.get(key);
816
822 values.add(value);
823 }
824 > map.ts
825 > delete(key: K, value: V): void {
826 const values = this.map.get(key);
827
836 }
837 }
838 > map.ts
839 > forEach(key: K, fn: (value: V) => void): void {
840 const values = this.map.get(key);
841
846 values.forEach(fn);
847 }
848 > map.ts
849 > get(key: K): ReadonlySet<V> {
850 const values = this.map.get(key);
851 if (!values) {
854 return values;
855 }
856 > } map.ts
857 >
858 > export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {
859 if (a === b) {
860 return true;
879 return true;
880 }
881 > map.ts
882 > /**
883 > * A map that is addressable with an arbitrary number of keys. This is useful in high performance
884 > * scenarios where creating a composite key whenever the data is accessed is too expensive. For
885 > * example for a very hot function, constructing a string like `first-second-third` for every call
886 > * will cause a significant hit to performance.
887 > */
888 > export class NKeyMap<TValue, TKeys extends (string | boolean | number)[]> {
889 private _data: Map<any, any> = new Map();
890 > map.ts
891 > /**
892 > * Sets a value on the map. Note that unlike a standard `Map`, the first argument is the value.
893 > * This is because the spread operator is used for the keys and must be last..
894 > * @param value The value to set.
895 > * @param keys The keys for the value.
896 > */
897 > public set(value: TValue, ...keys: [...TKeys]): void {
898 let currentMap = this._data;
899 for (let i = 0; i < keys.length - 1; i++) {
907 currentMap.set(keys[keys.length - 1], value);
908 }
909 > map.ts
910 > public get(...keys: [...TKeys]): TValue | undefined {
911 let currentMap = this._data;
912 for (let i = 0; i < keys.length - 1; i++) {
919 return currentMap.get(keys[keys.length - 1]);
920 }
921 > map.ts
922 > public delete(...keys: [...TKeys]): boolean {
923 const maps: Map<any, any>[] = [this._data];
924 let currentMap = this._data;
939 return deleted;
940 }
941 > map.ts
942 > public deleteAll(...keys: Partial<TKeys>): boolean {
943 if (keys.length === 0) {
944 const hadData = this._data.size > 0;
964 return deleted;
965 }
966 > map.ts
967 > public clear(): void {
968 this._data.clear();
969 }
970 > map.ts
971 > public *getAll(...keys: Partial<TKeys>): IterableIterator<TValue> {
972 let currentMap = this._data;
973 for (const key of keys) {
980 yield* this._values(currentMap);
981 }
982 > map.ts
983 > public *values(): IterableIterator<TValue> {
984 yield* this._values(this._data);
985 }
986 > map.ts
987 > private *_values(map: Map<any, any>): IterableIterator<TValue> {
988 for (const value of map.values()) {
989 if (value instanceof Map) {
994 }
995 }
996 > map.ts
997 > /**
998 > * Get a textual representation of the map for debugging purposes.
999 > */
1000 > public toString(): string {
1001 const printMap = (map: Map<any, any>, depth: number): string => {
1002 let result = '';
1014 return printMap(this._data, 0);
1015 }
1016 > } map.ts
src/vs/base/common/color.ts 308 covered LOC · 55 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- color.ts
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 { CharCode } from './charCode.js';
7 >
8 > function roundFloat(number: number, decimalPoints: number): number {
9 > const decimal = Math.pow(10, decimalPoints);
10 > return Math.round(number * decimal) / decimal;
11 > }
12 >
13 > export class RGBA {
14 > _rgbaBrand: void = undefined;
15 >
16 > /**
17 > * Red: integer in [0-255]
18 > */
19 > readonly r: number;
20 >
21 > /**
22 > * Green: integer in [0-255]
23 > */
24 > readonly g: number;
25 >
26 > /**
27 > * Blue: integer in [0-255]
28 > */
29 > readonly b: number;
30 >
31 > /**
32 > * Alpha: float in [0-1]
33 > */
34 > readonly a: number;
35 >
36 > constructor(r: number, g: number, b: number, a: number = 1) {
37 > this.r = Math.min(255, Math.max(0, r)) | 0;
38 > this.g = Math.min(255, Math.max(0, g)) | 0;
39 > this.b = Math.min(255, Math.max(0, b)) | 0;
40 > this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
41 > }
42 >
43 > static equals(a: RGBA, b: RGBA): boolean {
44 return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a;
45 }
46 > } color.ts
47 >
48 > export class HSLA {
49 >
50 > _hslaBrand: void = undefined;
51 >
52 > /**
53 > * Hue: integer in [0, 360]
54 > */
55 > readonly h: number;
56 >
57 > /**
58 > * Saturation: float in [0, 1]
59 > */
60 > readonly s: number;
61 >
62 > /**
63 > * Luminosity: float in [0, 1]
64 > */
65 > readonly l: number;
66 >
67 > /**
68 > * Alpha: float in [0, 1]
69 > */
70 > readonly a: number;
71 >
72 > constructor(h: number, s: number, l: number, a: number) {
73 this.h = Math.max(Math.min(360, h), 0) | 0;
74 this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
76 this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
77 }
78 > color.ts
79 > static equals(a: HSLA, b: HSLA): boolean {
80 return a.h === b.h && a.s === b.s && a.l === b.l && a.a === b.a;
81 }
82 > color.ts
83 > /**
84 > * Converts an RGB color value to HSL. Conversion formula
85 > * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
86 > * Assumes r, g, and b are contained in the set [0, 255] and
87 > * returns h in the set [0, 360], s, and l in the set [0, 1].
88 > */
89 > static fromRGBA(rgba: RGBA): HSLA {
90 const r = rgba.r / 255;
91 const g = rgba.g / 255;
114 return new HSLA(h, s, l, a);
115 }
116 > color.ts
117 > private static _hue2rgb(p: number, q: number, t: number): number {
118 if (t < 0) {
119 t += 1;
133 return p;
134 }
135 > color.ts
136 > /**
137 > * Converts an HSL color value to RGB. Conversion formula
138 > * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
139 > * Assumes h in the set [0, 360] s, and l are contained in the set [0, 1] and
140 > * returns r, g, and b in the set [0, 255].
141 > */
142 > static toRGBA(hsla: HSLA): RGBA {
143 const h = hsla.h / 360;
144 const { s, l, a } = hsla;
157 return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
158 }
159 > } color.ts
160 >
161 > export class HSVA {
162 >
163 > _hsvaBrand: void = undefined;
164 >
165 > /**
166 > * Hue: integer in [0, 360]
167 > */
168 > readonly h: number;
169 >
170 > /**
171 > * Saturation: float in [0, 1]
172 > */
173 > readonly s: number;
174 >
175 > /**
176 > * Value: float in [0, 1]
177 > */
178 > readonly v: number;
179 >
180 > /**
181 > * Alpha: float in [0, 1]
182 > */
183 > readonly a: number;
184 >
185 > constructor(h: number, s: number, v: number, a: number) {
186 this.h = Math.max(Math.min(360, h), 0) | 0;
187 this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
189 this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
190 }
191 > color.ts
192 > static equals(a: HSVA, b: HSVA): boolean {
193 return a.h === b.h && a.s === b.s && a.v === b.v && a.a === b.a;
194 }
195 > color.ts
196 > // from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm
197 > static fromRGBA(rgba: RGBA): HSVA {
198 const r = rgba.r / 255;
199 const g = rgba.g / 255;
217 return new HSVA(Math.round(m * 60), s, cmax, rgba.a);
218 }
219 > color.ts
220 > // from http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
221 > static toRGBA(hsva: HSVA): RGBA {
222 const { h, s, v, a } = hsva;
223 const c = v * s;
252 return new RGBA(r, g, b, a);
253 }
254 > } color.ts
255 >
256 > export class Color {
257 >
258 > static fromHex(hex: string): Color {
259 return Color.Format.CSS.parseHex(hex) || Color.red;
260 }
261 > color.ts
262 > static equals(a: Color | null, b: Color | null): boolean {
263 if (!a && !b) {
264 return true;
269 return a.equals(b);
270 }
271 > color.ts
272 > readonly rgba: RGBA;
273 > private _hsla?: HSLA;
274 > get hsla(): HSLA {
275 if (this._hsla) {
276 return this._hsla;
279 }
280 }
281 > color.ts
282 > private _hsva?: HSVA;
283 > get hsva(): HSVA {
284 if (this._hsva) {
285 return this._hsva;
287 return HSVA.fromRGBA(this.rgba);
288 }
289 > color.ts
290 > constructor(arg: RGBA | HSLA | HSVA) {
291 > if (!arg) {
292 throw new Error('Color needs a value');
293 > } else if (arg instanceof RGBA) { color.ts
294 > this.rgba = arg;
295 > } else if (arg instanceof HSLA) {
296 this._hsla = arg;
297 this.rgba = HSLA.toRGBA(arg);
302 throw new Error('Invalid color ctor argument');
303 }
304 > } color.ts
305 >
306 > equals(other: Color | null): boolean {
307 return !!other && RGBA.equals(this.rgba, other.rgba) && HSLA.equals(this.hsla, other.hsla) && HSVA.equals(this.hsva, other.hsva);
308 }
309 > color.ts
310 > /**
311 > * http://www.w3.org/TR/WCAG20/#relativeluminancedef
312 > * Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
313 > */
314 > getRelativeLuminance(): number {
315 const R = Color._relativeLuminanceForComponent(this.rgba.r);
316 const G = Color._relativeLuminanceForComponent(this.rgba.g);
320 return roundFloat(luminance, 4);
321 }
322 > color.ts
323 > /**
324 > * Reduces the "foreground" color on this "background" color unti it is
325 > * below the relative luminace ratio.
326 > * @returns the new foreground color
327 > * @see https://github.com/xtermjs/xterm.js/blob/44f9fa39ae03e2ca6d28354d88a399608686770e/src/common/Color.ts#L315
328 > */
329 > reduceRelativeLuminace(foreground: Color, ratio: number): Color {
330 // This is a naive but fast approach to reducing luminance as converting to
331 // HSL and back is expensive
343 return new Color(new RGBA(fgR, fgG, fgB));
344 }
345 > color.ts
346 > /**
347 > * Increases the "foreground" color on this "background" color unti it is
348 > * below the relative luminace ratio.
349 > * @returns the new foreground color
350 > * @see https://github.com/xtermjs/xterm.js/blob/44f9fa39ae03e2ca6d28354d88a399608686770e/src/common/Color.ts#L335
351 > */
352 > increaseRelativeLuminace(foreground: Color, ratio: number): Color {
353 // This is a naive but fast approach to reducing luminance as converting to
354 // HSL and back is expensive
364 return new Color(new RGBA(fgR, fgG, fgB));
365 }
366 > color.ts
367 > private static _relativeLuminanceForComponent(color: number): number {
368 const c = color / 255;
369 return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4);
370 }
371 > color.ts
372 > /**
373 > * http://www.w3.org/TR/WCAG20/#contrast-ratiodef
374 > * Returns the contrast ration number in the set [1, 21].
375 > */
376 > getContrastRatio(another: Color): number {
377 const lum1 = this.getRelativeLuminance();
378 const lum2 = another.getRelativeLuminance();
379 return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05);
380 }
381 > color.ts
382 > /**
383 > * http://24ways.org/2010/calculating-color-contrast
384 > * Return 'true' if darker color otherwise 'false'
385 > */
386 > isDarker(): boolean {
387 const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
388 return yiq < 128;
389 }
390 > color.ts
391 > /**
392 > * http://24ways.org/2010/calculating-color-contrast
393 > * Return 'true' if lighter color otherwise 'false'
394 > */
395 > isLighter(): boolean {
396 const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
397 return yiq >= 128;
398 }
399 > color.ts
400 > isLighterThan(another: Color): boolean {
401 const lum1 = this.getRelativeLuminance();
402 const lum2 = another.getRelativeLuminance();
403 return lum1 > lum2;
404 }
405 > color.ts
406 > isDarkerThan(another: Color): boolean {
407 const lum1 = this.getRelativeLuminance();
408 const lum2 = another.getRelativeLuminance();
409 return lum1 < lum2;
410 }
411 > color.ts
412 > /**
413 > * Based on xterm.js: https://github.com/xtermjs/xterm.js/blob/44f9fa39ae03e2ca6d28354d88a399608686770e/src/common/Color.ts#L288
414 > *
415 > * Given a foreground color and a background color, either increase or reduce the luminance of the
416 > * foreground color until the specified contrast ratio is met. If pure white or black is hit
417 > * without the contrast ratio being met, go the other direction using the background color as the
418 > * foreground color and take either the first or second result depending on which has the higher
419 > * contrast ratio.
420 > *
421 > * @param foreground The foreground color.
422 > * @param ratio The contrast ratio to achieve.
423 > * @returns The adjusted foreground color.
424 > */
425 > ensureConstrast(foreground: Color, ratio: number): Color {
426 const bgL = this.getRelativeLuminance();
427 const fgL = foreground.getRelativeLuminance();
450 return foreground;
451 }
452 > color.ts
453 > lighten(factor: number): Color {
454 return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l + this.hsla.l * factor, this.hsla.a));
455 }
456 > color.ts
457 > darken(factor: number): Color {
458 return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l - this.hsla.l * factor, this.hsla.a));
459 }
460 > color.ts
461 > transparent(factor: number): Color {
462 const { r, g, b, a } = this.rgba;
463 return new Color(new RGBA(r, g, b, a * factor));
464 }
465 > color.ts
466 > isTransparent(): boolean {
467 return this.rgba.a === 0;
468 }
469 > color.ts
470 > isOpaque(): boolean {
471 return this.rgba.a === 1;
472 }
473 > color.ts
474 > opposite(): Color {
475 return new Color(new RGBA(255 - this.rgba.r, 255 - this.rgba.g, 255 - this.rgba.b, this.rgba.a));
476 }
477 > color.ts
478 > blend(c: Color): Color {
479 const rgba = c.rgba;
480
494 return new Color(new RGBA(r, g, b, a));
495 }
496 > color.ts
497 > /**
498 > * Mixes the current color with the provided color based on the given factor.
499 > * @param color The color to mix with
500 > * @param factor The factor of mixing (0 means this color, 1 means the input color, 0.5 means equal mix)
501 > * @returns A new color representing the mix
502 > */
503 > mix(color: Color, factor: number = 0.5): Color {
504 const normalize = Math.min(Math.max(factor, 0), 1);
505 const thisRGBA = this.rgba;
513 return new Color(new RGBA(r, g, b, a));
514 }
515 > color.ts
516 > makeOpaque(opaqueBackground: Color): Color {
517 if (this.isOpaque() || opaqueBackground.rgba.a !== 1) {
518 // only allow to blend onto a non-opaque color onto a opaque color
530 ));
531 }
532 > color.ts
533 > flatten(...backgrounds: Color[]): Color {
534 const background = backgrounds.reduceRight((accumulator, color) => {
535 return Color._flatten(color, accumulator);
537 return Color._flatten(this, background);
538 }
539 > color.ts
540 > private static _flatten(foreground: Color, background: Color) {
541 const backgroundAlpha = 1 - foreground.rgba.a;
542 return new Color(new RGBA(
546 ));
547 }
548 > color.ts
549 > private _toString?: string;
550 > toString(): string {
551 if (!this._toString) {
552 this._toString = Color.Format.CSS.format(this);
554 return this._toString;
555 }
556 > color.ts
557 > private _toNumber32Bit?: number;
558 > toNumber32Bit(): number {
559 if (!this._toNumber32Bit) {
560 this._toNumber32Bit = (
567 return this._toNumber32Bit;
568 }
569 > color.ts
570 > static getLighterColor(of: Color, relative: Color, factor?: number): Color {
571 if (of.isLighterThan(relative)) {
572 return of;
578 return of.lighten(factor);
579 }
580 > color.ts
581 > static getDarkerColor(of: Color, relative: Color, factor?: number): Color {
582 if (of.isDarkerThan(relative)) {
583 return of;
589 return of.darken(factor);
590 }
591 > color.ts
592 > static readonly white = new Color(new RGBA(255, 255, 255, 1));
593 > static readonly black = new Color(new RGBA(0, 0, 0, 1));
594 > static readonly red = new Color(new RGBA(255, 0, 0, 1));
595 > static readonly blue = new Color(new RGBA(0, 0, 255, 1));
596 > static readonly green = new Color(new RGBA(0, 255, 0, 1));
597 > static readonly cyan = new Color(new RGBA(0, 255, 255, 1));
598 > static readonly lightgrey = new Color(new RGBA(211, 211, 211, 1));
599 > static readonly transparent = new Color(new RGBA(0, 0, 0, 0));
600 > }
601 >
602 > export namespace Color {
603 > export namespace Format {
604 > export namespace CSS {
605 >
606 > export function formatRGB(color: Color): string {
607 if (color.rgba.a === 1) {
608 return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`;
611 return Color.Format.CSS.formatRGBA(color);
612 }
613 > color.ts
614 > export function formatRGBA(color: Color): string {
615 return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`;
616 }
617 > color.ts
618 > export function formatHSL(color: Color): string {
619 if (color.hsla.a === 1) {
620 return `hsl(${color.hsla.h}, ${Math.round(color.hsla.s * 100)}%, ${Math.round(color.hsla.l * 100)}%)`;
623 return Color.Format.CSS.formatHSLA(color);
624 }
625 > color.ts
626 > export function formatHSLA(color: Color): string {
627 return `hsla(${color.hsla.h}, ${Math.round(color.hsla.s * 100)}%, ${Math.round(color.hsla.l * 100)}%, ${color.hsla.a.toFixed(2)})`;
628 }
629 > color.ts
630 > function _toTwoDigitHex(n: number): string {
631 const r = n.toString(16);
632 return r.length !== 2 ? '0' + r : r;
633 }
634 > color.ts
635 > /**
636 > * Formats the color as #RRGGBB
637 > */
638 > export function formatHex(color: Color): string {
639 return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}`;
640 }
641 > color.ts
642 > /**
643 > * Formats the color as #RRGGBBAA
644 > * If 'compact' is set, colors without transparancy will be printed as #RRGGBB
645 > */
646 > export function formatHexA(color: Color, compact = false): string {
647 if (compact && color.rgba.a === 1) {
648 return Color.Format.CSS.formatHex(color);
651 return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(Math.round(color.rgba.a * 255))}`;
652 }
653 > color.ts
654 > /**
655 > * The default format will use HEX if opaque and RGBA otherwise.
656 > */
657 > export function format(color: Color): string {
658 if (color.isOpaque()) {
659 return Color.Format.CSS.formatHex(color);
662 return Color.Format.CSS.formatRGBA(color);
663 }
664 > color.ts
665 > /**
666 > * Parse a CSS color and return a {@link Color}.
667 > * @param css The CSS color to parse.
668 > * @see https://drafts.csswg.org/css-color/#typedef-color
669 > */
670 > export function parse(css: string): Color | null {
671 if (css === 'transparent') {
672 return Color.transparent;
699 return parseNamedKeyword(css);
700 }
701 > color.ts
702 > function parseNamedKeyword(css: string): Color | null {
703 // https://drafts.csswg.org/css-color/#named-colors
704 switch (css) {
854 }
855 }
856 > color.ts
857 > /**
858 > * Converts an Hex color value to a Color.
859 > * returns r, g, and b are contained in the set [0, 255]
860 > * @param hex string (#RGB, #RGBA, #RRGGBB or #RRGGBBAA).
861 > */
862 > export function parseHex(hex: string): Color | null {
863 const length = hex.length;
864
910 return null;
911 }
912 > color.ts
913 > function _parseHexDigit(charCode: CharCode): number {
914 switch (charCode) {
915 case CharCode.Digit0: return 0;
938 return 0;
939 }
940 > } color.ts
941 > }
942 > }
src/vs/base/common/types.ts 299 covered LOC · 31 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- types.ts
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.js';
7 >
8 > /**
9 > * @returns whether the provided parameter is a JavaScript String or not.
10 > */
11 > export function isString(str: unknown): str is string {
12 > return (typeof str === 'string'); types.ts
13 > }
14 > types.ts
15 > /**
16 > * @returns whether the provided parameter is a JavaScript Array and each element in the array is a string.
17 > */
18 > export function isStringArray(value: unknown): value is string[] {
19 return isArrayOf(value, isString);
20 }
21 > types.ts
22 > /**
23 > * @returns whether the provided parameter is a JavaScript Array and each element in the array satisfies the provided type guard.
24 > */
25 > export function isArrayOf<T>(value: unknown, check: (item: unknown) => item is T): value is T[] {
26 return Array.isArray(value) && value.every(check);
27 }
28 > types.ts
29 > /**
30 > * @returns whether the provided parameter is of type `object` but **not**
31 > * `null`, an `array`, a `regexp`, nor a `date`.
32 > */
33 > export function isObject(obj: unknown): obj is Object {
34 > // The method can't do a type cast since there are type (like strings) which types.ts
35 > // are subclasses of any put not positvely matched by the function. Hence type
36 > // narrowing results in wrong results.
37 > return typeof obj === 'object'
38 > && obj !== null types.ts
39 > && !Array.isArray(obj)
40 > && !(obj instanceof RegExp) types.ts
41 > && !(obj instanceof Date);
42 > } types.ts
43 > types.ts
44 > /**
45 > * @returns whether the provided parameter is of type `Buffer` or Uint8Array dervived type
46 > */
47 > export function isTypedArray(obj: unknown): obj is Object {
48 const TypedArray = Object.getPrototypeOf(Uint8Array);
49 return typeof obj === 'object'
50 && obj instanceof TypedArray;
51 }
52 > types.ts
53 > /**
54 > * In **contrast** to just checking `typeof` this will return `false` for `NaN`.
55 > * @returns whether the provided parameter is a JavaScript Number or not.
56 > */
57 > export function isNumber(obj: unknown): obj is number {
58 return (typeof obj === 'number' && !isNaN(obj));
59 }
60 > types.ts
61 > /**
62 > * @returns whether the provided parameter is an Iterable, casting to the given generic
63 > */
64 > export function isIterable<T>(obj: unknown): obj is Iterable<T> {
65 // eslint-disable-next-line local/code-no-any-casts
66 return !!obj && typeof (obj as any)[Symbol.iterator] === 'function';
67 }
68 > types.ts
69 > /**
70 > * @returns whether the provided parameter is an Iterable, casting to the given generic
71 > */
72 > export function isAsyncIterable<T>(obj: unknown): obj is AsyncIterable<T> {
73 // eslint-disable-next-line local/code-no-any-casts
74 return !!obj && typeof (obj as any)[Symbol.asyncIterator] === 'function';
75 }
76 > types.ts
77 > /**
78 > * @returns whether the provided parameter is a JavaScript Boolean or not.
79 > */
80 > export function isBoolean(obj: unknown): obj is boolean {
81 return (obj === true || obj === false);
82 }
83 > types.ts
84 > /**
85 > * @returns whether the provided parameter is undefined.
86 > */
87 > export function isUndefined(obj: unknown): obj is undefined {
88 > return (typeof obj === 'undefined'); types.ts
89 > }
90 > types.ts
91 > /**
92 > * @returns whether the provided parameter is defined.
93 > */
94 > export function isDefined<T>(arg: T | null | undefined): arg is T {
95 return !isUndefinedOrNull(arg);
96 }
97 > types.ts
98 > /**
99 > * @returns whether the provided parameter is undefined or null.
100 > */
101 > export function isUndefinedOrNull(obj: unknown): obj is undefined | null {
102 > return (isUndefined(obj) || obj === null); types.ts
103 > }
104 > types.ts
105 >
106 > export function assertType(condition: unknown, type?: string): asserts condition {
107 if (!condition) {
108 throw new Error(type ? `Unexpected type, expected '${type}'` : 'Unexpected type');
109 }
110 }
111 > types.ts
112 > /**
113 > * Asserts that the argument passed in is neither undefined nor null.
114 > *
115 > * @see {@link assertDefined} for a similar utility that leverages TS assertion functions to narrow down the type of `arg` to be non-nullable.
116 > */
117 > export function assertReturnsDefined<T>(arg: T | null | undefined): NonNullable<T> {
118 assert(
119 arg !== null && arg !== undefined,
123 return arg;
124 }
125 > types.ts
126 > /**
127 > * Asserts that a provided `value` is `defined` - not `null` or `undefined`,
128 > * throwing an error with the provided error or error message, while also
129 > * narrowing down the type of the `value` to be `NonNullable` using TS
130 > * assertion functions.
131 > *
132 > * @throws if the provided `value` is `null` or `undefined`.
133 > *
134 > * ## Examples
135 > *
136 > * ```typescript
137 > * // an assert with an error message
138 > * assertDefined('some value', 'String constant is not defined o_O.');
139 > *
140 > * // `throws!` the provided error
141 > * assertDefined(null, new Error('Should throw this error.'));
142 > *
143 > * // narrows down the type of `someValue` to be non-nullable
144 > * const someValue: string | undefined | null = blackbox();
145 > * assertDefined(someValue, 'Some value must be defined.');
146 > * console.log(someValue.length); // now type of `someValue` is `string`
147 > * ```
148 > *
149 > * @see {@link assertReturnsDefined} for a similar utility but without assertion.
150 > * @see {@link https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions typescript-3-7.html#assertion-functions}
151 > */
152 > export function assertDefined<T>(value: T, error: string | NonNullable<Error>): asserts value is NonNullable<T> {
153 if (value === null || value === undefined) {
154 const errorToThrow = typeof error === 'string' ? new Error(error) : error;
157 }
158 }
159 > types.ts
160 > /**
161 > * Asserts that each argument passed in is neither undefined nor null.
162 > */
163 > export function assertReturnsAllDefined<T1, T2>(t1: T1 | null | undefined, t2: T2 | null | undefined): [T1, T2];
164 > export function assertReturnsAllDefined<T1, T2, T3>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined): [T1, T2, T3];
165 > export function assertReturnsAllDefined<T1, T2, T3, T4>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined, t4: T4 | null | undefined): [T1, T2, T3, T4];
166 > export function assertReturnsAllDefined(...args: (unknown | null | undefined)[]): unknown[] {
167 const result = [];
168
179 return result;
180 }
181 > types.ts
182 > /**
183 > * Checks if the provided value is one of the vales in the provided list.
184 > *
185 > * ## Examples
186 > *
187 > * ```typescript
188 > * // note! item type is a `subset of string`
189 > * type TItem = ':' | '.' | '/';
190 > *
191 > * // note! item is type of `string` here
192 > * const item: string = ':';
193 > * // list of the items to check against
194 > * const list: TItem[] = [':', '.'];
195 > *
196 > * // ok
197 > * assert(
198 > * isOneOf(item, list),
199 > * 'Must succeed.',
200 > * );
201 > *
202 > * // `item` is of `TItem` type now
203 > * ```
204 > */
205 > export const isOneOf = <TType, TSubtype extends TType>(
206 value: TType,
207 validValues: readonly TSubtype[],
211 return validValues.includes(<TSubtype>value);
212 };
213 > types.ts
214 > /**
215 > * Compile-time type check of a variable.
216 > */
217 > export function typeCheck<T = never>(_thing: NoInfer<T>): void { }
218 >
219 > const hasOwnProperty = Object.prototype.hasOwnProperty;
220 >
221 > /**
222 > * @returns whether the provided parameter is an empty JavaScript Object or not.
223 > */
224 > export function isEmptyObject(obj: unknown): obj is object {
225 if (!isObject(obj)) {
226 return false;
235 return true;
236 }
237 > types.ts
238 > /**
239 > * @returns whether the provided parameter is a JavaScript Function or not.
240 > */
241 > export function isFunction(obj: unknown): obj is Function {
242 return (typeof obj === 'function');
243 }
244 > types.ts
245 > /**
246 > * @returns whether the provided parameters is are JavaScript Function or not.
247 > */
248 > export function areFunctions(...objects: unknown[]): boolean {
249 return objects.length > 0 && objects.every(isFunction);
250 }
251 > types.ts
252 > export type TypeConstraint = string | Function;
253 >
254 > export function validateConstraints(args: unknown[], constraints: Array<TypeConstraint | undefined>): void {
255 const len = Math.min(args.length, constraints.length);
256 for (let i = 0; i < len; i++) {
258 }
259 }
260 > types.ts
261 > export function validateConstraint(arg: unknown, constraint: TypeConstraint | undefined): void {
262
263 if (isString(constraint)) {
283 }
284 }
285 > types.ts
286 > /**
287 > * Helper type assertion that safely upcasts a type to a supertype.
288 > *
289 > * This can be used to make sure the argument correctly conforms to the subtype while still being able to pass it
290 > * to contexts that expects the supertype.
291 > */
292 > export function upcast<Base, Sub extends Base = Base>(x: Sub): Base {
293 return x;
294 }
295 > types.ts
296 > type AddFirstParameterToFunction<T, TargetFunctionsReturnType, FirstParameter> = T extends (...args: any[]) => TargetFunctionsReturnType ?
297 > // Function: add param to function
298 > (firstArg: FirstParameter, ...args: Parameters<T>) => ReturnType<T> :
299 >
300 > // Else: just leave as is
301 > T;
302 >
303 > /**
304 > * Allows to add a first parameter to functions of a type.
305 > */
306 > export type AddFirstParameterToFunctions<Target, TargetFunctionsReturnType, FirstParameter> = {
307 > // For every property
308 > [K in keyof Target]: AddFirstParameterToFunction<Target[K], TargetFunctionsReturnType, FirstParameter>;
309 > };
310 >
311 > /**
312 > * Given an object with all optional properties, requires at least one to be defined.
313 > * i.e. AtLeastOne<MyObject>;
314 > */
315 > export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
316 >
317 > /**
318 > * Only picks the non-optional properties of a type.
319 > */
320 > export type OmitOptional<T> = { [K in keyof T as T[K] extends Required<T>[K] ? K : never]: T[K] };
321 >
322 > /**
323 > * A type that removed readonly-less from all properties of `T`
324 > */
325 > export type Mutable<T> = {
326 > -readonly [P in keyof T]: T[P]
327 > };
328 >
329 > /**
330 > * A type that adds readonly to all properties of T, recursively.
331 > */
332 > export type DeepImmutable<T> = T extends (infer U)[]
333 > ? ReadonlyArray<DeepImmutable<U>>
334 > : T extends ReadonlyArray<infer U>
335 > ? ReadonlyArray<DeepImmutable<U>>
336 > : T extends Map<infer K, infer V>
337 > ? ReadonlyMap<K, DeepImmutable<V>>
338 > : T extends Set<infer U>
339 > ? ReadonlySet<DeepImmutable<U>>
340 > : T extends object
341 > ? {
342 > readonly [K in keyof T]: DeepImmutable<T[K]>;
343 > }
344 > : T;
345 >
346 > /**
347 > * A single object or an array of the objects.
348 > */
349 > export type SingleOrMany<T> = T | T[];
350 >
351 > /**
352 > * Given a `type X = { foo?: string }` checking that an object `satisfies X`
353 > * will ensure each property was explicitly defined, ensuring no properties
354 > * are omitted or forgotten.
355 > */
356 > export type WithDefinedProps<T> = { [K in keyof Required<T>]: T[K] };
357 >
358 >
359 > /**
360 > * A type that recursively makes all properties of `T` required
361 > */
362 > export type DeepRequiredNonNullable<T> = {
363 > [P in keyof T]-?: T[P] extends object ? DeepRequiredNonNullable<T[P]> : Required<NonNullable<T[P]>>;
364 > };
365 >
366 >
367 > /**
368 > * Represents a type that is a partial version of a given type `T`, where all properties are optional and can be deeply nested.
369 > */
370 > export type DeepPartial<T> = {
371 > [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : Partial<T[P]>;
372 > };
373 >
374 > /**
375 > * Represents a type that is a partial version of a given type `T`, except a subset.
376 > */
377 > export type PartialExcept<T, K extends keyof T> = Partial<Omit<T, K>> & Pick<T, K>;
378 >
379 >
380 > type KeysOfUnionType<T> = T extends T ? keyof T : never;
381 > type FilterType<T, TTest> = T extends TTest ? T : never;
382 > type MakeOptionalAndTrue<T extends object> = { [K in keyof T]?: true };
383 >
384 > /**
385 > * Type guard that checks if an object has specific keys and narrows the type accordingly.
386 > *
387 > * @param x - The object to check
388 > * @param key - An object with boolean values indicating which keys to check for
389 > * @returns true if all specified keys exist in the object, false otherwise
390 > *
391 > * @example
392 > * ```typescript
393 > * type A = { a: string };
394 > * type B = { b: number };
395 > * const obj: A | B = getObject();
396 > *
397 > * if (hasKey(obj, { a: true })) {
398 > * // obj is now narrowed to type A
399 > * console.log(obj.a);
400 > * }
401 > * ```
402 > */
403 > export function hasKey<T extends object, TKeys extends MakeOptionalAndTrue<T>>(x: T, key: TKeys): x is FilterType<T, { [K in KeysOfUnionType<T> & keyof TKeys]: unknown }> {
404 for (const k in key) {
405 if (!(k in x)) {
src/vs/base/common/network.ts 292 covered LOC · 20 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- network.ts
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 * as errors from './errors.js';
7 > import * as platform from './platform.js';
8 > import { equalsIgnoreCase, startsWithIgnoreCase } from './strings.js';
9 > import { URI } from './uri.js';
10 > import * as paths from './path.js';
11 >
12 > export namespace Schemas {
13 >
14 > /**
15 > * A schema that is used for models that exist in memory
16 > * only and that have no correspondence on a server or such.
17 > */
18 > export const inMemory = 'inmemory';
19 >
20 > /**
21 > * A schema that is used for setting files
22 > */
23 > export const vscode = 'vscode';
24 >
25 > /**
26 > * A schema that is used for internal private files
27 > */
28 > export const internal = 'private';
29 >
30 > /**
31 > * A walk-through document.
32 > */
33 > export const walkThrough = 'walkThrough';
34 >
35 > /**
36 > * An embedded code snippet.
37 > */
38 > export const walkThroughSnippet = 'walkThroughSnippet';
39 >
40 > export const http = 'http';
41 >
42 > export const https = 'https';
43 >
44 > export const file = 'file';
45 >
46 > export const mailto = 'mailto';
47 >
48 > export const untitled = 'untitled';
49 >
50 > export const data = 'data';
51 >
52 > export const command = 'command';
53 >
54 > export const vscodeRemote = 'vscode-remote';
55 >
56 > export const vscodeRemoteResource = 'vscode-remote-resource';
57 >
58 > export const vscodeManagedRemoteResource = 'vscode-managed-remote-resource';
59 >
60 > export const vscodeUserData = 'vscode-userdata';
61 >
62 > export const vscodeCustomEditor = 'vscode-custom-editor';
63 >
64 > export const vscodeNotebookCell = 'vscode-notebook-cell';
65 > export const vscodeNotebookCellMetadata = 'vscode-notebook-cell-metadata';
66 > export const vscodeNotebookCellMetadataDiff = 'vscode-notebook-cell-metadata-diff';
67 > export const vscodeNotebookCellOutput = 'vscode-notebook-cell-output';
68 > export const vscodeNotebookCellOutputDiff = 'vscode-notebook-cell-output-diff';
69 > export const vscodeNotebookMetadata = 'vscode-notebook-metadata';
70 > export const vscodeInteractiveInput = 'vscode-interactive-input';
71 >
72 > export const vscodeSettings = 'vscode-settings';
73 >
74 > export const vscodeWorkspaceTrust = 'vscode-workspace-trust';
75 >
76 > export const vscodeTerminal = 'vscode-terminal';
77 >
78 > /** Scheme used for the image carousel editor. */
79 > export const vscodeImageCarousel = 'vscode-image-carousel';
80 >
81 > /** Scheme used for code blocks in chat. */
82 > export const vscodeChatCodeBlock = 'vscode-chat-code-block';
83 >
84 > /** Scheme used for LHS of code compare (aka diff) blocks in chat. */
85 > export const vscodeChatCodeCompareBlock = 'vscode-chat-code-compare-block';
86 >
87 > /** Scheme used for the chat input editor. */
88 > export const vscodeChatEditor = 'vscode-chat-editor';
89 >
90 > /** Scheme used for the chat input part */
91 > export const vscodeChatInput = 'chatSessionInput';
92 >
93 > /** Scheme used for local chat session content */
94 > export const vscodeLocalChatSession = 'vscode-chat-session';
95 >
96 > /**
97 > * Scheme used internally for webviews that aren't linked to a resource (i.e. not custom editors)
98 > */
99 > export const webviewPanel = 'webview-panel';
100 >
101 > /**
102 > * Scheme used for loading the wrapper html and script in webviews.
103 > */
104 > export const vscodeWebview = 'vscode-webview';
105 >
106 > /**
107 > * Scheme used for integrated browser tabs using WebContentsView.
108 > */
109 > export const vscodeBrowser = 'vscode-browser';
110 >
111 > /**
112 > * Scheme used for extension pages
113 > */
114 > export const extension = 'extension';
115 >
116 > /**
117 > * Scheme used as a replacement of `file` scheme to load
118 > * files with our custom protocol handler (desktop only).
119 > */
120 > export const vscodeFileResource = 'vscode-file';
121 >
122 > /**
123 > * Scheme used for temporary resources
124 > */
125 > export const tmp = 'tmp';
126 >
127 > /**
128 > * Scheme used vs live share
129 > */
130 > export const vsls = 'vsls';
131 >
132 > /**
133 > * Scheme used for the Source Control commit input's text document
134 > */
135 > export const vscodeSourceControl = 'vscode-scm';
136 >
137 > /**
138 > * Scheme used for input box for creating comments.
139 > */
140 > export const commentsInput = 'comment';
141 >
142 > /**
143 > * Scheme used for special rendering of settings in the release notes
144 > */
145 > export const codeSetting = 'code-setting';
146 >
147 > /**
148 > * Scheme used for output panel resources
149 > */
150 > export const outputChannel = 'output';
151 >
152 > /**
153 > * Scheme used for the accessible view
154 > */
155 > export const accessibleView = 'accessible-view';
156 >
157 > /**
158 > * Used for snapshots of chat edits
159 > */
160 > export const chatEditingSnapshotScheme = 'chat-editing-snapshot-text-model';
161 > export const chatEditingModel = 'chat-editing-text-model';
162 >
163 > /**
164 > * Used for rendering multidiffs in copilot agent sessions
165 > */
166 > export const copilotPr = 'copilot-pr';
167 > }
168 >
169 > export function matchesScheme(target: URI | string, scheme: string): boolean {
170 if (URI.isUri(target)) {
171 return equalsIgnoreCase(target.scheme, scheme);
174 }
175 }
176 > network.ts
177 > export function matchesSomeScheme(target: URI | string, ...schemes: string[]): boolean {
178 return schemes.some(scheme => matchesScheme(target, scheme));
179 }
180 > network.ts
181 > export const connectionTokenCookieName = 'vscode-tkn';
182 > export const connectionTokenQueryName = 'tkn';
183 >
184 > class RemoteAuthoritiesImpl {
185 > private readonly _hosts: { [authority: string]: string | undefined } = Object.create(null);
186 > private readonly _ports: { [authority: string]: number | undefined } = Object.create(null);
187 > private readonly _connectionTokens: { [authority: string]: string | undefined } = Object.create(null);
188 > private _preferredWebSchema: 'http' | 'https' = 'http';
189 > private _delegate: ((uri: URI) => URI) | null = null;
190 > private _serverRootPath: string = '/';
191 >
192 > setPreferredWebSchema(schema: 'http' | 'https') {
193 this._preferredWebSchema = schema;
194 }
195 > network.ts
196 > setDelegate(delegate: (uri: URI) => URI): void {
197 this._delegate = delegate;
198 }
199 > network.ts
200 > setServerRootPath(product: { quality?: string; commit?: string }, serverBasePath: string | undefined): void {
201 this._serverRootPath = paths.posix.join(serverBasePath ?? '/', getServerProductSegment(product));
202 }
203 > network.ts
204 > getServerRootPath(): string {
205 return this._serverRootPath;
206 }
207 > network.ts
208 > private get _remoteResourcesPath(): string {
209 return paths.posix.join(this._serverRootPath, Schemas.vscodeRemoteResource);
210 }
211 > network.ts
212 > set(authority: string, host: string, port: number): void {
213 this._hosts[authority] = host;
214 this._ports[authority] = port;
215 }
216 > network.ts
217 > setConnectionToken(authority: string, connectionToken: string): void {
218 this._connectionTokens[authority] = connectionToken;
219 }
220 > network.ts
221 > getPreferredWebSchema(): 'http' | 'https' {
222 return this._preferredWebSchema;
223 }
224 > network.ts
225 > rewrite(uri: URI): URI {
226 if (this._delegate) {
227 try {
250 });
251 }
252 > } network.ts
253 >
254 > export const RemoteAuthorities = new RemoteAuthoritiesImpl();
255 >
256 > export function getServerProductSegment(product: { quality?: string; commit?: string }) {
257 return `${product.quality ?? 'oss'}-${product.commit ?? 'dev'}`;
258 }
259 > network.ts
260 > /**
261 > * A string pointing to a path inside the app. It should not begin with ./ or ../
262 > */
263 > export type AppResourcePath = (
264 > `a${string}` | `b${string}` | `c${string}` | `d${string}` | `e${string}` | `f${string}`
265 > | `g${string}` | `h${string}` | `i${string}` | `j${string}` | `k${string}` | `l${string}`
266 > | `m${string}` | `n${string}` | `o${string}` | `p${string}` | `q${string}` | `r${string}`
267 > | `s${string}` | `t${string}` | `u${string}` | `v${string}` | `w${string}` | `x${string}`
268 > | `y${string}` | `z${string}`
269 > );
270 >
271 > export const builtinExtensionsPath: AppResourcePath = 'vs/../../extensions';
272 > export const nodeModulesPath: AppResourcePath = 'vs/../../node_modules';
273 > export const nodeModulesAsarPath: AppResourcePath = 'vs/../../node_modules.asar';
274 > export const nodeModulesAsarUnpackedPath: AppResourcePath = 'vs/../../node_modules.asar.unpacked';
275 >
276 > export const VSCODE_AUTHORITY = 'vscode-app';
277 >
278 > class FileAccessImpl {
279 >
280 > private static readonly FALLBACK_AUTHORITY = VSCODE_AUTHORITY;
281 >
282 > /**
283 > * Returns a URI to use in contexts where the browser is responsible
284 > * for loading (e.g. fetch()) or when used within the DOM.
285 > *
286 > * **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context.
287 > */
288 > asBrowserUri(resourcePath: AppResourcePath | ''): URI {
289 const uri = this.toUri(resourcePath);
290 return this.uriToBrowserUri(uri);
291 }
292 > network.ts
293 > /**
294 > * Returns a URI to use in contexts where the browser is responsible
295 > * for loading (e.g. fetch()) or when used within the DOM.
296 > *
297 > * **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context.
298 > */
299 > uriToBrowserUri(uri: URI): URI {
300 // Handle remote URIs via `RemoteAuthorities`
301 if (uri.scheme === Schemas.vscodeRemote) {
328 return uri;
329 }
330 > network.ts
331 > /**
332 > * Returns the `file` URI to use in contexts where node.js
333 > * is responsible for loading.
334 > */
335 > asFileUri(resourcePath: AppResourcePath | ''): URI {
336 const uri = this.toUri(resourcePath);
337 return this.uriToFileUri(uri);
338 }
339 > network.ts
340 > /**
341 > * Returns the `file` URI to use in contexts where node.js
342 > * is responsible for loading.
343 > */
344 > uriToFileUri(uri: URI): URI {
345 // Only convert the URI if it is `vscode-file:` scheme
346 if (uri.scheme === Schemas.vscodeFileResource) {
358 return uri;
359 }
360 > network.ts
361 > private toUri(uriOrModule: URI | string): URI {
362 if (URI.isUri(uriOrModule)) {
363 return uriOrModule;
379 throw new Error('Cannot determine URI for module id!');
380 }
381 > } network.ts
382 >
383 > export const FileAccess = new FileAccessImpl();
384 >
385 > export const CacheControlheaders: Record<string, string> = Object.freeze({
386 > 'Cache-Control': 'no-cache, no-store'
387 > });
388 >
389 > export const DocumentPolicyheaders: Record<string, string> = Object.freeze({
390 > 'Document-Policy': 'include-js-call-stacks-in-crash-reports'
391 > });
392 >
393 > export namespace COI {
394 >
395 > const coiHeaders = new Map<'3' | '2' | '1' | string, Record<string, string>>([
396 > ['1', { 'Cross-Origin-Opener-Policy': 'same-origin' }],
397 > ['2', { 'Cross-Origin-Embedder-Policy': 'require-corp' }],
398 > ['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' }],
399 > ]);
400 >
401 > export const CoopAndCoep = Object.freeze(coiHeaders.get('3'));
402 >
403 > const coiSearchParamName = 'vscode-coi';
404 >
405 > /**
406 > * Extract desired headers from `vscode-coi` invocation
407 > */
408 > export function getHeadersFromQuery(url: string | URI | URL): Record<string, string> | undefined {
409 let params: URLSearchParams | undefined;
410 if (typeof url === 'string') {
421 return coiHeaders.get(value);
422 }
423 > network.ts
424 > /**
425 > * Add the `vscode-coi` query attribute based on wanting `COOP` and `COEP`. Will be a noop when `crossOriginIsolated`
426 > * isn't enabled the current context
427 > */
428 > export function addSearchParam(urlOrSearch: URLSearchParams | Record<string, string>, coop: boolean, coep: boolean): void {
429 if (!(globalThis as typeof globalThis & { crossOriginIsolated?: boolean }).crossOriginIsolated) {
430 // depends on the current context being COI
src/vs/base/common/uri.ts 283 covered LOC · 27 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- uri.ts
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 { CharCode } from './charCode.js';
7 > import { MarshalledId } from './marshallingIds.js';
8 > import * as paths from './path.js';
9 > import { isWindows } from './platform.js';
10 >
11 > const _schemePattern = /^\w[\w\d+.-]*$/;
12 > const _singleSlashStart = /^\//;
13 > const _doubleSlashStart = /^\/\//;
14 >
15 function _validateUri(ret: URI, _strict?: boolean): void {
16
47 }
48 }
49 > uri.ts
50 > // for a while we allowed uris *without* schemes and this is the migration
51 > // for them, e.g. an uri without scheme and without strict-mode warns and falls
52 > // back to the file-scheme. that should cause the least carnage and still be a
53 > // clear warning
54 function _schemeFix(scheme: string, _strict: boolean): string {
55 if (!scheme && !_strict) {
58 return scheme;
59 }
60 > uri.ts
61 > // implements a bit of https://tools.ietf.org/html/rfc3986#section-5
62 function _referenceResolution(scheme: string, path: string): string {
63
79 return path;
80 }
81 > uri.ts
82 > const _empty = '';
83 > const _slash = '/';
84 > const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
85 >
86 > /**
87 > * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
88 > * This class is a simple parser which creates the basic component parts
89 > * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
90 > * and encoding.
91 > *
92 > * ```txt
93 > * foo://example.com:8042/over/there?name=ferret#nose
94 > * \_/ \______________/\_________/ \_________/ \__/
95 > * | | | | |
96 > * scheme authority path query fragment
97 > * | _____________________|__
98 > * / \ / \
99 > * urn:example:animal:ferret:nose
100 > * ```
101 > */
102 > export class URI implements UriComponents {
103 >
104 > static isUri(thing: unknown): thing is URI {
105 if (thing instanceof URI) {
106 return true;
118 && typeof (<URI>thing).toString === 'function';
119 }
120 > uri.ts
121 > /**
122 > * scheme is the 'http' part of 'http://www.example.com/some/path?query#fragment'.
123 > * The part before the first colon.
124 > */
125 > readonly scheme: string;
126 >
127 > /**
128 > * authority is the 'www.example.com' part of 'http://www.example.com/some/path?query#fragment'.
129 > * The part between the first double slashes and the next slash.
130 > */
131 > readonly authority: string;
132 >
133 > /**
134 > * path is the '/some/path' part of 'http://www.example.com/some/path?query#fragment'.
135 > */
136 > readonly path: string;
137 >
138 > /**
139 > * query is the 'query' part of 'http://www.example.com/some/path?query#fragment'.
140 > */
141 > readonly query: string;
142 >
143 > /**
144 > * fragment is the 'fragment' part of 'http://www.example.com/some/path?query#fragment'.
145 > */
146 > readonly fragment: string;
147 >
148 > /**
149 > * @internal
150 > */
151 > protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean);
152 >
153 > /**
154 > * @internal
155 > */
156 > protected constructor(components: UriComponents);
157 >
158 > /**
159 > * @internal
160 > */
161 > protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict: boolean = false) {
162
163 if (typeof schemeOrData === 'object') {
180 }
181 }
182 > uri.ts
183 > // ---- filesystem path -----------------------
184 >
185 > /**
186 > * Returns a string representing the corresponding file system path of this URI.
187 > * Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
188 > * platform specific path separator.
189 > *
190 > * * Will *not* validate the path for invalid characters and semantics.
191 > * * Will *not* look at the scheme of this URI.
192 > * * The result shall *not* be used for display purposes but for accessing a file on disk.
193 > *
194 > *
195 > * The *difference* to `URI#path` is the use of the platform specific separator and the handling
196 > * of UNC paths. See the below sample of a file-uri with an authority (UNC path).
197 > *
198 > * ```ts
199 > const u = URI.parse('file://server/c$/folder/file.txt')
200 > u.authority === 'server'
201 > u.path === '/shares/c$/file.txt'
202 > u.fsPath === '\\server\c$\folder\file.txt'
203 > ```
204 > *
205 > * Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
206 > * namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
207 > * with URIs that represent files on disk (`file` scheme).
208 > */
209 > get fsPath(): string {
210 // if (this.scheme !== 'file') {
211 // console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`);
213 return uriToFsPath(this, false);
214 }
215 > uri.ts
216 > // ---- modify to new -------------------------
217 >
218 > with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI {
219
220 if (!change) {
260 return new Uri(scheme, authority, path, query, fragment);
261 }
262 > uri.ts
263 > // ---- parse & validate ------------------------
264 >
265 > /**
266 > * Creates a new URI from a string, e.g. `http://www.example.com/some/path`,
267 > * `file:///usr/home`, or `scheme:with/path`.
268 > *
269 > * @param value A string which represents an URI (see `URI#toString`).
270 > */
271 > static parse(value: string, _strict: boolean = false): URI {
272 const match = _regexp.exec(value);
273 if (!match) {
283 );
284 }
285 > uri.ts
286 > /**
287 > * Creates a new URI from a file system path, e.g. `c:\my\files`,
288 > * `/usr/home`, or `\\server\share\some\path`.
289 > *
290 > * The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
291 > * as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
292 > * `URI.parse('file://' + path)` because the path might contain characters that are
293 > * interpreted (# and ?). See the following sample:
294 > * ```ts
295 > const good = URI.file('/coding/c#/project1');
296 > good.scheme === 'file';
297 > good.path === '/coding/c#/project1';
298 > good.fragment === '';
299 > const bad = URI.parse('file://' + '/coding/c#/project1');
300 > bad.scheme === 'file';
301 > bad.path === '/coding/c'; // path is now broken
302 > bad.fragment === '/project1';
303 > ```
304 > *
305 > * @param path A file system path (see `URI#fsPath`)
306 > */
307 > static file(path: string): URI {
308
309 let authority = _empty;
331 return new Uri('file', authority, path, _empty, _empty);
332 }
333 > uri.ts
334 > /**
335 > * Creates new URI from uri components.
336 > *
337 > * Unless `strict` is `true` the scheme is defaults to be `file`. This function performs
338 > * validation and should be used for untrusted uri components retrieved from storage,
339 > * user input, command arguments etc
340 > */
341 > static from(components: UriComponents, strict?: boolean): URI {
342 const result = new Uri(
343 components.scheme,
350 return result;
351 }
352 > uri.ts
353 > /**
354 > * Join a URI path with path fragments and normalizes the resulting path.
355 > *
356 > * @param uri The input URI.
357 > * @param pathFragment The path fragment to add to the URI path.
358 > * @returns The resulting URI.
359 > */
360 > static joinPath(uri: URI, ...pathFragment: string[]): URI {
361 if (!uri.path) {
362 throw new Error(`[UriError]: cannot call joinPath on URI without path: ${uri.toString()}`);
370 return uri.with({ path: newPath });
371 }
372 > uri.ts
373 > // ---- printing/externalize ---------------------------
374 >
375 > /**
376 > * Creates a string representation for this URI. It's guaranteed that calling
377 > * `URI.parse` with the result of this function creates an URI which is equal
378 > * to this URI.
379 > *
380 > * * The result shall *not* be used for display purposes but for externalization or transport.
381 > * * The result will be encoded using the percentage encoding and encoding happens mostly
382 > * ignore the scheme-specific encoding rules.
383 > *
384 > * @param skipEncoding Do not encode the result, default is `false`
385 > */
386 > toString(skipEncoding: boolean = false): string {
387 return _asFormatted(this, skipEncoding);
388 }
389 > uri.ts
390 > toJSON(): UriComponents {
391 return this;
392 }
393 > uri.ts
394 > /**
395 > * A helper function to revive URIs.
396 > *
397 > * **Note** that this function should only be used when receiving URI#toJSON generated data
398 > * and that it doesn't do any validation. Use {@link URI.from} when received "untrusted"
399 > * uri components such as command arguments or data from storage.
400 > *
401 > * @param data The URI components or URI to revive.
402 > * @returns The revived URI or undefined or null.
403 > */
404 > static revive(data: UriComponents | URI): URI;
405 > static revive(data: UriComponents | URI | undefined): URI | undefined;
406 > static revive(data: UriComponents | URI | null): URI | null;
407 > static revive(data: UriComponents | URI | undefined | null): URI | undefined | null;
408 > static revive(data: UriComponents | URI | undefined | null): URI | undefined | null {
409 if (!data) {
410 return data;
418 }
419 }
420 > uri.ts
421 > [Symbol.for('debug.description')]() {
422 return `URI(${this.toString()})`;
423 }
424 > } uri.ts
425 >
426 > export interface UriComponents {
427 > scheme: string;
428 > authority?: string;
429 > path?: string;
430 > query?: string;
431 > fragment?: string;
432 > }
433 >
434 > export function isUriComponents(thing: unknown): thing is UriComponents {
435 if (!thing || typeof thing !== 'object') {
436 return false;
442 && (typeof (<UriComponents>thing).fragment === 'string' || typeof (<UriComponents>thing).fragment === 'undefined');
443 }
444 > uri.ts
445 > interface UriState extends UriComponents {
446 > $mid: MarshalledId.Uri;
447 > external?: string;
448 > fsPath?: string;
449 > _sep?: 1;
450 > }
451 >
452 > const _pathSepMarker = isWindows ? 1 : undefined;
453 >
454 > // This class exists so that URI is compatible with vscode.Uri (API).
455 class Uri extends URI {
456
457 _formatted: string | null = null;
458 _fsPath: string | null = null;
459 > uri.ts
460 > override get fsPath(): string {
461 if (!this._fsPath) {
462 this._fsPath = uriToFsPath(this, false);
464 return this._fsPath;
465 }
466 > uri.ts
467 > override toString(skipEncoding: boolean = false): string {
468 if (!skipEncoding) {
469 if (!this._formatted) {
476 }
477 }
478 > uri.ts
479 > override toJSON(): UriComponents {
480 // eslint-disable-next-line local/code-no-dangerous-type-assertions
481 const res = <UriState>{
512 return res;
513 }
514 > } uri.ts
515 >
516 > // reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2
517 > const encodeTable: { [ch: number]: string } = {
518 > [CharCode.Colon]: '%3A', // gen-delims
519 > [CharCode.Slash]: '%2F',
520 > [CharCode.QuestionMark]: '%3F',
521 > [CharCode.Hash]: '%23',
522 > [CharCode.OpenSquareBracket]: '%5B',
523 > [CharCode.CloseSquareBracket]: '%5D',
524 > [CharCode.AtSign]: '%40',
525 >
526 > [CharCode.ExclamationMark]: '%21', // sub-delims
527 > [CharCode.DollarSign]: '%24',
528 > [CharCode.Ampersand]: '%26',
529 > [CharCode.SingleQuote]: '%27',
530 > [CharCode.OpenParen]: '%28',
531 > [CharCode.CloseParen]: '%29',
532 > [CharCode.Asterisk]: '%2A',
533 > [CharCode.Plus]: '%2B',
534 > [CharCode.Comma]: '%2C',
535 > [CharCode.Semicolon]: '%3B',
536 > [CharCode.Equals]: '%3D',
537 >
538 > [CharCode.Space]: '%20',
539 > };
540 >
541 function encodeURIComponentFast(uriComponent: string, isPath: boolean, isAuthority: boolean): string {
542 let res: string | undefined = undefined;
602 return res !== undefined ? res : uriComponent;
603 }
604 > uri.ts
605 function encodeURIComponentMinimal(path: string): string {
606 let res: string | undefined = undefined;
620 return res !== undefined ? res : path;
621 }
622 > uri.ts
623 > /**
624 > * Compute `fsPath` for the given uri
625 > */
626 > export function uriToFsPath(uri: URI, keepDriveLetterCasing: boolean): string {
627
628 let value: string;
650 return value;
651 }
652 > uri.ts
653 > /**
654 > * Create the external version of a uri
655 > */
656 function _asFormatted(uri: URI, skipEncoding: boolean): string {
657
723 return res;
724 }
725 > uri.ts
726 > // --- decode
727 >
728 function decodeURIComponentGraceful(str: string): string {
729 try {
737 }
738 }
739 > uri.ts
740 > const _rEncodedAsHex = /(%[0-9A-Za-z][0-9A-Za-z])+/g;
741 >
742 function percentDecode(str: string): string {
743 if (!str.match(_rEncodedAsHex)) {
746 return str.replace(_rEncodedAsHex, (match) => decodeURIComponentGraceful(match));
747 }
748 > uri.ts
749 > /**
750 > * Mapped-type that replaces all occurrences of URI with UriComponents
751 > */
752 > export type UriDto<T> = { [K in keyof T]: T[K] extends URI
753 > ? UriComponents
754 > : UriDto<T[K]> };
src/vs/editor/common/core/range.ts 247 covered LOC · 40 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- range.ts
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 { IPosition, Position } from './position.js';
7 >
8 > /**
9 > * A range in the editor. This interface is suitable for serialization.
10 > */
11 > export interface IRange {
12 > /**
13 > * Line number on which the range starts (starts at 1).
14 > */
15 > readonly startLineNumber: number;
16 > /**
17 > * Column on which the range starts in line `startLineNumber` (starts at 1).
18 > */
19 > readonly startColumn: number;
20 > /**
21 > * Line number on which the range ends.
22 > */
23 > readonly endLineNumber: number;
24 > /**
25 > * Column on which the range ends in line `endLineNumber`.
26 > */
27 > readonly endColumn: number;
28 > }
29 >
30 > /**
31 > * A range in the editor. (startLineNumber,startColumn) is <= (endLineNumber,endColumn)
32 > */
33 > export class Range {
34 >
35 > /**
36 > * Line number on which the range starts (starts at 1).
37 > */
38 > public readonly startLineNumber: number;
39 > /**
40 > * Column on which the range starts in line `startLineNumber` (starts at 1).
41 > */
42 > public readonly startColumn: number;
43 > /**
44 > * Line number on which the range ends.
45 > */
46 > public readonly endLineNumber: number;
47 > /**
48 > * Column on which the range ends in line `endLineNumber`.
49 > */
50 > public readonly endColumn: number;
51 >
52 > constructor(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) {
53 if ((startLineNumber > endLineNumber) || (startLineNumber === endLineNumber && startColumn > endColumn)) {
54 this.startLineNumber = endLineNumber;
63 }
64 }
65 > range.ts
66 > /**
67 > * Test if this range is empty.
68 > */
69 > public isEmpty(): boolean {
70 return Range.isEmpty(this);
71 }
72 > range.ts
73 > /**
74 > * Test if `range` is empty.
75 > */
76 > public static isEmpty(range: IRange): boolean {
77 return (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn);
78 }
79 > range.ts
80 > /**
81 > * Test if position is in this range. If the position is at the edges, will return true.
82 > */
83 > public containsPosition(position: IPosition): boolean {
84 return Range.containsPosition(this, position);
85 }
86 > range.ts
87 > /**
88 > * Test if `position` is in `range`. If the position is at the edges, will return true.
89 > */
90 > public static containsPosition(range: IRange, position: IPosition): boolean {
91 if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) {
92 return false;
100 return true;
101 }
102 > range.ts
103 > /**
104 > * Test if `position` is in `range`. If the position is at the edges, will return false.
105 > * @internal
106 > */
107 > public static strictContainsPosition(range: IRange, position: IPosition): boolean {
108 if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) {
109 return false;
117 return true;
118 }
119 > range.ts
120 > /**
121 > * Test if range is in this range. If the range is equal to this range, will return true.
122 > */
123 > public containsRange(range: IRange): boolean {
124 return Range.containsRange(this, range);
125 }
126 > range.ts
127 > /**
128 > * Test if `otherRange` is in `range`. If the ranges are equal, will return true.
129 > */
130 > public static containsRange(range: IRange, otherRange: IRange): boolean {
131 if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) {
132 return false;
143 return true;
144 }
145 > range.ts
146 > /**
147 > * Test if `range` is strictly in this range. `range` must start after and end before this range for the result to be true.
148 > */
149 > public strictContainsRange(range: IRange): boolean {
150 return Range.strictContainsRange(this, range);
151 }
152 > range.ts
153 > /**
154 > * Test if `otherRange` is strictly in `range` (must start after, and end before). If the ranges are equal, will return false.
155 > */
156 > public static strictContainsRange(range: IRange, otherRange: IRange): boolean {
157 if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) {
158 return false;
169 return true;
170 }
171 > range.ts
172 > /**
173 > * A reunion of the two ranges.
174 > * The smallest position will be used as the start point, and the largest one as the end point.
175 > */
176 > public plusRange(range: IRange): Range {
177 return Range.plusRange(this, range);
178 }
179 > range.ts
180 > /**
181 > * A reunion of the two ranges.
182 > * The smallest position will be used as the start point, and the largest one as the end point.
183 > */
184 > public static plusRange(a: IRange, b: IRange): Range {
185 let startLineNumber: number;
186 let startColumn: number;
212 return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
213 }
214 > range.ts
215 > /**
216 > * A intersection of the two ranges.
217 > */
218 > public intersectRanges(range: IRange): Range | null {
219 return Range.intersectRanges(this, range);
220 }
221 > range.ts
222 > /**
223 > * A intersection of the two ranges.
224 > */
225 > public static intersectRanges(a: IRange, b: IRange): Range | null {
226 let resultStartLineNumber = a.startLineNumber;
227 let resultStartColumn = a.startColumn;
256 return new Range(resultStartLineNumber, resultStartColumn, resultEndLineNumber, resultEndColumn);
257 }
258 > range.ts
259 > /**
260 > * Test if this range equals other.
261 > */
262 > public equalsRange(other: IRange | null | undefined): boolean {
263 return Range.equalsRange(this, other);
264 }
265 > range.ts
266 > /**
267 > * Test if range `a` equals `b`.
268 > */
269 > public static equalsRange(a: IRange | null | undefined, b: IRange | null | undefined): boolean {
270 if (!a && !b) {
271 return true;
280 );
281 }
282 > range.ts
283 > /**
284 > * Return the end position (which will be after or equal to the start position)
285 > */
286 > public getEndPosition(): Position {
287 return Range.getEndPosition(this);
288 }
289 > range.ts
290 > /**
291 > * Return the end position (which will be after or equal to the start position)
292 > */
293 > public static getEndPosition(range: IRange): Position {
294 return new Position(range.endLineNumber, range.endColumn);
295 }
296 > range.ts
297 > /**
298 > * Return the start position (which will be before or equal to the end position)
299 > */
300 > public getStartPosition(): Position {
301 return Range.getStartPosition(this);
302 }
303 > range.ts
304 > /**
305 > * Return the start position (which will be before or equal to the end position)
306 > */
307 > public static getStartPosition(range: IRange): Position {
308 return new Position(range.startLineNumber, range.startColumn);
309 }
310 > range.ts
311 > /**
312 > * Transform to a user presentable string representation.
313 > */
314 > public toString(): string {
315 return '[' + this.startLineNumber + ',' + this.startColumn + ' -> ' + this.endLineNumber + ',' + this.endColumn + ']';
316 }
317 > range.ts
318 > /**
319 > * Create a new range using this range's start position, and using endLineNumber and endColumn as the end position.
320 > */
321 > public setEndPosition(endLineNumber: number, endColumn: number): Range {
322 return new Range(this.startLineNumber, this.startColumn, endLineNumber, endColumn);
323 }
324 > range.ts
325 > /**
326 > * Create a new range using this range's end position, and using startLineNumber and startColumn as the start position.
327 > */
328 > public setStartPosition(startLineNumber: number, startColumn: number): Range {
329 return new Range(startLineNumber, startColumn, this.endLineNumber, this.endColumn);
330 }
331 > range.ts
332 > /**
333 > * Create a new empty range using this range's start position.
334 > */
335 > public collapseToStart(): Range {
336 return Range.collapseToStart(this);
337 }
338 > range.ts
339 > /**
340 > * Create a new empty range using this range's start position.
341 > */
342 > public static collapseToStart(range: IRange): Range {
343 return new Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn);
344 }
345 > range.ts
346 > /**
347 > * Create a new empty range using this range's end position.
348 > */
349 > public collapseToEnd(): Range {
350 return Range.collapseToEnd(this);
351 }
352 > range.ts
353 > /**
354 > * Create a new empty range using this range's end position.
355 > */
356 > public static collapseToEnd(range: IRange): Range {
357 return new Range(range.endLineNumber, range.endColumn, range.endLineNumber, range.endColumn);
358 }
359 > range.ts
360 > /**
361 > * Moves the range by the given amount of lines.
362 > */
363 > public delta(lineCount: number): Range {
364 return new Range(this.startLineNumber + lineCount, this.startColumn, this.endLineNumber + lineCount, this.endColumn);
365 }
366 > range.ts
367 > /**
368 > * Test if this range starts and ends on the same line.
369 > */
370 > public isSingleLine(): boolean {
371 return this.startLineNumber === this.endLineNumber;
372 }
373 > range.ts
374 > // ---
375 >
376 > public static fromPositions(start: IPosition, end: IPosition = start): Range {
377 return new Range(start.lineNumber, start.column, end.lineNumber, end.column);
378 }
379 > range.ts
380 > /**
381 > * Create a `Range` from an `IRange`.
382 > */
383 > public static lift(range: undefined | null): null;
384 > public static lift(range: IRange): Range;
385 > public static lift(range: IRange | undefined | null): Range | null;
386 > public static lift(range: IRange | undefined | null): Range | null {
387 if (!range) {
388 return null;
390 return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
391 }
392 > range.ts
393 > /**
394 > * Test if `obj` is an `IRange`.
395 > */
396 > public static isIRange(obj: unknown): obj is IRange {
397 return (
398 !!obj
403 );
404 }
405 > range.ts
406 > /**
407 > * Test if the two ranges are touching in any way.
408 > */
409 > public static areIntersectingOrTouching(a: IRange, b: IRange): boolean {
410 // Check if `a` is before `b`
411 if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn < b.startColumn)) {
421 return true;
422 }
423 > range.ts
424 > /**
425 > * Test if the two ranges are intersecting. If the ranges are touching it returns true.
426 > */
427 > public static areIntersecting(a: IRange, b: IRange): boolean {
428 // Check if `a` is before `b`
429 if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn <= b.startColumn)) {
439 return true;
440 }
441 > range.ts
442 > /**
443 > * Test if the two ranges are intersecting, but not touching at all.
444 > */
445 > public static areOnlyIntersecting(a: IRange, b: IRange): boolean {
446 // Check if `a` is before `b`
447 if (a.endLineNumber < (b.startLineNumber - 1) || (a.endLineNumber === b.startLineNumber && a.endColumn < (b.startColumn - 1))) {
457 return true;
458 }
459 > range.ts
460 > /**
461 > * A function that compares ranges, useful for sorting ranges
462 > * It will first compare ranges on the startPosition and then on the endPosition
463 > */
464 > public static compareRangesUsingStarts(a: IRange | null | undefined, b: IRange | null | undefined): number {
465 if (a && b) {
466 const aStartLineNumber = a.startLineNumber | 0;
490 return aExists - bExists;
491 }
492 > range.ts
493 > /**
494 > * A function that compares ranges, useful for sorting ranges
495 > * It will first compare ranges on the endPosition and then on the startPosition
496 > */
497 > public static compareRangesUsingEnds(a: IRange, b: IRange): number {
498 if (a.endLineNumber === b.endLineNumber) {
499 if (a.endColumn === b.endColumn) {
507 return a.endLineNumber - b.endLineNumber;
508 }
509 > range.ts
510 > /**
511 > * Test if the range spans multiple lines.
512 > */
513 > public static spansMultipleLines(range: IRange): boolean {
514 return range.endLineNumber > range.startLineNumber;
515 }
516 > range.ts
517 > public toJSON(): IRange {
518 return this;
519 }
520 > } range.ts
src/vs/nls.ts 213 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- nls.ts
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 > export function getNLSMessages(): string[] {
7 return globalThis._VSCODE_NLS_MESSAGES;
8 }
9 > nls.ts
10 > export function getNLSLanguage(): string | undefined {
11 > return globalThis._VSCODE_NLS_LANGUAGE;
12 > }
13 >
14 > declare const document: { location?: { hash?: string } } | undefined;
15 > const isPseudo = getNLSLanguage() === 'pseudo' || (typeof document !== 'undefined' && document.location && typeof document.location.hash === 'string' && document.location.hash.indexOf('pseudo=true') >= 0);
16 >
17 > export interface ILocalizeInfo {
18 > key: string;
19 > comment: string[];
20 > }
21 >
22 > export interface ILocalizedString {
23 > original: string;
24 > value: string;
25 > }
26 >
27 > function _format(message: string, args: (string | number | boolean | undefined | null)[]): string { nls.ts
28 > let result: string;
29 >
30 > if (args.length === 0) {
31 > result = message; nls.ts
32 > } else { nls.ts
33 > result = message.replace(/\{(\d+)\}/g, (match, rest) => { nls.ts
34 > const index = rest[0];
35 > const arg = args[index];
36 > let result = match;
37 > if (typeof arg === 'string') {
38 > result = arg; nls.ts
39 > } else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) { nls.ts
40 > result = String(arg); nls.ts
41 > }
42 > return result; nls.ts
43 > });
44 > }
45 > nls.ts
46 > if (isPseudo) {
47 // FF3B and FF3D is the Unicode zenkaku representation for [ and ]
48 result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D';
49 }
50 > nls.ts
51 > return result;
52 > }
53 > nls.ts
54 > /**
55 > * Marks a string to be localized. Returns the localized string.
56 > *
57 > * @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
58 > * @param message The string to localize
59 > * @param args The arguments to the string
60 > *
61 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
62 > * @example `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
63 > *
64 > * @returns string The localized string.
65 > */
66 > export function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
67 >
68 > /**
69 > * Marks a string to be localized. Returns the localized string.
70 > *
71 > * @param key The key to use for localizing the string
72 > * @param message The string to localize
73 > * @param args The arguments to the string
74 > *
75 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
76 > * @example For example, `localize('sayHello', 'hello {0}', name)`
77 > *
78 > * @returns string The localized string.
79 > */
80 > export function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
81 >
82 > /**
83 > * @skipMangle
84 > */
85 > export function localize(data: ILocalizeInfo | string /* | number when built */, message: string /* | null when built */, ...args: (string | number | boolean | undefined | null)[]): string {
86 > if (typeof data === 'number') { nls.ts
87 return _format(lookupMessage(data, message), args);
88 }
89 > return _format(message, args); nls.ts
90 > }
91 > nls.ts
92 > /**
93 > * Only used when built: Looks up the message in the global NLS table.
94 > * This table is being made available as a global through bootstrapping
95 > * depending on the target context.
96 > */
97 function lookupMessage(index: number, fallback: string | null): string {
98 const message = getNLSMessages()?.[index];
105 return message;
106 }
107 > nls.ts
108 > /**
109 > * Marks a string to be localized. Returns an {@linkcode ILocalizedString}
110 > * which contains the localized string and the original string.
111 > *
112 > * @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
113 > * @param message The string to localize
114 > * @param args The arguments to the string
115 > *
116 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
117 > * @example `localize2({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
118 > *
119 > * @returns ILocalizedString which contains the localized string and the original string.
120 > */
121 > export function localize2(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
122 >
123 > /**
124 > * Marks a string to be localized. Returns an {@linkcode ILocalizedString}
125 > * which contains the localized string and the original string.
126 > *
127 > * @param key The key to use for localizing the string
128 > * @param message The string to localize
129 > * @param args The arguments to the string
130 > *
131 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
132 > * @example `localize('sayHello', 'hello {0}', name)`
133 > *
134 > * @returns ILocalizedString which contains the localized string and the original string.
135 > */
136 > export function localize2(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
137 >
138 > /**
139 > * @skipMangle
140 > */
141 > export function localize2(data: ILocalizeInfo | string /* | number when built */, originalMessage: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString {
142 let message: string;
143 if (typeof data === 'number') {
154 };
155 }
156 > nls.ts
157 > export interface INLSLanguagePackConfiguration {
158 >
159 > /**
160 > * The path to the translations config file that contains pointers to
161 > * all message bundles for `main` and extensions.
162 > */
163 > readonly translationsConfigFile: string;
164 >
165 > /**
166 > * The path to the file containing the translations for this language
167 > * pack as flat string array.
168 > */
169 > readonly messagesFile: string;
170 >
171 > /**
172 > * The path to the file that can be used to signal a corrupt language
173 > * pack, for example when reading the `messagesFile` fails. This will
174 > * instruct the application to re-create the cache on next startup.
175 > */
176 > readonly corruptMarkerFile: string;
177 > }
178 >
179 > export interface INLSConfiguration {
180 >
181 > /**
182 > * Locale as defined in `argv.json` or `app.getLocale()`.
183 > */
184 > readonly userLocale: string;
185 >
186 > /**
187 > * Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`).
188 > */
189 > readonly osLocale: string;
190 >
191 > /**
192 > * The actual language of the UI that ends up being used considering `userLocale`
193 > * and `osLocale`.
194 > */
195 > readonly resolvedLanguage: string;
196 >
197 > /**
198 > * Defined if a language pack is used that is not the
199 > * default english language pack. This requires a language
200 > * pack to be installed as extension.
201 > */
202 > readonly languagePack?: INLSLanguagePackConfiguration;
203 >
204 > /**
205 > * The path to the file containing the default english messages
206 > * as flat string array. The file is only present in built
207 > * versions of the application.
208 > */
209 > readonly defaultMessagesFile: string;
210 >
211 > /**
212 > * Below properties are deprecated and only there to continue support
213 > * for `vscode-nls` module that depends on them.
214 > * Refs https://github.com/microsoft/vscode-nls/blob/main/src/node/main.ts#L36-L46
215 > */
216 > /** @deprecated */
217 > readonly locale: string;
218 > /** @deprecated */
219 > readonly availableLanguages: Record<string, string>;
220 > /** @deprecated */
221 > readonly _languagePackSupport?: boolean;
222 > /** @deprecated */
223 > readonly _languagePackId?: string;
224 > /** @deprecated */
225 > readonly _translationsConfigFile?: string;
226 > /** @deprecated */
227 > readonly _cacheRoot?: string;
228 > /** @deprecated */
229 > readonly _resolvedLanguagePackCoreLocation?: string;
230 > /** @deprecated */
231 > readonly _corruptedFile?: string;
232 > }
233 >
234 > export interface ILanguagePack {
235 > readonly hash: string;
236 > readonly label: string | undefined;
237 > readonly extensions: {
238 > readonly extensionIdentifier: { readonly id: string; readonly uuid?: string };
239 > readonly version: string;
240 > }[];
241 > readonly translations: Record<string, string | undefined>;
242 > }
243 >
244 > export type ILanguagePacks = Record<string, ILanguagePack | undefined>;
src/vs/platform/configuration/common/configuration.ts 203 covered LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- configuration.ts
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 { assertNever } from '../../../base/common/assert.js';
7 > import { IStringDictionary } from '../../../base/common/collections.js';
8 > import { Event } from '../../../base/common/event.js';
9 > import * as types from '../../../base/common/types.js';
10 > import { URI, UriComponents } from '../../../base/common/uri.js';
11 > import { createDecorator } from '../../instantiation/common/instantiation.js';
12 > import { IWorkspaceFolder } from '../../workspace/common/workspace.js';
13 >
14 > export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
15 >
16 > export function isConfigurationOverrides(obj: unknown): obj is IConfigurationOverrides {
17 const thing = obj as IConfigurationOverrides;
18 return thing
21 && (!thing.resource || thing.resource instanceof URI);
22 }
24 > export interface IConfigurationOverrides {
25 > overrideIdentifier?: string | null;
26 > resource?: URI | null;
27 > }
28 >
29 > export function isConfigurationUpdateOverrides(obj: unknown): obj is IConfigurationUpdateOverrides {
30 const thing = obj as IConfigurationUpdateOverrides | IConfigurationOverrides;
31 return thing
35 && (!thing.resource || thing.resource instanceof URI);
36 }
38 > export type IConfigurationUpdateOverrides = Omit<IConfigurationOverrides, 'overrideIdentifier'> & { overrideIdentifiers?: string[] | null };
39 >
40 > export const enum ConfigurationTarget {
41 > APPLICATION = 1,
42 > USER,
43 > USER_LOCAL,
44 > USER_REMOTE,
45 > WORKSPACE,
46 > WORKSPACE_FOLDER,
47 > DEFAULT,
48 > MEMORY
49 > }
50 > export function ConfigurationTargetToString(configurationTarget: ConfigurationTarget) {
51 switch (configurationTarget) {
52 case ConfigurationTarget.APPLICATION: return 'APPLICATION';
60 }
61 }
63 > export interface IConfigurationChange {
64 > keys: string[];
65 > overrides: [string, string[]][];
66 > }
67 >
68 > export interface IConfigurationChangeEvent {
69 >
70 > readonly source: ConfigurationTarget;
71 > readonly affectedKeys: ReadonlySet<string>;
72 > readonly change: IConfigurationChange;
73 >
74 > affectsConfiguration(configuration: string, overrides?: IConfigurationOverrides): boolean;
75 > }
76 >
77 > export interface IInspectValue<T> {
78 > readonly value?: T;
79 > readonly override?: T;
80 > readonly overrides?: { readonly identifiers: string[]; readonly value: T }[];
81 > }
82 >
83 > export interface IConfigurationValue<T> {
84 >
85 > readonly defaultValue?: T;
86 > readonly applicationValue?: T;
87 > readonly userValue?: T;
88 > readonly userLocalValue?: T;
89 > readonly userRemoteValue?: T;
90 > readonly workspaceValue?: T;
91 > readonly workspaceFolderValue?: T;
92 > readonly memoryValue?: T;
93 > readonly policyValue?: T;
94 > readonly value?: T;
95 >
96 > readonly default?: IInspectValue<T>;
97 > readonly application?: IInspectValue<T>;
98 > readonly user?: IInspectValue<T>;
99 > readonly userLocal?: IInspectValue<T>;
100 > readonly userRemote?: IInspectValue<T>;
101 > readonly workspace?: IInspectValue<T>;
102 > readonly workspaceFolder?: IInspectValue<T>;
103 > readonly memory?: IInspectValue<T>;
104 > readonly policy?: { value?: T };
105 >
106 > readonly overrideIdentifiers?: string[];
107 > }
108 >
109 > export function getConfigValueInTarget<T>(configValue: IConfigurationValue<T>, scope: ConfigurationTarget): T | undefined {
110 switch (scope) {
111 case ConfigurationTarget.APPLICATION:
129 }
130 }
132 > export function isConfigured<T>(configValue: IConfigurationValue<T>): configValue is IConfigurationValue<T> & { value: T } {
133 return configValue.applicationValue !== undefined ||
134 configValue.userValue !== undefined ||
138 configValue.workspaceFolderValue !== undefined;
139 }
141 > export interface IConfigurationUpdateOptions {
142 > /**
143 > * If `true`, do not notifies the error to user by showing the message box. Default is `false`.
144 > */
145 > donotNotifyError?: boolean;
146 > /**
147 > * How to handle dirty file when updating the configuration.
148 > */
149 > handleDirtyFile?: 'save' | 'revert';
150 > }
151 >
152 > export interface IConfigurationService {
153 > readonly _serviceBrand: undefined;
154 >
155 > readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
156 >
157 > getConfigurationData(): IConfigurationData | null;
158 >
159 > /**
160 > * Fetches the value of the section for the given overrides.
161 > * Value can be of native type or an object keyed off the section name.
162 > *
163 > * @param section - Section of the configuration. Can be `null` or `undefined`.
164 > * @param overrides - Overrides that has to be applied while fetching
165 > *
166 > */
167 > getValue<T>(): T;
168 > getValue<T>(section: string): T;
169 > getValue<T>(overrides: IConfigurationOverrides): T;
170 > getValue<T>(section: string, overrides: IConfigurationOverrides): T;
171 >
172 > /**
173 > * Update a configuration value.
174 > *
175 > * Use `target` to update the configuration in a specific `ConfigurationTarget`.
176 > *
177 > * Use `overrides` to update the configuration for a resource or for override identifiers or both.
178 > *
179 > * Passing a resource through overrides will update the configuration in the workspace folder containing that resource.
180 > *
181 > * *Note 1:* Updating configuration to a default value will remove the configuration from the requested target. If not target is passed, it will be removed from all writeable targets.
182 > *
183 > * *Note 2:* Use `undefined` value to remove the configuration from the given target. If not target is passed, it will be removed from all writeable targets.
184 > *
185 > * Use `donotNotifyError` and set it to `true` to surpresss errors.
186 > *
187 > * @param key setting to be updated
188 > * @param value The new value
189 > */
190 > updateValue(key: string, value: unknown): Promise<void>;
191 > updateValue(key: string, value: unknown, target: ConfigurationTarget): Promise<void>;
192 > updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;
193 > updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, options?: IConfigurationUpdateOptions): Promise<void>;
194 >
195 > inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<Readonly<T>>;
196 >
197 > reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise<void>;
198 >
199 > keys(): {
200 > default: string[];
201 > policy: string[];
202 > user: string[];
203 > workspace: string[];
204 > workspaceFolder: string[];
205 > memory?: string[];
206 > };
207 > }
208 >
209 > export interface IConfigurationModel {
210 > contents: IStringDictionary<unknown>;
211 > keys: string[];
212 > overrides: IOverrides[];
213 > raw?: ReadonlyArray<IStringDictionary<unknown>> | IStringDictionary<unknown>;
214 > }
215 >
216 > export interface IOverrides {
217 > keys: string[];
218 > contents: IStringDictionary<unknown>;
219 > identifiers: string[];
220 > }
221 >
222 > export interface IConfigurationData {
223 > defaults: IConfigurationModel;
224 > policy: IConfigurationModel;
225 > application: IConfigurationModel;
226 > userLocal: IConfigurationModel;
227 > userRemote: IConfigurationModel;
228 > workspace: IConfigurationModel;
229 > folders: [UriComponents, IConfigurationModel][];
230 > }
231 >
232 > export interface IConfigurationCompareResult {
233 > added: string[];
234 > removed: string[];
235 > updated: string[];
236 > overrides: [string, string[]][];
237 > }
238 >
239 > export function toValuesTree(properties: IStringDictionary<unknown>, conflictReporter: (message: string) => void): IStringDictionary<unknown> {
240 const root = Object.create(null);
241
246 return root;
247 }
249 > export function addToValueTree(settingsTreeRoot: IStringDictionary<unknown>, key: string, value: unknown, conflictReporter: (message: string) => void): void {
250 const segments = key.split('.');
251 const last = segments.pop()!;
282 }
283 }
285 > export function removeFromValueTree(valueTree: IStringDictionary<unknown>, key: string): void {
286 const segments = key.split('.');
287 doRemoveFromValueTree(valueTree, segments);
288 }
290 function doRemoveFromValueTree(valueTree: IStringDictionary<unknown> | unknown, segments: string[]): void {
291 if (!valueTree) {
311 }
312 }
314 > /**
315 > * A helper function to get the configuration value with a specific settings path (e.g. config.some.setting)
316 > */
317 > export function getConfigurationValue<T>(config: IStringDictionary<unknown>, settingPath: string): T | undefined;
318 > export function getConfigurationValue<T>(config: IStringDictionary<unknown>, settingPath: string, defaultValue: T): T;
319 > export function getConfigurationValue<T>(config: IStringDictionary<unknown>, settingPath: string, defaultValue?: T): T | undefined {
320 function accessSetting(config: IStringDictionary<unknown>, path: string[]): unknown {
321 let current: unknown = config;
334 return typeof result === 'undefined' ? defaultValue : result as T;
335 }
337 > export function merge(base: IStringDictionary<unknown>, add: IStringDictionary<unknown>, overwrite: boolean): void {
338 Object.keys(add).forEach(key => {
339 if (key !== '__proto__') {
350 });
351 }
353 > export function getLanguageTagSettingPlainKey(settingKey: string) {
354 return settingKey
355 .replace(/^\[/, '')
src/vs/base/common/ternarySearchTree.ts 200 covered LOC · 63 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- ternarySearchTree.ts
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 { shuffle } from './arrays.js';
7 > import { assert } from './assert.js';
8 > import { CharCode } from './charCode.js';
9 > import { compare, compareIgnoreCase, compareSubstring, compareSubstringIgnoreCase } from './strings.js';
10 > import { URI } from './uri.js';
11 >
12 > export interface IKeyIterator<K> {
13 > reset(key: K): this;
14 > next(): this;
15 >
16 > hasNext(): boolean;
17 > cmp(a: string): number;
18 > value(): string;
19 > }
20 >
21 > export class StringIterator implements IKeyIterator<string> {
22
23 private _value: string = '';
24 private _pos: number = 0;
26 > reset(key: string): this {
27 this._value = key;
28 this._pos = 0;
29 return this;
30 }
32 > next(): this {
33 this._pos += 1;
34 return this;
35 }
37 > hasNext(): boolean {
38 return this._pos < this._value.length - 1;
39 }
41 > cmp(a: string): number {
42 const aCode = a.charCodeAt(0);
43 const thisCode = this._value.charCodeAt(this._pos);
44 return aCode - thisCode;
45 }
47 > value(): string {
48 return this._value[this._pos];
49 }
51 >
52 > export class ConfigKeysIterator implements IKeyIterator<string> {
53 >
54 > private _value!: string;
55 > private _from!: number;
56 > private _to!: number;
57 >
58 > constructor(
59 private readonly _caseSensitive: boolean = true
60 ) { }
62 > reset(key: string): this {
63 this._value = key;
64 this._from = 0;
66 return this.next();
67 }
69 > hasNext(): boolean {
70 return this._to < this._value.length;
71 }
73 > next(): this {
74 // this._data = key.split(/[\\/]/).filter(s => !!s);
75 this._from = this._to;
89 return this;
90 }
92 > cmp(a: string): number {
93 return this._caseSensitive
94 ? compareSubstring(a, this._value, 0, a.length, this._from, this._to)
95 : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
96 }
98 > value(): string {
99 return this._value.substring(this._from, this._to);
100 }
102 >
103 > export class PathIterator implements IKeyIterator<string> {
104 >
105 > private _value!: string;
106 > private _valueLen!: number;
107 > private _from!: number;
108 > private _to!: number;
109 >
110 > constructor(
111 > private readonly _splitOnBackslash: boolean = true, ternarySearchTree.ts
112 > private readonly _caseSensitive: boolean = true
113 > ) { }
115 > reset(key: string): this {
116 this._from = 0;
117 this._to = 0;
127 return this.next();
128 }
130 > hasNext(): boolean {
131 return this._to < this._valueLen;
132 }
134 > next(): this {
135 // this._data = key.split(/[\\/]/).filter(s => !!s);
136 this._from = this._to;
150 return this;
151 }
153 > cmp(a: string): number {
154 return this._caseSensitive
155 ? compareSubstring(a, this._value, 0, a.length, this._from, this._to)
156 : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
157 }
159 > value(): string {
160 return this._value.substring(this._from, this._to);
161 }
163 >
164 > const enum UriIteratorState {
165 > Scheme = 1, Authority = 2, Path = 3, Query = 4, Fragment = 5
166 > }
167 >
168 > export class UriIterator implements IKeyIterator<URI> {
169 >
170 > private _pathIterator!: PathIterator;
171 > private _value!: URI;
172 > private _states: UriIteratorState[] = [];
173 > private _stateIdx: number = 0;
174 >
175 > constructor(
176 private readonly _ignorePathCasing: (uri: URI) => boolean,
177 private readonly _ignoreQueryAndFragment: (uri: URI) => boolean) { }
179 > reset(key: URI): this {
180 this._value = key;
181 this._states = [];
204 return this;
205 }
207 > next(): this {
208 if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) {
209 this._pathIterator.next();
213 return this;
214 }
216 > hasNext(): boolean {
217 return (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext())
218 || this._stateIdx < this._states.length - 1;
219 }
221 > cmp(a: string): number {
222 if (this._states[this._stateIdx] === UriIteratorState.Scheme) {
223 return compareIgnoreCase(a, this._value.scheme);
233 throw new Error();
234 }
236 > value(): string {
237 if (this._states[this._stateIdx] === UriIteratorState.Scheme) {
238 return this._value.scheme;
248 throw new Error();
249 }
251 >
252 > abstract class Undef {
253 >
254 > static readonly Val: unique symbol = Symbol('undefined_placeholder');
255 >
256 > static wrap<V>(value: V | undefined): V | typeof Undef.Val {
257 return value === undefined ? Undef.Val : value;
258 }
260 > static unwrap<V>(value: V | typeof Undef.Val): V | undefined {
261 return value === Undef.Val ? undefined : value;
262 }
264 >
265 class TernarySearchTreeNode<K, V> {
266 height: number = 1;
271 mid: TernarySearchTreeNode<K, V> | undefined = undefined;
272 right: TernarySearchTreeNode<K, V> | undefined = undefined;
274 > isEmpty(): boolean {
275 return !this.left && !this.mid && !this.right && this.value === undefined;
276 }
278 > rotateLeft() {
279 const tmp = this.right!;
280 this.right = tmp.left;
284 return tmp;
285 }
287 > rotateRight() {
288 const tmp = this.left!;
289 this.left = tmp.right;
293 return tmp;
294 }
296 > updateHeight() {
297 this.height = 1 + Math.max(this.heightLeft, this.heightRight);
298 }
300 > balanceFactor() {
301 return this.heightRight - this.heightLeft;
302 }
304 > get heightLeft() {
305 return this.left?.height ?? 0;
306 }
308 > get heightRight() {
309 return this.right?.height ?? 0;
310 }
312 >
313 > const enum Dir {
314 > Left = -1,
315 > Mid = 0,
316 > Right = 1
317 > }
318 >
319 > export class TernarySearchTree<K, V> {
320 >
321 > static forUris<E>(ignorePathCasing: (key: URI) => boolean = () => false, ignoreQueryAndFragment: (key: URI) => boolean = () => false): TernarySearchTree<URI, E> {
322 return new TernarySearchTree<URI, E>(new UriIterator(ignorePathCasing, ignoreQueryAndFragment));
323 }
325 > static forPaths<E>(ignorePathCasing = false): TernarySearchTree<string, E> {
326 > return new TernarySearchTree<string, E>(new PathIterator(undefined, !ignorePathCasing)); ternarySearchTree.ts
327 > }
329 > static forStrings<E>(): TernarySearchTree<string, E> {
330 return new TernarySearchTree<string, E>(new StringIterator());
331 }
333 > static forConfigKeys<E>(): TernarySearchTree<string, E> {
334 return new TernarySearchTree<string, E>(new ConfigKeysIterator());
335 }
337 > private _iter: IKeyIterator<K>;
338 > private _root: TernarySearchTreeNode<K, V> | undefined;
339 >
340 > constructor(segments: IKeyIterator<K>) {
341 > this._iter = segments; ternarySearchTree.ts
342 > }
344 > clear(): void {
345 this._root = undefined;
346 }
348 > /**
349 > * Fill the tree with the same value of the given keys
350 > */
351 > fill(element: V, keys: readonly K[]): void;
352 > /**
353 > * Fill the tree with given [key,value]-tuples
354 > */
355 > fill(values: readonly [K, V][]): void;
356 > fill(values: readonly [K, V][] | V, keys?: readonly K[]): void {
357 if (keys) {
358 const arr = keys.slice(0);
369 }
370 }
372 > set(key: K, element: V): V | undefined {
373 const iter = this._iter.reset(key);
374 let node: TernarySearchTreeNode<K, V>;
476 return oldElement;
477 }
479 > get(key: K): V | undefined {
480 return Undef.unwrap(this._getNode(key)?.value);
481 }
483 > private _getNode(key: K) {
484 const iter = this._iter.reset(key);
485 let node = this._root;
502 return node;
503 }
505 > has(key: K): boolean {
506 const node = this._getNode(key);
507 return !(node?.value === undefined && node?.mid === undefined);
508 }
510 > delete(key: K): void {
511 return this._delete(key, false);
512 }
514 > deleteSuperstr(key: K): void {
515 return this._delete(key, true);
516 }
518 > private _delete(key: K, superStr: boolean): void {
519 const iter = this._iter.reset(key);
520 const stack: [Dir, TernarySearchTreeNode<K, V>][] = [];
620 this._root = this._balanceByStack(stack) ?? this._root;
621 }
623 > private _min(node: TernarySearchTreeNode<K, V>, stack: [Dir, TernarySearchTreeNode<K, V>][]): TernarySearchTreeNode<K, V> {
624 while (node.left) {
625 stack.push([Dir.Left, node]);
628 return node;
629 }
631 > private _balanceByStack(stack: [Dir, TernarySearchTreeNode<K, V>][]) {
632
633 for (let i = stack.length - 1; i >= 0; i--) {
679 return undefined;
680 }
682 > findSubstr(key: K): V | undefined {
683 const iter = this._iter.reset(key);
684 let node = this._root;
703 return node && Undef.unwrap(node.value) || candidate;
704 }
706 > findSuperstr(key: K): IterableIterator<[K, V]> | undefined {
707 return this._findSuperstrOrElement(key, false);
708 }
710 > private _findSuperstrOrElement(key: K, allowValue: true): IterableIterator<[K, V]> | V | undefined;
711 > private _findSuperstrOrElement(key: K, allowValue: false): IterableIterator<[K, V]> | undefined;
712 > private _findSuperstrOrElement(key: K, allowValue: boolean): IterableIterator<[K, V]> | V | undefined {
713 const iter = this._iter.reset(key);
714 let node = this._root;
740 return undefined;
741 }
743 > hasElementOrSubtree(key: K): boolean {
744 return this._findSuperstrOrElement(key, true) !== undefined;
745 }
747 > forEach(callback: (value: V, index: K) => unknown): void {
748 for (const [key, value] of this) {
749 callback(value, key);
750 }
751 }
753 > *[Symbol.iterator](): IterableIterator<[K, V]> {
754 yield* this._entries(this._root);
755 }
757 > private _entries(node: TernarySearchTreeNode<K, V> | undefined): IterableIterator<[K, V]> {
758 const result: [K, V][] = [];
759 this._dfsEntries(node, result);
760 return result[Symbol.iterator]();
761 }
763 > private _dfsEntries(node: TernarySearchTreeNode<K, V> | undefined, bucket: [K, V][]) {
764 // DFS
765 if (!node) {
779 }
780 }
782 > // for debug/testing
783 > _isBalanced(): boolean {
784 const nodeIsBalanced = (node: TernarySearchTreeNode<unknown, unknown> | undefined): boolean => {
785 if (!node) {
src/vs/base/common/jsonSchema.ts 194 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- jsonSchema.ts
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 > export type JSONSchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'array' | 'object';
7 >
8 > export interface IJSONSchema {
9 > id?: string;
10 > $id?: string;
11 > $schema?: string;
12 > type?: JSONSchemaType | JSONSchemaType[];
13 > title?: string;
14 > default?: any;
15 > definitions?: IJSONSchemaMap;
16 > description?: string;
17 > properties?: IJSONSchemaMap;
18 > patternProperties?: IJSONSchemaMap;
19 > additionalProperties?: boolean | IJSONSchema;
20 > minProperties?: number;
21 > maxProperties?: number;
22 > dependencies?: IJSONSchemaMap | { [prop: string]: string[] };
23 > items?: IJSONSchema | IJSONSchema[];
24 > minItems?: number;
25 > maxItems?: number;
26 > uniqueItems?: boolean;
27 > additionalItems?: boolean | IJSONSchema;
28 > pattern?: string;
29 > minLength?: number;
30 > maxLength?: number;
31 > minimum?: number;
32 > maximum?: number;
33 > exclusiveMinimum?: boolean | number;
34 > exclusiveMaximum?: boolean | number;
35 > multipleOf?: number;
36 > required?: string[];
37 > $ref?: string;
38 > anyOf?: IJSONSchema[];
39 > allOf?: IJSONSchema[];
40 > oneOf?: IJSONSchema[];
41 > not?: IJSONSchema;
42 > enum?: any[];
43 > format?: string;
44 >
45 > // schema draft 06
46 > const?: any;
47 > contains?: IJSONSchema;
48 > propertyNames?: IJSONSchema;
49 > examples?: any[];
50 >
51 > // schema draft 07
52 > $comment?: string;
53 > if?: IJSONSchema;
54 > then?: IJSONSchema;
55 > else?: IJSONSchema;
56 >
57 > // schema 2019-09
58 > unevaluatedProperties?: boolean | IJSONSchema;
59 > unevaluatedItems?: boolean | IJSONSchema;
60 > minContains?: number;
61 > maxContains?: number;
62 > deprecated?: boolean;
63 > dependentRequired?: { [prop: string]: string[] };
64 > dependentSchemas?: IJSONSchemaMap;
65 > $defs?: { [name: string]: IJSONSchema };
66 > $anchor?: string;
67 > $recursiveRef?: string;
68 > $recursiveAnchor?: string;
69 > $vocabulary?: any;
70 >
71 > // schema 2020-12
72 > prefixItems?: IJSONSchema[];
73 > $dynamicRef?: string;
74 > $dynamicAnchor?: string;
75 >
76 > // VSCode extensions
77 >
78 > defaultSnippets?: IJSONSchemaSnippet[];
79 > errorMessage?: string;
80 > patternErrorMessage?: string;
81 > deprecationMessage?: string;
82 > markdownDeprecationMessage?: string;
83 > enumDescriptions?: string[];
84 > markdownEnumDescriptions?: string[];
85 > markdownDescription?: string;
86 > doNotSuggest?: boolean;
87 > suggestSortText?: string;
88 > allowComments?: boolean;
89 > allowTrailingCommas?: boolean;
90 > secret?: boolean;
91 > }
92 >
93 > export interface IJSONSchemaMap {
94 > [name: string]: IJSONSchema;
95 > }
96 >
97 > export interface IJSONSchemaSnippet {
98 > label?: string;
99 > description?: string;
100 > body?: any; // a object that will be JSON stringified
101 > bodyText?: string; // an already stringified JSON object that can contain new lines (\n) and tabs (\t)
102 > }
103 >
104 > /**
105 > * Converts a basic JSON schema to a TypeScript type.
106 > */
107 > export type TypeFromJsonSchema<T> =
108 > // enum
109 > T extends { enum: infer EnumValues }
110 > ? UnionOf<EnumValues>
111 >
112 > // Object with list of required properties.
113 > // Values are required or optional based on `required` list.
114 > : T extends { type: 'object'; properties: infer P; required: infer RequiredList }
115 > ? {
116 > [K in keyof P]: IsRequired<K, RequiredList> extends true ? TypeFromJsonSchema<P[K]> : TypeFromJsonSchema<P[K]> | undefined;
117 > } & AdditionalPropertiesType<T>
118 >
119 > // Object with no required properties.
120 > // All values are optional
121 > : T extends { type: 'object'; properties: infer P }
122 > ? { [K in keyof P]: TypeFromJsonSchema<P[K]> | undefined } & AdditionalPropertiesType<T>
123 >
124 > // Array
125 > : T extends { type: 'array'; items: infer Items }
126 > ? Items extends [...infer R]
127 > // If items is an array, we treat it like a tuple
128 > ? { [K in keyof R]: TypeFromJsonSchema<Items[K]> }
129 > : Array<TypeFromJsonSchema<Items>>
130 >
131 > // oneOf / anyof
132 > // These are handled the same way as they both represent a union type.
133 > // However at the validation level, they have different semantics.
134 > : T extends { oneOf: infer I }
135 > ? MapSchemaToType<I>
136 > : T extends { anyOf: infer I }
137 > ? MapSchemaToType<I>
138 >
139 > // Primitive types
140 > : T extends { type: infer Type }
141 > // Basic type
142 > ? Type extends 'string' | 'number' | 'integer' | 'boolean' | 'null'
143 > ? SchemaPrimitiveTypeNameToType<Type>
144 > // Union of primitive types
145 > : Type extends [...infer R]
146 > ? UnionOf<{ [K in keyof R]: SchemaPrimitiveTypeNameToType<R[K]> }>
147 > : never
148 >
149 > // Fallthrough
150 > : never;
151 >
152 > type SchemaPrimitiveTypeNameToType<T> =
153 > T extends 'string' ? string :
154 > T extends 'number' | 'integer' ? number :
155 > T extends 'boolean' ? boolean :
156 > T extends 'null' ? null :
157 > never;
158 >
159 > type UnionOf<T> =
160 > T extends [infer First, ...infer Rest]
161 > ? First | UnionOf<Rest>
162 > : never;
163 >
164 > type IsRequired<K, RequiredList> =
165 > RequiredList extends []
166 > ? false
167 >
168 > : RequiredList extends [K, ...infer _]
169 > ? true
170 >
171 > : RequiredList extends [infer _, ...infer R]
172 > ? IsRequired<K, R>
173 >
174 > : false;
175 >
176 > type AdditionalPropertiesType<Schema> =
177 > Schema extends { additionalProperties: infer AP }
178 > ? AP extends false ? {} : { [key: string]: TypeFromJsonSchema<Schema['additionalProperties']> }
179 > : {};
180 >
181 > type MapSchemaToType<T> = T extends [infer First, ...infer Rest]
182 > ? TypeFromJsonSchema<First> | MapSchemaToType<Rest>
183 > : never;
184 >
185 > interface Equals { schemas: IJSONSchema[]; id?: string }
186 >
187 > export function getCompressedContent(schema: IJSONSchema): string {
188 let hasDups = false;
189
258 return str;
259 }
261 > type IJSONSchemaRef = IJSONSchema | boolean;
262 >
263 function isObject(thing: unknown): thing is object {
264 return typeof thing === 'object' && thing !== null;
265 }
267 > /*
268 > * Traverse a JSON schema and visit each schema node
269 > */
270 function traverseNodes(root: IJSONSchema, visit: (schema: IJSONSchema) => boolean) {
271 if (!root || typeof root !== 'object') {
src/vs/base/common/platform.ts 189 covered LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- platform.ts
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 * as nls from '../../nls.js';
7 >
8 > export const LANGUAGE_DEFAULT = 'en';
9 >
10 > let _isWindows = false;
11 > let _isMacintosh = false;
12 > let _isLinux = false;
13 > let _isLinuxSnap = false;
14 > let _isNative = false;
15 > let _isWeb = false;
16 > let _isElectron = false;
17 > let _isIOS = false;
18 > let _isCI = false;
19 > let _isMobile = false;
20 > let _locale: string | undefined = undefined;
21 > let _language: string = LANGUAGE_DEFAULT;
22 > let _platformLocale: string = LANGUAGE_DEFAULT;
23 > let _translationsConfigFile: string | undefined = undefined;
24 > let _userAgent: string | undefined = undefined;
25 >
26 > export interface IProcessEnvironment {
27 > [key: string]: string | undefined;
28 > }
29 >
30 > /**
31 > * This interface is intentionally not identical to node.js
32 > * process because it also works in sandboxed environments
33 > * where the process object is implemented differently. We
34 > * define the properties here that we need for `platform`
35 > * to work and nothing else.
36 > */
37 > export interface INodeProcess {
38 > platform: string;
39 > arch: string;
40 > env: IProcessEnvironment;
41 > versions?: {
42 > node?: string;
43 > electron?: string;
44 > chrome?: string;
45 > };
46 > type?: string;
47 > cwd: () => string;
48 > }
49 >
50 > declare const process: INodeProcess;
51 >
52 > const $globalThis: any = globalThis;
53 >
54 > let nodeProcess: INodeProcess | undefined = undefined;
55 > if (typeof $globalThis.vscode !== 'undefined' && typeof $globalThis.vscode.process !== 'undefined') {
56 // Native environment (sandboxed)
57 nodeProcess = $globalThis.vscode.process;
58 > } else if (typeof process !== 'undefined' && typeof process?.versions?.node === 'string') { platform.ts
59 > // Native environment (non-sandboxed)
60 > nodeProcess = process;
61 > }
62 >
63 > const isElectronProcess = typeof nodeProcess?.versions?.electron === 'string';
64 > const isElectronRenderer = isElectronProcess && nodeProcess?.type === 'renderer';
65 >
66 > interface INavigator {
67 > userAgent: string;
68 > maxTouchPoints?: number;
69 > language: string;
70 > }
71 > declare const navigator: INavigator;
72 >
73 > // Native environment
74 > if (typeof nodeProcess === 'object') {
75 > _isWindows = (nodeProcess.platform === 'win32');
76 > _isMacintosh = (nodeProcess.platform === 'darwin');
77 > _isLinux = (nodeProcess.platform === 'linux');
78 > _isLinuxSnap = _isLinux && !!nodeProcess.env['SNAP'] && !!nodeProcess.env['SNAP_REVISION'];
79 > _isElectron = isElectronProcess;
80 > _isCI = !!nodeProcess.env['CI'] || !!nodeProcess.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] || !!nodeProcess.env['GITHUB_WORKSPACE'];
81 > _locale = LANGUAGE_DEFAULT;
82 > _language = LANGUAGE_DEFAULT;
83 > const rawNlsConfig = nodeProcess.env['VSCODE_NLS_CONFIG'];
84 > if (rawNlsConfig) {
85 try {
86 const nlsConfig: nls.INLSConfiguration = JSON.parse(rawNlsConfig);
113 console.error('Unable to resolve platform.');
114 }
115 > platform.ts
116 > export const enum Platform {
117 > Web,
118 > Mac,
119 > Linux,
120 > Windows
121 > }
122 > export type PlatformName = 'Web' | 'Windows' | 'Mac' | 'Linux';
123 >
124 > export function PlatformToString(platform: Platform): PlatformName {
125 switch (platform) {
126 case Platform.Web: return 'Web';
130 }
131 }
132 > platform.ts
133 > let _platform: Platform = Platform.Web;
134 > if (_isMacintosh) {
135 _platform = Platform.Mac;
136 > } else if (_isWindows) { platform.ts
137 _platform = Platform.Windows;
138 > } else if (_isLinux) { platform.ts
139 > _platform = Platform.Linux;
140 > }
141 >
142 > export const isWindows = _isWindows;
143 > export const isMacintosh = _isMacintosh;
144 > export const isLinux = _isLinux;
145 > export const isLinuxSnap = _isLinuxSnap;
146 > export const isNative = _isNative;
147 > export const isElectron = _isElectron;
148 > export const isWeb = _isWeb;
149 > export const isWebWorker = (_isWeb && typeof $globalThis.importScripts === 'function');
150 > export const webWorkerOrigin = isWebWorker ? $globalThis.origin : undefined;
151 > export const isIOS = _isIOS;
152 > export const isMobile = _isMobile;
153 > /**
154 > * Whether we run inside a CI environment, such as
155 > * GH actions or Azure Pipelines.
156 > */
157 > export const isCI = _isCI;
158 > export const platform = _platform;
159 > export const userAgent = _userAgent;
160 >
161 > /**
162 > * The language used for the user interface. The format of
163 > * the string is all lower case (e.g. zh-tw for Traditional
164 > * Chinese or de for German)
165 > */
166 > export const language = _language;
167 >
168 > export namespace Language {
169 >
170 > export function value(): string {
171 return language;
172 }
173 > platform.ts
174 > export function isDefaultVariant(): boolean {
175 if (language.length === 2) {
176 return language === 'en';
181 }
182 }
183 > platform.ts
184 > export function isDefault(): boolean {
185 return language === 'en';
186 }
187 > } platform.ts
188 >
189 > /**
190 > * Desktop: The OS locale or the locale specified by --locale or `argv.json`.
191 > * Web: matches `platformLocale`.
192 > *
193 > * The UI is not necessarily shown in the provided locale.
194 > */
195 > export const locale = _locale;
196 >
197 > /**
198 > * This will always be set to the OS/browser's locale regardless of
199 > * what was specified otherwise. The format of the string is all
200 > * lower case (e.g. zh-tw for Traditional Chinese). The UI is not
201 > * necessarily shown in the provided locale.
202 > */
203 > export const platformLocale = _platformLocale;
204 >
205 > /**
206 > * The translations that are available through language packs.
207 > */
208 > export const translationsConfigFile = _translationsConfigFile;
209 >
210 > export const setTimeout0IsFaster = (typeof $globalThis.postMessage === 'function' && !$globalThis.importScripts);
211 >
212 > /**
213 > * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-.
214 > *
215 > * Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay
216 > * that browsers set when the nesting level is > 5.
217 > */
218 > export const setTimeout0 = (() => {
219 > if (setTimeout0IsFaster) {
220 interface IQueueElement {
221 id: number;
246 };
247 }
248 > return (callback: () => void) => setTimeout(callback); platform.ts
249 > })();
250 >
251 > export const enum OperatingSystem {
252 > Windows = 1,
253 > Macintosh = 2,
254 > Linux = 3
255 > }
256 > export const OS = (_isMacintosh || _isIOS ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux));
257 >
258 > let _isLittleEndian = true;
259 > let _isLittleEndianComputed = false;
260 > export function isLittleEndian(): boolean {
261 if (!_isLittleEndianComputed) {
262 _isLittleEndianComputed = true;
269 return _isLittleEndian;
270 }
271 > platform.ts
272 > export const isChrome = !!(userAgent && userAgent.indexOf('Chrome') >= 0);
273 > export const isFirefox = !!(userAgent && userAgent.indexOf('Firefox') >= 0);
274 > export const isSafari = !!(!isChrome && (userAgent && userAgent.indexOf('Safari') >= 0));
275 > export const isEdge = !!(userAgent && userAgent.indexOf('Edg/') >= 0);
276 > export const isAndroid = !!(userAgent && userAgent.indexOf('Android') >= 0);
277 > export const hasElectronUserAgent = !!(userAgent && userAgent.indexOf('Electron') >= 0);
278 >
279 > export function isTahoeOrNewer(osVersion: string): boolean {
280 return parseFloat(osVersion) >= 25;
281 }
src/vs/base/common/errors.ts 183 covered LOC · 32 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- errors.ts
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 > export interface ErrorListenerCallback {
7 > (error: any): void;
8 > }
9 >
10 > export interface ErrorListenerUnbind {
11 > (): void;
12 > }
13 >
14 > // Avoid circular dependency on EventEmitter by implementing a subset of the interface.
15 > export class ErrorHandler {
16 > private unexpectedErrorHandler: (e: any) => void;
17 > private listeners: ErrorListenerCallback[];
18 >
19 > constructor() {
20 >
21 > this.listeners = [];
22 >
23 > this.unexpectedErrorHandler = function (e: any) {
24 setTimeout(() => {
25 if (e.stack) {
34 }, 0);
35 };
36 > } errors.ts
37 >
38 > addListener(listener: ErrorListenerCallback): ErrorListenerUnbind {
39 this.listeners.push(listener);
40
43 };
44 }
45 > errors.ts
46 > private emit(e: any): void {
47 this.listeners.forEach((listener) => {
48 listener(e);
49 });
50 }
51 > errors.ts
52 > private _removeListener(listener: ErrorListenerCallback): void {
53 this.listeners.splice(this.listeners.indexOf(listener), 1);
54 }
55 > errors.ts
56 > setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {
57 > this.unexpectedErrorHandler = newUnexpectedErrorHandler;
58 > }
59 >
60 > getUnexpectedErrorHandler(): (e: any) => void {
61 return this.unexpectedErrorHandler;
62 }
63 > errors.ts
64 > onUnexpectedError(e: any): void {
65 this.unexpectedErrorHandler(e);
66 this.emit(e);
67 }
68 > errors.ts
69 > // For external errors, we don't want the listeners to be called
70 > onUnexpectedExternalError(e: any): void {
71 this.unexpectedErrorHandler(e);
72 }
73 > } errors.ts
74 >
75 > export const errorHandler = new ErrorHandler();
76 >
77 > /** @skipMangle */
78 > export function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void {
79 > errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler);
80 > }
81 >
82 > /**
83 > * Returns if the error is a SIGPIPE error. SIGPIPE errors should generally be
84 > * logged at most once, to avoid a loop.
85 > *
86 > * @see https://github.com/microsoft/vscode-remote-release/issues/6481
87 > */
88 > export function isSigPipeError(e: unknown): e is Error {
89 if (!e || typeof e !== 'object') {
90 return false;
94 return cast.code === 'EPIPE' && cast.syscall?.toUpperCase() === 'WRITE';
95 }
96 > errors.ts
97 > /**
98 > * This function should only be called with errors that indicate a bug in the product.
99 > * E.g. buggy extensions/invalid user-input/network issues should not be able to trigger this code path.
100 > * If they are, this indicates there is also a bug in the product.
101 > */
102 > export function onBugIndicatingError(e: any): undefined {
103 errorHandler.onUnexpectedError(e);
104 return undefined;
105 }
106 > errors.ts
107 > export function onUnexpectedError(e: any): undefined {
108 // ignore errors from cancelled promises
109 if (!isCancellationError(e)) {
112 return undefined;
113 }
114 > errors.ts
115 > export function onUnexpectedExternalError(e: any): undefined {
116 // ignore errors from cancelled promises
117 if (!isCancellationError(e)) {
120 return undefined;
121 }
122 > errors.ts
123 > type ObjectWithCode = {
124 > readonly code: unknown;
125 > };
126 >
127 function hasErrorCode(error: object): error is ObjectWithCode {
128 return Object.hasOwn(error, 'code');
129 }
130 > errors.ts
131 > export function getErrorCode(error: unknown): string | undefined {
132 if (!error || typeof error !== 'object' || !hasErrorCode(error)) {
133 return undefined;
136 return typeof code === 'string' || typeof code === 'number' ? String(code) : undefined;
137 }
138 > errors.ts
139 > export interface SerializedError {
140 > readonly $isError: true;
141 > readonly name: string;
142 > readonly message: string;
143 > readonly stack: string;
144 > readonly noTelemetry: boolean;
145 > readonly code?: string;
146 > readonly cause?: SerializedError;
147 > }
148 >
149 > type ErrorWithCode = Error & {
150 > code: string | undefined;
151 > };
152 >
153 > export function transformErrorForSerialization(error: Error): SerializedError;
154 > export function transformErrorForSerialization(error: any): any;
155 > export function transformErrorForSerialization(error: any): any {
156 if (error instanceof Error) {
157 const { name, message, cause } = error;
172 return error;
173 }
174 > errors.ts
175 > export function transformErrorFromSerialization(data: SerializedError): Error {
176 let error: Error;
177 if (data.noTelemetry) {
191 return error;
192 }
193 > errors.ts
194 > // see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces
195 > export interface V8CallSite {
196 > getThis(): unknown;
197 > getTypeName(): string | null;
198 > getFunction(): Function | undefined;
199 > getFunctionName(): string | null;
200 > getMethodName(): string | null;
201 > getFileName(): string | null;
202 > getLineNumber(): number | null;
203 > getColumnNumber(): number | null;
204 > getEvalOrigin(): string | undefined;
205 > isToplevel(): boolean;
206 > isEval(): boolean;
207 > isNative(): boolean;
208 > isConstructor(): boolean;
209 > toString(): string;
210 > }
211 >
212 > export const canceledName = 'Canceled';
213 >
214 > /**
215 > * Checks if the given error is a promise in canceled state
216 > */
217 > export function isCancellationError(error: any): boolean {
218 if (error instanceof CancellationError) {
219 return true;
221 return error instanceof Error && error.name === canceledName && error.message === canceledName;
222 }
223 > errors.ts
224 > // !!!IMPORTANT!!!
225 > // Do NOT change this class because it is also used as an API-type.
226 > export class CancellationError extends Error {
227 > constructor() {
228 super(canceledName);
229 this.name = this.message;
230 }
231 > } errors.ts
232 >
233 > export class PendingMigrationError extends Error {
234 >
235 > private static readonly _name = 'PendingMigrationError';
236 >
237 > static is(error: unknown): error is PendingMigrationError {
238 return error instanceof PendingMigrationError || (error instanceof Error && error.name === PendingMigrationError._name);
239 }
240 > errors.ts
241 > constructor(message: string) {
242 super(message);
243 this.name = PendingMigrationError._name;
244 }
245 > } errors.ts
246 >
247 > /**
248 > * @deprecated use {@link CancellationError `new CancellationError()`} instead
249 > */
250 > export function canceled(): Error {
251 const error = new Error(canceledName);
252 error.name = error.message;
253 return error;
254 }
255 > errors.ts
256 > export function illegalArgument(name?: string): Error {
257 if (name) {
258 return new Error(`Illegal argument: ${name}`);
261 }
262 }
263 > errors.ts
264 > export function illegalState(name?: string): Error {
265 if (name) {
266 return new Error(`Illegal state: ${name}`);
269 }
270 }
271 > errors.ts
272 > export class ReadonlyError extends TypeError {
273 > constructor(name?: string) {
274 super(name ? `${name} is read-only and cannot be changed` : 'Cannot change read-only property');
275 }
276 > } errors.ts
277 >
278 > export function getErrorMessage(err: any): string {
279 if (!err) {
280 return 'Error';
291 return String(err);
292 }
293 > errors.ts
294 > export class NotImplementedError extends Error {
295 > constructor(message?: string) {
296 super('NotImplemented');
297 if (message) {
299 }
300 }
301 > } errors.ts
302 >
303 > export class NotSupportedError extends Error {
304 > constructor(message?: string) {
305 super('NotSupported');
306 if (message) {
308 }
309 }
310 > } errors.ts
311 >
312 > export class ExpectedError extends Error {
313 readonly isExpected = true;
314 > } errors.ts
315 >
316 > /**
317 > * Error that when thrown won't be logged in telemetry as an unhandled error.
318 > */
319 > export class ErrorNoTelemetry extends Error {
320 > override readonly name: string;
321 >
322 > constructor(msg?: string) {
323 super(msg);
324 this.name = 'CodeExpectedError';
325 }
326 > errors.ts
327 > public static fromError(err: Error): ErrorNoTelemetry {
328 if (err instanceof ErrorNoTelemetry) {
329 return err;
335 return result;
336 }
337 > errors.ts
338 > public static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry {
339 return err.name === 'CodeExpectedError';
340 }
341 > } errors.ts
342 >
343 > /**
344 > * This error indicates a bug.
345 > * Do not throw this for invalid user input.
346 > * Only catch this error to recover gracefully from bugs.
347 > */
348 > export class BugIndicatingError extends Error {
349 > constructor(message?: string) {
350 super(message || 'An unexpected bug occurred.');
351 Object.setPrototypeOf(this, BugIndicatingError.prototype);
src/vs/base/common/path.ts 177 covered LOC · 31 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- path.ts
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 > // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace
7 > // Copied from: https://github.com/nodejs/node/commits/v22.15.0/lib/path.js
8 > // Excluding: the change that adds primordials
9 > // (https://github.com/nodejs/node/commit/187a862d221dec42fa9a5c4214e7034d9092792f and others)
10 > // Excluding: the change that adds glob matching
11 > // (https://github.com/nodejs/node/commit/57b8b8e18e5e2007114c63b71bf0baedc01936a6)
12 >
13 > /**
14 > * Copyright Joyent, Inc. and other Node contributors.
15 > *
16 > * Permission is hereby granted, free of charge, to any person obtaining a
17 > * copy of this software and associated documentation files (the
18 > * "Software"), to deal in the Software without restriction, including
19 > * without limitation the rights to use, copy, modify, merge, publish,
20 > * distribute, sublicense, and/or sell copies of the Software, and to permit
21 > * persons to whom the Software is furnished to do so, subject to the
22 > * following conditions:
23 > *
24 > * The above copyright notice and this permission notice shall be included
25 > * in all copies or substantial portions of the Software.
26 > *
27 > * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28 > * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 > * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
30 > * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
31 > * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
32 > * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
33 > * USE OR OTHER DEALINGS IN THE SOFTWARE.
34 > */
35 >
36 > import * as process from './process.js';
37 >
38 > const CHAR_UPPERCASE_A = 65;/* A */
39 > const CHAR_LOWERCASE_A = 97; /* a */
40 > const CHAR_UPPERCASE_Z = 90; /* Z */
41 > const CHAR_LOWERCASE_Z = 122; /* z */
42 > const CHAR_DOT = 46; /* . */
43 > const CHAR_FORWARD_SLASH = 47; /* / */
44 > const CHAR_BACKWARD_SLASH = 92; /* \ */
45 > const CHAR_COLON = 58; /* : */
46 > const CHAR_QUESTION_MARK = 63; /* ? */
47 >
48 > class ErrorInvalidArgType extends Error {
49 > code: 'ERR_INVALID_ARG_TYPE';
50 > constructor(name: string, expected: string, actual: unknown) {
51 // determiner: 'must be' or 'must not be'
52 let determiner;
66 this.code = 'ERR_INVALID_ARG_TYPE';
67 }
68 > } path.ts
69 >
70 function validateObject(pathObject: object, name: string) {
71 if (pathObject === null || typeof pathObject !== 'object') {
73 }
74 }
75 > path.ts
76 function validateString(value: string, name: string) {
77 if (typeof value !== 'string') {
79 }
80 }
81 > path.ts
82 > const platformIsWin32 = (process.platform === 'win32');
83 >
84 function isPathSeparator(code: number | undefined) {
85 return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
86 }
87 > path.ts
88 function isPosixPathSeparator(code: number | undefined) {
89 return code === CHAR_FORWARD_SLASH;
90 }
91 > path.ts
92 function isWindowsDeviceRoot(code: number) {
93 return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||
94 (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z);
95 }
96 > path.ts
97 > // Resolves . and .. elements in a path with directory names
98 function normalizeString(path: string, allowAboveRoot: boolean, separator: string, isPathSeparator: (code?: number) => boolean) {
99 let res = '';
163 return res;
164 }
165 > path.ts
166 function formatExt(ext: string): string {
167 return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : '';
168 }
169 > path.ts
170 function _format(sep: string, pathObject: ParsedPath) {
171 validateObject(pathObject, 'pathObject');
178 return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`;
179 }
180 > path.ts
181 > export interface ParsedPath {
182 > root: string;
183 > dir: string;
184 > base: string;
185 > ext: string;
186 > name: string;
187 > }
188 >
189 > export interface IPath {
190 > normalize(path: string): string;
191 > isAbsolute(path: string): boolean;
192 > join(...paths: string[]): string;
193 > resolve(...pathSegments: string[]): string;
194 > relative(from: string, to: string): string;
195 > dirname(path: string): string;
196 > basename(path: string, suffix?: string): string;
197 > extname(path: string): string;
198 > format(pathObject: ParsedPath): string;
199 > parse(path: string): ParsedPath;
200 > toNamespacedPath(path: string): string;
201 > sep: '\\' | '/';
202 > delimiter: string;
203 > win32: IPath | null;
204 > posix: IPath | null;
205 > }
206 >
207 > export const win32: IPath = {
208 > // path.resolve([from ...], to)
209 > resolve(...pathSegments: string[]): string {
210 let resolvedDevice = '';
211 let resolvedTail = '';
343 `${resolvedDevice}${resolvedTail}` || '.';
344 },
345 > path.ts
346 > normalize(path: string): string {
347 validateString(path, 'path');
348 const len = path.length;
450 return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`;
451 },
452 > path.ts
453 > isAbsolute(path: string): boolean {
454 validateString(path, 'path');
455 const len = path.length;
466 isPathSeparator(path.charCodeAt(2)));
467 },
468 > path.ts
469 > join(...paths: string[]): string {
470 if (paths.length === 0) {
471 return '.';
536 return win32.normalize(joined);
537 },
538 > path.ts
539 >
540 > // It will solve the relative path from `from` to `to`, for instance:
541 > // from = 'C:\\orandea\\test\\aaa'
542 > // to = 'C:\\orandea\\impl\\bbb'
543 > // The output of the function should be: '..\\..\\impl\\bbb'
544 > relative(from: string, to: string): string {
545 validateString(from, 'from');
546 validateString(to, 'to');
699 return toOrig.slice(toStart, toEnd);
700 },
701 > path.ts
702 > toNamespacedPath(path: string): string {
703 // Note: this will *probably* throw somewhere.
704 if (typeof path !== 'string' || path.length === 0) {
730 return resolvedPath;
731 },
732 > path.ts
733 > dirname(path: string): string {
734 validateString(path, 'path');
735 const len = path.length;
818 return path.slice(0, end);
819 },
820 > path.ts
821 > basename(path: string, suffix?: string): string {
822 if (suffix !== undefined) {
823 validateString(suffix, 'suffix');
906 return path.slice(start, end);
907 },
908 > path.ts
909 > extname(path: string): string {
910 validateString(path, 'path');
911 let start = 0;
972 return path.slice(startDot, end);
973 },
974 > path.ts
975 > format: _format.bind(null, '\\'),
976 >
977 > parse(path) {
978 validateString(path, 'path');
979
1126 return ret;
1127 },
1128 > path.ts
1129 > sep: '\\',
1130 > delimiter: ';',
1131 > win32: null,
1132 > posix: null
1133 > };
1134 >
1135 > const posixCwd = (() => {
1136 > if (platformIsWin32) {
1137 // Converts Windows' backslash path separators to POSIX forward slashes
1138 // and truncates any drive indicator
1143 };
1144 }
1145 > path.ts
1146 > // We're already on POSIX, no need for any transformations
1147 > return () => process.cwd();
1148 > })();
1149 >
1150 > export const posix: IPath = {
1151 > // path.resolve([from ...], to)
1152 > resolve(...pathSegments: string[]): string {
1153 let resolvedPath = '';
1154 let resolvedAbsolute = false;
1186 return resolvedPath.length > 0 ? resolvedPath : '.';
1187 },
1188 > path.ts
1189 > normalize(path: string): string {
1190 validateString(path, 'path');
1191
1213 return isAbsolute ? `/${path}` : path;
1214 },
1215 > path.ts
1216 > isAbsolute(path: string): boolean {
1217 validateString(path, 'path');
1218 return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1219 },
1220 > path.ts
1221 > join(...paths: string[]): string {
1222 if (paths.length === 0) {
1223 return '.';
1239 return posix.normalize(path.join('/'));
1240 },
1241 > path.ts
1242 > relative(from: string, to: string): string {
1243 validateString(from, 'from');
1244 validateString(to, 'to');
1312 return `${out}${to.slice(toStart + lastCommonSep)}`;
1313 },
1314 > path.ts
1315 > toNamespacedPath(path: string): string {
1316 // Non-op on posix systems
1317 return path;
1318 },
1319 > path.ts
1320 > dirname(path: string): string {
1321 validateString(path, 'path');
1322 if (path.length === 0) {
1346 return path.slice(0, end);
1347 },
1348 > path.ts
1349 > basename(path: string, suffix?: string): string {
1350 if (suffix !== undefined) {
1351 validateString(suffix, 'suffix');
1426 return path.slice(start, end);
1427 },
1428 > path.ts
1429 > extname(path: string): string {
1430 validateString(path, 'path');
1431 let startDot = -1;
1480 return path.slice(startDot, end);
1481 },
1482 > path.ts
1483 > format: _format.bind(null, '/'),
1484 >
1485 > parse(path: string): ParsedPath {
1486 validateString(path, 'path');
1487
1565 return ret;
1566 },
1567 > path.ts
1568 > sep: '/',
1569 > delimiter: ':',
1570 > win32: null,
1571 > posix: null
1572 > };
1573 >
1574 > posix.win32 = win32.win32 = win32;
1575 > posix.posix = win32.posix = posix;
1576 >
1577 > export const normalize = (platformIsWin32 ? win32.normalize : posix.normalize);
1578 > export const isAbsolute = (platformIsWin32 ? win32.isAbsolute : posix.isAbsolute);
1579 > export const join = (platformIsWin32 ? win32.join : posix.join);
1580 > export const resolve = (platformIsWin32 ? win32.resolve : posix.resolve);
1581 > export const relative = (platformIsWin32 ? win32.relative : posix.relative);
1582 > export const dirname = (platformIsWin32 ? win32.dirname : posix.dirname);
1583 > export const basename = (platformIsWin32 ? win32.basename : posix.basename);
1584 > export const extname = (platformIsWin32 ? win32.extname : posix.extname);
1585 > export const format = (platformIsWin32 ? win32.format : posix.format);
1586 > export const parse = (platformIsWin32 ? win32.parse : posix.parse);
1587 > export const toNamespacedPath = (platformIsWin32 ? win32.toNamespacedPath : posix.toNamespacedPath);
1588 > export const sep = (platformIsWin32 ? win32.sep : posix.sep);
1589 > export const delimiter = (platformIsWin32 ? win32.delimiter : posix.delimiter);
src/vs/base/common/actions.ts 151 covered LOC · 25 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- actions.ts
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 { Emitter, Event } from './event.js';
7 > import { Disposable, IDisposable } from './lifecycle.js';
8 > import * as nls from '../../nls.js';
9 >
10 > export interface ITelemetryData {
11 > readonly from?: string;
12 > readonly target?: string;
13 > [key: string]: unknown;
14 > }
15 >
16 > export type WorkbenchActionExecutedClassification = {
17 > id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the action that was run.' };
18 > from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the component the action was run from.' };
19 > detail?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Optional details about how the action was run, e.g which keybinding was used.' };
20 > owner: 'isidorn';
21 > comment: 'Provides insight into actions that are executed within the workbench.';
22 > };
23 >
24 > export type WorkbenchActionExecutedEvent = {
25 > id: string;
26 > from: string;
27 > detail?: string;
28 > };
29 >
30 > export interface IAction {
31 > readonly id: string;
32 > label: string;
33 > tooltip: string;
34 > class: string | undefined;
35 > enabled: boolean;
36 > checked?: boolean;
37 > run(...args: unknown[]): unknown;
38 > }
39 >
40 > export interface IActionRunner extends IDisposable {
41 > readonly onDidRun: Event<IRunEvent>;
42 > readonly onWillRun: Event<IRunEvent>;
43 >
44 > run(action: IAction, context?: unknown): unknown;
45 > }
46 >
47 > export interface IActionChangeEvent {
48 > readonly label?: string;
49 > readonly tooltip?: string;
50 > readonly class?: string;
51 > readonly enabled?: boolean;
52 > readonly checked?: boolean;
53 > }
54 >
55 > /**
56 > * A concrete implementation of {@link IAction}.
57 > *
58 > * Note that in most cases you should use the lighter-weight {@linkcode toAction} function instead.
59 > */
60 > export class Action extends Disposable implements IAction {
61 >
62 > protected _onDidChange = this._register(new Emitter<IActionChangeEvent>());
63 > get onDidChange() { return this._onDidChange.event; }
64 >
65 > protected readonly _id: string;
66 > protected _label: string;
67 > protected _tooltip: string | undefined;
68 > protected _cssClass: string | undefined;
69 > protected _enabled: boolean = true;
70 > protected _checked?: boolean;
71 > protected readonly _actionCallback?: (event?: unknown) => unknown;
72 >
73 > constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: unknown) => unknown) {
74 super();
75 this._id = id;
79 this._actionCallback = actionCallback;
80 }
81 > actions.ts
82 > get id(): string {
83 return this._id;
84 }
85 > actions.ts
86 > get label(): string {
87 return this._label;
88 }
89 > actions.ts
90 > set label(value: string) {
91 this._setLabel(value);
92 }
93 > actions.ts
94 > private _setLabel(value: string): void {
95 if (this._label !== value) {
96 this._label = value;
98 }
99 }
100 > actions.ts
101 > get tooltip(): string {
102 return this._tooltip || '';
103 }
104 > actions.ts
105 > set tooltip(value: string) {
106 this._setTooltip(value);
107 }
108 > actions.ts
109 > protected _setTooltip(value: string): void {
110 if (this._tooltip !== value) {
111 this._tooltip = value;
113 }
114 }
115 > actions.ts
116 > get class(): string | undefined {
117 return this._cssClass;
118 }
119 > actions.ts
120 > set class(value: string | undefined) {
121 this._setClass(value);
122 }
123 > actions.ts
124 > protected _setClass(value: string | undefined): void {
125 if (this._cssClass !== value) {
126 this._cssClass = value;
128 }
129 }
130 > actions.ts
131 > get enabled(): boolean {
132 return this._enabled;
133 }
134 > actions.ts
135 > set enabled(value: boolean) {
136 this._setEnabled(value);
137 }
138 > actions.ts
139 > protected _setEnabled(value: boolean): void {
140 if (this._enabled !== value) {
141 this._enabled = value;
143 }
144 }
145 > actions.ts
146 > get checked(): boolean | undefined {
147 return this._checked;
148 }
149 > actions.ts
150 > set checked(value: boolean | undefined) {
151 this._setChecked(value);
152 }
153 > actions.ts
154 > protected _setChecked(value: boolean | undefined): void {
155 if (this._checked !== value) {
156 this._checked = value;
158 }
159 }
160 > actions.ts
161 > async run(event?: unknown, data?: ITelemetryData): Promise<void> {
162 if (this._actionCallback) {
163 await this._actionCallback(event);
164 }
165 }
166 > } actions.ts
167 >
168 > export interface IRunEvent {
169 > readonly action: IAction;
170 > readonly error?: Error;
171 > }
172 >
173 > export class ActionRunner extends Disposable implements IActionRunner {
174
175 private readonly _onWillRun = this._register(new Emitter<IRunEvent>());
177
178 private readonly _onDidRun = this._register(new Emitter<IRunEvent>());
179 > get onDidRun() { return this._onDidRun.event; } actions.ts
180 >
181 > async run(action: IAction, context?: unknown): Promise<void> {
182 if (!action.enabled) {
183 return;
195 this._onDidRun.fire({ action, error });
196 }
197 > actions.ts
198 > protected async runAction(action: IAction, context?: unknown): Promise<void> {
199 await action.run(context);
200 }
201 > } actions.ts
202 >
203 > export class Separator implements IAction {
204
205 /**
248 readonly enabled: boolean = false;
249 readonly checked: undefined = undefined;
250 > async run() { } actions.ts
251 > }
252 >
253 > export class SubmenuAction implements IAction {
254 >
255 > readonly id: string;
256 > readonly label: string;
257 > readonly class: string | undefined;
258 > readonly tooltip: string = '';
259 > readonly enabled: boolean = true;
260 > readonly checked: undefined = undefined;
261 >
262 > private readonly _actions: readonly IAction[];
263 > get actions(): readonly IAction[] { return this._actions; }
264 >
265 > constructor(id: string, label: string, actions: readonly IAction[], cssClass?: string) {
266 this.id = id;
267 this.label = label;
269 this._actions = actions;
270 }
271 > actions.ts
272 > async run(): Promise<void> { }
273 > }
274 >
275 > export class EmptySubmenuAction extends Action {
276 >
277 > static readonly ID = 'vs.actions.empty';
278 >
279 > constructor() {
280 super(EmptySubmenuAction.ID, nls.localize('submenu.empty', '(empty)'), undefined, false);
281 }
282 > } actions.ts
283 >
284 > export function toAction(props: { id: string; label: string; tooltip?: string; enabled?: boolean; checked?: boolean; class?: string; run: Function }): IAction {
285 return {
286 id: props.id,
src/vs/workbench/services/preferences/common/preferencesValidation.ts 146 covered LOC · 26 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- preferencesValidation.ts
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 * as nls from '../../../../nls.js';
7 > import { JSONSchemaType } from '../../../../base/common/jsonSchema.js';
8 > import { Color } from '../../../../base/common/color.js';
9 > import { isObject, isUndefinedOrNull, isString, isStringArray } from '../../../../base/common/types.js';
10 > import { IConfigurationPropertySchema } from '../../../../platform/configuration/common/configurationRegistry.js';
11 >
12 > type Validator<T> = { enabled: boolean; isValid: (value: T) => boolean; message: string };
13 >
14 > function canBeType(propTypes: (string | undefined)[], ...types: JSONSchemaType[]): boolean { preferencesValidation.ts
15 > return types.some(t => propTypes.includes(t));
16 > }
18 function isNullOrEmpty(value: unknown): boolean {
19 return value === '' || isUndefinedOrNull(value);
20 }
22 > export function createValidator(prop: IConfigurationPropertySchema): (value: any) => (string | null) {
23 > const type: (string | undefined)[] = Array.isArray(prop.type) ? prop.type : [prop.type]; preferencesValidation.ts
24 > const isNullable = canBeType(type, 'null');
25 > const isNumeric = (canBeType(type, 'number') || canBeType(type, 'integer')) && (type.length === 1 || type.length === 2 && isNullable);
26 >
27 > const numericValidations = getNumericValidators(prop);
28 > const stringValidations = getStringValidators(prop);
29 > const arrayValidator = getArrayValidator(prop);
30 > const objectValidator = getObjectValidator(prop);
31 >
32 > return value => {
33 if (isNullable && isNullOrEmpty(value)) { return ''; }
34
76 return '';
77 };
80 > /**
81 > * Returns an error string if the value is invalid and can't be displayed in the settings UI for the given type.
82 > */
83 > export function getInvalidTypeError(value: any, type: undefined | string | string[]): string | undefined {
84 if (typeof type === 'undefined') {
85 return;
93 return;
94 }
96 function valueValidatesAsType(value: any, type: string): boolean {
97 const valueType = typeof value;
131 }
132 }
134 > function getStringValidators(prop: IConfigurationPropertySchema) { preferencesValidation.ts
135 > const uriRegex = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
136 > let patternRegex: RegExp | undefined;
137 > if (typeof prop.pattern === 'string') {
138 patternRegex = toRegExp(prop.pattern);
139 }
141 > return [
142 > {
143 > enabled: prop.maxLength !== undefined,
144 > isValid: ((value: { length: number }) => value.length <= prop.maxLength!),
145 > message: nls.localize('validations.maxLength', "Value must be {0} or fewer characters long.", prop.maxLength)
146 > },
147 > {
148 > enabled: prop.minLength !== undefined,
149 > isValid: ((value: { length: number }) => value.length >= prop.minLength!),
150 > message: nls.localize('validations.minLength', "Value must be {0} or more characters long.", prop.minLength)
151 > },
152 > {
153 > enabled: patternRegex !== undefined,
154 > isValid: ((value: string) => patternRegex!.test(value)),
155 > message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern)
156 > },
157 > {
158 > enabled: prop.format === 'color-hex',
159 > isValid: ((value: string) => Color.Format.CSS.parseHex(value)),
160 > message: nls.localize('validations.colorFormat', "Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.")
161 > },
162 > {
163 > enabled: prop.format === 'uri' || prop.format === 'uri-reference',
164 > isValid: ((value: string) => !!value.length),
165 > message: nls.localize('validations.uriEmpty', "URI expected.")
166 > },
167 > {
168 > enabled: prop.format === 'uri' || prop.format === 'uri-reference',
169 > isValid: ((value: string) => uriRegex.test(value)),
170 > message: nls.localize('validations.uriMissing', "URI is expected.")
171 > },
172 > {
173 > enabled: prop.format === 'uri',
174 > isValid: ((value: string) => {
175 const matches = value.match(uriRegex);
176 return !!(matches && matches[2]);
177 }),
178 > message: nls.localize('validations.uriSchemeMissing', "URI with a scheme is expected.") preferencesValidation.ts
179 > },
180 > {
181 > enabled: prop.enum !== undefined,
182 > isValid: ((value: string) => {
183 return prop.enum!.includes(value);
184 }),
185 > message: nls.localize('validations.invalidStringEnumValue', "Value is not accepted. Valid values: {0}.", preferencesValidation.ts
186 > prop.enum ? prop.enum.map(key => `"${key}"`).join(', ') : '[]')
187 > }
188 > ].filter(validation => validation.enabled);
189 > }
191 > function getNumericValidators(prop: IConfigurationPropertySchema): Validator<number>[] { preferencesValidation.ts
192 > const type: (string | undefined)[] = Array.isArray(prop.type) ? prop.type : [prop.type];
193 >
194 > const isNullable = canBeType(type, 'null');
195 > const isIntegral = (canBeType(type, 'integer')) && (type.length === 1 || type.length === 2 && isNullable);
196 > const isNumeric = canBeType(type, 'number', 'integer') && (type.length === 1 || type.length === 2 && isNullable);
197 > if (!isNumeric) {
198 > return []; preferencesValidation.ts
199 > }
200
201 let exclusiveMax: number | undefined;
217 {
218 enabled: exclusiveMax !== undefined && (prop.maximum === undefined || exclusiveMax <= prop.maximum),
219 > isValid: ((value: number) => value < exclusiveMax!), preferencesValidation.ts
220 > message: nls.localize('validations.exclusiveMax', "Value must be strictly less than {0}.", exclusiveMax)
221 > },
222 > {
223 > enabled: exclusiveMin !== undefined && (prop.minimum === undefined || exclusiveMin >= prop.minimum),
224 > isValid: ((value: number) => value > exclusiveMin!),
225 > message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin)
226 > },
227 > {
228 > enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum),
229 > isValid: ((value: number) => value <= prop.maximum!),
230 > message: nls.localize('validations.max', "Value must be less than or equal to {0}.", prop.maximum)
231 > },
232 > {
233 > enabled: prop.minimum !== undefined && (exclusiveMin === undefined || exclusiveMin < prop.minimum),
234 > isValid: ((value: number) => value >= prop.minimum!),
235 > message: nls.localize('validations.min', "Value must be greater than or equal to {0}.", prop.minimum)
236 > },
237 > {
238 > enabled: prop.multipleOf !== undefined,
239 > isValid: ((value: number) => value % prop.multipleOf! === 0),
240 > message: nls.localize('validations.multipleOf', "Value must be a multiple of {0}.", prop.multipleOf)
241 > },
242 > {
243 > enabled: isIntegral,
244 > isValid: ((value: number) => value % 1 === 0),
245 > message: nls.localize('validations.expectedInteger', "Value must be an integer.")
246 > },
247 > ].filter(validation => validation.enabled);
248 > }
250 > function getArrayValidator(prop: IConfigurationPropertySchema): ((value: any) => (string | null)) | null { preferencesValidation.ts
251 > if (prop.type === 'array' && prop.items && !Array.isArray(prop.items)) {
252 const propItems = prop.items;
253 if (propItems && !Array.isArray(propItems.type)) {
334 }
335 }
337 > return null;
338 > }
340 > function getObjectValidator(prop: IConfigurationPropertySchema): ((value: any) => (string | null)) | null { preferencesValidation.ts
341 > if (prop.type === 'object') {
342 const { properties, patternProperties, additionalProperties, propertyNames } = prop;
343 return value => {
404 };
405 }
407 > return null;
408 > }
410 > /**
411 > * Validates a single property name against the propertyNames.pattern schema.
412 > * Returns true if the key is valid, false otherwise.
413 > */
414 > export function validatePropertyName(propertyNames: IConfigurationPropertySchema['propertyNames'], key: string): boolean {
415 if (!propertyNames?.pattern) {
416 return true;
419 return patternRegex.test(key);
420 }
422 function getErrorsForSchema(propertySchema: IConfigurationPropertySchema, data: any): string | null {
423 const validator = createValidator(propertySchema);
src/vs/editor/common/core/selection.ts 141 covered LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- selection.ts
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 { IPosition, Position } from './position.js';
7 > import { Range } from './range.js';
8 >
9 > /**
10 > * A selection in the editor.
11 > * The selection is a range that has an orientation.
12 > */
13 > export interface ISelection {
14 > /**
15 > * The line number on which the selection has started.
16 > */
17 > readonly selectionStartLineNumber: number;
18 > /**
19 > * The column on `selectionStartLineNumber` where the selection has started.
20 > */
21 > readonly selectionStartColumn: number;
22 > /**
23 > * The line number on which the selection has ended.
24 > */
25 > readonly positionLineNumber: number;
26 > /**
27 > * The column on `positionLineNumber` where the selection has ended.
28 > */
29 > readonly positionColumn: number;
30 > }
31 >
32 > /**
33 > * The direction of a selection.
34 > */
35 > export const enum SelectionDirection {
36 > /**
37 > * The selection starts above where it ends.
38 > */
39 > LTR,
40 > /**
41 > * The selection starts below where it ends.
42 > */
43 > RTL
44 > }
45 >
46 > /**
47 > * A selection in the editor.
48 > * The selection is a range that has an orientation.
49 > */
50 > export class Selection extends Range {
51 > /**
52 > * The line number on which the selection has started.
53 > */
54 > public readonly selectionStartLineNumber: number;
55 > /**
56 > * The column on `selectionStartLineNumber` where the selection has started.
57 > */
58 > public readonly selectionStartColumn: number;
59 > /**
60 > * The line number on which the selection has ended.
61 > */
62 > public readonly positionLineNumber: number;
63 > /**
64 > * The column on `positionLineNumber` where the selection has ended.
65 > */
66 > public readonly positionColumn: number;
67 >
68 > constructor(selectionStartLineNumber: number, selectionStartColumn: number, positionLineNumber: number, positionColumn: number) {
69 super(selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn);
70 this.selectionStartLineNumber = selectionStartLineNumber;
73 this.positionColumn = positionColumn;
74 }
76 > /**
77 > * Transform to a human-readable representation.
78 > */
79 > public override toString(): string {
80 return '[' + this.selectionStartLineNumber + ',' + this.selectionStartColumn + ' -> ' + this.positionLineNumber + ',' + this.positionColumn + ']';
81 }
83 > /**
84 > * Test if equals other selection.
85 > */
86 > public equalsSelection(other: ISelection): boolean {
87 return (
88 Selection.selectionsEqual(this, other)
89 );
90 }
92 > /**
93 > * Test if the two selections are equal.
94 > */
95 > public static selectionsEqual(a: ISelection, b: ISelection): boolean {
96 return (
97 a.selectionStartLineNumber === b.selectionStartLineNumber &&
101 );
102 }
103 > selection.ts
104 > /**
105 > * Get directions (LTR or RTL).
106 > */
107 > public getDirection(): SelectionDirection {
108 if (this.selectionStartLineNumber === this.startLineNumber && this.selectionStartColumn === this.startColumn) {
109 return SelectionDirection.LTR;
111 return SelectionDirection.RTL;
112 }
113 > selection.ts
114 > /**
115 > * Create a new selection with a different `positionLineNumber` and `positionColumn`.
116 > */
117 > public override setEndPosition(endLineNumber: number, endColumn: number): Selection {
118 if (this.getDirection() === SelectionDirection.LTR) {
119 return new Selection(this.startLineNumber, this.startColumn, endLineNumber, endColumn);
121 return new Selection(endLineNumber, endColumn, this.startLineNumber, this.startColumn);
122 }
123 > selection.ts
124 > /**
125 > * Get the position at `positionLineNumber` and `positionColumn`.
126 > */
127 > public getPosition(): Position {
128 return new Position(this.positionLineNumber, this.positionColumn);
129 }
130 > selection.ts
131 > /**
132 > * Get the position at the start of the selection.
133 > */
134 > public getSelectionStart(): Position {
135 return new Position(this.selectionStartLineNumber, this.selectionStartColumn);
136 }
137 > selection.ts
138 > /**
139 > * Create a new selection with a different `selectionStartLineNumber` and `selectionStartColumn`.
140 > */
141 > public override setStartPosition(startLineNumber: number, startColumn: number): Selection {
142 if (this.getDirection() === SelectionDirection.LTR) {
143 return new Selection(startLineNumber, startColumn, this.endLineNumber, this.endColumn);
145 return new Selection(this.endLineNumber, this.endColumn, startLineNumber, startColumn);
146 }
147 > selection.ts
148 > // ----
149 >
150 > /**
151 > * Create a `Selection` from one or two positions
152 > */
153 > public static override fromPositions(start: IPosition, end: IPosition = start): Selection {
154 return new Selection(start.lineNumber, start.column, end.lineNumber, end.column);
155 }
156 > selection.ts
157 > /**
158 > * Creates a `Selection` from a range, given a direction.
159 > */
160 > public static fromRange(range: Range, direction: SelectionDirection): Selection {
161 if (direction === SelectionDirection.LTR) {
162 return new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
165 }
166 }
167 > selection.ts
168 > /**
169 > * Create a `Selection` from an `ISelection`.
170 > */
171 > public static liftSelection(sel: ISelection): Selection {
172 return new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn);
173 }
174 > selection.ts
175 > /**
176 > * `a` equals `b`.
177 > */
178 > public static selectionsArrEqual(a: ISelection[], b: ISelection[]): boolean {
179 if (a && !b || !a && b) {
180 return false;
193 return true;
194 }
195 > selection.ts
196 > /**
197 > * Test if `obj` is an `ISelection`.
198 > */
199 > public static isISelection(obj: unknown): obj is ISelection {
200 return (
201 !!obj
206 );
207 }
208 > selection.ts
209 > /**
210 > * Create with a direction.
211 > */
212 > public static createWithDirection(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, direction: SelectionDirection): Selection {
213
214 if (direction === SelectionDirection.LTR) {
218 return new Selection(endLineNumber, endColumn, startLineNumber, startColumn);
219 }
220 > } selection.ts
src/vs/platform/instantiation/common/instantiation.ts 123 covered LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- instantiation.ts
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 } from '../../../base/common/lifecycle.js';
7 > import * as descriptors from './descriptors.js';
8 > import { ServiceCollection } from './serviceCollection.js';
9 >
10 > // ------ internal util
11 >
12 > export namespace _util {
13 >
14 > export const serviceIds = new Map<string, ServiceIdentifier<any>>();
15 >
16 > export const DI_TARGET = '$di$target';
17 > export const DI_DEPENDENCIES = '$di$dependencies';
18 >
19 > export function getServiceDependencies(ctor: DI_TARGET_OBJ): { id: ServiceIdentifier<any>; index: number }[] {
20 return ctor[DI_DEPENDENCIES] || [];
21 }
23 > export interface DI_TARGET_OBJ extends Function {
24 > [DI_TARGET]: Function;
25 > [DI_DEPENDENCIES]: { id: ServiceIdentifier<any>; index: number }[];
26 > }
27 > }
28 >
29 > // --- interfaces ------
30 >
31 > export type BrandedService = { _serviceBrand: undefined };
32 >
33 > export interface IConstructorSignature<T, Args extends any[] = []> {
34 > new <Services extends BrandedService[]>(...args: [...Args, ...Services]): T;
35 > }
36 >
37 > export interface ServicesAccessor {
38 > get<T>(id: ServiceIdentifier<T>): T;
39 > }
40 >
41 > export const IInstantiationService = createDecorator<IInstantiationService>('instantiationService');
42 >
43 > /**
44 > * Given a list of arguments as a tuple, attempt to extract the leading, non-service arguments
45 > * to their own tuple.
46 > */
47 > export type GetLeadingNonServiceArgs<TArgs extends any[]> =
48 > TArgs extends [] ? []
49 > : TArgs extends [...infer TFirst, BrandedService] ? GetLeadingNonServiceArgs<TFirst>
50 > : TArgs;
51 >
52 > export interface IInstantiationService {
53 >
54 > readonly _serviceBrand: undefined;
55 >
56 > /**
57 > * Synchronously creates an instance that is denoted by the descriptor
58 > */
59 > createInstance<T>(descriptor: descriptors.SyncDescriptor0<T>): T;
60 > createInstance<Ctor extends new (...args: any[]) => unknown, R extends InstanceType<Ctor>>(ctor: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
61 >
62 > /**
63 > * Calls a function with a service accessor.
64 > */
65 > invokeFunction<R, TS extends any[] = []>(fn: (accessor: ServicesAccessor, ...args: TS) => R, ...args: TS): R;
66 >
67 > /**
68 > * Creates a child of this service which inherits all current services
69 > * and adds/overwrites the given services.
70 > *
71 > * NOTE that the returned child is `disposable` and should be disposed when not used
72 > * anymore. This will also dispose all the services that this service has created.
73 > */
74 > createChild(services: ServiceCollection, store?: DisposableStore): IInstantiationService;
75 >
76 > /**
77 > * Disposes this instantiation service.
78 > *
79 > * - Will dispose all services that this instantiation service has created.
80 > * - Will dispose all its children but not its parent.
81 > * - Will NOT dispose services-instances that this service has been created with
82 > * - Will NOT dispose consumer-instances this service has created
83 > */
84 > dispose(): void;
85 > }
86 >
87 >
88 > /**
89 > * Identifies a service of type `T`.
90 > */
91 > export interface ServiceIdentifier<T> {
92 > (...args: any[]): void;
93 > type: T;
94 > }
95 >
96 >
97 > function storeServiceDependency(id: ServiceIdentifier<unknown>, target: Function, index: number): void { instantiation.ts
98 > if ((target as _util.DI_TARGET_OBJ)[_util.DI_TARGET] === target) {
99 (target as _util.DI_TARGET_OBJ)[_util.DI_DEPENDENCIES].push({ id, index });
100 > } else { instantiation.ts
101 > (target as _util.DI_TARGET_OBJ)[_util.DI_DEPENDENCIES] = [{ id, index }];
102 > (target as _util.DI_TARGET_OBJ)[_util.DI_TARGET] = target;
103 > }
104 > }
106 > /**
107 > * The *only* valid way to create a {{ServiceIdentifier}}.
108 > */
109 > export function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {
110 >
111 > if (_util.serviceIds.has(serviceId)) {
112 return _util.serviceIds.get(serviceId)!;
113 }
115 > const id = function (target: Function, key: string, index: number) {
116 > if (arguments.length !== 3) { instantiation.ts
117 throw new Error('@IServiceName-decorator can only be used to decorate a parameter');
118 }
119 > storeServiceDependency(id, target, index); instantiation.ts
120 > } as ServiceIdentifier<T>;
122 > id.toString = () => serviceId;
123 >
124 > _util.serviceIds.set(serviceId, id);
125 > return id;
126 > }
127 >
128 > export function refineServiceDecorator<T1, T extends T1>(serviceIdentifier: ServiceIdentifier<T1>): ServiceIdentifier<T> {
129 > return <ServiceIdentifier<T>>serviceIdentifier; instantiation.ts
130 > }
src/vs/platform/keybinding/common/keybinding.ts 123 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- keybinding.ts
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 { Event } from '../../../base/common/event.js';
7 > import { IJSONSchema } from '../../../base/common/jsonSchema.js';
8 > import { KeyCode } from '../../../base/common/keyCodes.js';
9 > import { ResolvedKeybinding, Keybinding } from '../../../base/common/keybindings.js';
10 > import { IDisposable } from '../../../base/common/lifecycle.js';
11 > import { IContextKeyService, IContextKeyServiceTarget } from '../../contextkey/common/contextkey.js';
12 > import { createDecorator } from '../../instantiation/common/instantiation.js';
13 > import { ResolutionResult } from './keybindingResolver.js';
14 > import { ResolvedKeybindingItem } from './resolvedKeybindingItem.js';
15 >
16 > export interface IUserFriendlyKeybinding {
17 > key: string;
18 > command: string;
19 > args?: any;
20 > when?: string;
21 > /**
22 > * When `true`, the keybinding is registered as a system-wide (OS global) shortcut that fires
23 > * even when VS Code does not have focus. Desktop only; ignored on web/server. Only honored for
24 > * user `keybindings.json` entries (not extension-contributed keybindings).
25 > */
26 > systemWide?: boolean;
27 > }
28 >
29 > export interface IKeyboardEvent {
30 > readonly _standardKeyboardEventBrand: true;
31 >
32 > readonly ctrlKey: boolean;
33 > readonly shiftKey: boolean;
34 > readonly altKey: boolean;
35 > readonly metaKey: boolean;
36 > readonly altGraphKey: boolean;
37 > readonly keyCode: KeyCode;
38 > readonly code: string;
39 > }
40 >
41 > export interface KeybindingsSchemaContribution {
42 > readonly onDidChange?: Event<void>;
43 >
44 > getSchemaAdditions(): IJSONSchema[];
45 > }
46 >
47 > export const IKeybindingService = createDecorator<IKeybindingService>('keybindingService');
48 >
49 > export interface IKeybindingService {
50 > readonly _serviceBrand: undefined;
51 >
52 > readonly inChordMode: boolean;
53 >
54 > readonly onDidUpdateKeybindings: Event<void>;
55 >
56 > /**
57 > * Returns none, one or many (depending on keyboard layout)!
58 > */
59 > resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
60 >
61 > resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
62 >
63 > resolveUserBinding(userBinding: string): ResolvedKeybinding[];
64 >
65 > /**
66 > * Resolve and dispatch `keyboardEvent` and invoke the command.
67 > */
68 > dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean;
69 >
70 > /**
71 > * Resolve and dispatch `keyboardEvent`, but do not invoke the command or change inner state.
72 > */
73 > softDispatch(keyboardEvent: IKeyboardEvent, target: IContextKeyServiceTarget): ResolutionResult;
74 >
75 > /**
76 > * Enable hold mode for this command. This is only possible if the command is current being dispatched, meaning
77 > * we are after its keydown and before is keyup event.
78 > *
79 > * @returns A promise that resolves when hold stops, returns undefined if hold mode could not be enabled.
80 > */
81 > enableKeybindingHoldMode(commandId: string): Promise<void> | undefined;
82 >
83 > dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void;
84 >
85 > /**
86 > * Look up keybindings for a command.
87 > * Use `lookupKeybinding` if you are interested in the preferred keybinding.
88 > */
89 > lookupKeybindings(commandId: string): ResolvedKeybinding[];
90 >
91 > /**
92 > * Look up the preferred (last defined) keybinding for a command.
93 > * @returns The preferred keybinding or null if the command is not bound.
94 > */
95 > lookupKeybinding(commandId: string, context?: IContextKeyService, enforceContextCheck?: boolean): ResolvedKeybinding | undefined;
96 >
97 > getDefaultKeybindingsContent(): string;
98 >
99 > getDefaultKeybindings(): readonly ResolvedKeybindingItem[];
100 >
101 > getKeybindings(): readonly ResolvedKeybindingItem[];
102 >
103 > customKeybindingsCount(): number;
104 >
105 > /**
106 > * Will the given key event produce a character that's rendered on screen, e.g. in a
107 > * text box. *Note* that the results of this function can be incorrect.
108 > */
109 > mightProducePrintableCharacter(event: IKeyboardEvent): boolean;
110 >
111 > registerSchemaContribution(contribution: KeybindingsSchemaContribution): IDisposable;
112 >
113 > toggleLogging(): boolean;
114 >
115 > /**
116 > * Given a UI element label and a command ID, appends the keybinding label if any.
117 > * If the command is defined and has a keybinding, returns `${label} (keybinding label)`, otherwise just `label`.
118 > */
119 > appendKeybinding(label: string, commandId: string | undefined | null, context?: IContextKeyService, enforceContextCheck?: boolean): string;
120 >
121 > _dumpDebugInfo(): string;
122 > _dumpDebugInfoJSON(): string;
123 > }
src/vs/editor/common/core/position.ts 113 covered LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- position.ts
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 > /**
7 > * A position in the editor. This interface is suitable for serialization.
8 > */
9 > export interface IPosition {
10 > /**
11 > * line number (starts at 1)
12 > */
13 > readonly lineNumber: number;
14 > /**
15 > * column (the first character in a line is between column 1 and column 2)
16 > */
17 > readonly column: number;
18 > }
19 >
20 > /**
21 > * A position in the editor.
22 > */
23 > export class Position {
24 > /**
25 > * line number (starts at 1)
26 > */
27 > public readonly lineNumber: number;
28 > /**
29 > * column (the first character in a line is between column 1 and column 2)
30 > */
31 > public readonly column: number;
32 >
33 > constructor(lineNumber: number, column: number) {
34 this.lineNumber = lineNumber;
35 this.column = column;
36 }
38 > /**
39 > * Create a new position from this position.
40 > *
41 > * @param newLineNumber new line number
42 > * @param newColumn new column
43 > */
44 > with(newLineNumber: number = this.lineNumber, newColumn: number = this.column): Position {
45 if (newLineNumber === this.lineNumber && newColumn === this.column) {
46 return this;
49 }
50 }
52 > /**
53 > * Derive a new position from this position.
54 > *
55 > * @param deltaLineNumber line number delta
56 > * @param deltaColumn column delta
57 > */
58 > delta(deltaLineNumber: number = 0, deltaColumn: number = 0): Position {
59 return this.with(Math.max(1, this.lineNumber + deltaLineNumber), Math.max(1, this.column + deltaColumn));
60 }
62 > /**
63 > * Test if this position equals other position
64 > */
65 > public equals(other: IPosition): boolean {
66 return Position.equals(this, other);
67 }
69 > /**
70 > * Test if position `a` equals position `b`
71 > */
72 > public static equals(a: IPosition | null, b: IPosition | null): boolean {
73 if (!a && !b) {
74 return true;
81 );
82 }
84 > /**
85 > * Test if this position is before other position.
86 > * If the two positions are equal, the result will be false.
87 > */
88 > public isBefore(other: IPosition): boolean {
89 return Position.isBefore(this, other);
90 }
92 > /**
93 > * Test if position `a` is before position `b`.
94 > * If the two positions are equal, the result will be false.
95 > */
96 > public static isBefore(a: IPosition, b: IPosition): boolean {
97 if (a.lineNumber < b.lineNumber) {
98 return true;
103 return a.column < b.column;
104 }
105 > position.ts
106 > /**
107 > * Test if this position is before other position.
108 > * If the two positions are equal, the result will be true.
109 > */
110 > public isBeforeOrEqual(other: IPosition): boolean {
111 return Position.isBeforeOrEqual(this, other);
112 }
113 > position.ts
114 > /**
115 > * Test if position `a` is before position `b`.
116 > * If the two positions are equal, the result will be true.
117 > */
118 > public static isBeforeOrEqual(a: IPosition, b: IPosition): boolean {
119 if (a.lineNumber < b.lineNumber) {
120 return true;
125 return a.column <= b.column;
126 }
127 > position.ts
128 > /**
129 > * A function that compares positions, useful for sorting
130 > */
131 > public static compare(a: IPosition, b: IPosition): number {
132 const aLineNumber = a.lineNumber | 0;
133 const bLineNumber = b.lineNumber | 0;
141 return aLineNumber - bLineNumber;
142 }
143 > position.ts
144 > /**
145 > * Clone this position.
146 > */
147 > public clone(): Position {
148 return new Position(this.lineNumber, this.column);
149 }
150 > position.ts
151 > /**
152 > * Convert to a human-readable representation.
153 > */
154 > public toString(): string {
155 return '(' + this.lineNumber + ',' + this.column + ')';
156 }
157 > position.ts
158 > // ---
159 >
160 > /**
161 > * Create a `Position` from an `IPosition`.
162 > */
163 > public static lift(pos: IPosition): Position {
164 return new Position(pos.lineNumber, pos.column);
165 }
166 > position.ts
167 > /**
168 > * Test if `obj` is an `IPosition`.
169 > */
170 > public static isIPosition(obj: unknown): obj is IPosition {
171 return (
172 !!obj
175 );
176 }
177 > position.ts
178 > public toJSON(): IPosition {
179 return {
180 lineNumber: this.lineNumber,
src/vs/workbench/services/configuration/common/configuration.ts 101 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- configuration.ts
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 { ConfigurationScope } from '../../../../platform/configuration/common/configurationRegistry.js';
7 > import { URI } from '../../../../base/common/uri.js';
8 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
9 > import { refineServiceDecorator } from '../../../../platform/instantiation/common/instantiation.js';
10 > import { Event } from '../../../../base/common/event.js';
11 > import { ResourceMap } from '../../../../base/common/map.js';
12 > import { IAnyWorkspaceIdentifier } from '../../../../platform/workspace/common/workspace.js';
13 >
14 > export const FOLDER_CONFIG_FOLDER_NAME = '.vscode';
15 > export const FOLDER_SETTINGS_NAME = 'settings';
16 > export const FOLDER_SETTINGS_PATH = `${FOLDER_CONFIG_FOLDER_NAME}/${FOLDER_SETTINGS_NAME}.json`;
17 >
18 > export const defaultSettingsSchemaId = 'vscode://schemas/settings/default';
19 > export const userSettingsSchemaId = 'vscode://schemas/settings/user';
20 > export const profileSettingsSchemaId = 'vscode://schemas/settings/profile';
21 > export const machineSettingsSchemaId = 'vscode://schemas/settings/machine';
22 > export const workspaceSettingsSchemaId = 'vscode://schemas/settings/workspace';
23 > export const folderSettingsSchemaId = 'vscode://schemas/settings/folder';
24 > export const launchSchemaId = 'vscode://schemas/launch';
25 > export const tasksSchemaId = 'vscode://schemas/tasks';
26 > export const mcpSchemaId = 'vscode://schemas/mcp';
27 >
28 > export const APPLICATION_SCOPES = [ConfigurationScope.APPLICATION, ConfigurationScope.APPLICATION_MACHINE];
29 > export const PROFILE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.LANGUAGE_OVERRIDABLE, ConfigurationScope.MACHINE_OVERRIDABLE];
30 > export const LOCAL_MACHINE_PROFILE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.LANGUAGE_OVERRIDABLE];
31 > export const LOCAL_MACHINE_SCOPES = [ConfigurationScope.APPLICATION, ...LOCAL_MACHINE_PROFILE_SCOPES];
32 > export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.APPLICATION_MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.LANGUAGE_OVERRIDABLE, ConfigurationScope.MACHINE_OVERRIDABLE];
33 > export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.LANGUAGE_OVERRIDABLE, ConfigurationScope.MACHINE_OVERRIDABLE];
34 > export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE, ConfigurationScope.LANGUAGE_OVERRIDABLE, ConfigurationScope.MACHINE_OVERRIDABLE];
35 >
36 > export const TASKS_CONFIGURATION_KEY = 'tasks';
37 > export const LAUNCH_CONFIGURATION_KEY = 'launch';
38 > export const MCP_CONFIGURATION_KEY = 'mcp';
39 >
40 > export const WORKSPACE_STANDALONE_CONFIGURATIONS = Object.create(null);
41 > WORKSPACE_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${TASKS_CONFIGURATION_KEY}.json`;
42 > WORKSPACE_STANDALONE_CONFIGURATIONS[LAUNCH_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${LAUNCH_CONFIGURATION_KEY}.json`;
43 > WORKSPACE_STANDALONE_CONFIGURATIONS[MCP_CONFIGURATION_KEY] = `${FOLDER_CONFIG_FOLDER_NAME}/${MCP_CONFIGURATION_KEY}.json`;
44 > export const USER_STANDALONE_CONFIGURATIONS = Object.create(null);
45 > USER_STANDALONE_CONFIGURATIONS[TASKS_CONFIGURATION_KEY] = `${TASKS_CONFIGURATION_KEY}.json`;
46 > USER_STANDALONE_CONFIGURATIONS[MCP_CONFIGURATION_KEY] = `${MCP_CONFIGURATION_KEY}.json`;
47 >
48 > export type ConfigurationKey = { type: 'defaults' | 'user' | 'workspaces' | 'folder'; key: string };
49 >
50 > export interface IConfigurationCache {
51 >
52 > needsCaching(resource: URI): boolean;
53 > read(key: ConfigurationKey): Promise<string>;
54 > write(key: ConfigurationKey, content: string): Promise<void>;
55 > remove(key: ConfigurationKey): Promise<void>;
56 >
57 > }
58 >
59 > export type RestrictedSettings = {
60 > default: ReadonlyArray<string>;
61 > application?: ReadonlyArray<string>;
62 > userLocal?: ReadonlyArray<string>;
63 > userRemote?: ReadonlyArray<string>;
64 > workspace?: ReadonlyArray<string>;
65 > workspaceFolder?: ResourceMap<ReadonlyArray<string>>;
66 > };
67 >
68 > export const IWorkbenchConfigurationService = refineServiceDecorator<IConfigurationService, IWorkbenchConfigurationService>(IConfigurationService);
69 > export interface IWorkbenchConfigurationService extends IConfigurationService {
70 > /**
71 > * Restricted settings defined in each configuration target
72 > */
73 > readonly restrictedSettings: RestrictedSettings;
74 >
75 > /**
76 > * Event that triggers when the restricted settings changes
77 > */
78 > readonly onDidChangeRestrictedSettings: Event<RestrictedSettings>;
79 >
80 > /**
81 > * A promise that resolves when the remote configuration is loaded in a remote window.
82 > * The promise is resolved immediately if the window is not remote.
83 > */
84 > whenRemoteConfigurationLoaded(): Promise<void>;
85 >
86 > /**
87 > * Initialize configuration service for the given workspace
88 > * @param arg workspace Identifier
89 > */
90 > initialize(arg: IAnyWorkspaceIdentifier): Promise<void>;
91 >
92 > /**
93 > * Returns true if the setting can be applied for all profiles otherwise false.
94 > * @param setting
95 > */
96 > isSettingAppliedForAllProfiles(setting: string): boolean;
97 > }
98 >
99 > export const TASKS_DEFAULT = '{\n\t\"version\": \"2.0.0\",\n\t\"tasks\": []\n}';
100 >
101 > export const APPLY_ALL_PROFILES_SETTING = 'workbench.settings.applyToAllProfiles';
src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts 100 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- jsonContributionRegistry.ts
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 { Emitter, Event } from '../../../base/common/event.js';
7 > import { getCompressedContent, IJSONSchema } from '../../../base/common/jsonSchema.js';
8 > import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
9 > import * as platform from '../../registry/common/platform.js';
10 >
11 > export const Extensions = {
12 > JSONContribution: 'base.contributions.json'
13 > };
14 >
15 > export interface ISchemaContributions {
16 > schemas: { [id: string]: IJSONSchema };
17 > }
18 >
19 > export interface IJSONContributionRegistry {
20 >
21 > readonly onDidChangeSchema: Event<string>;
22 > readonly onDidChangeSchemaAssociations: Event<void>;
23 >
24 > /**
25 > * Register a schema to the registry.
26 > */
27 > registerSchema(uri: string, unresolvedSchemaContent: IJSONSchema, store?: DisposableStore): void;
28 >
29 > registerSchemaAssociation(uri: string, glob: string): IDisposable;
30 >
31 > /**
32 > * Notifies all listeners that the content of the given schema has changed.
33 > * @param uri The id of the schema
34 > */
35 > notifySchemaChanged(uri: string): void;
36 >
37 > /**
38 > * Get all schemas
39 > */
40 > getSchemaContributions(): ISchemaContributions;
41 >
42 > getSchemaAssociations(): { [uri: string]: string[] };
43 >
44 > /**
45 > * Gets the (compressed) content of the schema with the given schema ID (if any)
46 > * @param uri The id of the schema
47 > */
48 > getSchemaContent(uri: string): string | undefined;
49 >
50 > /**
51 > * Returns true if there's a schema that matches the given schema ID
52 > * @param uri The id of the schema
53 > */
54 > hasSchemaContent(uri: string): boolean;
55 > }
56 >
57 >
58 >
59 > function normalizeId(id: string) {
60 > if (id.length > 0 && id.charAt(id.length - 1) === '#') {
61 return id.substring(0, id.length - 1);
62 }
63 > return id; jsonContributionRegistry.ts
64 > }
65 >
66 >
67 >
68 > class JSONContributionRegistry extends Disposable implements IJSONContributionRegistry {
69 >
70 > private readonly schemasById: { [id: string]: IJSONSchema } = {};
71 > private readonly schemaAssociations: { [uri: string]: string[] } = {};
72 >
73 > private readonly _onDidChangeSchema = this._register(new Emitter<string>());
74 > readonly onDidChangeSchema: Event<string> = this._onDidChangeSchema.event;
75 >
76 > private readonly _onDidChangeSchemaAssociations = this._register(new Emitter<void>());
77 > readonly onDidChangeSchemaAssociations: Event<void> = this._onDidChangeSchemaAssociations.event;
78 >
79 > public registerSchema(uri: string, unresolvedSchemaContent: IJSONSchema, store?: DisposableStore): void {
80 > const normalizedUri = normalizeId(uri);
81 > this.schemasById[normalizedUri] = unresolvedSchemaContent;
82 > this._onDidChangeSchema.fire(uri);
83 >
84 > if (store) {
85 store.add(toDisposable(() => {
86 delete this.schemasById[normalizedUri];
88 }));
89 }
91 >
92 > public registerSchemaAssociation(uri: string, glob: string): IDisposable {
93 const normalizedUri = normalizeId(uri);
94 if (!this.schemaAssociations[normalizedUri]) {
114 });
115 }
117 > public notifySchemaChanged(uri: string): void {
118 this._onDidChangeSchema.fire(uri);
119 }
121 > public getSchemaContributions(): ISchemaContributions {
122 return {
123 schemas: this.schemasById,
124 };
125 }
127 > public getSchemaContent(uri: string): string | undefined {
128 const schema = this.schemasById[uri];
129 return schema ? getCompressedContent(schema) : undefined;
130 }
132 > public hasSchemaContent(uri: string): boolean {
133 return !!this.schemasById[uri];
134 }
136 > public getSchemaAssociations(): { [uri: string]: string[] } {
137 return this.schemaAssociations;
138 }
140 > }
141 >
142 > const jsonContributionRegistry = new JSONContributionRegistry();
143 > platform.Registry.add(Extensions.JSONContribution, jsonContributionRegistry);
src/vs/base/common/arraysFind.ts 95 covered LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- arraysFind.ts
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 { Comparator } from './arrays.js';
7 >
8 > export function findLast<T, R extends T>(array: readonly T[], predicate: (item: T, index: number) => item is R, fromIndex?: number): R | undefined;
9 > export function findLast<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex?: number): T | undefined;
10 > export function findLast<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = array.length - 1): T | undefined {
11 const idx = findLastIdx(array, predicate, fromIndex);
12 if (idx === -1) {
15 return array[idx];
16 }
18 > export function findLastIdx<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = array.length - 1): number {
19 for (let i = fromIndex; i >= 0; i--) {
20 const element = array[i];
27 return -1;
28 }
30 > export function findFirst<T, R extends T>(array: readonly T[], predicate: (item: T, index: number) => item is R, fromIndex?: number): R | undefined;
31 > export function findFirst<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex?: number): T | undefined;
32 > export function findFirst<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = 0): T | undefined {
33 const idx = findFirstIdx(array, predicate, fromIndex);
34 if (idx === -1) {
37 return array[idx];
38 }
40 > export function findFirstIdx<T>(array: readonly T[], predicate: (item: T, index: number) => unknown, fromIndex = 0): number {
41 for (let i = fromIndex; i < array.length; i++) {
42 const element = array[i];
49 return -1;
50 }
52 > /**
53 > * Finds the last item where predicate is true using binary search.
54 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
55 > *
56 > * @returns `undefined` if no item matches, otherwise the last item that matches the predicate.
57 > */
58 > export function findLastMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
59 const idx = findLastIdxMonotonous(array, predicate);
60 return idx === -1 ? undefined : array[idx];
61 }
63 > /**
64 > * Finds the last item where predicate is true using binary search.
65 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
66 > *
67 > * @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate.
68 > */
69 > export function findLastIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
70 let i = startIdx;
71 let j = endIdxEx;
80 return i - 1;
81 }
83 > /**
84 > * Finds the first item where predicate is true using binary search.
85 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
86 > *
87 > * @returns `undefined` if no item matches, otherwise the first item that matches the predicate.
88 > */
89 > export function findFirstMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean): T | undefined {
90 const idx = findFirstIdxMonotonousOrArrLen(array, predicate);
91 return idx === array.length ? undefined : array[idx];
92 }
94 > /**
95 > * Finds the first item where predicate is true using binary search.
96 > * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`!
97 > *
98 > * @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate.
99 > */
100 > export function findFirstIdxMonotonousOrArrLen<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
101 let i = startIdx;
102 let j = endIdxEx;
111 return i;
112 }
114 > export function findFirstIdxMonotonous<T>(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number {
115 const idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx);
116 return idx === array.length ? -1 : idx;
117 }
119 > /**
120 > * Use this when
121 > * * You have a sorted array
122 > * * You query this array with a monotonous predicate to find the last item that has a certain property.
123 > * * You query this array multiple times with monotonous predicates that get weaker and weaker.
124 > */
125 > export class MonotonousArray<T> {
126 > public static assertInvariants = false;
127 >
128 > private _findLastMonotonousLastIdx = 0;
129 > private _prevFindLastPredicate: ((item: T) => boolean) | undefined;
130 >
131 > constructor(private readonly _array: readonly T[]) {
132 }
134 > /**
135 > * The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`!
136 > * For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`.
137 > */
138 > findLastMonotonous(predicate: (item: T) => boolean): T | undefined {
139 if (MonotonousArray.assertInvariants) {
140 if (this._prevFindLastPredicate) {
152 return idx === -1 ? undefined : this._array[idx];
153 }
154 > } arraysFind.ts
155 >
156 > /**
157 > * Returns the first item that is equal to or greater than every other item.
158 > */
159 > export function findFirstMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
160 if (array.length === 0) {
161 return undefined;
171 return max;
172 }
174 > /**
175 > * Returns the last item that is equal to or greater than every other item.
176 > */
177 > export function findLastMax<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
178 if (array.length === 0) {
179 return undefined;
189 return max;
190 }
192 > /**
193 > * Returns the first item that is equal to or less than every other item.
194 > */
195 > export function findFirstMin<T>(array: readonly T[], comparator: Comparator<T>): T | undefined {
196 return findFirstMax(array, (a, b) => -comparator(a, b));
197 }
199 > export function findMaxIdx<T>(array: readonly T[], comparator: Comparator<T>): number {
200 if (array.length === 0) {
201 return -1;
211 return maxIdx;
212 }
214 > /**
215 > * Returns the first mapped value of the array which is not undefined.
216 > */
217 > export function mapFindFirst<T, R>(items: Iterable<T>, mapFn: (value: T) => R | undefined): R | undefined {
218 for (const value of items) {
219 const mapped = mapFn(value);
src/vs/base/common/cancellation.ts 90 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- cancellation.ts
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 { Emitter, Event } from './event.js';
7 > import { DisposableStore, IDisposable } from './lifecycle.js';
8 >
9 > export interface CancellationToken {
10 >
11 > /**
12 > * A flag signalling is cancellation has been requested.
13 > */
14 > readonly isCancellationRequested: boolean;
15 >
16 > /**
17 > * An event which fires when cancellation is requested. This event
18 > * only ever fires `once` as cancellation can only happen once. Listeners
19 > * that are registered after cancellation will be called (next event loop run),
20 > * but also only once.
21 > *
22 > * @event
23 > */
24 > readonly onCancellationRequested: (listener: (e: void) => unknown, thisArgs?: unknown, disposables?: IDisposable[]) => IDisposable;
25 > }
26 >
27 > const shortcutEvent: Event<void> = Object.freeze(function (callback, context?): IDisposable {
28 const handle = setTimeout(callback.bind(context), 0);
29 return { dispose() { clearTimeout(handle); } };
30 });
32 > export namespace CancellationToken {
33 >
34 > export function isCancellationToken(thing: unknown): thing is CancellationToken {
35 if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {
36 return true;
45 && typeof (thing as CancellationToken).onCancellationRequested === 'function';
46 }
48 >
49 > export const None = Object.freeze<CancellationToken>({
50 > isCancellationRequested: false,
51 > onCancellationRequested: Event.None
52 > });
53 >
54 > export const Cancelled = Object.freeze<CancellationToken>({
55 > isCancellationRequested: true,
56 > onCancellationRequested: shortcutEvent
57 > });
58 > }
59 >
60 class MutableToken implements CancellationToken {
61
62 private _isCancelled: boolean = false;
63 private _emitter: Emitter<void> | null = null;
65 > public cancel() {
66 if (!this._isCancelled) {
67 this._isCancelled = true;
72 }
73 }
75 > get isCancellationRequested(): boolean {
76 return this._isCancelled;
77 }
79 > get onCancellationRequested(): Event<void> {
80 if (this._isCancelled) {
81 return shortcutEvent;
86 return this._emitter.event;
87 }
89 > public dispose(): void {
90 if (this._emitter) {
91 this._emitter.dispose();
93 }
94 }
96 >
97 > export class CancellationTokenSource {
98 >
99 > private _token?: CancellationToken = undefined;
100 > private _parentListener?: IDisposable = undefined;
101 >
102 > constructor(parent?: CancellationToken) {
103 this._parentListener = parent && parent.onCancellationRequested(this.cancel, this);
104 }
106 > get token(): CancellationToken {
107 if (!this._token) {
108 // be lazy and create the token only when
112 return this._token;
113 }
115 > cancel(): void {
116 if (!this._token) {
117 // save an object by returning the default
125 }
126 }
128 > dispose(cancel: boolean = false): void {
129 if (cancel) {
130 this.cancel();
140 }
141 }
142 > } cancellation.ts
143 >
144 > export function cancelOnDispose(store: DisposableStore): CancellationToken {
145 const source = new CancellationTokenSource();
146 store.add({ dispose() { source.cancel(); } });
147 return source.token;
148 }
150 > /**
151 > * A pool that aggregates multiple cancellation tokens. The pool's own token
152 > * (accessible via `pool.token`) is cancelled only after every token added
153 > * to the pool has been cancelled. Adding tokens after the pool token has
154 > * been cancelled has no effect.
155 > */
156 > export class CancellationTokenPool {
157
158 private readonly _source = new CancellationTokenSource();
162 private _cancelled: number = 0;
163 private _isDone: boolean = false;
165 > get token(): CancellationToken {
166 return this._source.token;
167 }
169 > /**
170 > * Add a token to the pool. If the token is already cancelled it is counted
171 > * immediately. Tokens added after the pool token has been cancelled are ignored.
172 > */
173 > add(token: CancellationToken): void {
174 if (this._isDone) {
175 return;
191 this._listeners.add(d);
192 }
194 > private _check(): void {
195 if (!this._isDone && this._total > 0 && this._total === this._cancelled) {
196 this._isDone = true;
src/vs/base/common/cache.ts 88 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- cache.ts
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 { CancellationToken, CancellationTokenSource } from './cancellation.js';
7 > import { IDisposable } from './lifecycle.js';
8 >
9 > export interface CacheResult<T> extends IDisposable {
10 > promise: Promise<T>;
11 > }
12 >
13 > export class Cache<T> {
14 >
15 > private result: CacheResult<T> | null = null;
16 > constructor(private task: (ct: CancellationToken) => Promise<T>) { }
17 >
18 > get(): CacheResult<T> {
19 if (this.result) {
20 return this.result;
35 return this.result;
36 }
37 > } cache.ts
38 >
39 > export function identity<T>(t: T): T {
40 return t;
41 }
42 > cache.ts
43 > interface ICacheOptions<TArg> {
44 > /**
45 > * The cache key is used to identify the cache entry.
46 > * Strict equality is used to compare cache keys.
47 > */
48 > getCacheKey: (arg: TArg) => unknown;
49 > }
50 >
51 > /**
52 > * Uses a LRU cache to make a given parametrized function cached.
53 > * Caches just the last key/value.
54 > */
55 > export class LRUCachedFunction<TArg, TComputed> {
56 > private lastCache: TComputed | undefined = undefined;
57 > private lastArgKey: unknown | undefined = undefined;
58 >
59 > private readonly _fn: (arg: TArg) => TComputed;
60 > private readonly _computeKey: (arg: TArg) => unknown;
61 >
62 > constructor(fn: (arg: TArg) => TComputed);
63 > constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
64 > constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
65 > if (typeof arg1 === 'function') {
66 > this._fn = arg1;
67 > this._computeKey = identity;
68 > } else {
69 this._fn = arg2!;
70 this._computeKey = arg1.getCacheKey;
71 }
72 > } cache.ts
73 >
74 > public get(arg: TArg): TComputed {
75 const key = this._computeKey(arg);
76 if (this.lastArgKey !== key) {
80 return this.lastCache!;
81 }
82 > } cache.ts
83 >
84 > /**
85 > * Uses an unbounded cache to memoize the results of the given function.
86 > */
87 > export class CachedFunction<TArg, TComputed> {
88 > private readonly _map = new Map<TArg, TComputed>();
89 > private readonly _map2 = new Map<unknown, TComputed>();
90 > public get cachedValues(): ReadonlyMap<TArg, TComputed> {
91 > return this._map;
92 > }
93 >
94 > private readonly _fn: (arg: TArg) => TComputed;
95 > private readonly _computeKey: (arg: TArg) => unknown;
96 >
97 > constructor(fn: (arg: TArg) => TComputed);
98 > constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
99 > constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
100 if (typeof arg1 === 'function') {
101 this._fn = arg1;
106 }
107 }
108 > cache.ts
109 > public get(arg: TArg): TComputed {
110 const key = this._computeKey(arg);
111 if (this._map2.has(key)) {
118 return value;
119 }
120 > } cache.ts
121 >
122 > /**
123 > * Uses an unbounded cache to memoize the results of the given function.
124 > */
125 > export class WeakCachedFunction<TArg, TComputed> {
126 > private readonly _map = new WeakMap<WeakKey, TComputed>();
127 >
128 > private readonly _fn: (arg: TArg) => TComputed;
129 > private readonly _computeKey: (arg: TArg) => unknown;
130 >
131 > constructor(fn: (arg: TArg) => TComputed);
132 > constructor(options: ICacheOptions<TArg>, fn: (arg: TArg) => TComputed);
133 > constructor(arg1: ICacheOptions<TArg> | ((arg: TArg) => TComputed), arg2?: (arg: TArg) => TComputed) {
134 if (typeof arg1 === 'function') {
135 this._fn = arg1;
140 }
141 }
142 > cache.ts
143 > public get(arg: TArg): TComputed {
144 const key = this._computeKey(arg) as WeakKey;
145 if (this._map.has(key)) {
151 return value;
152 }
153 > } cache.ts
src/vs/base/common/collections.ts 74 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- collections.ts
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 > /**
7 > * An interface for a JavaScript object that
8 > * acts a dictionary. The keys are strings.
9 > */
10 > export type IStringDictionary<V> = Record<string, V>;
11 >
12 > /**
13 > * An interface for a JavaScript object that
14 > * acts a dictionary. The keys are numbers.
15 > */
16 > export type INumberDictionary<V> = Record<number, V>;
17 >
18 > /**
19 > * Groups the collection into a dictionary based on the provided
20 > * group function.
21 > */
22 > export function groupBy<K extends string | number | symbol, V>(data: readonly V[], groupFn: (element: V) => K): Partial<Record<K, V[]>> {
23 const result: Partial<Record<K, V[]>> = Object.create(null);
24 for (const element of data) {
32 return result;
33 }
35 > export function groupByMap<K, V>(data: V[], groupFn: (element: V) => K): Map<K, V[]> {
36 const result = new Map<K, V[]>();
37 for (const element of data) {
46 return result;
47 }
49 > export function diffSets<T>(before: ReadonlySet<T>, after: ReadonlySet<T>): { removed: T[]; added: T[] } {
50 const removed: T[] = [];
51 const added: T[] = [];
62 return { removed, added };
63 }
65 > /**
66 > * Checks whether two sets contain exactly the same elements.
67 > *
68 > * @param a - The first set.
69 > * @param b - The second set.
70 > * @returns `true` if both sets have the same size and every element of `a` is also in `b`.
71 > */
72 > export function equalSets<T>(a: ReadonlySet<T>, b: ReadonlySet<T>): boolean {
73 if (a === b) {
74 return true;
84 return true;
85 }
87 > export function diffMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[]; added: V[] } {
88 const removed: V[] = [];
89 const added: V[] = [];
100 return { removed, added };
101 }
103 > /**
104 > * Computes the intersection of two sets.
105 > *
106 > * @param setA - The first set.
107 > * @param setB - The second iterable.
108 > * @returns A new set containing the elements that are in both `setA` and `setB`.
109 > */
110 > export function intersection<T>(setA: Set<T>, setB: Iterable<T>): Set<T> {
111 const result = new Set<T>();
112 for (const elem of setB) {
117 return result;
118 }
120 > export class SetWithKey<T> implements Set<T> {
121 > private _map = new Map<unknown, T>();
122 >
123 > constructor(values: T[], private toKey: (t: T) => unknown) {
124 for (const value of values) {
125 this.add(value);
126 }
127 }
129 > get size(): number {
130 return this._map.size;
131 }
133 > add(value: T): this {
134 const key = this.toKey(value);
135 this._map.set(key, value);
136 return this;
137 }
139 > delete(value: T): boolean {
140 return this._map.delete(this.toKey(value));
141 }
143 > has(value: T): boolean {
144 return this._map.has(this.toKey(value));
145 }
147 > *entries(): SetIterator<[T, T]> {
148 for (const entry of this._map.values()) {
149 yield [entry, entry];
150 }
151 }
153 > keys(): SetIterator<T> {
154 return this.values();
155 }
157 > *values(): SetIterator<T> {
158 for (const entry of this._map.values()) {
159 yield entry;
160 }
161 }
163 > clear(): void {
164 this._map.clear();
165 }
167 > forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: unknown): void {
168 this._map.forEach(entry => callbackfn.call(thisArg, entry, entry, this));
169 }
171 > [Symbol.iterator](): SetIterator<T> {
172 return this.values();
173 }
175 > [Symbol.toStringTag]: string = 'SetWithKey';
176 > }
src/vs/base/common/iterator.ts 65 covered LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- iterator.ts
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 { isIterable } from './types.js';
7 >
8 > export namespace Iterable {
9 >
10 > export function is<T = unknown>(thing: unknown): thing is Iterable<T> {
11 > return !!thing && typeof thing === 'object' && typeof (thing as Iterable<T>)[Symbol.iterator] === 'function'; iterator.ts
12 > }
14 > const _empty: Iterable<never> = Object.freeze([]);
15 > export function empty<T = never>(): readonly never[] {
16 return _empty as readonly never[];
17 }
19 > export function* single<T>(element: T): Iterable<T> {
20 yield element;
21 }
23 > export function wrap<T>(iterableOrElement: Iterable<T> | T): Iterable<T> {
24 if (is(iterableOrElement)) {
25 return iterableOrElement;
28 }
29 }
31 > export function from<T>(iterable: Iterable<T> | undefined | null): Iterable<T> {
32 return iterable ?? (_empty as Iterable<T>);
33 }
35 > export function* reverse<T>(array: ReadonlyArray<T>): Iterable<T> {
36 for (let i = array.length - 1; i >= 0; i--) {
37 yield array[i];
38 }
39 }
41 > export function isEmpty<T>(iterable: Iterable<T> | undefined | null): boolean {
42 return !iterable || iterable[Symbol.iterator]().next().done === true;
43 }
45 > export function first<T>(iterable: Iterable<T>): T | undefined {
46 return iterable[Symbol.iterator]().next().value;
47 }
49 > export function some<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
50 let i = 0;
51 for (const element of iterable) {
56 return false;
57 }
59 > export function every<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
60 let i = 0;
61 for (const element of iterable) {
66 return true;
67 }
69 > export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): R | undefined;
70 > export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
71 > export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
72 for (const element of iterable) {
73 if (predicate(element)) {
78 return undefined;
79 }
81 > export function filter<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): Iterable<R>;
82 > export function filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T>;
83 > export function* filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T> {
84 for (const element of iterable) {
85 if (predicate(element)) {
88 }
89 }
91 > export function* map<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => R): Iterable<R> {
92 let index = 0;
93 for (const element of iterable) {
95 }
96 }
98 > export function* flatMap<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => Iterable<R>): Iterable<R> {
99 let index = 0;
100 for (const element of iterable) {
102 }
103 }
104 > iterator.ts
105 > export function* concat<T>(...iterables: (Iterable<T> | T)[]): Iterable<T> {
106 for (const item of iterables) {
107 if (isIterable(item)) {
112 }
113 }
114 > iterator.ts
115 > export function reduce<T, R>(iterable: Iterable<T>, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R {
116 let value = initialValue;
117 for (const element of iterable) {
120 return value;
121 }
122 > iterator.ts
123 > export function length<T>(iterable: Iterable<T>): number {
124 let count = 0;
125 for (const _ of iterable) {
128 return count;
129 }
130 > iterator.ts
131 > /**
132 > * Returns an iterable slice of the array, with the same semantics as `array.slice()`.
133 > */
134 > export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
135 if (from < -arr.length) {
136 from = 0;
150 }
151 }
152 > iterator.ts
153 > /**
154 > * Consumes `atMost` elements from iterable and returns the consumed elements,
155 > * and an iterable for the rest of the elements.
156 > */
157 > export function consume<T>(iterable: Iterable<T>, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable<T>] {
158 const consumed: T[] = [];
159
176 return [consumed, { [Symbol.iterator]() { return iterator; } }];
177 }
178 > iterator.ts
179 > export async function asyncToArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
180 const result: T[] = [];
181 for await (const item of iterable) {
184 return result;
185 }
186 > iterator.ts
187 > export async function asyncToArrayFlat<T>(iterable: AsyncIterable<T[]>): Promise<T[]> {
188 let result: T[] = [];
189 for await (const item of iterable) {
src/vs/base/test/common/utils.ts 58 covered LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utils.ts
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, DisposableTracker, IDisposable, setDisposableTracker } from '../../common/lifecycle.js';
7 > import { join } from '../../common/path.js';
8 > import { isWindows } from '../../common/platform.js';
9 > import { URI } from '../../common/uri.js';
10 >
11 > export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
12 >
13 > export function toResource(this: any, path: string): URI {
14 if (isWindows) {
15 return URI.file(join('C:\\', btoa(this.test.fullTitle()), path));
18 return URI.file(join('/', btoa(this.test.fullTitle()), path));
19 }
20 > utils.ts
21 > export function suiteRepeat(n: number, description: string, callback: (this: any) => void): void {
22 for (let i = 0; i < n; i++) {
23 suite(`${description} (iteration ${i})`, callback);
24 }
25 }
26 > utils.ts
27 > export function testRepeat(n: number, description: string, callback: (this: any) => any): void {
28 for (let i = 0; i < n; i++) {
29 test(`${description} (iteration ${i})`, callback);
30 }
31 }
32 > utils.ts
33 export async function assertThrowsAsync(block: () => any, message: string | Error = 'Missing expected exception'): Promise<void> {
34 try {
41 throw err;
42 }
43 > utils.ts
44 > /**
45 > * Use this function to ensure that all disposables are cleaned up at the end of each test in the current suite.
46 > *
47 > * Use `markAsSingleton` if disposable singletons are created lazily that are allowed to outlive the test.
48 > * Make sure that the singleton properly registers all child disposables so that they are excluded too.
49 > *
50 > * @returns A {@link DisposableStore} that can optionally be used to track disposables in the test.
51 > * This will be automatically disposed on test teardown.
52 > */
53 > export function ensureNoDisposablesAreLeakedInTestSuite(): Pick<DisposableStore, 'add'> {
54 > let tracker: DisposableTracker | undefined;
55 > let store: DisposableStore;
56 > setup(() => {
57 > store = new DisposableStore(); utils.ts
58 > tracker = new DisposableTracker();
59 > setDisposableTracker(tracker);
60 > }); utils.ts
61 >
62 > teardown(function (this: import('mocha').Context) {
63 > store.dispose(); utils.ts
64 > setDisposableTracker(null);
65 > if (this.currentTest?.state !== 'failed') {
66 > const result = tracker!.computeLeakingDisposables();
67 > if (result) {
68 console.error(result.details);
69 throw new Error(`There are ${result.leaks.length} undisposed disposables!${result.details}`);
70 }
71 > } utils.ts
72 > }); utils.ts
73 >
74 > // Wrap store as the suite function is called before it's initialized
75 > const testContext = {
76 > add<T extends IDisposable>(o: T): T {
77 > return store.add(o); utils.ts
78 > }
79 > }; utils.ts
80 > return testContext;
81 > }
82 >
83 > export function throwIfDisposablesAreLeaked(body: () => void, logToConsole = true): void {
84 const tracker = new DisposableTracker();
85 setDisposableTracker(tracker);
88 computeLeakingDisposables(tracker, logToConsole);
89 }
90 > utils.ts
91 export async function throwIfDisposablesAreLeakedAsync(body: () => Promise<void>): Promise<void> {
92 const tracker = new DisposableTracker();
96 computeLeakingDisposables(tracker);
97 }
98 > utils.ts
99 function computeLeakingDisposables(tracker: DisposableTracker, logToConsole = true) {
100 const result = tracker.computeLeakingDisposables();
src/vs/platform/registry/common/platform.ts 54 covered LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- platform.ts
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 * as Assert from '../../../base/common/assert.js';
7 > import * as Types from '../../../base/common/types.js';
8 >
9 > export interface IRegistry {
10 >
11 > /**
12 > * Adds the extension functions and properties defined by data to the
13 > * platform. The provided id must be unique.
14 > * @param id a unique identifier
15 > * @param data a contribution
16 > */
17 > add(id: string, data: any): void;
18 >
19 > /**
20 > * Returns true iff there is an extension with the provided id.
21 > * @param id an extension identifier
22 > */
23 > knows(id: string): boolean;
24 >
25 > /**
26 > * Returns the extension functions and properties defined by the specified key or null.
27 > * @param id an extension identifier
28 > */
29 > as<T>(id: string): T;
30 > }
31 >
32 > class RegistryImpl implements IRegistry {
33 >
34 > private readonly data = new Map<string, any>();
35 >
36 > public add(id: string, data: any): void {
37 > Assert.ok(Types.isString(id)); platform.ts
38 > Assert.ok(Types.isObject(data));
39 > Assert.ok(!this.data.has(id), 'There is already an extension with this id');
40 >
41 > this.data.set(id, data);
42 > }
44 > public knows(id: string): boolean {
45 return this.data.has(id);
46 }
48 > public as(id: string): any {
49 > return this.data.get(id) || null; platform.ts
50 > }
52 > public dispose() {
53 this.data.forEach((value) => {
54 if (Types.isFunction(value.dispose)) {
58 this.data.clear();
59 }
61 > }
62 >
63 > export const Registry: IRegistry = new RegistryImpl();
src/vs/base/common/process.ts 53 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- process.ts
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 { INodeProcess, isMacintosh, isWindows } from './platform.js';
7 >
8 > let safeProcess: Omit<INodeProcess, 'arch'> & { arch: string | undefined };
9 > declare const process: INodeProcess;
10 >
11 > // Native sandbox environment
12 > const vscodeGlobal = (globalThis as { vscode?: { process?: INodeProcess } }).vscode;
13 > if (typeof vscodeGlobal !== 'undefined' && typeof vscodeGlobal.process !== 'undefined') {
14 const sandboxProcess: INodeProcess = vscodeGlobal.process;
15 safeProcess = {
20 };
21 }
22 > process.ts
23 > // Native node.js environment
24 > else if (typeof process !== 'undefined' && typeof process?.versions?.node === 'string') {
25 > safeProcess = {
26 > get platform() { return process.platform; },
27 > get arch() { return process.arch; },
28 > get env() { return process.env; },
29 > cwd() { return process.env['VSCODE_CWD'] || process.cwd(); }
30 > };
31 }
32
44 };
45 }
46 > process.ts
47 > /**
48 > * Provides safe access to the `cwd` property in node.js, sandboxed or web
49 > * environments.
50 > *
51 > * Note: in web, this property is hardcoded to be `/`.
52 > *
53 > * @skipMangle
54 > */
55 > export const cwd = safeProcess.cwd;
56 >
57 > /**
58 > * Provides safe access to the `env` property in node.js, sandboxed or web
59 > * environments.
60 > *
61 > * Note: in web, this property is hardcoded to be `{}`.
62 > */
63 > export const env = safeProcess.env;
64 >
65 > /**
66 > * Provides safe access to the `platform` property in node.js, sandboxed or web
67 > * environments.
68 > */
69 > export const platform = safeProcess.platform;
70 >
71 > /**
72 > * Provides safe access to the `arch` method in node.js, sandboxed or web
73 > * environments.
74 > * Note: `arch` is `undefined` in web
75 > */
76 > export const arch = safeProcess.arch;
src/vs/base/common/assert.ts 50 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- assert.ts
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 { BugIndicatingError, onUnexpectedError } from './errors.js';
7 >
8 > /**
9 > * Throws an error with the provided message if the provided value does not evaluate to a true Javascript value.
10 > *
11 > * @deprecated Use `assert(...)` instead.
12 > * This method is usually used like this:
13 > * ```ts
14 > * import * as assert from 'vs/base/common/assert';
15 > * assert.ok(...);
16 > * ```
17 > *
18 > * However, `assert` in that example is a user chosen name.
19 > * There is no tooling for generating such an import statement.
20 > * Thus, the `assert(...)` function should be used instead.
21 > */
22 > export function ok(value?: unknown, message?: string) {
23 > if (!value) { assert.ts
24 throw new Error(message ? `Assertion failed (${message})` : 'Assertion Failed');
25 }
26 > } assert.ts
27 > assert.ts
28 > export function assertNever(value: never, message = 'Unreachable'): never {
29 throw new Error(message);
30 }
31 > assert.ts
32 > export function softAssertNever(value: never): void {
33 // no-op
34 }
35 > assert.ts
36 > /**
37 > * Asserts that a condition is `truthy`.
38 > *
39 > * @throws provided {@linkcode messageOrError} if the {@linkcode condition} is `falsy`.
40 > *
41 > * @param condition The condition to assert.
42 > * @param messageOrError An error message or error object to throw if condition is `falsy`.
43 > */
44 > export function assert(
45 condition: boolean,
46 messageOrError: string | Error = 'unexpected state',
55 }
56 }
57 > assert.ts
58 > /**
59 > * Like assert, but doesn't throw.
60 > */
61 > export function softAssert(condition: boolean, message = 'Soft Assertion Failed'): void {
62 if (!condition) {
63 onUnexpectedError(new BugIndicatingError(message));
64 }
65 }
66 > assert.ts
67 > /**
68 > * condition must be side-effect free!
69 > */
70 > export function assertFn(condition: () => boolean): void {
71 if (!condition()) {
72 // eslint-disable-next-line no-debugger
77 }
78 }
79 > assert.ts
80 > export function checkAdjacentItems<T>(items: readonly T[], predicate: (item1: T, item2: T) => boolean): boolean {
81 let i = 0;
82 while (i < items.length - 1) {
src/vs/platform/configuration/test/common/testConfigurationService.ts 50 covered LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- testConfigurationService.ts
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 { Emitter } from '../../../../base/common/event.js';
7 > import { TernarySearchTree } from '../../../../base/common/ternarySearchTree.js';
8 > import { URI } from '../../../../base/common/uri.js';
9 > import { getConfigurationValue, IConfigurationChangeEvent, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from '../../common/configuration.js';
10 > import { Extensions, IConfigurationRegistry } from '../../common/configurationRegistry.js';
11 > import { Registry } from '../../../registry/common/platform.js';
12 >
13 > export class TestConfigurationService implements IConfigurationService {
14 > public _serviceBrand: undefined;
15 >
16 > private configuration: Record<string, unknown>;
17 > readonly onDidChangeConfigurationEmitter = new Emitter<IConfigurationChangeEvent>();
18 > readonly onDidChangeConfiguration = this.onDidChangeConfigurationEmitter.event;
19 >
20 > constructor(configuration?: Record<string, unknown>) {
21 > this.configuration = configuration || Object.create(null); testConfigurationService.ts
22 > }
24 > private configurationByRoot: TernarySearchTree<string, Record<string, unknown>> = TernarySearchTree.forPaths<Record<string, unknown>>();
25 >
26 > public reloadConfiguration<T>(): Promise<T> {
27 return Promise.resolve(this.getValue() as T);
28 }
30 > public getValue<T>(arg1?: string | IConfigurationOverrides, arg2?: IConfigurationOverrides): T | undefined {
31 let configuration;
32 const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : undefined;
42 return configuration as T;
43 }
45 > public updateValue(key: string, value: unknown): Promise<void> {
46 return Promise.resolve(undefined);
47 }
49 > public setUserConfiguration(key: string, value: unknown, root?: URI): Promise<void> {
50 if (root) {
51 const configForRoot = this.configurationByRoot.get(root.fsPath) || Object.create(null);
58 return Promise.resolve(undefined);
59 }
61 > private overrideIdentifiers: Map<string, string[]> = new Map();
62 > public setOverrideIdentifiers(key: string, identifiers: string[]): void {
63 this.overrideIdentifiers.set(key, identifiers);
64 }
66 > public inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {
67 const value = this.getValue(key, overrides) as T;
68
src/vs/base/common/linkedList.ts 44 covered LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- linkedList.ts
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 > class Node<E> {
7 >
8 > static readonly Undefined = new Node<unknown>(undefined);
9 >
10 > element: E;
11 > next: Node<E> | typeof Node.Undefined;
12 > prev: Node<E> | typeof Node.Undefined;
13 >
14 > constructor(element: E) {
15 > this.element = element;
16 > this.next = Node.Undefined;
17 > this.prev = Node.Undefined;
18 > }
19 > }
20 >
21 > export class LinkedList<E> {
22
23 private _first: Node<E> | typeof Node.Undefined = Node.Undefined;
24 private _last: Node<E> | typeof Node.Undefined = Node.Undefined;
25 private _size: number = 0;
27 > get size(): number {
28 return this._size;
29 }
31 > isEmpty(): boolean {
32 return this._first === Node.Undefined;
33 }
35 > clear(): void {
36 let node = this._first;
37 while (node !== Node.Undefined) {
46 this._size = 0;
47 }
49 > unshift(element: E): () => void {
50 return this._insert(element, false);
51 }
53 > push(element: E): () => void {
54 return this._insert(element, true);
55 }
57 > private _insert(element: E, atTheEnd: boolean): () => void {
58 const newNode = new Node(element);
59 if (this._first === Node.Undefined) {
85 };
86 }
88 > shift(): E | undefined {
89 if (this._first === Node.Undefined) {
90 return undefined;
95 }
96 }
98 > pop(): E | undefined {
99 if (this._last === Node.Undefined) {
100 return undefined;
105 }
106 }
108 > peek(): E | undefined {
109 if (this._last === Node.Undefined) {
110 return undefined;
114 }
115 }
117 > private _remove(node: Node<E> | typeof Node.Undefined): void {
118 if (node.prev !== Node.Undefined && node.next !== Node.Undefined) {
119 // middle
141 this._size -= 1;
142 }
144 > *[Symbol.iterator](): Iterator<E> {
145 let node = this._first;
146 while (node !== Node.Undefined) {
src/vs/base/common/uint.ts 43 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- uint.ts
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 > export const enum Constants {
7 > /**
8 > * MAX SMI (SMall Integer) as defined in v8.
9 > * one bit is lost for boxing/unboxing flag.
10 > * one bit is lost for sign flag.
11 > * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
12 > */
13 > MAX_SAFE_SMALL_INTEGER = 1 << 30,
14 >
15 > /**
16 > * MIN SMI (SMall Integer) as defined in v8.
17 > * one bit is lost for boxing/unboxing flag.
18 > * one bit is lost for sign flag.
19 > * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
20 > */
21 > MIN_SAFE_SMALL_INTEGER = -(1 << 30),
22 >
23 > /**
24 > * Max unsigned integer that fits on 8 bits.
25 > */
26 > MAX_UINT_8 = 255, // 2^8 - 1
27 >
28 > /**
29 > * Max unsigned integer that fits on 16 bits.
30 > */
31 > MAX_UINT_16 = 65535, // 2^16 - 1
32 >
33 > /**
34 > * Max unsigned integer that fits on 32 bits.
35 > */
36 > MAX_UINT_32 = 4294967295, // 2^32 - 1
37 >
38 > UNICODE_SUPPLEMENTARY_PLANE_BEGIN = 0x010000
39 > }
40 >
41 > export function toUint8(v: number): number {
42 if (v < 0) {
43 return 0;
48 return v | 0;
49 }
50 > uint.ts
51 > export function toUint32(v: number): number {
52 if (v < 0) {
53 return 0;
src/vs/base/common/lazy.ts 39 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lazy.ts
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 > enum LazyValueState {
7 > Uninitialized,
8 > Running,
9 > Completed,
10 > }
11 >
12 > export class Lazy<T> {
13 >
14 > private _state = LazyValueState.Uninitialized;
15 > private _value?: T;
16 > private _error: Error | undefined;
17 >
18 > constructor(
19 > private readonly executor: () => T, lazy.ts
20 > ) { }
21 > lazy.ts
22 > /**
23 > * True if the lazy value has been resolved.
24 > */
25 > get hasValue(): boolean { return this._state === LazyValueState.Completed; }
26 >
27 > /**
28 > * Get the wrapped value.
29 > *
30 > * This will force evaluation of the lazy value if it has not been resolved yet. Lazy values are only
31 > * resolved once. `getValue` will re-throw exceptions that are hit while resolving the value
32 > */
33 > get value(): T {
34 if (this._state === LazyValueState.Uninitialized) {
35 this._state = LazyValueState.Running;
50 return this._value!;
51 }
52 > lazy.ts
53 > /**
54 > * Get the wrapped value without forcing evaluation.
55 > */
56 > get rawValue(): T | undefined { return this._value; }
57 > }
src/vs/platform/product/common/product.ts 39 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- product.ts
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 { env } from '../../../base/common/process.js';
7 > import { IProductConfiguration } from '../../../base/common/product.js';
8 > import { ISandboxConfiguration } from '../../../base/parts/sandbox/common/sandboxTypes.js';
9 >
10 > /**
11 > * @deprecated It is preferred that you use `IProductService` if you can. This
12 > * allows web embedders to override our defaults. But for things like `product.quality`,
13 > * the use is fine because that property is not overridable.
14 > */
15 > let product: IProductConfiguration;
16 >
17 > // Native sandbox environment
18 > const vscodeGlobal = (globalThis as { vscode?: { context?: { configuration(): ISandboxConfiguration | undefined } } }).vscode;
19 > if (typeof vscodeGlobal !== 'undefined' && typeof vscodeGlobal.context !== 'undefined') {
20 const configuration: ISandboxConfiguration | undefined = vscodeGlobal.context.configuration();
21 if (configuration) {
25 }
26 }
27 > // _VSCODE environment product.ts
28 > else if (globalThis._VSCODE_PRODUCT_JSON && globalThis._VSCODE_PACKAGE_JSON) {
29 > // Obtain values from product.json and package.json-data
30 > product = globalThis._VSCODE_PRODUCT_JSON as unknown as IProductConfiguration;
31 >
32 > // Running out of sources
33 > if (env['VSCODE_DEV']) {
34 Object.assign(product, {
35 nameShort: `${product.nameShort} Dev`,
39 });
40 }
41 > product.ts
42 > // Version is added during built time, but we still
43 > // want to have it running out of sources so we
44 > // read it from package.json only when we need it.
45 > if (!product.version) {
46 > const pkg = globalThis._VSCODE_PACKAGE_JSON as { version: string };
47 >
48 > Object.assign(product, {
49 > version: pkg.version
50 > });
51 > }
52 }
53
90 }
91 }
92 > product.ts
93 > export default product;
src/vs/workbench/common/editor/editorModel.ts 35 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editorModel.ts
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 { Emitter } from '../../../base/common/event.js';
7 > import { Disposable } from '../../../base/common/lifecycle.js';
8 >
9 > /**
10 > * The editor model is the heavyweight counterpart of editor input. Depending on the editor input, it
11 > * resolves from a file system retrieve content and may allow for saving it back or reverting it.
12 > * Editor models are typically cached for some while because they are expensive to construct.
13 > */
14 > export class EditorModel extends Disposable {
15
16 private readonly _onWillDispose = this._register(new Emitter<void>());
18
19 private resolved = false;
21 > /**
22 > * Causes this model to resolve returning a promise when loading is completed.
23 > */
24 > async resolve(): Promise<void> {
25 this.resolved = true;
26 }
28 > /**
29 > * Returns whether this model was loaded or not.
30 > */
31 > isResolved(): boolean {
32 return this.resolved;
33 }
35 > /**
36 > * Find out if this model has been disposed.
37 > */
38 > isDisposed(): boolean {
39 return this._store.isDisposed;
40 }
42 > /**
43 > * Subclasses should implement to free resources that have been claimed through loading.
44 > */
45 > override dispose(): void {
46 this._onWillDispose.fire();
47
48 super.dispose();
49 }
src/vs/base/common/marshallingIds.ts 33 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- marshallingIds.ts
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 > export const enum MarshalledId {
7 > Uri = 1,
8 > Regexp,
9 > ScmResource,
10 > ScmResourceGroup,
11 > ScmProvider,
12 > CommentController,
13 > CommentThread,
14 > CommentThreadInstance,
15 > CommentThreadReply,
16 > CommentNode,
17 > CommentThreadNode,
18 > TimelineActionContext,
19 > NotebookCellActionContext,
20 > NotebookActionContext,
21 > TerminalContext,
22 > TestItemContext,
23 > Date,
24 > TestMessageMenuArgs,
25 > ChatViewContext,
26 > LanguageModelToolResult,
27 > LanguageModelTextPart,
28 > LanguageModelThinkingPart,
29 > LanguageModelPromptTsxPart,
30 > LanguageModelDataPart,
31 > AgentSessionContext,
32 > ChatResponsePullRequestPart,
33 > }
src/vs/base/common/severity.ts 32 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- severity.ts
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 * as strings from './strings.js';
7 >
8 > enum Severity {
9 > Ignore = 0,
10 > Info = 1,
11 > Warning = 2,
12 > Error = 3
13 > }
14 >
15 > namespace Severity {
16 >
17 > const _error = 'error';
18 > const _warning = 'warning';
19 > const _warn = 'warn';
20 > const _info = 'info';
21 > const _ignore = 'ignore';
22 >
23 > /**
24 > * Parses 'error', 'warning', 'warn', 'info' in call casings
25 > * and falls back to ignore.
26 > */
27 > export function fromValue(value: string): Severity {
28 if (!value) {
29 return Severity.Ignore;
43 return Severity.Ignore;
44 }
46 > export function toString(severity: Severity): string {
47 switch (severity) {
48 case Severity.Error: return _error;
52 }
53 }
54 > } severity.ts
55 >
56 > export default Severity;
src/vs/base/common/errorMessage.ts 29 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- errorMessage.ts
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 * as arrays from './arrays.js';
7 > import * as types from './types.js';
8 > import * as nls from '../../nls.js';
9 > import { IAction } from './actions.js';
10 >
11 function exceptionToErrorMessage(exception: any, verbose: boolean): string {
12 if (verbose && (exception.stack || exception.stacktrace)) {
16 return detectSystemErrorMessage(exception);
17 }
19 function stackToString(stack: string[] | string | undefined): string | undefined {
20 if (Array.isArray(stack)) {
24 return stack;
25 }
27 function detectSystemErrorMessage(exception: any): string {
28
39 return exception.message || nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details.");
40 }
42 > /**
43 > * Tries to generate a human readable error message out of the error. If the verbose parameter
44 > * is set to true, the error message will include stacktrace details if provided.
45 > *
46 > * @returns A string containing the error message.
47 > */
48 > export function toErrorMessage(error: any = null, verbose: boolean = false): string {
49 if (!error) {
50 return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details.");
88 return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details.");
89 }
91 >
92 > export interface IErrorWithActions extends Error {
93 > actions: IAction[];
94 > }
95 >
96 > export function isErrorWithActions(obj: unknown): obj is IErrorWithActions {
97 const candidate = obj as IErrorWithActions | undefined;
98
99 return candidate instanceof Error && Array.isArray(candidate.actions);
100 }
102 > export function createErrorWithActions(messageOrError: string | Error, actions: IAction[]): IErrorWithActions {
103 let error: IErrorWithActions;
104 if (typeof messageOrError === 'string') {
src/vs/base/common/stopwatch.ts 25 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- stopwatch.ts
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 > declare const globalThis: { performance: { now(): number } };
7 > const performanceNow = globalThis.performance.now.bind(globalThis.performance);
8 >
9 > export class StopWatch {
10 >
11 > private _startTime: number;
12 > private _stopTime: number;
13 >
14 > private readonly _now: () => number;
15 >
16 > public static create(highResolution?: boolean): StopWatch {
17 return new StopWatch(highResolution);
18 }
20 > constructor(highResolution?: boolean) {
21 this._now = highResolution === false ? Date.now : performanceNow;
22 this._startTime = this._now();
23 this._stopTime = -1;
24 }
26 > public stop(): void {
27 this._stopTime = this._now();
28 }
30 > public reset(): void {
31 this._startTime = this._now();
32 this._stopTime = -1;
33 }
35 > public elapsed(): number {
36 if (this._stopTime !== -1) {
37 return this._stopTime - this._startTime;
39 return this._now() - this._startTime;
40 }
41 > } stopwatch.ts
src/vs/base/common/functional.ts 9 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- functional.ts
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 > /**
7 > * Given a function, returns a function that is only calling that function once.
8 > */
9 > export function createSingleCallFunction<T extends Function>(this: unknown, fn: T, fnDidRunCallback?: () => void): T {
10 const _this = this;
11 let didCall = false;