Atlas › Test

debugModel.test|title=DebugModel InstructionBreakpoint removeInstructionBreakpoints with only address removes the matching entry and leaves others|occurrence=1

Exact test identity: mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/contrib/debug/test/common/debugModel.test|title=DebugModel InstructionBreakpoint removeInstructionBreakpoints with only address removes the matching entry and leaves others|occurrence=1

Package
mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/contrib/debug/test/common
Suite / test hierarchy
debugModel.test|title=DebugModel InstructionBreakpoint removeInstructionBreakpoints with only address removes the matching entry and leaves others|occurrence=1
Test
debugModel.test|title=DebugModel InstructionBreakpoint removeInstructionBreakpoints with only address removes the matching entry and leaves others|occurrence=1
Introduced at
debugModel.ts ×1 Frontier kind: Joint frontier
Covered ranges
3388
Covered lines
31718
Covered files
156

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/contrib/debug/common/debug.ts 1431 covered LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debug.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 { IAction } from '../../../../base/common/actions.js';
7 > import { VSBuffer } from '../../../../base/common/buffer.js';
8 > import { CancellationToken } from '../../../../base/common/cancellation.js';
9 > import { Color } from '../../../../base/common/color.js';
10 > import { Event } from '../../../../base/common/event.js';
11 > import { IJSONSchema, IJSONSchemaSnippet } from '../../../../base/common/jsonSchema.js';
12 > import { IDisposable } from '../../../../base/common/lifecycle.js';
13 > import severity from '../../../../base/common/severity.js';
14 > import { URI, UriComponents, URI as uri } from '../../../../base/common/uri.js';
15 > import { IPosition, Position } from '../../../../editor/common/core/position.js';
16 > import { IRange } from '../../../../editor/common/core/range.js';
17 > import * as editorCommon from '../../../../editor/common/editorCommon.js';
18 > import { ITextModel as EditorIModel } from '../../../../editor/common/model.js';
19 > import * as nls from '../../../../nls.js';
20 > import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js';
21 > import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
22 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
23 > import { ITelemetryEndpoint } from '../../../../platform/telemetry/common/telemetry.js';
24 > import { IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js';
25 > import { IEditorPane } from '../../../common/editor.js';
26 > import { DebugCompoundRoot } from './debugCompoundRoot.js';
27 > import { IDataBreakpointOptions, IFunctionBreakpointOptions, IInstructionBreakpointOptions } from './debugModel.js';
28 > import { Source } from './debugSource.js';
29 > import { ITaskIdentifier } from '../../tasks/common/tasks.js';
30 > import { LiveTestResult } from '../../testing/common/testResult.js';
31 > import { IEditorService } from '../../../services/editor/common/editorService.js';
32 > import { IView } from '../../../common/views.js';
33 >
34 > export const VIEWLET_ID = 'workbench.view.debug';
35 >
36 > export const VARIABLES_VIEW_ID = 'workbench.debug.variablesView';
37 > export const WATCH_VIEW_ID = 'workbench.debug.watchExpressionsView';
38 > export const CALLSTACK_VIEW_ID = 'workbench.debug.callStackView';
39 > export const LOADED_SCRIPTS_VIEW_ID = 'workbench.debug.loadedScriptsView';
40 > export const BREAKPOINTS_VIEW_ID = 'workbench.debug.breakPointsView';
41 > export const DISASSEMBLY_VIEW_ID = 'workbench.debug.disassemblyView';
42 > export const DEBUG_PANEL_ID = 'workbench.panel.repl';
43 > export const REPL_VIEW_ID = 'workbench.panel.repl.view';
44 > export const CONTEXT_DEBUG_TYPE = new RawContextKey<string>('debugType', undefined, { type: 'string', description: nls.localize('debugType', "Debug type of the active debug session. For example 'python'.") });
45 > export const CONTEXT_DEBUG_CONFIGURATION_TYPE = new RawContextKey<string>('debugConfigurationType', undefined, { type: 'string', description: nls.localize('debugConfigurationType', "Debug type of the selected launch configuration. For example 'python'.") });
46 > export const CONTEXT_DEBUG_STATE = new RawContextKey<string>('debugState', 'inactive', { type: 'string', description: nls.localize('debugState', "State that the focused debug session is in. One of the following: 'inactive', 'initializing', 'stopped' or 'running'.") });
47 > export const CONTEXT_DEBUG_UX_KEY = 'debugUx';
48 > export const CONTEXT_DEBUG_UX = new RawContextKey<string>(CONTEXT_DEBUG_UX_KEY, 'default', { type: 'string', description: nls.localize('debugUX', "Debug UX state. When there are no debug configurations it is 'simple', otherwise 'default'. Used to decide when to show welcome views in the debug viewlet.") });
49 > export const CONTEXT_HAS_DEBUGGED = new RawContextKey<boolean>('hasDebugged', false, { type: 'boolean', description: nls.localize('hasDebugged', "True when a debug session has been started at least once, false otherwise.") });
50 > export const CONTEXT_IN_DEBUG_MODE = new RawContextKey<boolean>('inDebugMode', false, { type: 'boolean', description: nls.localize('inDebugMode', "True when debugging, false otherwise.") });
51 > export const CONTEXT_IN_DEBUG_REPL = new RawContextKey<boolean>('inDebugRepl', false, { type: 'boolean', description: nls.localize('inDebugRepl', "True when focus is in the debug console, false otherwise.") });
52 > export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey<boolean>('breakpointWidgetVisible', false, { type: 'boolean', description: nls.localize('breakpointWidgetVisibile', "True when breakpoint editor zone widget is visible, false otherwise.") });
53 > export const CONTEXT_IN_BREAKPOINT_WIDGET = new RawContextKey<boolean>('inBreakpointWidget', false, { type: 'boolean', description: nls.localize('inBreakpointWidget', "True when focus is in the breakpoint editor zone widget, false otherwise.") });
54 > export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey<boolean>('breakpointsFocused', true, { type: 'boolean', description: nls.localize('breakpointsFocused', "True when the BREAKPOINTS view is focused, false otherwise.") });
55 > export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey<boolean>('watchExpressionsFocused', true, { type: 'boolean', description: nls.localize('watchExpressionsFocused', "True when the WATCH view is focused, false otherwise.") });
56 > export const CONTEXT_WATCH_EXPRESSIONS_EXIST = new RawContextKey<boolean>('watchExpressionsExist', false, { type: 'boolean', description: nls.localize('watchExpressionsExist', "True when at least one watch expression exists, false otherwise.") });
57 > export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey<boolean>('variablesFocused', true, { type: 'boolean', description: nls.localize('variablesFocused', "True when the VARIABLES views is focused, false otherwise") });
58 > export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey<boolean>('expressionSelected', false, { type: 'boolean', description: nls.localize('expressionSelected', "True when an expression input box is open in either the WATCH or the VARIABLES view, false otherwise.") });
59 > export const CONTEXT_BREAKPOINT_INPUT_FOCUSED = new RawContextKey<boolean>('breakpointInputFocused', false, { type: 'boolean', description: nls.localize('breakpointInputFocused', "True when the input box has focus in the BREAKPOINTS view.") });
60 > export const CONTEXT_CALLSTACK_ITEM_TYPE = new RawContextKey<string>('callStackItemType', undefined, { type: 'string', description: nls.localize('callStackItemType', "Represents the item type of the focused element in the CALL STACK view. For example: 'session', 'thread', 'stackFrame'") });
61 > export const CONTEXT_CALLSTACK_SESSION_IS_ATTACH = new RawContextKey<boolean>('callStackSessionIsAttach', false, { type: 'boolean', description: nls.localize('callStackSessionIsAttach', "True when the session in the CALL STACK view is attach, false otherwise. Used internally for inline menus in the CALL STACK view.") });
62 > export const CONTEXT_CALLSTACK_ITEM_STOPPED = new RawContextKey<boolean>('callStackItemStopped', false, { type: 'boolean', description: nls.localize('callStackItemStopped', "True when the focused item in the CALL STACK is stopped. Used internally for inline menus in the CALL STACK view.") });
63 > export const CONTEXT_CALLSTACK_SESSION_HAS_ONE_THREAD = new RawContextKey<boolean>('callStackSessionHasOneThread', false, { type: 'boolean', description: nls.localize('callStackSessionHasOneThread', "True when the focused session in the CALL STACK view has exactly one thread. Used internally for inline menus in the CALL STACK view.") });
64 > export const CONTEXT_CALLSTACK_FOCUSED = new RawContextKey<boolean>('callStackFocused', true, { type: 'boolean', description: nls.localize('callStackFocused', "True when the CALLSTACK view is focused, false otherwise.") });
65 > export const CONTEXT_WATCH_ITEM_TYPE = new RawContextKey<string>('watchItemType', undefined, { type: 'string', description: nls.localize('watchItemType', "Represents the item type of the focused element in the WATCH view. For example: 'expression', 'variable'") });
66 > export const CONTEXT_CAN_VIEW_MEMORY = new RawContextKey<boolean>('canViewMemory', undefined, { type: 'boolean', description: nls.localize('canViewMemory', "Indicates whether the item in the view has an associated memory reference.") });
67 > export const CONTEXT_BREAKPOINT_ITEM_TYPE = new RawContextKey<string>('breakpointItemType', undefined, { type: 'string', description: nls.localize('breakpointItemType', "Represents the item type of the focused element in the BREAKPOINTS view. For example: 'breakpoint', 'exceptionBreakpoint', 'functionBreakpoint', 'dataBreakpoint'") });
68 > export const CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES = new RawContextKey<boolean>('breakpointItemBytes', undefined, { type: 'boolean', description: nls.localize('breakpointItemIsDataBytes', "Whether the breakpoint item is a data breakpoint on a byte range.") });
69 > export const CONTEXT_BREAKPOINT_HAS_MODES = new RawContextKey<boolean>('breakpointHasModes', false, { type: 'boolean', description: nls.localize('breakpointHasModes', "Whether the breakpoint has multiple modes it can switch to.") });
70 > export const CONTEXT_BREAKPOINT_SUPPORTS_CONDITION = new RawContextKey<boolean>('breakpointSupportsCondition', false, { type: 'boolean', description: nls.localize('breakpointSupportsCondition', "True when the focused breakpoint supports conditions.") });
71 > export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey<boolean>('loadedScriptsSupported', false, { type: 'boolean', description: nls.localize('loadedScriptsSupported', "True when the focused sessions supports the LOADED SCRIPTS view") });
72 > export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey<string>('loadedScriptsItemType', undefined, { type: 'string', description: nls.localize('loadedScriptsItemType', "Represents the item type of the focused element in the LOADED SCRIPTS view.") });
73 > export const CONTEXT_FOCUSED_SESSION_IS_ATTACH = new RawContextKey<boolean>('focusedSessionIsAttach', false, { type: 'boolean', description: nls.localize('focusedSessionIsAttach', "True when the focused session is 'attach'.") });
74 > export const CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG = new RawContextKey<boolean>('focusedSessionIsNoDebug', false, { type: 'boolean', description: nls.localize('focusedSessionIsNoDebug', "True when the focused session is run without debugging.") });
75 > export const CONTEXT_STEP_BACK_SUPPORTED = new RawContextKey<boolean>('stepBackSupported', false, { type: 'boolean', description: nls.localize('stepBackSupported', "True when the focused session supports 'stepBack' requests.") });
76 > export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey<boolean>('restartFrameSupported', false, { type: 'boolean', description: nls.localize('restartFrameSupported', "True when the focused session supports 'restartFrame' requests.") });
77 > export const CONTEXT_STACK_FRAME_SUPPORTS_RESTART = new RawContextKey<boolean>('stackFrameSupportsRestart', false, { type: 'boolean', description: nls.localize('stackFrameSupportsRestart', "True when the focused stack frame supports 'restartFrame'.") });
78 > export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey<boolean>('jumpToCursorSupported', false, { type: 'boolean', description: nls.localize('jumpToCursorSupported', "True when the focused session supports 'jumpToCursor' request.") });
79 > export const CONTEXT_STEP_INTO_TARGETS_SUPPORTED = new RawContextKey<boolean>('stepIntoTargetsSupported', false, { type: 'boolean', description: nls.localize('stepIntoTargetsSupported', "True when the focused session supports 'stepIntoTargets' request.") });
80 > export const CONTEXT_BREAKPOINTS_EXIST = new RawContextKey<boolean>('breakpointsExist', false, { type: 'boolean', description: nls.localize('breakpointsExist', "True when at least one breakpoint exists.") });
81 > export const CONTEXT_DEBUGGERS_AVAILABLE = new RawContextKey<boolean>('debuggersAvailable', false, { type: 'boolean', description: nls.localize('debuggersAvailable', "True when there is at least one debug extensions active.") });
82 > export const CONTEXT_DEBUG_EXTENSION_AVAILABLE = new RawContextKey<boolean>('debugExtensionAvailable', true, { type: 'boolean', description: nls.localize('debugExtensionsAvailable', "True when there is at least one debug extension installed and enabled.") });
83 > export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey<string>('debugProtocolVariableMenuContext', undefined, { type: 'string', description: nls.localize('debugProtocolVariableMenuContext', "Represents the context the debug adapter sets on the focused variable in the VARIABLES view.") });
84 > export const CONTEXT_SET_VARIABLE_SUPPORTED = new RawContextKey<boolean>('debugSetVariableSupported', false, { type: 'boolean', description: nls.localize('debugSetVariableSupported', "True when the focused session supports 'setVariable' request.") });
85 > export const CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED = new RawContextKey<boolean>('debugSetDataBreakpointAddressSupported', false, { type: 'boolean', description: nls.localize('debugSetDataBreakpointAddressSupported', "True when the focused session supports 'getBreakpointInfo' request on an address.") });
86 > export const CONTEXT_SET_EXPRESSION_SUPPORTED = new RawContextKey<boolean>('debugSetExpressionSupported', false, { type: 'boolean', description: nls.localize('debugSetExpressionSupported', "True when the focused session supports 'setExpression' request.") });
87 > export const CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED = new RawContextKey<boolean>('breakWhenValueChangesSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueChangesSupported', "True when the focused session supports to break when value changes.") });
88 > export const CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED = new RawContextKey<boolean>('breakWhenValueIsAccessedSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueIsAccessedSupported', "True when the focused breakpoint supports to break when value is accessed.") });
89 > export const CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED = new RawContextKey<boolean>('breakWhenValueIsReadSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueIsReadSupported', "True when the focused breakpoint supports to break when value is read.") });
90 > export const CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED = new RawContextKey<boolean>('terminateDebuggeeSupported', false, { type: 'boolean', description: nls.localize('terminateDebuggeeSupported', "True when the focused session supports the terminate debuggee capability.") });
91 > export const CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED = new RawContextKey<boolean>('suspendDebuggeeSupported', false, { type: 'boolean', description: nls.localize('suspendDebuggeeSupported', "True when the focused session supports the suspend debuggee capability.") });
92 > export const CONTEXT_TERMINATE_THREADS_SUPPORTED = new RawContextKey<boolean>('terminateThreadsSupported', false, { type: 'boolean', description: nls.localize('terminateThreadsSupported', "True when the focused session supports the terminate threads capability.") });
93 > export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey<boolean>('variableEvaluateNamePresent', false, { type: 'boolean', description: nls.localize('variableEvaluateNamePresent', "True when the focused variable has an 'evaluateName' field set.") });
94 > export const CONTEXT_VARIABLE_IS_READONLY = new RawContextKey<boolean>('variableIsReadonly', false, { type: 'boolean', description: nls.localize('variableIsReadonly', "True when the focused variable is read-only.") });
95 > export const CONTEXT_VARIABLE_VALUE = new RawContextKey<boolean>('variableValue', false, { type: 'string', description: nls.localize('variableValue', "Value of the variable, present for debug visualization clauses.") });
96 > export const CONTEXT_VARIABLE_TYPE = new RawContextKey<boolean>('variableType', false, { type: 'string', description: nls.localize('variableType', "Type of the variable, present for debug visualization clauses.") });
97 > export const CONTEXT_VARIABLE_INTERFACES = new RawContextKey<boolean>('variableInterfaces', false, { type: 'array', description: nls.localize('variableInterfaces', "Any interfaces or contracts that the variable satisfies, present for debug visualization clauses.") });
98 > export const CONTEXT_VARIABLE_NAME = new RawContextKey<boolean>('variableName', false, { type: 'string', description: nls.localize('variableName', "Name of the variable, present for debug visualization clauses.") });
99 > export const CONTEXT_VARIABLE_LANGUAGE = new RawContextKey<boolean>('variableLanguage', false, { type: 'string', description: nls.localize('variableLanguage', "Language of the variable source, present for debug visualization clauses.") });
100 > export const CONTEXT_VARIABLE_EXTENSIONID = new RawContextKey<boolean>('variableExtensionId', false, { type: 'string', description: nls.localize('variableExtensionId', "Extension ID of the variable source, present for debug visualization clauses.") });
101 > export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey<boolean>('exceptionWidgetVisible', false, { type: 'boolean', description: nls.localize('exceptionWidgetVisible', "True when the exception widget is visible.") });
102 > export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey<boolean>('multiSessionRepl', false, { type: 'boolean', description: nls.localize('multiSessionRepl', "True when there is more than 1 debug console.") });
103 > export const CONTEXT_MULTI_SESSION_DEBUG = new RawContextKey<boolean>('multiSessionDebug', false, { type: 'boolean', description: nls.localize('multiSessionDebug', "True when there is more than 1 active debug session.") });
104 > export const CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED = new RawContextKey<boolean>('disassembleRequestSupported', false, { type: 'boolean', description: nls.localize('disassembleRequestSupported', "True when the focused sessions supports disassemble request.") });
105 > export const CONTEXT_DISASSEMBLY_VIEW_FOCUS = new RawContextKey<boolean>('disassemblyViewFocus', false, { type: 'boolean', description: nls.localize('disassemblyViewFocus', "True when the Disassembly View is focused.") });
106 > export const CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST = new RawContextKey<boolean>('languageSupportsDisassembleRequest', false, { type: 'boolean', description: nls.localize('languageSupportsDisassembleRequest', "True when the language in the current editor supports disassemble request.") });
107 > export const CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE = new RawContextKey<boolean>('focusedStackFrameHasInstructionReference', false, { type: 'boolean', description: nls.localize('focusedStackFrameHasInstructionReference', "True when the focused stack frame has instruction pointer reference.") });
108 >
109 > export const debuggerDisabledMessage = (debugType: string) => nls.localize('debuggerDisabled', "Configured debug type '{0}' is installed but not supported in this environment.", debugType);
110 >
111 > export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
112 > export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint';
113 > export const DEBUG_SCHEME = 'debug';
114 > export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = {
115 > enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
116 > default: 'openOnFirstSessionStart',
117 > description: nls.localize('internalConsoleOptions', "Controls when the internal Debug Console should open.")
118 > };
119 >
120 > export interface IDebugViewWithVariables extends IView {
121 > readonly treeSelection: IExpression[];
122 > }
123 >
124 > // raw
125 >
126 > export interface IRawModelUpdate {
127 > sessionId: string;
128 > threads: DebugProtocol.Thread[];
129 > stoppedDetails?: IRawStoppedDetails;
130 > }
131 >
132 > export interface IRawStoppedDetails {
133 > reason?: string;
134 > description?: string;
135 > threadId?: number;
136 > text?: string;
137 > totalFrames?: number;
138 > allThreadsStopped?: boolean;
139 > preserveFocusHint?: boolean;
140 > framesErrorMessage?: string;
141 > hitBreakpointIds?: number[];
142 > }
143 >
144 > // model
145 >
146 > export interface ITreeElement {
147 > getId(): string;
148 > }
149 >
150 > export interface IReplElement extends ITreeElement {
151 > toString(includeSource?: boolean): string;
152 > readonly sourceData?: IReplElementSource;
153 > }
154 >
155 > export interface INestingReplElement extends IReplElement {
156 > readonly hasChildren: boolean;
157 > getChildren(): Promise<IReplElement[]> | IReplElement[];
158 > }
159 >
160 > export interface IReplElementSource {
161 > readonly source: Source;
162 > readonly lineNumber: number;
163 > readonly column: number;
164 > }
165 >
166 > export interface IExpressionValue {
167 > readonly value: string;
168 > readonly type?: string;
169 > valueChanged?: boolean;
170 > }
171 >
172 > export interface IExpressionContainer extends ITreeElement, IExpressionValue {
173 > readonly hasChildren: boolean;
174 > getSession(): IDebugSession | undefined;
175 > evaluateLazy(): Promise<void>;
176 > getChildren(): Promise<IExpression[]>;
177 > readonly reference?: number;
178 > readonly memoryReference?: string;
179 > readonly presentationHint?: DebugProtocol.VariablePresentationHint | undefined;
180 > readonly valueLocationReference?: number;
181 > }
182 >
183 > export interface IExpression extends IExpressionContainer {
184 > name: string;
185 > }
186 >
187 > export interface IDebugger {
188 > readonly type: string;
189 > createDebugAdapter(session: IDebugSession): Promise<IDebugAdapter>;
190 > runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
191 > startDebugging(args: IConfig, parentSessionId: string): Promise<boolean>;
192 > getCustomTelemetryEndpoint(): ITelemetryEndpoint | undefined;
193 > getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise<string>;
194 > }
195 >
196 > export interface IDebuggerMetadata {
197 > label: string;
198 > type: string;
199 > strings?: { [key in DebuggerString]: string };
200 > interestedInLanguage(languageId: string): boolean;
201 > }
202 >
203 > export const enum State {
204 > Inactive,
205 > Initializing,
206 > Stopped,
207 > Running
208 > }
209 >
210 > export function getStateLabel(state: State): string {
211 switch (state) {
212 case State.Initializing: return 'initializing';
216 }
217 }
218 > debug.ts
219 > export interface AdapterEndEvent {
220 > error?: Error;
221 > sessionLengthInSeconds: number;
222 > emittedStopped: boolean;
223 > }
224 >
225 > export interface LoadedSourceEvent {
226 > reason: 'new' | 'changed' | 'removed';
227 > source: Source;
228 > }
229 >
230 > export type IDebugSessionReplMode = 'separate' | 'mergeWithParent';
231 >
232 > export interface IDebugTestRunReference {
233 > runId: string;
234 > taskId: string;
235 > }
236 >
237 > export interface IDebugSessionOptions {
238 > noDebug?: boolean;
239 > parentSession?: IDebugSession;
240 > lifecycleManagedByParent?: boolean;
241 > repl?: IDebugSessionReplMode;
242 > compoundRoot?: DebugCompoundRoot;
243 > compact?: boolean;
244 > startedByUser?: boolean;
245 > saveBeforeRestart?: boolean;
246 > suppressDebugToolbar?: boolean;
247 > suppressDebugStatusbar?: boolean;
248 > suppressDebugView?: boolean;
249 > /**
250 > * Set if the debug session is correlated with a test run. Stopping/restarting
251 > * the session will instead stop/restart the test run.
252 > */
253 > testRun?: IDebugTestRunReference;
254 > }
255 >
256 > export interface IDataBreakpointInfoResponse {
257 > dataId: string | null;
258 > description: string;
259 > canPersist?: boolean;
260 > accessTypes?: DebugProtocol.DataBreakpointAccessType[];
261 > }
262 >
263 > export interface IMemoryInvalidationEvent {
264 > fromOffset: number;
265 > toOffset: number;
266 > }
267 >
268 > export const enum MemoryRangeType {
269 > Valid,
270 > Unreadable,
271 > Error,
272 > }
273 >
274 > export interface IMemoryRange {
275 > type: MemoryRangeType;
276 > offset: number;
277 > length: number;
278 > }
279 >
280 > export interface IValidMemoryRange extends IMemoryRange {
281 > type: MemoryRangeType.Valid;
282 > offset: number;
283 > length: number;
284 > data: VSBuffer;
285 > }
286 >
287 > export interface IUnreadableMemoryRange extends IMemoryRange {
288 > type: MemoryRangeType.Unreadable;
289 > }
290 >
291 > export interface IErrorMemoryRange extends IMemoryRange {
292 > type: MemoryRangeType.Error;
293 > error: string;
294 > }
295 >
296 > /**
297 > * Union type of memory that can be returned from read(). Since a read request
298 > * could encompass multiple previously-read ranges, multiple of these types
299 > * are possible to return.
300 > */
301 > export type MemoryRange = IValidMemoryRange | IUnreadableMemoryRange | IErrorMemoryRange;
302 >
303 > export const DEBUG_MEMORY_SCHEME = 'vscode-debug-memory';
304 >
305 > /**
306 > * An IMemoryRegion corresponds to a contiguous range of memory referred to
307 > * by a DAP `memoryReference`.
308 > */
309 > export interface IMemoryRegion extends IDisposable {
310 > /**
311 > * Event that fires when memory changes. Can be a result of memory events or
312 > * `write` requests.
313 > */
314 > readonly onDidInvalidate: Event<IMemoryInvalidationEvent>;
315 >
316 > /**
317 > * Whether writes are supported on this memory region.
318 > */
319 > readonly writable: boolean;
320 >
321 > /**
322 > * Requests memory ranges from the debug adapter. It returns a list of memory
323 > * ranges that overlap (but may exceed!) the given offset. Use the `offset`
324 > * and `length` of each range for display.
325 > */
326 > read(fromOffset: number, toOffset: number): Promise<MemoryRange[]>;
327 >
328 > /**
329 > * Writes memory to the debug adapter at the given offset.
330 > */
331 > write(offset: number, data: VSBuffer): Promise<number>;
332 > }
333 >
334 > /** Data that can be inserted in {@link IDebugSession.appendToRepl} */
335 > export interface INewReplElementData {
336 > /**
337 > * Output string to display
338 > */
339 > output: string;
340 >
341 > /**
342 > * Expression data to display. Will result in the item being expandable in
343 > * the REPL. Its value will be used if {@link output} is not provided.
344 > */
345 > expression?: IExpression;
346 >
347 > /**
348 > * Output severity.
349 > */
350 > sev: severity;
351 >
352 > /**
353 > * Originating location.
354 > */
355 > source?: IReplElementSource;
356 > }
357 >
358 > export interface IDebugEvaluatePosition {
359 > line: number;
360 > column: number;
361 > source: DebugProtocol.Source;
362 > }
363 >
364 > export interface IDebugLocationReferenced {
365 > line: number;
366 > column: number;
367 > endLine?: number;
368 > endColumn?: number;
369 > source: Source;
370 > }
371 >
372 > export interface IDebugSession extends ITreeElement, IDisposable {
373 >
374 > readonly configuration: IConfig;
375 > readonly unresolvedConfiguration: IConfig | undefined;
376 > readonly state: State;
377 > readonly root: IWorkspaceFolder | undefined;
378 > readonly parentSession: IDebugSession | undefined;
379 > readonly subId: string | undefined;
380 > readonly compact: boolean;
381 > readonly compoundRoot: DebugCompoundRoot | undefined;
382 > readonly saveBeforeRestart: boolean;
383 > readonly name: string;
384 > readonly autoExpandLazyVariables: boolean;
385 > readonly suppressDebugToolbar: boolean;
386 > readonly suppressDebugStatusbar: boolean;
387 > readonly suppressDebugView: boolean;
388 > readonly lifecycleManagedByParent: boolean;
389 > /** Test run this debug session was spawned by */
390 > readonly correlatedTestRun?: LiveTestResult;
391 >
392 > setSubId(subId: string | undefined): void;
393 >
394 > getMemory(memoryReference: string): IMemoryRegion;
395 >
396 > setName(name: string): void;
397 > readonly onDidChangeName: Event<string>;
398 > getLabel(): string;
399 >
400 > getSourceForUri(modelUri: uri): Source | undefined;
401 > getSource(raw?: DebugProtocol.Source): Source;
402 >
403 > setConfiguration(configuration: { resolved: IConfig; unresolved: IConfig | undefined }): void;
404 > rawUpdate(data: IRawModelUpdate): void;
405 >
406 > getThread(threadId: number): IThread | undefined;
407 > getAllThreads(): IThread[];
408 > clearThreads(removeThreads: boolean, reference?: number): void;
409 > getStoppedDetails(): IRawStoppedDetails | undefined;
410 >
411 > getReplElements(): IReplElement[];
412 > hasSeparateRepl(): boolean;
413 > removeReplExpressions(): void;
414 > addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise<void>;
415 > appendToRepl(data: INewReplElementData): void;
416 > /** Cancel any associated test run set through the DebugSessionOptions */
417 > cancelCorrelatedTestRun(): void;
418 >
419 > // session events
420 > readonly onDidEndAdapter: Event<AdapterEndEvent | undefined>;
421 > readonly onDidChangeState: Event<void>;
422 > readonly onDidChangeReplElements: Event<IReplElement | undefined>;
423 >
424 > /** DA capabilities. Set only when there is a running session available. */
425 > readonly capabilities: DebugProtocol.Capabilities;
426 > /** DA capabilities. These are retained on the session even after is implementation ends. */
427 > readonly rememberedCapabilities?: DebugProtocol.Capabilities;
428 >
429 > // DAP events
430 >
431 > readonly onDidLoadedSource: Event<LoadedSourceEvent>;
432 > readonly onDidCustomEvent: Event<DebugProtocol.Event>;
433 > readonly onDidProgressStart: Event<DebugProtocol.ProgressStartEvent>;
434 > readonly onDidProgressUpdate: Event<DebugProtocol.ProgressUpdateEvent>;
435 > readonly onDidProgressEnd: Event<DebugProtocol.ProgressEndEvent>;
436 > readonly onDidInvalidateMemory: Event<DebugProtocol.MemoryEvent>;
437 >
438 > // DAP request
439 >
440 > initialize(dbgr: IDebugger): Promise<void>;
441 > launchOrAttach(config: IConfig): Promise<void>;
442 > restart(): Promise<void>;
443 > terminate(restart?: boolean /* false */): Promise<void>;
444 > disconnect(restart?: boolean /* false */, suspend?: boolean): Promise<void>;
445 >
446 > sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise<void>;
447 > sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise<void>;
448 > dataBreakpointInfo(name: string, variablesReference?: number, frameId?: number): Promise<IDataBreakpointInfoResponse | undefined>;
449 > dataBytesBreakpointInfo(address: string, bytes: number): Promise<IDataBreakpointInfoResponse | undefined>;
450 > sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise<void>;
451 > sendInstructionBreakpoints(dbps: IInstructionBreakpoint[]): Promise<void>;
452 > sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void>;
453 > breakpointsLocations(uri: uri, lineNumber: number): Promise<IPosition[]>;
454 > getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined;
455 > resolveLocationReference(locationReference: number): Promise<IDebugLocationReferenced>;
456 >
457 > stackTrace(threadId: number, startFrame: number, levels: number, token: CancellationToken): Promise<DebugProtocol.StackTraceResponse | undefined>;
458 > exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined>;
459 > scopes(frameId: number, threadId: number): Promise<DebugProtocol.ScopesResponse | undefined>;
460 > variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse | undefined>;
461 > evaluate(expression: string, frameId?: number, context?: string, location?: IDebugEvaluatePosition): Promise<DebugProtocol.EvaluateResponse | undefined>;
462 > customRequest(request: string, args: unknown): Promise<DebugProtocol.Response | undefined>;
463 > cancel(progressId: string): Promise<DebugProtocol.CancelResponse | undefined>;
464 > disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise<DebugProtocol.DisassembledInstruction[] | undefined>;
465 > readMemory(memoryReference: string, offset: number, count: number): Promise<DebugProtocol.ReadMemoryResponse | undefined>;
466 > writeMemory(memoryReference: string, offset: number, data: string, allowPartial?: boolean): Promise<DebugProtocol.WriteMemoryResponse | undefined>;
467 >
468 > restartFrame(frameId: number, threadId: number): Promise<void>;
469 > next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
470 > stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
471 > stepInTargets(frameId: number): Promise<DebugProtocol.StepInTarget[] | undefined>;
472 > stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
473 > stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
474 > continue(threadId: number): Promise<void>;
475 > reverseContinue(threadId: number): Promise<void>;
476 > pause(threadId: number): Promise<void>;
477 > terminateThreads(threadIds: number[]): Promise<void>;
478 >
479 > completions(frameId: number | undefined, threadId: number, text: string, position: Position, token: CancellationToken): Promise<DebugProtocol.CompletionsResponse | undefined>;
480 > setVariable(variablesReference: number | undefined, name: string, value: string): Promise<DebugProtocol.SetVariableResponse | undefined>;
481 > setExpression(frameId: number, expression: string, value: string): Promise<DebugProtocol.SetExpressionResponse | undefined>;
482 > loadSource(resource: uri): Promise<DebugProtocol.SourceResponse | undefined>;
483 > getLoadedSources(): Promise<Source[]>;
484 >
485 > gotoTargets(source: DebugProtocol.Source, line: number, column?: number): Promise<DebugProtocol.GotoTargetsResponse | undefined>;
486 > goto(threadId: number, targetId: number): Promise<DebugProtocol.GotoResponse | undefined>;
487 > }
488 >
489 > export interface IThread extends ITreeElement {
490 >
491 > /**
492 > * Process the thread belongs to
493 > */
494 > readonly session: IDebugSession;
495 >
496 > /**
497 > * Id of the thread generated by the debug adapter backend.
498 > */
499 > readonly threadId: number;
500 >
501 > /**
502 > * Name of the thread.
503 > */
504 > readonly name: string;
505 >
506 > /**
507 > * Information about the current thread stop event. Undefined if thread is not stopped.
508 > */
509 > readonly stoppedDetails: IRawStoppedDetails | undefined;
510 >
511 > /**
512 > * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise undefined.
513 > */
514 > readonly exceptionInfo: Promise<IExceptionInfo | undefined>;
515 >
516 > readonly stateLabel: string;
517 >
518 > /**
519 > * Gets the callstack if it has already been received from the debug
520 > * adapter.
521 > */
522 > getCallStack(): ReadonlyArray<IStackFrame>;
523 >
524 >
525 > /**
526 > * Gets the top stack frame that is not hidden if the callstack has already been received from the debug adapter
527 > */
528 > getTopStackFrame(): IStackFrame | undefined;
529 >
530 > /**
531 > * Invalidates the callstack cache
532 > */
533 > clearCallStack(): void;
534 >
535 > /**
536 > * Indicates whether this thread is stopped. The callstack for stopped
537 > * threads can be retrieved from the debug adapter.
538 > */
539 > readonly stopped: boolean;
540 >
541 > next(granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
542 > stepIn(granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
543 > stepOut(granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
544 > stepBack(granularity?: DebugProtocol.SteppingGranularity): Promise<void>;
545 > continue(): Promise<void>;
546 > pause(): Promise<void>;
547 > terminate(): Promise<void>;
548 > reverseContinue(): Promise<void>;
549 > }
550 >
551 > export interface IScope extends IExpressionContainer {
552 > readonly name: string;
553 > readonly expensive: boolean;
554 > readonly range?: IRange;
555 > readonly hasChildren: boolean;
556 > readonly childrenHaveBeenLoaded: boolean;
557 > }
558 >
559 > export interface IStackFrame extends ITreeElement {
560 > readonly thread: IThread;
561 > readonly name: string;
562 > readonly presentationHint: string | undefined;
563 > readonly frameId: number;
564 > readonly range: IRange;
565 > readonly source: Source;
566 > readonly canRestart: boolean;
567 > readonly instructionPointerReference?: string;
568 > getScopes(): Promise<IScope[]>;
569 > getMostSpecificScopes(range: IRange): Promise<ReadonlyArray<IScope>>;
570 > forgetScopes(): void;
571 > restart(): Promise<void>;
572 > toString(): string;
573 > openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<IEditorPane | undefined>;
574 > equals(other: IStackFrame): boolean;
575 > }
576 >
577 > export function isFrameDeemphasized(frame: IStackFrame): boolean {
578 const hint = frame.presentationHint ?? frame.source.presentationHint;
579 return hint === 'deemphasize' || hint === 'subtle';
580 }
581 > debug.ts
582 > export interface IEnablement extends ITreeElement {
583 > readonly enabled: boolean;
584 > }
585 >
586 > export interface IBreakpointData {
587 > readonly id?: string;
588 > readonly lineNumber: number;
589 > readonly column?: number;
590 > readonly enabled?: boolean;
591 > readonly condition?: string;
592 > readonly logMessage?: string;
593 > readonly hitCondition?: string;
594 > readonly triggeredBy?: string;
595 > readonly mode?: string;
596 > readonly modeLabel?: string;
597 > }
598 >
599 > export interface IBreakpointUpdateData {
600 > readonly condition?: string;
601 > readonly hitCondition?: string;
602 > readonly logMessage?: string;
603 > readonly lineNumber?: number;
604 > readonly column?: number;
605 > readonly triggeredBy?: string;
606 > readonly mode?: string;
607 > readonly modeLabel?: string;
608 > }
609 >
610 > export interface IBaseBreakpoint extends IEnablement {
611 > readonly condition?: string;
612 > readonly hitCondition?: string;
613 > readonly logMessage?: string;
614 > readonly verified: boolean;
615 > readonly supported: boolean;
616 > readonly message?: string;
617 > /** The preferred mode of the breakpoint from {@link DebugProtocol.BreakpointMode} */
618 > readonly mode?: string;
619 > /** The preferred mode label of the breakpoint from {@link DebugProtocol.BreakpointMode} */
620 > readonly modeLabel?: string;
621 > readonly sessionsThatVerified: string[];
622 > getIdFromAdapter(sessionId: string): number | undefined;
623 > }
624 >
625 > export interface IBreakpoint extends IBaseBreakpoint {
626 > /** URI where the breakpoint was first set by the user. */
627 > readonly originalUri: uri;
628 > /** URI where the breakpoint is currently shown; may be moved by debugger */
629 > readonly uri: uri;
630 > readonly lineNumber: number;
631 > readonly endLineNumber?: number;
632 > readonly column?: number;
633 > readonly endColumn?: number;
634 > readonly adapterData: unknown;
635 > readonly sessionAgnosticData: { lineNumber: number; column: number | undefined };
636 > /** An ID of the breakpoint that triggers this breakpoint. */
637 > readonly triggeredBy?: string;
638 > /** Pending on the trigger breakpoint, which means this breakpoint is not yet sent to DA */
639 > readonly pending: boolean;
640 >
641 > /** Marks that a session did trigger the breakpoint. */
642 > setSessionDidTrigger(sessionId: string, didTrigger?: boolean): void;
643 > /** Gets whether the `triggeredBy` condition has been met in the given sesison ID. */
644 > getSessionDidTrigger(sessionId: string): boolean;
645 >
646 > toDAP(): DebugProtocol.SourceBreakpoint;
647 > }
648 >
649 > export interface IFunctionBreakpoint extends IBaseBreakpoint {
650 > readonly name: string;
651 > toDAP(): DebugProtocol.FunctionBreakpoint;
652 > }
653 >
654 > export interface IExceptionBreakpoint extends IBaseBreakpoint {
655 > readonly filter: string;
656 > readonly label: string;
657 > readonly description: string | undefined;
658 > }
659 >
660 > export const enum DataBreakpointSetType {
661 > Variable,
662 > Address,
663 > }
664 >
665 > /**
666 > * Source for a data breakpoint. A data breakpoint on a variable always has a
667 > * `dataId` because it cannot reference that variable globally, but addresses
668 > * can request info repeated and use session-specific data.
669 > */
670 > export type DataBreakpointSource =
671 > | { type: DataBreakpointSetType.Variable; dataId: string }
672 > | { type: DataBreakpointSetType.Address; address: string; bytes: number };
673 >
674 > export interface IDataBreakpoint extends IBaseBreakpoint {
675 > readonly description: string;
676 > readonly canPersist: boolean;
677 > readonly src: DataBreakpointSource;
678 > readonly accessType: DebugProtocol.DataBreakpointAccessType;
679 > toDAP(session: IDebugSession): Promise<DebugProtocol.DataBreakpoint | undefined>;
680 > }
681 >
682 > export interface IInstructionBreakpoint extends IBaseBreakpoint {
683 > readonly instructionReference: string;
684 > readonly offset?: number;
685 > /** Original instruction memory address; display purposes only */
686 > readonly address: bigint;
687 > toDAP(): DebugProtocol.InstructionBreakpoint;
688 > }
689 >
690 > export interface IExceptionInfo {
691 > readonly id?: string;
692 > readonly description?: string;
693 > readonly breakMode: string | null;
694 > readonly details?: DebugProtocol.ExceptionDetails;
695 > }
696 >
697 > // model interfaces
698 >
699 > export interface IViewModel extends ITreeElement {
700 > /**
701 > * Returns the focused debug session or undefined if no session is stopped.
702 > */
703 > readonly focusedSession: IDebugSession | undefined;
704 >
705 > /**
706 > * Returns the focused thread or undefined if no thread is stopped.
707 > */
708 > readonly focusedThread: IThread | undefined;
709 >
710 > /**
711 > * Returns the focused stack frame or undefined if there are no stack frames.
712 > */
713 > readonly focusedStackFrame: IStackFrame | undefined;
714 >
715 > setVisualizedExpression(original: IExpression, visualized: IExpression & { treeId: string } | undefined): void;
716 > /** Returns the visualized expression if loaded, or a tree it should be visualized with, or undefined */
717 > getVisualizedExpression(expression: IExpression): IExpression | string | undefined;
718 > getSelectedExpression(): { expression: IExpression; settingWatch: boolean } | undefined;
719 > setSelectedExpression(expression: IExpression | undefined, settingWatch: boolean): void;
720 > updateViews(): void;
721 >
722 > isMultiSessionView(): boolean;
723 >
724 > readonly onDidFocusSession: Event<IDebugSession | undefined>;
725 > readonly onDidFocusThread: Event<{ thread: IThread | undefined; explicit: boolean; session: IDebugSession | undefined }>;
726 > readonly onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined; explicit: boolean; session: IDebugSession | undefined }>;
727 > readonly onDidSelectExpression: Event<{ expression: IExpression; settingWatch: boolean } | undefined>;
728 > readonly onDidEvaluateLazyExpression: Event<IExpressionContainer>;
729 > /**
730 > * Fired when `setVisualizedExpression`, to migrate elements currently
731 > * rendered as `original` to the `replacement`.
732 > */
733 > readonly onDidChangeVisualization: Event<{ original: IExpression; replacement: IExpression }>;
734 > readonly onWillUpdateViews: Event<void>;
735 >
736 > evaluateLazyExpression(expression: IExpressionContainer): void;
737 > }
738 >
739 > export interface IEvaluate {
740 > evaluate(session: IDebugSession, stackFrame: IStackFrame, context: string): Promise<void>;
741 > }
742 >
743 > export interface IDebugModel extends ITreeElement {
744 > getSession(sessionId: string | undefined, includeInactive?: boolean): IDebugSession | undefined;
745 > getSessions(includeInactive?: boolean): IDebugSession[];
746 > getBreakpoints(filter?: { uri?: uri; originalUri?: uri; lineNumber?: number; column?: number; enabledOnly?: boolean; triggeredOnly?: boolean }): ReadonlyArray<IBreakpoint>;
747 > areBreakpointsActivated(): boolean;
748 > getFunctionBreakpoints(): ReadonlyArray<IFunctionBreakpoint>;
749 > getDataBreakpoints(): ReadonlyArray<IDataBreakpoint>;
750 >
751 > /**
752 > * Returns list of all exception breakpoints.
753 > */
754 > getExceptionBreakpoints(): ReadonlyArray<IExceptionBreakpoint>;
755 >
756 > /**
757 > * Returns list of exception breakpoints for the given session
758 > * @param sessionId Session id. If falsy, returns the breakpoints from the last set fallback session.
759 > */
760 > getExceptionBreakpointsForSession(sessionId?: string): ReadonlyArray<IExceptionBreakpoint>;
761 >
762 > getInstructionBreakpoints(): ReadonlyArray<IInstructionBreakpoint>;
763 > getWatchExpressions(): ReadonlyArray<IExpression & IEvaluate>;
764 > registerBreakpointModes(debugType: string, modes: DebugProtocol.BreakpointMode[]): void;
765 > getBreakpointModes(forBreakpointType: 'source' | 'exception' | 'data' | 'instruction'): DebugProtocol.BreakpointMode[];
766 > readonly onDidChangeBreakpoints: Event<IBreakpointsChangeEvent | undefined>;
767 > readonly onDidChangeCallStack: Event<void>;
768 > /**
769 > * The expression has been added, removed, or repositioned.
770 > */
771 > readonly onDidChangeWatchExpressions: Event<IExpression | undefined>;
772 > /**
773 > * The expression's value has changed.
774 > */
775 > readonly onDidChangeWatchExpressionValue: Event<IExpression | undefined>;
776 >
777 > fetchCallstack(thread: IThread, levels?: number): Promise<void>;
778 > }
779 >
780 > /**
781 > * An event describing a change to the set of [breakpoints](#debug.Breakpoint).
782 > */
783 > export interface IBreakpointsChangeEvent {
784 > added?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>;
785 > removed?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>;
786 > changed?: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint>;
787 > sessionOnly: boolean;
788 > }
789 >
790 > // Debug configuration interfaces
791 >
792 > export interface IDebugConfiguration {
793 > allowBreakpointsEverywhere: boolean;
794 > gutterMiddleClickAction: 'logpoint' | 'conditionalBreakpoint' | 'triggeredBreakpoint' | 'none';
795 > openDebug: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart' | 'openOnDebugBreak';
796 > openExplorerOnEnd: boolean;
797 > inlineValues: boolean | 'auto' | 'on' | 'off'; // boolean for back-compat
798 > toolBarLocation: 'floating' | 'docked' | 'commandCenter' | 'hidden';
799 > showInStatusBar: 'never' | 'always' | 'onFirstSessionStart';
800 > internalConsoleOptions: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
801 > extensionHostDebugAdapter: boolean;
802 > enableAllHovers: boolean;
803 > showSubSessionsInToolBar: boolean;
804 > closeReadonlyTabsOnEnd: boolean;
805 > console: {
806 > fontSize: number;
807 > fontFamily: string;
808 > lineHeight: number;
809 > wordWrap: boolean;
810 > closeOnEnd: boolean;
811 > collapseIdenticalLines: boolean;
812 > historySuggestions: boolean;
813 > acceptSuggestionOnEnter: 'off' | 'on';
814 > maximumLines: number;
815 > };
816 > focusWindowOnBreak: boolean;
817 > focusEditorOnBreak: boolean;
818 > onTaskErrors: 'debugAnyway' | 'showErrors' | 'prompt' | 'abort';
819 > showBreakpointsInOverviewRuler: boolean;
820 > showInlineBreakpointCandidates: boolean;
821 > confirmOnExit: 'always' | 'never';
822 > disassemblyView: {
823 > showSourceCode: boolean;
824 > };
825 > autoExpandLazyVariables: 'auto' | 'off' | 'on';
826 > enableStatusBarColor: boolean;
827 > showVariableTypes: boolean;
828 > hideSlowPreLaunchWarning: boolean;
829 > }
830 >
831 > export interface IGlobalConfig {
832 > version: string;
833 > compounds: ICompound[];
834 > configurations: IConfig[];
835 > }
836 >
837 > export interface IConfigPresentation {
838 > hidden?: boolean;
839 > group?: string;
840 > order?: number;
841 > }
842 >
843 > interface IEnvConfig {
844 > internalConsoleOptions?: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
845 > preRestartTask?: string | ITaskIdentifier;
846 > postRestartTask?: string | ITaskIdentifier;
847 > preLaunchTask?: string | ITaskIdentifier;
848 > postDebugTask?: string | ITaskIdentifier;
849 > debugServer?: number;
850 > noDebug?: boolean;
851 > suppressMultipleSessionWarning?: boolean;
852 > presentation?: IConfigPresentation;
853 > }
854 >
855 > export interface IConfig extends IEnvConfig {
856 >
857 > // fundamental attributes
858 > type: string;
859 > request: string;
860 > name: string;
861 > presentation?: IConfigPresentation;
862 > // platform specifics
863 > windows?: IEnvConfig;
864 > osx?: IEnvConfig;
865 > linux?: IEnvConfig;
866 >
867 > // internals
868 > __configurationTarget?: ConfigurationTarget;
869 > __sessionId?: string;
870 > __restart?: unknown;
871 > __autoAttach?: boolean;
872 > port?: number; // TODO
873 > }
874 >
875 > export interface ICompound {
876 > name: string;
877 > stopAll?: boolean;
878 > preLaunchTask?: string | ITaskIdentifier;
879 > configurations: (string | { name: string; folder: string })[];
880 > presentation?: IConfigPresentation;
881 > }
882 >
883 > export function isDebugConfig(thing: IConfig | ICompound): thing is IConfig {
884 return 'type' in thing && 'request' in thing;
885 }
886 > debug.ts
887 > export interface IDebugAdapter extends IDisposable {
888 > readonly onError: Event<Error>;
889 > readonly onExit: Event<number | null>;
890 > onRequest(callback: (request: DebugProtocol.Request) => void): void;
891 > onEvent(callback: (event: DebugProtocol.Event) => void): void;
892 > startSession(): Promise<void>;
893 > sendMessage(message: DebugProtocol.ProtocolMessage): void;
894 > sendResponse(response: DebugProtocol.Response): void;
895 > sendRequest(command: string, args: unknown, clb: (result: DebugProtocol.Response) => void, timeout?: number): number;
896 > stopSession(): Promise<void>;
897 > }
898 >
899 > export interface IDebugAdapterFactory extends ITerminalLauncher {
900 > createDebugAdapter(session: IDebugSession): IDebugAdapter;
901 > substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig>;
902 > }
903 >
904 > export interface IDebugAdapterExecutableOptions {
905 > cwd?: string;
906 > env?: { [key: string]: string };
907 > }
908 >
909 > export interface IDebugAdapterExecutable {
910 > readonly type: 'executable';
911 > readonly command: string;
912 > readonly args: string[];
913 > readonly options?: IDebugAdapterExecutableOptions;
914 > }
915 >
916 > export interface IDebugAdapterServer {
917 > readonly type: 'server';
918 > readonly port: number;
919 > readonly host?: string;
920 > }
921 >
922 > export interface IDebugAdapterNamedPipeServer {
923 > readonly type: 'pipeServer';
924 > readonly path: string;
925 > }
926 >
927 > export interface IDebugAdapterInlineImpl extends IDisposable {
928 > readonly onDidSendMessage: Event<DebugProtocol.Message>;
929 > handleMessage(message: DebugProtocol.Message): void;
930 > }
931 >
932 > export interface IDebugAdapterImpl {
933 > readonly type: 'implementation';
934 > }
935 >
936 > export type IAdapterDescriptor = IDebugAdapterExecutable | IDebugAdapterServer | IDebugAdapterNamedPipeServer | IDebugAdapterImpl;
937 >
938 > export interface IPlatformSpecificAdapterContribution {
939 > program?: string;
940 > args?: string[];
941 > runtime?: string;
942 > runtimeArgs?: string[];
943 > }
944 >
945 > export interface IDebuggerContribution extends IPlatformSpecificAdapterContribution {
946 > type: string;
947 > label?: string;
948 > win?: IPlatformSpecificAdapterContribution;
949 > winx86?: IPlatformSpecificAdapterContribution;
950 > windows?: IPlatformSpecificAdapterContribution;
951 > osx?: IPlatformSpecificAdapterContribution;
952 > linux?: IPlatformSpecificAdapterContribution;
953 >
954 > // internal
955 > aiKey?: string;
956 >
957 > // supported languages
958 > languages?: string[];
959 >
960 > // debug configuration support
961 > configurationAttributes?: Record<string, IJSONSchema>;
962 > initialConfigurations?: unknown[];
963 > configurationSnippets?: IJSONSchemaSnippet[];
964 > variables?: { [key: string]: string };
965 > when?: string;
966 > hiddenWhen?: string;
967 > deprecated?: string;
968 > strings?: { [key in DebuggerString]: string };
969 > /** @deprecated */
970 > uiMessages?: { [key in DebuggerString]: string };
971 > }
972 >
973 > export interface IBreakpointContribution {
974 > language: string;
975 > when?: string;
976 > }
977 >
978 > export enum DebugConfigurationProviderTriggerKind {
979 > /**
980 > * `DebugConfigurationProvider.provideDebugConfigurations` is called to provide the initial debug configurations for a newly created launch.json.
981 > */
982 > Initial = 1,
983 > /**
984 > * `DebugConfigurationProvider.provideDebugConfigurations` is called to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command).
985 > */
986 > Dynamic = 2
987 > }
988 >
989 > export interface IDebugConfigurationProvider {
990 > readonly type: string;
991 > readonly triggerKind: DebugConfigurationProviderTriggerKind;
992 > resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
993 > resolveDebugConfigurationWithSubstitutedVariables?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
994 > provideDebugConfigurations?(folderUri: uri | undefined, token: CancellationToken): Promise<IConfig[]>;
995 > }
996 >
997 > export interface IDebugAdapterDescriptorFactory {
998 > readonly type: string;
999 > createDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor>;
1000 > }
1001 >
1002 > interface ITerminalLauncher {
1003 > runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
1004 > }
1005 >
1006 > export interface IConfigurationManager {
1007 >
1008 > /**
1009 > * Returns an object containing the selected launch configuration and the selected configuration name. Both these fields can be null (no folder workspace).
1010 > */
1011 > readonly selectedConfiguration: {
1012 > launch: ILaunch | undefined;
1013 > // Potentially activates extensions
1014 > getConfig: () => Promise<IConfig | undefined>;
1015 > name: string | undefined;
1016 > // Type is used when matching dynamic configurations to their corresponding provider
1017 > type: string | undefined;
1018 > };
1019 >
1020 > selectConfiguration(launch: ILaunch | undefined, name?: string, config?: IConfig, dynamicConfigOptions?: { type?: string }): Promise<void>;
1021 >
1022 > getLaunches(): ReadonlyArray<ILaunch>;
1023 > getLaunch(workspaceUri: uri | undefined): ILaunch | undefined;
1024 > getAllConfigurations(): { launch: ILaunch; name: string; presentation?: IConfigPresentation }[];
1025 > removeRecentDynamicConfigurations(name: string, type: string): void;
1026 > getRecentDynamicConfigurations(): { name: string; type: string }[];
1027 >
1028 > /**
1029 > * Allows to register on change of selected debug configuration.
1030 > */
1031 > readonly onDidSelectConfiguration: Event<void>;
1032 >
1033 > /**
1034 > * Allows to register on change of selected debug configuration.
1035 > */
1036 > readonly onDidChangeConfigurationProviders: Event<void>;
1037 >
1038 > hasDebugConfigurationProvider(debugType: string, triggerKind?: DebugConfigurationProviderTriggerKind): boolean;
1039 > getDynamicProviders(): Promise<{ label: string; type: string; pick: () => Promise<{ launch: ILaunch; config: IConfig; label: string } | undefined> }[]>;
1040 > getDynamicConfigurationsByType(type: string, token?: CancellationToken): Promise<{ launch: ILaunch; config: IConfig; label: string }[]>;
1041 >
1042 > registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable;
1043 > unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void;
1044 >
1045 > resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: unknown, token: CancellationToken): Promise<IConfig | null | undefined>;
1046 > }
1047 >
1048 > export enum DebuggerString {
1049 > UnverifiedBreakpoints = 'unverifiedBreakpoints'
1050 > }
1051 >
1052 > export interface IAdapterManager {
1053 >
1054 > readonly onDidRegisterDebugger: Event<void>;
1055 >
1056 > hasEnabledDebuggers(): boolean;
1057 > getDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor | undefined>;
1058 > getDebuggerLabel(type: string): string | undefined;
1059 > someDebuggerInterestedInLanguage(language: string): boolean;
1060 > getDebugger(type: string): IDebuggerMetadata | undefined;
1061 >
1062 > activateDebuggers(activationEvent: string, debugType?: string): Promise<void>;
1063 > registerDebugAdapterFactory(debugTypes: string[], debugAdapterFactory: IDebugAdapterFactory): IDisposable;
1064 > createDebugAdapter(session: IDebugSession): IDebugAdapter | undefined;
1065 > registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable;
1066 > unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void;
1067 >
1068 > substituteVariables(debugType: string, folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig>;
1069 > runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, sessionId: string): Promise<number | undefined>;
1070 > getEnabledDebugger(type: string): (IDebugger & IDebuggerMetadata) | undefined;
1071 > guessDebugger(gettingConfigurations: boolean): Promise<IGuessedDebugger | undefined>;
1072 >
1073 > get onDidDebuggersExtPointRead(): Event<void>;
1074 > }
1075 >
1076 > export interface IGuessedDebugger {
1077 > debugger: IDebugger;
1078 > withConfig?: {
1079 > label: string;
1080 > launch: ILaunch;
1081 > config: IConfig;
1082 > };
1083 > }
1084 >
1085 > export interface ILaunch {
1086 >
1087 > /**
1088 > * Resource pointing to the launch.json this object is wrapping.
1089 > */
1090 > readonly uri: uri;
1091 >
1092 > /**
1093 > * Name of the launch.
1094 > */
1095 > readonly name: string;
1096 >
1097 > /**
1098 > * Workspace of the launch. Can be undefined.
1099 > */
1100 > readonly workspace: IWorkspaceFolder | undefined;
1101 >
1102 > /**
1103 > * Should this launch be shown in the debug dropdown.
1104 > */
1105 > readonly hidden: boolean;
1106 >
1107 > /**
1108 > * Returns a configuration with the specified name.
1109 > * Returns undefined if there is no configuration with the specified name.
1110 > */
1111 > getConfiguration(name: string): IConfig | undefined;
1112 >
1113 > /**
1114 > * Returns a compound with the specified name.
1115 > * Returns undefined if there is no compound with the specified name.
1116 > */
1117 > getCompound(name: string): ICompound | undefined;
1118 >
1119 > /**
1120 > * Returns the names of all configurations and compounds.
1121 > * Ignores configurations which are invalid.
1122 > */
1123 > getConfigurationNames(ignoreCompoundsAndPresentation?: boolean): string[];
1124 >
1125 > /**
1126 > * Opens the launch.json file. Creates if it does not exist.
1127 > */
1128 > openConfigFile(options: { preserveFocus: boolean; type?: string; suppressInitialConfigs?: boolean }, token?: CancellationToken): Promise<{ editor: IEditorPane | null; created: boolean }>;
1129 > }
1130 >
1131 > // Debug service interfaces
1132 >
1133 > export const IDebugService = createDecorator<IDebugService>('debugService');
1134 >
1135 > export interface IDebugService {
1136 > readonly _serviceBrand: undefined;
1137 >
1138 > /**
1139 > * Gets the current debug state.
1140 > */
1141 > readonly state: State;
1142 >
1143 > readonly initializingOptions?: IDebugSessionOptions | undefined;
1144 >
1145 > /**
1146 > * Allows to register on debug state changes.
1147 > */
1148 > readonly onDidChangeState: Event<State>;
1149 >
1150 > /**
1151 > * Allows to register on sessions about to be created (not yet fully initialised).
1152 > * This is fired exactly one time for any given session.
1153 > */
1154 > readonly onWillNewSession: Event<IDebugSession>;
1155 >
1156 > /**
1157 > * Fired when a new debug session is started. This may fire multiple times
1158 > * for a single session due to restarts.
1159 > */
1160 > readonly onDidNewSession: Event<IDebugSession>;
1161 >
1162 > /**
1163 > * Allows to register on end session events.
1164 > *
1165 > * Contains a boolean indicating whether the session will restart. If restart
1166 > * is true, the session should not considered to be dead yet.
1167 > */
1168 > readonly onDidEndSession: Event<{ session: IDebugSession; restart: boolean }>;
1169 >
1170 > /**
1171 > * Gets the configuration manager.
1172 > */
1173 > getConfigurationManager(): IConfigurationManager;
1174 >
1175 > /**
1176 > * Gets the adapter manager.
1177 > */
1178 > getAdapterManager(): IAdapterManager;
1179 >
1180 > /**
1181 > * Sets the focused stack frame and evaluates all expressions against the newly focused stack frame,
1182 > */
1183 > focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, options?: { explicit?: boolean; preserveFocus?: boolean; sideBySide?: boolean; pinned?: boolean }): Promise<void>;
1184 >
1185 > /**
1186 > * Returns true if breakpoints can be set for a given editor model. Depends on mode.
1187 > */
1188 > canSetBreakpointsIn(model: EditorIModel): boolean;
1189 >
1190 > /**
1191 > * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes.
1192 > */
1193 > addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], ariaAnnounce?: boolean): Promise<IBreakpoint[]>;
1194 >
1195 > /**
1196 > * Updates the breakpoints.
1197 > */
1198 > updateBreakpoints(originalUri: uri, data: Map<string, IBreakpointUpdateData>, sendOnResourceSaved: boolean): Promise<void>;
1199 >
1200 > /**
1201 > * Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint.
1202 > * Notifies debug adapter of breakpoint changes.
1203 > */
1204 > enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise<void>;
1205 >
1206 > /**
1207 > * Sets the global activated property for all breakpoints.
1208 > * Notifies debug adapter of breakpoint changes.
1209 > */
1210 > setBreakpointsActivated(activated: boolean): Promise<void>;
1211 >
1212 > /**
1213 > * Removes all breakpoints. If id is passed only removes the breakpoint associated with that id.
1214 > * Notifies debug adapter of breakpoint changes.
1215 > */
1216 > removeBreakpoints(id?: string | string[]): Promise<void>;
1217 >
1218 > /**
1219 > * Adds a new function breakpoint for the given name.
1220 > */
1221 > addFunctionBreakpoint(opts?: IFunctionBreakpointOptions, id?: string): void;
1222 >
1223 > /**
1224 > * Updates an already existing function breakpoint.
1225 > * Notifies debug adapter of breakpoint changes.
1226 > */
1227 > updateFunctionBreakpoint(id: string, update: { name?: string; hitCondition?: string; condition?: string }): Promise<void>;
1228 >
1229 > /**
1230 > * Removes all function breakpoints. If id is passed only removes the function breakpoint with the passed id.
1231 > * Notifies debug adapter of breakpoint changes.
1232 > */
1233 > removeFunctionBreakpoints(id?: string): Promise<void>;
1234 >
1235 > /**
1236 > * Adds a new data breakpoint.
1237 > */
1238 > addDataBreakpoint(opts: IDataBreakpointOptions): Promise<void>;
1239 >
1240 > /**
1241 > * Updates an already existing data breakpoint.
1242 > * Notifies debug adapter of breakpoint changes.
1243 > */
1244 > updateDataBreakpoint(id: string, update: { hitCondition?: string; condition?: string }): Promise<void>;
1245 >
1246 > /**
1247 > * Removes all data breakpoints. If id is passed only removes the data breakpoint with the passed id.
1248 > * Notifies debug adapter of breakpoint changes.
1249 > */
1250 > removeDataBreakpoints(id?: string): Promise<void>;
1251 >
1252 > /**
1253 > * Adds a new instruction breakpoint.
1254 > */
1255 > addInstructionBreakpoint(opts: IInstructionBreakpointOptions): Promise<void>;
1256 >
1257 > /**
1258 > * Removes all instruction breakpoints. If `address` is passed, only the
1259 > * instruction breakpoint with the matching resolved memory address is
1260 > * removed; this is preferred because the debug adapter is allowed to
1261 > * return different `instructionReference` strings for the same memory
1262 > * location on subsequent disassemble requests. If `address` is not
1263 > * provided, falls back to matching on `instructionReference` (and
1264 > * `offset` when specified). When no arguments are provided, all
1265 > * instruction breakpoints are removed. Notifies the debug adapter of
1266 > * breakpoint changes.
1267 > */
1268 > removeInstructionBreakpoints(instructionReference?: string, offset?: number, address?: bigint): Promise<void>;
1269 >
1270 > setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string | undefined): Promise<void>;
1271 >
1272 > /**
1273 > * Creates breakpoints based on the sesison filter options. This will create
1274 > * disabled breakpoints (or enabled, if the filter indicates it's a default)
1275 > * for each filter provided in the session.
1276 > */
1277 > setExceptionBreakpointsForSession(session: IDebugSession, filters: DebugProtocol.ExceptionBreakpointsFilter[]): void;
1278 >
1279 > /**
1280 > * Sends all breakpoints to the passed session.
1281 > * If session is not passed, sends all breakpoints to each session.
1282 > */
1283 > sendAllBreakpoints(session?: IDebugSession): Promise<void>;
1284 >
1285 > /**
1286 > * Sends breakpoints of the given source to the passed session.
1287 > */
1288 > sendBreakpoints(modelUri: uri, sourceModified?: boolean, session?: IDebugSession): Promise<void>;
1289 >
1290 > /**
1291 > * Adds a new watch expression and evaluates it against the debug adapter.
1292 > */
1293 > addWatchExpression(name?: string): void;
1294 >
1295 > /**
1296 > * Renames a watch expression and evaluates it against the debug adapter.
1297 > */
1298 > renameWatchExpression(id: string, newName: string): void;
1299 >
1300 > /**
1301 > * Moves a watch expression to a new possition. Used for reordering watch expressions.
1302 > */
1303 > moveWatchExpression(id: string, position: number): void;
1304 >
1305 > /**
1306 > * Removes all watch expressions. If id is passed only removes the watch expression with the passed id.
1307 > */
1308 > removeWatchExpressions(id?: string): void;
1309 >
1310 > /**
1311 > * Starts debugging. If the configOrName is not passed uses the selected configuration in the debug dropdown.
1312 > * Also saves all files, manages if compounds are present in the configuration
1313 > * and resolveds configurations via DebugConfigurationProviders.
1314 > *
1315 > * Returns true if the start debugging was successful. For compound launches, all configurations have to start successfully for it to return success.
1316 > * On errors the startDebugging will throw an error, however some error and cancelations are handled and in that case will simply return false.
1317 > */
1318 > startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, options?: IDebugSessionOptions, saveBeforeStart?: boolean): Promise<boolean>;
1319 >
1320 > /**
1321 > * Restarts a session or creates a new one if there is no active session.
1322 > */
1323 > restartSession(session: IDebugSession, restartData?: unknown): Promise<void>;
1324 >
1325 > /**
1326 > * Stops the session. If no session is specified then all sessions are stopped.
1327 > */
1328 > stopSession(session: IDebugSession | undefined, disconnect?: boolean, suspend?: boolean): Promise<void>;
1329 >
1330 > /**
1331 > * Makes unavailable all sources with the passed uri. Source will appear as grayed out in callstack view.
1332 > */
1333 > sourceIsNotAvailable(uri: uri): void;
1334 >
1335 > /**
1336 > * Gets the current debug model.
1337 > */
1338 > getModel(): IDebugModel;
1339 >
1340 > /**
1341 > * Gets the current view model.
1342 > */
1343 > getViewModel(): IViewModel;
1344 >
1345 > /**
1346 > * Resumes execution and pauses until the given position is reached.
1347 > */
1348 > runTo(uri: uri, lineNumber: number, column?: number): Promise<void>;
1349 > }
1350 >
1351 > // Editor interfaces
1352 > export const enum BreakpointWidgetContext {
1353 > CONDITION = 0,
1354 > HIT_COUNT = 1,
1355 > LOG_MESSAGE = 2,
1356 > TRIGGER_POINT = 3
1357 > }
1358 >
1359 > export interface IDebugEditorContribution extends editorCommon.IEditorContribution {
1360 > showHover(range: Position, focus: boolean): Promise<void>;
1361 > addLaunchConfiguration(): Promise<void>;
1362 > closeExceptionWidget(): void;
1363 > }
1364 >
1365 > export interface IBreakpointEditorContribution extends editorCommon.IEditorContribution {
1366 > showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void;
1367 > closeBreakpointWidget(): void;
1368 > getContextMenuActionsAtPosition(lineNumber: number, model: EditorIModel): IAction[];
1369 > }
1370 >
1371 > export interface IReplConfiguration {
1372 > readonly fontSize: number;
1373 > readonly fontFamily: string;
1374 > readonly lineHeight: number;
1375 > readonly cssLineHeight: string;
1376 > readonly backgroundColor: Color | undefined;
1377 > readonly fontSizeForTwistie: number;
1378 > }
1379 >
1380 > export interface IReplOptions {
1381 > readonly replConfiguration: IReplConfiguration;
1382 > }
1383 >
1384 > export interface IDebugVisualizationContext {
1385 > variable: DebugProtocol.Variable;
1386 > containerId?: number;
1387 > frameId?: number;
1388 > threadId: number;
1389 > sessionId: string;
1390 > }
1391 >
1392 > export const enum DebugVisualizationType {
1393 > Command,
1394 > Tree,
1395 > }
1396 >
1397 > export type MainThreadDebugVisualization =
1398 > | { type: DebugVisualizationType.Command }
1399 > | { type: DebugVisualizationType.Tree; id: string };
1400 >
1401 >
1402 > export const enum DebugTreeItemCollapsibleState {
1403 > None = 0,
1404 > Collapsed = 1,
1405 > Expanded = 2
1406 > }
1407 >
1408 > export interface IDebugVisualizationTreeItem {
1409 > id: number;
1410 > label: string;
1411 > description?: string;
1412 > collapsibleState: DebugTreeItemCollapsibleState;
1413 > contextValue?: string;
1414 > canEdit?: boolean;
1415 > }
1416 >
1417 > export namespace IDebugVisualizationTreeItem {
1418 > export type Serialized = IDebugVisualizationTreeItem;
1419 > export const deserialize = (v: Serialized): IDebugVisualizationTreeItem => v;
1420 > export const serialize = (item: IDebugVisualizationTreeItem): Serialized => item;
1421 > }
1422 >
1423 > export interface IDebugVisualization {
1424 > id: number;
1425 > name: string;
1426 > iconPath: { light?: URI; dark: URI } | undefined;
1427 > iconClass: string | undefined;
1428 > visualization: MainThreadDebugVisualization | undefined;
1429 > }
1430 >
1431 > export namespace IDebugVisualization {
1432 > export interface Serialized {
1433 > id: number;
1434 > name: string;
1435 > iconPath?: { light?: UriComponents; dark: UriComponents };
1436 > iconClass?: string;
1437 > visualization?: MainThreadDebugVisualization;
1438 > }
1439 >
1440 > export const deserialize = (v: Serialized): IDebugVisualization => ({
1441 id: v.id,
1442 name: v.name,
1445 visualization: v.visualization,
1446 });
1447 > debug.ts
1448 > export const serialize = (visualizer: IDebugVisualization): Serialized => visualizer;
1449 > }
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/async.ts 1282 covered LOC · 217 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- async.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 { BugIndicatingError, CancellationError, isCancellationError } from './errors.js';
8 > import { Emitter, Event } from './event.js';
9 > import { Disposable, DisposableMap, DisposableStore, IDisposable, isDisposable, MutableDisposable, toDisposable } from './lifecycle.js';
10 > import { extUri as defaultExtUri, IExtUri } from './resources.js';
11 > import { URI } from './uri.js';
12 > import { setTimeout0 } from './platform.js';
13 > import { MicrotaskDelay } from './symbols.js';
14 > import { Lazy } from './lazy.js';
15 >
16 > export function isThenable<T>(obj: unknown): obj is Promise<T> {
17 return !!obj && typeof (obj as unknown as Promise<T>).then === 'function';
18 }
19 > async.ts
20 > export interface CancelablePromise<T> extends Promise<T> {
21 > cancel(): void;
22 > }
23 >
24 > /**
25 > * Returns a promise that can be cancelled using the provided cancellation token.
26 > *
27 > * @remarks When cancellation is requested, the promise will be rejected with a {@link CancellationError}.
28 > * If the promise resolves to a disposable object, it will be automatically disposed when cancellation
29 > * is requested.
30 > *
31 > * @param callback A function that accepts a cancellation token and returns a promise
32 > * @returns A promise that can be cancelled
33 > */
34 > export function createCancelablePromise<T>(callback: (token: CancellationToken) => Promise<T>): CancelablePromise<T> {
35 const source = new CancellationTokenSource();
36
80 };
81 }
82 > async.ts
83 > /**
84 > * Returns a promise that resolves with `undefined` as soon as the passed token is cancelled.
85 > * @see {@link raceCancellationError}
86 > */
87 > export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken): Promise<T | undefined>;
88 >
89 > /**
90 > * Returns a promise that resolves with `defaultValue` as soon as the passed token is cancelled.
91 > * @see {@link raceCancellationError}
92 > */
93 > export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken, defaultValue: T): Promise<T>;
94 >
95 > export function raceCancellation<T>(promise: Promise<T>, token: CancellationToken, defaultValue?: T): Promise<T | undefined> {
96 return new Promise((resolve, reject) => {
97 const ref = token.onCancellationRequested(() => {
102 });
103 }
104 > async.ts
105 > /**
106 > * Returns a promise that rejects with an {@CancellationError} as soon as the passed token is cancelled.
107 > * @see {@link raceCancellation}
108 > */
109 > export function raceCancellationError<T>(promise: Promise<T>, token: CancellationToken): Promise<T> {
110 return new Promise((resolve, reject) => {
111 const ref = token.onCancellationRequested(() => {
116 });
117 }
118 > async.ts
119 > export function rejectIfNotCanceled(err: unknown): undefined {
120 if (isCancellationError(err)) {
121 return undefined;
123 return Promise.reject(err) as never;
124 }
125 > async.ts
126 > /**
127 > * Wraps a cancellable promise such that it is no cancellable. Can be used to
128 > * avoid issues with shared promises that would normally be returned as
129 > * cancellable to consumers.
130 > */
131 > export function notCancellablePromise<T>(promise: CancelablePromise<T>): Promise<T> {
132 return new Promise<T>((resolve, reject) => {
133 promise.then(resolve, reject);
134 });
135 }
136 > async.ts
137 > /**
138 > * Returns as soon as one of the promises resolves or rejects and cancels remaining promises
139 > */
140 > export function raceCancellablePromises<T>(cancellablePromises: (CancelablePromise<T> | Promise<T>)[]): CancelablePromise<T> {
141 let resolvedPromiseIndex = -1;
142 const promises = cancellablePromises.map((promise, index) => promise.then(result => { resolvedPromiseIndex = index; return result; }));
154 return promise;
155 }
156 > async.ts
157 > export function raceTimeout<T>(promise: Promise<T>, timeout: number, onTimeout?: () => void): Promise<T | undefined> {
158 let promiseResolve: ((value: T | undefined) => void) | undefined = undefined;
159
168 ]);
169 }
170 > async.ts
171 > export function asPromise<T>(callback: () => T | Thenable<T>): Promise<T> {
172 return new Promise<T>((resolve, reject) => {
173 const item = callback();
179 });
180 }
181 > async.ts
182 > /**
183 > * Creates and returns a new promise, plus its `resolve` and `reject` callbacks.
184 > *
185 > * Replace with standardized [`Promise.withResolvers`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers) once it is supported
186 > */
187 > export function promiseWithResolvers<T>(): { promise: Promise<T>; resolve: (value: T | PromiseLike<T>) => void; reject: (err?: any) => void } {
188 let resolve: (value: T | PromiseLike<T>) => void;
189 let reject: (reason?: any) => void;
194 return { promise, resolve: resolve!, reject: reject! };
195 }
196 > async.ts
197 > export interface ITask<T> {
198 > (): T;
199 > }
200 >
201 > export interface ICancellableTask<T> {
202 > (token: CancellationToken): T;
203 > }
204 >
205 > /**
206 > * A helper to prevent accumulation of sequential async tasks.
207 > *
208 > * Imagine a mail man with the sole task of delivering letters. As soon as
209 > * a letter submitted for delivery, he drives to the destination, delivers it
210 > * and returns to his base. Imagine that during the trip, N more letters were submitted.
211 > * When the mail man returns, he picks those N letters and delivers them all in a
212 > * single trip. Even though N+1 submissions occurred, only 2 deliveries were made.
213 > *
214 > * The throttler implements this via the queue() method, by providing it a task
215 > * factory. Following the example:
216 > *
217 > * const throttler = new Throttler();
218 > * const letters = [];
219 > *
220 > * function deliver() {
221 > * const lettersToDeliver = letters;
222 > * letters = [];
223 > * return makeTheTrip(lettersToDeliver);
224 > * }
225 > *
226 > * function onLetterReceived(l) {
227 > * letters.push(l);
228 > * throttler.queue(deliver);
229 > * }
230 > */
231 > export class Throttler implements IDisposable {
232 >
233 > private activePromise: Promise<any> | null;
234 > private queuedPromise: Promise<any> | null;
235 > private queuedPromiseFactory: ICancellableTask<Promise<any>> | null;
236 > private cancellationTokenSource: CancellationTokenSource;
237 >
238 > constructor() {
239 > this.activePromise = null; async.ts
240 > this.queuedPromise = null;
241 > this.queuedPromiseFactory = null;
242 >
243 > this.cancellationTokenSource = new CancellationTokenSource();
244 > }
245 > async.ts
246 > queue<T>(promiseFactory: ICancellableTask<Promise<T>>): Promise<T> {
247 if (this.cancellationTokenSource.token.isCancellationRequested) {
248 return Promise.reject(new Error('Throttler is disposed'));
288 });
289 }
290 > async.ts
291 > dispose(): void {
292 > this.cancellationTokenSource.cancel(); async.ts
293 > }
294 > } async.ts
295 >
296 > export class Sequencer {
297
298 private current: Promise<unknown> = Promise.resolve(null);
299 > async.ts
300 > queue<T>(promiseTask: ITask<Promise<T>>): Promise<T> {
301 return this.current = this.current.then(() => promiseTask(), () => promiseTask());
302 }
303 > } async.ts
304 >
305 > /**
306 > * A {@link Throttler} per key. Calls for the same key coalesce (only the most
307 > * recently queued task runs after the active one settles); calls for different
308 > * keys are independent. Idle keys are cleaned up automatically.
309 > */
310 > export class ThrottlerByKey<TKey> implements IDisposable {
311
312 private readonly throttlers = new Map<TKey, { throttler: Throttler; count: number }>();
313 > async.ts
314 > queue<T>(key: TKey, task: ITask<Promise<T>>): Promise<T> {
315 let entry = this.throttlers.get(key);
316 if (!entry) {
327 });
328 }
329 > async.ts
330 > dispose(): void {
331 for (const { throttler } of this.throttlers.values()) {
332 throttler.dispose();
334 this.throttlers.clear();
335 }
336 > } async.ts
337 >
338 > export class SequencerByKey<TKey> {
339
340 private promiseMap = new Map<TKey, Promise<unknown>>();
341 > async.ts
342 > queue<T>(key: TKey, promiseTask: ITask<Promise<T>>): Promise<T> {
343 const runningPromise = this.promiseMap.get(key) ?? Promise.resolve();
344 const newPromise = runningPromise
353 return newPromise;
354 }
355 > async.ts
356 > peek(key: TKey): Promise<unknown> | undefined {
357 return this.promiseMap.get(key) || undefined;
358 }
359 > async.ts
360 > keys(): IterableIterator<TKey> {
361 return this.promiseMap.keys();
362 }
363 > } async.ts
364 >
365 > interface IScheduledLater extends IDisposable {
366 > isTriggered(): boolean;
367 > }
368 >
369 > const timeoutDeferred = (timeout: number, fn: () => void): IScheduledLater => {
370 let scheduled = true;
371 const handle = setTimeout(() => {
381 };
382 };
383 > async.ts
384 > const microtaskDeferred = (fn: () => void): IScheduledLater => {
385 let scheduled = true;
386 queueMicrotask(() => {
396 };
397 };
398 > async.ts
399 > /**
400 > * A helper to delay (debounce) execution of a task that is being requested often.
401 > *
402 > * Following the throttler, now imagine the mail man wants to optimize the number of
403 > * trips proactively. The trip itself can be long, so he decides not to make the trip
404 > * as soon as a letter is submitted. Instead he waits a while, in case more
405 > * letters are submitted. After said waiting period, if no letters were submitted, he
406 > * decides to make the trip. Imagine that N more letters were submitted after the first
407 > * one, all within a short period of time between each other. Even though N+1
408 > * submissions occurred, only 1 delivery was made.
409 > *
410 > * The delayer offers this behavior via the trigger() method, into which both the task
411 > * to be executed and the waiting period (delay) must be passed in as arguments. Following
412 > * the example:
413 > *
414 > * const delayer = new Delayer(WAITING_PERIOD);
415 > * const letters = [];
416 > *
417 > * function letterReceived(l) {
418 > * letters.push(l);
419 > * delayer.trigger(() => { return makeTheTrip(); });
420 > * }
421 > */
422 > export class Delayer<T> implements IDisposable {
423 >
424 > private deferred: IScheduledLater | null;
425 > private completionPromise: Promise<any> | null;
426 > private doResolve: ((value?: any | Promise<any>) => void) | null;
427 > private doReject: ((err: unknown) => void) | null;
428 > private task: ITask<T | Promise<T>> | null;
429 >
430 > constructor(public defaultDelay: number | typeof MicrotaskDelay) {
431 > this.deferred = null; async.ts
432 > this.completionPromise = null;
433 > this.doResolve = null;
434 > this.doReject = null;
435 > this.task = null;
436 > }
437 > async.ts
438 > trigger(task: ITask<T | Promise<T>>, delay = this.defaultDelay): Promise<T> {
439 this.task = task;
440 this.cancelTimeout();
465 return this.completionPromise;
466 }
467 > async.ts
468 > isTriggered(): boolean {
469 return !!this.deferred?.isTriggered();
470 }
471 > async.ts
472 > cancel(): void {
473 > this.cancelTimeout(); async.ts
474 >
475 > if (this.completionPromise) {
476 this.doReject?.(new CancellationError());
477 this.completionPromise = null;
478 }
479 > } async.ts
480 > async.ts
481 > private cancelTimeout(): void {
482 > this.deferred?.dispose(); async.ts
483 > this.deferred = null;
484 > }
485 > async.ts
486 > dispose(): void {
487 > this.cancel(); async.ts
488 > }
489 > } async.ts
490 >
491 > /**
492 > * A helper to delay execution of a task that is being requested often, while
493 > * preventing accumulation of consecutive executions, while the task runs.
494 > *
495 > * The mail man is clever and waits for a certain amount of time, before going
496 > * out to deliver letters. While the mail man is going out, more letters arrive
497 > * and can only be delivered once he is back. Once he is back the mail man will
498 > * do one more trip to deliver the letters that have accumulated while he was out.
499 > */
500 > export class ThrottledDelayer<T> {
501 >
502 > private delayer: Delayer<Promise<T>>;
503 > private throttler: Throttler;
504 >
505 > constructor(defaultDelay: number) {
506 > this.delayer = new Delayer(defaultDelay); async.ts
507 > this.throttler = new Throttler();
508 > }
509 > async.ts
510 > trigger(promiseFactory: ICancellableTask<Promise<T>>, delay?: number): Promise<T> {
511 return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as unknown as Promise<T>;
512 }
513 > async.ts
514 > isTriggered(): boolean {
515 return this.delayer.isTriggered();
516 }
517 > async.ts
518 > cancel(): void {
519 this.delayer.cancel();
520 }
521 > async.ts
522 > dispose(): void {
523 > this.delayer.dispose(); async.ts
524 > this.throttler.dispose();
525 > }
526 > } async.ts
527 >
528 > /**
529 > * A barrier that is initially closed and then becomes opened permanently.
530 > */
531 > export class Barrier {
532 > private _isOpen: boolean;
533 > private _promise: Promise<boolean>;
534 > private _completePromise!: (v: boolean) => void;
535 >
536 > constructor() {
537 this._isOpen = false;
538 this._promise = new Promise<boolean>((c, e) => {
540 });
541 }
542 > async.ts
543 > isOpen(): boolean {
544 return this._isOpen;
545 }
546 > async.ts
547 > open(): void {
548 this._isOpen = true;
549 this._completePromise(true);
550 }
551 > async.ts
552 > wait(): Promise<boolean> {
553 return this._promise;
554 }
555 > } async.ts
556 >
557 > /**
558 > * A barrier that is initially closed and then becomes opened permanently after a certain period of
559 > * time or when open is called explicitly
560 > */
561 > export class AutoOpenBarrier extends Barrier {
562 >
563 > private readonly _timeout: Timeout;
564 >
565 > constructor(autoOpenTimeMs: number) {
566 super();
567 this._timeout = setTimeout(() => this.open(), autoOpenTimeMs);
568 }
569 > async.ts
570 > override open(): void {
571 clearTimeout(this._timeout);
572 super.open();
573 }
574 > } async.ts
575 >
576 > export function timeout(millis: number): CancelablePromise<void>;
577 > export function timeout(millis: number, token: CancellationToken): Promise<void>;
578 > export function timeout(millis: number, token?: CancellationToken): CancelablePromise<void> | Promise<void> {
579 if (!token) {
580 return createCancelablePromise(token => timeout(millis, token));
593 });
594 }
595 > async.ts
596 > /**
597 > * Creates a timeout that can be disposed using its returned value.
598 > * @param handler The timeout handler.
599 > * @param timeout An optional timeout in milliseconds.
600 > * @param store An optional {@link DisposableStore} that will have the timeout disposable managed automatically.
601 > *
602 > * @example
603 > * const store = new DisposableStore;
604 > * // Call the timeout after 1000ms at which point it will be automatically
605 > * // evicted from the store.
606 > * const timeoutDisposable = disposableTimeout(() => {}, 1000, store);
607 > *
608 > * if (foo) {
609 > * // Cancel the timeout and evict it from store.
610 > * timeoutDisposable.dispose();
611 > * }
612 > */
613 > export function disposableTimeout(handler: () => void, timeout = 0, store?: DisposableStore): IDisposable {
614 const timer = setTimeout(() => {
615 handler();
625 return disposable;
626 }
627 > async.ts
628 > /**
629 > * The largest delay (in milliseconds) a single `setTimeout` can represent.
630 > * Larger values overflow its internal 32-bit signed integer and fire (almost)
631 > * immediately instead of waiting.
632 > */
633 > export const MAX_TIMEOUT_DELAY = 2 ** 31 - 1; // ~24.8 days
634 >
635 > /**
636 > * Like {@link disposableTimeout}, but supports delays larger than
637 > * {@link MAX_TIMEOUT_DELAY} (~24.8 days), which a single `setTimeout` cannot
638 > * represent. The wait is split into chunks and re-armed until the target time is
639 > * reached, so the handler fires at approximately `Date.now() + timeout`.
640 > *
641 > * Note: like `setTimeout`, firing is best-effort and may drift across system
642 > * sleep or wall-clock changes; do not rely on it for precise scheduling.
643 > *
644 > * @param handler The timeout handler.
645 > * @param timeout The timeout in milliseconds. May exceed {@link MAX_TIMEOUT_DELAY}.
646 > * @param store An optional {@link DisposableStore} that will have the timeout disposable managed automatically.
647 > */
648 > export function disposableLongTimeout(handler: () => void, timeout: number, store?: DisposableStore): IDisposable {
649 const target = Date.now() + timeout;
650 let timer: Timeout;
671 return disposable;
672 }
673 > async.ts
674 > /**
675 > * Runs the provided list of promise factories in sequential order. The returned
676 > * promise will complete to an array of results from each promise.
677 > */
678 >
679 > export function sequence<T>(promiseFactories: ITask<Promise<T>>[]): Promise<T[]> {
680 const results: T[] = [];
681 let index = 0;
701 return Promise.resolve(null).then(thenHandler);
702 }
703 > async.ts
704 > export function first<T>(promiseFactories: ITask<Promise<T>>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null): Promise<T | null> {
705 let index = 0;
706 const len = promiseFactories.length;
725 return loop();
726 }
727 > async.ts
728 > /**
729 > * Returns the result of the first promise that matches the "shouldStop",
730 > * running all promises in parallel. Supports cancelable promises.
731 > */
732 > export function firstParallel<T>(promiseList: Promise<T>[], shouldStop?: (t: T) => boolean, defaultValue?: T | null): Promise<T | null>;
733 > export function firstParallel<T, R extends T>(promiseList: Promise<T>[], shouldStop: (t: T) => t is R, defaultValue?: R | null): Promise<R | null>;
734 > export function firstParallel<T>(promiseList: Promise<T>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null) {
735 if (promiseList.length === 0) {
736 return Promise.resolve(defaultValue);
764 });
765 }
766 > async.ts
767 > interface ILimitedTaskFactory<T> {
768 > factory: ITask<Promise<T>>;
769 > c: (value: T | Promise<T>) => void;
770 > e: (error?: unknown) => void;
771 > }
772 >
773 > export interface ILimiter<T> {
774 >
775 > readonly size: number;
776 >
777 > queue(factory: ITask<Promise<T>>): Promise<T>;
778 >
779 > clear(): void;
780 > }
781 >
782 > /**
783 > * A helper to queue N promises and run them all with a max degree of parallelism. The helper
784 > * ensures that at any time no more than M promises are running at the same time.
785 > */
786 > export class Limiter<T> implements ILimiter<T> {
787 >
788 > private _size = 0;
789 > private _isDisposed = false;
790 > private runningPromises: number;
791 > private readonly maxDegreeOfParalellism: number;
792 > private readonly outstandingPromises: ILimitedTaskFactory<T>[];
793 > private readonly _onDrained: Emitter<void>;
794 >
795 > constructor(maxDegreeOfParalellism: number) {
796 this.maxDegreeOfParalellism = maxDegreeOfParalellism;
797 this.outstandingPromises = [];
799 this._onDrained = new Emitter<void>();
800 }
801 > async.ts
802 > /**
803 > *
804 > * @returns A promise that resolved when all work is done (onDrained) or when
805 > * there is nothing to do
806 > */
807 > whenIdle(): Promise<void> {
808 return this.size > 0
809 ? Event.toPromise(this.onDrained)
810 : Promise.resolve();
811 }
812 > async.ts
813 > get onDrained(): Event<void> {
814 return this._onDrained.event;
815 }
816 > async.ts
817 > get size(): number {
818 return this._size;
819 }
820 > async.ts
821 > queue(factory: ITask<Promise<T>>): Promise<T> {
822 if (this._isDisposed) {
823 throw new Error('Object has been disposed');
830 });
831 }
832 > async.ts
833 > private consume(): void {
834 while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
835 const iLimitedTask = this.outstandingPromises.shift()!;
841 }
842 }
843 > async.ts
844 > private consumed(): void {
845 if (this._isDisposed) {
846 return;
855 }
856 }
857 > async.ts
858 > clear(): void {
859 if (this._isDisposed) {
860 throw new Error('Object has been disposed');
863 this._size = this.runningPromises;
864 }
865 > async.ts
866 > dispose(): void {
867 this._isDisposed = true;
868 this.outstandingPromises.length = 0; // stop further processing
870 this._onDrained.dispose();
871 }
872 > } async.ts
873 >
874 > /**
875 > * A queue is handles one promise at a time and guarantees that at any time only one promise is executing.
876 > */
877 > export class Queue<T> extends Limiter<T> {
878 >
879 > constructor() {
880 super(1);
881 }
882 > } async.ts
883 >
884 > /**
885 > * Same as `Queue`, ensures that only 1 task is executed at the same time. The difference to `Queue` is that
886 > * there is only 1 task about to be scheduled next. As such, calling `queue` while a task is executing will
887 > * replace the currently queued task until it executes.
888 > *
889 > * As such, the returned promise may not be from the factory that is passed in but from the next factory that
890 > * is running after having called `queue`.
891 > */
892 > export class LimitedQueue {
893
894 private readonly sequentializer = new TaskSequentializer();
895
896 private tasks = 0;
897 > async.ts
898 > queue(factory: ITask<Promise<void>>): Promise<void> {
899 if (!this.sequentializer.isRunning()) {
900 return this.sequentializer.run(this.tasks++, factory());
905 });
906 }
907 > } async.ts
908 >
909 > /**
910 > * A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource
911 > * by disposing them once the queue is empty.
912 > */
913 > export class ResourceQueue implements IDisposable {
914
915 private readonly queues = new Map<string, Queue<void>>();
919 private drainListeners: DisposableMap<number> | undefined = undefined;
920 private drainListenerCount = 0;
921 > async.ts
922 > async whenDrained(): Promise<void> {
923 if (this.isDrained()) {
924 return;
930 return promise.p;
931 }
932 > async.ts
933 > private isDrained(): boolean {
934 for (const [, queue] of this.queues) {
935 if (queue.size > 0) {
940 return true;
941 }
942 > async.ts
943 > queueSize(resource: URI, extUri: IExtUri = defaultExtUri): number {
944 const key = extUri.getComparisonKey(resource);
945
946 return this.queues.get(key)?.size ?? 0;
947 }
948 > async.ts
949 > queueFor(resource: URI, factory: ITask<Promise<void>>, extUri: IExtUri = defaultExtUri): Promise<void> {
950 const key = extUri.getComparisonKey(resource);
951
977 return queue.queue(factory);
978 }
979 > async.ts
980 > private onDidQueueDrain(): void {
981 if (!this.isDrained()) {
982 return; // not done yet
985 this.releaseDrainers();
986 }
987 > async.ts
988 > private releaseDrainers(): void {
989 for (const drainer of this.drainers) {
990 drainer.complete();
993 this.drainers.clear();
994 }
995 > async.ts
996 > dispose(): void {
997 for (const [, queue] of this.queues) {
998 queue.dispose();
1011 this.drainListeners?.dispose();
1012 }
1013 > } async.ts
1014 >
1015 > export type Task<T = void> = () => (Promise<T> | T);
1016 >
1017 > /**
1018 > * Wrap a type in an optional promise. This can be useful to avoid the runtime
1019 > * overhead of creating a promise.
1020 > */
1021 > export type MaybePromise<T> = Promise<T> | T;
1022 >
1023 > /**
1024 > * Processes tasks in the order they were scheduled.
1025 > */
1026 > export class TaskQueue {
1027 private _runningTask: Task<any> | undefined = undefined;
1028 private _pendingTasks: { task: Task<any>; deferred: DeferredPromise<any>; setUndefinedWhenCleared: boolean }[] = [];
1029 > async.ts
1030 > /**
1031 > * Waits for the current and pending tasks to finish, then runs and awaits the given task.
1032 > * If the task is skipped because of clearPending, the promise is rejected with a CancellationError.
1033 > */
1034 > public schedule<T>(task: Task<T>): Promise<T> {
1035 const deferred = new DeferredPromise<T>();
1036 this._pendingTasks.push({ task, deferred, setUndefinedWhenCleared: false });
1038 return deferred.p;
1039 }
1040 > async.ts
1041 > /**
1042 > * Waits for the current and pending tasks to finish, then runs and awaits the given task.
1043 > * If the task is skipped because of clearPending, the promise is resolved with undefined.
1044 > */
1045 > public scheduleSkipIfCleared<T>(task: Task<T>): Promise<T | undefined> {
1046 const deferred = new DeferredPromise<T>();
1047 this._pendingTasks.push({ task, deferred, setUndefinedWhenCleared: true });
1049 return deferred.p;
1050 }
1051 > async.ts
1052 > private _runIfNotRunning(): void {
1053 if (this._runningTask === undefined) {
1054 this._processQueue();
1055 }
1056 }
1057 > async.ts
1058 > private async _processQueue(): Promise<void> {
1059 if (this._pendingTasks.length === 0) {
1060 return;
1082 }
1083 }
1084 > async.ts
1085 > /**
1086 > * Clears all pending tasks. Does not cancel the currently running task.
1087 > */
1088 > public clearPending(): void {
1089 const tasks = this._pendingTasks;
1090 this._pendingTasks = [];
1097 }
1098 }
1099 > } async.ts
1100 >
1101 > export class TimeoutTimer implements IDisposable {
1102 > private _token: Timeout | undefined;
1103 > private _isDisposed = false;
1104 >
1105 > constructor();
1106 > constructor(runner: () => void, timeout: number);
1107 > constructor(runner?: () => void, timeout?: number) {
1108 this._token = undefined;
1109
1112 }
1113 }
1114 > async.ts
1115 > dispose(): void {
1116 this.cancel();
1117 this._isDisposed = true;
1118 }
1119 > async.ts
1120 > cancel(): void {
1121 if (this._token !== undefined) {
1122 clearTimeout(this._token);
1124 }
1125 }
1126 > async.ts
1127 > cancelAndSet(runner: () => void, timeout: number): void {
1128 if (this._isDisposed) {
1129 throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed TimeoutTimer`);
1136 }, timeout);
1137 }
1138 > async.ts
1139 > setIfNotSet(runner: () => void, timeout: number): void {
1140 if (this._isDisposed) {
1141 throw new BugIndicatingError(`Calling 'setIfNotSet' on a disposed TimeoutTimer`);
1151 }, timeout);
1152 }
1153 > } async.ts
1154 >
1155 > export class IntervalTimer implements IDisposable {
1156
1157 private disposable: IDisposable | undefined = undefined;
1158 private isDisposed = false;
1159 > async.ts
1160 > cancel(): void {
1161 this.disposable?.dispose();
1162 this.disposable = undefined;
1163 }
1164 > async.ts
1165 > cancelAndSet(runner: () => void, interval: number, context = globalThis): void {
1166 if (this.isDisposed) {
1167 throw new BugIndicatingError(`Calling 'cancelAndSet' on a disposed IntervalTimer`);
1178 });
1179 }
1180 > async.ts
1181 > dispose(): void {
1182 this.cancel();
1183 this.isDisposed = true;
1184 }
1185 > } async.ts
1186 >
1187 > export class RunOnceScheduler<Runner extends (...args: any[]) => any = () => any> implements IDisposable {
1188 >
1189 > protected runner: Runner | null;
1190 >
1191 > private timeoutToken: Timeout | undefined;
1192 > private timeout: number;
1193 > private timeoutHandler: () => void;
1194 >
1195 > constructor(runner: Runner, delay: number) {
1196 > this.timeoutToken = undefined; async.ts
1197 > this.runner = runner;
1198 > this.timeout = delay;
1199 > this.timeoutHandler = this.onTimeout.bind(this);
1200 > }
1201 > async.ts
1202 > /**
1203 > * Dispose RunOnceScheduler
1204 > */
1205 > dispose(): void {
1206 > this.cancel(); async.ts
1207 > this.runner = null;
1208 > }
1209 > async.ts
1210 > /**
1211 > * Cancel current scheduled runner (if any).
1212 > */
1213 > cancel(): void {
1214 > if (this.isScheduled()) { async.ts
1215 clearTimeout(this.timeoutToken);
1216 this.timeoutToken = undefined;
1217 }
1218 > } async.ts
1219 > async.ts
1220 > /**
1221 > * Cancel previous runner (if any) & schedule a new runner.
1222 > */
1223 > schedule(delay = this.timeout): void {
1224 > this.cancel(); async.ts
1225 > this.timeoutToken = setTimeout(this.timeoutHandler, delay);
1226 > }
1227 > async.ts
1228 > get delay(): number {
1229 return this.timeout;
1230 }
1231 > async.ts
1232 > set delay(value: number) {
1233 this.timeout = value;
1234 }
1235 > async.ts
1236 > /**
1237 > * Returns true if scheduled.
1238 > */
1239 > isScheduled(): boolean {
1240 > return this.timeoutToken !== undefined; async.ts
1241 > }
1242 > async.ts
1243 > flush(): void {
1244 if (this.isScheduled()) {
1245 this.cancel();
1247 }
1248 }
1249 > async.ts
1250 > private onTimeout() {
1251 this.timeoutToken = undefined;
1252 if (this.runner) {
1254 }
1255 }
1256 > async.ts
1257 > protected doRun(): void {
1258 this.runner?.();
1259 }
1260 > } async.ts
1261 >
1262 > /**
1263 > * Same as `RunOnceScheduler`, but doesn't count the time spent in sleep mode.
1264 > * > **NOTE**: Only offers 1s resolution.
1265 > *
1266 > * When calling `setTimeout` with 3hrs, and putting the computer immediately to sleep
1267 > * for 8hrs, `setTimeout` will fire **as soon as the computer wakes from sleep**. But
1268 > * this scheduler will execute 3hrs **after waking the computer from sleep**.
1269 > */
1270 > export class ProcessTimeRunOnceScheduler {
1271 >
1272 > private runner: (() => void) | null;
1273 > private timeout: number;
1274 >
1275 > private counter: number;
1276 > private intervalToken: Timeout | undefined;
1277 > private intervalHandler: () => void;
1278 >
1279 > constructor(runner: () => void, delay: number) {
1280 if (delay % 1000 !== 0) {
1281 console.warn(`ProcessTimeRunOnceScheduler resolution is 1s, ${delay}ms is not a multiple of 1000ms.`);
1287 this.intervalHandler = this.onInterval.bind(this);
1288 }
1289 > async.ts
1290 > dispose(): void {
1291 this.cancel();
1292 this.runner = null;
1293 }
1294 > async.ts
1295 > cancel(): void {
1296 if (this.isScheduled()) {
1297 clearInterval(this.intervalToken);
1299 }
1300 }
1301 > async.ts
1302 > /**
1303 > * Cancel previous runner (if any) & schedule a new runner.
1304 > */
1305 > schedule(delay = this.timeout): void {
1306 if (delay % 1000 !== 0) {
1307 console.warn(`ProcessTimeRunOnceScheduler resolution is 1s, ${delay}ms is not a multiple of 1000ms.`);
1311 this.intervalToken = setInterval(this.intervalHandler, 1000);
1312 }
1313 > async.ts
1314 > /**
1315 > * Returns true if scheduled.
1316 > */
1317 > isScheduled(): boolean {
1318 return this.intervalToken !== undefined;
1319 }
1320 > async.ts
1321 > private onInterval() {
1322 this.counter--;
1323 if (this.counter > 0) {
1331 this.runner?.();
1332 }
1333 > } async.ts
1334 >
1335 > export class RunOnceWorker<T> extends RunOnceScheduler<(units: T[]) => void> {
1336 >
1337 > private units: T[] = [];
1338 >
1339 > constructor(runner: (units: T[]) => void, timeout: number) {
1340 super(runner, timeout);
1341 }
1342 > async.ts
1343 > work(unit: T): void {
1344 this.units.push(unit);
1345
1348 }
1349 }
1350 > async.ts
1351 > protected override doRun(): void {
1352 const units = this.units;
1353 this.units = [];
1355 this.runner?.(units);
1356 }
1357 > async.ts
1358 > override dispose(): void {
1359 this.units = [];
1360
1361 super.dispose();
1362 }
1363 > } async.ts
1364 >
1365 > export interface IThrottledWorkerOptions {
1366 >
1367 > /**
1368 > * maximum of units the worker will pass onto handler at once
1369 > */
1370 > maxWorkChunkSize: number;
1371 >
1372 > /**
1373 > * maximum of units the worker will keep in memory for processing
1374 > */
1375 > maxBufferedWork: number | undefined;
1376 >
1377 > /**
1378 > * delay before processing the next round of chunks when chunk size exceeds limits
1379 > */
1380 > throttleDelay: number;
1381 >
1382 > /**
1383 > * When enabled will guarantee that two distinct calls to `work()` are not executed
1384 > * without throttle delay between them.
1385 > * Otherwise if the worker isn't currently throttling it will execute work immediately.
1386 > */
1387 > waitThrottleDelayBetweenWorkUnits?: boolean;
1388 > }
1389 >
1390 > /**
1391 > * The `ThrottledWorker` will accept units of work `T`
1392 > * to handle. The contract is:
1393 > * * there is a maximum of units the worker can handle at once (via `maxWorkChunkSize`)
1394 > * * there is a maximum of units the worker will keep in memory for processing (via `maxBufferedWork`)
1395 > * * after having handled `maxWorkChunkSize` units, the worker needs to rest (via `throttleDelay`)
1396 > */
1397 > export class ThrottledWorker<T> extends Disposable {
1398 >
1399 > private readonly pendingWork: T[] = [];
1400 >
1401 > private readonly throttler = this._register(new MutableDisposable<RunOnceScheduler>());
1402 > private disposed = false;
1403 > private lastExecutionTime = 0;
1404 >
1405 > constructor(
1406 private options: IThrottledWorkerOptions,
1407 private readonly handler: (units: T[]) => void
1409 super();
1410 }
1411 > async.ts
1412 > /**
1413 > * The number of work units that are pending to be processed.
1414 > */
1415 > get pending(): number { return this.pendingWork.length; }
1416 >
1417 > /**
1418 > * Add units to be worked on. Use `pending` to figure out
1419 > * how many units are not yet processed after this method
1420 > * was called.
1421 > *
1422 > * @returns whether the work was accepted or not. If the
1423 > * worker is disposed, it will not accept any more work.
1424 > * If the number of pending units would become larger
1425 > * than `maxPendingWork`, more work will also not be accepted.
1426 > */
1427 > work(units: readonly T[]): boolean {
1428 if (this.disposed) {
1429 return false; // work not accepted: disposed
1469 return true; // work accepted
1470 }
1471 > async.ts
1472 > private doWork(): void {
1473 this.lastExecutionTime = Date.now();
1474
1481 }
1482 }
1483 > async.ts
1484 > private scheduleThrottler(delay = this.options.throttleDelay): void {
1485 this.throttler.value = new RunOnceScheduler(() => {
1486 this.throttler.clear();
1490 this.throttler.value.schedule();
1491 }
1492 > async.ts
1493 > override dispose(): void {
1494 super.dispose();
1495
1497 this.disposed = true;
1498 }
1499 > } async.ts
1500 >
1501 > //#region -- run on idle tricks ------------
1502 >
1503 > export interface IdleDeadline {
1504 > readonly didTimeout: boolean;
1505 > timeRemaining(): number;
1506 > }
1507 >
1508 > type IdleApi = Pick<typeof globalThis, 'requestIdleCallback' | 'cancelIdleCallback'>;
1509 >
1510 >
1511 > /**
1512 > * Execute the callback the next time the browser is idle, returning an
1513 > * {@link IDisposable} that will cancel the callback when disposed. This wraps
1514 > * [requestIdleCallback] so it will fallback to [setTimeout] if the environment
1515 > * doesn't support it.
1516 > *
1517 > * @param callback The callback to run when idle, this includes an
1518 > * [IdleDeadline] that provides the time alloted for the idle callback by the
1519 > * browser. Not respecting this deadline will result in a degraded user
1520 > * experience.
1521 > * @param timeout A timeout at which point to queue no longer wait for an idle
1522 > * callback but queue it on the regular event loop (like setTimeout). Typically
1523 > * this should not be used.
1524 > *
1525 > * [IdleDeadline]: https://developer.mozilla.org/en-US/docs/Web/API/IdleDeadline
1526 > * [requestIdleCallback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
1527 > * [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout
1528 > *
1529 > * **Note** that there is `dom.ts#runWhenWindowIdle` which is better suited when running inside a browser
1530 > * context
1531 > */
1532 > export let runWhenGlobalIdle: (callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable;
1533 >
1534 > export let _runWhenIdle: (targetWindow: IdleApi, callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable;
1535 >
1536 > (function () {
1537 > const safeGlobal: any = globalThis;
1538 > if (typeof safeGlobal.requestIdleCallback !== 'function' || typeof safeGlobal.cancelIdleCallback !== 'function') {
1539 > _runWhenIdle = (_targetWindow, runner, timeout?) => {
1540 setTimeout0(() => {
1541 if (disposed) {
1561 };
1562 };
1563 > } else { async.ts
1564 _runWhenIdle = (targetWindow: typeof safeGlobal, runner, timeout?) => {
1565 const handle: number = targetWindow.requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined);
1576 };
1577 }
1578 > runWhenGlobalIdle = (runner, timeout) => _runWhenIdle(globalThis, runner, timeout); async.ts
1579 > })();
1580 >
1581 > export function installFakeRunWhenIdle(fakeImpl: typeof _runWhenIdle): IDisposable {
1582 const origRunWhenIdle = _runWhenIdle;
1583 const origRunWhenGlobalIdle = runWhenGlobalIdle;
1589 });
1590 }
1591 > async.ts
1592 > export abstract class AbstractIdleValue<T> {
1593 >
1594 > private readonly _executor: () => void;
1595 > private readonly _handle: IDisposable;
1596 >
1597 > private _didRun: boolean = false;
1598 > private _value?: T;
1599 > private _error: unknown;
1600 >
1601 > constructor(targetWindow: IdleApi, executor: () => T) {
1602 this._executor = () => {
1603 try {
1611 this._handle = _runWhenIdle(targetWindow, () => this._executor());
1612 }
1613 > async.ts
1614 > dispose(): void {
1615 this._handle.dispose();
1616 }
1617 > async.ts
1618 > get value(): T {
1619 if (!this._didRun) {
1620 this._handle.dispose();
1626 return this._value!;
1627 }
1628 > async.ts
1629 > get isInitialized(): boolean {
1630 return this._didRun;
1631 }
1632 > } async.ts
1633 >
1634 > /**
1635 > * An `IdleValue` that always uses the current window (which might be throttled or inactive)
1636 > *
1637 > * **Note** that there is `dom.ts#WindowIdleValue` which is better suited when running inside a browser
1638 > * context
1639 > */
1640 > export class GlobalIdleValue<T> extends AbstractIdleValue<T> {
1641 >
1642 > constructor(executor: () => T) {
1643 super(globalThis, executor);
1644 }
1645 > } async.ts
1646 >
1647 > //#endregion
1648 >
1649 export async function retry<T>(task: ITask<Promise<T>>, delay: number, retries: number): Promise<T> {
1650 let lastError: Error | undefined;
1662 throw lastError;
1663 }
1664 > async.ts
1665 > //#region Task Sequentializer
1666 >
1667 > interface IRunningTask {
1668 > readonly taskId: number;
1669 > readonly cancel: () => void;
1670 > readonly promise: Promise<void>;
1671 > }
1672 >
1673 > interface IQueuedTask {
1674 > readonly promise: Promise<void>;
1675 > readonly promiseResolve: () => void;
1676 > readonly promiseReject: (error: Error) => void;
1677 > run: ITask<Promise<void>>;
1678 > }
1679 >
1680 > export interface ITaskSequentializerWithRunningTask {
1681 > readonly running: Promise<void>;
1682 > }
1683 >
1684 > export interface ITaskSequentializerWithQueuedTask {
1685 > readonly queued: IQueuedTask;
1686 > }
1687 >
1688 > /**
1689 > * @deprecated use `LimitedQueue` instead for an easier to use API
1690 > */
1691 > export class TaskSequentializer {
1692 >
1693 > private _running?: IRunningTask;
1694 > private _queued?: IQueuedTask;
1695 >
1696 > isRunning(taskId?: number): this is ITaskSequentializerWithRunningTask {
1697 if (typeof taskId === 'number') {
1698 return this._running?.taskId === taskId;
1701 return !!this._running;
1702 }
1703 > async.ts
1704 > get running(): Promise<void> | undefined {
1705 return this._running?.promise;
1706 }
1707 > async.ts
1708 > cancelRunning(): void {
1709 this._running?.cancel();
1710 }
1711 > async.ts
1712 > run(taskId: number, promise: Promise<void>, onCancel?: () => void,): Promise<void> {
1713 this._running = { taskId, cancel: () => onCancel?.(), promise };
1714
1717 return promise;
1718 }
1719 > async.ts
1720 > private doneRunning(taskId: number): void {
1721 if (this._running && taskId === this._running.taskId) {
1722
1728 }
1729 }
1730 > async.ts
1731 > private runQueued(): void {
1732 if (this._queued) {
1733 const queued = this._queued;
1738 }
1739 }
1740 > async.ts
1741 > /**
1742 > * Note: the promise to schedule as next run MUST itself call `run`.
1743 > * Otherwise, this sequentializer will report `false` for `isRunning`
1744 > * even when this task is running. Missing this detail means that
1745 > * suddenly multiple tasks will run in parallel.
1746 > */
1747 > queue(run: ITask<Promise<void>>): Promise<void> {
1748
1749 // this is our first queued task, so we create associated promise with it
1767 return this._queued.promise;
1768 }
1769 > async.ts
1770 > hasQueued(): this is ITaskSequentializerWithQueuedTask {
1771 return !!this._queued;
1772 }
1773 > async.ts
1774 > async join(): Promise<void> {
1775 return this._queued?.promise ?? this._running?.promise;
1776 }
1777 > } async.ts
1778 >
1779 > //#endregion
1780 >
1781 > //#region
1782 >
1783 > /**
1784 > * The `IntervalCounter` allows to count the number
1785 > * of calls to `increment()` over a duration of
1786 > * `interval`. This utility can be used to conditionally
1787 > * throttle a frequent task when a certain threshold
1788 > * is reached.
1789 > */
1790 > export class IntervalCounter {
1791 >
1792 > private lastIncrementTime = 0;
1793 >
1794 > private value = 0;
1795 >
1796 > constructor(private readonly interval: number, private readonly nowFn = () => Date.now()) { }
1797 >
1798 > increment(): number {
1799 const now = this.nowFn();
1800
1810 return this.value;
1811 }
1812 > } async.ts
1813 >
1814 > //#endregion
1815 >
1816 > //#region
1817 >
1818 > export type ValueCallback<T = unknown> = (value: T | Promise<T>) => void;
1819 >
1820 > const enum DeferredOutcome {
1821 > Resolved,
1822 > Rejected
1823 > }
1824 >
1825 > /**
1826 > * Creates a promise whose resolution or rejection can be controlled imperatively.
1827 > */
1828 > export class DeferredPromise<T> {
1829 >
1830 > public static fromPromise<T>(promise: Promise<T>): DeferredPromise<T> {
1831 const deferred = new DeferredPromise<T>();
1832 deferred.settleWith(promise);
1833 return deferred;
1834 }
1835 > async.ts
1836 > private completeCallback!: ValueCallback<T>;
1837 > private errorCallback!: (err: unknown) => void;
1838 > private outcome?: { outcome: DeferredOutcome.Rejected; value: unknown } | { outcome: DeferredOutcome.Resolved; value: T };
1839 >
1840 > public get isRejected() {
1841 return this.outcome?.outcome === DeferredOutcome.Rejected;
1842 }
1843 > async.ts
1844 > public get isResolved() {
1845 return this.outcome?.outcome === DeferredOutcome.Resolved;
1846 }
1847 > async.ts
1848 > public get isSettled() {
1849 return !!this.outcome;
1850 }
1851 > async.ts
1852 > public get value() {
1853 return this.outcome?.outcome === DeferredOutcome.Resolved ? this.outcome?.value : undefined;
1854 }
1855 > async.ts
1856 > public readonly p: Promise<T>;
1857 >
1858 > constructor() {
1859 this.p = new Promise<T>((c, e) => {
1860 this.completeCallback = c;
1862 });
1863 }
1864 > async.ts
1865 > public complete(value: T) {
1866 if (this.isSettled) {
1867 return Promise.resolve();
1874 });
1875 }
1876 > async.ts
1877 > public error(err: unknown) {
1878 if (this.isSettled) {
1879 return Promise.resolve();
1886 });
1887 }
1888 > async.ts
1889 > public settleWith(promise: Promise<T>): Promise<void> {
1890 return promise.then(
1891 value => this.complete(value),
1893 );
1894 }
1895 > async.ts
1896 > public cancel() {
1897 return this.error(new CancellationError());
1898 }
1899 > } async.ts
1900 >
1901 > //#endregion
1902 >
1903 > //#region Promises
1904 >
1905 > export namespace Promises {
1906 >
1907 > /**
1908 > * A drop-in replacement for `Promise.all` with the only difference
1909 > * that the method awaits every promise to either fulfill or reject.
1910 > *
1911 > * Similar to `Promise.all`, only the first error will be returned
1912 > * if any.
1913 > */
1914 > export async function settled<T>(promises: Promise<T>[]): Promise<T[]> {
1915 let firstError: Error | undefined = undefined;
1916
1929 return result as unknown as T[]; // cast is needed and protected by the `throw` above
1930 }
1931 > async.ts
1932 > /**
1933 > * A helper to create a new `Promise<T>` with a body that is a promise
1934 > * itself. By default, an error that raises from the async body will
1935 > * end up as a unhandled rejection, so this utility properly awaits the
1936 > * body and rejects the promise as a normal promise does without async
1937 > * body.
1938 > *
1939 > * This method should only be used in rare cases where otherwise `async`
1940 > * cannot be used (e.g. when callbacks are involved that require this).
1941 > */
1942 > export function withAsyncBody<T, E = Error>(bodyFn: (resolve: (value: T) => unknown, reject: (error: E) => unknown) => Promise<unknown>): Promise<T> {
1943 // eslint-disable-next-line no-async-promise-executor
1944 return new Promise<T>(async (resolve, reject) => {
1950 });
1951 }
1952 > } async.ts
1953 >
1954 > export class StatefulPromise<T> {
1955 > private _value: T | undefined = undefined;
1956 > get value(): T | undefined { return this._value; }
1957 >
1958 > private _error: unknown = undefined;
1959 > get error(): unknown { return this._error; }
1960 >
1961 > private _isResolved = false;
1962 > get isResolved() { return this._isResolved; }
1963 >
1964 > public readonly promise: Promise<T>;
1965 >
1966 > constructor(promise: Promise<T>) {
1967 this.promise = promise.then(
1968 value => {
1978 );
1979 }
1980 > async.ts
1981 > /**
1982 > * Returns the resolved value.
1983 > * Throws if the promise is not resolved yet.
1984 > */
1985 > public requireValue(): T {
1986 if (!this._isResolved) {
1987 throw new BugIndicatingError('Promise is not resolved yet');
1992 return this._value!;
1993 }
1994 > } async.ts
1995 >
1996 > export class LazyStatefulPromise<T> {
1997 > private readonly _promise = new Lazy(() => new StatefulPromise(this._compute()));
1998 >
1999 > constructor(
2000 private readonly _compute: () => Promise<T>,
2001 ) { }
2002 > async.ts
2003 > /**
2004 > * Returns the resolved value.
2005 > * Throws if the promise is not resolved yet.
2006 > */
2007 > public requireValue(): T {
2008 return this._promise.value.requireValue();
2009 }
2010 > async.ts
2011 > /**
2012 > * Returns the promise (and triggers a computation of the promise if not yet done so).
2013 > */
2014 > public getPromise(): Promise<T> {
2015 return this._promise.value.promise;
2016 }
2017 > async.ts
2018 > /**
2019 > * Reads the current value without triggering a computation of the promise.
2020 > */
2021 > public get currentValue(): T | undefined {
2022 return this._promise.rawValue?.value;
2023 }
2024 > } async.ts
2025 >
2026 > //#endregion
2027 >
2028 > //#region
2029 >
2030 > const enum AsyncIterableSourceState {
2031 > Initial,
2032 > DoneOK,
2033 > DoneError,
2034 > }
2035 >
2036 > /**
2037 > * An object that allows to emit async values asynchronously or bring the iterable to an error state using `reject()`.
2038 > * This emitter is valid only for the duration of the executor (until the promise returned by the executor settles).
2039 > */
2040 > export interface AsyncIterableEmitter<T> {
2041 > /**
2042 > * The value will be appended at the end.
2043 > *
2044 > * **NOTE** If `reject()` has already been called, this method has no effect.
2045 > */
2046 > emitOne(value: T): void;
2047 > /**
2048 > * The values will be appended at the end.
2049 > *
2050 > * **NOTE** If `reject()` has already been called, this method has no effect.
2051 > */
2052 > emitMany(values: T[]): void;
2053 > /**
2054 > * Writing an error will permanently invalidate this iterable.
2055 > * The current users will receive an error thrown, as will all future users.
2056 > *
2057 > * **NOTE** If `reject()` have already been called, this method has no effect.
2058 > */
2059 > reject(error: Error): void;
2060 > }
2061 >
2062 > /**
2063 > * An executor for the `AsyncIterableObject` that has access to an emitter.
2064 > */
2065 > export interface AsyncIterableExecutor<T> {
2066 > /**
2067 > * @param emitter An object that allows to emit async values valid only for the duration of the executor.
2068 > */
2069 > (emitter: AsyncIterableEmitter<T>): unknown | Promise<unknown>;
2070 > }
2071 >
2072 > /**
2073 > * A rich implementation for an `AsyncIterable<T>`.
2074 > */
2075 > export class AsyncIterableObject<T> implements AsyncIterable<T> {
2076 >
2077 > public static fromArray<T>(items: T[]): AsyncIterableObject<T> {
2078 > return new AsyncIterableObject<T>((writer) => {
2079 > writer.emitMany(items);
2080 > });
2081 > }
2082 >
2083 > public static fromPromise<T>(promise: Promise<T[]>): AsyncIterableObject<T> {
2084 return new AsyncIterableObject<T>(async (emitter) => {
2085 emitter.emitMany(await promise);
2086 });
2087 }
2088 > async.ts
2089 > public static fromPromisesResolveOrder<T>(promises: Promise<T>[]): AsyncIterableObject<T> {
2090 return new AsyncIterableObject<T>(async (emitter) => {
2091 await Promise.all(promises.map(async (p) => emitter.emitOne(await p)));
2092 });
2093 }
2094 > async.ts
2095 > public static merge<T>(iterables: AsyncIterable<T>[]): AsyncIterableObject<T> {
2096 return new AsyncIterableObject(async (emitter) => {
2097 await Promise.all(iterables.map(async (iterable) => {
2102 });
2103 }
2104 > async.ts
2105 > public static EMPTY = AsyncIterableObject.fromArray<any>([]);
2106 >
2107 > private _state: AsyncIterableSourceState;
2108 > private _results: T[];
2109 > private _error: Error | null;
2110 > private readonly _onReturn?: () => void | Promise<void>;
2111 > private readonly _onStateChanged: Emitter<void>;
2112 >
2113 > constructor(executor: AsyncIterableExecutor<T>, onReturn?: () => void | Promise<void>) {
2114 > this._state = AsyncIterableSourceState.Initial;
2115 > this._results = [];
2116 > this._error = null;
2117 > this._onReturn = onReturn;
2118 > this._onStateChanged = new Emitter<void>();
2119 >
2120 > queueMicrotask(async () => {
2121 > const writer: AsyncIterableEmitter<T> = {
2122 > emitOne: (item) => this.emitOne(item),
2123 > emitMany: (items) => this.emitMany(items),
2124 > reject: (error) => this.reject(error)
2125 > };
2126 > try {
2127 > await Promise.resolve(executor(writer));
2128 > this.resolve();
2129 > } catch (err) {
2130 this.reject(err);
2131 > } finally { async.ts
2132 > // The executor has settled; emitting afterwards must be a no-op per the
2133 > // documented "no effect after resolve()/reject()" contract (see emitOne).
2134 > writer.emitOne = () => { };
2135 > writer.emitMany = () => { };
2136 > writer.reject = () => { };
2137 > }
2138 > });
2139 > }
2140 >
2141 > [Symbol.asyncIterator](): AsyncIterator<T, undefined, undefined> {
2142 let i = 0;
2143 return {
2162 };
2163 }
2164 > async.ts
2165 > public static map<T, R>(iterable: AsyncIterable<T>, mapFn: (item: T) => R): AsyncIterableObject<R> {
2166 return new AsyncIterableObject<R>(async (emitter) => {
2167 for await (const item of iterable) {
2170 });
2171 }
2172 > async.ts
2173 > public map<R>(mapFn: (item: T) => R): AsyncIterableObject<R> {
2174 return AsyncIterableObject.map(this, mapFn);
2175 }
2176 > async.ts
2177 > public static filter<T>(iterable: AsyncIterable<T>, filterFn: (item: T) => boolean): AsyncIterableObject<T> {
2178 return new AsyncIterableObject<T>(async (emitter) => {
2179 for await (const item of iterable) {
2184 });
2185 }
2186 > async.ts
2187 > public filter<T2 extends T>(filterFn: (item: T) => item is T2): AsyncIterableObject<T2>;
2188 > public filter(filterFn: (item: T) => boolean): AsyncIterableObject<T>;
2189 > public filter(filterFn: (item: T) => boolean): AsyncIterableObject<T> {
2190 return AsyncIterableObject.filter(this, filterFn);
2191 }
2192 > async.ts
2193 > public static coalesce<T>(iterable: AsyncIterable<T | undefined | null>): AsyncIterableObject<T> {
2194 return <AsyncIterableObject<T>>AsyncIterableObject.filter(iterable, item => !!item);
2195 }
2196 > async.ts
2197 > public coalesce(): AsyncIterableObject<NonNullable<T>> {
2198 return AsyncIterableObject.coalesce(this) as AsyncIterableObject<NonNullable<T>>;
2199 }
2200 > async.ts
2201 > public static async toPromise<T>(iterable: AsyncIterable<T>): Promise<T[]> {
2202 const result: T[] = [];
2203 for await (const item of iterable) {
2206 return result;
2207 }
2208 > async.ts
2209 > public toPromise(): Promise<T[]> {
2210 return AsyncIterableObject.toPromise(this);
2211 }
2212 > async.ts
2213 > /**
2214 > * The value will be appended at the end.
2215 > *
2216 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2217 > */
2218 > private emitOne(value: T): void {
2219 if (this._state !== AsyncIterableSourceState.Initial) {
2220 return;
2225 this._onStateChanged.fire();
2226 }
2227 > async.ts
2228 > /**
2229 > * The values will be appended at the end.
2230 > *
2231 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2232 > */
2233 > private emitMany(values: T[]): void {
2234 > if (this._state !== AsyncIterableSourceState.Initial) {
2235 return;
2236 }
2237 > // it is important to add new values at the end, async.ts
2238 > // as we may have iterators already running on the array
2239 > this._results = this._results.concat(values);
2240 > this._onStateChanged.fire();
2241 > }
2242 >
2243 > /**
2244 > * Calling `resolve()` will mark the result array as complete.
2245 > *
2246 > * **NOTE** `resolve()` must be called, otherwise all consumers of this iterable will hang indefinitely, similar to a non-resolved promise.
2247 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2248 > */
2249 > private resolve(): void {
2250 > if (this._state !== AsyncIterableSourceState.Initial) {
2251 return;
2252 }
2253 > this._state = AsyncIterableSourceState.DoneOK; async.ts
2254 > this._onStateChanged.fire();
2255 > }
2256 >
2257 > /**
2258 > * Writing an error will permanently invalidate this iterable.
2259 > * The current users will receive an error thrown, as will all future users.
2260 > *
2261 > * **NOTE** If `resolve()` or `reject()` have already been called, this method has no effect.
2262 > */
2263 > private reject(error: Error) {
2264 if (this._state !== AsyncIterableSourceState.Initial) {
2265 return;
2269 this._onStateChanged.fire();
2270 }
2271 > } async.ts
2272 >
2273 >
2274 > export function createCancelableAsyncIterableProducer<T>(callback: (token: CancellationToken) => AsyncIterable<T>): CancelableAsyncIterableProducer<T> {
2275 const source = new CancellationTokenSource();
2276 const innerIterable = callback(source.token);
2299 });
2300 }
2301 > async.ts
2302 > export class AsyncIterableSource<T> {
2303 >
2304 > private readonly _deferred = new DeferredPromise<void>();
2305 > private readonly _asyncIterable: AsyncIterableObject<T>;
2306 >
2307 > private _errorFn: (error: Error) => void;
2308 > private _emitOneFn: (item: T) => void;
2309 > private _emitManyFn: (item: T[]) => void;
2310 >
2311 > /**
2312 > *
2313 > * @param onReturn A function that will be called when consuming the async iterable
2314 > * has finished by the consumer, e.g the for-await-loop has be existed (break, return) early.
2315 > * This is NOT called when resolving this source by its owner.
2316 > */
2317 > constructor(onReturn?: () => Promise<void> | void) {
2318 this._asyncIterable = new AsyncIterableObject(emitter => {
2319
2354 };
2355 }
2356 > async.ts
2357 > get asyncIterable(): AsyncIterableObject<T> {
2358 return this._asyncIterable;
2359 }
2360 > async.ts
2361 > resolve(): void {
2362 this._deferred.complete();
2363 }
2364 > async.ts
2365 > reject(error: Error): void {
2366 this._errorFn(error);
2367 this._deferred.complete();
2368 }
2369 > async.ts
2370 > emitOne(item: T): void {
2371 this._emitOneFn(item);
2372 }
2373 > async.ts
2374 > emitMany(items: T[]) {
2375 this._emitManyFn(items);
2376 }
2377 > } async.ts
2378 >
2379 > export function cancellableIterable<T>(iterableOrIterator: AsyncIterator<T> | AsyncIterable<T>, token: CancellationToken): AsyncIterableIterator<T> {
2380 const iterator = Symbol.asyncIterator in iterableOrIterator ? iterableOrIterator[Symbol.asyncIterator]() : iterableOrIterator;
2381
2395 };
2396 }
2397 > async.ts
2398 > type ProducerConsumerValue<T> = {
2399 > ok: true;
2400 > value: T;
2401 > } | {
2402 > ok: false;
2403 > error: Error;
2404 > };
2405 >
2406 > class ProducerConsumer<T> {
2407 > private readonly _unsatisfiedConsumers: DeferredPromise<T>[] = [];
2408 > private readonly _unconsumedValues: ProducerConsumerValue<T>[] = [];
2409 > private _finalValue: ProducerConsumerValue<T> | undefined;
2410 >
2411 > public get hasFinalValue(): boolean {
2412 > return !!this._finalValue;
2413 > }
2414 >
2415 > produce(value: ProducerConsumerValue<T>): void {
2416 this._ensureNoFinalValue();
2417 if (this._unsatisfiedConsumers.length > 0) {
2422 }
2423 }
2424 > async.ts
2425 > produceFinal(value: ProducerConsumerValue<T>): void {
2426 > this._ensureNoFinalValue();
2427 > this._finalValue = value;
2428 > for (const deferred of this._unsatisfiedConsumers) {
2429 this._resolveOrRejectDeferred(deferred, value);
2430 }
2431 > this._unsatisfiedConsumers.length = 0; async.ts
2432 > }
2433 >
2434 > private _ensureNoFinalValue(): void {
2435 > if (this._finalValue) {
2436 throw new BugIndicatingError('ProducerConsumer: cannot produce after final value has been set');
2437 }
2438 > } async.ts
2439 >
2440 > private _resolveOrRejectDeferred(deferred: DeferredPromise<T>, value: ProducerConsumerValue<T>): void {
2441 if (value.ok) {
2442 deferred.complete(value.value);
2445 }
2446 }
2447 > async.ts
2448 > consume(): Promise<T> {
2449 if (this._unconsumedValues.length > 0 || this._finalValue) {
2450 const value = this._unconsumedValues.length > 0 ? this._unconsumedValues.shift()! : this._finalValue!;
2460 }
2461 }
2462 > } async.ts
2463 >
2464 > /**
2465 > * Important difference to AsyncIterableObject:
2466 > * If it is iterated two times, the second iterator will not see the values emitted by the first iterator.
2467 > */
2468 > export class AsyncIterableProducer<T> implements AsyncIterable<T> {
2469 > private readonly _producerConsumer = new ProducerConsumer<IteratorResult<T>>();
2470 >
2471 > constructor(executor: AsyncIterableExecutor<T>, private readonly _onReturn?: () => void) {
2472 > queueMicrotask(async () => {
2473 > const p = executor({
2474 > emitOne: value => this._producerConsumer.produce({ ok: true, value: { done: false, value: value } }),
2475 > emitMany: values => {
2476 > for (const value of values) {
2477 this._producerConsumer.produce({ ok: true, value: { done: false, value: value } });
2478 }
2479 > }, async.ts
2480 > reject: error => this._finishError(error),
2481 > });
2482 >
2483 > if (!this._producerConsumer.hasFinalValue) {
2484 > try {
2485 > await p;
2486 > this._finishOk();
2487 > } catch (error) {
2488 this._finishError(error);
2489 }
2490 > } async.ts
2491 > });
2492 > }
2493 >
2494 > public static fromArray<T>(items: T[]): AsyncIterableProducer<T> {
2495 > return new AsyncIterableProducer<T>((writer) => {
2496 > writer.emitMany(items);
2497 > });
2498 > }
2499 >
2500 > public static fromPromise<T>(promise: Promise<T[]>): AsyncIterableProducer<T> {
2501 return new AsyncIterableProducer<T>(async (emitter) => {
2502 emitter.emitMany(await promise);
2503 });
2504 }
2505 > async.ts
2506 > public static fromPromisesResolveOrder<T>(promises: Promise<T>[]): AsyncIterableProducer<T> {
2507 return new AsyncIterableProducer<T>(async (emitter) => {
2508 await Promise.all(promises.map(async (p) => emitter.emitOne(await p)));
2509 });
2510 }
2511 > async.ts
2512 > public static merge<T>(iterables: AsyncIterable<T>[]): AsyncIterableProducer<T> {
2513 return new AsyncIterableProducer(async (emitter) => {
2514 await Promise.all(iterables.map(async (iterable) => {
2519 });
2520 }
2521 > async.ts
2522 > public static EMPTY = AsyncIterableProducer.fromArray<any>([]);
2523 >
2524 > public static map<T, R>(iterable: AsyncIterable<T>, mapFn: (item: T) => R): AsyncIterableProducer<R> {
2525 return new AsyncIterableProducer<R>(async (emitter) => {
2526 for await (const item of iterable) {
2529 });
2530 }
2531 > async.ts
2532 > public static tee<T>(iterable: AsyncIterable<T>): [AsyncIterableProducer<T>, AsyncIterableProducer<T>] {
2533 let emitter1: AsyncIterableEmitter<T> | undefined;
2534 let emitter2: AsyncIterableEmitter<T> | undefined;
2565 return [p1, p2];
2566 }
2567 > async.ts
2568 > public map<R>(mapFn: (item: T) => R): AsyncIterableProducer<R> {
2569 return AsyncIterableProducer.map(this, mapFn);
2570 }
2571 > async.ts
2572 > public static coalesce<T>(iterable: AsyncIterable<T | undefined | null>): AsyncIterableProducer<T> {
2573 return <AsyncIterableProducer<T>>AsyncIterableProducer.filter(iterable, item => !!item);
2574 }
2575 > async.ts
2576 > public coalesce(): AsyncIterableProducer<NonNullable<T>> {
2577 return AsyncIterableProducer.coalesce(this) as AsyncIterableProducer<NonNullable<T>>;
2578 }
2579 > async.ts
2580 > public static filter<T>(iterable: AsyncIterable<T>, filterFn: (item: T) => boolean): AsyncIterableProducer<T> {
2581 return new AsyncIterableProducer<T>(async (emitter) => {
2582 for await (const item of iterable) {
2587 });
2588 }
2589 > async.ts
2590 > public filter<T2 extends T>(filterFn: (item: T) => item is T2): AsyncIterableProducer<T2>;
2591 > public filter(filterFn: (item: T) => boolean): AsyncIterableProducer<T>;
2592 > public filter(filterFn: (item: T) => boolean): AsyncIterableProducer<T> {
2593 return AsyncIterableProducer.filter(this, filterFn);
2594 }
2595 > async.ts
2596 > private _finishOk(): void {
2597 > if (!this._producerConsumer.hasFinalValue) {
2598 > this._producerConsumer.produceFinal({ ok: true, value: { done: true, value: undefined } });
2599 > }
2600 > }
2601 >
2602 > private _finishError(error: Error): void {
2603 if (!this._producerConsumer.hasFinalValue) {
2604 this._producerConsumer.produceFinal({ ok: false, error: error });
2606 // Warning: this can cause to dropped errors.
2607 }
2608 > async.ts
2609 > private readonly _iterator: AsyncIterator<T, void, void> = {
2610 > next: () => this._producerConsumer.consume(),
2611 > return: () => {
2612 this._onReturn?.();
2613 return Promise.resolve({ done: true, value: undefined });
2614 },
2615 > throw: async (e) => { async.ts
2616 this._finishError(e);
2617 return { done: true, value: undefined };
2618 },
2619 > }; async.ts
2620 >
2621 > [Symbol.asyncIterator](): AsyncIterator<T, void, void> {
2622 return this._iterator;
2623 }
2624 > } async.ts
2625 >
2626 > export class CancelableAsyncIterableProducer<T> extends AsyncIterableProducer<T> {
2627 > constructor(
2628 private readonly _source: CancellationTokenSource,
2629 executor: AsyncIterableExecutor<T>
2631 super(executor);
2632 }
2633 > async.ts
2634 > cancel(): void {
2635 this._source.cancel();
2636 }
2637 > } async.ts
2638 >
2639 > //#endregion
2640 >
2641 > export const AsyncReaderEndOfStream = Symbol('AsyncReaderEndOfStream');
2642 >
2643 > export class AsyncReader<T> {
2644 > private _buffer: T[] = [];
2645 > private _atEnd = false;
2646 >
2647 > public get endOfStream(): boolean { return this._buffer.length === 0 && this._atEnd; }
2648 > private _extendBufferPromise: Promise<void> | undefined;
2649 >
2650 > constructor(
2651 private readonly _source: AsyncIterator<T>
2652 ) {
2653 }
2654 > async.ts
2655 > public async read(): Promise<T | typeof AsyncReaderEndOfStream> {
2656 if (this._buffer.length === 0 && !this._atEnd) {
2657 await this._extendBuffer();
2662 return this._buffer.shift()!;
2663 }
2664 > async.ts
2665 > public async readWhile(predicate: (value: T) => boolean, callback: (element: T) => unknown): Promise<void> {
2666 do {
2667 const piece = await this.peek();
2676 } while (true);
2677 }
2678 > async.ts
2679 > public readBufferedOrThrow(): T | typeof AsyncReaderEndOfStream {
2680 const value = this.peekBufferedOrThrow();
2681 this._buffer.shift();
2682 return value;
2683 }
2684 > async.ts
2685 > public async consumeToEnd(): Promise<void> {
2686 while (!this.endOfStream) {
2687 await this.read();
2688 }
2689 }
2690 > async.ts
2691 > public async peek(): Promise<T | typeof AsyncReaderEndOfStream> {
2692 if (this._buffer.length === 0 && !this._atEnd) {
2693 await this._extendBuffer();
2698 return this._buffer[0];
2699 }
2700 > async.ts
2701 > public peekBufferedOrThrow(): T | typeof AsyncReaderEndOfStream {
2702 if (this._buffer.length === 0) {
2703 if (this._atEnd) {
2709 return this._buffer[0];
2710 }
2711 > async.ts
2712 > public async peekTimeout(timeoutMs: number): Promise<T | typeof AsyncReaderEndOfStream | undefined> {
2713 if (this._buffer.length === 0 && !this._atEnd) {
2714 await raceTimeout(this._extendBuffer(), timeoutMs);
2722 return this._buffer[0];
2723 }
2724 > async.ts
2725 > private _extendBuffer(): Promise<void> {
2726 if (this._atEnd) {
2727 return Promise.resolve();
2742 return this._extendBufferPromise;
2743 }
2744 > } async.ts
2745 >
2746 > export function createTimeout(ms: number, cb: () => void): IDisposable {
2747 const t = setTimeout(cb, ms);
2748 return toDisposable(() => clearTimeout(t));
src/vs/workbench/services/editor/common/editorGroupsService.ts 1075 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editorGroupsService.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 { IInstantiationService, createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
8 > import { IEditorPane, GroupIdentifier, EditorInputWithOptions, CloseDirection, IEditorPartOptions, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, IEditorCloseEvent, IUntypedEditorInput, isEditorInput, IEditorWillMoveEvent, IMatchEditorOptions, IActiveEditorChangeEvent, IFindEditorOptions, IToolbarActions } from '../../../common/editor.js';
9 > import { EditorInput } from '../../../common/editor/editorInput.js';
10 > import { IEditorOptions, IModalEditorNavigation, IModalEditorPartOptions } from '../../../../platform/editor/common/editor.js';
11 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
12 > import { IDimension } from '../../../../editor/common/core/2d/dimension.js';
13 > import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';
14 > import { ContextKeyValue, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
15 > import { URI } from '../../../../base/common/uri.js';
16 > import { IGroupModelChangeEvent } from '../../../common/editor/editorGroupModel.js';
17 > import { IRectangle } from '../../../../platform/window/common/window.js';
18 > import { IMenuChangeEvent, MenuId } from '../../../../platform/actions/common/actions.js';
19 > import { DeepPartial } from '../../../../base/common/types.js';
20 >
21 > export const IEditorGroupsService = createDecorator<IEditorGroupsService>('editorGroupsService');
22 >
23 > export const enum GroupActivationReason {
24 >
25 > /**
26 > * Group was activated explicitly by user or programmatic action.
27 > */
28 > DEFAULT = 0,
29 >
30 > /**
31 > * Group was activated because a modal or auxiliary editor part was closing.
32 > */
33 > PART_CLOSE = 1
34 > }
35 >
36 > export interface IEditorGroupActivationEvent {
37 > readonly group: IEditorGroup;
38 > readonly reason: GroupActivationReason;
39 > }
40 >
41 > export const enum GroupDirection {
42 > UP,
43 > DOWN,
44 > LEFT,
45 > RIGHT
46 > }
47 >
48 > export const enum GroupOrientation {
49 > HORIZONTAL,
50 > VERTICAL
51 > }
52 >
53 > export const enum GroupLocation {
54 > FIRST,
55 > LAST,
56 > NEXT,
57 > PREVIOUS
58 > }
59 >
60 > export interface IFindGroupScope {
61 > readonly direction?: GroupDirection;
62 > readonly location?: GroupLocation;
63 > }
64 >
65 > export const enum GroupsArrangement {
66 > /**
67 > * Make the current active group consume the entire
68 > * editor area.
69 > */
70 > MAXIMIZE,
71 >
72 > /**
73 > * Make the current active group consume the maximum
74 > * amount of space possible.
75 > */
76 > EXPAND,
77 >
78 > /**
79 > * Size all groups evenly.
80 > */
81 > EVEN
82 > }
83 >
84 > export interface GroupLayoutArgument {
85 >
86 > /**
87 > * Only applies when there are multiple groups
88 > * arranged next to each other in a row or column.
89 > * If provided, their sum must be 1 to be applied
90 > * per row or column.
91 > */
92 > readonly size?: number;
93 >
94 > /**
95 > * Editor groups will be laid out orthogonal to the
96 > * parent orientation.
97 > */
98 > readonly groups?: GroupLayoutArgument[];
99 > }
100 >
101 > export interface EditorGroupLayout {
102 >
103 > /**
104 > * The initial orientation of the editor groups at the root.
105 > */
106 > readonly orientation: GroupOrientation;
107 >
108 > /**
109 > * The editor groups at the root of the layout.
110 > */
111 > readonly groups: GroupLayoutArgument[];
112 > }
113 >
114 > export const enum MergeGroupMode {
115 > COPY_EDITORS,
116 > MOVE_EDITORS
117 > }
118 >
119 > export interface IMergeGroupOptions {
120 > mode?: MergeGroupMode;
121 > readonly index?: number;
122 >
123 > /**
124 > * Set this to prevent editors already present in the
125 > * target group from moving to a different index as
126 > * they are in the source group.
127 > */
128 > readonly preserveExistingIndex?: boolean;
129 > }
130 >
131 > export interface ICloseEditorOptions {
132 > readonly preserveFocus?: boolean;
133 > }
134 >
135 > export type ICloseEditorsFilter = {
136 > readonly except?: EditorInput;
137 > readonly direction?: CloseDirection;
138 > readonly savedOnly?: boolean;
139 > readonly excludeSticky?: boolean;
140 > };
141 >
142 > export interface ICloseAllEditorsOptions {
143 > readonly excludeSticky?: boolean;
144 > readonly excludeConfirming?: boolean;
145 > }
146 >
147 > export interface IEditorReplacement {
148 > readonly editor: EditorInput;
149 > readonly replacement: EditorInput;
150 > readonly options?: IEditorOptions;
151 >
152 > /**
153 > * Skips asking the user for confirmation and doesn't
154 > * save the document. Only use this if you really need to!
155 > */
156 > readonly forceReplaceDirty?: boolean;
157 > }
158 >
159 > export function isEditorReplacement(replacement: unknown): replacement is IEditorReplacement {
160 const candidate = replacement as IEditorReplacement | undefined;
161
162 return isEditorInput(candidate?.editor) && isEditorInput(candidate?.replacement);
163 }
165 > export const enum GroupsOrder {
166 >
167 > /**
168 > * Groups sorted by creation order (oldest one first)
169 > */
170 > CREATION_TIME,
171 >
172 > /**
173 > * Groups sorted by most recent activity (most recent active first)
174 > */
175 > MOST_RECENTLY_ACTIVE,
176 >
177 > /**
178 > * Groups sorted by grid widget order
179 > */
180 > GRID_APPEARANCE
181 > }
182 >
183 > export interface IEditorSideGroup {
184 >
185 > /**
186 > * Open an editor in this group.
187 > *
188 > * @returns a promise that resolves around an IEditor instance unless
189 > * the call failed, or the editor was not opened as active editor.
190 > */
191 > openEditor(editor: EditorInput, options?: IEditorOptions): Promise<IEditorPane | undefined>;
192 > }
193 >
194 > export interface IEditorDropTargetDelegate {
195 >
196 > /**
197 > * A helper to figure out if the drop target contains the provided group.
198 > */
199 > containsGroup?(groupView: IEditorGroup): boolean;
200 > }
201 >
202 > /**
203 > * The basic primitive to work with editor groups. This interface is both implemented
204 > * by editor part component as well as the editor groups service that operates across
205 > * all opened editor parts.
206 > */
207 > export interface IEditorGroupsContainer {
208 >
209 > /**
210 > * An event for when the active editor group changes. The active editor
211 > * group is the default location for new editors to open.
212 > */
213 > readonly onDidChangeActiveGroup: Event<IEditorGroup>;
214 >
215 > /**
216 > * An event for when a new group was added.
217 > */
218 > readonly onDidAddGroup: Event<IEditorGroup>;
219 >
220 > /**
221 > * An event for when a group was removed.
222 > */
223 > readonly onDidRemoveGroup: Event<IEditorGroup>;
224 >
225 > /**
226 > * An event for when a group was moved.
227 > */
228 > readonly onDidMoveGroup: Event<IEditorGroup>;
229 >
230 > /**
231 > * An event for when a group gets activated.
232 > */
233 > readonly onDidActivateGroup: Event<IEditorGroupActivationEvent>;
234 >
235 > /**
236 > * An event for when the index of a group changes.
237 > */
238 > readonly onDidChangeGroupIndex: Event<IEditorGroup>;
239 >
240 > /**
241 > * An event for when the locked state of a group changes.
242 > */
243 > readonly onDidChangeGroupLocked: Event<IEditorGroup>;
244 >
245 > /**
246 > * An event for when the maximized state of a group changes.
247 > */
248 > readonly onDidChangeGroupMaximized: Event<boolean>;
249 >
250 > /**
251 > * An event that notifies when container options change.
252 > */
253 > readonly onDidChangeEditorPartOptions: Event<IEditorPartOptionsChangeEvent>;
254 >
255 > /**
256 > * A property that indicates when groups have been created
257 > * and are ready to be used in the container.
258 > */
259 > readonly isReady: boolean;
260 >
261 > /**
262 > * A promise that resolves when groups have been created
263 > * and are ready to be used in the container.
264 > *
265 > * Await this promise to safely work on the editor groups model
266 > * (for example, install editor group listeners).
267 > *
268 > * Use the `whenRestored` property to await visible editors
269 > * having fully resolved.
270 > */
271 > readonly whenReady: Promise<void>;
272 >
273 > /**
274 > * A promise that resolves when groups have been restored in
275 > * the container.
276 > *
277 > * For groups with active editor, the promise will resolve
278 > * when the visible editor has finished to resolve.
279 > *
280 > * Use the `whenReady` property to not await editors to
281 > * resolve.
282 > */
283 > readonly whenRestored: Promise<void>;
284 >
285 > /**
286 > * Find out if the container has UI state to restore
287 > * from a previous session.
288 > */
289 > readonly hasRestorableState: boolean;
290 >
291 > /**
292 > * An active group is the default location for new editors to open.
293 > */
294 > readonly activeGroup: IEditorGroup;
295 >
296 > /**
297 > * A side group allows a subset of methods on a group that is either
298 > * created to the side or picked if already there.
299 > */
300 > readonly sideGroup: IEditorSideGroup;
301 >
302 > /**
303 > * All groups that are currently visible in the container in the order
304 > * of their creation (oldest first).
305 > */
306 > readonly groups: readonly IEditorGroup[];
307 >
308 > /**
309 > * The number of editor groups that are currently opened in the
310 > * container.
311 > */
312 > readonly count: number;
313 >
314 > /**
315 > * The current layout orientation of the root group.
316 > */
317 > readonly orientation: GroupOrientation;
318 >
319 > /**
320 > * Access the options of the container.
321 > */
322 > readonly partOptions: IEditorPartOptions;
323 >
324 > /**
325 > * Enforce container options temporarily.
326 > */
327 > enforcePartOptions(options: DeepPartial<IEditorPartOptions>): IDisposable;
328 >
329 > /**
330 > * Get all groups that are currently visible in the container.
331 > *
332 > * @param order the order of the editors to use
333 > */
334 > getGroups(order: GroupsOrder): readonly IEditorGroup[];
335 >
336 > /**
337 > * Allows to convert a group identifier to a group.
338 > */
339 > getGroup(identifier: GroupIdentifier): IEditorGroup | undefined;
340 >
341 > /**
342 > * Set a group as active. An active group is the default location for new editors to open.
343 > */
344 > activateGroup(group: IEditorGroup | GroupIdentifier): IEditorGroup;
345 >
346 > /**
347 > * Returns the size of a group.
348 > */
349 > getSize(group: IEditorGroup | GroupIdentifier): { width: number; height: number };
350 >
351 > /**
352 > * Sets the size of a group.
353 > */
354 > setSize(group: IEditorGroup | GroupIdentifier, size: { width: number; height: number }): void;
355 >
356 > /**
357 > * Arrange all groups in the container according to the provided arrangement.
358 > */
359 > arrangeGroups(arrangement: GroupsArrangement, target?: IEditorGroup | GroupIdentifier): void;
360 >
361 > /**
362 > * Toggles the target goup size to maximize/unmaximize.
363 > */
364 > toggleMaximizeGroup(group?: IEditorGroup | GroupIdentifier): void;
365 >
366 > /**
367 > * Toggles the target goup size to expand/distribute even.
368 > */
369 > toggleExpandGroup(group?: IEditorGroup | GroupIdentifier): void;
370 >
371 > /**
372 > * Applies the provided layout by either moving existing groups or creating new groups.
373 > */
374 > applyLayout(layout: EditorGroupLayout): void;
375 >
376 > /**
377 > * Returns an editor layout of the container.
378 > */
379 > getLayout(): EditorGroupLayout;
380 >
381 > /**
382 > * Sets the orientation of the root group to be either vertical or horizontal.
383 > */
384 > setGroupOrientation(orientation: GroupOrientation): void;
385 >
386 > /**
387 > * Find a group in a specific scope:
388 > * * `GroupLocation.FIRST`: the first group
389 > * * `GroupLocation.LAST`: the last group
390 > * * `GroupLocation.NEXT`: the next group from either the active one or `source`
391 > * * `GroupLocation.PREVIOUS`: the previous group from either the active one or `source`
392 > * * `GroupDirection.UP`: the next group above the active one or `source`
393 > * * `GroupDirection.DOWN`: the next group below the active one or `source`
394 > * * `GroupDirection.LEFT`: the next group to the left of the active one or `source`
395 > * * `GroupDirection.RIGHT`: the next group to the right of the active one or `source`
396 > *
397 > * @param scope the scope of the group to search in
398 > * @param source optional source to search from
399 > * @param wrap optionally wrap around if reaching the edge of groups
400 > */
401 > findGroup(scope: IFindGroupScope, source?: IEditorGroup | GroupIdentifier, wrap?: boolean): IEditorGroup | undefined;
402 >
403 > /**
404 > * Add a new group to the container. A new group is added by splitting a provided one in
405 > * one of the four directions.
406 > *
407 > * @param location the group from which to split to add a new group
408 > * @param direction the direction of where to split to
409 > */
410 > addGroup(location: IEditorGroup | GroupIdentifier, direction: GroupDirection): IEditorGroup;
411 >
412 > /**
413 > * Remove a group from the container.
414 > */
415 > removeGroup(group: IEditorGroup | GroupIdentifier): void;
416 >
417 > /**
418 > * Move a group to a new group in the container.
419 > *
420 > * @param group the group to move
421 > * @param location the group from which to split to add the moved group
422 > * @param direction the direction of where to split to
423 > */
424 > moveGroup(group: IEditorGroup | GroupIdentifier, location: IEditorGroup | GroupIdentifier, direction: GroupDirection): IEditorGroup;
425 >
426 > /**
427 > * Merge the editors of a group into a target group. By default, all editors will
428 > * move and the source group will close. This behaviour can be configured via the
429 > * `IMergeGroupOptions` options.
430 > *
431 > * @param group the group to merge
432 > * @param target the target group to merge into
433 > * @param options controls how the merge should be performed. by default all editors
434 > * will be moved over to the target and the source group will close. Configure to
435 > * `MOVE_EDITORS_KEEP_GROUP` to prevent the source group from closing. Set to
436 > * `COPY_EDITORS` to copy the editors into the target instead of moding them.
437 > *
438 > * @returns if merging was successful
439 > */
440 > mergeGroup(group: IEditorGroup | GroupIdentifier, target: IEditorGroup | GroupIdentifier, options?: IMergeGroupOptions): boolean;
441 >
442 > /**
443 > * Merge all editor groups into the target one.
444 > *
445 > * @returns if merging was successful
446 > */
447 > mergeAllGroups(target: IEditorGroup | GroupIdentifier): boolean;
448 >
449 > /**
450 > * Copy a group to a new group in the container.
451 > *
452 > * @param group the group to copy
453 > * @param location the group from which to split to add the copied group
454 > * @param direction the direction of where to split to
455 > */
456 > copyGroup(group: IEditorGroup | GroupIdentifier, location: IEditorGroup | GroupIdentifier, direction: GroupDirection): IEditorGroup;
457 >
458 > /**
459 > * Allows to register a drag and drop target for editors
460 > * on the provided `container`.
461 > */
462 > createEditorDropTarget(container: unknown /* HTMLElement */, delegate: IEditorDropTargetDelegate): IDisposable;
463 > }
464 >
465 > /**
466 > * An editor part is a viewer of editor groups. There can be multiple editor
467 > * parts opened in multiple windows.
468 > */
469 > export interface IEditorPart extends IEditorGroupsContainer {
470 >
471 > /**
472 > * An event for when the editor part is layed out.
473 > */
474 > readonly onDidLayout: Event<IDimension>;
475 >
476 > /**
477 > * An event for when the editor part is scrolled.
478 > */
479 > readonly onDidScroll: Event<void>;
480 >
481 > /**
482 > * An event for when the editor part is disposed.
483 > */
484 > readonly onWillDispose: Event<void>;
485 >
486 > /**
487 > * The identifier of the window the editor part is contained in.
488 > */
489 > readonly windowId: number;
490 >
491 > /**
492 > * The size of the editor part.
493 > */
494 > readonly contentDimension: IDimension;
495 >
496 > /**
497 > * Find out if an editor group is currently maximized.
498 > */
499 > hasMaximizedGroup(): boolean;
500 >
501 > /**
502 > * Enable or disable centered editor layout.
503 > */
504 > centerLayout(active: boolean): void;
505 >
506 > /**
507 > * Find out if the editor layout is currently centered.
508 > */
509 > isLayoutCentered(): boolean;
510 > }
511 >
512 > export interface IAuxiliaryEditorPart extends IEditorPart {
513 >
514 > /**
515 > * Close this auxiliary editor part after moving all
516 > * dirty editors of all groups back to the main editor
517 > * part.
518 > *
519 > * @returns `false` if an editor could not be moved back.
520 > */
521 > close(): boolean;
522 > }
523 >
524 > export interface IModalEditorPart extends IEditorPart {
525 >
526 > /**
527 > * Modal container of the editor part.
528 > */
529 > readonly modalElement: unknown /* HTMLElement */;
530 >
531 > /**
532 > * Whether the modal editor part is currently maximized.
533 > */
534 > readonly maximized: boolean;
535 >
536 > /**
537 > * Fired when the maximized state changes.
538 > */
539 > readonly onDidChangeMaximized: Event<boolean>;
540 >
541 > /**
542 > * Toggle between default and maximized size.
543 > */
544 > toggleMaximized(): void;
545 >
546 > /**
547 > * Size set by the user via resizing, if any.
548 > */
549 > readonly size: IDimension | undefined;
550 >
551 > /**
552 > * Position set by the user via dragging, if any.
553 > */
554 > readonly position: { left: number; top: number } | undefined;
555 >
556 > /**
557 > * Whether the modal editor part has a sidebar.
558 > */
559 > readonly hasSidebar: boolean;
560 >
561 > /**
562 > * Sidebar width set by the user via resizing, if any.
563 > */
564 > readonly sidebarWidth: number | undefined;
565 >
566 > /**
567 > * Whether the sidebar is hidden.
568 > */
569 > readonly sidebarHidden: boolean;
570 >
571 > /**
572 > * Toggle sidebar visibility.
573 > */
574 > toggleSidebar(): void;
575 >
576 > /**
577 > * The current navigation context, if any.
578 > */
579 > readonly navigation: IModalEditorNavigation | undefined;
580 >
581 > /**
582 > * Update options for the modal editor part.
583 > */
584 > updateOptions(options?: IModalEditorPartOptions): void;
585 >
586 > /**
587 > * Fired when this modal editor part is about to close.
588 > */
589 > readonly onWillClose: Event<void>;
590 >
591 > /**
592 > * Close this modal editor part after closing all
593 > * editors of all groups. Dirty editors will trigger
594 > * a confirmation dialog asking the user to save.
595 > *
596 > * The option `mergeAllEditorsToMainPart` can be used
597 > * to first move all editors from this modal editor part
598 > * back to the main editor part, where they remain open.
599 > * This avoids the confirmation dialog because the editors
600 > * are not closed as part of this operation.
601 > *
602 > * @returns `false` if the close was cancelled.
603 > */
604 > close(options?: { mergeAllEditorsToMainPart?: boolean }): Promise<boolean>;
605 > }
606 >
607 > export interface IEditorWorkingSet {
608 > readonly id: string;
609 > readonly name: string;
610 > }
611 >
612 > export interface IEditorWorkingSetOptions {
613 > readonly preserveFocus?: boolean;
614 > }
615 >
616 > export interface IEditorGroupContextKeyProvider<T extends ContextKeyValue> {
617 >
618 > /**
619 > * The context key that needs to be set for each editor group context and the global context.
620 > */
621 > readonly contextKey: RawContextKey<T>;
622 >
623 > /**
624 > * Retrieves the context key value for the given editor group.
625 > */
626 > readonly getGroupContextKeyValue: (group: IEditorGroup) => T;
627 >
628 > /**
629 > * An event that is fired when there was a change leading to the context key value to be re-evaluated.
630 > */
631 > readonly onDidChange?: Event<void>;
632 > }
633 >
634 > /**
635 > * The main service to interact with editor groups across all opened editor parts.
636 > */
637 > export interface IEditorGroupsService extends IEditorGroupsContainer {
638 >
639 > readonly _serviceBrand: undefined;
640 >
641 > /**
642 > * An event for when a new auxiliary editor part is created.
643 > */
644 > readonly onDidCreateAuxiliaryEditorPart: Event<IAuxiliaryEditorPart>;
645 >
646 > /**
647 > * Provides access to the main window editor part.
648 > */
649 > readonly mainPart: IEditorPart;
650 >
651 > /**
652 > * Provides access to all editor parts.
653 > */
654 > readonly parts: ReadonlyArray<IEditorPart>;
655 >
656 > /**
657 > * Get the editor part that contains the group with the provided identifier.
658 > */
659 > getPart(group: IEditorGroup | GroupIdentifier): IEditorPart;
660 >
661 > /**
662 > * Get the editor part that is rooted in the provided container.
663 > */
664 > getPart(container: unknown /* HTMLElement */): IEditorPart;
665 >
666 > /**
667 > * Opens a new window with a full editor part instantiated
668 > * in there at the optional position and size on screen.
669 > */
670 > createAuxiliaryEditorPart(options?: { bounds?: Partial<IRectangle>; compact?: boolean; alwaysOnTop?: boolean }): Promise<IAuxiliaryEditorPart>;
671 >
672 > /**
673 > * Creates a modal editor part that shows in a modal overlay
674 > * on top of the main workbench window.
675 > *
676 > * If a modal part already exists, it will be returned
677 > * instead of creating a new one.
678 > */
679 > createModalEditorPart(options?: IModalEditorPartOptions): Promise<IModalEditorPart>;
680 >
681 > /**
682 > * The currently active modal editor part, if any.
683 > */
684 > readonly activeModalEditorPart: IModalEditorPart | undefined;
685 >
686 > /**
687 > * Returns the instantiation service that is scoped to the
688 > * provided editor part. Use this method when building UI
689 > * that contributes to auxiliary editor parts to ensure the
690 > * UI is scoped to that part.
691 > */
692 > getScopedInstantiationService(part: IEditorPart): IInstantiationService;
693 >
694 > /**
695 > * Save a new editor working set from the currently opened
696 > * editors and group layout.
697 > */
698 > saveWorkingSet(name: string): IEditorWorkingSet;
699 >
700 > /**
701 > * Returns all known editor working sets.
702 > */
703 > getWorkingSets(): IEditorWorkingSet[];
704 >
705 > /**
706 > * Applies the working set. Use `empty` to apply an empty working set.
707 > *
708 > * @returns `true` when the working set as applied.
709 > */
710 > applyWorkingSet(workingSet: IEditorWorkingSet | 'empty', options?: IEditorWorkingSetOptions): Promise<boolean>;
711 >
712 > /**
713 > * Deletes a working set.
714 > */
715 > deleteWorkingSet(workingSet: IEditorWorkingSet): void;
716 >
717 > /**
718 > * Registers a context key provider. This provider sets a context key for each scoped editor group context and the global context.
719 > *
720 > * @param provider - The context key provider to be registered.
721 > * @returns - A disposable object to unregister the provider.
722 > */
723 > registerContextKeyProvider<T extends ContextKeyValue>(provider: IEditorGroupContextKeyProvider<T>): IDisposable;
724 > }
725 >
726 > export const enum OpenEditorContext {
727 > NEW_EDITOR = 1,
728 > MOVE_EDITOR = 2,
729 > COPY_EDITOR = 3
730 > }
731 >
732 > export interface IActiveEditorActions {
733 > readonly actions: IToolbarActions;
734 > readonly onDidChange: Event<IMenuChangeEvent | void>;
735 > }
736 >
737 > export interface IEditorGroup {
738 >
739 > /**
740 > * An event which fires whenever the underlying group model changes.
741 > */
742 > readonly onDidModelChange: Event<IGroupModelChangeEvent>;
743 >
744 > /**
745 > * An event that is fired when the group gets disposed.
746 > */
747 > readonly onWillDispose: Event<void>;
748 >
749 > /**
750 > * An event that is fired when the active editor in the group changed.
751 > */
752 > readonly onDidActiveEditorChange: Event<IActiveEditorChangeEvent>;
753 >
754 > /**
755 > * An event that is fired when an editor is about to close.
756 > */
757 > readonly onWillCloseEditor: Event<IEditorCloseEvent>;
758 >
759 > /**
760 > * An event that is fired when an editor is closed.
761 > */
762 > readonly onDidCloseEditor: Event<IEditorCloseEvent>;
763 >
764 > /**
765 > * An event that is fired when an editor is about to move to
766 > * a different group.
767 > */
768 > readonly onWillMoveEditor: Event<IEditorWillMoveEvent>;
769 >
770 > /**
771 > * A unique identifier of this group that remains identical even if the
772 > * group is moved to different locations.
773 > */
774 > readonly id: GroupIdentifier;
775 >
776 > /**
777 > * The identifier of the window this editor group is part of.
778 > */
779 > readonly windowId: number;
780 >
781 > /**
782 > * A number that indicates the position of this group in the visual
783 > * order of groups from left to right and top to bottom. The lowest
784 > * index will likely be top-left while the largest index in most
785 > * cases should be bottom-right, but that depends on the grid.
786 > */
787 > readonly index: number;
788 >
789 > /**
790 > * A human readable label for the group. This label can change depending
791 > * on the layout of all editor groups. Clients should listen on the
792 > * `onDidGroupModelChange` event to react to that.
793 > */
794 > readonly label: string;
795 >
796 > /**
797 > * A human readable label for the group to be used by screen readers.
798 > */
799 > readonly ariaLabel: string;
800 >
801 > /**
802 > * The active editor pane is the currently visible editor pane of the group.
803 > */
804 > readonly activeEditorPane: IVisibleEditorPane | undefined;
805 >
806 > /**
807 > * The active editor is the currently visible editor of the group
808 > * within the current active editor pane.
809 > */
810 > readonly activeEditor: EditorInput | null;
811 >
812 > /**
813 > * All selected editor in this group in sequential order.
814 > * The active editor is always part of the selection.
815 > */
816 > readonly selectedEditors: EditorInput[];
817 >
818 > /**
819 > * The editor in the group that is in preview mode if any. There can
820 > * only ever be one editor in preview mode.
821 > */
822 > readonly previewEditor: EditorInput | null;
823 >
824 > /**
825 > * The number of opened editors in this group.
826 > */
827 > readonly count: number;
828 >
829 > /**
830 > * Whether the group has editors or not.
831 > */
832 > readonly isEmpty: boolean;
833 >
834 > /**
835 > * Whether this editor group is locked or not. Locked editor groups
836 > * will only be considered for editors to open in when the group is
837 > * explicitly provided for the editor.
838 > *
839 > * Note: editor group locking only applies when more than one group
840 > * is opened.
841 > */
842 > readonly isLocked: boolean;
843 >
844 > /**
845 > * The number of sticky editors in this group.
846 > */
847 > readonly stickyCount: number;
848 >
849 > /**
850 > * All opened editors in the group in sequential order of their appearance.
851 > */
852 > readonly editors: readonly EditorInput[];
853 >
854 > /**
855 > * The scoped context key service for this group.
856 > */
857 > readonly scopedContextKeyService: IContextKeyService;
858 >
859 > /**
860 > * Get all editors that are currently opened in the group.
861 > *
862 > * @param order the order of the editors to use
863 > * @param options options to select only specific editors as instructed
864 > */
865 > getEditors(order: EditorsOrder, options?: { excludeSticky?: boolean }): readonly EditorInput[];
866 >
867 > /**
868 > * Finds all editors for the given resource that are currently
869 > * opened in the group. This method will return an entry for
870 > * each editor that reports a `resource` that matches the
871 > * provided one.
872 > *
873 > * @param resource the resource of the editor to find
874 > * @param options whether to support side by side editors or not
875 > */
876 > findEditors(resource: URI, options?: IFindEditorOptions): readonly EditorInput[];
877 >
878 > /**
879 > * Returns the editor at a specific index of the group.
880 > */
881 > getEditorByIndex(index: number): EditorInput | undefined;
882 >
883 > /**
884 > * Returns the index of the editor in the group or -1 if not opened.
885 > */
886 > getIndexOfEditor(editor: EditorInput): number;
887 >
888 > /**
889 > * Whether the editor is the first in the group.
890 > */
891 > isFirst(editor: EditorInput): boolean;
892 >
893 > /**
894 > * Whether the editor is the last in the group.
895 > */
896 > isLast(editor: EditorInput): boolean;
897 >
898 > /**
899 > * Open an editor in this group.
900 > *
901 > * @returns a promise that resolves around an IEditor instance unless
902 > * the call failed, or the editor was not opened as active editor.
903 > */
904 > openEditor(editor: EditorInput, options?: IEditorOptions): Promise<IEditorPane | undefined>;
905 >
906 > /**
907 > * Opens editors in this group.
908 > *
909 > * @returns a promise that resolves around an IEditor instance unless
910 > * the call failed, or the editor was not opened as active editor. Since
911 > * a group can only ever have one active editor, even if many editors are
912 > * opened, the result will only be one editor.
913 > */
914 > openEditors(editors: EditorInputWithOptions[]): Promise<IEditorPane | undefined>;
915 >
916 > /**
917 > * Find out if the provided editor is pinned in the group.
918 > */
919 > isPinned(editorOrIndex: EditorInput | number): boolean;
920 >
921 > /**
922 > * Find out if the provided editor or index of editor is sticky in the group.
923 > */
924 > isSticky(editorOrIndex: EditorInput | number): boolean;
925 >
926 > /**
927 > * Find out if the provided editor or index of editor is transient in the group.
928 > */
929 > isTransient(editorOrIndex: EditorInput | number): boolean;
930 >
931 > /**
932 > * Find out if the provided editor is active in the group.
933 > */
934 > isActive(editor: EditorInput | IUntypedEditorInput): boolean;
935 >
936 > /**
937 > * Whether the editor is selected in the group.
938 > */
939 > isSelected(editor: EditorInput): boolean;
940 >
941 > /**
942 > * Set a new selection for this group. This will replace the current
943 > * selection with the new selection.
944 > *
945 > * @param activeSelectedEditor the editor to set as active selected editor
946 > * @param inactiveSelectedEditors the inactive editors to set as selected
947 > */
948 > setSelection(activeSelectedEditor: EditorInput, inactiveSelectedEditors: EditorInput[]): Promise<void>;
949 >
950 > /**
951 > * Find out if a certain editor is included in the group.
952 > *
953 > * @param candidate the editor to find
954 > * @param options fine tune how to match editors
955 > */
956 > contains(candidate: EditorInput | IUntypedEditorInput, options?: IMatchEditorOptions): boolean;
957 >
958 > /**
959 > * Move an editor from this group either within this group or to another group.
960 > *
961 > * @returns whether the editor was moved or not.
962 > */
963 > moveEditor(editor: EditorInput, target: IEditorGroup, options?: IEditorOptions): boolean;
964 >
965 > /**
966 > * Move editors from this group either within this group or to another group.
967 > *
968 > * @returns whether all editors were moved or not.
969 > */
970 > moveEditors(editors: EditorInputWithOptions[], target: IEditorGroup): boolean;
971 >
972 > /**
973 > * Copy an editor from this group to another group.
974 > *
975 > * Note: It is currently not supported to show the same editor more than once in the same group.
976 > */
977 > copyEditor(editor: EditorInput, target: IEditorGroup, options?: IEditorOptions): void;
978 >
979 > /**
980 > * Copy editors from this group to another group.
981 > *
982 > * Note: It is currently not supported to show the same editor more than once in the same group.
983 > */
984 > copyEditors(editors: EditorInputWithOptions[], target: IEditorGroup): void;
985 >
986 > /**
987 > * Close an editor from the group. This may trigger a confirmation dialog if
988 > * the editor is dirty and thus returns a promise as value.
989 > *
990 > * @param editor the editor to close, or the currently active editor
991 > * if unspecified.
992 > *
993 > * @returns a promise when the editor is closed or not. If `true`, the editor
994 > * is closed and if `false` there was a veto closing the editor, e.g. when it
995 > * is dirty.
996 > */
997 > closeEditor(editor?: EditorInput, options?: ICloseEditorOptions): Promise<boolean>;
998 >
999 > /**
1000 > * Closes specific editors in this group. This may trigger a confirmation dialog if
1001 > * there are dirty editors and thus returns a promise as value.
1002 > *
1003 > * @returns a promise whether the editors were closed or not. If `true`, the editors
1004 > * were closed and if `false` there was a veto closing the editors, e.g. when one
1005 > * is dirty.
1006 > */
1007 > closeEditors(editors: EditorInput[] | ICloseEditorsFilter, options?: ICloseEditorOptions): Promise<boolean>;
1008 >
1009 > /**
1010 > * Closes all editors from the group. This may trigger a confirmation dialog if
1011 > * there are dirty editors and thus returns a promise as value.
1012 > *
1013 > * @returns a promise if confirmation is needed when all editors are closed.
1014 > */
1015 > closeAllEditors(options: { excludeConfirming: true }): boolean;
1016 > closeAllEditors(options?: ICloseAllEditorsOptions): Promise<boolean>;
1017 >
1018 > /**
1019 > * Replaces editors in this group with the provided replacement.
1020 > *
1021 > * @param editors the editors to replace
1022 > *
1023 > * @returns a promise that is resolved when the replaced active
1024 > * editor (if any) has finished loading.
1025 > */
1026 > replaceEditors(editors: IEditorReplacement[]): Promise<void>;
1027 >
1028 > /**
1029 > * Set an editor to be pinned. A pinned editor is not replaced
1030 > * when another editor opens at the same location.
1031 > *
1032 > * @param editor the editor to pin, or the currently active editor
1033 > * if unspecified.
1034 > */
1035 > pinEditor(editor?: EditorInput): void;
1036 >
1037 > /**
1038 > * Set an editor to be sticky. A sticky editor is showing in the beginning
1039 > * of the tab stripe and will not be impacted by close operations.
1040 > *
1041 > * @param editor the editor to make sticky, or the currently active editor
1042 > * if unspecified.
1043 > */
1044 > stickEditor(editor?: EditorInput): void;
1045 >
1046 > /**
1047 > * Set an editor to be non-sticky and thus moves back to a location after
1048 > * sticky editors and can be closed normally.
1049 > *
1050 > * @param editor the editor to make unsticky, or the currently active editor
1051 > * if unspecified.
1052 > */
1053 > unstickEditor(editor?: EditorInput): void;
1054 >
1055 > /**
1056 > * Whether this editor group should be locked or not.
1057 > *
1058 > * See {@linkcode IEditorGroup.isLocked `isLocked`}
1059 > */
1060 > lock(locked: boolean): void;
1061 >
1062 > /**
1063 > * Move keyboard focus into the group.
1064 > */
1065 > focus(): void;
1066 >
1067 > /**
1068 > * Create the editor actions for the current active editor.
1069 > */
1070 > createEditorActions(disposables: DisposableStore, menuId?: MenuId): IActiveEditorActions;
1071 > }
1072 >
1073 > export function isEditorGroup(obj: unknown): obj is IEditorGroup {
1074 const group = obj as IEditorGroup | undefined;
1075
1076 return !!group && typeof group.id === 'number' && Array.isArray(group.editors);
1077 }
1079 > //#region Editor Group Helpers
1080 >
1081 > export function preferredSideBySideGroupDirection(configurationService: IConfigurationService): GroupDirection.DOWN | GroupDirection.RIGHT {
1082 const openSideBySideDirection = configurationService.getValue('workbench.editor.openSideBySideDirection');
1083
src/vs/base/common/event.ts 970 covered LOC · 139 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); event.ts
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; event.ts
215 >
216 > const options: EmitterOptions | undefined = {
217 > onWillAddFirstListener() {
218 > listener = event(emitter.fire, emitter); event.ts
219 > },
220 > onDidRemoveLastListener() { event.ts
221 > listener?.dispose(); event.ts
222 > }
223 > }; event.ts
224 >
225 > if (!disposable) {
226 _addLeakageTraceLogic(options);
227 }
228 > event.ts
229 > const emitter = new Emitter<T>(options);
230 >
231 > disposable?.add(emitter);
232 >
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) { event.ts
1224 const listeners = this._listeners;
1225 queueMicrotask(() => {
1227 });
1228 }
1229 > event.ts
1230 > this._listeners = undefined;
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 event.ts
1312 > }
1313
1314 if (this._size === 1) {
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) { event.ts
1354 return;
1355 }
1356 > event.ts
1357 > const errorHandler = this._options?.onListenerError || onUnexpectedError;
1358 > if (!errorHandler) {
1359 listener.value(value);
1360 return;
1361 }
1362 > event.ts
1363 > try {
1364 > listener.value(value);
1365 > } catch (e) {
1366 errorHandler(e);
1367 }
1368 > } event.ts
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); event.ts
1396 > } else { event.ts
1397 const dq = this._deliveryQueue!;
1398 dq.enqueue(this, event, this._listeners.length);
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); event.ts
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>(); event.ts
1924 > let oldData = new Set(getData());
1925 > for (const d of oldData) {
1926 map.set(d, handleItem(d));
1927 }
1928 > event.ts
1929 > const store = new DisposableStore();
1930 > store.add(onDidChangeData(() => {
1931 const newData = getData();
1932 const diff = diffSets(oldData, newData);
1938 }
1939 oldData = new Set(newData);
1940 > })); event.ts
1941 > store.add(map);
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/contextkey/common/contextkey.ts 833 covered LOC · 219 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- contextkey.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 '../../../base/common/charCode.js';
7 > import { Event } from '../../../base/common/event.js';
8 > import { isChrome, isEdge, isFirefox, isLinux, isMacintosh, isSafari, isWeb, isWindows } from '../../../base/common/platform.js';
9 > import { isFalsyOrWhitespace } from '../../../base/common/strings.js';
10 > import { Scanner, LexingError, Token, TokenType } from './scanner.js';
11 > import { createDecorator } from '../../instantiation/common/instantiation.js';
12 > import { localize } from '../../../nls.js';
13 > import { IDisposable } from '../../../base/common/lifecycle.js';
14 > import { illegalArgument } from '../../../base/common/errors.js';
15 >
16 > const CONSTANT_VALUES = new Map<string, boolean>();
17 > CONSTANT_VALUES.set('false', false);
18 > CONSTANT_VALUES.set('true', true);
19 > CONSTANT_VALUES.set('isMac', isMacintosh);
20 > CONSTANT_VALUES.set('isLinux', isLinux);
21 > CONSTANT_VALUES.set('isWindows', isWindows);
22 > CONSTANT_VALUES.set('isWeb', isWeb);
23 > CONSTANT_VALUES.set('isMacNative', isMacintosh && !isWeb);
24 > CONSTANT_VALUES.set('isEdge', isEdge);
25 > CONSTANT_VALUES.set('isFirefox', isFirefox);
26 > CONSTANT_VALUES.set('isChrome', isChrome);
27 > CONSTANT_VALUES.set('isSafari', isSafari);
28 >
29 > /** allow register constant context keys that are known only after startup; requires running `substituteConstants` on the context key - https://github.com/microsoft/vscode/issues/174218#issuecomment-1437972127 */
30 > export function setConstant(key: string, value: boolean) {
31 if (CONSTANT_VALUES.get(key) !== undefined) { throw illegalArgument('contextkey.setConstant(k, v) invoked with already set constant `k`'); }
32
33 CONSTANT_VALUES.set(key, value);
34 }
36 > const hasOwnProperty = Object.prototype.hasOwnProperty;
37 >
38 > export const enum ContextKeyExprType {
39 > False = 0,
40 > True = 1,
41 > Defined = 2,
42 > Not = 3,
43 > Equals = 4,
44 > NotEquals = 5,
45 > And = 6,
46 > Regex = 7,
47 > NotRegex = 8,
48 > Or = 9,
49 > In = 10,
50 > NotIn = 11,
51 > Greater = 12,
52 > GreaterEquals = 13,
53 > Smaller = 14,
54 > SmallerEquals = 15,
55 > }
56 >
57 > export interface IContextKeyExprMapper {
58 > mapDefined(key: string): ContextKeyExpression;
59 > mapNot(key: string): ContextKeyExpression;
60 > mapEquals(key: string, value: any): ContextKeyExpression;
61 > mapNotEquals(key: string, value: any): ContextKeyExpression;
62 > mapGreater(key: string, value: any): ContextKeyExpression;
63 > mapGreaterEquals(key: string, value: any): ContextKeyExpression;
64 > mapSmaller(key: string, value: any): ContextKeyExpression;
65 > mapSmallerEquals(key: string, value: any): ContextKeyExpression;
66 > mapRegex(key: string, regexp: RegExp | null): ContextKeyRegexExpr;
67 > mapIn(key: string, valueKey: string): ContextKeyInExpr;
68 > mapNotIn(key: string, valueKey: string): ContextKeyNotInExpr;
69 > }
70 >
71 > export interface IContextKeyExpression {
72 > cmp(other: ContextKeyExpression): number;
73 > equals(other: ContextKeyExpression): boolean;
74 > substituteConstants(): ContextKeyExpression | undefined;
75 > evaluate(context: IContext): boolean;
76 > serialize(): string;
77 > keys(): string[];
78 > map(mapFnc: IContextKeyExprMapper): ContextKeyExpression;
79 > negate(): ContextKeyExpression;
80 >
81 > }
82 >
83 > export type ContextKeyExpression = (
84 > ContextKeyFalseExpr | ContextKeyTrueExpr | ContextKeyDefinedExpr | ContextKeyNotExpr
85 > | ContextKeyEqualsExpr | ContextKeyNotEqualsExpr | ContextKeyRegexExpr
86 > | ContextKeyNotRegexExpr | ContextKeyAndExpr | ContextKeyOrExpr | ContextKeyInExpr
87 > | ContextKeyNotInExpr | ContextKeyGreaterExpr | ContextKeyGreaterEqualsExpr
88 > | ContextKeySmallerExpr | ContextKeySmallerEqualsExpr
89 > );
90 >
91 >
92 > /*
93 >
94 > Syntax grammar:
95 >
96 > ```ebnf
97 >
98 > expression ::= or
99 >
100 > or ::= and { '||' and }*
101 >
102 > and ::= term { '&&' term }*
103 >
104 > term ::=
105 > | '!' (KEY | true | false | parenthesized)
106 > | primary
107 >
108 > primary ::=
109 > | 'true'
110 > | 'false'
111 > | parenthesized
112 > | KEY '=~' REGEX
113 > | KEY [ ('==' | '!=' | '<' | '<=' | '>' | '>=' | 'not' 'in' | 'in') value ]
114 >
115 > parenthesized ::=
116 > | '(' expression ')'
117 >
118 > value ::=
119 > | 'true'
120 > | 'false'
121 > | 'in' // we support `in` as a value because there's an extension that uses it, ie "when": "languageId == in"
122 > | VALUE // matched by the same regex as KEY; consider putting the value in single quotes if it's a string (e.g., with spaces)
123 > | SINGLE_QUOTED_STR
124 > | EMPTY_STR // this allows "when": "foo == " which's used by existing extensions
125 >
126 > ```
127 > */
128 >
129 > export type ParserConfig = {
130 > /**
131 > * with this option enabled, the parser can recover from regex parsing errors, e.g., unescaped slashes: `/src//` is accepted as `/src\//` would be
132 > */
133 > regexParsingWithErrorRecovery: boolean;
134 > };
135 >
136 > const defaultConfig: ParserConfig = {
137 > regexParsingWithErrorRecovery: true
138 > };
139 >
140 > export type ParsingError = {
141 > message: string;
142 > offset: number;
143 > lexeme: string;
144 > additionalInfo?: string;
145 > };
146 >
147 > const errorEmptyString = localize('contextkey.parser.error.emptyString', "Empty context key expression");
148 > const hintEmptyString = localize('contextkey.parser.error.emptyString.hint', "Did you forget to write an expression? You can also put 'false' or 'true' to always evaluate to false or true, respectively.");
149 > const errorNoInAfterNot = localize('contextkey.parser.error.noInAfterNot', "'in' after 'not'.");
150 > const errorClosingParenthesis = localize('contextkey.parser.error.closingParenthesis', "closing parenthesis ')'");
151 > const errorUnexpectedToken = localize('contextkey.parser.error.unexpectedToken', "Unexpected token");
152 > const hintUnexpectedToken = localize('contextkey.parser.error.unexpectedToken.hint', "Did you forget to put && or || before the token?");
153 > const errorUnexpectedEOF = localize('contextkey.parser.error.unexpectedEOF', "Unexpected end of expression");
154 > const hintUnexpectedEOF = localize('contextkey.parser.error.unexpectedEOF.hint', "Did you forget to put a context key?");
155 >
156 > /**
157 > * A parser for context key expressions.
158 > *
159 > * Example:
160 > * ```ts
161 > * const parser = new Parser();
162 > * const expr = parser.parse('foo == "bar" && baz == true');
163 > *
164 > * if (expr === undefined) {
165 > * // there were lexing or parsing errors
166 > * // process lexing errors with `parser.lexingErrors`
167 > * // process parsing errors with `parser.parsingErrors`
168 > * } else {
169 > * // expr is a valid expression
170 > * }
171 > * ```
172 > */
173 > export class Parser {
174 > // Note: this doesn't produce an exact syntax tree but a normalized one
175 > // ContextKeyExpression's that we use as AST nodes do not expose constructors that do not normalize
176 >
177 > private static _parseError = new Error();
178 >
179 > // lifetime note: `_scanner` lives as long as the parser does, i.e., is not reset between calls to `parse`
180 > private readonly _scanner = new Scanner();
181 >
182 > // lifetime note: `_tokens`, `_current`, and `_parsingErrors` must be reset between calls to `parse`
183 > private _tokens: Token[] = [];
184 > private _current = 0; // invariant: 0 <= this._current < this._tokens.length ; any incrementation of this value must first call `_isAtEnd`
185 > private _parsingErrors: ParsingError[] = [];
186 >
187 > get lexingErrors(): Readonly<LexingError[]> {
188 return this._scanner.errors;
189 }
191 > get parsingErrors(): Readonly<ParsingError[]> {
192 return this._parsingErrors;
193 }
195 > constructor(private readonly _config: ParserConfig = defaultConfig) {
196 > }
197 >
198 > /**
199 > * Parse a context key expression.
200 > *
201 > * @param input the expression to parse
202 > * @returns the parsed expression or `undefined` if there's an error - call `lexingErrors` and `parsingErrors` to see the errors
203 > */
204 > parse(input: string): ContextKeyExpression | undefined {
205
206 if (input === '') {
231 }
232 }
234 > private _expr(): ContextKeyExpression | undefined {
235 return this._or();
236 }
238 > private _or(): ContextKeyExpression | undefined {
239 const expr = [this._and()];
240
246 return expr.length === 1 ? expr[0] : ContextKeyExpr.or(...expr);
247 }
249 > private _and(): ContextKeyExpression | undefined {
250 const expr = [this._term()];
251
257 return expr.length === 1 ? expr[0] : ContextKeyExpr.and(...expr);
258 }
260 > private _term(): ContextKeyExpression | undefined {
261 if (this._matchOne(TokenType.Neg)) {
262 const peek = this._peek();
283 return this._primary();
284 }
286 > private _primary(): ContextKeyExpression | undefined {
287
288 const peek = this._peek();
498 }
499 }
501 > private _value(): string {
502 const token = this._peek();
503 switch (token.type) {
521 }
522 }
524 > private _flagsGYRe = /g|y/g;
525 > private _removeFlagsGY(flags: string): string {
526 return flags.replaceAll(this._flagsGYRe, '');
527 }
529 > // careful: this can throw if current token is the initial one (ie index = 0)
530 > private _previous() {
531 return this._tokens[this._current - 1];
532 }
534 > private _matchOne(token: TokenType) {
535 if (this._check(token)) {
536 this._advance();
540 return false;
541 }
543 > private _advance() {
544 if (!this._isAtEnd()) {
545 this._current++;
547 return this._previous();
548 }
550 > private _consume(type: TokenType, message: string) {
551 if (this._check(type)) {
552 return this._advance();
555 throw this._errExpectedButGot(message, this._peek());
556 }
558 > private _errExpectedButGot(expected: string, got: Token, additionalInfo?: string) {
559 const message = localize('contextkey.parser.error.expectedButGot', "Expected: {0}\nReceived: '{1}'.", expected, Scanner.getLexeme(got));
560 const offset = got.offset;
563 return Parser._parseError;
564 }
566 > private _check(type: TokenType) {
567 return this._peek().type === type;
568 }
570 > private _peek() {
571 return this._tokens[this._current];
572 }
574 > private _isAtEnd() {
575 return this._peek().type === TokenType.EOF;
576 }
577 > } contextkey.ts
578 >
579 > export abstract class ContextKeyExpr {
580 >
581 > public static false(): ContextKeyExpression {
582 return ContextKeyFalseExpr.INSTANCE;
583 }
584 > public static true(): ContextKeyExpression { contextkey.ts
585 return ContextKeyTrueExpr.INSTANCE;
586 }
587 > public static has(key: string): ContextKeyExpression { contextkey.ts
588 return ContextKeyDefinedExpr.create(key);
589 }
590 > public static equals(key: string, value: any): ContextKeyExpression { contextkey.ts
591 return ContextKeyEqualsExpr.create(key, value);
592 }
593 > public static notEquals(key: string, value: any): ContextKeyExpression { contextkey.ts
594 return ContextKeyNotEqualsExpr.create(key, value);
595 }
596 > public static regex(key: string, value: RegExp): ContextKeyExpression { contextkey.ts
597 return ContextKeyRegexExpr.create(key, value);
598 }
599 > public static in(key: string, value: string): ContextKeyExpression { contextkey.ts
600 return ContextKeyInExpr.create(key, value);
601 }
602 > public static notIn(key: string, value: string): ContextKeyExpression { contextkey.ts
603 return ContextKeyNotInExpr.create(key, value);
604 }
605 > public static not(key: string): ContextKeyExpression { contextkey.ts
606 return ContextKeyNotExpr.create(key);
607 }
608 > public static and(...expr: Array<ContextKeyExpression | undefined | null>): ContextKeyExpression | undefined { contextkey.ts
609 return ContextKeyAndExpr.create(expr, null, true);
610 }
611 > public static or(...expr: Array<ContextKeyExpression | undefined | null>): ContextKeyExpression | undefined { contextkey.ts
612 return ContextKeyOrExpr.create(expr, null, true);
613 }
614 > public static greater(key: string, value: number): ContextKeyExpression { contextkey.ts
615 return ContextKeyGreaterExpr.create(key, value);
616 }
617 > public static greaterEquals(key: string, value: number): ContextKeyExpression { contextkey.ts
618 return ContextKeyGreaterEqualsExpr.create(key, value);
619 }
620 > public static smaller(key: string, value: number): ContextKeyExpression { contextkey.ts
621 return ContextKeySmallerExpr.create(key, value);
622 }
623 > public static smallerEquals(key: string, value: number): ContextKeyExpression { contextkey.ts
624 return ContextKeySmallerEqualsExpr.create(key, value);
625 }
627 > private static _parser = new Parser({ regexParsingWithErrorRecovery: false });
628 > public static deserialize(serialized: string | null | undefined): ContextKeyExpression | undefined {
629 if (serialized === undefined || serialized === null) { // an empty string needs to be handled by the parser to get a corresponding parsing error reported
630 return undefined;
634 return expr;
635 }
637 > }
638 >
639 >
640 > export function validateWhenClauses(whenClauses: string[]): any {
641
642 const parser = new Parser({ regexParsingWithErrorRecovery: false }); // we run with no recovery to guide users to use correct regexes
664 });
665 }
667 > export function expressionsAreEqualWithConstantSubstitution(a: ContextKeyExpression | null | undefined, b: ContextKeyExpression | null | undefined): boolean {
668 const aExpr = a ? a.substituteConstants() : undefined;
669 const bExpr = b ? b.substituteConstants() : undefined;
676 return aExpr.equals(bExpr);
677 }
679 function cmp(a: ContextKeyExpression, b: ContextKeyExpression): number {
680 return a.cmp(b);
681 }
683 > export class ContextKeyFalseExpr implements IContextKeyExpression {
684 > public static INSTANCE = new ContextKeyFalseExpr();
685 >
686 > public readonly type = ContextKeyExprType.False;
687 >
688 > protected constructor() {
689 > }
690 >
691 > public cmp(other: ContextKeyExpression): number {
692 return this.type - other.type;
693 }
695 > public equals(other: ContextKeyExpression): boolean {
696 return (other.type === this.type);
697 }
699 > public substituteConstants(): ContextKeyExpression | undefined {
700 return this;
701 }
703 > public evaluate(context: IContext): boolean {
704 return false;
705 }
707 > public serialize(): string {
708 return 'false';
709 }
711 > public keys(): string[] {
712 return [];
713 }
715 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
716 return this;
717 }
719 > public negate(): ContextKeyExpression {
720 return ContextKeyTrueExpr.INSTANCE;
721 }
722 > } contextkey.ts
723 >
724 > export class ContextKeyTrueExpr implements IContextKeyExpression {
725 > public static INSTANCE = new ContextKeyTrueExpr();
726 >
727 > public readonly type = ContextKeyExprType.True;
728 >
729 > protected constructor() {
730 > }
731 >
732 > public cmp(other: ContextKeyExpression): number {
733 return this.type - other.type;
734 }
736 > public equals(other: ContextKeyExpression): boolean {
737 return (other.type === this.type);
738 }
740 > public substituteConstants(): ContextKeyExpression | undefined {
741 return this;
742 }
744 > public evaluate(context: IContext): boolean {
745 return true;
746 }
748 > public serialize(): string {
749 return 'true';
750 }
752 > public keys(): string[] {
753 return [];
754 }
756 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
757 return this;
758 }
760 > public negate(): ContextKeyExpression {
761 return ContextKeyFalseExpr.INSTANCE;
762 }
763 > } contextkey.ts
764 >
765 > export class ContextKeyDefinedExpr implements IContextKeyExpression {
766 > public static create(key: string, negated: ContextKeyExpression | null = null): ContextKeyExpression {
767 > const constantValue = CONSTANT_VALUES.get(key);
768 > if (typeof constantValue === 'boolean') {
769 > return constantValue ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE; contextkey.ts
770 > }
771 > return new ContextKeyDefinedExpr(key, negated); contextkey.ts
772 > }
773 >
774 > public readonly type = ContextKeyExprType.Defined;
775 >
776 > protected constructor(
777 > readonly key: string, contextkey.ts
778 > private negated: ContextKeyExpression | null
779 > ) {
780 > }
782 > public cmp(other: ContextKeyExpression): number {
783 if (other.type !== this.type) {
784 return this.type - other.type;
786 return cmp1(this.key, other.key);
787 }
789 > public equals(other: ContextKeyExpression): boolean {
790 if (other.type === this.type) {
791 return (this.key === other.key);
793 return false;
794 }
796 > public substituteConstants(): ContextKeyExpression | undefined {
797 const constantValue = CONSTANT_VALUES.get(this.key);
798 if (typeof constantValue === 'boolean') {
801 return this;
802 }
804 > public evaluate(context: IContext): boolean {
805 return (!!context.getValue(this.key));
806 }
808 > public serialize(): string {
809 return this.key;
810 }
812 > public keys(): string[] {
813 return [this.key];
814 }
816 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
817 return mapFnc.mapDefined(this.key);
818 }
820 > public negate(): ContextKeyExpression {
821 if (!this.negated) {
822 this.negated = ContextKeyNotExpr.create(this.key, this);
824 return this.negated;
825 }
826 > } contextkey.ts
827 >
828 > export class ContextKeyEqualsExpr implements IContextKeyExpression {
829 >
830 > public static create(key: string, value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
831 > if (typeof value === 'boolean') {
832 > return (value ? ContextKeyDefinedExpr.create(key, negated) : ContextKeyNotExpr.create(key, negated)); contextkey.ts
833 > }
834 > const constantValue = CONSTANT_VALUES.get(key); contextkey.ts
835 > if (typeof constantValue === 'boolean') {
836 > const trueValue = constantValue ? 'true' : 'false'; contextkey.ts
837 > return (value === trueValue ? ContextKeyTrueExpr.INSTANCE : ContextKeyFalseExpr.INSTANCE);
838 > }
839 > return new ContextKeyEqualsExpr(key, value, negated); contextkey.ts
840 > } contextkey.ts
841 >
842 > public readonly type = ContextKeyExprType.Equals;
843 >
844 > private constructor(
845 private readonly key: string,
846 private readonly value: any,
848 ) {
849 }
851 > public cmp(other: ContextKeyExpression): number {
852 if (other.type !== this.type) {
853 return this.type - other.type;
855 return cmp2(this.key, this.value, other.key, other.value);
856 }
858 > public equals(other: ContextKeyExpression): boolean {
859 if (other.type === this.type) {
860 return (this.key === other.key && this.value === other.value);
862 return false;
863 }
865 > public substituteConstants(): ContextKeyExpression | undefined {
866 const constantValue = CONSTANT_VALUES.get(this.key);
867 if (typeof constantValue === 'boolean') {
871 return this;
872 }
874 > public evaluate(context: IContext): boolean {
875 // Intentional ==
876 // eslint-disable-next-line eqeqeq
877 return (context.getValue(this.key) == this.value);
878 }
880 > public serialize(): string {
881 return `${this.key} == '${this.value}'`;
882 }
884 > public keys(): string[] {
885 return [this.key];
886 }
888 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
889 return mapFnc.mapEquals(this.key, this.value);
890 }
892 > public negate(): ContextKeyExpression {
893 if (!this.negated) {
894 this.negated = ContextKeyNotEqualsExpr.create(this.key, this.value, this);
896 return this.negated;
897 }
898 > } contextkey.ts
899 >
900 > export class ContextKeyInExpr implements IContextKeyExpression {
901 >
902 > public static create(key: string, valueKey: string): ContextKeyInExpr {
903 > return new ContextKeyInExpr(key, valueKey);
904 > }
905 >
906 > public readonly type = ContextKeyExprType.In;
907 > private negated: ContextKeyExpression | null = null;
908 >
909 > private constructor(
910 private readonly key: string,
911 private readonly valueKey: string,
912 ) {
913 }
915 > public cmp(other: ContextKeyExpression): number {
916 if (other.type !== this.type) {
917 return this.type - other.type;
919 return cmp2(this.key, this.valueKey, other.key, other.valueKey);
920 }
922 > public equals(other: ContextKeyExpression): boolean {
923 if (other.type === this.type) {
924 return (this.key === other.key && this.valueKey === other.valueKey);
926 return false;
927 }
929 > public substituteConstants(): ContextKeyExpression | undefined {
930 return this;
931 }
933 > public evaluate(context: IContext): boolean {
934 const source = context.getValue(this.valueKey);
935
964 return false;
965 }
967 > public serialize(): string {
968 return `${this.key} in '${this.valueKey}'`;
969 }
971 > public keys(): string[] {
972 return [this.key, this.valueKey];
973 }
975 > public map(mapFnc: IContextKeyExprMapper): ContextKeyInExpr {
976 return mapFnc.mapIn(this.key, this.valueKey);
977 }
979 > public negate(): ContextKeyExpression {
980 if (!this.negated) {
981 this.negated = ContextKeyNotInExpr.create(this.key, this.valueKey);
983 return this.negated;
984 }
985 > } contextkey.ts
986 >
987 > export class ContextKeyNotInExpr implements IContextKeyExpression {
988 >
989 > public static create(key: string, valueKey: string): ContextKeyNotInExpr {
990 > return new ContextKeyNotInExpr(key, valueKey);
991 > }
992 >
993 > public readonly type = ContextKeyExprType.NotIn;
994 >
995 > private readonly _negated: ContextKeyInExpr;
996 >
997 > private constructor(
998 private readonly key: string,
999 private readonly valueKey: string,
1001 this._negated = ContextKeyInExpr.create(key, valueKey);
1002 }
1003 > contextkey.ts
1004 > public cmp(other: ContextKeyExpression): number {
1005 if (other.type !== this.type) {
1006 return this.type - other.type;
1008 return this._negated.cmp(other._negated);
1009 }
1010 > contextkey.ts
1011 > public equals(other: ContextKeyExpression): boolean {
1012 if (other.type === this.type) {
1013 return this._negated.equals(other._negated);
1015 return false;
1016 }
1017 > contextkey.ts
1018 > public substituteConstants(): ContextKeyExpression | undefined {
1019 return this;
1020 }
1021 > contextkey.ts
1022 > public evaluate(context: IContext): boolean {
1023 return !this._negated.evaluate(context);
1024 }
1025 > contextkey.ts
1026 > public serialize(): string {
1027 return `${this.key} not in '${this.valueKey}'`;
1028 }
1029 > contextkey.ts
1030 > public keys(): string[] {
1031 return this._negated.keys();
1032 }
1033 > contextkey.ts
1034 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1035 return mapFnc.mapNotIn(this.key, this.valueKey);
1036 }
1037 > contextkey.ts
1038 > public negate(): ContextKeyExpression {
1039 return this._negated;
1040 }
1041 > } contextkey.ts
1042 >
1043 > export class ContextKeyNotEqualsExpr implements IContextKeyExpression {
1044 >
1045 > public static create(key: string, value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1046 > if (typeof value === 'boolean') {
1047 > if (value) { contextkey.ts
1048 > return ContextKeyNotExpr.create(key, negated); contextkey.ts
1049 > }
1050 > return ContextKeyDefinedExpr.create(key, negated); contextkey.ts
1051 > }
1052 > const constantValue = CONSTANT_VALUES.get(key); contextkey.ts
1053 > if (typeof constantValue === 'boolean') {
1054 > const falseValue = constantValue ? 'true' : 'false'; contextkey.ts
1055 > return (value === falseValue ? ContextKeyFalseExpr.INSTANCE : ContextKeyTrueExpr.INSTANCE);
1056 > }
1057 > return new ContextKeyNotEqualsExpr(key, value, negated); contextkey.ts
1058 > } contextkey.ts
1059 >
1060 > public readonly type = ContextKeyExprType.NotEquals;
1061 >
1062 > private constructor(
1063 private readonly key: string,
1064 private readonly value: any,
1066 ) {
1067 }
1068 > contextkey.ts
1069 > public cmp(other: ContextKeyExpression): number {
1070 if (other.type !== this.type) {
1071 return this.type - other.type;
1073 return cmp2(this.key, this.value, other.key, other.value);
1074 }
1075 > contextkey.ts
1076 > public equals(other: ContextKeyExpression): boolean {
1077 if (other.type === this.type) {
1078 return (this.key === other.key && this.value === other.value);
1080 return false;
1081 }
1082 > contextkey.ts
1083 > public substituteConstants(): ContextKeyExpression | undefined {
1084 const constantValue = CONSTANT_VALUES.get(this.key);
1085 if (typeof constantValue === 'boolean') {
1089 return this;
1090 }
1091 > contextkey.ts
1092 > public evaluate(context: IContext): boolean {
1093 // Intentional !=
1094 // eslint-disable-next-line eqeqeq
1095 return (context.getValue(this.key) != this.value);
1096 }
1097 > contextkey.ts
1098 > public serialize(): string {
1099 return `${this.key} != '${this.value}'`;
1100 }
1101 > contextkey.ts
1102 > public keys(): string[] {
1103 return [this.key];
1104 }
1105 > contextkey.ts
1106 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1107 return mapFnc.mapNotEquals(this.key, this.value);
1108 }
1109 > contextkey.ts
1110 > public negate(): ContextKeyExpression {
1111 if (!this.negated) {
1112 this.negated = ContextKeyEqualsExpr.create(this.key, this.value, this);
1114 return this.negated;
1115 }
1116 > } contextkey.ts
1117 >
1118 > export class ContextKeyNotExpr implements IContextKeyExpression {
1119 >
1120 > public static create(key: string, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1121 > const constantValue = CONSTANT_VALUES.get(key);
1122 > if (typeof constantValue === 'boolean') {
1123 > return (constantValue ? ContextKeyFalseExpr.INSTANCE : ContextKeyTrueExpr.INSTANCE); contextkey.ts
1124 > }
1125 > return new ContextKeyNotExpr(key, negated); contextkey.ts
1126 > }
1127 >
1128 > public readonly type = ContextKeyExprType.Not;
1129 >
1130 > private constructor(
1131 private readonly key: string,
1132 private negated: ContextKeyExpression | null
1133 ) {
1134 }
1135 > contextkey.ts
1136 > public cmp(other: ContextKeyExpression): number {
1137 if (other.type !== this.type) {
1138 return this.type - other.type;
1140 return cmp1(this.key, other.key);
1141 }
1142 > contextkey.ts
1143 > public equals(other: ContextKeyExpression): boolean {
1144 if (other.type === this.type) {
1145 return (this.key === other.key);
1147 return false;
1148 }
1149 > contextkey.ts
1150 > public substituteConstants(): ContextKeyExpression | undefined {
1151 const constantValue = CONSTANT_VALUES.get(this.key);
1152 if (typeof constantValue === 'boolean') {
1155 return this;
1156 }
1157 > contextkey.ts
1158 > public evaluate(context: IContext): boolean {
1159 return (!context.getValue(this.key));
1160 }
1161 > contextkey.ts
1162 > public serialize(): string {
1163 return `!${this.key}`;
1164 }
1165 > contextkey.ts
1166 > public keys(): string[] {
1167 return [this.key];
1168 }
1169 > contextkey.ts
1170 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1171 return mapFnc.mapNot(this.key);
1172 }
1173 > contextkey.ts
1174 > public negate(): ContextKeyExpression {
1175 if (!this.negated) {
1176 this.negated = ContextKeyDefinedExpr.create(this.key, this);
1178 return this.negated;
1179 }
1180 > } contextkey.ts
1181 >
1182 function withFloatOrStr<T extends ContextKeyExpression>(value: any, callback: (value: number | string) => T): T | ContextKeyFalseExpr {
1183 if (typeof value === 'string') {
1192 return ContextKeyFalseExpr.INSTANCE;
1193 }
1194 > contextkey.ts
1195 > export class ContextKeyGreaterExpr implements IContextKeyExpression {
1196 >
1197 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1198 > return withFloatOrStr(_value, (value) => new ContextKeyGreaterExpr(key, value, negated));
1199 > }
1200 >
1201 > public readonly type = ContextKeyExprType.Greater;
1202 >
1203 > private constructor(
1204 private readonly key: string,
1205 private readonly value: number | string,
1206 private negated: ContextKeyExpression | null
1207 ) { }
1208 > contextkey.ts
1209 > public cmp(other: ContextKeyExpression): number {
1210 if (other.type !== this.type) {
1211 return this.type - other.type;
1213 return cmp2(this.key, this.value, other.key, other.value);
1214 }
1215 > contextkey.ts
1216 > public equals(other: ContextKeyExpression): boolean {
1217 if (other.type === this.type) {
1218 return (this.key === other.key && this.value === other.value);
1220 return false;
1221 }
1222 > contextkey.ts
1223 > public substituteConstants(): ContextKeyExpression | undefined {
1224 return this;
1225 }
1226 > contextkey.ts
1227 > public evaluate(context: IContext): boolean {
1228 if (typeof this.value === 'string') {
1229 return false;
1231 return (parseFloat(context.getValue<any>(this.key)) > this.value);
1232 }
1233 > contextkey.ts
1234 > public serialize(): string {
1235 return `${this.key} > ${this.value}`;
1236 }
1237 > contextkey.ts
1238 > public keys(): string[] {
1239 return [this.key];
1240 }
1241 > contextkey.ts
1242 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1243 return mapFnc.mapGreater(this.key, this.value);
1244 }
1245 > contextkey.ts
1246 > public negate(): ContextKeyExpression {
1247 if (!this.negated) {
1248 this.negated = ContextKeySmallerEqualsExpr.create(this.key, this.value, this);
1250 return this.negated;
1251 }
1252 > } contextkey.ts
1253 >
1254 > export class ContextKeyGreaterEqualsExpr implements IContextKeyExpression {
1255 >
1256 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1257 > return withFloatOrStr(_value, (value) => new ContextKeyGreaterEqualsExpr(key, value, negated));
1258 > }
1259 >
1260 > public readonly type = ContextKeyExprType.GreaterEquals;
1261 >
1262 > private constructor(
1263 private readonly key: string,
1264 private readonly value: number | string,
1265 private negated: ContextKeyExpression | null
1266 ) { }
1267 > contextkey.ts
1268 > public cmp(other: ContextKeyExpression): number {
1269 if (other.type !== this.type) {
1270 return this.type - other.type;
1272 return cmp2(this.key, this.value, other.key, other.value);
1273 }
1274 > contextkey.ts
1275 > public equals(other: ContextKeyExpression): boolean {
1276 if (other.type === this.type) {
1277 return (this.key === other.key && this.value === other.value);
1279 return false;
1280 }
1281 > contextkey.ts
1282 > public substituteConstants(): ContextKeyExpression | undefined {
1283 return this;
1284 }
1285 > contextkey.ts
1286 > public evaluate(context: IContext): boolean {
1287 if (typeof this.value === 'string') {
1288 return false;
1290 return (parseFloat(context.getValue<any>(this.key)) >= this.value);
1291 }
1292 > contextkey.ts
1293 > public serialize(): string {
1294 return `${this.key} >= ${this.value}`;
1295 }
1296 > contextkey.ts
1297 > public keys(): string[] {
1298 return [this.key];
1299 }
1300 > contextkey.ts
1301 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1302 return mapFnc.mapGreaterEquals(this.key, this.value);
1303 }
1304 > contextkey.ts
1305 > public negate(): ContextKeyExpression {
1306 if (!this.negated) {
1307 this.negated = ContextKeySmallerExpr.create(this.key, this.value, this);
1309 return this.negated;
1310 }
1311 > } contextkey.ts
1312 >
1313 > export class ContextKeySmallerExpr implements IContextKeyExpression {
1314 >
1315 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1316 > return withFloatOrStr(_value, (value) => new ContextKeySmallerExpr(key, value, negated));
1317 > }
1318 >
1319 > public readonly type = ContextKeyExprType.Smaller;
1320 >
1321 > private constructor(
1322 private readonly key: string,
1323 private readonly value: number | string,
1325 ) {
1326 }
1327 > contextkey.ts
1328 > public cmp(other: ContextKeyExpression): number {
1329 if (other.type !== this.type) {
1330 return this.type - other.type;
1332 return cmp2(this.key, this.value, other.key, other.value);
1333 }
1334 > contextkey.ts
1335 > public equals(other: ContextKeyExpression): boolean {
1336 if (other.type === this.type) {
1337 return (this.key === other.key && this.value === other.value);
1339 return false;
1340 }
1341 > contextkey.ts
1342 > public substituteConstants(): ContextKeyExpression | undefined {
1343 return this;
1344 }
1345 > contextkey.ts
1346 > public evaluate(context: IContext): boolean {
1347 if (typeof this.value === 'string') {
1348 return false;
1350 return (parseFloat(context.getValue<any>(this.key)) < this.value);
1351 }
1352 > contextkey.ts
1353 > public serialize(): string {
1354 return `${this.key} < ${this.value}`;
1355 }
1356 > contextkey.ts
1357 > public keys(): string[] {
1358 return [this.key];
1359 }
1360 > contextkey.ts
1361 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1362 return mapFnc.mapSmaller(this.key, this.value);
1363 }
1364 > contextkey.ts
1365 > public negate(): ContextKeyExpression {
1366 if (!this.negated) {
1367 this.negated = ContextKeyGreaterEqualsExpr.create(this.key, this.value, this);
1369 return this.negated;
1370 }
1371 > } contextkey.ts
1372 >
1373 > export class ContextKeySmallerEqualsExpr implements IContextKeyExpression {
1374 >
1375 > public static create(key: string, _value: any, negated: ContextKeyExpression | null = null): ContextKeyExpression {
1376 > return withFloatOrStr(_value, (value) => new ContextKeySmallerEqualsExpr(key, value, negated));
1377 > }
1378 >
1379 > public readonly type = ContextKeyExprType.SmallerEquals;
1380 >
1381 > private constructor(
1382 private readonly key: string,
1383 private readonly value: number | string,
1385 ) {
1386 }
1387 > contextkey.ts
1388 > public cmp(other: ContextKeyExpression): number {
1389 if (other.type !== this.type) {
1390 return this.type - other.type;
1392 return cmp2(this.key, this.value, other.key, other.value);
1393 }
1394 > contextkey.ts
1395 > public equals(other: ContextKeyExpression): boolean {
1396 if (other.type === this.type) {
1397 return (this.key === other.key && this.value === other.value);
1399 return false;
1400 }
1401 > contextkey.ts
1402 > public substituteConstants(): ContextKeyExpression | undefined {
1403 return this;
1404 }
1405 > contextkey.ts
1406 > public evaluate(context: IContext): boolean {
1407 if (typeof this.value === 'string') {
1408 return false;
1410 return (parseFloat(context.getValue<any>(this.key)) <= this.value);
1411 }
1412 > contextkey.ts
1413 > public serialize(): string {
1414 return `${this.key} <= ${this.value}`;
1415 }
1416 > contextkey.ts
1417 > public keys(): string[] {
1418 return [this.key];
1419 }
1420 > contextkey.ts
1421 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1422 return mapFnc.mapSmallerEquals(this.key, this.value);
1423 }
1424 > contextkey.ts
1425 > public negate(): ContextKeyExpression {
1426 if (!this.negated) {
1427 this.negated = ContextKeyGreaterExpr.create(this.key, this.value, this);
1429 return this.negated;
1430 }
1431 > } contextkey.ts
1432 >
1433 > export class ContextKeyRegexExpr implements IContextKeyExpression {
1434 >
1435 > public static create(key: string, regexp: RegExp | null): ContextKeyRegexExpr {
1436 > return new ContextKeyRegexExpr(key, regexp);
1437 > }
1438 >
1439 > public readonly type = ContextKeyExprType.Regex;
1440 > private negated: ContextKeyExpression | null = null;
1441 >
1442 > private constructor(
1443 private readonly key: string,
1444 private readonly regexp: RegExp | null
1446 //
1447 }
1448 > contextkey.ts
1449 > public cmp(other: ContextKeyExpression): number {
1450 if (other.type !== this.type) {
1451 return this.type - other.type;
1467 return 0;
1468 }
1469 > contextkey.ts
1470 > public equals(other: ContextKeyExpression): boolean {
1471 if (other.type === this.type) {
1472 const thisSource = this.regexp ? this.regexp.source : '';
1476 return false;
1477 }
1478 > contextkey.ts
1479 > public substituteConstants(): ContextKeyExpression | undefined {
1480 return this;
1481 }
1482 > contextkey.ts
1483 > public evaluate(context: IContext): boolean {
1484 const value = context.getValue<any>(this.key);
1485 return this.regexp ? this.regexp.test(value) : false;
1486 }
1487 > contextkey.ts
1488 > public serialize(): string {
1489 const value = this.regexp
1490 ? `/${this.regexp.source}/${this.regexp.flags}`
1492 return `${this.key} =~ ${value}`;
1493 }
1494 > contextkey.ts
1495 > public keys(): string[] {
1496 return [this.key];
1497 }
1498 > contextkey.ts
1499 > public map(mapFnc: IContextKeyExprMapper): ContextKeyRegexExpr {
1500 return mapFnc.mapRegex(this.key, this.regexp);
1501 }
1502 > contextkey.ts
1503 > public negate(): ContextKeyExpression {
1504 if (!this.negated) {
1505 this.negated = ContextKeyNotRegexExpr.create(this);
1507 return this.negated;
1508 }
1509 > } contextkey.ts
1510 >
1511 > export class ContextKeyNotRegexExpr implements IContextKeyExpression {
1512 >
1513 > public static create(actual: ContextKeyRegexExpr): ContextKeyExpression {
1514 > return new ContextKeyNotRegexExpr(actual);
1515 > }
1516 >
1517 > public readonly type = ContextKeyExprType.NotRegex;
1518 >
1519 > private constructor(private readonly _actual: ContextKeyRegexExpr) {
1520 //
1521 }
1522 > contextkey.ts
1523 > public cmp(other: ContextKeyExpression): number {
1524 if (other.type !== this.type) {
1525 return this.type - other.type;
1527 return this._actual.cmp(other._actual);
1528 }
1529 > contextkey.ts
1530 > public equals(other: ContextKeyExpression): boolean {
1531 if (other.type === this.type) {
1532 return this._actual.equals(other._actual);
1534 return false;
1535 }
1536 > contextkey.ts
1537 > public substituteConstants(): ContextKeyExpression | undefined {
1538 return this;
1539 }
1540 > contextkey.ts
1541 > public evaluate(context: IContext): boolean {
1542 return !this._actual.evaluate(context);
1543 }
1544 > contextkey.ts
1545 > public serialize(): string {
1546 return `!(${this._actual.serialize()})`;
1547 }
1548 > contextkey.ts
1549 > public keys(): string[] {
1550 return this._actual.keys();
1551 }
1552 > contextkey.ts
1553 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1554 return new ContextKeyNotRegexExpr(this._actual.map(mapFnc));
1555 }
1556 > contextkey.ts
1557 > public negate(): ContextKeyExpression {
1558 return this._actual;
1559 }
1560 > } contextkey.ts
1561 >
1562 > /**
1563 > * @returns the same instance if nothing changed.
1564 > */
1565 function eliminateConstantsInArray(arr: ContextKeyExpression[]): (ContextKeyExpression | undefined)[] {
1566 // Allocate array only if there is a difference
1591 return newArr;
1592 }
1593 > contextkey.ts
1594 > export class ContextKeyAndExpr implements IContextKeyExpression {
1595 >
1596 > public static create(_expr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1597 > return ContextKeyAndExpr._normalizeArr(_expr, negated, extraRedundantCheck);
1598 > }
1599 >
1600 > public readonly type = ContextKeyExprType.And;
1601 >
1602 > private constructor(
1603 public readonly expr: ContextKeyExpression[],
1604 private negated: ContextKeyExpression | null
1605 ) {
1606 }
1607 > contextkey.ts
1608 > public cmp(other: ContextKeyExpression): number {
1609 if (other.type !== this.type) {
1610 return this.type - other.type;
1624 return 0;
1625 }
1626 > contextkey.ts
1627 > public equals(other: ContextKeyExpression): boolean {
1628 if (other.type === this.type) {
1629 if (this.expr.length !== other.expr.length) {
1639 return false;
1640 }
1641 > contextkey.ts
1642 > public substituteConstants(): ContextKeyExpression | undefined {
1643 const exprArr = eliminateConstantsInArray(this.expr);
1644 if (exprArr === this.expr) {
1648 return ContextKeyAndExpr.create(exprArr, this.negated, false);
1649 }
1650 > contextkey.ts
1651 > public evaluate(context: IContext): boolean {
1652 for (let i = 0, len = this.expr.length; i < len; i++) {
1653 if (!this.expr[i].evaluate(context)) {
1657 return true;
1658 }
1659 > contextkey.ts
1660 > private static _normalizeArr(arr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1661 const expr: ContextKeyExpression[] = [];
1662 let hasTrue = false;
1762 return new ContextKeyAndExpr(expr, negated);
1763 }
1764 > contextkey.ts
1765 > public serialize(): string {
1766 return this.expr.map(e => e.serialize()).join(' && ');
1767 }
1768 > contextkey.ts
1769 > public keys(): string[] {
1770 const result: string[] = [];
1771 for (const expr of this.expr) {
1774 return result;
1775 }
1776 > contextkey.ts
1777 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1778 return new ContextKeyAndExpr(this.expr.map(expr => expr.map(mapFnc)), null);
1779 }
1780 > contextkey.ts
1781 > public negate(): ContextKeyExpression {
1782 if (!this.negated) {
1783 const result: ContextKeyExpression[] = [];
1789 return this.negated;
1790 }
1791 > } contextkey.ts
1792 >
1793 > export class ContextKeyOrExpr implements IContextKeyExpression {
1794 >
1795 > public static create(_expr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1796 > return ContextKeyOrExpr._normalizeArr(_expr, negated, extraRedundantCheck);
1797 > }
1798 >
1799 > public readonly type = ContextKeyExprType.Or;
1800 >
1801 > private constructor(
1802 public readonly expr: ContextKeyExpression[],
1803 private negated: ContextKeyExpression | null
1804 ) {
1805 }
1806 > contextkey.ts
1807 > public cmp(other: ContextKeyExpression): number {
1808 if (other.type !== this.type) {
1809 return this.type - other.type;
1823 return 0;
1824 }
1825 > contextkey.ts
1826 > public equals(other: ContextKeyExpression): boolean {
1827 if (other.type === this.type) {
1828 if (this.expr.length !== other.expr.length) {
1838 return false;
1839 }
1840 > contextkey.ts
1841 > public substituteConstants(): ContextKeyExpression | undefined {
1842 const exprArr = eliminateConstantsInArray(this.expr);
1843 if (exprArr === this.expr) {
1847 return ContextKeyOrExpr.create(exprArr, this.negated, false);
1848 }
1849 > contextkey.ts
1850 > public evaluate(context: IContext): boolean {
1851 for (let i = 0, len = this.expr.length; i < len; i++) {
1852 if (this.expr[i].evaluate(context)) {
1856 return false;
1857 }
1858 > contextkey.ts
1859 > private static _normalizeArr(arr: ReadonlyArray<ContextKeyExpression | null | undefined>, negated: ContextKeyExpression | null, extraRedundantCheck: boolean): ContextKeyExpression | undefined {
1860 let expr: ContextKeyExpression[] = [];
1861 let hasFalse = false;
1932 return new ContextKeyOrExpr(expr, negated);
1933 }
1934 > contextkey.ts
1935 > public serialize(): string {
1936 return this.expr.map(e => e.serialize()).join(' || ');
1937 }
1938 > contextkey.ts
1939 > public keys(): string[] {
1940 const result: string[] = [];
1941 for (const expr of this.expr) {
1944 return result;
1945 }
1946 > contextkey.ts
1947 > public map(mapFnc: IContextKeyExprMapper): ContextKeyExpression {
1948 return new ContextKeyOrExpr(this.expr.map(expr => expr.map(mapFnc)), null);
1949 }
1950 > contextkey.ts
1951 > public negate(): ContextKeyExpression {
1952 if (!this.negated) {
1953 const result: ContextKeyExpression[] = [];
1976 return this.negated;
1977 }
1978 > } contextkey.ts
1979 >
1980 > export interface ContextKeyInfo {
1981 > readonly key: string;
1982 > readonly type?: string;
1983 > readonly description?: string;
1984 > }
1985 >
1986 > export class RawContextKey<T extends ContextKeyValue> extends ContextKeyDefinedExpr {
1987 >
1988 > private static _info: ContextKeyInfo[] = [];
1989 >
1990 > static all(): IterableIterator<ContextKeyInfo> {
1991 return RawContextKey._info.values();
1992 }
1993 > contextkey.ts
1994 > private readonly _defaultValue: T | undefined;
1995 >
1996 > constructor(key: string, defaultValue: T | undefined, metaOrHide?: string | true | { type: string; description: string }) {
1997 > super(key, null); contextkey.ts
1998 > this._defaultValue = defaultValue;
1999 >
2000 > // collect all context keys into a central place
2001 > if (typeof metaOrHide === 'object') {
2002 > RawContextKey._info.push({ ...metaOrHide, key }); contextkey.ts
2003 > } else if (metaOrHide !== true) { contextkey.ts
2004 > RawContextKey._info.push({ key, description: metaOrHide, type: defaultValue !== null && defaultValue !== undefined ? typeof defaultValue : undefined }); contextkey.ts
2005 > }
2006 > } contextkey.ts
2007 > contextkey.ts
2008 > public bindTo(target: IContextKeyService): IContextKey<T> {
2009 return target.createKey(this.key, this._defaultValue);
2010 }
2011 > contextkey.ts
2012 > public getValue(target: IContextKeyService): T | undefined {
2013 return target.getContextKeyValue<T>(this.key);
2014 }
2015 > contextkey.ts
2016 > public toNegated(): ContextKeyExpression {
2017 return this.negate();
2018 }
2019 > contextkey.ts
2020 > public isEqualTo(value: any): ContextKeyExpression {
2021 return ContextKeyEqualsExpr.create(this.key, value);
2022 }
2023 > contextkey.ts
2024 > public notEqualsTo(value: any): ContextKeyExpression {
2025 return ContextKeyNotEqualsExpr.create(this.key, value);
2026 }
2027 > contextkey.ts
2028 > public greater(value: any): ContextKeyExpression {
2029 return ContextKeyGreaterExpr.create(this.key, value);
2030 }
2031 > } contextkey.ts
2032 >
2033 > export type ContextKeyValue = null | undefined | boolean | number | string
2034 > | Array<null | undefined | boolean | number | string>
2035 > | Record<string, null | undefined | boolean | number | string>;
2036 >
2037 > export interface IContext {
2038 > getValue<T extends ContextKeyValue = ContextKeyValue>(key: string): T | undefined;
2039 > }
2040 >
2041 > export interface IContextKey<T extends ContextKeyValue = ContextKeyValue> {
2042 > set(value: T): void;
2043 > reset(): void;
2044 > get(): T | undefined;
2045 > }
2046 >
2047 > export interface IContextKeyServiceTarget {
2048 > parentElement: IContextKeyServiceTarget | null;
2049 > setAttribute(attr: string, value: string): void;
2050 > removeAttribute(attr: string): void;
2051 > hasAttribute(attr: string): boolean;
2052 > getAttribute(attr: string): string | null;
2053 > }
2054 >
2055 > export const IContextKeyService = createDecorator<IContextKeyService>('contextKeyService');
2056 >
2057 > export interface IReadableSet<T> {
2058 > has(value: T): boolean;
2059 > }
2060 >
2061 > export interface IContextKeyChangeEvent {
2062 > affectsSome(keys: IReadableSet<string>): boolean;
2063 > allKeysContainedIn(keys: IReadableSet<string>): boolean;
2064 > }
2065 >
2066 > export type IScopedContextKeyService = IContextKeyService & IDisposable;
2067 >
2068 > export interface IContextKeyService {
2069 > readonly _serviceBrand: undefined;
2070 >
2071 > readonly onDidChangeContext: Event<IContextKeyChangeEvent>;
2072 > bufferChangeEvents(callback: Function): void;
2073 >
2074 > createKey<T extends ContextKeyValue>(key: string, defaultValue: T | undefined): IContextKey<T>;
2075 > contextMatchesRules(rules: ContextKeyExpression | undefined): boolean;
2076 > getContextKeyValue<T>(key: string): T | undefined;
2077 >
2078 > createScoped(target: IContextKeyServiceTarget): IScopedContextKeyService;
2079 > createOverlay(overlay: Iterable<[string, any]>): IContextKeyService;
2080 > getContext(target: IContextKeyServiceTarget | null): IContext;
2081 >
2082 > updateParent(parentContextKeyService: IContextKeyService): void;
2083 > }
2084 >
2085 function cmp1(key1: string, key2: string): number {
2086 if (key1 < key2) {
2092 return 0;
2093 }
2094 > contextkey.ts
2095 function cmp2(key1: string, value1: any, key2: string, value2: any): number {
2096 if (key1 < key2) {
2108 return 0;
2109 }
2110 > contextkey.ts
2111 > /**
2112 > * Returns true if it is provable `p` implies `q`.
2113 > */
2114 > export function implies(p: ContextKeyExpression, q: ContextKeyExpression): boolean {
2115
2116 if (p.type === ContextKeyExprType.False || q.type === ContextKeyExprType.True) {
2152 return p.equals(q);
2153 }
2154 > contextkey.ts
2155 > /**
2156 > * Returns true if all elements in `p` are also present in `q`.
2157 > * The two arrays are assumed to be sorted
2158 > */
2159 function allElementsIncluded(p: ContextKeyExpression[], q: ContextKeyExpression[]): boolean {
2160 let pIndex = 0;
2175 return (pIndex === p.length);
2176 }
2177 > contextkey.ts
2178 function getTerminals(node: ContextKeyExpression) {
2179 if (node.type === ContextKeyExprType.Or) {
src/vs/base/common/codiconsLibrary.ts 758 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- codiconsLibrary.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 > import { register } from './codiconsUtil.js';
6 >
7 >
8 > // This file is automatically generated by (microsoft/vscode-codicons)/scripts/export-to-ts.js
9 > // Please don't edit it, as your changes will be overwritten.
10 > // Instead, add mappings to codiconsDerived in codicons.ts.
11 > export const codiconsLibrary = {
12 > add: register('add', 0xea60),
13 > plus: register('plus', 0xea60),
14 > gistNew: register('gist-new', 0xea60),
15 > repoCreate: register('repo-create', 0xea60),
16 > lightbulb: register('lightbulb', 0xea61),
17 > lightBulb: register('light-bulb', 0xea61),
18 > repo: register('repo', 0xea62),
19 > repoDelete: register('repo-delete', 0xea62),
20 > gistFork: register('gist-fork', 0xea63),
21 > repoForked: register('repo-forked', 0xea63),
22 > gitPullRequest: register('git-pull-request', 0xea64),
23 > gitPullRequestAbandoned: register('git-pull-request-abandoned', 0xea64),
24 > recordKeys: register('record-keys', 0xea65),
25 > keyboard: register('keyboard', 0xea65),
26 > tag: register('tag', 0xea66),
27 > gitPullRequestLabel: register('git-pull-request-label', 0xea66),
28 > tagAdd: register('tag-add', 0xea66),
29 > tagRemove: register('tag-remove', 0xea66),
30 > person: register('person', 0xea67),
31 > personFollow: register('person-follow', 0xea67),
32 > personOutline: register('person-outline', 0xea67),
33 > personFilled: register('person-filled', 0xea67),
34 > sourceControl: register('source-control', 0xea68),
35 > mirror: register('mirror', 0xea69),
36 > mirrorPublic: register('mirror-public', 0xea69),
37 > star: register('star', 0xea6a),
38 > starAdd: register('star-add', 0xea6a),
39 > starDelete: register('star-delete', 0xea6a),
40 > starEmpty: register('star-empty', 0xea6a),
41 > comment: register('comment', 0xea6b),
42 > commentAdd: register('comment-add', 0xea6b),
43 > alert: register('alert', 0xea6c),
44 > warning: register('warning', 0xea6c),
45 > search: register('search', 0xea6d),
46 > searchSave: register('search-save', 0xea6d),
47 > logOut: register('log-out', 0xea6e),
48 > signOut: register('sign-out', 0xea6e),
49 > logIn: register('log-in', 0xea6f),
50 > signIn: register('sign-in', 0xea6f),
51 > eye: register('eye', 0xea70),
52 > eyeUnwatch: register('eye-unwatch', 0xea70),
53 > eyeWatch: register('eye-watch', 0xea70),
54 > circleFilled: register('circle-filled', 0xea71),
55 > primitiveDot: register('primitive-dot', 0xea71),
56 > closeDirty: register('close-dirty', 0xea71),
57 > debugBreakpoint: register('debug-breakpoint', 0xea71),
58 > debugBreakpointDisabled: register('debug-breakpoint-disabled', 0xea71),
59 > debugHint: register('debug-hint', 0xea71),
60 > terminalDecorationSuccess: register('terminal-decoration-success', 0xea71),
61 > primitiveSquare: register('primitive-square', 0xea72),
62 > edit: register('edit', 0xea73),
63 > pencil: register('pencil', 0xea73),
64 > info: register('info', 0xea74),
65 > issueOpened: register('issue-opened', 0xea74),
66 > gistPrivate: register('gist-private', 0xea75),
67 > gitForkPrivate: register('git-fork-private', 0xea75),
68 > lock: register('lock', 0xea75),
69 > mirrorPrivate: register('mirror-private', 0xea75),
70 > close: register('close', 0xea76),
71 > removeClose: register('remove-close', 0xea76),
72 > x: register('x', 0xea76),
73 > repoSync: register('repo-sync', 0xea77),
74 > sync: register('sync', 0xea77),
75 > clone: register('clone', 0xea78),
76 > desktopDownload: register('desktop-download', 0xea78),
77 > beaker: register('beaker', 0xea79),
78 > microscope: register('microscope', 0xea79),
79 > vm: register('vm', 0xea7a),
80 > deviceDesktop: register('device-desktop', 0xea7a),
81 > file: register('file', 0xea7b),
82 > more: register('more', 0xea7c),
83 > ellipsis: register('ellipsis', 0xea7c),
84 > kebabHorizontal: register('kebab-horizontal', 0xea7c),
85 > mailReply: register('mail-reply', 0xea7d),
86 > reply: register('reply', 0xea7d),
87 > organization: register('organization', 0xea7e),
88 > organizationFilled: register('organization-filled', 0xea7e),
89 > organizationOutline: register('organization-outline', 0xea7e),
90 > newFile: register('new-file', 0xea7f),
91 > fileAdd: register('file-add', 0xea7f),
92 > newFolder: register('new-folder', 0xea80),
93 > fileDirectoryCreate: register('file-directory-create', 0xea80),
94 > trash: register('trash', 0xea81),
95 > trashcan: register('trashcan', 0xea81),
96 > history: register('history', 0xea82),
97 > clock: register('clock', 0xea82),
98 > folder: register('folder', 0xea83),
99 > fileDirectory: register('file-directory', 0xea83),
100 > symbolFolder: register('symbol-folder', 0xea83),
101 > logoGithub: register('logo-github', 0xea84),
102 > markGithub: register('mark-github', 0xea84),
103 > github: register('github', 0xea84),
104 > terminal: register('terminal', 0xea85),
105 > console: register('console', 0xea85),
106 > repl: register('repl', 0xea85),
107 > zap: register('zap', 0xea86),
108 > symbolEvent: register('symbol-event', 0xea86),
109 > error: register('error', 0xea87),
110 > stop: register('stop', 0xea87),
111 > variable: register('variable', 0xea88),
112 > symbolVariable: register('symbol-variable', 0xea88),
113 > array: register('array', 0xea8a),
114 > symbolArray: register('symbol-array', 0xea8a),
115 > symbolModule: register('symbol-module', 0xea8b),
116 > symbolPackage: register('symbol-package', 0xea8b),
117 > symbolNamespace: register('symbol-namespace', 0xea8b),
118 > symbolObject: register('symbol-object', 0xea8b),
119 > symbolMethod: register('symbol-method', 0xea8c),
120 > symbolFunction: register('symbol-function', 0xea8c),
121 > symbolConstructor: register('symbol-constructor', 0xea8c),
122 > symbolBoolean: register('symbol-boolean', 0xea8f),
123 > symbolNull: register('symbol-null', 0xea8f),
124 > symbolNumeric: register('symbol-numeric', 0xea90),
125 > symbolNumber: register('symbol-number', 0xea90),
126 > symbolStructure: register('symbol-structure', 0xea91),
127 > symbolStruct: register('symbol-struct', 0xea91),
128 > symbolParameter: register('symbol-parameter', 0xea92),
129 > symbolTypeParameter: register('symbol-type-parameter', 0xea92),
130 > symbolKey: register('symbol-key', 0xea93),
131 > symbolText: register('symbol-text', 0xea93),
132 > symbolReference: register('symbol-reference', 0xea94),
133 > goToFile: register('go-to-file', 0xea94),
134 > symbolEnum: register('symbol-enum', 0xea95),
135 > symbolValue: register('symbol-value', 0xea95),
136 > symbolRuler: register('symbol-ruler', 0xea96),
137 > symbolUnit: register('symbol-unit', 0xea96),
138 > activateBreakpoints: register('activate-breakpoints', 0xea97),
139 > archive: register('archive', 0xea98),
140 > arrowBoth: register('arrow-both', 0xea99),
141 > arrowDown: register('arrow-down', 0xea9a),
142 > arrowLeft: register('arrow-left', 0xea9b),
143 > arrowRight: register('arrow-right', 0xea9c),
144 > arrowSmallDown: register('arrow-small-down', 0xea9d),
145 > arrowSmallLeft: register('arrow-small-left', 0xea9e),
146 > arrowSmallRight: register('arrow-small-right', 0xea9f),
147 > arrowSmallUp: register('arrow-small-up', 0xeaa0),
148 > arrowUp: register('arrow-up', 0xeaa1),
149 > bell: register('bell', 0xeaa2),
150 > bold: register('bold', 0xeaa3),
151 > book: register('book', 0xeaa4),
152 > bookmark: register('bookmark', 0xeaa5),
153 > debugBreakpointConditionalUnverified: register('debug-breakpoint-conditional-unverified', 0xeaa6),
154 > debugBreakpointConditional: register('debug-breakpoint-conditional', 0xeaa7),
155 > debugBreakpointConditionalDisabled: register('debug-breakpoint-conditional-disabled', 0xeaa7),
156 > debugBreakpointDataUnverified: register('debug-breakpoint-data-unverified', 0xeaa8),
157 > debugBreakpointData: register('debug-breakpoint-data', 0xeaa9),
158 > debugBreakpointDataDisabled: register('debug-breakpoint-data-disabled', 0xeaa9),
159 > debugBreakpointLogUnverified: register('debug-breakpoint-log-unverified', 0xeaaa),
160 > debugBreakpointLog: register('debug-breakpoint-log', 0xeaab),
161 > debugBreakpointLogDisabled: register('debug-breakpoint-log-disabled', 0xeaab),
162 > briefcase: register('briefcase', 0xeaac),
163 > broadcast: register('broadcast', 0xeaad),
164 > browser: register('browser', 0xeaae),
165 > bug: register('bug', 0xeaaf),
166 > calendar: register('calendar', 0xeab0),
167 > caseSensitive: register('case-sensitive', 0xeab1),
168 > check: register('check', 0xeab2),
169 > checklist: register('checklist', 0xeab3),
170 > chevronDown: register('chevron-down', 0xeab4),
171 > chevronLeft: register('chevron-left', 0xeab5),
172 > chevronRight: register('chevron-right', 0xeab6),
173 > chevronUp: register('chevron-up', 0xeab7),
174 > chromeClose: register('chrome-close', 0xeab8),
175 > chromeMaximize: register('chrome-maximize', 0xeab9),
176 > chromeMinimize: register('chrome-minimize', 0xeaba),
177 > chromeRestore: register('chrome-restore', 0xeabb),
178 > circleOutline: register('circle-outline', 0xeabc),
179 > circle: register('circle', 0xeabc),
180 > debugBreakpointUnverified: register('debug-breakpoint-unverified', 0xeabc),
181 > terminalDecorationIncomplete: register('terminal-decoration-incomplete', 0xeabc),
182 > circleSlash: register('circle-slash', 0xeabd),
183 > circuitBoard: register('circuit-board', 0xeabe),
184 > clearAll: register('clear-all', 0xeabf),
185 > clippy: register('clippy', 0xeac0),
186 > closeAll: register('close-all', 0xeac1),
187 > cloudDownload: register('cloud-download', 0xeac2),
188 > cloudUpload: register('cloud-upload', 0xeac3),
189 > code: register('code', 0xeac4),
190 > collapseAll: register('collapse-all', 0xeac5),
191 > colorMode: register('color-mode', 0xeac6),
192 > commentDiscussion: register('comment-discussion', 0xeac7),
193 > creditCard: register('credit-card', 0xeac9),
194 > dash: register('dash', 0xeacc),
195 > dashboard: register('dashboard', 0xeacd),
196 > database: register('database', 0xeace),
197 > debugContinue: register('debug-continue', 0xeacf),
198 > debugDisconnect: register('debug-disconnect', 0xead0),
199 > debugPause: register('debug-pause', 0xead1),
200 > debugRestart: register('debug-restart', 0xead2),
201 > debugStart: register('debug-start', 0xead3),
202 > debugStepInto: register('debug-step-into', 0xead4),
203 > debugStepOut: register('debug-step-out', 0xead5),
204 > debugStepOver: register('debug-step-over', 0xead6),
205 > debugStop: register('debug-stop', 0xead7),
206 > debug: register('debug', 0xead8),
207 > deviceCameraVideo: register('device-camera-video', 0xead9),
208 > deviceCamera: register('device-camera', 0xeada),
209 > deviceMobile: register('device-mobile', 0xeadb),
210 > diffAdded: register('diff-added', 0xeadc),
211 > diffIgnored: register('diff-ignored', 0xeadd),
212 > diffModified: register('diff-modified', 0xeade),
213 > diffRemoved: register('diff-removed', 0xeadf),
214 > diffRenamed: register('diff-renamed', 0xeae0),
215 > diff: register('diff', 0xeae1),
216 > diffSidebyside: register('diff-sidebyside', 0xeae1),
217 > discard: register('discard', 0xeae2),
218 > editorLayout: register('editor-layout', 0xeae3),
219 > emptyWindow: register('empty-window', 0xeae4),
220 > exclude: register('exclude', 0xeae5),
221 > extensions: register('extensions', 0xeae6),
222 > eyeClosed: register('eye-closed', 0xeae7),
223 > fileBinary: register('file-binary', 0xeae8),
224 > fileCode: register('file-code', 0xeae9),
225 > fileMedia: register('file-media', 0xeaea),
226 > filePdf: register('file-pdf', 0xeaeb),
227 > fileSubmodule: register('file-submodule', 0xeaec),
228 > fileSymlinkDirectory: register('file-symlink-directory', 0xeaed),
229 > fileSymlinkFile: register('file-symlink-file', 0xeaee),
230 > fileZip: register('file-zip', 0xeaef),
231 > files: register('files', 0xeaf0),
232 > filter: register('filter', 0xeaf1),
233 > flame: register('flame', 0xeaf2),
234 > foldDown: register('fold-down', 0xeaf3),
235 > foldUp: register('fold-up', 0xeaf4),
236 > fold: register('fold', 0xeaf5),
237 > folderActive: register('folder-active', 0xeaf6),
238 > folderOpened: register('folder-opened', 0xeaf7),
239 > gear: register('gear', 0xeaf8),
240 > gift: register('gift', 0xeaf9),
241 > gistSecret: register('gist-secret', 0xeafa),
242 > gist: register('gist', 0xeafb),
243 > gitCommit: register('git-commit', 0xeafc),
244 > gitCompare: register('git-compare', 0xeafd),
245 > compareChanges: register('compare-changes', 0xeafd),
246 > gitMerge: register('git-merge', 0xeafe),
247 > githubAction: register('github-action', 0xeaff),
248 > githubAlt: register('github-alt', 0xeb00),
249 > globe: register('globe', 0xeb01),
250 > grabber: register('grabber', 0xeb02),
251 > graph: register('graph', 0xeb03),
252 > gripper: register('gripper', 0xeb04),
253 > heart: register('heart', 0xeb05),
254 > home: register('home', 0xeb06),
255 > horizontalRule: register('horizontal-rule', 0xeb07),
256 > hubot: register('hubot', 0xeb08),
257 > inbox: register('inbox', 0xeb09),
258 > issueReopened: register('issue-reopened', 0xeb0b),
259 > issues: register('issues', 0xeb0c),
260 > italic: register('italic', 0xeb0d),
261 > jersey: register('jersey', 0xeb0e),
262 > json: register('json', 0xeb0f),
263 > bracket: register('bracket', 0xeb0f),
264 > kebabVertical: register('kebab-vertical', 0xeb10),
265 > key: register('key', 0xeb11),
266 > law: register('law', 0xeb12),
267 > lightbulbAutofix: register('lightbulb-autofix', 0xeb13),
268 > linkExternal: register('link-external', 0xeb14),
269 > link: register('link', 0xeb15),
270 > listOrdered: register('list-ordered', 0xeb16),
271 > listUnordered: register('list-unordered', 0xeb17),
272 > liveShare: register('live-share', 0xeb18),
273 > loading: register('loading', 0xeb19),
274 > location: register('location', 0xeb1a),
275 > mailRead: register('mail-read', 0xeb1b),
276 > mail: register('mail', 0xeb1c),
277 > markdown: register('markdown', 0xeb1d),
278 > megaphone: register('megaphone', 0xeb1e),
279 > mention: register('mention', 0xeb1f),
280 > milestone: register('milestone', 0xeb20),
281 > gitPullRequestMilestone: register('git-pull-request-milestone', 0xeb20),
282 > mortarBoard: register('mortar-board', 0xeb21),
283 > move: register('move', 0xeb22),
284 > multipleWindows: register('multiple-windows', 0xeb23),
285 > mute: register('mute', 0xeb24),
286 > noNewline: register('no-newline', 0xeb25),
287 > note: register('note', 0xeb26),
288 > octoface: register('octoface', 0xeb27),
289 > openPreview: register('open-preview', 0xeb28),
290 > package: register('package', 0xeb29),
291 > paintcan: register('paintcan', 0xeb2a),
292 > pin: register('pin', 0xeb2b),
293 > play: register('play', 0xeb2c),
294 > run: register('run', 0xeb2c),
295 > plug: register('plug', 0xeb2d),
296 > preserveCase: register('preserve-case', 0xeb2e),
297 > preview: register('preview', 0xeb2f),
298 > project: register('project', 0xeb30),
299 > pulse: register('pulse', 0xeb31),
300 > question: register('question', 0xeb32),
301 > quote: register('quote', 0xeb33),
302 > radioTower: register('radio-tower', 0xeb34),
303 > reactions: register('reactions', 0xeb35),
304 > references: register('references', 0xeb36),
305 > refresh: register('refresh', 0xeb37),
306 > regex: register('regex', 0xeb38),
307 > remoteExplorer: register('remote-explorer', 0xeb39),
308 > remote: register('remote', 0xeb3a),
309 > remove: register('remove', 0xeb3b),
310 > replaceAll: register('replace-all', 0xeb3c),
311 > replace: register('replace', 0xeb3d),
312 > repoClone: register('repo-clone', 0xeb3e),
313 > repoForcePush: register('repo-force-push', 0xeb3f),
314 > repoPull: register('repo-pull', 0xeb40),
315 > repoPush: register('repo-push', 0xeb41),
316 > report: register('report', 0xeb42),
317 > requestChanges: register('request-changes', 0xeb43),
318 > rocket: register('rocket', 0xeb44),
319 > rootFolderOpened: register('root-folder-opened', 0xeb45),
320 > rootFolder: register('root-folder', 0xeb46),
321 > rss: register('rss', 0xeb47),
322 > ruby: register('ruby', 0xeb48),
323 > saveAll: register('save-all', 0xeb49),
324 > saveAs: register('save-as', 0xeb4a),
325 > save: register('save', 0xeb4b),
326 > screenFull: register('screen-full', 0xeb4c),
327 > screenNormal: register('screen-normal', 0xeb4d),
328 > searchStop: register('search-stop', 0xeb4e),
329 > server: register('server', 0xeb50),
330 > settingsGear: register('settings-gear', 0xeb51),
331 > settings: register('settings', 0xeb52),
332 > shield: register('shield', 0xeb53),
333 > smiley: register('smiley', 0xeb54),
334 > sortPrecedence: register('sort-precedence', 0xeb55),
335 > splitHorizontal: register('split-horizontal', 0xeb56),
336 > splitVertical: register('split-vertical', 0xeb57),
337 > squirrel: register('squirrel', 0xeb58),
338 > starFull: register('star-full', 0xeb59),
339 > starHalf: register('star-half', 0xeb5a),
340 > symbolClass: register('symbol-class', 0xeb5b),
341 > symbolColor: register('symbol-color', 0xeb5c),
342 > symbolConstant: register('symbol-constant', 0xeb5d),
343 > symbolEnumMember: register('symbol-enum-member', 0xeb5e),
344 > symbolField: register('symbol-field', 0xeb5f),
345 > symbolFile: register('symbol-file', 0xeb60),
346 > symbolInterface: register('symbol-interface', 0xeb61),
347 > symbolKeyword: register('symbol-keyword', 0xeb62),
348 > symbolMisc: register('symbol-misc', 0xeb63),
349 > symbolOperator: register('symbol-operator', 0xeb64),
350 > symbolProperty: register('symbol-property', 0xeb65),
351 > wrench: register('wrench', 0xeb65),
352 > wrenchSubaction: register('wrench-subaction', 0xeb65),
353 > symbolSnippet: register('symbol-snippet', 0xeb66),
354 > tasklist: register('tasklist', 0xeb67),
355 > telescope: register('telescope', 0xeb68),
356 > textSize: register('text-size', 0xeb69),
357 > threeBars: register('three-bars', 0xeb6a),
358 > thumbsdown: register('thumbsdown', 0xeb6b),
359 > thumbsup: register('thumbsup', 0xeb6c),
360 > tools: register('tools', 0xeb6d),
361 > triangleDown: register('triangle-down', 0xeb6e),
362 > triangleLeft: register('triangle-left', 0xeb6f),
363 > triangleRight: register('triangle-right', 0xeb70),
364 > triangleUp: register('triangle-up', 0xeb71),
365 > twitter: register('twitter', 0xeb72),
366 > unfold: register('unfold', 0xeb73),
367 > unlock: register('unlock', 0xeb74),
368 > unmute: register('unmute', 0xeb75),
369 > unverified: register('unverified', 0xeb76),
370 > verified: register('verified', 0xeb77),
371 > versions: register('versions', 0xeb78),
372 > vmActive: register('vm-active', 0xeb79),
373 > vmOutline: register('vm-outline', 0xeb7a),
374 > vmRunning: register('vm-running', 0xeb7b),
375 > watch: register('watch', 0xeb7c),
376 > whitespace: register('whitespace', 0xeb7d),
377 > wholeWord: register('whole-word', 0xeb7e),
378 > window: register('window', 0xeb7f),
379 > wordWrap: register('word-wrap', 0xeb80),
380 > zoomIn: register('zoom-in', 0xeb81),
381 > zoomOut: register('zoom-out', 0xeb82),
382 > listFilter: register('list-filter', 0xeb83),
383 > listFlat: register('list-flat', 0xeb84),
384 > listSelection: register('list-selection', 0xeb85),
385 > selection: register('selection', 0xeb85),
386 > listTree: register('list-tree', 0xeb86),
387 > debugBreakpointFunctionUnverified: register('debug-breakpoint-function-unverified', 0xeb87),
388 > debugBreakpointFunction: register('debug-breakpoint-function', 0xeb88),
389 > debugBreakpointFunctionDisabled: register('debug-breakpoint-function-disabled', 0xeb88),
390 > debugStackframeActive: register('debug-stackframe-active', 0xeb89),
391 > circleSmallFilled: register('circle-small-filled', 0xeb8a),
392 > debugStackframeDot: register('debug-stackframe-dot', 0xeb8a),
393 > terminalDecorationMark: register('terminal-decoration-mark', 0xeb8a),
394 > debugStackframe: register('debug-stackframe', 0xeb8b),
395 > debugStackframeFocused: register('debug-stackframe-focused', 0xeb8b),
396 > debugBreakpointUnsupported: register('debug-breakpoint-unsupported', 0xeb8c),
397 > symbolString: register('symbol-string', 0xeb8d),
398 > debugReverseContinue: register('debug-reverse-continue', 0xeb8e),
399 > debugStepBack: register('debug-step-back', 0xeb8f),
400 > debugRestartFrame: register('debug-restart-frame', 0xeb90),
401 > debugAlt: register('debug-alt', 0xeb91),
402 > callIncoming: register('call-incoming', 0xeb92),
403 > callOutgoing: register('call-outgoing', 0xeb93),
404 > menu: register('menu', 0xeb94),
405 > expandAll: register('expand-all', 0xeb95),
406 > feedback: register('feedback', 0xeb96),
407 > gitPullRequestReviewer: register('git-pull-request-reviewer', 0xeb96),
408 > groupByRefType: register('group-by-ref-type', 0xeb97),
409 > ungroupByRefType: register('ungroup-by-ref-type', 0xeb98),
410 > account: register('account', 0xeb99),
411 > gitPullRequestAssignee: register('git-pull-request-assignee', 0xeb99),
412 > bellDot: register('bell-dot', 0xeb9a),
413 > debugConsole: register('debug-console', 0xeb9b),
414 > library: register('library', 0xeb9c),
415 > output: register('output', 0xeb9d),
416 > runAll: register('run-all', 0xeb9e),
417 > syncIgnored: register('sync-ignored', 0xeb9f),
418 > pinned: register('pinned', 0xeba0),
419 > githubInverted: register('github-inverted', 0xeba1),
420 > serverProcess: register('server-process', 0xeba2),
421 > serverEnvironment: register('server-environment', 0xeba3),
422 > pass: register('pass', 0xeba4),
423 > issueClosed: register('issue-closed', 0xeba4),
424 > stopCircle: register('stop-circle', 0xeba5),
425 > playCircle: register('play-circle', 0xeba6),
426 > record: register('record', 0xeba7),
427 > debugAltSmall: register('debug-alt-small', 0xeba8),
428 > vmConnect: register('vm-connect', 0xeba9),
429 > cloud: register('cloud', 0xebaa),
430 > merge: register('merge', 0xebab),
431 > export: register('export', 0xebac),
432 > graphLeft: register('graph-left', 0xebad),
433 > magnet: register('magnet', 0xebae),
434 > notebook: register('notebook', 0xebaf),
435 > redo: register('redo', 0xebb0),
436 > checkAll: register('check-all', 0xebb1),
437 > pinnedDirty: register('pinned-dirty', 0xebb2),
438 > passFilled: register('pass-filled', 0xebb3),
439 > circleLargeFilled: register('circle-large-filled', 0xebb4),
440 > circleLarge: register('circle-large', 0xebb5),
441 > circleLargeOutline: register('circle-large-outline', 0xebb5),
442 > combine: register('combine', 0xebb6),
443 > gather: register('gather', 0xebb6),
444 > table: register('table', 0xebb7),
445 > variableGroup: register('variable-group', 0xebb8),
446 > typeHierarchy: register('type-hierarchy', 0xebb9),
447 > typeHierarchySub: register('type-hierarchy-sub', 0xebba),
448 > typeHierarchySuper: register('type-hierarchy-super', 0xebbb),
449 > gitPullRequestCreate: register('git-pull-request-create', 0xebbc),
450 > runAbove: register('run-above', 0xebbd),
451 > runBelow: register('run-below', 0xebbe),
452 > notebookTemplate: register('notebook-template', 0xebbf),
453 > debugRerun: register('debug-rerun', 0xebc0),
454 > workspaceTrusted: register('workspace-trusted', 0xebc1),
455 > workspaceUntrusted: register('workspace-untrusted', 0xebc2),
456 > workspaceUnknown: register('workspace-unknown', 0xebc3),
457 > terminalCmd: register('terminal-cmd', 0xebc4),
458 > terminalDebian: register('terminal-debian', 0xebc5),
459 > terminalLinux: register('terminal-linux', 0xebc6),
460 > terminalPowershell: register('terminal-powershell', 0xebc7),
461 > terminalTmux: register('terminal-tmux', 0xebc8),
462 > terminalUbuntu: register('terminal-ubuntu', 0xebc9),
463 > terminalBash: register('terminal-bash', 0xebca),
464 > arrowSwap: register('arrow-swap', 0xebcb),
465 > copy: register('copy', 0xebcc),
466 > personAdd: register('person-add', 0xebcd),
467 > filterFilled: register('filter-filled', 0xebce),
468 > wand: register('wand', 0xebcf),
469 > debugLineByLine: register('debug-line-by-line', 0xebd0),
470 > inspect: register('inspect', 0xebd1),
471 > layers: register('layers', 0xebd2),
472 > layersDot: register('layers-dot', 0xebd3),
473 > layersActive: register('layers-active', 0xebd4),
474 > compass: register('compass', 0xebd5),
475 > compassDot: register('compass-dot', 0xebd6),
476 > compassActive: register('compass-active', 0xebd7),
477 > azure: register('azure', 0xebd8),
478 > issueDraft: register('issue-draft', 0xebd9),
479 > gitPullRequestClosed: register('git-pull-request-closed', 0xebda),
480 > gitPullRequestDraft: register('git-pull-request-draft', 0xebdb),
481 > debugAll: register('debug-all', 0xebdc),
482 > debugCoverage: register('debug-coverage', 0xebdd),
483 > runErrors: register('run-errors', 0xebde),
484 > folderLibrary: register('folder-library', 0xebdf),
485 > debugContinueSmall: register('debug-continue-small', 0xebe0),
486 > beakerStop: register('beaker-stop', 0xebe1),
487 > graphLine: register('graph-line', 0xebe2),
488 > graphScatter: register('graph-scatter', 0xebe3),
489 > pieChart: register('pie-chart', 0xebe4),
490 > bracketDot: register('bracket-dot', 0xebe5),
491 > bracketError: register('bracket-error', 0xebe6),
492 > lockSmall: register('lock-small', 0xebe7),
493 > azureDevops: register('azure-devops', 0xebe8),
494 > verifiedFilled: register('verified-filled', 0xebe9),
495 > newline: register('newline', 0xebea),
496 > layout: register('layout', 0xebeb),
497 > layoutActivitybarLeft: register('layout-activitybar-left', 0xebec),
498 > layoutActivitybarRight: register('layout-activitybar-right', 0xebed),
499 > layoutPanelLeft: register('layout-panel-left', 0xebee),
500 > layoutPanelCenter: register('layout-panel-center', 0xebef),
501 > layoutPanelJustify: register('layout-panel-justify', 0xebf0),
502 > layoutPanelRight: register('layout-panel-right', 0xebf1),
503 > layoutPanel: register('layout-panel', 0xebf2),
504 > layoutSidebarLeft: register('layout-sidebar-left', 0xebf3),
505 > layoutSidebarRight: register('layout-sidebar-right', 0xebf4),
506 > layoutStatusbar: register('layout-statusbar', 0xebf5),
507 > layoutMenubar: register('layout-menubar', 0xebf6),
508 > layoutCentered: register('layout-centered', 0xebf7),
509 > target: register('target', 0xebf8),
510 > indent: register('indent', 0xebf9),
511 > recordSmall: register('record-small', 0xebfa),
512 > errorSmall: register('error-small', 0xebfb),
513 > terminalDecorationError: register('terminal-decoration-error', 0xebfb),
514 > arrowCircleDown: register('arrow-circle-down', 0xebfc),
515 > arrowCircleLeft: register('arrow-circle-left', 0xebfd),
516 > arrowCircleRight: register('arrow-circle-right', 0xebfe),
517 > arrowCircleUp: register('arrow-circle-up', 0xebff),
518 > layoutSidebarRightOff: register('layout-sidebar-right-off', 0xec00),
519 > layoutPanelOff: register('layout-panel-off', 0xec01),
520 > layoutSidebarLeftOff: register('layout-sidebar-left-off', 0xec02),
521 > blank: register('blank', 0xec03),
522 > heartFilled: register('heart-filled', 0xec04),
523 > map: register('map', 0xec05),
524 > mapHorizontal: register('map-horizontal', 0xec05),
525 > foldHorizontal: register('fold-horizontal', 0xec05),
526 > mapFilled: register('map-filled', 0xec06),
527 > mapHorizontalFilled: register('map-horizontal-filled', 0xec06),
528 > foldHorizontalFilled: register('fold-horizontal-filled', 0xec06),
529 > circleSmall: register('circle-small', 0xec07),
530 > bellSlash: register('bell-slash', 0xec08),
531 > bellSlashDot: register('bell-slash-dot', 0xec09),
532 > commentUnresolved: register('comment-unresolved', 0xec0a),
533 > gitPullRequestGoToChanges: register('git-pull-request-go-to-changes', 0xec0b),
534 > gitPullRequestNewChanges: register('git-pull-request-new-changes', 0xec0c),
535 > searchFuzzy: register('search-fuzzy', 0xec0d),
536 > commentDraft: register('comment-draft', 0xec0e),
537 > send: register('send', 0xec0f),
538 > sparkle: register('sparkle', 0xec10),
539 > insert: register('insert', 0xec11),
540 > mic: register('mic', 0xec12),
541 > thumbsdownFilled: register('thumbsdown-filled', 0xec13),
542 > thumbsupFilled: register('thumbsup-filled', 0xec14),
543 > coffee: register('coffee', 0xec15),
544 > snake: register('snake', 0xec16),
545 > game: register('game', 0xec17),
546 > vr: register('vr', 0xec18),
547 > chip: register('chip', 0xec19),
548 > piano: register('piano', 0xec1a),
549 > music: register('music', 0xec1b),
550 > micFilled: register('mic-filled', 0xec1c),
551 > repoFetch: register('repo-fetch', 0xec1d),
552 > copilot: register('copilot', 0xec1e),
553 > lightbulbSparkle: register('lightbulb-sparkle', 0xec1f),
554 > robot: register('robot', 0xec20),
555 > sparkleFilled: register('sparkle-filled', 0xec21),
556 > diffSingle: register('diff-single', 0xec22),
557 > diffMultiple: register('diff-multiple', 0xec23),
558 > surroundWith: register('surround-with', 0xec24),
559 > share: register('share', 0xec25),
560 > gitStash: register('git-stash', 0xec26),
561 > gitStashApply: register('git-stash-apply', 0xec27),
562 > gitStashPop: register('git-stash-pop', 0xec28),
563 > vscode: register('vscode', 0xec29),
564 > vscodeInsiders: register('vscode-insiders', 0xec2a),
565 > codeOss: register('code-oss', 0xec2b),
566 > runCoverage: register('run-coverage', 0xec2c),
567 > runAllCoverage: register('run-all-coverage', 0xec2d),
568 > coverage: register('coverage', 0xec2e),
569 > githubProject: register('github-project', 0xec2f),
570 > mapVertical: register('map-vertical', 0xec30),
571 > foldVertical: register('fold-vertical', 0xec30),
572 > mapVerticalFilled: register('map-vertical-filled', 0xec31),
573 > foldVerticalFilled: register('fold-vertical-filled', 0xec31),
574 > goToSearch: register('go-to-search', 0xec32),
575 > percentage: register('percentage', 0xec33),
576 > sortPercentage: register('sort-percentage', 0xec33),
577 > attach: register('attach', 0xec34),
578 > goToEditingSession: register('go-to-editing-session', 0xec35),
579 > editSession: register('edit-session', 0xec36),
580 > codeReview: register('code-review', 0xec37),
581 > copilotWarning: register('copilot-warning', 0xec38),
582 > python: register('python', 0xec39),
583 > copilotLarge: register('copilot-large', 0xec3a),
584 > copilotWarningLarge: register('copilot-warning-large', 0xec3b),
585 > keyboardTab: register('keyboard-tab', 0xec3c),
586 > copilotBlocked: register('copilot-blocked', 0xec3d),
587 > copilotNotConnected: register('copilot-not-connected', 0xec3e),
588 > flag: register('flag', 0xec3f),
589 > lightbulbEmpty: register('lightbulb-empty', 0xec40),
590 > symbolMethodArrow: register('symbol-method-arrow', 0xec41),
591 > copilotUnavailable: register('copilot-unavailable', 0xec42),
592 > repoPinned: register('repo-pinned', 0xec43),
593 > keyboardTabAbove: register('keyboard-tab-above', 0xec44),
594 > keyboardTabBelow: register('keyboard-tab-below', 0xec45),
595 > gitPullRequestDone: register('git-pull-request-done', 0xec46),
596 > mcp: register('mcp', 0xec47),
597 > extensionsLarge: register('extensions-large', 0xec48),
598 > layoutPanelDock: register('layout-panel-dock', 0xec49),
599 > layoutSidebarLeftDock: register('layout-sidebar-left-dock', 0xec4a),
600 > layoutSidebarRightDock: register('layout-sidebar-right-dock', 0xec4b),
601 > copilotInProgress: register('copilot-in-progress', 0xec4c),
602 > copilotError: register('copilot-error', 0xec4d),
603 > copilotSuccess: register('copilot-success', 0xec4e),
604 > chatSparkle: register('chat-sparkle', 0xec4f),
605 > searchSparkle: register('search-sparkle', 0xec50),
606 > editSparkle: register('edit-sparkle', 0xec51),
607 > copilotSnooze: register('copilot-snooze', 0xec52),
608 > sendToRemoteAgent: register('send-to-remote-agent', 0xec53),
609 > commentDiscussionSparkle: register('comment-discussion-sparkle', 0xec54),
610 > chatSparkleWarning: register('chat-sparkle-warning', 0xec55),
611 > chatSparkleError: register('chat-sparkle-error', 0xec56),
612 > collection: register('collection', 0xec57),
613 > newCollection: register('new-collection', 0xec58),
614 > thinking: register('thinking', 0xec59),
615 > build: register('build', 0xec5a),
616 > commentDiscussionQuote: register('comment-discussion-quote', 0xec5b),
617 > cursor: register('cursor', 0xec5c),
618 > eraser: register('eraser', 0xec5d),
619 > fileText: register('file-text', 0xec5e),
620 > quotes: register('quotes', 0xec60),
621 > rename: register('rename', 0xec61),
622 > runWithDeps: register('run-with-deps', 0xec62),
623 > debugConnected: register('debug-connected', 0xec63),
624 > strikethrough: register('strikethrough', 0xec64),
625 > openInProduct: register('open-in-product', 0xec65),
626 > indexZero: register('index-zero', 0xec66),
627 > agent: register('agent', 0xec67),
628 > editCode: register('edit-code', 0xec68),
629 > repoSelected: register('repo-selected', 0xec69),
630 > skip: register('skip', 0xec6a),
631 > mergeInto: register('merge-into', 0xec6b),
632 > gitBranchChanges: register('git-branch-changes', 0xec6c),
633 > gitBranchStagedChanges: register('git-branch-staged-changes', 0xec6d),
634 > gitBranchConflicts: register('git-branch-conflicts', 0xec6e),
635 > gitBranch: register('git-branch', 0xec6f),
636 > gitBranchCreate: register('git-branch-create', 0xec6f),
637 > gitBranchDelete: register('git-branch-delete', 0xec6f),
638 > searchLarge: register('search-large', 0xec70),
639 > terminalGitBash: register('terminal-git-bash', 0xec71),
640 > windowActive: register('window-active', 0xec72),
641 > forward: register('forward', 0xec73),
642 > download: register('download', 0xec74),
643 > clockface: register('clockface', 0xec75),
644 > unarchive: register('unarchive', 0xec76),
645 > sessionInProgress: register('session-in-progress', 0xec77),
646 > collectionSmall: register('collection-small', 0xec78),
647 > vmSmall: register('vm-small', 0xec79),
648 > cloudSmall: register('cloud-small', 0xec7a),
649 > addSmall: register('add-small', 0xec7b),
650 > removeSmall: register('remove-small', 0xec7c),
651 > worktreeSmall: register('worktree-small', 0xec7d),
652 > worktree: register('worktree', 0xec7e),
653 > screenCut: register('screen-cut', 0xec7f),
654 > ask: register('ask', 0xec80),
655 > openai: register('openai', 0xec81),
656 > claude: register('claude', 0xec82),
657 > openInWindow: register('open-in-window', 0xec83),
658 > newSession: register('new-session', 0xec84),
659 > terminalSecure: register('terminal-secure', 0xec85),
660 > chatImport: register('chat-import', 0xec86),
661 > chatExport: register('chat-export', 0xec87),
662 > shareWindow: register('share-window', 0xec88),
663 > circleSlashCompact: register('circle-slash-compact', 0xec89),
664 > copilotCompact: register('copilot-compact', 0xec8a),
665 > folderOpenedCompact: register('folder-opened-compact', 0xec8b),
666 > folderCompact: register('folder-compact', 0xec8c),
667 > gearCompact: register('gear-compact', 0xec8d),
668 > gitBranchCompact: register('git-branch-compact', 0xec8e),
669 > libraryCompact: register('library-compact', 0xec8f),
670 > recordKeysCompact: register('record-keys-compact', 0xec90),
671 > remoteCompact: register('remote-compact', 0xec91),
672 > repoForkedCompact: register('repo-forked-compact', 0xec92),
673 > repoCompact: register('repo-compact', 0xec93),
674 > shieldCompact: register('shield-compact', 0xec94),
675 > sparkleCompact: register('sparkle-compact', 0xec95),
676 > symbolColorCompact: register('symbol-color-compact', 0xec96),
677 > windowCompact: register('window-compact', 0xec97),
678 > errorCompact: register('error-compact', 0xec98),
679 > warningCompact: register('warning-compact', 0xec99),
680 > passCompact: register('pass-compact', 0xec9a),
681 > important: register('important', 0xec9b),
682 > importantCompact: register('important-compact', 0xec9c),
683 > rocketCompact: register('rocket-compact', 0xec9d),
684 > unpin: register('unpin', 0xec9e),
685 > addCompact: register('add-compact', 0xec9f),
686 > attachCompact: register('attach-compact', 0xeca0),
687 > beakerCompact: register('beaker-compact', 0xeca1),
688 > checkCompact: register('check-compact', 0xeca2),
689 > checklistCompact: register('checklist-compact', 0xeca3),
690 > chevronDownCompact: register('chevron-down-compact', 0xeca4),
691 > chevronLeftCompact: register('chevron-left-compact', 0xeca5),
692 > chevronRightCompact: register('chevron-right-compact', 0xeca6),
693 > chevronUpCompact: register('chevron-up-compact', 0xeca7),
694 > circleFilledCompact: register('circle-filled-compact', 0xeca8),
695 > circleSmallFilledCompact: register('circle-small-filled-compact', 0xeca9),
696 > closeCompact: register('close-compact', 0xecaa),
697 > collapseAllCompact: register('collapse-all-compact', 0xecab),
698 > commentCompact: register('comment-compact', 0xecac),
699 > commentUnresolvedCompact: register('comment-unresolved-compact', 0xecad),
700 > debugConnectedCompact: register('debug-connected-compact', 0xecae),
701 > debugDisconnectCompact: register('debug-disconnect-compact', 0xecaf),
702 > editCompact: register('edit-compact', 0xecb0),
703 > fileMediaCompact: register('file-media-compact', 0xecb1),
704 > gitFetch: register('git-fetch', 0xecb2),
705 > lightbulbCompact: register('lightbulb-compact', 0xecb3),
706 > loadingCompact: register('loading-compact', 0xecb4),
707 > passFilledCompact: register('pass-filled-compact', 0xecb5),
708 > projectCompact: register('project-compact', 0xecb6),
709 > refreshCompact: register('refresh-compact', 0xecb7),
710 > searchCompact: register('search-compact', 0xecb8),
711 > sessionInProgressCompact: register('session-in-progress-compact', 0xecb9),
712 > syncCompact: register('sync-compact', 0xecba),
713 > terminalCompact: register('terminal-compact', 0xecbb),
714 > vmPending: register('vm-pending', 0xecbc),
715 > worktreeCompact: register('worktree-compact', 0xecbd),
716 > developerTools: register('developer-tools', 0xecbe),
717 > cloudCompact: register('cloud-compact', 0xecbf),
718 > agentCompact: register('agent-compact', 0xecc0),
719 > askCompact: register('ask-compact', 0xecc1),
720 > settingsCompact: register('settings-compact', 0xecc2),
721 > vmCompact: register('vm-compact', 0xecc3),
722 > runCompact: register('run-compact', 0xecc4),
723 > gitPullRequestComment: register('git-pull-request-comment', 0xecc5),
724 > gitPullRequestError: register('git-pull-request-error', 0xecc6),
725 > rightPanelHide: register('right-panel-hide', 0xecc7),
726 > rightPanelShow: register('right-panel-show', 0xecc8),
727 > vscodeInsidersOutline: register('vscode-insiders-outline', 0xecc9),
728 > vscodeOutline: register('vscode-outline', 0xecca),
729 > voiceMode: register('voice-mode', 0xeccb),
730 > voiceModeCompact: register('voice-mode-compact', 0xeccc),
731 > micDownload: register('mic-download', 0xeccd),
732 > micDownloadCompact: register('mic-download-compact', 0xecce),
733 > voiceModeDownload: register('voice-mode-download', 0xeccf),
734 > voiceModeDownloadCompact: register('voice-mode-download-compact', 0xecd0),
735 > googleGemini: register('google-gemini', 0xecd1),
736 > kimi: register('kimi', 0xecd2),
737 > microsoft: register('microsoft', 0xecd3),
738 > fish1Happy: register('fish1-happy', 0xecd4),
739 > fish1Neutral: register('fish1-neutral', 0xecd5),
740 > fish1Sad: register('fish1-sad', 0xecd6),
741 > fish1VerySad: register('fish1-very-sad', 0xecd7),
742 > fish2Happy: register('fish2-happy', 0xecd8),
743 > fish2Neutral: register('fish2-neutral', 0xecd9),
744 > fish2Sad: register('fish2-sad', 0xecda),
745 > fish2VerySad: register('fish2-very-sad', 0xecdb),
746 > fish3Happy: register('fish3-happy', 0xecdc),
747 > fish3Neutral: register('fish3-neutral', 0xecdd),
748 > fish3Sad: register('fish3-sad', 0xecde),
749 > fish3VerySad: register('fish3-very-sad', 0xecdf),
750 > fish4Happy: register('fish4-happy', 0xece0),
751 > fish4Neutral: register('fish4-neutral', 0xece1),
752 > fish4Sad: register('fish4-sad', 0xece2),
753 > fish4VerySad: register('fish4-very-sad', 0xece3),
754 > personVoice: register('person-voice', 0xece4),
755 > personVoiceCompact: register('person-voice-compact', 0xece5),
756 > personVoiceFilled: register('person-voice-filled', 0xece6),
757 > personVoiceFilledCompact: register('person-voice-filled-compact', 0xece7),
758 > } as const;
src/vs/workbench/contrib/debug/common/debugModel.ts 712 covered LOC · 196 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugModel.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 { DeferredPromise, RunOnceScheduler } from '../../../../base/common/async.js';
8 > import { VSBuffer, decodeBase64, encodeBase64 } from '../../../../base/common/buffer.js';
9 > import { CancellationTokenSource } from '../../../../base/common/cancellation.js';
10 > import { Emitter, Event, trackSetChanges } from '../../../../base/common/event.js';
11 > import { stringHash } from '../../../../base/common/hash.js';
12 > import { Disposable } from '../../../../base/common/lifecycle.js';
13 > import { mixin } from '../../../../base/common/objects.js';
14 > import { autorun } from '../../../../base/common/observable.js';
15 > import * as resources from '../../../../base/common/resources.js';
16 > import { isString, isUndefinedOrNull } from '../../../../base/common/types.js';
17 > import { URI, URI as uri } from '../../../../base/common/uri.js';
18 > import { generateUuid } from '../../../../base/common/uuid.js';
19 > import { IRange, Range } from '../../../../editor/common/core/range.js';
20 > import * as nls from '../../../../nls.js';
21 > import { ILogService } from '../../../../platform/log/common/log.js';
22 > import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
23 > import { IEditorPane } from '../../../common/editor.js';
24 > import { DEBUG_MEMORY_SCHEME, DataBreakpointSetType, DataBreakpointSource, DebugTreeItemCollapsibleState, IBaseBreakpoint, IBreakpoint, IBreakpointData, IBreakpointUpdateData, IBreakpointsChangeEvent, IDataBreakpoint, IDebugEvaluatePosition, IDebugModel, IDebugSession, IDebugVisualizationTreeItem, IEnablement, IExceptionBreakpoint, IExceptionInfo, IExpression, IExpressionContainer, IFunctionBreakpoint, IInstructionBreakpoint, IMemoryInvalidationEvent, IMemoryRegion, IRawModelUpdate, IRawStoppedDetails, IScope, IStackFrame, IThread, ITreeElement, MemoryRange, MemoryRangeType, State, isFrameDeemphasized } from './debug.js';
25 > import { Source, UNKNOWN_SOURCE_LABEL, getUriFromSource } from './debugSource.js';
26 > import { DebugStorage } from './debugStorage.js';
27 > import { IDebugVisualizerService } from './debugVisualizers.js';
28 > import { DisassemblyViewInput } from './disassemblyViewInput.js';
29 > import { IEditorService } from '../../../services/editor/common/editorService.js';
30 > import { ITextFileService } from '../../../services/textfile/common/textfiles.js';
31 >
32 > interface IDebugProtocolVariableWithContext extends DebugProtocol.Variable {
33 > __vscodeVariableMenuContext?: string;
34 > }
35 >
36 > export class ExpressionContainer implements IExpressionContainer {
37 >
38 > public static readonly allValues = new Map<string, string>();
39 > // Use chunks to support variable paging #9537
40 > private static readonly BASE_CHUNK_SIZE = 100;
41 >
42 > public type: string | undefined;
43 > public valueChanged = false;
44 > private _value: string = '';
45 > protected children?: Promise<IExpression[]>;
46 >
47 > constructor(
48 protected session: IDebugSession | undefined,
49 protected readonly threadId: number | undefined,
57 public valueLocationReference: number | undefined = undefined,
58 ) { }
60 > get reference(): number | undefined {
61 return this._reference;
62 }
64 > set reference(value: number | undefined) {
65 this._reference = value;
66 this.children = undefined; // invalidate children cache
67 }
69 > async evaluateLazy(): Promise<void> {
70 if (typeof this.reference === 'undefined') {
71 return;
88 this.adoptLazyResponse(dummyVar);
89 }
91 > protected adoptLazyResponse(response: DebugProtocol.Variable): void {
92 }
94 > getChildren(): Promise<IExpression[]> {
95 if (!this.children) {
96 this.children = this.doGetChildren();
99 return this.children;
100 }
102 > private async doGetChildren(): Promise<IExpression[]> {
103 if (!this.hasChildren) {
104 return [];
133 return children.concat(variables);
134 }
136 > getId(): string {
137 return this.id;
138 }
140 > getSession(): IDebugSession | undefined {
141 return this.session;
142 }
144 > get value(): string {
145 return this._value;
146 }
148 > get hasChildren(): boolean {
149 // only variables with reference > 0 have children.
150 return !!this.reference && this.reference > 0 && !this.presentationHint?.lazy;
151 }
153 > private async fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise<Variable[]> {
154 try {
155 const response = await this.session!.variables(this.reference || 0, this.threadId, filter, start, count);
178 }
179 }
181 > // The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked.
182 > private get getChildrenInChunks(): boolean {
183 return !!this.indexedVariables;
184 }
186 > set value(value: string) {
187 this._value = value;
188 this.valueChanged = !!ExpressionContainer.allValues.get(this.getId()) &&
190 ExpressionContainer.allValues.set(this.getId(), value);
191 }
193 > toString(): string {
194 return this.value;
195 }
197 > async evaluateExpression(
198 expression: string,
199 session: IDebugSession | undefined,
238 }
239 }
240 > } debugModel.ts
241 >
242 function handleSetResponse(expression: ExpressionContainer, response: DebugProtocol.SetVariableResponse | DebugProtocol.SetExpressionResponse | undefined): void {
243 if (response && response.body) {
251 }
252 }
254 > export class VisualizedExpression implements IExpression {
255 > public errorMessage?: string;
256 > private readonly id = generateUuid();
257 >
258 > evaluateLazy(): Promise<void> {
259 > return Promise.resolve();
260 > }
261 > getChildren(): Promise<IExpression[]> {
262 return this.visualizer.getVisualizedChildren(this.session, this.treeId, this.treeItem.id);
263 }
265 > getId(): string {
266 return this.id;
267 }
269 > get name() {
270 return this.treeItem.label;
271 }
273 > get value() {
274 return this.treeItem.description || '';
275 }
277 > get hasChildren() {
278 return this.treeItem.collapsibleState !== DebugTreeItemCollapsibleState.None;
279 }
281 > constructor(
282 private readonly session: IDebugSession | undefined,
283 private readonly visualizer: IDebugVisualizerService,
286 public readonly original?: Variable,
287 ) { }
289 > public getSession(): IDebugSession | undefined {
290 return this.session;
291 }
293 > /** Edits the value, sets the {@link errorMessage} and returns false if unsuccessful */
294 > public async edit(newValue: string) {
295 try {
296 await this.visualizer.editTreeItem(this.treeId, this.treeItem, newValue);
301 }
302 }
303 > } debugModel.ts
304 >
305 > export class Expression extends ExpressionContainer implements IExpression {
306 > static readonly DEFAULT_VALUE = nls.localize('notAvailable', "not available");
307 >
308 > public available: boolean;
309 >
310 > private readonly _onDidChangeValue = new Emitter<IExpression>();
311 > public readonly onDidChangeValue: Event<IExpression> = this._onDidChangeValue.event;
312 >
313 > constructor(public name: string, id = generateUuid()) {
314 super(undefined, undefined, 0, id);
315 this.available = false;
320 }
321 }
323 > async evaluate(session: IDebugSession | undefined, stackFrame: IStackFrame | undefined, context: string, keepLazyVars?: boolean, location?: IDebugEvaluatePosition): Promise<void> {
324 const hadDefaultValue = this.value === Expression.DEFAULT_VALUE;
325 this.available = await this.evaluateExpression(this.name, session, stackFrame, context, keepLazyVars, location);
328 }
329 }
331 > override toString(): string {
332 return `${this.name}\n${this.value}`;
333 }
335 > toJSON() {
336 return {
337 sessionId: this.getSession()?.getId(),
339 };
340 }
342 > toDebugProtocolObject(): DebugProtocol.Variable {
343 return {
344 name: this.name,
350 };
351 }
353 > async setExpression(value: string, stackFrame: IStackFrame): Promise<void> {
354 if (!this.session) {
355 return;
359 handleSetResponse(this, response);
360 }
361 > } debugModel.ts
362 >
363 > export class Variable extends ExpressionContainer implements IExpression {
364 >
365 > // Used to show the error message coming from the adapter when setting the value #7807
366 > public errorMessage: string | undefined;
367 >
368 > constructor(
369 session: IDebugSession | undefined,
370 threadId: number | undefined,
390 this.type = type;
391 }
393 > getThreadId() {
394 return this.threadId;
395 }
397 > async setVariable(value: string, stackFrame: IStackFrame): Promise<void> {
398 if (!this.session) {
399 return;
412 }
413 }
415 > async setExpression(value: string, stackFrame: IStackFrame): Promise<void> {
416 if (!this.session || !this.evaluateName) {
417 return;
421 handleSetResponse(this, response);
422 }
424 > override toString(): string {
425 return this.name ? `${this.name}: ${this.value}` : this.value;
426 }
428 > toJSON() {
429 return {
430 sessionId: this.getSession()?.getId(),
435 };
436 }
438 > protected override adoptLazyResponse(response: DebugProtocol.Variable): void {
439 this.evaluateName = response.evaluateName;
440 }
442 > toDebugProtocolObject(): DebugProtocol.Variable {
443 return {
444 name: this.name,
450 };
451 }
452 > } debugModel.ts
453 >
454 > export class Scope extends ExpressionContainer implements IScope {
455 >
456 > constructor(
457 public readonly stackFrame: IStackFrame,
458 id: number,
466 super(stackFrame.thread.session, stackFrame.thread.threadId, reference, `scope:${name}:${id}`, namedVariables, indexedVariables);
467 }
469 > get childrenHaveBeenLoaded(): boolean {
470 return !!this.children;
471 }
473 > override toString(): string {
474 return this.name;
475 }
477 > toDebugProtocolObject(): DebugProtocol.Scope {
478 return {
479 name: this.name,
482 };
483 }
484 > } debugModel.ts
485 >
486 > export class ErrorScope extends Scope {
487 >
488 > constructor(
489 stackFrame: IStackFrame,
490 index: number,
493 super(stackFrame, index, message, 0, false);
494 }
496 > override toString(): string {
497 return this.name;
498 }
499 > } debugModel.ts
500 >
501 > export class StackFrame implements IStackFrame {
502 >
503 > private scopes: Promise<Scope[]> | undefined;
504 >
505 > constructor(
506 public readonly thread: Thread,
507 public readonly frameId: number,
514 public readonly instructionPointerReference?: string
515 ) { }
517 > getId(): string {
518 return `stackframe:${this.thread.getId()}:${this.index}:${this.source.name}`;
519 }
521 > getScopes(): Promise<IScope[]> {
522 if (!this.scopes) {
523 this.scopes = this.thread.session.scopes(this.frameId, this.thread.threadId).then(response => {
545 return this.scopes;
546 }
548 > async getMostSpecificScopes(range: IRange): Promise<IScope[]> {
549 const scopes = await this.getScopes();
550 const nonExpensiveScopes = scopes.filter(s => !s.expensive);
558 return scopesContainingRange.length ? scopesContainingRange : nonExpensiveScopes;
559 }
561 > restart(): Promise<void> {
562 return this.thread.session.restartFrame(this.frameId, this.thread.threadId);
563 }
565 > forgetScopes(): void {
566 this.scopes = undefined;
567 }
569 > toString(): string {
570 const lineNumberToString = typeof this.range.startLineNumber === 'number' ? `:${this.range.startLineNumber}` : '';
571 const sourceToString = `${this.source.inMemory ? this.source.name : this.source.uri.fsPath}${lineNumberToString}`;
573 return sourceToString === UNKNOWN_SOURCE_LABEL ? this.name : `${this.name} (${sourceToString})`;
574 }
576 > async openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<IEditorPane | undefined> {
577 const threadStopReason = this.thread.stoppedDetails?.reason;
578 if (this.instructionPointerReference &&
588 return undefined;
589 }
591 > equals(other: IStackFrame): boolean {
592 return (this.name === other.name) && (other.thread === this.thread) && (this.frameId === other.frameId) && (other.source === this.source) && (Range.equalsRange(this.range, other.range));
593 }
594 > } debugModel.ts
595 >
596 > const KEEP_SUBTLE_FRAME_AT_TOP_REASONS: readonly string[] = ['breakpoint', 'step', 'function breakpoint'];
597 >
598 > export class Thread implements IThread {
599 > private callStack: IStackFrame[];
600 > private staleCallStack: IStackFrame[];
601 > private callStackCancellationTokens: CancellationTokenSource[] = [];
602 > public stoppedDetails: IRawStoppedDetails | undefined;
603 > public stopped: boolean;
604 > public reachedEndOfCallStack = false;
605 > public lastSteppingGranularity: DebugProtocol.SteppingGranularity | undefined;
606 >
607 > constructor(public readonly session: IDebugSession, public name: string, public readonly threadId: number) {
608 this.callStack = [];
609 this.staleCallStack = [];
610 this.stopped = false;
611 }
613 > getId(): string {
614 return `thread:${this.session.getId()}:${this.threadId}`;
615 }
617 > clearCallStack(): void {
618 if (this.callStack.length) {
619 this.staleCallStack = this.callStack;
623 this.callStackCancellationTokens = [];
624 }
626 > getCallStack(): IStackFrame[] {
627 return this.callStack;
628 }
630 > getStaleCallStack(): ReadonlyArray<IStackFrame> {
631 return this.staleCallStack;
632 }
634 > getTopStackFrame(): IStackFrame | undefined {
635 const callStack = this.getCallStack();
636 const stopReason = this.stoppedDetails?.reason;
641 return firstAvailableStackFrame;
642 }
644 > get stateLabel(): string {
645 if (this.stoppedDetails) {
646 return this.stoppedDetails.description ||
650 return nls.localize({ key: 'running', comment: ['indicates state'] }, "Running");
651 }
653 > /**
654 > * Queries the debug adapter for the callstack and returns a promise
655 > * which completes once the call stack has been retrieved.
656 > * If the thread is not stopped, it returns a promise to an empty array.
657 > * Only fetches the first stack frame for performance reasons. Calling this method consecutive times
658 > * gets the remainder of the call stack.
659 > */
660 > async fetchCallStack(levels = 20): Promise<void> {
661 if (this.stopped) {
662 const start = this.callStack.length;
673 }
674 }
676 > private async getCallStackImpl(startFrame: number, levels: number): Promise<IStackFrame[]> {
677 try {
678 const tokenSource = new CancellationTokenSource();
705 }
706 }
708 > /**
709 > * Returns exception info promise if the exception was thrown, otherwise undefined
710 > */
711 > get exceptionInfo(): Promise<IExceptionInfo | undefined> {
712 if (this.stoppedDetails && this.stoppedDetails.reason === 'exception') {
713 if (this.session.capabilities.supportsExceptionInfoRequest) {
721 return Promise.resolve(undefined);
722 }
724 > next(granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
725 return this.session.next(this.threadId, granularity);
726 }
728 > stepIn(granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
729 return this.session.stepIn(this.threadId, undefined, granularity);
730 }
732 > stepOut(granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
733 return this.session.stepOut(this.threadId, granularity);
734 }
736 > stepBack(granularity?: DebugProtocol.SteppingGranularity): Promise<void> {
737 return this.session.stepBack(this.threadId, granularity);
738 }
740 > continue(): Promise<void> {
741 return this.session.continue(this.threadId);
742 }
744 > pause(): Promise<void> {
745 return this.session.pause(this.threadId);
746 }
748 > terminate(): Promise<void> {
749 return this.session.terminateThreads([this.threadId]);
750 }
752 > reverseContinue(): Promise<void> {
753 return this.session.reverseContinue(this.threadId);
754 }
755 > } debugModel.ts
756 >
757 > /**
758 > * Gets a URI to a memory in the given session ID.
759 > */
760 > export const getUriForDebugMemory = (
761 sessionId: string,
762 memoryReference: string,
771 });
772 };
774 > export class MemoryRegion extends Disposable implements IMemoryRegion {
775 > private readonly invalidateEmitter = this._register(new Emitter<IMemoryInvalidationEvent>());
776 >
777 > /** @inheritdoc */
778 > public readonly onDidInvalidate = this.invalidateEmitter.event;
779 >
780 > /** @inheritdoc */
781 > public readonly writable: boolean;
782 >
783 > constructor(private readonly memoryReference: string, private readonly session: IDebugSession) {
784 super();
785 this.writable = !!this.session.capabilities.supportsWriteMemoryRequest;
790 }));
791 }
793 > public async read(fromOffset: number, toOffset: number): Promise<MemoryRange[]> {
794 const length = toOffset - fromOffset;
795 const offset = fromOffset;
826 ];
827 }
829 > public async write(offset: number, data: VSBuffer): Promise<number> {
830 const result = await this.session.writeMemory(this.memoryReference, offset, encodeBase64(data), true);
831 const written = result?.body?.bytesWritten ?? data.byteLength;
833 return written;
834 }
836 > public override dispose() {
837 super.dispose();
838 }
840 > private invalidate(fromOffset: number, toOffset: number) {
841 this.invalidateEmitter.fire({ fromOffset, toOffset });
842 }
843 > } debugModel.ts
844 >
845 > export class Enablement implements IEnablement {
846 > constructor(
847 > public enabled: boolean, debugModel.ts
848 > private readonly id: string
849 > ) { }
851 > getId(): string {
852 return this.id;
853 }
854 > } debugModel.ts
855 >
856 > interface IBreakpointSessionData extends DebugProtocol.Breakpoint {
857 > supportsConditionalBreakpoints: boolean;
858 > supportsHitConditionalBreakpoints: boolean;
859 > supportsLogPoints: boolean;
860 > supportsFunctionBreakpoints: boolean;
861 > supportsDataBreakpoints: boolean;
862 > supportsInstructionBreakpoints: boolean;
863 > sessionId: string;
864 > }
865 >
866 function toBreakpointSessionData(data: DebugProtocol.Breakpoint, capabilities: DebugProtocol.Capabilities): IBreakpointSessionData {
867 return mixin({
874 }, data);
875 }
877 > export interface IBaseBreakpointOptions {
878 > enabled?: boolean;
879 > hitCondition?: string;
880 > condition?: string;
881 > logMessage?: string;
882 > mode?: string;
883 > modeLabel?: string;
884 > }
885 >
886 > export abstract class BaseBreakpoint extends Enablement implements IBaseBreakpoint {
887 >
888 > private sessionData = new Map<string, IBreakpointSessionData>();
889 > protected data: IBreakpointSessionData | undefined;
890 > public hitCondition: string | undefined;
891 > public condition: string | undefined;
892 > public logMessage: string | undefined;
893 > public mode: string | undefined;
894 > public modeLabel: string | undefined;
895 >
896 > constructor(
897 > id: string, debugModel.ts
898 > opts: IBaseBreakpointOptions
899 > ) {
900 > super(opts.enabled ?? true, id);
901 > this.condition = opts.condition;
902 > this.hitCondition = opts.hitCondition;
903 > this.logMessage = opts.logMessage;
904 > this.mode = opts.mode;
905 > this.modeLabel = opts.modeLabel;
906 > }
908 > setSessionData(sessionId: string, data: IBreakpointSessionData | undefined): void {
909 if (!data) {
910 this.sessionData.delete(sessionId);
924 }
925 }
927 > get message(): string | undefined {
928 if (!this.data) {
929 return undefined;
932 return this.data.message;
933 }
935 > get verified(): boolean {
936 return this.data ? this.data.verified : true;
937 }
939 > get sessionsThatVerified() {
940 const sessionIds: string[] = [];
941 for (const [sessionId, data] of this.sessionData) {
947 return sessionIds;
948 }
950 > abstract get supported(): boolean;
951 >
952 > getIdFromAdapter(sessionId: string): number | undefined {
953 const data = this.sessionData.get(sessionId);
954 return data ? data.id : undefined;
955 }
957 > getDebugProtocolBreakpoint(sessionId: string): DebugProtocol.Breakpoint | undefined {
958 const data = this.sessionData.get(sessionId);
959 if (data) {
974 return undefined;
975 }
977 > toJSON(): IBaseBreakpointOptions & { id: string } {
978 return {
979 id: this.getId(),
986 };
987 }
988 > } debugModel.ts
989 >
990 > export interface IBreakpointOptions extends IBaseBreakpointOptions {
991 > uri: uri;
992 > lineNumber: number;
993 > column: number | undefined;
994 > adapterData: unknown;
995 > triggeredBy: string | undefined;
996 > }
997 >
998 > export class Breakpoint extends BaseBreakpoint implements IBreakpoint {
999 > private sessionsDidTrigger?: Set<string>;
1000 > private readonly _uri: uri;
1001 > private _adapterData: unknown;
1002 > private _lineNumber: number;
1003 > private _column: number | undefined;
1004 > public triggeredBy: string | undefined;
1005 >
1006 > constructor(
1007 opts: IBreakpointOptions,
1008 private readonly textFileService: ITextFileService,
1018 this.triggeredBy = opts.triggeredBy;
1019 }
1020 > debugModel.ts
1021 > toDAP(): DebugProtocol.SourceBreakpoint {
1022 return {
1023 line: this.sessionAgnosticData.lineNumber,
1029 };
1030 }
1031 > debugModel.ts
1032 > get originalUri() {
1033 return this._uri;
1034 }
1035 > debugModel.ts
1036 > get lineNumber(): number {
1037 return this.verified && this.data && typeof this.data.line === 'number' ? this.data.line : this._lineNumber;
1038 }
1039 > debugModel.ts
1040 > override get verified(): boolean {
1041 if (this.data) {
1042 return this.data.verified && !this.textFileService.isDirty(this._uri);
1045 return true;
1046 }
1047 > debugModel.ts
1048 > get pending(): boolean {
1049 if (this.data) {
1050 return false;
1052 return this.triggeredBy !== undefined;
1053 }
1054 > debugModel.ts
1055 > get uri(): uri {
1056 return this.verified && this.data && this.data.source ? getUriFromSource(this.data.source, this.data.source.path, this.data.sessionId, this.uriIdentityService, this.logService) : this._uri;
1057 }
1058 > debugModel.ts
1059 > get column(): number | undefined {
1060 return this.verified && this.data && typeof this.data.column === 'number' ? this.data.column : this._column;
1061 }
1062 > debugModel.ts
1063 > override get message(): string | undefined {
1064 if (this.textFileService.isDirty(this.uri)) {
1065 return nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session.");
1068 return super.message;
1069 }
1070 > debugModel.ts
1071 > get adapterData(): unknown {
1072 return this.data && this.data.source && this.data.source.adapterData ? this.data.source.adapterData : this._adapterData;
1073 }
1074 > debugModel.ts
1075 > get endLineNumber(): number | undefined {
1076 return this.verified && this.data ? this.data.endLine : undefined;
1077 }
1078 > debugModel.ts
1079 > get endColumn(): number | undefined {
1080 return this.verified && this.data ? this.data.endColumn : undefined;
1081 }
1082 > debugModel.ts
1083 > get sessionAgnosticData(): { lineNumber: number; column: number | undefined } {
1084 return {
1085 lineNumber: this._lineNumber,
1087 };
1088 }
1089 > debugModel.ts
1090 > get supported(): boolean {
1091 if (!this.data) {
1092 return true;
1104 return true;
1105 }
1106 > debugModel.ts
1107 > override setSessionData(sessionId: string, data: IBreakpointSessionData | undefined): void {
1108 super.setSessionData(sessionId, data);
1109 if (!this._adapterData) {
1111 }
1112 }
1113 > debugModel.ts
1114 > override toJSON(): IBreakpointOptions & { id: string } {
1115 return {
1116 ...super.toJSON(),
1122 };
1123 }
1124 > debugModel.ts
1125 > override toString(): string {
1126 return `${resources.basenameOrAuthority(this.uri)} ${this.lineNumber}`;
1127 }
1128 > debugModel.ts
1129 > public setSessionDidTrigger(sessionId: string, didTrigger = true): void {
1130 if (didTrigger) {
1131 this.sessionsDidTrigger ??= new Set();
1135 }
1136 }
1137 > debugModel.ts
1138 > public getSessionDidTrigger(sessionId: string): boolean {
1139 return !!this.sessionsDidTrigger?.has(sessionId);
1140 }
1141 > debugModel.ts
1142 > update(data: IBreakpointUpdateData): void {
1143 if (data.hasOwnProperty('lineNumber') && !isUndefinedOrNull(data.lineNumber)) {
1144 this._lineNumber = data.lineNumber;
1165 }
1166 }
1167 > } debugModel.ts
1168 >
1169 > export interface IFunctionBreakpointOptions extends IBaseBreakpointOptions {
1170 > name: string;
1171 > }
1172 >
1173 > export class FunctionBreakpoint extends BaseBreakpoint implements IFunctionBreakpoint {
1174 > public name: string;
1175 >
1176 > constructor(
1177 opts: IFunctionBreakpointOptions,
1178 id = generateUuid()
1181 this.name = opts.name;
1182 }
1183 > debugModel.ts
1184 > toDAP(): DebugProtocol.FunctionBreakpoint {
1185 return {
1186 name: this.name,
1189 };
1190 }
1191 > debugModel.ts
1192 > override toJSON(): IFunctionBreakpointOptions & { id: string } {
1193 return {
1194 ...super.toJSON(),
1196 };
1197 }
1198 > debugModel.ts
1199 > get supported(): boolean {
1200 if (!this.data) {
1201 return true;
1204 return this.data.supportsFunctionBreakpoints;
1205 }
1206 > debugModel.ts
1207 > override toString(): string {
1208 return this.name;
1209 }
1210 > } debugModel.ts
1211 >
1212 > export interface IDataBreakpointOptions extends IBaseBreakpointOptions {
1213 > description: string;
1214 > src: DataBreakpointSource;
1215 > canPersist: boolean;
1216 > initialSessionData?: { session: IDebugSession; dataId: string };
1217 > accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined;
1218 > accessType: DebugProtocol.DataBreakpointAccessType;
1219 > }
1220 >
1221 > export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint {
1222 > private readonly sessionDataIdForAddr = new WeakMap<IDebugSession, string | null>();
1223 >
1224 > public readonly description: string;
1225 > public readonly src: DataBreakpointSource;
1226 > public readonly canPersist: boolean;
1227 > public readonly accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined;
1228 > public readonly accessType: DebugProtocol.DataBreakpointAccessType;
1229 >
1230 > constructor(
1231 opts: IDataBreakpointOptions,
1232 id = generateUuid()
1245 }
1246 }
1247 > debugModel.ts
1248 > async toDAP(session: IDebugSession): Promise<DebugProtocol.DataBreakpoint | undefined> {
1249 let dataId: string;
1250 if (this.src.type === DataBreakpointSetType.Variable) {
1269 };
1270 }
1271 > debugModel.ts
1272 > override toJSON(): IDataBreakpointOptions & { id: string } {
1273 return {
1274 ...super.toJSON(),
1280 };
1281 }
1282 > debugModel.ts
1283 > get supported(): boolean {
1284 if (!this.data) {
1285 return true;
1288 return this.data.supportsDataBreakpoints;
1289 }
1290 > debugModel.ts
1291 > override toString(): string {
1292 return this.description;
1293 }
1294 > } debugModel.ts
1295 >
1296 > export interface IExceptionBreakpointOptions extends IBaseBreakpointOptions {
1297 > filter: string;
1298 > label: string;
1299 > supportsCondition: boolean;
1300 > description: string | undefined;
1301 > conditionDescription: string | undefined;
1302 > fallback?: boolean;
1303 > }
1304 >
1305 > export class ExceptionBreakpoint extends BaseBreakpoint implements IExceptionBreakpoint {
1306 >
1307 > private supportedSessions: Set<string> = new Set();
1308 >
1309 > public readonly filter: string;
1310 > public readonly label: string;
1311 > public readonly supportsCondition: boolean;
1312 > public readonly description: string | undefined;
1313 > public readonly conditionDescription: string | undefined;
1314 > private fallback: boolean = false;
1315 >
1316 > constructor(
1317 opts: IExceptionBreakpointOptions,
1318 id = generateUuid(),
1326 this.fallback = opts.fallback || false;
1327 }
1328 > debugModel.ts
1329 > override toJSON(): IExceptionBreakpointOptions & { id: string } {
1330 return {
1331 ...super.toJSON(),
1340 };
1341 }
1342 > debugModel.ts
1343 > setSupportedSession(sessionId: string, supported: boolean): void {
1344 if (supported) {
1345 this.supportedSessions.add(sessionId);
1349 }
1350 }
1351 > debugModel.ts
1352 > /**
1353 > * Used to specify which breakpoints to show when no session is specified.
1354 > * Useful when no session is active and we want to show the exception breakpoints from the last session.
1355 > */
1356 > setFallback(isFallback: boolean) {
1357 this.fallback = isFallback;
1358 }
1359 > debugModel.ts
1360 > get supported(): boolean {
1361 return true;
1362 }
1363 > debugModel.ts
1364 > /**
1365 > * Checks if the breakpoint is applicable for the specified session.
1366 > * If sessionId is undefined, returns true if this breakpoint is a fallback breakpoint.
1367 > */
1368 > isSupportedSession(sessionId?: string): boolean {
1369 return sessionId ? this.supportedSessions.has(sessionId) : this.fallback;
1370 }
1371 > debugModel.ts
1372 > matches(filter: DebugProtocol.ExceptionBreakpointsFilter) {
1373 return this.filter === filter.filter
1374 && this.label === filter.label
1377 && this.description === filter.description;
1378 }
1379 > debugModel.ts
1380 > override toString(): string {
1381 return this.label;
1382 }
1383 > } debugModel.ts
1384 >
1385 > export interface IInstructionBreakpointOptions extends IBaseBreakpointOptions {
1386 > instructionReference: string;
1387 > offset: number;
1388 > canPersist: boolean;
1389 > address: bigint;
1390 > }
1391 >
1392 > export class InstructionBreakpoint extends BaseBreakpoint implements IInstructionBreakpoint {
1393 > public readonly instructionReference: string;
1394 > public readonly offset: number;
1395 > public readonly canPersist: boolean;
1396 > public readonly address: bigint;
1397 >
1398 > constructor(
1399 > opts: IInstructionBreakpointOptions, debugModel.ts
1400 > id = generateUuid()
1401 > ) {
1402 > super(id, opts);
1403 > this.instructionReference = opts.instructionReference;
1404 > this.offset = opts.offset;
1405 > this.canPersist = opts.canPersist;
1406 > this.address = opts.address;
1407 > }
1408 > debugModel.ts
1409 > toDAP(): DebugProtocol.InstructionBreakpoint {
1410 return {
1411 instructionReference: this.instructionReference,
1416 };
1417 }
1418 > debugModel.ts
1419 > override toJSON(): IInstructionBreakpointOptions & { id: string } {
1420 return {
1421 ...super.toJSON(),
1426 };
1427 }
1428 > debugModel.ts
1429 > get supported(): boolean {
1430 if (!this.data) {
1431 return true;
1434 return this.data.supportsInstructionBreakpoints;
1435 }
1436 > debugModel.ts
1437 > override toString(): string {
1438 return this.instructionReference;
1439 }
1440 > } debugModel.ts
1441 >
1442 > export class ThreadAndSessionIds implements ITreeElement {
1443 > constructor(public sessionId: string, public threadId: number) { }
1444 >
1445 > getId(): string {
1446 return `${this.sessionId}:${this.threadId}`;
1447 }
1448 > } debugModel.ts
1449 >
1450 > interface IBreakpointModeInternal extends DebugProtocol.BreakpointMode {
1451 > firstFromDebugType: string;
1452 > }
1453 >
1454 > export class DebugModel extends Disposable implements IDebugModel {
1455 >
1456 > private sessions: IDebugSession[];
1457 > private schedulers = new Map<string, { scheduler: RunOnceScheduler; completeDeferred: DeferredPromise<void> }>();
1458 > private breakpointsActivated = true;
1459 > private readonly _onDidChangeBreakpoints = this._register(new Emitter<IBreakpointsChangeEvent | undefined>());
1460 > private readonly _onDidChangeCallStack = this._register(new Emitter<void>());
1461 > private _onDidChangeCallStackFire = this._register(new RunOnceScheduler(() => {
1462 this._onDidChangeCallStack.fire(undefined);
1463 > }, 100)); debugModel.ts
1464 > private readonly _onDidChangeWatchExpressions = this._register(new Emitter<IExpression | undefined>());
1465 > private readonly _onDidChangeWatchExpressionValue = this._register(new Emitter<IExpression | undefined>());
1466 > private readonly _breakpointModes = new Map<string, IBreakpointModeInternal>();
1467 > private breakpoints!: Breakpoint[];
1468 > private functionBreakpoints!: FunctionBreakpoint[];
1469 > private exceptionBreakpoints!: ExceptionBreakpoint[];
1470 > private dataBreakpoints!: DataBreakpoint[];
1471 > private watchExpressions!: Expression[];
1472 > private instructionBreakpoints: InstructionBreakpoint[];
1473 >
1474 > constructor(
1475 > debugStorage: DebugStorage, debugModel.ts
1476 > @ITextFileService private readonly textFileService: ITextFileService,
1477 > @IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
1478 > @ILogService private readonly logService: ILogService
1479 > ) {
1480 > super();
1481 >
1482 > this._register(autorun(reader => {
1483 > this.breakpoints = debugStorage.breakpoints.read(reader);
1484 > this.functionBreakpoints = debugStorage.functionBreakpoints.read(reader);
1485 > this.exceptionBreakpoints = debugStorage.exceptionBreakpoints.read(reader);
1486 > this.dataBreakpoints = debugStorage.dataBreakpoints.read(reader);
1487 > this._onDidChangeBreakpoints.fire(undefined);
1488 > }));
1489 >
1490 > this._register(autorun(reader => {
1491 > this.watchExpressions = debugStorage.watchExpressions.read(reader);
1492 > this._onDidChangeWatchExpressions.fire(undefined);
1493 > }));
1494 >
1495 > this._register(trackSetChanges(
1496 > () => new Set(this.watchExpressions),
1497 > this.onDidChangeWatchExpressions,
1498 > (we) => we.onDidChangeValue((e) => this._onDidChangeWatchExpressionValue.fire(e)))
1499 > );
1500 >
1501 > this.instructionBreakpoints = [];
1502 > this.sessions = [];
1503 > }
1504 > debugModel.ts
1505 > getId(): string {
1506 return 'root';
1507 }
1508 > debugModel.ts
1509 > getSession(sessionId: string | undefined, includeInactive = false): IDebugSession | undefined {
1510 if (sessionId) {
1511 return this.getSessions(includeInactive).find(s => s.getId() === sessionId);
1513 return undefined;
1514 }
1515 > debugModel.ts
1516 > getSessions(includeInactive = false): IDebugSession[] {
1517 // By default do not return inactive sessions.
1518 // However we are still holding onto inactive sessions due to repl and debug service session revival (eh scenario)
1519 return this.sessions.filter(s => includeInactive || s.state !== State.Inactive);
1520 }
1521 > debugModel.ts
1522 > private shouldDisposeSession(session: IDebugSession, newSession: IDebugSession): boolean {
1523 if (session.state !== State.Inactive) {
1524 return false;
1536 return rootSession.state === State.Inactive && rootSession.configuration.name === newSession.configuration.name;
1537 }
1538 > debugModel.ts
1539 > addSession(session: IDebugSession): void {
1540 this.sessions = this.sessions.filter(s => {
1541 if (s.getId() === session.getId()) {
1569 this._onDidChangeCallStack.fire(undefined);
1570 }
1571 > debugModel.ts
1572 > get onDidChangeBreakpoints(): Event<IBreakpointsChangeEvent | undefined> {
1573 return this._onDidChangeBreakpoints.event;
1574 }
1575 > debugModel.ts
1576 > get onDidChangeCallStack(): Event<void> {
1577 return this._onDidChangeCallStack.event;
1578 }
1579 > debugModel.ts
1580 > get onDidChangeWatchExpressions(): Event<IExpression | undefined> {
1581 > return this._onDidChangeWatchExpressions.event; debugModel.ts
1582 > }
1583 > debugModel.ts
1584 > get onDidChangeWatchExpressionValue(): Event<IExpression | undefined> {
1585 return this._onDidChangeWatchExpressionValue.event;
1586 }
1587 > debugModel.ts
1588 > rawUpdate(data: IRawModelUpdate): void {
1589 const session = this.sessions.find(p => p.getId() === data.sessionId);
1590 if (session) {
1593 }
1594 }
1595 > debugModel.ts
1596 > clearThreads(id: string, removeThreads: boolean, reference: number | undefined = undefined): void {
1597 const session = this.sessions.find(p => p.getId() === id);
1598 if (session) {
1620 }
1621 }
1622 > debugModel.ts
1623 > /**
1624 > * Update the call stack and notify the call stack view that changes have occurred.
1625 > */
1626 > async fetchCallstack(thread: IThread, levels?: number): Promise<void> {
1627
1628 if ((<Thread>thread).reachedEndOfCallStack) {
1644 return;
1645 }
1646 > debugModel.ts
1647 > refreshTopOfCallstack(thread: Thread, fetchFullStack = true): { topCallStack: Promise<void>; wholeCallStack: Promise<void> } {
1648 if (thread.session.capabilities.supportsDelayedStackTraceLoading) {
1649 // For improved performance load the first stack frame and then load the rest async.
1694 return { wholeCallStack, topCallStack: wholeCallStack };
1695 }
1696 > debugModel.ts
1697 > getBreakpoints(filter?: { uri?: uri; originalUri?: uri; lineNumber?: number; column?: number; enabledOnly?: boolean; triggeredOnly?: boolean }): IBreakpoint[] {
1698 if (filter) {
1699 const uriStr = filter.uri?.toString();
1725 return this.breakpoints;
1726 }
1727 > debugModel.ts
1728 > getFunctionBreakpoints(): IFunctionBreakpoint[] {
1729 return this.functionBreakpoints;
1730 }
1731 > debugModel.ts
1732 > getDataBreakpoints(): IDataBreakpoint[] {
1733 return this.dataBreakpoints;
1734 }
1735 > debugModel.ts
1736 > getExceptionBreakpoints(): IExceptionBreakpoint[] {
1737 return this.exceptionBreakpoints;
1738 }
1739 > debugModel.ts
1740 > getExceptionBreakpointsForSession(sessionId?: string): IExceptionBreakpoint[] {
1741 return this.exceptionBreakpoints.filter(ebp => ebp.isSupportedSession(sessionId));
1742 }
1743 > debugModel.ts
1744 > getInstructionBreakpoints(): IInstructionBreakpoint[] {
1745 > return this.instructionBreakpoints; debugModel.ts
1746 > }
1747 > debugModel.ts
1748 > setExceptionBreakpointsForSession(sessionId: string, filters: DebugProtocol.ExceptionBreakpointsFilter[]): void {
1749 if (!filters) {
1750 return;
1775 }
1776 }
1777 > debugModel.ts
1778 > removeExceptionBreakpointsForSession(sessionId: string): void {
1779 this.exceptionBreakpoints.forEach(ebp => ebp.setSupportedSession(sessionId, false));
1780 }
1781 > debugModel.ts
1782 > // Set last focused session as fallback session.
1783 > // This is done to keep track of the exception breakpoints to show when no session is active.
1784 > setExceptionBreakpointFallbackSession(sessionId: string): void {
1785 this.exceptionBreakpoints.forEach(ebp => ebp.setFallback(ebp.isSupportedSession(sessionId)));
1786 }
1787 > debugModel.ts
1788 > setExceptionBreakpointCondition(exceptionBreakpoint: IExceptionBreakpoint, condition: string | undefined): void {
1789 (exceptionBreakpoint as ExceptionBreakpoint).condition = condition;
1790 this._onDidChangeBreakpoints.fire(undefined);
1791 }
1792 > debugModel.ts
1793 > areBreakpointsActivated(): boolean {
1794 return this.breakpointsActivated;
1795 }
1796 > debugModel.ts
1797 > setBreakpointsActivated(activated: boolean): void {
1798 this.breakpointsActivated = activated;
1799 this._onDidChangeBreakpoints.fire(undefined);
1800 }
1801 > debugModel.ts
1802 > addBreakpoints(uri: uri, rawData: IBreakpointData[], fireEvent = true): IBreakpoint[] {
1803 const newBreakpoints = rawData.map(rawBp => {
1804 return new Breakpoint({
1826 return newBreakpoints;
1827 }
1828 > debugModel.ts
1829 > removeBreakpoints(toRemove: IBreakpoint[]): void {
1830 this.breakpoints = this.breakpoints.filter(bp => !toRemove.some(toRemove => toRemove.getId() === bp.getId()));
1831 this._onDidChangeBreakpoints.fire({ removed: toRemove, sessionOnly: false });
1832 }
1833 > debugModel.ts
1834 > updateBreakpoints(data: Map<string, IBreakpointUpdateData>): void {
1835 const updated: IBreakpoint[] = [];
1836 this.breakpoints.forEach(bp => {
1844 this._onDidChangeBreakpoints.fire({ changed: updated, sessionOnly: false });
1845 }
1846 > debugModel.ts
1847 > setBreakpointSessionData(sessionId: string, capabilites: DebugProtocol.Capabilities, data: Map<string, DebugProtocol.Breakpoint> | undefined): void {
1848 this.breakpoints.forEach(bp => {
1849 if (!data) {
1901 });
1902 }
1903 > debugModel.ts
1904 > getDebugProtocolBreakpoint(breakpointId: string, sessionId: string): DebugProtocol.Breakpoint | undefined {
1905 const bp = this.breakpoints.find(bp => bp.getId() === breakpointId);
1906 if (bp) {
1909 return undefined;
1910 }
1911 > debugModel.ts
1912 > getBreakpointModes(forBreakpointType: 'source' | 'exception' | 'data' | 'instruction'): DebugProtocol.BreakpointMode[] {
1913 return [...this._breakpointModes.values()].filter(mode => mode.appliesTo.includes(forBreakpointType));
1914 }
1915 > debugModel.ts
1916 > registerBreakpointModes(debugType: string, modes: DebugProtocol.BreakpointMode[]) {
1917 for (const mode of modes) {
1918 const key = `${mode.mode}/${mode.label}`;
1940 }
1941 }
1942 > debugModel.ts
1943 > private sortAndDeDup(): void {
1944 this.breakpoints = this.breakpoints.sort((first, second) => {
1945 if (first.uri.toString() !== second.uri.toString()) {
1957 this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`);
1958 }
1959 > debugModel.ts
1960 > setEnablement(element: IEnablement, enable: boolean): void {
1961 if (element instanceof Breakpoint || element instanceof FunctionBreakpoint || element instanceof ExceptionBreakpoint || element instanceof DataBreakpoint || element instanceof InstructionBreakpoint) {
1962 const changed: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint> = [];
1973 }
1974 }
1975 > debugModel.ts
1976 > enableOrDisableAllBreakpoints(enable: boolean): void {
1977 const changed: Array<IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IInstructionBreakpoint> = [];
1978
2008 this._onDidChangeBreakpoints.fire({ changed: changed, sessionOnly: false });
2009 }
2010 > debugModel.ts
2011 > addFunctionBreakpoint(opts: IFunctionBreakpointOptions, id?: string): IFunctionBreakpoint {
2012 const newFunctionBreakpoint = new FunctionBreakpoint(opts, id);
2013 this.functionBreakpoints.push(newFunctionBreakpoint);
2016 return newFunctionBreakpoint;
2017 }
2018 > debugModel.ts
2019 > updateFunctionBreakpoint(id: string, update: { name?: string; hitCondition?: string; condition?: string }): void {
2020 const functionBreakpoint = this.functionBreakpoints.find(fbp => fbp.getId() === id);
2021 if (functionBreakpoint) {
2032 }
2033 }
2034 > debugModel.ts
2035 > removeFunctionBreakpoints(id?: string): void {
2036 let removed: FunctionBreakpoint[];
2037 if (id) {
2044 this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false });
2045 }
2046 > debugModel.ts
2047 > addDataBreakpoint(opts: IDataBreakpointOptions, id?: string): void {
2048 const newDataBreakpoint = new DataBreakpoint(opts, id);
2049 this.dataBreakpoints.push(newDataBreakpoint);
2050 this._onDidChangeBreakpoints.fire({ added: [newDataBreakpoint], sessionOnly: false });
2051 }
2052 > debugModel.ts
2053 > updateDataBreakpoint(id: string, update: { hitCondition?: string; condition?: string }): void {
2054 const dataBreakpoint = this.dataBreakpoints.find(fbp => fbp.getId() === id);
2055 if (dataBreakpoint) {
2063 }
2064 }
2065 > debugModel.ts
2066 > removeDataBreakpoints(id?: string): void {
2067 let removed: DataBreakpoint[];
2068 if (id) {
2075 this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false });
2076 }
2077 > debugModel.ts
2078 > addInstructionBreakpoint(opts: IInstructionBreakpointOptions): void {
2079 > const newInstructionBreakpoint = new InstructionBreakpoint(opts); debugModel.ts
2080 > this.instructionBreakpoints.push(newInstructionBreakpoint);
2081 > this._onDidChangeBreakpoints.fire({ added: [newInstructionBreakpoint], sessionOnly: true });
2082 > }
2083 > debugModel.ts
2084 > removeInstructionBreakpoints(instructionReference?: string, offset?: number, address?: bigint): void {
2085 > let removed: InstructionBreakpoint[] = []; debugModel.ts
2086 > if (address !== undefined) {
2087 > // Prefer matching by resolved memory address: `instructionReference` is debugModel.ts
2088 > // allowed by the Debug Adapter Protocol to change between disassemble
2089 > // requests (e.g. after symbol reloads), so matching on reference+offset
2090 > // alone would fail to locate the breakpoint that the user is trying to
2091 > // toggle off. The `address` on an `InstructionBreakpoint` is the stable
2092 > // resolved memory address and uniquely identifies it.
2093 > for (let i = 0; i < this.instructionBreakpoints.length; i++) {
2094 > const ibp = this.instructionBreakpoints[i];
2095 > if (ibp.address === address) {
2096 > removed.push(ibp);
2097 > this.instructionBreakpoints.splice(i--, 1);
2098 > }
2099 > }
2100 > } else if (instructionReference) { debugModel.ts
2101 for (let i = 0; i < this.instructionBreakpoints.length; i++) {
2102 const ibp = this.instructionBreakpoints[i];
2110 this.instructionBreakpoints = [];
2111 }
2112 > this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false }); debugModel.ts
2113 > }
2114 > debugModel.ts
2115 > getWatchExpressions(): Expression[] {
2116 return this.watchExpressions;
2117 }
2118 > debugModel.ts
2119 > addWatchExpression(name?: string): IExpression {
2120 const we = new Expression(name || '');
2121 this.watchExpressions.push(we);
2124 return we;
2125 }
2126 > debugModel.ts
2127 > renameWatchExpression(id: string, newName: string): void {
2128 const filtered = this.watchExpressions.filter(we => we.getId() === id);
2129 if (filtered.length === 1) {
2132 }
2133 }
2134 > debugModel.ts
2135 > removeWatchExpressions(id: string | null = null): void {
2136 this.watchExpressions = id ? this.watchExpressions.filter(we => we.getId() !== id) : [];
2137 this._onDidChangeWatchExpressions.fire(undefined);
2138 }
2139 > debugModel.ts
2140 > moveWatchExpression(id: string, position: number): void {
2141 const we = this.watchExpressions.find(we => we.getId() === id);
2142 if (we) {
2146 }
2147 }
2148 > debugModel.ts
2149 > sourceIsNotAvailable(uri: uri): void {
2150 this.sessions.forEach(s => {
2151 const source = s.getSourceForUri(uri);
2156 this._onDidChangeCallStack.fire(undefined);
2157 }
2158 > } debugModel.ts
src/vs/platform/extensionManagement/common/extensionManagement.ts 660 covered LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensionManagement.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 } from '../../../base/common/cancellation.js';
7 > import { IStringDictionary } from '../../../base/common/collections.js';
8 > import { Event } from '../../../base/common/event.js';
9 > import { IMarkdownString } from '../../../base/common/htmlContent.js';
10 > import { IPager } from '../../../base/common/paging.js';
11 > import { Platform } from '../../../base/common/platform.js';
12 > import { PolicyCategory } from '../../../base/common/policy.js';
13 > import { URI } from '../../../base/common/uri.js';
14 > import { localize, localize2 } from '../../../nls.js';
15 > import { ConfigurationScope, Extensions, IConfigurationRegistry } from '../../configuration/common/configurationRegistry.js';
16 > import { ExtensionType, IExtension, IExtensionManifest, TargetPlatform } from '../../extensions/common/extensions.js';
17 > import { FileOperationError, FileOperationResult, IFileService, IFileStat } from '../../files/common/files.js';
18 > import { createDecorator } from '../../instantiation/common/instantiation.js';
19 > import { Registry } from '../../registry/common/platform.js';
20 > import { IExtensionGalleryManifest } from './extensionGalleryManifest.js';
21 >
22 > export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$';
23 > export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
24 > export const WEB_EXTENSION_TAG = '__web_extension';
25 > export const LANGUAGE_MODEL_CHAT_PROVIDER_EXTENSION_TAG = 'language-models';
26 > export const EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT = 'skipWalkthrough';
27 > export const EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT = 'skipPublisherTrust';
28 > export const EXTENSION_INSTALL_SOURCE_CONTEXT = 'extensionInstallSource';
29 > export const EXTENSION_INSTALL_DEP_PACK_CONTEXT = 'dependecyOrPackExtensionInstall';
30 > export const EXTENSION_INSTALL_CLIENT_TARGET_PLATFORM_CONTEXT = 'clientTargetPlatform';
31 >
32 > export const enum ExtensionInstallSource {
33 > COMMAND = 'command',
34 > SETTINGS_SYNC = 'settingsSync',
35 > }
36 >
37 > export interface IProductVersion {
38 > readonly version: string;
39 > readonly date?: string;
40 > }
41 >
42 > export function TargetPlatformToString(targetPlatform: TargetPlatform) {
43 switch (targetPlatform) {
44 case TargetPlatform.WIN32_X64: return 'Windows 64 bit';
62 }
63 }
65 > export function toTargetPlatform(targetPlatform: string): TargetPlatform {
66 switch (targetPlatform) {
67 case TargetPlatform.WIN32_X64: return TargetPlatform.WIN32_X64;
84 }
85 }
87 > export function getTargetPlatform(platform: Platform | 'alpine', arch: string | undefined): TargetPlatform {
88 switch (platform) {
89 case Platform.Windows:
129 }
130 }
132 > export function isNotWebExtensionInWebTargetPlatform(allTargetPlatforms: TargetPlatform[], productTargetPlatform: TargetPlatform): boolean {
133 // Not a web extension in web target platform
134 return productTargetPlatform === TargetPlatform.WEB && !allTargetPlatforms.includes(TargetPlatform.WEB);
135 }
137 > export function isTargetPlatformCompatible(extensionTargetPlatform: TargetPlatform, allTargetPlatforms: TargetPlatform[], productTargetPlatform: TargetPlatform): boolean {
138 // Not compatible when extension is not a web extension in web target platform
139 if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, productTargetPlatform)) {
163 return false;
164 }
166 > export interface IGalleryExtensionProperties {
167 > dependencies?: string[];
168 > extensionPack?: string[];
169 > engine?: string;
170 > enabledApiProposals?: string[];
171 > localizedLanguages?: string[];
172 > targetPlatform: TargetPlatform;
173 > isPreReleaseVersion: boolean;
174 > executesCode?: boolean;
175 > }
176 >
177 > export interface IGalleryExtensionAsset {
178 > uri: string;
179 > fallbackUri: string;
180 > }
181 >
182 > export interface IGalleryExtensionAssets {
183 > manifest: IGalleryExtensionAsset | null;
184 > readme: IGalleryExtensionAsset | null;
185 > changelog: IGalleryExtensionAsset | null;
186 > license: IGalleryExtensionAsset | null;
187 > repository: IGalleryExtensionAsset | null;
188 > download: IGalleryExtensionAsset;
189 > icon: IGalleryExtensionAsset | null;
190 > signature: IGalleryExtensionAsset | null;
191 > coreTranslations: [string, IGalleryExtensionAsset][];
192 > }
193 >
194 > export function isIExtensionIdentifier(obj: unknown): obj is IExtensionIdentifier {
195 const thing = obj as IExtensionIdentifier | undefined;
196 return !!thing
199 && (!thing.uuid || typeof thing.uuid === 'string');
200 }
202 > export interface IExtensionIdentifier {
203 > id: string;
204 > uuid?: string;
205 > }
206 >
207 > export interface IGalleryExtensionIdentifier extends IExtensionIdentifier {
208 > uuid: string;
209 > }
210 >
211 > export interface IGalleryExtensionVersion {
212 > version: string;
213 > date: string;
214 > isPreReleaseVersion: boolean;
215 > targetPlatforms: TargetPlatform[];
216 > }
217 >
218 > export interface IGalleryExtension {
219 > type: 'gallery';
220 > name: string;
221 > identifier: IGalleryExtensionIdentifier;
222 > version: string;
223 > displayName: string;
224 > publisherId: string;
225 > publisher: string;
226 > publisherDisplayName: string;
227 > publisherDomain?: { link: string; verified: boolean };
228 > publisherLink?: string;
229 > publisherSponsorLink?: string;
230 > description: string;
231 > installCount: number;
232 > rating: number;
233 > ratingCount: number;
234 > categories: readonly string[];
235 > tags: readonly string[];
236 > releaseDate: number;
237 > lastUpdated: number;
238 > preview: boolean;
239 > private: boolean;
240 > hasPreReleaseVersion: boolean;
241 > hasReleaseVersion: boolean;
242 > isSigned: boolean;
243 > allTargetPlatforms: TargetPlatform[];
244 > assets: IGalleryExtensionAssets;
245 > properties: IGalleryExtensionProperties;
246 > detailsLink?: string;
247 > ratingLink?: string;
248 > supportLink?: string;
249 > telemetryData?: IStringDictionary<unknown>;
250 > queryContext?: IStringDictionary<unknown>;
251 > }
252 >
253 > export type InstallSource = 'gallery' | 'vsix' | 'resource';
254 >
255 > export interface IGalleryMetadata {
256 > id: string;
257 > publisherId: string;
258 > private: boolean;
259 > publisherDisplayName: string;
260 > isPreReleaseVersion: boolean;
261 > targetPlatform?: TargetPlatform;
262 > }
263 >
264 > export type Metadata = Partial<IGalleryMetadata & {
265 > isApplicationScoped: boolean;
266 > isMachineScoped: boolean;
267 > isBuiltin: boolean;
268 > isSystem: boolean;
269 > updated: boolean;
270 > preRelease: boolean;
271 > hasPreReleaseVersion: boolean;
272 > installedTimestamp: number;
273 > pinned: boolean;
274 > source: InstallSource;
275 > size: number;
276 > }>;
277 >
278 > export interface ILocalExtension extends IExtension {
279 > isWorkspaceScoped: boolean;
280 > isMachineScoped: boolean;
281 > isApplicationScoped: boolean;
282 > publisherId: string | null;
283 > installedTimestamp?: number;
284 > isPreReleaseVersion: boolean;
285 > hasPreReleaseVersion: boolean;
286 > private: boolean;
287 > preRelease: boolean;
288 > updated: boolean;
289 > pinned: boolean;
290 > forceAutoUpdate: boolean;
291 > source: InstallSource;
292 > size: number;
293 > }
294 >
295 > export const enum SortBy {
296 > NoneOrRelevance = 'NoneOrRelevance',
297 > LastUpdatedDate = 'LastUpdatedDate',
298 > Title = 'Title',
299 > PublisherName = 'PublisherName',
300 > InstallCount = 'InstallCount',
301 > PublishedDate = 'PublishedDate',
302 > AverageRating = 'AverageRating',
303 > WeightedRating = 'WeightedRating'
304 > }
305 >
306 > export const enum SortOrder {
307 > Default = 0,
308 > Ascending = 1,
309 > Descending = 2
310 > }
311 >
312 > export const enum FilterType {
313 > Category = 'Category',
314 > ExtensionId = 'ExtensionId',
315 > ExtensionName = 'ExtensionName',
316 > ExcludeWithFlags = 'ExcludeWithFlags',
317 > Featured = 'Featured',
318 > SearchText = 'SearchText',
319 > Tag = 'Tag',
320 > Target = 'Target',
321 > }
322 >
323 > export interface IQueryOptions {
324 > text?: string;
325 > exclude?: string[];
326 > pageSize?: number;
327 > sortBy?: SortBy;
328 > sortOrder?: SortOrder;
329 > source?: string;
330 > includePreRelease?: boolean;
331 > productVersion?: IProductVersion;
332 > }
333 >
334 > export const enum StatisticType {
335 > Install = 'install',
336 > Uninstall = 'uninstall'
337 > }
338 >
339 > export interface IDeprecationInfo {
340 > readonly disallowInstall?: boolean;
341 > readonly extension?: {
342 > readonly id: string;
343 > readonly displayName: string;
344 > readonly autoMigrate?: {
345 > readonly storage: boolean;
346 > readonly donotDisable?: boolean;
347 > };
348 > readonly preRelease?: boolean;
349 > };
350 > readonly settings?: readonly string[];
351 > readonly additionalInfo?: string;
352 > }
353 >
354 > export interface ISearchPrefferedResults {
355 > readonly query?: string;
356 > readonly preferredResults?: string[];
357 > }
358 >
359 > export type MaliciousExtensionInfo = {
360 > readonly extensionOrPublisher: IExtensionIdentifier | string;
361 > readonly learnMoreLink?: string;
362 > };
363 >
364 > export interface IExtensionsControlManifest {
365 > readonly malicious: ReadonlyArray<MaliciousExtensionInfo>;
366 > readonly deprecated: IStringDictionary<IDeprecationInfo>;
367 > readonly search: ISearchPrefferedResults[];
368 > readonly autoUpdate?: IStringDictionary<string>;
369 > }
370 >
371 > export const enum InstallOperation {
372 > None = 1,
373 > Install,
374 > Update,
375 > Migrate,
376 > }
377 >
378 > export interface ITranslation {
379 > contents: { [key: string]: {} };
380 > }
381 >
382 > export interface IExtensionInfo extends IExtensionIdentifier {
383 > version?: string;
384 > preRelease?: boolean;
385 > hasPreRelease?: boolean;
386 > currentVersion?: string;
387 > }
388 >
389 > export interface IExtensionQueryOptions {
390 > targetPlatform?: TargetPlatform;
391 > productVersion?: IProductVersion;
392 > compatible?: boolean;
393 > queryAllVersions?: boolean;
394 > source?: string;
395 > }
396 >
397 > export interface IExtensionGalleryCapabilities {
398 > readonly query: {
399 > readonly sortBy: readonly SortBy[];
400 > readonly filters: readonly FilterType[];
401 > };
402 > readonly allRepositorySigned: boolean;
403 > }
404 >
405 > export const IExtensionGalleryService = createDecorator<IExtensionGalleryService>('extensionGalleryService');
406 >
407 > /**
408 > * Service to interact with the Visual Studio Code Marketplace to get extensions.
409 > * @throws Error if the Marketplace is not enabled or not reachable.
410 > */
411 > export interface IExtensionGalleryService {
412 > readonly _serviceBrand: undefined;
413 > isEnabled(): boolean;
414 > query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>;
415 > getExtensions(extensionInfos: ReadonlyArray<IExtensionInfo>, token: CancellationToken): Promise<IGalleryExtension[]>;
416 > getExtensions(extensionInfos: ReadonlyArray<IExtensionInfo>, options: IExtensionQueryOptions, token: CancellationToken): Promise<IGalleryExtension[]>;
417 > isExtensionCompatible(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion?: IProductVersion): Promise<boolean>;
418 > getCompatibleExtension(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion?: IProductVersion): Promise<IGalleryExtension | null>;
419 > getAllCompatibleVersions(extensionIdentifier: IExtensionIdentifier, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<IGalleryExtensionVersion[]>;
420 > getAllVersions(extensionIdentifier: IExtensionIdentifier): Promise<IGalleryExtensionVersion[]>;
421 > download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void>;
422 > downloadSignatureArchive(extension: IGalleryExtension, location: URI): Promise<void>;
423 > reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>;
424 > getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
425 > getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest | null>;
426 > getChangelog(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
427 > getCoreTranslation(extension: IGalleryExtension, languageId: string): Promise<ITranslation | null>;
428 > getExtensionsControlManifest(): Promise<IExtensionsControlManifest>;
429 > }
430 >
431 > export interface InstallExtensionEvent {
432 > readonly identifier: IExtensionIdentifier;
433 > readonly source: URI | IGalleryExtension;
434 > readonly profileLocation: URI;
435 > readonly applicationScoped?: boolean;
436 > readonly workspaceScoped?: boolean;
437 > }
438 >
439 > export interface InstallExtensionResult {
440 > readonly identifier: IExtensionIdentifier;
441 > readonly operation: InstallOperation;
442 > readonly source?: URI | IGalleryExtension;
443 > readonly local?: ILocalExtension;
444 > readonly error?: Error;
445 > readonly context?: IStringDictionary<unknown>;
446 > readonly profileLocation: URI;
447 > readonly applicationScoped?: boolean;
448 > readonly workspaceScoped?: boolean;
449 > }
450 >
451 > export interface UninstallExtensionEvent {
452 > readonly identifier: IExtensionIdentifier;
453 > readonly profileLocation: URI;
454 > readonly applicationScoped?: boolean;
455 > readonly workspaceScoped?: boolean;
456 > }
457 >
458 > export interface DidUninstallExtensionEvent {
459 > readonly identifier: IExtensionIdentifier;
460 > readonly error?: string;
461 > readonly profileLocation: URI;
462 > readonly applicationScoped?: boolean;
463 > readonly workspaceScoped?: boolean;
464 > }
465 >
466 > export interface DidUpdateExtensionMetadata {
467 > readonly profileLocation: URI;
468 > readonly local: ILocalExtension;
469 > }
470 >
471 > export const enum ExtensionGalleryErrorCode {
472 > Timeout = 'Timeout',
473 > Cancelled = 'Cancelled',
474 > ClientError = 'ClientError',
475 > ServerError = 'ServerError',
476 > Failed = 'Failed',
477 > DownloadFailedWriting = 'DownloadFailedWriting',
478 > Offline = 'Offline',
479 > }
480 >
481 > export class ExtensionGalleryError extends Error {
482 > constructor(message: string, readonly code: ExtensionGalleryErrorCode) {
483 super(message);
484 this.name = code;
485 }
487 >
488 > export const enum ExtensionManagementErrorCode {
489 > NotFound = 'NotFound',
490 > Unsupported = 'Unsupported',
491 > Deprecated = 'Deprecated',
492 > Malicious = 'Malicious',
493 > Incompatible = 'Incompatible',
494 > IncompatibleApi = 'IncompatibleApi',
495 > IncompatibleTargetPlatform = 'IncompatibleTargetPlatform',
496 > ReleaseVersionNotFound = 'ReleaseVersionNotFound',
497 > Invalid = 'Invalid',
498 > Download = 'Download',
499 > DownloadSignature = 'DownloadSignature',
500 > DownloadFailedWriting = ExtensionGalleryErrorCode.DownloadFailedWriting,
501 > UpdateMetadata = 'UpdateMetadata',
502 > Extract = 'Extract',
503 > Scanning = 'Scanning',
504 > ScanningExtension = 'ScanningExtension',
505 > ReadRemoved = 'ReadRemoved',
506 > UnsetRemoved = 'UnsetRemoved',
507 > Delete = 'Delete',
508 > Rename = 'Rename',
509 > IntializeDefaultProfile = 'IntializeDefaultProfile',
510 > AddToProfile = 'AddToProfile',
511 > InstalledExtensionNotFound = 'InstalledExtensionNotFound',
512 > PostInstall = 'PostInstall',
513 > CorruptZip = 'CorruptZip',
514 > IncompleteZip = 'IncompleteZip',
515 > PackageNotSigned = 'PackageNotSigned',
516 > SignatureVerificationInternal = 'SignatureVerificationInternal',
517 > SignatureVerificationFailed = 'SignatureVerificationFailed',
518 > NotAllowed = 'NotAllowed',
519 > Gallery = 'Gallery',
520 > Cancelled = 'Cancelled',
521 > Unknown = 'Unknown',
522 > Internal = 'Internal',
523 > }
524 >
525 > export enum ExtensionSignatureVerificationCode {
526 > 'NotSigned' = 'NotSigned',
527 > 'Success' = 'Success',
528 > 'RequiredArgumentMissing' = 'RequiredArgumentMissing', // A required argument is missing.
529 > 'InvalidArgument' = 'InvalidArgument', // An argument is invalid.
530 > 'PackageIsUnreadable' = 'PackageIsUnreadable', // The extension package is unreadable.
531 > 'UnhandledException' = 'UnhandledException', // An unhandled exception occurred.
532 > 'SignatureManifestIsMissing' = 'SignatureManifestIsMissing', // The extension is missing a signature manifest file (.signature.manifest).
533 > 'SignatureManifestIsUnreadable' = 'SignatureManifestIsUnreadable', // The signature manifest is unreadable.
534 > 'SignatureIsMissing' = 'SignatureIsMissing', // The extension is missing a signature file (.signature.p7s).
535 > 'SignatureIsUnreadable' = 'SignatureIsUnreadable', // The signature is unreadable.
536 > 'CertificateIsUnreadable' = 'CertificateIsUnreadable', // The certificate is unreadable.
537 > 'SignatureArchiveIsUnreadable' = 'SignatureArchiveIsUnreadable',
538 > 'FileAlreadyExists' = 'FileAlreadyExists', // The output file already exists.
539 > 'SignatureArchiveIsInvalidZip' = 'SignatureArchiveIsInvalidZip',
540 > 'SignatureArchiveHasSameSignatureFile' = 'SignatureArchiveHasSameSignatureFile', // The signature archive has the same signature file.
541 > 'PackageIntegrityCheckFailed' = 'PackageIntegrityCheckFailed', // The package integrity check failed.
542 > 'SignatureIsInvalid' = 'SignatureIsInvalid', // The extension has an invalid signature file (.signature.p7s).
543 > 'SignatureManifestIsInvalid' = 'SignatureManifestIsInvalid', // The extension has an invalid signature manifest file (.signature.manifest).
544 > 'SignatureIntegrityCheckFailed' = 'SignatureIntegrityCheckFailed', // The extension's signature integrity check failed. Extension integrity is suspect.
545 > 'EntryIsMissing' = 'EntryIsMissing', // An entry referenced in the signature manifest was not found in the extension.
546 > 'EntryIsTampered' = 'EntryIsTampered', // The integrity check for an entry referenced in the signature manifest failed.
547 > 'Untrusted' = 'Untrusted', // An X.509 certificate in the extension signature is untrusted.
548 > 'CertificateRevoked' = 'CertificateRevoked', // An X.509 certificate in the extension signature has been revoked.
549 > 'SignatureIsNotValid' = 'SignatureIsNotValid', // The extension signature is invalid.
550 > 'UnknownError' = 'UnknownError', // An unknown error occurred.
551 > 'PackageIsInvalidZip' = 'PackageIsInvalidZip', // The extension package is not valid ZIP format.
552 > 'SignatureArchiveHasTooManyEntries' = 'SignatureArchiveHasTooManyEntries', // The signature archive has too many entries.
553 > }
554 >
555 > export class ExtensionManagementError extends Error {
556 > constructor(message: string, readonly code: ExtensionManagementErrorCode) {
557 super(message);
558 this.name = code;
559 }
561 >
562 > export interface InstallExtensionSummary {
563 > failed: {
564 > id: string;
565 > installOptions: InstallOptions;
566 > }[];
567 > }
568 >
569 > export type InstallOptions = {
570 > isBuiltin?: boolean;
571 > isWorkspaceScoped?: boolean;
572 > isMachineScoped?: boolean;
573 > isApplicationScoped?: boolean;
574 > pinned?: boolean;
575 > donotIncludePackAndDependencies?: boolean;
576 > installGivenVersion?: boolean;
577 > preRelease?: boolean;
578 > installPreReleaseVersion?: boolean;
579 > donotVerifySignature?: boolean;
580 > operation?: InstallOperation;
581 > profileLocation?: URI;
582 > productVersion?: IProductVersion;
583 > keepExisting?: boolean;
584 > downloadExtensionsLocally?: boolean;
585 > /**
586 > * Context passed through to InstallExtensionResult
587 > */
588 > context?: IStringDictionary<unknown>;
589 > };
590 >
591 > export type UninstallOptions = {
592 > readonly profileLocation?: URI;
593 > readonly donotIncludePack?: boolean;
594 > readonly donotCheckDependents?: boolean;
595 > readonly versionOnly?: boolean;
596 > readonly remove?: boolean;
597 > };
598 >
599 > export interface IExtensionManagementParticipant {
600 > postInstall(local: ILocalExtension, source: URI | IGalleryExtension, options: InstallOptions, token: CancellationToken): Promise<void>;
601 > postUninstall(local: ILocalExtension, options: UninstallOptions, token: CancellationToken): Promise<void>;
602 > }
603 >
604 > export type InstallExtensionInfo = { readonly extension: IGalleryExtension; readonly options: InstallOptions };
605 > export type UninstallExtensionInfo = { readonly extension: ILocalExtension; readonly options?: UninstallOptions };
606 >
607 > export const IExtensionManagementService = createDecorator<IExtensionManagementService>('extensionManagementService');
608 > export interface IExtensionManagementService {
609 > readonly _serviceBrand: undefined;
610 >
611 > readonly preferPreReleases: boolean;
612 >
613 > onInstallExtension: Event<InstallExtensionEvent>;
614 > onDidInstallExtensions: Event<readonly InstallExtensionResult[]>;
615 > onUninstallExtension: Event<UninstallExtensionEvent>;
616 > onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
617 > onDidUpdateExtensionMetadata: Event<DidUpdateExtensionMetadata>;
618 >
619 > zip(extension: ILocalExtension): Promise<URI>;
620 > getManifest(vsix: URI): Promise<IExtensionManifest>;
621 > install(vsix: URI, options?: InstallOptions): Promise<ILocalExtension>;
622 > canInstall(extension: IGalleryExtension): Promise<true | IMarkdownString>;
623 > installFromGallery(extension: IGalleryExtension, options?: InstallOptions): Promise<ILocalExtension>;
624 > installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]>;
625 > installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension>;
626 > installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]>;
627 > uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void>;
628 > uninstallExtensions(extensions: UninstallExtensionInfo[]): Promise<void>;
629 > toggleApplicationScope(extension: ILocalExtension, fromProfileLocation: URI): Promise<ILocalExtension>;
630 > getInstalled(type?: ExtensionType, profileLocation?: URI, productVersion?: IProductVersion, language?: string): Promise<ILocalExtension[]>;
631 > getExtensionsControlManifest(): Promise<IExtensionsControlManifest>;
632 > copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void>;
633 > updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI): Promise<ILocalExtension>;
634 > resetPinnedStateForAllUserExtensions(pinned: boolean): Promise<void>;
635 >
636 > download(extension: IGalleryExtension, operation: InstallOperation, donotVerifySignature: boolean): Promise<URI>;
637 >
638 > registerParticipant(pariticipant: IExtensionManagementParticipant): void;
639 > getTargetPlatform(): Promise<TargetPlatform>;
640 >
641 > cleanUp(): Promise<void>;
642 > }
643 >
644 > export const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled';
645 > export const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled';
646 > export const IGlobalExtensionEnablementService = createDecorator<IGlobalExtensionEnablementService>('IGlobalExtensionEnablementService');
647 >
648 > export interface IGlobalExtensionEnablementService {
649 > readonly _serviceBrand: undefined;
650 > readonly onDidChangeEnablement: Event<{ readonly extensions: IExtensionIdentifier[]; readonly source?: string }>;
651 >
652 > getDisabledExtensions(): IExtensionIdentifier[];
653 > enableExtension(extension: IExtensionIdentifier, source?: string): Promise<boolean>;
654 > disableExtension(extension: IExtensionIdentifier, source?: string): Promise<boolean>;
655 >
656 > }
657 >
658 > export type IConfigBasedExtensionTip = {
659 > readonly extensionId: string;
660 > readonly extensionName: string;
661 > readonly isExtensionPack: boolean;
662 > readonly configName: string;
663 > readonly important: boolean;
664 > readonly whenNotInstalled?: string[];
665 > };
666 >
667 > export type IExecutableBasedExtensionTip = {
668 > readonly extensionId: string;
669 > readonly extensionName: string;
670 > readonly isExtensionPack: boolean;
671 > readonly exeName: string;
672 > readonly exeFriendlyName: string;
673 > readonly windowsPath?: string;
674 > readonly whenNotInstalled?: string[];
675 > };
676 >
677 > export const IExtensionTipsService = createDecorator<IExtensionTipsService>('IExtensionTipsService');
678 > export interface IExtensionTipsService {
679 > readonly _serviceBrand: undefined;
680 >
681 > getConfigBasedTips(folder: URI): Promise<IConfigBasedExtensionTip[]>;
682 > getImportantExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]>;
683 > getOtherExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]>;
684 > }
685 >
686 > export type AllowedExtensionsConfigValueType = IStringDictionary<boolean | string | string[]>;
687 >
688 > export const IAllowedExtensionsService = createDecorator<IAllowedExtensionsService>('IAllowedExtensionsService');
689 > export interface IAllowedExtensionsService {
690 > readonly _serviceBrand: undefined;
691 >
692 > readonly allowedExtensionsConfigValue: AllowedExtensionsConfigValueType | undefined;
693 > readonly onDidChangeAllowedExtensionsConfigValue: Event<void>;
694 >
695 > isAllowed(extension: IGalleryExtension | IExtension): true | IMarkdownString;
696 > isAllowed(extension: { id: string; publisherDisplayName: string | undefined; version?: string; prerelease?: boolean; targetPlatform?: TargetPlatform }): true | IMarkdownString;
697 > }
698 >
699 export async function computeSize(location: URI, fileService: IFileService): Promise<number> {
700 let stat: IFileStat;
713 return stat.size ?? 0;
714 }
716 > export const ExtensionsLocalizedLabel = localize2('extensions', "Extensions");
717 > export const PreferencesLocalizedLabel = localize2('preferences', 'Preferences');
718 > export const AllowedExtensionsConfigKey = 'extensions.allowed';
719 > export const VerifyExtensionSignatureConfigKey = 'extensions.verifySignature';
720 > export const ExtensionRequestsTimeoutConfigKey = 'extensions.requestTimeout';
721 >
722 > Registry.as<IConfigurationRegistry>(Extensions.Configuration)
723 > .registerConfiguration({
724 > id: 'extensions',
725 > order: 30,
726 > title: localize('extensionsConfigurationTitle', "Extensions"),
727 > type: 'object',
728 > properties: {
729 > [AllowedExtensionsConfigKey]: {
730 > // Note: Type is set only to object because to support policies generation during build time, where single type is expected.
731 > type: 'object',
732 > markdownDescription: localize('extensions.allowed', "Specify a list of extensions that are allowed to use. This helps maintain a secure and consistent development environment by restricting the use of unauthorized extensions. For more information on how to configure this setting, please visit the [Configure Allowed Extensions](https://aka.ms/vscode/enterprise/extensions/allowed) section."),
733 > default: '*',
734 > defaultSnippets: [{
735 > body: {},
736 > description: localize('extensions.allowed.none', "No extensions are allowed."),
737 > }, {
738 > body: {
739 > '*': true
740 > },
741 > description: localize('extensions.allowed.all', "All extensions are allowed."),
742 > }],
743 > scope: ConfigurationScope.APPLICATION,
744 > policy: {
745 > name: 'AllowedExtensions',
746 > category: PolicyCategory.Extensions,
747 > minimumVersion: '1.96',
748 > localization: {
749 > description: {
750 > key: 'extensions.allowed.policy',
751 > value: localize('extensions.allowed.policy', "Specify a list of extensions that are allowed to use. This helps maintain a secure and consistent development environment by restricting the use of unauthorized extensions. More information: https://aka.ms/vscode/enterprise/extensions/allowed"),
752 > }
753 > }
754 > },
755 > additionalProperties: false,
756 > patternProperties: {
757 > '([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$': {
758 > anyOf: [
759 > {
760 > type: ['boolean', 'string'],
761 > enum: [true, false, 'stable'],
762 > description: localize('extensions.allow.description', "Allow or disallow the extension."),
763 > enumDescriptions: [
764 > localize('extensions.allowed.enable.desc', "Extension is allowed."),
765 > localize('extensions.allowed.disable.desc', "Extension is not allowed."),
766 > localize('extensions.allowed.disable.stable.desc', "Allow only stable versions of the extension."),
767 > ],
768 > },
769 > {
770 > type: 'array',
771 > items: {
772 > type: 'string',
773 > },
774 > description: localize('extensions.allow.version.description', "Allow or disallow specific versions of the extension. To specifcy a platform specific version, use the format `[email protected]`, e.g. `[email protected]`. Supported platforms are `win32-x64`, `win32-arm64`, `linux-x64`, `linux-arm64`, `linux-armhf`, `alpine-x64`, `alpine-arm64`, `darwin-x64`, `darwin-arm64`"),
775 > },
776 > ]
777 > },
778 > '([a-z0-9A-Z][a-z0-9-A-Z]*)$': {
779 > type: ['boolean', 'string'],
780 > enum: [true, false, 'stable'],
781 > description: localize('extension.publisher.allow.description', "Allow or disallow all extensions from the publisher."),
782 > enumDescriptions: [
783 > localize('extensions.publisher.allowed.enable.desc', "All extensions from the publisher are allowed."),
784 > localize('extensions.publisher.allowed.disable.desc', "All extensions from the publisher are not allowed."),
785 > localize('extensions.publisher.allowed.disable.stable.desc', "Allow only stable versions of the extensions from the publisher."),
786 > ],
787 > },
788 > '\\*': {
789 > type: 'boolean',
790 > enum: [true, false],
791 > description: localize('extensions.allow.all.description', "Allow or disallow all extensions."),
792 > enumDescriptions: [
793 > localize('extensions.allow.all.enable', "Allow all extensions."),
794 > localize('extensions.allow.all.disable', "Disallow all extensions.")
795 > ],
796 > }
797 > }
798 > }
799 > }
800 > });
801 >
802 > export function shouldRequireRepositorySignatureFor(isPrivate: boolean, galleryManifest: IExtensionGalleryManifest | null): boolean {
803 if (isPrivate) {
804 return galleryManifest?.capabilities.signing?.allPrivateRepositorySigned === true;
src/vs/workbench/services/chat/common/chatEntitlementService.ts 651 covered LOC · 52 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatEntitlementService.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 product from '../../../../platform/product/common/product.js';
7 > import { Barrier } from '../../../../base/common/async.js';
8 > import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
9 > import { Emitter, Event } from '../../../../base/common/event.js';
10 > import { Lazy } from '../../../../base/common/lazy.js';
11 > import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
12 > import { IRequestContext } from '../../../../base/parts/request/common/request.js';
13 > import { localize } from '../../../../nls.js';
14 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
15 > import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
16 > import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
17 > import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
18 > import { ILogService, LogLevel } from '../../../../platform/log/common/log.js';
19 > import { IProductService } from '../../../../platform/product/common/productService.js';
20 > import { asText, IRequestService } from '../../../../platform/request/common/request.js';
21 > import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
22 > import { ITelemetryService, TelemetryLevel } from '../../../../platform/telemetry/common/telemetry.js';
23 > import { AuthenticationSession, IAuthenticationService } from '../../authentication/common/authentication.js';
24 > import { IOpenerService } from '../../../../platform/opener/common/opener.js';
25 > import { URI } from '../../../../base/common/uri.js';
26 > import Severity from '../../../../base/common/severity.js';
27 > import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
28 > import { isWeb } from '../../../../base/common/platform.js';
29 > import { ILifecycleService } from '../../lifecycle/common/lifecycle.js';
30 > import { Mutable } from '../../../../base/common/types.js';
31 > import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
32 > import { IObservable, observableFromEvent } from '../../../../base/common/observable.js';
33 > import { IDefaultAccountService } from '../../../../platform/defaultAccount/common/defaultAccount.js';
34 > import { IDefaultAccount, IEntitlementsData } from '../../../../base/common/defaultAccount.js';
35 >
36 > export namespace ChatEntitlementContextKeys {
37 >
38 > export const Setup = {
39 > hidden: new RawContextKey<boolean>('chatSetupHidden', false, true), // True when chat setup is explicitly hidden.
40 > installed: new RawContextKey<boolean>('chatSetupInstalled', false, true), // True when the chat extension is installed and enabled.
41 > disabled: new RawContextKey<boolean>('chatSetupDisabled', false, true), // True when the chat extension is disabled due to any other reason than workspace trust.
42 > disabledInWorkspace: new RawContextKey<boolean>('chatSetupDisabledInWorkspace', false, true), // True when chat is disabled at the workspace level via settings.
43 > untrusted: new RawContextKey<boolean>('chatSetupUntrusted', false, true), // True when the chat extension is disabled due to workspace trust.
44 > later: new RawContextKey<boolean>('chatSetupLater', false, true), // True when the user wants to finish setup later.
45 > registered: new RawContextKey<boolean>('chatSetupRegistered', false, true), // True when the user has registered as Free or Pro user.
46 > completed: new RawContextKey<boolean>('chatSetupCompleted', false, true) // True when the user has completed the setup flow, regardless of the outcome.
47 > };
48 >
49 > export const Entitlement = {
50 > signedOut: new RawContextKey<boolean>('chatEntitlementSignedOut', false, true), // True when user is signed out.
51 > canSignUp: new RawContextKey<boolean>('chatPlanCanSignUp', false, true), // True when user can sign up to be a chat free user.
52 >
53 > planFree: new RawContextKey<boolean>('chatPlanFree', false, true), // True when user is a chat free user.
54 > planPro: new RawContextKey<boolean>('chatPlanPro', false, true), // True when user is a chat pro user.
55 > planEdu: new RawContextKey<boolean>('chatPlanEdu', false, true), // True when user is a chat edu user.
56 > planProPlus: new RawContextKey<boolean>('chatPlanProPlus', false, true), // True when user is a chat pro plus user.
57 > planMax: new RawContextKey<boolean>('chatPlanMax', false, true), // True when user is a chat max user.
58 > planBusiness: new RawContextKey<boolean>('chatPlanBusiness', false, true), // True when user is a chat business user.
59 > planEnterprise: new RawContextKey<boolean>('chatPlanEnterprise', false, true), // True when user is a chat enterprise user.
60 >
61 > organisations: new RawContextKey<string[]>('chatEntitlementOrganisations', undefined, true), // The organizations the user belongs to.
62 > internal: new RawContextKey<boolean>('chatEntitlementInternal', false, true), // True when user belongs to internal organisation.
63 > sku: new RawContextKey<string>('chatEntitlementSku', undefined, true), // The SKU of the user.
64 > };
65 >
66 > export const chatQuotaExceeded = new RawContextKey<boolean>('chatQuotaExceeded', false, true);
67 > export const completionsQuotaExceeded = new RawContextKey<boolean>('completionsQuotaExceeded', false, true);
68 >
69 > export const chatAnonymous = new RawContextKey<boolean>('chatAnonymous', false, true);
70 >
71 > export const clientByokEnabled = new RawContextKey<boolean>('github.copilot.clientByokEnabled', true, true);
72 >
73 > export const hasByokModels = new RawContextKey<boolean>('github.copilot.hasByokModels', false, true);
74 > }
75 >
76 > export const IChatEntitlementService = createDecorator<IChatEntitlementService>('chatEntitlementService');
77 >
78 > export enum ChatEntitlement {
79 > /** Signed out */
80 > Unknown = 1,
81 > /** Signed in but not yet resolved */
82 > Unresolved = 2,
83 > /** Signed in and entitled to Free */
84 > Available = 3,
85 > /** Signed in but not entitled to Free */
86 > Unavailable = 4,
87 > /** Signed-up to Free */
88 > Free = 5,
89 > /** Signed-up to EDU */
90 > EDU = 10,
91 > /** Signed-up to Pro */
92 > Pro = 6,
93 > /** Signed-up to Pro Plus */
94 > ProPlus = 7,
95 > /** Signed-up to Business */
96 > Business = 8,
97 > /** Signed-up to Enterprise */
98 > Enterprise = 9,
99 > /** Signed-up to Max */
100 > Max = 11,
101 > }
102 >
103 > export interface IChatSentiment {
104 >
105 > /**
106 > * Whether the user has completed the setup flow or not, regardless of the outcome
107 > */
108 > completed?: boolean;
109 >
110 > /**
111 > * User has Chat installed.
112 > */
113 > installed?: boolean;
114 >
115 > /**
116 > * User signals no intent in using Chat.
117 > *
118 > * Note: in contrast to `disabled`, this should not only disable
119 > * Chat but also hide all of its UI.
120 > */
121 > hidden?: boolean;
122 >
123 > /**
124 > * User signals intent to disable Chat.
125 > *
126 > * Note: in contrast to `hidden`, this should not hide
127 > * Chat but but disable its functionality.
128 > */
129 > disabled?: boolean;
130 >
131 > /**
132 > * Chat is disabled at the workspace level
133 > *
134 > * Note: in contrast to `hidden` (which hides all UI globally),
135 > * this only disables Chat in the current workspace while
136 > * keeping its UI visible so the user can re-enable it.
137 > */
138 > disabledInWorkspace?: boolean;
139 >
140 > /**
141 > * Chat is disabled due to missing workspace trust.
142 > *
143 > * Note: even though this disables Chat, we want to treat it
144 > * different from the `disabled` state that is by explicit
145 > * user choice.
146 > */
147 > untrusted?: boolean;
148 >
149 > /**
150 > * User signals intent to use Chat later.
151 > */
152 > later?: boolean;
153 >
154 > /**
155 > * User has registered as Free or Pro user.
156 > */
157 > registered?: boolean;
158 > }
159 >
160 > /**
161 > * The inputs needed to decide whether Chat still requires the user to run setup
162 > * (sign in / sign up / trust / enable) before it can service a request.
163 > */
164 > export interface IChatSetupRequirement {
165 > /** Whether the setup flow has been completed (any outcome). */
166 > readonly completed: boolean;
167 > /** Whether the chat extension is disabled for a reason other than trust. */
168 > readonly disabled: boolean;
169 > /** Whether the chat extension is disabled because the workspace is untrusted. */
170 > readonly untrusted: boolean;
171 > /** The user's last known or resolved entitlement. */
172 > readonly entitlement: ChatEntitlement;
173 > /** Whether anonymous (signed-out) Chat access is enabled. */
174 > readonly anonymous: boolean;
175 > /** Whether BYOK models are available. */
176 > readonly hasByokModels: boolean;
177 > }
178 >
179 > /**
180 > * Single source of truth for whether Chat still requires setup before it can
181 > * service a request. Shared by the setup agent (which routes a sent message
182 > * through setup) and the model picker (which surfaces a "Sign in to use Copilot"
183 > * state instead of a misleading lone "Auto"). BYOK models and anonymous access
184 > * intentionally satisfy the entitlement-based checks so those flows keep working.
185 > */
186 > export function chatRequiresSetup(context: IChatSetupRequirement): boolean {
187 return (
188 (!context.completed && !context.hasByokModels) || // Setup not completed (unless BYOK models are available)
197 );
198 }
200 > export interface IChatEntitlementService {
201 >
202 > _serviceBrand: undefined;
203 >
204 > readonly onDidChangeEntitlement: Event<void>;
205 >
206 > readonly entitlement: ChatEntitlement;
207 > readonly entitlementObs: IObservable<ChatEntitlement>;
208 >
209 > readonly clientByokEnabled: boolean;
210 > readonly hasByokModels: boolean;
211 >
212 > readonly organisations: string[] | undefined;
213 > readonly isInternal: boolean;
214 > readonly sku: string | undefined;
215 > readonly copilotTrackingId: string | undefined;
216 >
217 > readonly onDidChangeQuotaExceeded: Event<void>;
218 > readonly onDidChangeQuotaRemaining: Event<void>;
219 > readonly onDidChangeUsageBasedBilling: Event<void>;
220 >
221 > readonly quotas: IQuotas;
222 >
223 > readonly onDidChangeSentiment: Event<void>;
224 >
225 > readonly sentiment: IChatSentiment;
226 > readonly sentimentObs: IObservable<IChatSentiment>;
227 >
228 > // TODO@bpasero eventually this will become enabled by default
229 > // and in that case we only need to check on entitlements change
230 > // between `unknown` and any other entitlement.
231 > readonly onDidChangeAnonymous: Event<void>;
232 > readonly anonymous: boolean;
233 > readonly anonymousObs: IObservable<boolean>;
234 >
235 > acceptQuotas(quotas: IQuotas): void;
236 >
237 > /**
238 > * Clear all quota state.
239 > */
240 > clearQuotas(): void;
241 >
242 > markAnonymousRateLimited(): void;
243 >
244 > /**
245 > * Mark the chat setup flow as completed.
246 > */
247 > markSetupCompleted(): void;
248 >
249 > /**
250 > * Force the hidden state on or off, overriding the normal entitlement logic.
251 > * Used by the account policy gate to hide all AI features when the gate is
252 > * active and unsatisfied.
253 > */
254 > setForceHidden(hidden: boolean): void;
255 >
256 > update(token: CancellationToken): Promise<void>;
257 > }
258 >
259 > //#region Helper Functions
260 >
261 > /**
262 > * Checks the chat entitlements to see if the user falls into the paid category
263 > * @param chatEntitlement The chat entitlement to check
264 > * @returns Whether or not they are a paid user
265 > */
266 > export function isProUser(chatEntitlement: ChatEntitlement): boolean {
267 return chatEntitlement === ChatEntitlement.EDU ||
268 chatEntitlement === ChatEntitlement.Pro ||
272 chatEntitlement === ChatEntitlement.Enterprise;
273 }
275 > /**
276 > * Gets the full plan name for the given chat entitlement
277 > * @param chatEntitlement The chat entitlement to get the plan name for
278 > * @returns The localized full plan name (e.g., "Copilot Pro", "Copilot Free")
279 > */
280 > export function getChatPlanName(chatEntitlement: ChatEntitlement): string {
281 switch (chatEntitlement) {
282 case ChatEntitlement.EDU:
296 }
297 }
299 > //#region Service Implementation
300 >
301 > const defaultChatAgent = {
302 > upgradePlanUrl: product.defaultChatAgent?.upgradePlanUrl ?? '',
303 > providerUriSetting: product.defaultChatAgent?.providerUriSetting ?? '',
304 > entitlementSignupLimitedUrl: product.defaultChatAgent?.entitlementSignupLimitedUrl ?? '',
305 > chatQuotaExceededContext: product.defaultChatAgent?.chatQuotaExceededContext ?? '',
306 > completionsQuotaExceededContext: product.defaultChatAgent?.completionsQuotaExceededContext ?? ''
307 > };
308 >
309 > interface IChatQuotasAccessor {
310 > clearQuotas(): void;
311 > acceptQuotas(quotas: IQuotas): void;
312 > }
313 >
314 > const CHAT_ALLOW_ANONYMOUS_CONFIGURATION_KEY = 'chat.allowAnonymousAccess';
315 >
316 function isAnonymous(configurationService: IConfigurationService, entitlement: ChatEntitlement, sentiment: IChatSentiment): boolean {
317 if (configurationService.getValue(CHAT_ALLOW_ANONYMOUS_CONFIGURATION_KEY) !== true) {
329 return true;
330 }
332 > type ChatEntitlementClassification = {
333 > owner: 'bpasero';
334 > comment: 'Provides insight into chat entitlements.';
335 > chatHidden: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether chat is hidden or not.' };
336 > chatEntitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The current chat entitlement of the user.' };
337 > chatAnonymous: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user is anonymously using chat.' };
338 > chatRegistered: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user is registered for chat.' };
339 > chatDisabled: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether chat is disabled or not.' };
340 > };
341 > type ChatEntitlementEvent = {
342 > chatHidden: boolean;
343 > chatEntitlement: ChatEntitlement;
344 > chatAnonymous: boolean;
345 > chatRegistered: boolean;
346 > chatDisabled: boolean;
347 > };
348 >
349 function logChatEntitlements(state: IChatEntitlementContextState, configurationService: IConfigurationService, telemetryService: ITelemetryService): void {
350 telemetryService.publicLog2<ChatEntitlementEvent, ChatEntitlementClassification>('chatEntitlements', {
356 });
357 }
359 > type ChatAdditionalSpendConfigurationClassification = {
360 > owner: 'pwang347';
361 > comment: 'Tracks when a user enables or disables additional spend.';
362 > enabled: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether additional spend is now enabled or disabled.' };
363 > entitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The current chat entitlement of the user.' };
364 > };
365 > type ChatAdditionalSpendConfigurationEvent = {
366 > enabled: boolean;
367 > entitlement: ChatEntitlement;
368 > };
369 >
370 > type ChatAdditionalSpendActiveClassification = {
371 > owner: 'pwang347';
372 > comment: 'Tracks when a user enters additional spend (included quota exhausted while additional spend is enabled).';
373 > entitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The current chat entitlement of the user.' };
374 > additionalUsageCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The number of additional spend interactions used so far.' };
375 > };
376 > type ChatAdditionalSpendActiveEvent = {
377 > entitlement: ChatEntitlement;
378 > additionalUsageCount: number;
379 > };
380 >
381 > export class ChatEntitlementService extends Disposable implements IChatEntitlementService {
382 >
383 > declare _serviceBrand: undefined;
384 >
385 > private static readonly CACHED_UBB_STORAGE_KEY = 'chat.usageBasedBilling';
386 >
387 > readonly context: Lazy<ChatEntitlementContext> | undefined;
388 > readonly requests: Lazy<ChatEntitlementRequests> | undefined;
389 >
390 > constructor(
391 @IInstantiationService instantiationService: IInstantiationService,
392 @IProductService productService: IProductService,
467 this.registerListeners();
468 }
470 > //#region --- Entitlements
471 >
472 > readonly onDidChangeEntitlement: Event<void>;
473 > readonly entitlementObs: IObservable<ChatEntitlement>;
474 >
475 > get entitlement(): ChatEntitlement {
476 if (this.contextKeyService.getContextKeyValue<boolean>(ChatEntitlementContextKeys.Entitlement.planEdu.key) === true) {
477 return ChatEntitlement.EDU;
496 return ChatEntitlement.Unresolved;
497 }
499 > get isInternal(): boolean {
500 return this.contextKeyService.getContextKeyValue<boolean>(ChatEntitlementContextKeys.Entitlement.internal.key) === true;
501 }
503 > get organisations(): string[] | undefined {
504 return this.contextKeyService.getContextKeyValue<string[]>(ChatEntitlementContextKeys.Entitlement.organisations.key);
505 }
507 > get sku(): string | undefined {
508 return this.contextKeyService.getContextKeyValue<string>(ChatEntitlementContextKeys.Entitlement.sku.key);
509 }
511 > get copilotTrackingId(): string | undefined {
512 return this.context?.value.state.copilotTrackingId;
513 }
515 > get clientByokEnabled(): boolean {
516 return this.contextKeyService.getContextKeyValue<boolean>('github.copilot.clientByokEnabled') === true;
517 }
519 > get hasByokModels(): boolean {
520 return this.contextKeyService.getContextKeyValue<boolean>('github.copilot.hasByokModels') === true;
521 }
523 > //#endregion
524 >
525 > //#region --- Quotas
526 >
527 > private readonly _onDidChangeQuotaExceeded = this._register(new Emitter<void>());
528 > readonly onDidChangeQuotaExceeded = this._onDidChangeQuotaExceeded.event;
529 >
530 > private readonly _onDidChangeQuotaRemaining = this._register(new Emitter<void>());
531 > readonly onDidChangeQuotaRemaining = this._onDidChangeQuotaRemaining.event;
532 >
533 > private readonly _onDidChangeUsageBasedBilling = this._register(new Emitter<void>());
534 > readonly onDidChangeUsageBasedBilling = this._onDidChangeUsageBasedBilling.event;
535 >
536 > private _quotas: IQuotas;
537 > private quotaCopilotTrackingId: string | undefined;
538 > get quotas() { return this._quotas; }
539 >
540 > private readonly chatQuotaExceededContextKey: IContextKey<boolean>;
541 > private readonly completionsQuotaExceededContextKey: IContextKey<boolean>;
542 >
543 > private ExtensionQuotaContextKeys = {
544 > chatQuotaExceeded: defaultChatAgent.chatQuotaExceededContext,
545 > completionsQuotaExceeded: defaultChatAgent.completionsQuotaExceededContext,
546 > };
547 >
548 > private registerListeners(): void {
549 const quotaExceededSet = new Set([this.ExtensionQuotaContextKeys.chatQuotaExceeded, this.ExtensionQuotaContextKeys.completionsQuotaExceeded]);
550
585 this._register(this.onDidChangeSentiment(() => updateAnonymousUsage()));
586 }
588 > acceptQuotas(incomingQuotas: IQuotas): void {
589 const oldQuota = this._quotas;
590 const cachedQuota = this.quotaCopilotTrackingId === this.copilotTrackingId ? oldQuota : {};
649 }
650 }
652 > private compareQuotas(oldQuota: IQuotaSnapshot | undefined, newQuota: IQuotaSnapshot | undefined): { changed: { exceeded: boolean; remaining: boolean } } {
653 return {
654 changed: {
659 };
660 }
662 > clearQuotas(): void {
663 this.acceptQuotas({});
664 }
666 > private updateContextKeys(): void {
667 const chatExhausted = this._quotas.chat?.percentRemaining === 0;
668 const premiumChatExhausted = this._quotas.premiumChat?.unlimited
677 this.completionsQuotaExceededContextKey.set(this._quotas.completions?.percentRemaining === 0);
678 }
680 > //#endregion
681 >
682 > //#region --- Sentiment
683 >
684 > readonly onDidChangeSentiment: Event<void>;
685 > readonly sentimentObs: IObservable<IChatSentiment>;
686 >
687 > get sentiment(): IChatSentiment {
688 return {
689 completed: this.contextKeyService.getContextKeyValue<boolean>(ChatEntitlementContextKeys.Setup.completed.key) === true,
697 };
698 }
700 > //#endregion
701 >
702 > //region --- Anonymous
703 >
704 > private readonly anonymousContextKey: IContextKey<boolean>;
705 >
706 > private readonly _onDidChangeAnonymous = this._register(new Emitter<void>());
707 > readonly onDidChangeAnonymous = this._onDidChangeAnonymous.event;
708 >
709 > readonly anonymousObs = observableFromEvent(this.onDidChangeAnonymous, () => this.anonymous);
710 >
711 > get anonymous(): boolean {
712 return isAnonymous(this.configurationService, this.entitlement, this.sentiment);
713 }
715 > //#endregion
716 >
717 > markAnonymousRateLimited(): void {
718 if (!this.anonymous) {
719 return;
723 this._onDidChangeQuotaExceeded.fire();
724 }
726 > markSetupCompleted(): void {
727 this.context?.value.update({ completed: true });
728 }
730 > setForceHidden(hidden: boolean): void {
731 if (this.context) {
732 this.context.value.setForceHidden(hidden);
737 }
738 }
740 > async update(token: CancellationToken): Promise<void> {
741 await this.requests?.value.forceResolveEntitlement(token);
742 }
744 >
745 > //#endregion
746 >
747 > //#region Chat Entitlement Request Service
748 >
749 > type EntitlementClassification = {
750 > tid: { classification: 'EndUserPseudonymizedInformation'; purpose: 'BusinessInsight'; comment: 'The anonymized analytics id returned by the service'; endpoint: 'GoogleAnalyticsId' };
751 > entitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Flag indicating the chat entitlement state' };
752 > sku: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The SKU of the chat entitlement' };
753 > quotaChatUnlimited: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user has unlimited chat requests' };
754 > quotaChatHasQuota: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user currently has chat quota available' };
755 > quotaChatEntitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The raw chat quota entitlement count' };
756 > quotaPremiumChat: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The percentage of premium chat requests remaining for the user' };
757 > quotaPremiumChatUnlimited: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user has unlimited premium chat requests' };
758 > quotaPremiumChatHasQuota: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user currently has premium chat quota available' };
759 > quotaPremiumChatEntitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The raw premium chat quota entitlement count' };
760 > quotaCompletions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The percentage of completions remaining for the user' };
761 > quotaCompletionsUnlimited: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user has unlimited completions' };
762 > quotaCompletionsHasQuota: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user currently has completions quota available' };
763 > quotaCompletionsEntitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The raw completions quota entitlement count' };
764 > quotaResetDate: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The date the quota will reset' };
765 > usageBasedBilling: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user is on usage-based billing' };
766 > additionalUsageEnabled: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether overage / additional spend is enabled' };
767 > additionalUsageCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The number of overage interactions used' };
768 > canUpgradePlan: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user is eligible to upgrade their plan' };
769 > owner: 'bpasero';
770 > comment: 'Reporting chat entitlements';
771 > };
772 >
773 > type EntitlementEvent = {
774 > entitlement: ChatEntitlement;
775 > tid: string;
776 > sku: string | undefined;
777 > quotaChatUnlimited: boolean | undefined;
778 > quotaChatHasQuota: boolean | undefined;
779 > quotaChatEntitlement: number | undefined;
780 > quotaPremiumChat: number | undefined;
781 > quotaPremiumChatUnlimited: boolean | undefined;
782 > quotaPremiumChatHasQuota: boolean | undefined;
783 > quotaPremiumChatEntitlement: number | undefined;
784 > quotaCompletions: number | undefined;
785 > quotaCompletionsUnlimited: boolean | undefined;
786 > quotaCompletionsHasQuota: boolean | undefined;
787 > quotaCompletionsEntitlement: number | undefined;
788 > quotaResetDate: string | undefined;
789 > usageBasedBilling: boolean | undefined;
790 > additionalUsageEnabled: boolean | undefined;
791 > additionalUsageCount: number | undefined;
792 > canUpgradePlan: boolean | undefined;
793 > };
794 >
795 > interface IEntitlements {
796 > readonly entitlement: ChatEntitlement;
797 > readonly organisations?: string[];
798 > readonly sku?: string;
799 > readonly copilotTrackingId?: string;
800 > readonly quotas?: IQuotas;
801 > }
802 >
803 > export interface IQuotaSnapshot {
804 > readonly percentRemaining: number;
805 > readonly unlimited: boolean;
806 > readonly hasQuota?: boolean;
807 > readonly resetAt?: number;
808 > readonly usageBasedBilling?: boolean;
809 > readonly entitlement?: number;
810 > readonly quotaRemaining?: number;
811 > readonly creditsUsed?: number;
812 > }
813 >
814 > export interface IRateLimitSnapshot {
815 > readonly percentRemaining: number;
816 > readonly unlimited: boolean;
817 > readonly resetDate?: string;
818 > }
819 >
820 > interface IQuotas {
821 > readonly resetDate?: string;
822 > readonly resetDateHasTime?: boolean;
823 >
824 > readonly usageBasedBilling?: boolean;
825 > readonly canUpgradePlan?: boolean;
826 >
827 > readonly chat?: IQuotaSnapshot;
828 > readonly completions?: IQuotaSnapshot;
829 > readonly premiumChat?: IQuotaSnapshot;
830 > readonly additionalUsageEnabled?: boolean;
831 > readonly additionalUsageCount?: number;
832 > readonly additionalUsageEntitlement?: number;
833 >
834 > readonly sessionRateLimit?: IRateLimitSnapshot;
835 > readonly weeklyRateLimit?: IRateLimitSnapshot;
836 > }
837 >
838 function mergeDefinedSnapshot<T extends object>(previous: T | undefined, current: T): T {
839 const result = { ...previous, ...current };
845 return result;
846 }
848 > export function parseQuotas(entitlementsData: IEntitlementsData): IQuotas {
849 const quotas: Mutable<IQuotas> = {
850 resetDate: entitlementsData.quota_reset_date_utc ?? entitlementsData.quota_reset_date ?? entitlementsData.limited_user_reset_date,
919 return quotas;
920 }
922 > export class ChatEntitlementRequests extends Disposable {
923 >
924 > private state: IEntitlements;
925 >
926 > private pendingResolveCts = new CancellationTokenSource();
927 >
928 > constructor(
929 private readonly context: ChatEntitlementContext,
930 private readonly chatQuotasAccessor: IChatQuotasAccessor,
946 this.resolve();
947 }
949 > private registerListeners(): void {
950 this._register(this.defaultAccountService.onDidChangeDefaultAccount(() => this.resolve()));
951
959 }));
960 }
962 > private async resolve(): Promise<void> {
963 this.pendingResolveCts.dispose(true);
964 const cts = this.pendingResolveCts = new CancellationTokenSource();
989 }
990 }
992 > private async resolveEntitlement(defaultAccount: IDefaultAccount, token: CancellationToken): Promise<IEntitlements | undefined> {
993 const entitlements = await this.doResolveEntitlement(defaultAccount, token);
994 if (typeof entitlements?.entitlement === 'number' && !token.isCancellationRequested) {
997 return entitlements;
998 }
1000 > private async doResolveEntitlement(defaultAccount: IDefaultAccount, token: CancellationToken): Promise<IEntitlements | undefined> {
1001 if (token.isCancellationRequested) {
1002 return undefined;
1065 return entitlements;
1066 }
1068 > private toQuotas(entitlementsData: IEntitlementsData): IQuotas {
1069 return parseQuotas(entitlementsData);
1070 }
1072 > private async request(url: string, type: 'GET', body: undefined, sessions: AuthenticationSession[], token: CancellationToken, callSite: string): Promise<IRequestContext | undefined>;
1073 > private async request(url: string, type: 'POST', body: object, sessions: AuthenticationSession[], token: CancellationToken, callSite: string): Promise<IRequestContext | undefined>;
1074 > private async request(url: string, type: 'GET' | 'POST', body: object | undefined, sessions: AuthenticationSession[], token: CancellationToken, callSite: string): Promise<IRequestContext | undefined> {
1075 let lastRequest: IRequestContext | undefined;
1076
1108 return lastRequest;
1109 }
1111 > private update(state: IEntitlements): void {
1112 this.state = state;
1113
1118 }
1119 }
1121 > async forceResolveEntitlement(token = CancellationToken.None): Promise<IEntitlements | undefined> {
1122 const defaultAccount = await this.defaultAccountService.refresh({ forceRefresh: true });
1123 if (!defaultAccount) {
1127 return this.resolveEntitlement(defaultAccount, token);
1128 }
1130 > async signUpFree(): Promise<true /* signed up */ | false /* already signed up */ | { errorCode: number } /* error */ | undefined /* no session */> {
1131 const sessions = await this.getSessions();
1132 if (sessions.length === 0) {
1135 return this.doSignUpFree(sessions);
1136 }
1138 > private async doSignUpFree(sessions: AuthenticationSession[]): Promise<true /* signed up */ | false /* already signed up */ | { errorCode: number } /* error */> {
1139 const body = {
1140 restricted_telemetry: this.telemetryService.telemetryLevel === TelemetryLevel.NONE ? 'disabled' : 'enabled',
1194 return Boolean(parsedResult?.subscribed);
1195 }
1197 > private async getSessions(): Promise<AuthenticationSession[]> {
1198 const defaultAccount = await this.defaultAccountService.getDefaultAccount();
1199 if (defaultAccount) {
1206 return [...(await this.authenticationService.getSessions(this.defaultAccountService.getDefaultAccountAuthenticationProvider().id))];
1207 }
1209 > private async onUnknownSignUpError(detail: string, logMessage: string): Promise<boolean> {
1210 this.logService.error(logMessage);
1211
1223 return false;
1224 }
1226 > private onUnprocessableSignUpError(logMessage: string, logDetails: string): void {
1227 this.logService.error(logMessage);
1228
1245 }
1246 }
1248 > async signIn(options?: { useSocialProvider?: string; additionalScopes?: readonly string[] }): Promise<{ defaultAccount?: IDefaultAccount; entitlements?: IEntitlements }> {
1249 const defaultAccount = await this.defaultAccountService.signIn({
1250 additionalScopes: options?.additionalScopes,
1259 return { defaultAccount, entitlements };
1260 }
1262 > override dispose(): void {
1263 this.pendingResolveCts.dispose(true);
1264
1265 super.dispose();
1266 }
1268 >
1269 > //#endregion
1270 >
1271 > //#region Context
1272 >
1273 > export interface IChatEntitlementContextState extends IChatSentiment {
1274 >
1275 > /**
1276 > * Users last known or resolved entitlement.
1277 > */
1278 > entitlement: ChatEntitlement;
1279 >
1280 > /**
1281 > * User's last known or resolved raw SKU type.
1282 > */
1283 > sku: string | undefined;
1284 >
1285 > /**
1286 > * User's last known or resolved organisations.
1287 > */
1288 > organisations: string[] | undefined;
1289 >
1290 > /**
1291 > * User's Copilot tracking ID from the entitlement API.
1292 > */
1293 > copilotTrackingId: string | undefined;
1294 > }
1295 >
1296 > export class ChatEntitlementContext extends Disposable {
1297 >
1298 > private static readonly CHAT_ENTITLEMENT_CONTEXT_STORAGE_KEY = 'chat.setupContext';
1299 > private static readonly CHAT_ENTITLEMENT_CONTEXT_MIGRATED_STORAGE_KEY = 'chat.setupContext.migrated.v1';
1300 >
1301 > private static readonly CHAT_DISABLED_CONFIGURATION_KEY = 'chat.disableAIFeatures';
1302 >
1303 > private readonly canSignUpContextKey: IContextKey<boolean>;
1304 > private readonly signedOutContextKey: IContextKey<boolean>;
1305 >
1306 > private readonly freeContextKey: IContextKey<boolean>;
1307 > private readonly eduContextKey: IContextKey<boolean>;
1308 > private readonly proContextKey: IContextKey<boolean>;
1309 > private readonly proPlusContextKey: IContextKey<boolean>;
1310 > private readonly maxContextKey: IContextKey<boolean>;
1311 > private readonly businessContextKey: IContextKey<boolean>;
1312 > private readonly enterpriseContextKey: IContextKey<boolean>;
1313 >
1314 > private readonly organisationsContextKey: IContextKey<string[] | undefined>;
1315 > private readonly isInternalContextKey: IContextKey<boolean>;
1316 > private readonly skuContextKey: IContextKey<string | undefined>;
1317 >
1318 > private readonly completedContext: IContextKey<boolean>;
1319 > private readonly hiddenContext: IContextKey<boolean>;
1320 > private readonly disabledInWorkspaceContext: IContextKey<boolean>;
1321 > private readonly laterContext: IContextKey<boolean>;
1322 > private readonly installedContext: IContextKey<boolean>;
1323 > private readonly disabledContext: IContextKey<boolean>;
1324 > private readonly untrustedContext: IContextKey<boolean>;
1325 > private readonly registeredContext: IContextKey<boolean>;
1326 >
1327 > private _state: IChatEntitlementContextState;
1328 > private suspendedState: IChatEntitlementContextState | undefined = undefined;
1329 > get state(): IChatEntitlementContextState { return this.withConfiguration(this.suspendedState ?? this._state); }
1330 >
1331 > private readonly _onDidChange = this._register(new Emitter<void>());
1332 > readonly onDidChange = this._onDidChange.event;
1333 >
1334 > private updateBarrier: Barrier | undefined = undefined;
1335 >
1336 > constructor(
1337 @IContextKeyService contextKeyService: IContextKeyService,
1338 @IStorageService private readonly storageService: IStorageService,
1387 this.registerListeners();
1388 }
1390 > private registerListeners(): void {
1391 this._register(this.configurationService.onDidChangeConfiguration(e => {
1392 if (e.affectsConfiguration(ChatEntitlementContext.CHAT_DISABLED_CONFIGURATION_KEY)) {
1395 }));
1396 }
1398 > private _forceHidden = false;
1399 >
1400 > private withConfiguration(state: IChatEntitlementContextState): IChatEntitlementContextState {
1401 if (this._forceHidden || this.configurationService.getValue(ChatEntitlementContext.CHAT_DISABLED_CONFIGURATION_KEY) === true) {
1402 return {
1408 return state;
1409 }
1411 > setForceHidden(hidden: boolean): void {
1412 if (this._forceHidden !== hidden) {
1413 this._forceHidden = hidden;
1415 }
1416 }
1418 > update(context: { installed: boolean; disabled: boolean; untrusted: boolean; disabledInWorkspace: boolean }): Promise<void>;
1419 > update(context: { completed: true }): Promise<void>;
1420 > update(context: { hidden: false }): Promise<void>; // legacy UI state from before we had a setting to hide, keep around to still support users who used this
1421 > update(context: { later: boolean }): Promise<void>;
1422 > update(context: { entitlement: ChatEntitlement; organisations: string[] | undefined; sku: string | undefined; copilotTrackingId: string | undefined }): Promise<void>;
1423 > async update(context: { completed?: boolean; installed?: boolean; disabled?: boolean; untrusted?: boolean; disabledInWorkspace?: boolean; hidden?: false; later?: boolean; entitlement?: ChatEntitlement; organisations?: string[]; sku?: string; copilotTrackingId?: string }): Promise<void> {
1424 this.logService.trace(`[chat entitlement context] update(): ${JSON.stringify(context)}`);
1425
1477 return this.updateContext();
1478 }
1480 > private async updateContext(): Promise<void> {
1481 await this.updateBarrier?.wait();
1482
1483 this.updateContextSync();
1484 }
1486 > private updateContextSync(): void {
1487 const state = this.withConfiguration(this._state);
1488
1516 this._onDidChange.fire();
1517 }
1519 > suspend(): void {
1520 this.suspendedState = { ...this._state };
1521 this.updateBarrier = new Barrier();
1522 }
1524 > resume(): void {
1525 this.suspendedState = undefined;
1526 this.updateBarrier?.open();
1527 this.updateBarrier = undefined;
1528 }
1530 >
1531 > //#endregion
1532 >
1533 > registerSingleton(IChatEntitlementService, ChatEntitlementService, InstantiationType.Eager /* To ensure context keys are set asap */);
src/vs/platform/configuration/common/configurationRegistry.ts 638 covered LOC · 98 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>();
410 this.doDeregisterConfigurations(configurations, properties);
414 this._onDidUpdateConfiguration.fire({ properties });
415 }
417 > public updateConfigurations({ add, remove }: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void {
418 > const properties = new Set<string>(); configurationRegistry.ts
419 > this.doDeregisterConfigurations(remove, properties);
420 > this.doRegisterConfigurations(add, false, properties);
421 >
422 > contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
423 > this._onDidSchemaChange.fire();
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) {
696 for (const key in configuration.properties) {
715 configuration.allOf?.forEach(node => deregisterConfiguration(node));
716 };
717 > for (const configuration of configurations) { configurationRegistry.ts
718 deregisterConfiguration(configuration);
719 const index = this.configurationContributors.indexOf(configuration);
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')) { configurationRegistry.ts
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); configurationRegistry.ts
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; configurationRegistry.ts
828 > }
830 > getPolicyReferenceConfigurations(): Map<PolicyName, Set<string>> {
831 return this.policyReferenceConfigurations;
832 }
834 > getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {
835 return this.excludedConfigurationProperties;
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; configurationRegistry.ts
871 > break;
872 > case ConfigurationScope.MACHINE: configurationRegistry.ts
873 > machineSettings.properties[key] = property; configurationRegistry.ts
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;
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];
896 switch (property.scope) {
917 }
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); configurationRegistry.ts
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; configurationRegistry.ts
1005 > switch (t) {
1006 > case 'boolean':
1007 return false;
1008 > case 'integer': configurationRegistry.ts
1009 > case 'number':
1010 return 0;
1011 > case 'string': configurationRegistry.ts
1012 > return ''; configurationRegistry.ts
1013 > case 'array': configurationRegistry.ts
1014 > return []; configurationRegistry.ts
1015 > case 'object': configurationRegistry.ts
1016 return {};
1017 > default: configurationRegistry.ts
1018 return null;
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/workbench/services/textfile/common/textfiles.ts 575 covered LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- textfiles.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 '../../../../base/common/uri.js';
7 > import { Event } from '../../../../base/common/event.js';
8 > import { IDisposable } from '../../../../base/common/lifecycle.js';
9 > import { ISaveOptions, IRevertOptions, SaveReason } from '../../../common/editor.js';
10 > import { ReadableStream } from '../../../../base/common/stream.js';
11 > import { IBaseFileStatWithMetadata, IFileStatWithMetadata, IWriteFileOptions, FileOperationError, FileOperationResult, IReadFileStreamOptions, IFileReadLimits } from '../../../../platform/files/common/files.js';
12 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
13 > import { ITextEditorModel } from '../../../../editor/common/services/resolverService.js';
14 > import { ITextBufferFactory, ITextModel, ITextSnapshot } from '../../../../editor/common/model.js';
15 > import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from '../../../../base/common/buffer.js';
16 > import { areFunctions, isUndefinedOrNull } from '../../../../base/common/types.js';
17 > import { IWorkingCopy, IWorkingCopySaveEvent } from '../../workingCopy/common/workingCopy.js';
18 > import { IUntitledTextEditorModelManager } from '../../untitled/common/untitledTextEditorService.js';
19 > import { CancellationToken } from '../../../../base/common/cancellation.js';
20 > import { IProgress, IProgressStep } from '../../../../platform/progress/common/progress.js';
21 > import { IFileOperationUndoRedoInfo } from '../../workingCopy/common/workingCopyFileService.js';
22 >
23 > export const ITextFileService = createDecorator<ITextFileService>('textFileService');
24 >
25 > export interface ITextFileService extends IDisposable {
26 >
27 > readonly _serviceBrand: undefined;
28 >
29 > /**
30 > * Access to the manager of text file editor models providing further
31 > * methods to work with them.
32 > */
33 > readonly files: ITextFileEditorModelManager;
34 >
35 > /**
36 > * Access to the manager of untitled text editor models providing further
37 > * methods to work with them.
38 > */
39 > readonly untitled: IUntitledTextEditorModelManager;
40 >
41 > /**
42 > * Helper to determine encoding for resources.
43 > */
44 > readonly encoding: IResourceEncodings;
45 >
46 > /**
47 > * A resource is dirty if it has unsaved changes or is an untitled file not yet saved.
48 > *
49 > * @param resource the resource to check for being dirty
50 > */
51 > isDirty(resource: URI): boolean;
52 >
53 > /**
54 > * Saves the resource.
55 > *
56 > * @param resource the resource to save
57 > * @param options optional save options
58 > * @return Path of the saved resource or undefined if canceled.
59 > */
60 > save(resource: URI, options?: ITextFileSaveOptions): Promise<URI | undefined>;
61 >
62 > /**
63 > * Saves the provided resource asking the user for a file name or using the provided one.
64 > *
65 > * @param resource the resource to save as.
66 > * @param targetResource the optional target to save to.
67 > * @param options optional save options
68 > * @return Path of the saved resource or undefined if canceled.
69 > */
70 > saveAs(resource: URI, targetResource?: URI, options?: ITextFileSaveAsOptions): Promise<URI | undefined>;
71 >
72 > /**
73 > * Reverts the provided resource.
74 > *
75 > * @param resource the resource of the file to revert.
76 > * @param force to force revert even when the file is not dirty
77 > */
78 > revert(resource: URI, options?: IRevertOptions): Promise<void>;
79 >
80 > /**
81 > * Read the contents of a file identified by the resource.
82 > */
83 > read(resource: URI, options?: IReadTextFileOptions): Promise<ITextFileContent>;
84 >
85 > /**
86 > * Read the contents of a file identified by the resource as stream.
87 > */
88 > readStream(resource: URI, options?: IReadTextFileOptions): Promise<ITextFileStreamContent>;
89 >
90 > /**
91 > * Update a file with given contents.
92 > */
93 > write(resource: URI, value: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<IFileStatWithMetadata>;
94 >
95 > /**
96 > * Create files. If the file exists it will be overwritten with the contents if
97 > * the options enable to overwrite.
98 > */
99 > create(operations: { resource: URI; value?: string | ITextSnapshot; options?: { overwrite?: boolean } }[], undoInfo?: IFileOperationUndoRedoInfo): Promise<readonly IFileStatWithMetadata[]>;
100 >
101 > /**
102 > * Returns the readable that uses the appropriate encoding. This method should
103 > * be used whenever a `string` or `ITextSnapshot` is being persisted to the
104 > * file system.
105 > */
106 > getEncodedReadable(resource: URI | undefined, value: ITextSnapshot, options?: IWriteTextFileOptions): Promise<VSBufferReadable>;
107 > getEncodedReadable(resource: URI | undefined, value: string, options?: IWriteTextFileOptions): Promise<VSBuffer | VSBufferReadable>;
108 > getEncodedReadable(resource: URI | undefined, value?: ITextSnapshot, options?: IWriteTextFileOptions): Promise<VSBufferReadable | undefined>;
109 > getEncodedReadable(resource: URI | undefined, value?: string, options?: IWriteTextFileOptions): Promise<VSBuffer | VSBufferReadable | undefined>;
110 > getEncodedReadable(resource: URI | undefined, value?: string | ITextSnapshot, options?: IWriteTextFileOptions): Promise<VSBuffer | VSBufferReadable | undefined>;
111 >
112 > /**
113 > * Returns a stream of strings that uses the appropriate encoding. This method should
114 > * be used whenever a `VSBufferReadableStream` is being loaded from the file system.
115 > *
116 > * Will throw an error if `acceptTextOnly: true` for resources that seem to be binary.
117 > */
118 > getDecodedStream(resource: URI | undefined, value: VSBufferReadableStream, options?: IReadTextFileEncodingOptions): Promise<ReadableStream<string>>;
119 >
120 > /**
121 > * Get the encoding for the provided `resource`. Will try to determine the encoding
122 > * from any existing model for that `resource` and fallback to the configured defaults.
123 > */
124 > getEncoding(resource: URI): string;
125 >
126 > /**
127 > * Get the properties for decoding the provided `resource` based on configuration.
128 > */
129 > resolveDecoding(resource: URI | undefined, options?: IReadTextFileEncodingOptions): Promise<{ preferredEncoding: string; guessEncoding: boolean; candidateGuessEncodings: string[] }>;
130 >
131 > /**
132 > * Get the properties for encoding the provided `resource` based on configuration.
133 > */
134 > resolveEncoding(resource: URI | undefined, options?: IWriteTextFileOptions): Promise<{ encoding: string; addBOM: boolean }>;
135 >
136 > /**
137 > * Given a detected encoding, validate it against the configured encoding options.
138 > */
139 > validateDetectedEncoding(resource: URI | undefined, detectedEncoding: string, options?: IReadTextFileEncodingOptions): Promise<string>;
140 > }
141 >
142 > export interface IReadTextFileEncodingOptions {
143 >
144 > /**
145 > * The optional encoding parameter allows to specify the desired encoding when resolving
146 > * the contents of the file.
147 > */
148 > readonly encoding?: string;
149 >
150 > /**
151 > * The optional guessEncoding parameter allows to guess encoding from content of the file.
152 > */
153 > readonly autoGuessEncoding?: boolean;
154 >
155 > /**
156 > * The optional candidateGuessEncodings parameter limits the allowed encodings to guess from.
157 > */
158 > readonly candidateGuessEncodings?: string[];
159 >
160 > /**
161 > * The optional acceptTextOnly parameter allows to fail this request early if the file
162 > * contents are not textual.
163 > */
164 > readonly acceptTextOnly?: boolean;
165 > }
166 >
167 > export interface IReadTextFileOptions extends IReadTextFileEncodingOptions, IReadFileStreamOptions { }
168 >
169 > export interface IWriteTextFileOptions extends IWriteFileOptions {
170 >
171 > /**
172 > * The encoding to use when updating a file.
173 > */
174 > readonly encoding?: string;
175 >
176 > /**
177 > * Whether to write to the file as elevated (admin) user. When setting this option a prompt will
178 > * ask the user to authenticate as super user.
179 > */
180 > readonly writeElevated?: boolean;
181 > }
182 >
183 > export const enum TextFileOperationResult {
184 > FILE_IS_BINARY
185 > }
186 >
187 > export class TextFileOperationError extends FileOperationError {
188 >
189 > static isTextFileOperationError(obj: unknown): obj is TextFileOperationError {
190 > return obj instanceof Error && !isUndefinedOrNull((obj as TextFileOperationError).textFileOperationResult);
191 > }
192 >
193 > override readonly options?: IReadTextFileOptions & IWriteTextFileOptions;
194 >
195 > constructor(
196 message: string,
197 public textFileOperationResult: TextFileOperationResult,
202 this.options = options;
203 }
204 > } textfiles.ts
205 >
206 > export interface IResourceEncodings {
207 > getPreferredReadEncoding(resource: URI): Promise<IResourceEncoding>;
208 > getPreferredWriteEncoding(resource: URI, preferredEncoding?: string): Promise<IResourceEncoding>;
209 > }
210 >
211 > export interface IResourceEncoding {
212 > readonly encoding: string;
213 > readonly hasBOM: boolean;
214 > }
215 >
216 > /**
217 > * The save error handler can be installed on the text file editor model to install code that executes when save errors occur.
218 > */
219 > export interface ISaveErrorHandler {
220 >
221 > /**
222 > * Called whenever a save fails.
223 > */
224 > onSaveError(error: Error, model: ITextFileEditorModel, options: ITextFileSaveAsOptions): void;
225 > }
226 >
227 > /**
228 > * States the text file editor model can be in.
229 > */
230 > export const enum TextFileEditorModelState {
231 >
232 > /**
233 > * A model is saved.
234 > */
235 > SAVED,
236 >
237 > /**
238 > * A model is dirty.
239 > */
240 > DIRTY,
241 >
242 > /**
243 > * A model is currently being saved but this operation has not completed yet.
244 > */
245 > PENDING_SAVE,
246 >
247 > /**
248 > * A model is in conflict mode when changes cannot be saved because the
249 > * underlying file has changed. Models in conflict mode are always dirty.
250 > */
251 > CONFLICT,
252 >
253 > /**
254 > * A model is in orphan state when the underlying file has been deleted.
255 > */
256 > ORPHAN,
257 >
258 > /**
259 > * Any error that happens during a save that is not causing the CONFLICT state.
260 > * Models in error mode are always dirty.
261 > */
262 > ERROR
263 > }
264 >
265 > export const enum TextFileResolveReason {
266 > EDITOR = 1,
267 > REFERENCE = 2,
268 > OTHER = 3
269 > }
270 >
271 > interface IBaseTextFileContent extends IBaseFileStatWithMetadata {
272 >
273 > /**
274 > * The encoding of the content if known.
275 > */
276 > readonly encoding: string;
277 > }
278 >
279 > export interface ITextFileContent extends IBaseTextFileContent {
280 >
281 > /**
282 > * The content of a text file.
283 > */
284 > readonly value: string;
285 > }
286 >
287 > export interface ITextFileStreamContent extends IBaseTextFileContent {
288 >
289 > /**
290 > * The line grouped content of a text file.
291 > */
292 > readonly value: ITextBufferFactory;
293 > }
294 >
295 > export interface ITextFileEditorModelResolveOrCreateOptions extends ITextFileResolveOptions {
296 >
297 > /**
298 > * The language id to use for the model text content.
299 > */
300 > readonly languageId?: string;
301 >
302 > /**
303 > * The encoding to use when resolving the model text content.
304 > */
305 > readonly encoding?: string;
306 >
307 > /**
308 > * If the model was already resolved before, allows to trigger
309 > * a reload of it to fetch the latest contents.
310 > */
311 > readonly reload?: {
312 >
313 > /**
314 > * Controls whether the reload happens in the background
315 > * or whether `resolve` will await the reload to happen.
316 > */
317 > readonly async: boolean;
318 > };
319 > }
320 >
321 > export interface ITextFileSaveEvent extends ITextFileEditorModelSaveEvent {
322 >
323 > /**
324 > * The model that was saved.
325 > */
326 > readonly model: ITextFileEditorModel;
327 > }
328 >
329 > export interface ITextFileResolveEvent {
330 >
331 > /**
332 > * The model that was resolved.
333 > */
334 > readonly model: ITextFileEditorModel;
335 >
336 > /**
337 > * The reason why the model was resolved.
338 > */
339 > readonly reason: TextFileResolveReason;
340 > }
341 >
342 > export interface ITextFileSaveParticipantContext {
343 >
344 > /**
345 > * The reason why the save was triggered.
346 > */
347 > readonly reason: SaveReason;
348 >
349 > /**
350 > * Only applies to when a text file was saved as, for
351 > * example when starting with untitled and saving. This
352 > * provides access to the initial resource the text
353 > * file had before.
354 > */
355 > readonly savedFrom?: URI;
356 > }
357 >
358 > export interface ITextFileSaveParticipant {
359 >
360 > /**
361 > * The ordinal number which determines the order of participation.
362 > * Lower values mean to participant sooner
363 > */
364 > readonly ordinal?: number;
365 >
366 > /**
367 > * Participate in a save of a model. Allows to change the model
368 > * before it is being saved to disk.
369 > */
370 > participate(
371 > model: ITextFileEditorModel,
372 > context: ITextFileSaveParticipantContext,
373 > progress: IProgress<IProgressStep>,
374 > token: CancellationToken
375 > ): Promise<void>;
376 > }
377 >
378 > export interface ITextFileEditorModelManager {
379 >
380 > readonly onDidCreate: Event<ITextFileEditorModel>;
381 > readonly onDidResolve: Event<ITextFileResolveEvent>;
382 > readonly onDidChangeDirty: Event<ITextFileEditorModel>;
383 > readonly onDidChangeReadonly: Event<ITextFileEditorModel>;
384 > readonly onDidRemove: Event<URI>;
385 > readonly onDidChangeOrphaned: Event<ITextFileEditorModel>;
386 > readonly onDidChangeEncoding: Event<ITextFileEditorModel>;
387 > readonly onDidSaveError: Event<ITextFileEditorModel>;
388 > readonly onDidSave: Event<ITextFileSaveEvent>;
389 > readonly onDidRevert: Event<ITextFileEditorModel>;
390 >
391 > /**
392 > * Access to all text file editor models in memory.
393 > */
394 > readonly models: ITextFileEditorModel[];
395 >
396 > /**
397 > * Allows to configure the error handler that is called on save errors.
398 > */
399 > saveErrorHandler: ISaveErrorHandler;
400 >
401 > /**
402 > * Returns the text file editor model for the provided resource
403 > * or undefined if none.
404 > */
405 > get(resource: URI): ITextFileEditorModel | undefined;
406 >
407 > /**
408 > * Allows to resolve a text file model from disk.
409 > */
410 > resolve(resource: URI, options?: ITextFileEditorModelResolveOrCreateOptions): Promise<ITextFileEditorModel>;
411 >
412 > /**
413 > * Adds a participant for saving text file models.
414 > */
415 > addSaveParticipant(participant: ITextFileSaveParticipant): IDisposable;
416 >
417 > /**
418 > * Runs the registered save participants on the provided model.
419 > */
420 > runSaveParticipants(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void>;
421 >
422 > /**
423 > * Waits for the model to be ready to be disposed. There may be conditions
424 > * under which the model cannot be disposed, e.g. when it is dirty. Once the
425 > * promise is settled, it is safe to dispose the model.
426 > */
427 > canDispose(model: ITextFileEditorModel): true | Promise<true>;
428 > }
429 >
430 > export interface ITextFileSaveOptions extends ISaveOptions {
431 >
432 > /**
433 > * Save the file with an attempt to unlock it.
434 > */
435 > readonly writeUnlock?: boolean;
436 >
437 > /**
438 > * Save the file with elevated privileges.
439 > *
440 > * Note: This may not be supported in all environments.
441 > */
442 > readonly writeElevated?: boolean;
443 >
444 > /**
445 > * Allows to write to a file even if it has been modified on disk.
446 > */
447 > readonly ignoreModifiedSince?: boolean;
448 >
449 > /**
450 > * If set, will bubble up the error to the caller instead of handling it.
451 > */
452 > readonly ignoreErrorHandler?: boolean;
453 > }
454 >
455 > export interface ITextFileSaveAsOptions extends ITextFileSaveOptions {
456 >
457 > /**
458 > * Optional URI of the resource the text file is saved from if known.
459 > */
460 > readonly from?: URI;
461 >
462 > /**
463 > * Optional URI to use as suggested file path to save as.
464 > */
465 > readonly suggestedTarget?: URI;
466 > }
467 >
468 > export interface ITextFileResolveOptions {
469 >
470 > /**
471 > * The contents to use for the model if known. If not
472 > * provided, the contents will be retrieved from the
473 > * underlying resource or backup if present.
474 > */
475 > readonly contents?: ITextBufferFactory;
476 >
477 > /**
478 > * Go to file bypassing any cache of the model if any.
479 > */
480 > readonly forceReadFromFile?: boolean;
481 >
482 > /**
483 > * Allow to resolve a model even if we think it is a binary file.
484 > */
485 > readonly allowBinary?: boolean;
486 >
487 > /**
488 > * Context why the model is being resolved.
489 > */
490 > readonly reason?: TextFileResolveReason;
491 >
492 > /**
493 > * If provided, the size of the file will be checked against the limits
494 > * and an error will be thrown if any limit is exceeded.
495 > */
496 > readonly limits?: IFileReadLimits;
497 > }
498 >
499 > export const enum EncodingMode {
500 >
501 > /**
502 > * Instructs the encoding support to encode the object with the provided encoding
503 > */
504 > Encode,
505 >
506 > /**
507 > * Instructs the encoding support to decode the object with the provided encoding
508 > */
509 > Decode
510 > }
511 >
512 > export interface IEncodingSupport {
513 >
514 > /**
515 > * Gets the encoding of the object if known.
516 > */
517 > getEncoding(): string | undefined;
518 >
519 > /**
520 > * Sets the encoding for the object for saving.
521 > */
522 > setEncoding(encoding: string, mode: EncodingMode): Promise<void>;
523 > }
524 >
525 > export interface ILanguageSupport {
526 >
527 > /**
528 > * Sets the language id of the object.
529 > */
530 > setLanguageId(languageId: string, source?: string): void;
531 > }
532 >
533 > export interface ITextFileEditorModelSaveEvent extends IWorkingCopySaveEvent {
534 >
535 > /**
536 > * The resolved stat from the save operation.
537 > */
538 > readonly stat: IFileStatWithMetadata;
539 > }
540 >
541 > export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, ILanguageSupport, IWorkingCopy {
542 >
543 > readonly onDidSave: Event<ITextFileEditorModelSaveEvent>;
544 > readonly onDidSaveError: Event<void>;
545 > readonly onDidChangeOrphaned: Event<void>;
546 > readonly onDidChangeReadonly: Event<void>;
547 > readonly onDidChangeEncoding: Event<void>;
548 >
549 > hasState(state: TextFileEditorModelState): boolean;
550 > joinState(state: TextFileEditorModelState.PENDING_SAVE): Promise<void>;
551 >
552 > updatePreferredEncoding(encoding: string | undefined): void;
553 >
554 > save(options?: ITextFileSaveAsOptions): Promise<boolean>;
555 > revert(options?: IRevertOptions): Promise<void>;
556 >
557 > resolve(options?: ITextFileResolveOptions): Promise<void>;
558 >
559 > isDirty(): this is IResolvedTextFileEditorModel;
560 >
561 > getLanguageId(): string | undefined;
562 >
563 > isResolved(): this is IResolvedTextFileEditorModel;
564 > }
565 >
566 > export function isTextFileEditorModel(model: ITextEditorModel): model is ITextFileEditorModel {
567 const candidate = model as ITextFileEditorModel;
568
569 return areFunctions(candidate.setEncoding, candidate.getEncoding, candidate.save, candidate.revert, candidate.isDirty, candidate.getLanguageId);
570 }
571 > textfiles.ts
572 > export interface IResolvedTextFileEditorModel extends ITextFileEditorModel {
573 >
574 > readonly textEditorModel: ITextModel;
575 >
576 > createSnapshot(): ITextSnapshot;
577 > }
578 >
579 > export function snapshotToString(snapshot: ITextSnapshot): string {
580 const chunks: string[] = [];
581
587 return chunks.join('');
588 }
589 > textfiles.ts
590 > export function stringToSnapshot(value: string): ITextSnapshot {
591 let done = false;
592
603 };
604 }
605 > textfiles.ts
606 > export function toBufferOrReadable(value: string): VSBuffer;
607 > export function toBufferOrReadable(value: ITextSnapshot): VSBufferReadable;
608 > export function toBufferOrReadable(value: string | ITextSnapshot): VSBuffer | VSBufferReadable;
609 > export function toBufferOrReadable(value: string | ITextSnapshot | undefined): VSBuffer | VSBufferReadable | undefined;
610 > export function toBufferOrReadable(value: string | ITextSnapshot | undefined): VSBuffer | VSBufferReadable | undefined {
611 if (typeof value === 'undefined') {
612 return undefined;
src/vs/base/common/lifecycle.ts 574 covered LOC · 135 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; lifecycle.ts
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; lifecycle.ts
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); lifecycle.ts
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; lifecycle.ts
613 > markAsDisposed(this);
614 > this._value?.dispose();
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; lifecycle.ts
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); lifecycle.ts
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) { lifecycle.ts
783 > return; lifecycle.ts
784 > }
785
786 try {
789 this._store.clear();
790 }
791 > } lifecycle.ts
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/platform/editor/common/editor.ts 541 covered LOC · 3 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 { equals } from '../../../base/common/arrays.js';
7 > import { Event } from '../../../base/common/event.js';
8 > import { IDisposable } from '../../../base/common/lifecycle.js';
9 > import { URI } from '../../../base/common/uri.js';
10 > import { IContextKeyService } from '../../contextkey/common/contextkey.js';
11 > import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js';
12 > import { IRectangle } from '../../window/common/window.js';
13 >
14 > export interface IResolvableEditorModel extends IDisposable {
15 >
16 > /**
17 > * Resolves the model.
18 > */
19 > resolve(): Promise<void>;
20 >
21 > /**
22 > * Find out if the editor model was resolved or not.
23 > */
24 > isResolved(): boolean;
25 > }
26 >
27 > export function isResolvedEditorModel(model: IDisposable | undefined | null): model is IResolvableEditorModel {
28 const candidate = model as IResolvableEditorModel | undefined | null;
29
31 && typeof candidate?.isResolved === 'function';
32 }
33 > editor.ts
34 > export interface IBaseUntypedEditorInput {
35 >
36 > /**
37 > * Optional options to use when opening the input.
38 > */
39 > options?: IEditorOptions;
40 >
41 > /**
42 > * Label to show for the input.
43 > */
44 > readonly label?: string;
45 >
46 > /**
47 > * Description to show for the input.
48 > */
49 > readonly description?: string;
50 > }
51 >
52 > export interface IBaseResourceEditorInput extends IBaseUntypedEditorInput {
53 >
54 > /**
55 > * Hint to indicate that this input should be treated as a
56 > * untitled file.
57 > *
58 > * Without this hint, the editor service will make a guess by
59 > * looking at the scheme of the resource(s).
60 > *
61 > * Use `forceUntitled: true` when you pass in a `resource` that
62 > * does not use the `untitled` scheme. The `resource` will then
63 > * be used as associated path when saving the untitled file.
64 > */
65 > readonly forceUntitled?: boolean;
66 > }
67 >
68 > export interface IBaseTextResourceEditorInput extends IBaseResourceEditorInput {
69 >
70 > /**
71 > * Optional options to use when opening the text input.
72 > */
73 > options?: ITextEditorOptions;
74 >
75 > /**
76 > * The contents of the text input if known. If provided,
77 > * the input will not attempt to load the contents from
78 > * disk and may appear dirty.
79 > */
80 > contents?: string;
81 >
82 > /**
83 > * The encoding of the text input if known.
84 > */
85 > encoding?: string;
86 >
87 > /**
88 > * The identifier of the language id of the text input
89 > * if known to use when displaying the contents.
90 > */
91 > languageId?: string;
92 > }
93 >
94 > export interface IResourceEditorInput extends IBaseResourceEditorInput {
95 >
96 > /**
97 > * The resource URI of the resource to open.
98 > */
99 > readonly resource: URI;
100 > }
101 >
102 > export interface ITextResourceEditorInput extends IResourceEditorInput, IBaseTextResourceEditorInput {
103 >
104 > /**
105 > * Optional options to use when opening the text input.
106 > */
107 > options?: ITextEditorOptions;
108 > }
109 >
110 > /**
111 > * This identifier allows to uniquely identify an editor with a
112 > * resource, type and editor identifier.
113 > */
114 > export interface IResourceEditorInputIdentifier {
115 >
116 > /**
117 > * The type of the editor.
118 > */
119 > readonly typeId: string;
120 >
121 > /**
122 > * The identifier of the editor if provided.
123 > */
124 > readonly editorId: string | undefined;
125 >
126 > /**
127 > * The resource URI of the editor.
128 > */
129 > readonly resource: URI;
130 > }
131 >
132 > export enum EditorActivation {
133 >
134 > /**
135 > * Activate the editor after it opened. This will automatically restore
136 > * the editor if it is minimized.
137 > */
138 > ACTIVATE = 1,
139 >
140 > /**
141 > * Only restore the editor if it is minimized but do not activate it.
142 > *
143 > * Note: will only work in combination with the `preserveFocus: true` option.
144 > * Otherwise, if focus moves into the editor, it will activate and restore
145 > * automatically.
146 > */
147 > RESTORE,
148 >
149 > /**
150 > * Preserve the current active editor.
151 > *
152 > * Note: will only work in combination with the `preserveFocus: true` option.
153 > * Otherwise, if focus moves into the editor, it will activate and restore
154 > * automatically.
155 > */
156 > PRESERVE
157 > }
158 >
159 > export enum EditorResolution {
160 >
161 > /**
162 > * Displays a picker and allows the user to decide which editor to use.
163 > */
164 > PICK,
165 >
166 > /**
167 > * Only exclusive editors are considered.
168 > */
169 > EXCLUSIVE_ONLY
170 > }
171 >
172 > export enum EditorOpenSource {
173 >
174 > /**
175 > * Default: the editor is opening via a programmatic call
176 > * to the editor service API.
177 > */
178 > API,
179 >
180 > /**
181 > * Indicates that a user action triggered the opening, e.g.
182 > * via mouse or keyboard use.
183 > */
184 > USER
185 > }
186 >
187 > export interface IEditorOptions {
188 >
189 > /**
190 > * Tells the editor to not receive keyboard focus when the editor is being opened.
191 > *
192 > * Will also not activate the group the editor opens in unless the group is already
193 > * the active one. This behaviour can be overridden via the `activation` option.
194 > */
195 > preserveFocus?: boolean;
196 >
197 > /**
198 > * This option is only relevant if an editor is opened into a group that is not active
199 > * already and allows to control if the inactive group should become active, restored
200 > * or preserved.
201 > *
202 > * By default, the editor group will become active unless `preserveFocus` or `inactive`
203 > * is specified.
204 > */
205 > activation?: EditorActivation;
206 >
207 > /**
208 > * Tells the editor to reload the editor input in the editor even if it is identical to the one
209 > * already showing. By default, the editor will not reload the input if it is identical to the
210 > * one showing.
211 > */
212 > forceReload?: boolean;
213 >
214 > /**
215 > * Will reveal the editor if it is already opened and visible in any of the opened editor groups.
216 > *
217 > * Note that this option is just a hint that might be ignored if the user wants to open an editor explicitly
218 > * to the side of another one or into a specific editor group.
219 > */
220 > revealIfVisible?: boolean;
221 >
222 > /**
223 > * Will reveal the editor if it is already opened (even when not visible) in any of the opened editor groups.
224 > *
225 > * Note that this option is just a hint that might be ignored if the user wants to open an editor explicitly
226 > * to the side of another one or into a specific editor group.
227 > */
228 > revealIfOpened?: boolean;
229 >
230 > /**
231 > * An editor that is pinned remains in the editor stack even when another editor is being opened.
232 > * An editor that is not pinned will always get replaced by another editor that is not pinned.
233 > */
234 > pinned?: boolean;
235 >
236 > /**
237 > * An editor that is sticky moves to the beginning of the editors list within the group and will remain
238 > * there unless explicitly closed. Operations such as "Close All" will not close sticky editors.
239 > */
240 > sticky?: boolean;
241 >
242 > /**
243 > * The index in the document stack where to insert the editor into when opening.
244 > */
245 > index?: number;
246 >
247 > /**
248 > * An active editor that is opened will show its contents directly. Set to true to open an editor
249 > * in the background without loading its contents.
250 > *
251 > * Will also not activate the group the editor opens in unless the group is already
252 > * the active one. This behaviour can be overridden via the `activation` option.
253 > */
254 > inactive?: boolean;
255 >
256 > /**
257 > * In case of an error opening the editor, will not present this error to the user (e.g. by showing
258 > * a generic placeholder in the editor area). So it is up to the caller to provide error information
259 > * in that case.
260 > *
261 > * By default, an error when opening an editor will result in a placeholder editor that shows the error.
262 > * In certain cases a modal dialog may be presented to ask the user for further action.
263 > */
264 > ignoreError?: boolean;
265 >
266 > /**
267 > * Allows to override the editor that should be used to display the input:
268 > * - `undefined`: let the editor decide for itself
269 > * - `string`: specific override by id
270 > * - `EditorResolution`: specific override handling
271 > */
272 > override?: string | EditorResolution;
273 >
274 > /**
275 > * A optional hint to signal in which context the editor opens.
276 > *
277 > * If configured to be `EditorOpenSource.USER`, this hint can be
278 > * used in various places to control the experience. For example,
279 > * if the editor to open fails with an error, a notification could
280 > * inform about this in a modal dialog. If the editor opened through
281 > * some background task, the notification would show in the background,
282 > * not as a modal dialog.
283 > */
284 > source?: EditorOpenSource;
285 >
286 > /**
287 > * Indicates whether the editor is being opened due to an explicit user
288 > * action (`true`) or automatically (`false`) as a side effect of another
289 > * action (e.g. the chat agent opening files it has edited).
290 > *
291 > * When omitted, callers should be treated as explicit. Layout logic may
292 > * use this to decide whether to react to the visibility change (for
293 > * example, by leaving the auxiliary side bar maximized when the change
294 > * was not initiated by the user).
295 > */
296 > isExplicit?: boolean;
297 >
298 > /**
299 > * An optional property to signal that certain view state should be
300 > * applied when opening the editor.
301 > */
302 > viewState?: object;
303 >
304 > /**
305 > * A transient editor will attempt to appear as preview and certain components
306 > * (such as history tracking) may decide to ignore the editor when it becomes
307 > * active.
308 > * This option is meant to be used only when the editor is used for a short
309 > * period of time, for example when opening a preview of the editor from a
310 > * picker control in the background while navigating through results of the picker.
311 > *
312 > * Note: an editor that is already opened in a group that is not transient, will
313 > * not turn transient.
314 > */
315 > transient?: boolean;
316 >
317 > /**
318 > * Options that only apply when `AUX_WINDOW_GROUP` is used for opening.
319 > */
320 > auxiliary?: {
321 >
322 > /**
323 > * Define the bounds of the editor window.
324 > */
325 > bounds?: Partial<IRectangle>;
326 >
327 > /**
328 > * Show editor compact, hiding unnecessary elements.
329 > */
330 > compact?: boolean;
331 >
332 > /**
333 > * Show the editor always on top of other windows.
334 > */
335 > alwaysOnTop?: boolean;
336 > };
337 >
338 > /**
339 > * Options that only apply when `MODAL_GROUP` is used for opening.
340 > */
341 > modal?: IModalEditorPartOptions;
342 > }
343 >
344 > export interface IModalEditorPartOptions {
345 >
346 > /**
347 > * Whether the modal editor should be maximized.
348 > */
349 > readonly maximized?: boolean;
350 >
351 > /**
352 > * Size of the modal editor part unless it is maximized.
353 > */
354 > readonly size?: { readonly width: number; readonly height: number };
355 >
356 > /**
357 > * Position of the modal editor part unless it is maximized.
358 > */
359 > readonly position?: { readonly left: number; readonly top: number };
360 >
361 > /**
362 > * The navigation context for navigating between items
363 > * within this modal editor. Pass `undefined` to clear.
364 > */
365 > readonly navigation?: IModalEditorNavigation;
366 >
367 > /**
368 > * Optional sidebar content to render on the left side of the
369 > * modal editor. The caller provides a render callback that
370 > * receives a container element and a layout callback, and
371 > * returns a disposable to clean up when the modal closes.
372 > *
373 > * Note: the sidebar will only be shown when provided during
374 > * opening and cannot currently be added, removed, or updated
375 > * after the modal editor is opened.
376 > */
377 > readonly sidebar?: IModalEditorSidebar;
378 > }
379 >
380 > /**
381 > * Per-editor modal options provided by an editor input that wants to influence
382 > * how it is rendered inside the modal editor part. Unlike
383 > * {@link IModalEditorPartOptions}, these options are scoped to a single editor
384 > * and resolved from the active editor (not from the part-level options API).
385 > */
386 > export interface IModalEditorOptions {
387 >
388 > /**
389 > * When true, the modal editor renders a simplified header:
390 > * uses the editor background, hides the title icon, removes the
391 > * bottom border and uses a slightly taller fixed height. Useful
392 > * for editors that provide their own header chrome.
393 > */
394 > readonly compactHeader?: boolean;
395 > }
396 >
397 > /**
398 > * Marker interface for editor inputs that want to customize how they are
399 > * rendered when opened in the modal editor part (see {@link IModalEditorOptions}).
400 > */
401 > export interface IModalEditorOptionsProvider {
402 > getModalEditorOptions(): IModalEditorOptions | undefined;
403 > }
404 >
405 > export function isModalEditorOptionsProvider(obj: unknown): obj is IModalEditorOptionsProvider {
406 return !!obj && typeof (obj as IModalEditorOptionsProvider).getModalEditorOptions === 'function';
407 }
408 > editor.ts
409 > /**
410 > * Modal sidebar supports rendering custom content in a sidebar next to the main editor content.
411 > */
412 > export interface IModalEditorSidebar {
413 >
414 > /**
415 > * Sidebar width set by the user via resizing, if any.
416 > */
417 > readonly sidebarWidth?: number;
418 >
419 > /**
420 > * Whether the sidebar is hidden.
421 > */
422 > readonly sidebarHidden?: boolean;
423 >
424 > /**
425 > * Render the sidebar content into the given container.
426 > *
427 > * @param container The DOM element to render into.
428 > * @param onDidLayout An event that fires when the sidebar is
429 > * laid out with the available dimensions.
430 > * @param contextKeyService A context key service scoped to the modal
431 > * that content should descend from (e.g. when creating lists/trees)
432 > * so that modal-level context keys remain active while the content
433 > * has focus.
434 > * @returns A disposable to clean up when the modal closes.
435 > */
436 > readonly render: (container: unknown /* HTMLElement */, onDidLayout: Event<{ readonly height: number; readonly width: number }>, contextKeyService: IContextKeyService) => IDisposable;
437 > }
438 >
439 > /**
440 > * Context for navigating between items within a modal editor.
441 > */
442 > export interface IModalEditorNavigation {
443 >
444 > /**
445 > * Total number of items in the navigation list.
446 > */
447 > readonly total: number;
448 >
449 > /**
450 > * Current 0-based index in the navigation list.
451 > */
452 > readonly current: number;
453 >
454 > /**
455 > * Navigate to the item at the given 0-based index.
456 > */
457 > readonly navigate: (index: number) => void;
458 > }
459 >
460 > export interface ITextEditorSelection {
461 > readonly startLineNumber: number;
462 > readonly startColumn: number;
463 > readonly endLineNumber?: number;
464 > readonly endColumn?: number;
465 > }
466 >
467 > export const enum TextEditorSelectionRevealType {
468 > /**
469 > * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically.
470 > */
471 > Center = 0,
472 >
473 > /**
474 > * Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport.
475 > */
476 > CenterIfOutsideViewport = 1,
477 >
478 > /**
479 > * Option to scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport, but not quite at the top.
480 > */
481 > NearTop = 2,
482 >
483 > /**
484 > * Option to scroll vertically or horizontally as necessary and reveal a range close to the top of the viewport, but not quite at the top.
485 > * Only if it lies outside the viewport
486 > */
487 > NearTopIfOutsideViewport = 3,
488 > }
489 >
490 > export const enum TextEditorSelectionSource {
491 >
492 > /**
493 > * Programmatic source indicates a selection change that
494 > * was not triggered by the user via keyboard or mouse
495 > * but through text editor APIs.
496 > */
497 > PROGRAMMATIC = 'api',
498 >
499 > /**
500 > * Navigation source indicates a selection change that
501 > * was caused via some command or UI component such as
502 > * an outline tree.
503 > */
504 > NAVIGATION = 'code.navigation',
505 >
506 > /**
507 > * Jump source indicates a selection change that
508 > * was caused from within the text editor to another
509 > * location in the same or different text editor such
510 > * as "Go to definition".
511 > */
512 > JUMP = 'code.jump'
513 > }
514 >
515 > export interface ITextEditorOptions extends IEditorOptions {
516 >
517 > /**
518 > * Text editor selection.
519 > */
520 > selection?: ITextEditorSelection;
521 >
522 > /**
523 > * Option to control the text editor selection reveal type.
524 > * Defaults to TextEditorSelectionRevealType.Center
525 > */
526 > selectionRevealType?: TextEditorSelectionRevealType;
527 >
528 > /**
529 > * Source of the call that caused the selection.
530 > */
531 > selectionSource?: TextEditorSelectionSource | string;
532 > }
533 >
534 > export type ITextEditorChange = [
535 > originalStartLineNumber: number,
536 > originalEndLineNumberExclusive: number,
537 > modifiedStartLineNumber: number,
538 > modifiedEndLineNumberExclusive: number
539 > ];
540 >
541 > export interface ITextEditorDiffInformation {
542 > readonly documentVersion: number;
543 > readonly original: URI | undefined;
544 > readonly modified: URI;
545 > readonly changes: readonly ITextEditorChange[];
546 > }
547 >
548 > export function isTextEditorDiffInformationEqual(
549 uriIdentityService: IUriIdentityService,
550 diff1: ITextEditorDiffInformation | undefined,
src/vs/platform/dialogs/common/dialogs.ts 511 covered LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- dialogs.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 } from '../../../base/common/cancellation.js';
7 > import { Event } from '../../../base/common/event.js';
8 > import { ThemeIcon } from '../../../base/common/themables.js';
9 > import { IMarkdownString } from '../../../base/common/htmlContent.js';
10 > import { basename } from '../../../base/common/resources.js';
11 > import Severity from '../../../base/common/severity.js';
12 > import { URI } from '../../../base/common/uri.js';
13 > import { localize } from '../../../nls.js';
14 > import { createDecorator } from '../../instantiation/common/instantiation.js';
15 > import { ITelemetryData } from '../../telemetry/common/telemetry.js';
16 >
17 > export interface IDialogArgs {
18 > readonly confirmArgs?: IConfirmDialogArgs;
19 > readonly inputArgs?: IInputDialogArgs;
20 > readonly promptArgs?: IPromptDialogArgs;
21 > }
22 >
23 > export interface IBaseDialogOptions {
24 > readonly type?: Severity | DialogType;
25 >
26 > readonly title?: string;
27 > readonly message: string;
28 > readonly detail?: string;
29 >
30 > readonly checkbox?: ICheckbox;
31 >
32 > /**
33 > * Allows to enforce use of custom dialog even in native environments.
34 > */
35 > readonly custom?: boolean | ICustomDialogOptions;
36 >
37 > /**
38 > * An optional cancellation token that can be used to dismiss the dialog
39 > * programmatically for custom dialog implementations.
40 > *
41 > * When cancelled, the custom dialog resolves as if the cancel button was
42 > * pressed. Native dialog handlers cannot currently be dismissed
43 > * programmatically and ignore this option unless a custom dialog is
44 > * explicitly enforced via the {@link custom} option.
45 > */
46 > readonly token?: CancellationToken;
47 > }
48 >
49 > export interface IConfirmDialogArgs {
50 > readonly confirmation: IConfirmation;
51 > }
52 >
53 > export interface IConfirmation extends IBaseDialogOptions {
54 >
55 > /**
56 > * If not provided, defaults to `Yes`.
57 > */
58 > readonly primaryButton?: string;
59 >
60 > /**
61 > * If not provided, defaults to `Cancel`.
62 > */
63 > readonly cancelButton?: string;
64 > }
65 >
66 > export interface IConfirmationResult extends ICheckboxResult {
67 >
68 > /**
69 > * Will be true if the dialog was confirmed with the primary button pressed.
70 > */
71 > readonly confirmed: boolean;
72 > }
73 >
74 > export interface IInputDialogArgs {
75 > readonly input: IInput;
76 > }
77 >
78 > export interface IInput extends IConfirmation {
79 > readonly inputs: IInputElement[];
80 >
81 > /**
82 > * If not provided, defaults to `Ok`.
83 > */
84 > readonly primaryButton?: string;
85 > }
86 >
87 > export interface IInputElement {
88 > readonly type?: 'text' | 'password';
89 > readonly value?: string;
90 > readonly placeholder?: string;
91 > }
92 >
93 > export interface IInputResult extends IConfirmationResult {
94 >
95 > /**
96 > * Values for the input fields as provided by the user or `undefined` if none.
97 > */
98 > readonly values?: string[];
99 > }
100 >
101 > export interface IPromptDialogArgs {
102 > readonly prompt: IPrompt<unknown>;
103 > }
104 >
105 > export interface IPromptBaseButton<T> {
106 >
107 > /**
108 > * @returns the result of the prompt button will be returned
109 > * as result from the `prompt()` call.
110 > */
111 > run(checkbox: ICheckboxResult): T | Promise<T>;
112 > }
113 >
114 > export interface IPromptButton<T> extends IPromptBaseButton<T> {
115 > readonly label: string;
116 > }
117 >
118 > export interface IPromptCancelButton<T> extends IPromptBaseButton<T> {
119 >
120 > /**
121 > * The cancel button to show in the prompt. Defaults to
122 > * `Cancel` if not provided.
123 > */
124 > readonly label?: string;
125 > }
126 >
127 > export interface IPrompt<T> extends IBaseDialogOptions {
128 >
129 > /**
130 > * The buttons to show in the prompt. Defaults to `OK`
131 > * if no buttons or cancel button is provided.
132 > */
133 > readonly buttons?: IPromptButton<T>[];
134 >
135 > /**
136 > * The cancel button to show in the prompt. Defaults to
137 > * `Cancel` if set to `true`.
138 > */
139 > readonly cancelButton?: IPromptCancelButton<T> | true | string;
140 > }
141 >
142 > export interface IPromptWithCustomCancel<T> extends IPrompt<T> {
143 > readonly cancelButton: IPromptCancelButton<T>;
144 > }
145 >
146 > export interface IPromptWithDefaultCancel<T> extends IPrompt<T> {
147 > readonly cancelButton: true | string;
148 > }
149 >
150 > export interface IPromptResult<T> extends ICheckboxResult {
151 >
152 > /**
153 > * The result of the `IPromptButton` that was pressed or `undefined` if none.
154 > */
155 > readonly result?: T;
156 > }
157 >
158 > export interface IPromptResultWithCancel<T> extends IPromptResult<T> {
159 > readonly result: T;
160 > }
161 >
162 > export interface IAsyncPromptResult<T> extends ICheckboxResult {
163 >
164 > /**
165 > * The result of the `IPromptButton` that was pressed or `undefined` if none.
166 > */
167 > readonly result?: Promise<T>;
168 > }
169 >
170 > export interface IAsyncPromptResultWithCancel<T> extends IAsyncPromptResult<T> {
171 > readonly result: Promise<T>;
172 > }
173 >
174 > export type IDialogResult = IConfirmationResult | IInputResult | IAsyncPromptResult<unknown>;
175 >
176 > export type DialogType = 'none' | 'info' | 'error' | 'question' | 'warning';
177 >
178 > export interface ICheckbox {
179 > readonly label: string;
180 > readonly checked?: boolean;
181 > }
182 >
183 > export interface ICheckboxResult {
184 >
185 > /**
186 > * This will only be defined if the confirmation was created
187 > * with the checkbox option defined.
188 > */
189 > readonly checkboxChecked?: boolean;
190 > }
191 >
192 > export interface IPickAndOpenOptions {
193 > readonly forceNewWindow?: boolean;
194 > defaultUri?: URI;
195 > readonly telemetryExtraData?: ITelemetryData;
196 > availableFileSystems?: string[];
197 > remoteAuthority?: string | null;
198 > }
199 >
200 > export interface FileFilter {
201 > readonly extensions: string[];
202 > readonly name: string;
203 > }
204 >
205 > export interface ISaveDialogOptions {
206 >
207 > /**
208 > * A human-readable string for the dialog title
209 > */
210 > title?: string;
211 >
212 > /**
213 > * The resource the dialog shows when opened.
214 > */
215 > defaultUri?: URI;
216 >
217 > /**
218 > * A set of file filters that are used by the dialog. Each entry is a human readable label,
219 > * like "TypeScript", and an array of extensions.
220 > */
221 > filters?: FileFilter[];
222 >
223 > /**
224 > * A human-readable string for the ok button
225 > */
226 > readonly saveLabel?: { readonly withMnemonic: string; readonly withoutMnemonic: string } | string;
227 >
228 > /**
229 > * Specifies a list of schemas for the file systems the user can save to. If not specified, uses the schema of the defaultURI or, if also not specified,
230 > * the schema of the current window.
231 > */
232 > availableFileSystems?: readonly string[];
233 > }
234 >
235 > export interface IOpenDialogOptions {
236 >
237 > /**
238 > * A human-readable string for the dialog title
239 > */
240 > readonly title?: string;
241 >
242 > /**
243 > * The resource the dialog shows when opened.
244 > */
245 > defaultUri?: URI;
246 >
247 > /**
248 > * A human-readable string for the open button.
249 > */
250 > readonly openLabel?: { readonly withMnemonic: string; readonly withoutMnemonic: string } | string;
251 >
252 > /**
253 > * Allow to select files, defaults to `true`.
254 > */
255 > canSelectFiles?: boolean;
256 >
257 > /**
258 > * Allow to select folders, defaults to `false`.
259 > */
260 > canSelectFolders?: boolean;
261 >
262 > /**
263 > * Allow to select many files or folders.
264 > */
265 > readonly canSelectMany?: boolean;
266 >
267 > /**
268 > * A set of file filters that are used by the dialog. Each entry is a human readable label,
269 > * like "TypeScript", and an array of extensions.
270 > */
271 > filters?: FileFilter[];
272 >
273 > /**
274 > * Specifies a list of schemas for the file systems the user can load from. If not specified, uses the schema of the defaultURI or, if also not available,
275 > * the schema of the current window.
276 > */
277 > availableFileSystems?: readonly string[];
278 > }
279 >
280 > export const IDialogService = createDecorator<IDialogService>('dialogService');
281 >
282 > export interface ICustomDialogOptions {
283 > readonly buttonDetails?: string[];
284 > readonly markdownDetails?: ICustomDialogMarkdown[];
285 > readonly classes?: string[];
286 > readonly icon?: ThemeIcon;
287 > readonly disableCloseAction?: boolean;
288 > }
289 >
290 > export interface ICustomDialogMarkdown {
291 > readonly markdown: IMarkdownString;
292 > readonly classes?: string[];
293 > /** Custom link handler for markdown content, see {@link IContentActionHandler}. Defaults to {@link openLinkFromMarkdown}. */
294 > actionHandler?(link: string): Promise<boolean>;
295 > }
296 >
297 > /**
298 > * A handler to bring up modal dialogs.
299 > */
300 > export interface IDialogHandler {
301 >
302 > /**
303 > * Ask the user for confirmation with a modal dialog.
304 > */
305 > confirm(confirmation: IConfirmation): Promise<IConfirmationResult>;
306 >
307 > /**
308 > * Prompt the user with a modal dialog.
309 > */
310 > prompt<T>(prompt: IPrompt<T>): Promise<IAsyncPromptResult<T>>;
311 >
312 > /**
313 > * Present a modal dialog to the user asking for input.
314 > */
315 > input(input: IInput): Promise<IInputResult>;
316 >
317 > /**
318 > * Present the about dialog to the user.
319 > */
320 > about(title: string, details: string, detailsToCopy: string): Promise<void>;
321 > }
322 >
323 > enum DialogKind {
324 > Confirmation = 1,
325 > Prompt,
326 > Input
327 > }
328 >
329 > export abstract class AbstractDialogHandler implements IDialogHandler {
330 >
331 > protected getConfirmationButtons(dialog: IConfirmation): string[] {
332 return this.getButtons(dialog, DialogKind.Confirmation);
333 }
334 > dialogs.ts
335 > protected getPromptButtons(dialog: IPrompt<unknown>): string[] {
336 return this.getButtons(dialog, DialogKind.Prompt);
337 }
338 > dialogs.ts
339 > protected getInputButtons(dialog: IInput): string[] {
340 return this.getButtons(dialog, DialogKind.Input);
341 }
342 > dialogs.ts
343 > private getButtons(dialog: IConfirmation, kind: DialogKind.Confirmation): string[];
344 > private getButtons(dialog: IPrompt<unknown>, kind: DialogKind.Prompt): string[];
345 > private getButtons(dialog: IInput, kind: DialogKind.Input): string[];
346 > private getButtons(dialog: IConfirmation | IInput | IPrompt<unknown>, kind: DialogKind): string[] {
347
348 // We put buttons in the order of "default" button first and "cancel"
418 return buttons;
419 }
420 > dialogs.ts
421 > protected getDialogType(type: Severity | DialogType | undefined): DialogType | undefined {
422 if (typeof type === 'string') {
423 return type;
430 return undefined;
431 }
432 > dialogs.ts
433 > protected getPromptResult<T>(prompt: IPrompt<T>, buttonIndex: number, checkboxChecked: boolean | undefined): IAsyncPromptResult<T> {
434 const promptButtons: IPromptBaseButton<T>[] = [...(prompt.buttons ?? [])];
435 if (prompt.cancelButton && typeof prompt.cancelButton !== 'string' && typeof prompt.cancelButton !== 'boolean') {
444 return { result, checkboxChecked };
445 }
446 > dialogs.ts
447 > abstract confirm(confirmation: IConfirmation): Promise<IConfirmationResult>;
448 > abstract input(input: IInput): Promise<IInputResult>;
449 > abstract prompt<T>(prompt: IPrompt<T>): Promise<IAsyncPromptResult<T>>;
450 > abstract about(title: string, details: string, detailsToCopy: string): Promise<void>;
451 > }
452 >
453 > /**
454 > * A service to bring up modal dialogs.
455 > *
456 > * Note: use the `INotificationService.prompt()` method for a non-modal way to ask
457 > * the user for input.
458 > */
459 > export interface IDialogService {
460 >
461 > readonly _serviceBrand: undefined;
462 >
463 > /**
464 > * An event that fires when a dialog is about to show.
465 > */
466 > readonly onWillShowDialog: Event<void>;
467 >
468 > /**
469 > * An event that fires when a dialog did show (closed).
470 > */
471 > readonly onDidShowDialog: Event<void>;
472 >
473 > /**
474 > * Ask the user for confirmation with a modal dialog.
475 > */
476 > confirm(confirmation: IConfirmation): Promise<IConfirmationResult>;
477 >
478 > /**
479 > * Prompt the user with a modal dialog. Provides a bit
480 > * more control over the dialog compared to the simpler
481 > * `confirm` method. Specifically, allows to show more
482 > * than 2 buttons and makes it easier to just show a
483 > * message to the user.
484 > *
485 > * @returns a promise that resolves to the `T` result
486 > * from the provided `IPromptButton<T>` or `undefined`.
487 > */
488 > prompt<T>(prompt: IPromptWithCustomCancel<T>): Promise<IPromptResultWithCancel<T>>;
489 > prompt<T>(prompt: IPromptWithDefaultCancel<T>): Promise<IPromptResult<T>>;
490 > prompt<T>(prompt: IPrompt<T>): Promise<IPromptResult<T>>;
491 >
492 > /**
493 > * Present a modal dialog to the user asking for input.
494 > */
495 > input(input: IInput): Promise<IInputResult>;
496 >
497 > /**
498 > * Show a modal info dialog.
499 > */
500 > info(message: string, detail?: string): Promise<void>;
501 >
502 > /**
503 > * Show a modal warning dialog.
504 > */
505 > warn(message: string, detail?: string): Promise<void>;
506 >
507 > /**
508 > * Show a modal error dialog.
509 > */
510 > error(message: string, detail?: string): Promise<void>;
511 >
512 > /**
513 > * Present the about dialog to the user.
514 > */
515 > about(): Promise<void>;
516 > }
517 >
518 > export const IFileDialogService = createDecorator<IFileDialogService>('fileDialogService');
519 >
520 > /**
521 > * A service to bring up file dialogs.
522 > */
523 > export interface IFileDialogService {
524 >
525 > readonly _serviceBrand: undefined;
526 >
527 > /**
528 > * The default path for a new file based on previously used files.
529 > * @param schemeFilter The scheme of the file path. If no filter given, the scheme of the current window is used.
530 > * Falls back to user home in the absence of enough information to find a better URI.
531 > */
532 > defaultFilePath(schemeFilter?: string): Promise<URI>;
533 >
534 > /**
535 > * The default path for a new folder based on previously used folders.
536 > * @param schemeFilter The scheme of the folder path. If no filter given, the scheme of the current window is used.
537 > * Falls back to user home in the absence of enough information to find a better URI.
538 > */
539 > defaultFolderPath(schemeFilter?: string): Promise<URI>;
540 >
541 > /**
542 > * The default path for a new workspace based on previously used workspaces.
543 > * @param schemeFilter The scheme of the workspace path. If no filter given, the scheme of the current window is used.
544 > * Falls back to user home in the absence of enough information to find a better URI.
545 > */
546 > defaultWorkspacePath(schemeFilter?: string): Promise<URI>;
547 >
548 > /**
549 > * Shows a file-folder selection dialog and opens the selected entry.
550 > */
551 > pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
552 >
553 > /**
554 > * Shows a file selection dialog and opens the selected entry.
555 > */
556 > pickFileAndOpen(options: IPickAndOpenOptions): Promise<void>;
557 >
558 > /**
559 > * Shows a folder selection dialog and opens the selected entry.
560 > */
561 > pickFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
562 >
563 > /**
564 > * Shows a workspace selection dialog and opens the selected entry.
565 > */
566 > pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void>;
567 >
568 > /**
569 > * Shows a save file dialog and save the file at the chosen file URI.
570 > */
571 > pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined>;
572 >
573 > /**
574 > * The preferred folder path to open the dialog at.
575 > * @param schemeFilter The scheme of the file path. If no filter given, the scheme of the current window is used.
576 > * Falls back to user home in the absence of a setting.
577 > */
578 > preferredHome(schemeFilter?: string): Promise<URI>;
579 >
580 > /**
581 > * Shows a save file dialog and returns the chosen file URI.
582 > */
583 > showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined>;
584 >
585 > /**
586 > * Shows a confirm dialog for saving 1-N files.
587 > */
588 > showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult>;
589 >
590 > /**
591 > * Shows a open file dialog and returns the chosen file URI.
592 > */
593 > showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined>;
594 > }
595 >
596 > export const enum ConfirmResult {
597 > SAVE,
598 > DONT_SAVE,
599 > CANCEL
600 > }
601 >
602 > const MAX_CONFIRM_FILES = 10;
603 > export function getFileNamesMessage(fileNamesOrResources: readonly (string | URI)[]): string {
604 const message: string[] = [];
605 message.push(...fileNamesOrResources.slice(0, MAX_CONFIRM_FILES).map(fileNameOrResource => typeof fileNameOrResource === 'string' ? fileNameOrResource : basename(fileNameOrResource)));
616 return message.join('\n');
617 }
618 > dialogs.ts
619 > export interface INativeOpenDialogOptions {
620 > readonly forceNewWindow?: boolean;
621 >
622 > readonly defaultPath?: string;
623 >
624 > readonly telemetryEventName?: string;
625 > readonly telemetryExtraData?: ITelemetryData;
626 > }
src/vs/workbench/services/authentication/common/authentication.ts 507 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- authentication.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 > import { Event } from '../../../../base/common/event.js';
6 > import { IDisposable } from '../../../../base/common/lifecycle.js';
7 > import { IAuthenticationChallenge, IAuthorizationProtectedResourceMetadata, IAuthorizationServerMetadata } from '../../../../base/common/oauth.js';
8 > import { URI } from '../../../../base/common/uri.js';
9 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
10 >
11 > /**
12 > * Use this if you don't want the onDidChangeSessions event to fire in the extension host
13 > */
14 > export const INTERNAL_AUTH_PROVIDER_PREFIX = '__';
15 >
16 > export interface AuthenticationSessionAccount {
17 > label: string;
18 > id: string;
19 > }
20 >
21 > export interface AuthenticationSession {
22 > id: string;
23 > accessToken: string;
24 > account: AuthenticationSessionAccount;
25 > scopes: ReadonlyArray<string>;
26 > idToken?: string;
27 > }
28 >
29 > export interface AuthenticationSessionsChangeEvent {
30 > added: ReadonlyArray<AuthenticationSession> | undefined;
31 > removed: ReadonlyArray<AuthenticationSession> | undefined;
32 > changed: ReadonlyArray<AuthenticationSession> | undefined;
33 > }
34 >
35 > export interface AuthenticationProviderInformation {
36 > id: string;
37 > label: string;
38 > authorizationServerGlobs?: ReadonlyArray<string>;
39 > }
40 >
41 > /**
42 > * Options for creating an authentication session via the service.
43 > */
44 > export interface IAuthenticationCreateSessionOptions {
45 > activateImmediate?: boolean;
46 > /**
47 > * The account that is being asked about. If this is passed in, the provider should
48 > * attempt to return the sessions that are only related to this account.
49 > */
50 > account?: AuthenticationSessionAccount;
51 > /**
52 > * The authorization server URI to use for this creation request. If passed in, first we validate that
53 > * the provider can use this authorization server, then it is passed down to the auth provider.
54 > */
55 > authorizationServer?: URI;
56 > /**
57 > * When specified, the authentication provider will request a token bound to this resource URI
58 > * (RFC 8707 resource indicator).
59 > */
60 > resource?: string;
61 > /**
62 > * The audience for the requested access token. Primarily used for OAuth Identity Assertion
63 > * Authorization Grant (ID-JAG, defined in `draft-ietf-oauth-identity-assertion-authz-grant` using RFC 8693 token-exchange semantics) flows where the audience identifies the authorization server of the resource that
64 > * will redeem the assertion (typically the resource's authorization server URL). Providers that do not understand audience-bound tokens should
65 > * ignore this option.
66 > */
67 > audience?: string;
68 > /**
69 > * Allows the authentication provider to take in additional parameters.
70 > * It is up to the provider to define what these parameters are and handle them.
71 > * This is useful for passing in additional information that is specific to the provider
72 > * and not part of the standard authentication flow.
73 > */
74 > [key: string]: any;
75 > }
76 >
77 > export interface IAuthenticationWwwAuthenticateRequest {
78 > /**
79 > * The raw WWW-Authenticate header value that triggered this challenge.
80 > * This will be parsed by the authentication provider to extract the necessary
81 > * challenge information.
82 > */
83 > readonly wwwAuthenticate: string;
84 >
85 > /**
86 > * Optional scopes for the session. If not provided, the authentication provider
87 > * may use default scopes or extract them from the challenge.
88 > */
89 > readonly fallbackScopes?: readonly string[];
90 > }
91 >
92 > export function isAuthenticationWwwAuthenticateRequest(obj: unknown): obj is IAuthenticationWwwAuthenticateRequest {
93 return typeof obj === 'object'
94 && obj !== null
96 && (typeof obj.wwwAuthenticate === 'string');
97 }
99 > /**
100 > * Represents constraints for authentication, including challenges and optional scopes.
101 > * This is used when creating or retrieving sessions that must satisfy specific authentication
102 > * requirements from WWW-Authenticate headers.
103 > */
104 > export interface IAuthenticationConstraint {
105 > /**
106 > * Array of authentication challenges parsed from WWW-Authenticate headers.
107 > */
108 > readonly challenges: readonly IAuthenticationChallenge[];
109 >
110 > /**
111 > * Optional scopes for the session. If not provided, the authentication provider
112 > * may extract scopes from the challenges or use default scopes.
113 > */
114 > readonly fallbackScopes?: readonly string[];
115 > }
116 >
117 > /**
118 > * Options for getting authentication sessions via the service.
119 > */
120 > export interface IAuthenticationGetSessionsOptions {
121 > /**
122 > * Whether the provider must avoid user interaction while resolving existing sessions.
123 > */
124 > silent?: boolean;
125 > /**
126 > * The account that is being asked about. If this is passed in, the provider should
127 > * attempt to return the sessions that are only related to this account.
128 > */
129 > account?: AuthenticationSessionAccount;
130 > /**
131 > * The authorization server URI to use for this request. If passed in, first we validate that
132 > * the provider can use this authorization server, then it is passed down to the auth provider.
133 > */
134 > authorizationServer?: URI;
135 > /**
136 > * When specified, the authentication provider will request a token bound to this resource URI
137 > * (RFC 8707 resource indicator).
138 > */
139 > resource?: string;
140 > /**
141 > * The audience for the requested access token. Primarily used for OAuth Identity Assertion
142 > * Authorization Grant (ID-JAG, defined in `draft-ietf-oauth-identity-assertion-authz-grant` using RFC 8693 token-exchange semantics) flows where the audience identifies the authorization server of the resource that
143 > * will redeem the assertion (typically the resource's authorization server URL). Providers that do not understand audience-bound tokens should
144 > * ignore this option.
145 > */
146 > audience?: string;
147 > /**
148 > * Allows the authentication provider to take in additional parameters.
149 > * It is up to the provider to define what these parameters are and handle them.
150 > * This is useful for passing in additional information that is specific to the provider
151 > * and not part of the standard authentication flow.
152 > */
153 > [key: string]: any;
154 > }
155 >
156 > export interface AllowedExtension {
157 > id: string;
158 > name: string;
159 > /**
160 > * If true or undefined, the extension is allowed to use the account
161 > * If false, the extension is not allowed to use the account
162 > * TODO: undefined shouldn't be a valid value, but it is for now
163 > */
164 > allowed?: boolean;
165 > lastUsed?: number;
166 > // If true, this comes from the product.json
167 > trusted?: boolean;
168 > }
169 >
170 > export interface IAuthenticationProviderHostDelegate {
171 > /** Priority for this delegate, delegates are tested in descending priority order */
172 > readonly priority: number;
173 > create(authorizationServer: URI, serverMetadata: IAuthorizationServerMetadata, resource: IAuthorizationProtectedResourceMetadata | undefined, clientId?: string, clientSecret?: string): Promise<string>;
174 > /**
175 > * Creates an XAA (enterprise-managed, ID-JAG) authentication provider for the given SSO issuer.
176 > * The returned string is the provider id.
177 > */
178 > createXaa?(issuer: URI): Promise<string>;
179 > }
180 >
181 > export function getDynamicAuthenticationProviderId(authorizationServer: URI, resource: IAuthorizationProtectedResourceMetadata | undefined): string {
182 return resource ? `${authorizationServer.toString(true)} ${resource.resource}` : authorizationServer.toString(true);
183 }
185 > export const IAuthenticationService = createDecorator<IAuthenticationService>('IAuthenticationService');
186 >
187 > export interface IAuthenticationService {
188 > readonly _serviceBrand: undefined;
189 >
190 > /**
191 > * Fires when an authentication provider has been registered
192 > */
193 > readonly onDidRegisterAuthenticationProvider: Event<AuthenticationProviderInformation>;
194 > /**
195 > * Fires when an authentication provider has been unregistered
196 > */
197 > readonly onDidUnregisterAuthenticationProvider: Event<AuthenticationProviderInformation>;
198 >
199 > /**
200 > * Fires when the list of sessions for a provider has been added, removed or changed
201 > */
202 > readonly onDidChangeSessions: Event<{ providerId: string; label: string; event: AuthenticationSessionsChangeEvent }>;
203 >
204 > /**
205 > * Fires when the list of declaredProviders has changed
206 > */
207 > readonly onDidChangeDeclaredProviders: Event<void>;
208 >
209 > /**
210 > * All providers that have been statically declared by extensions. These may not actually be registered or active yet.
211 > */
212 > readonly declaredProviders: AuthenticationProviderInformation[];
213 >
214 > /**
215 > * Registers that an extension has declared an authentication provider in their package.json
216 > * @param provider The provider information to register
217 > */
218 > registerDeclaredAuthenticationProvider(provider: AuthenticationProviderInformation): void;
219 >
220 > /**
221 > * Unregisters a declared authentication provider
222 > * @param id The id of the provider to unregister
223 > */
224 > unregisterDeclaredAuthenticationProvider(id: string): void;
225 >
226 > /**
227 > * Checks if an authentication provider has been registered
228 > * @param id The id of the provider to check
229 > */
230 > isAuthenticationProviderRegistered(id: string): boolean;
231 >
232 > /**
233 > * Checks if an authentication provider is dynamic
234 > * @param id The id of the provider to check
235 > */
236 > isDynamicAuthenticationProvider(id: string): boolean;
237 >
238 > /**
239 > * Registers an authentication provider
240 > * @param id The id of the provider
241 > * @param provider The implementation of the provider
242 > */
243 > registerAuthenticationProvider(id: string, provider: IAuthenticationProvider): void;
244 >
245 > /**
246 > * Unregisters an authentication provider
247 > * @param id The id of the provider to unregister
248 > */
249 > unregisterAuthenticationProvider(id: string): void;
250 >
251 > /**
252 > * Gets the provider ids of all registered authentication providers
253 > */
254 > getProviderIds(): string[];
255 >
256 > /**
257 > * Gets the provider with the given id.
258 > * @param id The id of the provider to get
259 > * @throws if the provider is not registered
260 > */
261 > getProvider(id: string): IAuthenticationProvider;
262 >
263 > /**
264 > * Gets all accounts that are currently logged in across all sessions
265 > * @param id The id of the provider to ask for accounts
266 > * @returns A promise that resolves to an array of accounts
267 > */
268 > getAccounts(id: string): Promise<ReadonlyArray<AuthenticationSessionAccount>>;
269 >
270 > /**
271 > * Gets all sessions that satisfy the given scopes from the provider with the given id
272 > * @param id The id of the provider to ask for a session
273 > * @param scopes The scopes for the session
274 > * @param options Additional options for getting sessions
275 > * @param activateImmediate If true, the provider should activate immediately if it is not already
276 > */
277 > getSessions(id: string, scopeListOrRequest?: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, options?: IAuthenticationGetSessionsOptions, activateImmediate?: boolean): Promise<ReadonlyArray<AuthenticationSession>>;
278 >
279 > /**
280 > * Creates an AuthenticationSession with the given provider and scopes
281 > * @param providerId The id of the provider
282 > * @param scopes The scopes to request
283 > * @param options Additional options for creating the session
284 > */
285 > createSession(providerId: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, options?: IAuthenticationCreateSessionOptions): Promise<AuthenticationSession>;
286 >
287 > /**
288 > * Removes the session with the given id from the provider with the given id
289 > * @param providerId The id of the provider
290 > * @param sessionId The id of the session to remove
291 > */
292 > removeSession(providerId: string, sessionId: string): Promise<void>;
293 >
294 > /**
295 > * Gets a provider id for a specified authorization server
296 > * @param authorizationServer The authorization server url that this provider is responsible for
297 > * @param resourceServer The resource server URI that should match the provider's resourceServer (if defined)
298 > */
299 > getOrActivateProviderIdForServer(authorizationServer: URI, resourceServer?: URI): Promise<string | undefined>;
300 >
301 > /**
302 > * Allows the ability register a delegate that will be used to start authentication providers
303 > * @param delegate The delegate to register
304 > */
305 > registerAuthenticationProviderHostDelegate(delegate: IAuthenticationProviderHostDelegate): IDisposable;
306 >
307 > /**
308 > * Creates a dynamic authentication provider for the given server metadata
309 > * @param serverMetadata The metadata for the server that is being authenticated against
310 > */
311 > createDynamicAuthenticationProvider(authorizationServer: URI, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined, clientId?: string, clientSecret?: string): Promise<IAuthenticationProvider | undefined>;
312 >
313 > /**
314 > * Gets or creates a built-in XAA (enterprise-managed, ID-JAG) authentication provider for the given
315 > * SSO issuer. Subsequent calls with the same issuer return the existing provider. The returned id
316 > * can be used with {@link getSessions}/{@link createSession} just like any other provider.
317 > *
318 > * @param issuer The OAuth/OIDC issuer URL (typically read from `mcp.enterpriseManagedAuth.idp`).
319 > */
320 > createOrGetXaaProvider(issuer: URI): Promise<string | undefined>;
321 > }
322 >
323 > export function isAuthenticationSession(thing: unknown): thing is AuthenticationSession {
324 if (typeof thing !== 'object' || !thing) {
325 return false;
349 return true;
350 }
352 > // TODO: Move this into MainThreadAuthentication
353 > export const IAuthenticationExtensionsService = createDecorator<IAuthenticationExtensionsService>('IAuthenticationExtensionsService');
354 > export interface IAuthenticationExtensionsService {
355 > readonly _serviceBrand: undefined;
356 >
357 > /**
358 > * Fires when an account preference for a specific provider has changed for the specified extensions. Does not fire when:
359 > * * An account preference is removed
360 > * * A session preference is changed (because it's deprecated)
361 > * * A session preference is removed (because it's deprecated)
362 > */
363 > readonly onDidChangeAccountPreference: Event<{ extensionIds: string[]; providerId: string }>;
364 > /**
365 > * Returns the accountName (also known as account.label) to pair with `IAuthenticationAccessService` to get the account preference
366 > * @param providerId The authentication provider id
367 > * @param extensionId The extension id to get the preference for
368 > * @returns The accountName of the preference, or undefined if there is no preference set
369 > */
370 > getAccountPreference(extensionId: string, providerId: string): string | undefined;
371 > /**
372 > * Sets the account preference for the given provider and extension
373 > * @param providerId The authentication provider id
374 > * @param extensionId The extension id to set the preference for
375 > * @param account The account to set the preference to
376 > */
377 > updateAccountPreference(extensionId: string, providerId: string, account: AuthenticationSessionAccount): void;
378 > /**
379 > * Removes the account preference for the given provider and extension
380 > * @param providerId The authentication provider id
381 > * @param extensionId The extension id to remove the preference for
382 > */
383 > removeAccountPreference(extensionId: string, providerId: string): void;
384 > /**
385 > * @deprecated Sets the session preference for the given provider and extension
386 > * @param providerId
387 > * @param extensionId
388 > * @param session
389 > */
390 > updateSessionPreference(providerId: string, extensionId: string, session: AuthenticationSession): void;
391 > /**
392 > * @deprecated Gets the session preference for the given provider and extension
393 > * @param providerId
394 > * @param extensionId
395 > * @param scopes
396 > */
397 > getSessionPreference(providerId: string, extensionId: string, scopes: string[]): string | undefined;
398 > /**
399 > * @deprecated Removes the session preference for the given provider and extension
400 > * @param providerId
401 > * @param extensionId
402 > * @param scopes
403 > */
404 > removeSessionPreference(providerId: string, extensionId: string, scopes: string[]): void;
405 > selectSession(providerId: string, extensionId: string, extensionName: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, possibleSessions: readonly AuthenticationSession[]): Promise<AuthenticationSession>;
406 > requestSessionAccess(providerId: string, extensionId: string, extensionName: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, possibleSessions: readonly AuthenticationSession[]): void;
407 > requestNewSession(providerId: string, scopeListOrRequest: ReadonlyArray<string> | IAuthenticationWwwAuthenticateRequest, extensionId: string, extensionName: string): Promise<void>;
408 > updateNewSessionRequests(providerId: string, addedSessions: readonly AuthenticationSession[]): void;
409 > }
410 >
411 > /**
412 > * Options passed to the authentication provider when asking for sessions.
413 > */
414 > export interface IAuthenticationProviderSessionOptions {
415 > /**
416 > * Whether the provider must avoid user interaction while resolving existing sessions.
417 > */
418 > silent?: boolean;
419 > /**
420 > * The account that is being asked about. If this is passed in, the provider should
421 > * attempt to return the sessions that are only related to this account.
422 > */
423 > account?: AuthenticationSessionAccount;
424 > /**
425 > * The authorization server that is being asked about. If this is passed in, the provider should
426 > * attempt to return sessions that are only related to this authorization server.
427 > */
428 > authorizationServer?: URI;
429 > /**
430 > * When specified, the authentication provider will request a token bound to this resource URI
431 > * (RFC 8707 resource indicator).
432 > */
433 > resource?: string;
434 > /**
435 > * The audience for the requested access token. Primarily used for OAuth Identity Assertion
436 > * Authorization Grant (ID-JAG, defined in `draft-ietf-oauth-identity-assertion-authz-grant` using RFC 8693 token-exchange semantics) flows where the audience identifies the authorization server of the resource that
437 > * will redeem the assertion (typically the resource's authorization server URL). Providers that do not understand audience-bound tokens should
438 > * ignore this option.
439 > */
440 > audience?: string;
441 > /**
442 > * Allows the authentication provider to take in additional parameters.
443 > * It is up to the provider to define what these parameters are and handle them.
444 > * This is useful for passing in additional information that is specific to the provider
445 > * and not part of the standard authentication flow.
446 > */
447 > [key: string]: any;
448 > }
449 >
450 > /**
451 > * Represents an authentication provider.
452 > */
453 > export interface IAuthenticationProvider {
454 > /**
455 > * The unique identifier of the authentication provider.
456 > */
457 > readonly id: string;
458 >
459 > /**
460 > * The display label of the authentication provider.
461 > */
462 > readonly label: string;
463 >
464 > /**
465 > * The resource server URI that this provider is responsible for, if any.
466 > * TODO@TylerLeonhardt: Rather than this being added to the provider, it should be passed in to
467 > * getSessions/createSession/etc... this way we can have providers that handle multiple resource servers.
468 > */
469 > readonly resourceServer?: URI;
470 >
471 > /**
472 > * The resolved authorization servers. These can still contain globs, but should be concrete URIs
473 > */
474 > readonly authorizationServers?: ReadonlyArray<URI>;
475 >
476 > /**
477 > * Indicates whether the authentication provider supports multiple accounts.
478 > */
479 > readonly supportsMultipleAccounts: boolean;
480 >
481 > /**
482 > * Optional function to provide a custom confirmation message for authentication prompts.
483 > * If not implemented, the default confirmation messages will be used.
484 > * @param extensionName - The name of the extension requesting authentication.
485 > * @param recreatingSession - Whether this is recreating an existing session.
486 > * @returns A custom confirmation message or undefined to use the default message.
487 > */
488 > readonly confirmation?: (extensionName: string, recreatingSession: boolean) => string | undefined;
489 >
490 > /**
491 > * An {@link Event} which fires when the array of sessions has changed, or data
492 > * within a session has changed.
493 > */
494 > readonly onDidChangeSessions: Event<AuthenticationSessionsChangeEvent>;
495 >
496 > /**
497 > * Retrieves a list of authentication sessions.
498 > * @param scopes - An optional list of scopes. If provided, the sessions returned should match these permissions, otherwise all sessions should be returned.
499 > * @param options - Additional options for getting sessions.
500 > * @returns A promise that resolves to an array of authentication sessions.
501 > */
502 > getSessions(scopes: string[] | undefined, options: IAuthenticationProviderSessionOptions): Promise<readonly AuthenticationSession[]>;
503 >
504 > /**
505 > * Prompts the user to log in.
506 > * If login is successful, the `onDidChangeSessions` event should be fired.
507 > * If login fails, a rejected promise should be returned.
508 > * If the provider does not support multiple accounts, this method should not be called if there is already an existing session matching the provided scopes.
509 > * @param scopes - A list of scopes that the new session should be created with.
510 > * @param options - Additional options for creating the session.
511 > * @returns A promise that resolves to an authentication session.
512 > */
513 > createSession(scopes: string[], options: IAuthenticationProviderSessionOptions): Promise<AuthenticationSession>;
514 >
515 > /**
516 > * Get existing sessions that match the given authentication constraints.
517 > *
518 > * @param constraint The authentication constraint containing challenges and optional scopes
519 > * @param options Options for the session request
520 > * @returns A thenable that resolves to an array of existing authentication sessions
521 > */
522 > getSessionsFromChallenges?(constraint: IAuthenticationConstraint, options: IAuthenticationProviderSessionOptions): Promise<readonly AuthenticationSession[]>;
523 >
524 > /**
525 > * Create a new session based on authentication constraints.
526 > * This is called when no existing session matches the constraint requirements.
527 > *
528 > * @param constraint The authentication constraint containing challenges and optional scopes
529 > * @param options Options for the session creation
530 > * @returns A thenable that resolves to a new authentication session
531 > */
532 > createSessionFromChallenges?(constraint: IAuthenticationConstraint, options: IAuthenticationProviderSessionOptions): Promise<AuthenticationSession>;
533 >
534 > /**
535 > * Removes the session corresponding to the specified session ID.
536 > * If the removal is successful, the `onDidChangeSessions` event should be fired.
537 > * If a session cannot be removed, the provider should reject with an error message.
538 > * @param sessionId - The ID of the session to remove.
539 > */
540 > removeSession(sessionId: string): Promise<void>;
541 > }
src/vs/base/common/strings.ts 488 covered LOC · 114 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++) { strings.ts
355 > const codeA = a.charCodeAt(aStart);
356 > const codeB = b.charCodeAt(bStart);
357 > if (codeA < codeB) {
358 return -1;
359 > } else if (codeA > codeB) { strings.ts
360 return 1;
361 }
362 > } strings.ts
363 > const aLen = aEnd - aStart; strings.ts
364 > const bLen = bEnd - bStart;
365 > if (aLen < bLen) {
366 return -1;
367 > } else if (aLen > bLen) { strings.ts
368 return 1;
369 }
370 > return 0; strings.ts
371 > }
372 > strings.ts
373 > export function compareIgnoreCase(a: string, b: string): number {
374 > return compareSubstringIgnoreCase(a, b, 0, a.length, 0, b.length); strings.ts
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 > strings.ts
379 > for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
380 >
381 > let codeA = a.charCodeAt(aStart);
382 > let codeB = b.charCodeAt(bStart);
383 >
384 > if (codeA === codeB) {
385 > // equal strings.ts
386 > continue;
387 > }
388
389 > if (codeA >= 128 || codeB >= 128) { strings.ts
390 // not ASCII letters -> fallback to lower-casing strings
391 return compareSubstring(a.toLowerCase(), b.toLowerCase(), aStart, aEnd, bStart, bEnd);
409 return diff;
410 }
411 > strings.ts
412 > const aLen = aEnd - aStart;
413 > const bLen = bEnd - bStart;
414 >
415 > if (aLen < bLen) {
416 return -1;
417 > } else if (aLen > bLen) { strings.ts
418 return 1;
419 }
420 > strings.ts
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/platform/extensions/common/extensions.ts 486 covered LOC · 25 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensions.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 Severity from '../../../base/common/severity.js';
7 > import * as strings from '../../../base/common/strings.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { ILocalizedString } from '../../action/common/action.js';
10 > import { ExtensionKind } from '../../environment/common/environment.js';
11 > import { createDecorator } from '../../instantiation/common/instantiation.js';
12 > import { getRemoteName } from '../../remote/common/remoteHosts.js';
13 >
14 > export const USER_MANIFEST_CACHE_FILE = 'extensions.user.cache';
15 > export const BUILTIN_MANIFEST_CACHE_FILE = 'extensions.builtin.cache';
16 > export const UNDEFINED_PUBLISHER = 'undefined_publisher';
17 >
18 > export interface ICommand {
19 > command: string;
20 > title: string | ILocalizedString;
21 > category?: string | ILocalizedString;
22 > }
23 >
24 > export interface IDebugger {
25 > label?: string;
26 > type: string;
27 > runtime?: string;
28 > }
29 >
30 > export interface IGrammar {
31 > language?: string;
32 > }
33 >
34 > export interface IJSONValidation {
35 > fileMatch: string | string[];
36 > url: string;
37 > }
38 >
39 > export interface IJSONValidationRegistry {
40 > url: string;
41 > }
42 >
43 > export interface IKeyBinding {
44 > command: string;
45 > key: string;
46 > when?: string;
47 > mac?: string;
48 > linux?: string;
49 > win?: string;
50 > }
51 >
52 > export interface ILanguage {
53 > id: string;
54 > extensions: string[];
55 > aliases: string[];
56 > }
57 >
58 > export interface IMenu {
59 > command: string;
60 > alt?: string;
61 > when?: string;
62 > group?: string;
63 > }
64 >
65 > export interface ISnippet {
66 > language: string;
67 > }
68 >
69 > export interface ITheme {
70 > label: string;
71 > }
72 >
73 > export interface IViewContainer {
74 > id: string;
75 > title: string;
76 > }
77 >
78 > export interface IView {
79 > id: string;
80 > name: string;
81 > }
82 >
83 > export interface IColor {
84 > id: string;
85 > description: string;
86 > defaults: { light: string; dark: string; highContrast: string };
87 > }
88 >
89 > interface IWebviewEditor {
90 > readonly viewType: string;
91 > readonly priority: string;
92 > readonly selector: readonly {
93 > readonly filenamePattern?: string;
94 > }[];
95 > }
96 >
97 > export interface ICodeActionContributionAction {
98 > readonly kind: string;
99 > readonly title: string;
100 > readonly description?: string;
101 > }
102 >
103 > export interface ICodeActionContribution {
104 > readonly languages: readonly string[];
105 > readonly actions: readonly ICodeActionContributionAction[];
106 > }
107 >
108 > export interface IAuthenticationContribution {
109 > readonly id: string;
110 > readonly label: string;
111 > readonly authorizationServerGlobs?: string[];
112 > }
113 >
114 > export interface IWalkthroughStep {
115 > readonly id: string;
116 > readonly title: string;
117 > readonly description: string | undefined;
118 > readonly media:
119 > | { image: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; svg?: never; video?: never }
120 > | { markdown: string; image?: never; svg?: never; video?: never }
121 > | { svg: string; altText: string; markdown?: never; image?: never; video?: never }
122 > | { video: string | { dark: string; light: string; hc: string }; poster: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; image?: never; svg?: never };
123 > readonly completionEvents?: string[];
124 > /** @deprecated use `completionEvents: 'onCommand:...'` */
125 > readonly doneOn?: { command: string };
126 > readonly when?: string;
127 > }
128 >
129 > export interface IWalkthrough {
130 > readonly id: string;
131 > readonly title: string;
132 > readonly icon?: string;
133 > readonly description: string;
134 > readonly steps: IWalkthroughStep[];
135 > readonly featuredFor: string[] | undefined;
136 > readonly when?: string;
137 > }
138 >
139 > export interface IStartEntry {
140 > readonly title: string;
141 > readonly description: string;
142 > readonly command: string;
143 > readonly when?: string;
144 > readonly category: 'file' | 'folder' | 'notebook';
145 > }
146 >
147 > export interface INotebookEntry {
148 > readonly type: string;
149 > readonly displayName: string;
150 > }
151 >
152 > export interface INotebookRendererContribution {
153 > readonly id: string;
154 > readonly displayName: string;
155 > readonly mimeTypes: string[];
156 > }
157 >
158 > export interface IDebugVisualizationContribution {
159 > readonly id: string;
160 > readonly when: string;
161 > }
162 >
163 > export interface ITranslation {
164 > id: string;
165 > path: string;
166 > }
167 >
168 > export interface ILocalizationContribution {
169 > languageId: string;
170 > languageName?: string;
171 > localizedLanguageName?: string;
172 > translations: ITranslation[];
173 > minimalTranslations?: { [key: string]: string };
174 > }
175 >
176 > export interface IChatParticipantContribution {
177 > id: string;
178 > name: string;
179 > fullName: string;
180 > description?: string;
181 > isDefault?: boolean;
182 > commands?: { name: string }[];
183 > }
184 >
185 > export interface IToolContribution {
186 > name: string;
187 > displayName: string;
188 > modelDescription: string;
189 > userDescription?: string;
190 > }
191 >
192 > export interface IToolSetContribution {
193 > name: string;
194 > referenceName: string;
195 > description: string;
196 > icon?: string;
197 > tools: string[];
198 > }
199 >
200 > export interface IMcpCollectionContribution {
201 > readonly id: string;
202 > readonly label: string;
203 > readonly when?: string;
204 > }
205 >
206 > export interface IChatFileContribution {
207 > readonly path: string;
208 > readonly name?: string;
209 > readonly description?: string;
210 > readonly when?: string;
211 > readonly sessionTypes?: readonly string[];
212 > }
213 >
214 > export interface IExtensionContributions {
215 > commands?: ICommand[];
216 > configuration?: any;
217 > configurationDefaults?: any;
218 > debuggers?: IDebugger[];
219 > grammars?: IGrammar[];
220 > jsonValidation?: IJSONValidation[];
221 > jsonValidationRegistry?: IJSONValidationRegistry[];
222 > keybindings?: IKeyBinding[];
223 > languages?: ILanguage[];
224 > menus?: { [context: string]: IMenu[] };
225 > snippets?: ISnippet[];
226 > themes?: ITheme[];
227 > iconThemes?: ITheme[];
228 > productIconThemes?: ITheme[];
229 > viewsContainers?: { [location: string]: IViewContainer[] };
230 > views?: { [location: string]: IView[] };
231 > colors?: IColor[];
232 > localizations?: ILocalizationContribution[];
233 > readonly customEditors?: readonly IWebviewEditor[];
234 > readonly codeActions?: readonly ICodeActionContribution[];
235 > authentication?: IAuthenticationContribution[];
236 > walkthroughs?: IWalkthrough[];
237 > startEntries?: IStartEntry[];
238 > readonly notebooks?: INotebookEntry[];
239 > readonly notebookRenderer?: INotebookRendererContribution[];
240 > readonly debugVisualizers?: IDebugVisualizationContribution[];
241 > readonly chatParticipants?: ReadonlyArray<IChatParticipantContribution>;
242 > readonly chatPromptFiles?: ReadonlyArray<IChatFileContribution>;
243 > readonly chatInstructions?: ReadonlyArray<IChatFileContribution>;
244 > readonly chatAgents?: ReadonlyArray<IChatFileContribution>;
245 > readonly chatSkills?: ReadonlyArray<IChatFileContribution>;
246 > readonly chatPlugins?: ReadonlyArray<IChatFileContribution>;
247 > readonly languageModelTools?: ReadonlyArray<IToolContribution>;
248 > readonly languageModelToolSets?: ReadonlyArray<IToolSetContribution>;
249 > readonly mcpServerDefinitionProviders?: ReadonlyArray<IMcpCollectionContribution>;
250 > }
251 >
252 > export interface IExtensionCapabilities {
253 > readonly virtualWorkspaces?: ExtensionVirtualWorkspaceSupport;
254 > readonly untrustedWorkspaces?: ExtensionUntrustedWorkspaceSupport;
255 > }
256 >
257 >
258 > export const ALL_EXTENSION_KINDS: readonly ExtensionKind[] = ['ui', 'workspace', 'web'];
259 >
260 > export type LimitedWorkspaceSupportType = 'limited';
261 > export type ExtensionUntrustedWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
262 > export type ExtensionUntrustedWorkspaceSupport = { supported: true } | { supported: false; description: string } | { supported: LimitedWorkspaceSupportType; description: string; restrictedConfigurations?: string[] };
263 >
264 > export type ExtensionVirtualWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
265 > export type ExtensionVirtualWorkspaceSupport = boolean | { supported: true } | { supported: false | LimitedWorkspaceSupportType; description: string };
266 >
267 > export function getWorkspaceSupportTypeMessage(supportType: ExtensionUntrustedWorkspaceSupport | ExtensionVirtualWorkspaceSupport | undefined): string | undefined {
268 if (typeof supportType === 'object' && supportType !== null) {
269 if (supportType.supported !== true) {
273 return undefined;
274 }
276 >
277 > export interface IExtensionIdentifier {
278 > id: string;
279 > uuid?: string;
280 > }
281 >
282 > export const EXTENSION_CATEGORIES = [
283 > 'AI',
284 > 'Azure',
285 > 'Chat',
286 > 'Data Science',
287 > 'Debuggers',
288 > 'Extension Packs',
289 > 'Education',
290 > 'Formatters',
291 > 'Keymaps',
292 > 'Language Packs',
293 > 'Linters',
294 > 'Machine Learning',
295 > 'Notebooks',
296 > 'Programming Languages',
297 > 'SCM Providers',
298 > 'Snippets',
299 > 'Testing',
300 > 'Themes',
301 > 'Visualization',
302 > 'Other',
303 > ];
304 >
305 > export interface IRelaxedExtensionManifest {
306 > name: string;
307 > displayName?: string;
308 > publisher: string;
309 > version: string;
310 > engines: { readonly vscode: string };
311 > description?: string;
312 > main?: string;
313 > type?: string;
314 > browser?: string;
315 > preview?: boolean;
316 > // For now this only supports pointing to l10n bundle files
317 > // but it will be used for package.l10n.json files in the future
318 > l10n?: string;
319 > icon?: string;
320 > categories?: string[];
321 > keywords?: string[];
322 > activationEvents?: readonly string[];
323 > extensionDependencies?: string[];
324 > extensionAffinity?: string[];
325 > extensionPack?: string[];
326 > extensionKind?: ExtensionKind | ExtensionKind[];
327 > contributes?: IExtensionContributions;
328 > repository?: { url: string };
329 > bugs?: { url: string };
330 > originalEnabledApiProposals?: readonly string[];
331 > enabledApiProposals?: readonly string[];
332 > api?: string;
333 > scripts?: { [key: string]: string };
334 > capabilities?: IExtensionCapabilities;
335 > }
336 >
337 > export type IExtensionManifest = Readonly<IRelaxedExtensionManifest>;
338 >
339 > export const enum ExtensionType {
340 > System,
341 > User
342 > }
343 >
344 > export const enum TargetPlatform {
345 > WIN32_X64 = 'win32-x64',
346 > WIN32_ARM64 = 'win32-arm64',
347 >
348 > LINUX_X64 = 'linux-x64',
349 > LINUX_ARM64 = 'linux-arm64',
350 > LINUX_ARMHF = 'linux-armhf',
351 >
352 > ALPINE_X64 = 'alpine-x64',
353 > ALPINE_ARM64 = 'alpine-arm64',
354 >
355 > DARWIN_X64 = 'darwin-x64',
356 > DARWIN_ARM64 = 'darwin-arm64',
357 >
358 > WEB = 'web',
359 >
360 > UNIVERSAL = 'universal',
361 > UNKNOWN = 'unknown',
362 > UNDEFINED = 'undefined',
363 > }
364 >
365 > export interface IExtension {
366 > readonly type: ExtensionType;
367 > readonly isBuiltin: boolean;
368 > readonly identifier: IExtensionIdentifier;
369 > readonly manifest: IExtensionManifest;
370 > readonly location: URI;
371 > readonly targetPlatform: TargetPlatform;
372 > readonly publisherDisplayName?: string;
373 > readonly readmeUrl?: URI;
374 > readonly changelogUrl?: URI;
375 > readonly isValid: boolean;
376 > readonly validations: readonly [Severity, string][];
377 > readonly preRelease: boolean;
378 > }
379 >
380 > /**
381 > * **!Do not construct directly!**
382 > *
383 > * **!Only static methods because it gets serialized!**
384 > *
385 > * This represents the "canonical" version for an extension identifier. Extension ids
386 > * have to be case-insensitive (due to the marketplace), but we must ensure case
387 > * preservation because the extension API is already public at this time.
388 > *
389 > * For example, given an extension with the publisher `"Hello"` and the name `"World"`,
390 > * its canonical extension identifier is `"Hello.World"`. This extension could be
391 > * referenced in some other extension's dependencies using the string `"hello.world"`.
392 > *
393 > * To make matters more complicated, an extension can optionally have an UUID. When two
394 > * extensions have the same UUID, they are considered equal even if their identifier is different.
395 > */
396 > export class ExtensionIdentifier {
397 > public readonly value: string;
398 >
399 > /**
400 > * Do not use directly. This is public to avoid mangling and thus
401 > * allow compatibility between running from source and a built version.
402 > */
403 > readonly _lower: string;
404 >
405 > constructor(value: string) {
406 > this.value = value; extensions.ts
407 > this._lower = value.toLowerCase();
408 > }
410 > public static equals(a: ExtensionIdentifier | string | null | undefined, b: ExtensionIdentifier | string | null | undefined) {
411 if (typeof a === 'undefined' || a === null) {
412 return (typeof b === 'undefined' || b === null);
426 return (a._lower === b._lower);
427 }
429 > /**
430 > * Gives the value by which to index (for equality).
431 > */
432 > public static toKey(id: ExtensionIdentifier | string): string {
433 if (typeof id === 'string') {
434 return id.toLowerCase();
436 return id._lower;
437 }
438 > } extensions.ts
439 >
440 > export class ExtensionIdentifierSet {
441 >
442 > private readonly _set = new Set<string>();
443 >
444 > public get size(): number {
445 > return this._set.size;
446 > }
447 >
448 > constructor(iterable?: Iterable<ExtensionIdentifier | string>) {
449 if (iterable) {
450 for (const value of iterable) {
453 }
454 }
456 > public add(id: ExtensionIdentifier | string): void {
457 this._set.add(ExtensionIdentifier.toKey(id));
458 }
460 > public delete(extensionId: ExtensionIdentifier): boolean {
461 return this._set.delete(ExtensionIdentifier.toKey(extensionId));
462 }
464 > public has(id: ExtensionIdentifier | string): boolean {
465 return this._set.has(ExtensionIdentifier.toKey(id));
466 }
467 > } extensions.ts
468 >
469 > export class ExtensionIdentifierMap<T> {
470
471 private readonly _map = new Map<string, T>();
473 > public clear(): void {
474 this._map.clear();
475 }
477 > public delete(id: ExtensionIdentifier | string): void {
478 this._map.delete(ExtensionIdentifier.toKey(id));
479 }
481 > public get(id: ExtensionIdentifier | string): T | undefined {
482 return this._map.get(ExtensionIdentifier.toKey(id));
483 }
485 > public has(id: ExtensionIdentifier | string): boolean {
486 return this._map.has(ExtensionIdentifier.toKey(id));
487 }
489 > public set(id: ExtensionIdentifier | string, value: T): void {
490 this._map.set(ExtensionIdentifier.toKey(id), value);
491 }
493 > public values(): IterableIterator<T> {
494 return this._map.values();
495 }
497 > forEach(callbackfn: (value: T, key: string, map: Map<string, T>) => void): void {
498 this._map.forEach(callbackfn);
499 }
501 > [Symbol.iterator](): IterableIterator<[string, T]> {
502 return this._map[Symbol.iterator]();
503 }
504 > } extensions.ts
505 >
506 > /**
507 > * An error that is clearly from an extension, identified by the `ExtensionIdentifier`
508 > */
509 > export class ExtensionError extends Error {
510 >
511 > readonly extension: ExtensionIdentifier;
512 >
513 > constructor(extensionIdentifier: ExtensionIdentifier, cause: Error, message?: string) {
514 const detail = message && cause?.message ? `${message}: ${cause.message}` : (message ?? cause?.message);
515 super(`Error in extension ${ExtensionIdentifier.toKey(extensionIdentifier)}: ${detail}`, { cause });
523 }
524 }
525 > } extensions.ts
526 >
527 > export interface IRelaxedExtensionDescription extends IRelaxedExtensionManifest {
528 > id?: string;
529 > identifier: ExtensionIdentifier;
530 > uuid?: string;
531 > publisherDisplayName?: string;
532 > targetPlatform: TargetPlatform;
533 > isBuiltin: boolean;
534 > isUserBuiltin: boolean;
535 > isUnderDevelopment: boolean;
536 > extensionLocation: URI;
537 > preRelease: boolean;
538 > }
539 >
540 > export type IExtensionDescription = Readonly<IRelaxedExtensionDescription>;
541 >
542 > export function isApplicationScopedExtension(manifest: IExtensionManifest): boolean {
543 return isLanguagePackExtension(manifest);
544 }
546 > export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {
547 return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;
548 }
550 > export function isAuthenticationProviderExtension(manifest: IExtensionManifest): boolean {
551 return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;
552 }
554 > export function isResolverExtension(manifest: IExtensionManifest, remoteAuthority: string | undefined): boolean {
555 if (remoteAuthority) {
556 const activationEvent = `onResolveRemoteAuthority:${getRemoteName(remoteAuthority)}`;
559 return false;
560 }
562 > export function parseEnabledApiProposalNames(enabledApiProposals: string[]): string[] {
563 return enabledApiProposals.map(proposal => proposal.split('@')[0]);
564 }
566 > export const IBuiltinExtensionsScannerService = createDecorator<IBuiltinExtensionsScannerService>('IBuiltinExtensionsScannerService');
567 > export interface IBuiltinExtensionsScannerService {
568 > readonly _serviceBrand: undefined;
569 > scanBuiltinExtensions(): Promise<IExtension[]>;
570 > }
src/vs/workbench/services/extensions/common/extensions.ts 476 covered LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensions.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 { Disposable, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
8 > import { raceTimeout } from '../../../../base/common/async.js';
9 > import Severity from '../../../../base/common/severity.js';
10 > import { URI } from '../../../../base/common/uri.js';
11 > import { IMessagePassingProtocol } from '../../../../base/parts/ipc/common/ipc.js';
12 > import { IAssignmentService } from '../../../../platform/assignment/common/assignment.js';
13 > import { getExtensionId, getGalleryExtensionId } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';
14 > import { ImplicitActivationEvents } from '../../../../platform/extensionManagement/common/implicitActivationEvents.js';
15 > import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, ExtensionType, IExtension, IExtensionContributions, IExtensionDescription, TargetPlatform } from '../../../../platform/extensions/common/extensions.js';
16 > import { ApiProposalName } from '../../../../platform/extensions/common/extensionsApiProposals.js';
17 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
18 > import { IV8Profile } from '../../../../platform/profiling/common/profiling.js';
19 > import { ExtensionHostKind } from './extensionHostKind.js';
20 > import { IExtensionDescriptionDelta, IExtensionDescriptionSnapshot } from './extensionHostProtocol.js';
21 > import { ExtensionRunningLocation } from './extensionRunningLocation.js';
22 > import { IExtensionPoint } from './extensionsRegistry.js';
23 >
24 > export const nullExtensionDescription = Object.freeze<IExtensionDescription>({
25 > identifier: new ExtensionIdentifier('nullExtensionDescription'),
26 > name: 'Null Extension Description',
27 > version: '0.0.0',
28 > publisher: 'vscode',
29 > engines: { vscode: '' },
30 > extensionLocation: URI.parse('void:location'),
31 > isBuiltin: false,
32 > targetPlatform: TargetPlatform.UNDEFINED,
33 > isUserBuiltin: false,
34 > isUnderDevelopment: false,
35 > preRelease: false,
36 > });
37 >
38 > export type WebWorkerExtHostConfigValue = boolean | 'auto';
39 > export const webWorkerExtHostConfig = 'extensions.webWorker';
40 >
41 > export const IExtensionService = createDecorator<IExtensionService>('extensionService');
42 >
43 > export interface IMessage {
44 > type: Severity;
45 > message: string;
46 > extensionId: ExtensionIdentifier;
47 > extensionPointId: string;
48 > }
49 >
50 > export interface IExtensionsStatus {
51 > id: ExtensionIdentifier;
52 > messages: IMessage[];
53 > activationStarted: boolean;
54 > activationTimes: ActivationTimes | undefined;
55 > runtimeErrors: Error[];
56 > runningLocation: ExtensionRunningLocation | null;
57 > }
58 >
59 > export class MissingExtensionDependency {
60 > constructor(readonly dependency: string) { }
61 > }
62 >
63 > /**
64 > * e.g.
65 > * ```
66 > * {
67 > * startTime: 1511954813493000,
68 > * endTime: 1511954835590000,
69 > * deltas: [ 100, 1500, 123456, 1500, 100000 ],
70 > * ids: [ 'idle', 'self', 'extension1', 'self', 'idle' ]
71 > * }
72 > * ```
73 > */
74 > export interface IExtensionHostProfile {
75 > /**
76 > * Profiling start timestamp in microseconds.
77 > */
78 > startTime: number;
79 > /**
80 > * Profiling end timestamp in microseconds.
81 > */
82 > endTime: number;
83 > /**
84 > * Duration of segment in microseconds.
85 > */
86 > deltas: number[];
87 > /**
88 > * Segment identifier: extension id or one of the four known strings.
89 > */
90 > ids: ProfileSegmentId[];
91 >
92 > /**
93 > * Get the information as a .cpuprofile.
94 > */
95 > data: IV8Profile;
96 >
97 > /**
98 > * Get the aggregated time per segmentId
99 > */
100 > getAggregatedTimes(): Map<ProfileSegmentId, number>;
101 > }
102 >
103 > export const enum ExtensionHostStartup {
104 > /**
105 > * The extension host should be launched immediately and doesn't require a `$startExtensionHost` call.
106 > */
107 > EagerAutoStart = 1,
108 > /**
109 > * The extension host should be launched immediately and needs a `$startExtensionHost` call.
110 > */
111 > EagerManualStart = 2,
112 > /**
113 > * The extension host should be launched lazily and only when it has extensions it needs to host. It doesn't require a `$startExtensionHost` call.
114 > */
115 > LazyAutoStart = 3,
116 > }
117 >
118 > export interface IExtensionInspectInfo {
119 > readonly port: number;
120 > readonly host: string;
121 > readonly devtoolsUrl?: string;
122 > readonly devtoolsLabel?: string;
123 > }
124 >
125 > export interface IExtensionHost {
126 > readonly pid: number | null;
127 > readonly runningLocation: ExtensionRunningLocation;
128 > readonly remoteAuthority: string | null;
129 > readonly startup: ExtensionHostStartup;
130 > /**
131 > * A collection of extensions which includes information about which
132 > * extension will execute or is executing on this extension host.
133 > * **NOTE**: this will reflect extensions correctly only after `start()` resolves.
134 > */
135 > readonly extensions: ExtensionHostExtensions | null;
136 > readonly onExit: Event<[number, string | null]>;
137 >
138 > start(): Promise<IMessagePassingProtocol>;
139 > getInspectPort(): IExtensionInspectInfo | undefined;
140 > enableInspectPort(): Promise<boolean>;
141 > disconnect?(): Promise<void>;
142 > dispose(): void;
143 > }
144 >
145 > export class ExtensionHostExtensions {
146 > private _versionId: number;
147 > private _allExtensions: IExtensionDescription[];
148 > private _myExtensions: ExtensionIdentifier[];
149 > private _myActivationEvents: Set<string> | null;
150 >
151 > public get versionId(): number {
152 return this._versionId;
153 }
155 > public get allExtensions(): IExtensionDescription[] {
156 return this._allExtensions;
157 }
159 > public get myExtensions(): ExtensionIdentifier[] {
160 return this._myExtensions;
161 }
163 > constructor(versionId: number, allExtensions: readonly IExtensionDescription[], myExtensions: ExtensionIdentifier[]) {
164 this._versionId = versionId;
165 this._allExtensions = allExtensions.slice(0);
167 this._myActivationEvents = null;
168 }
170 > toSnapshot(): IExtensionDescriptionSnapshot {
171 return {
172 versionId: this._versionId,
176 };
177 }
179 > public set(versionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): IExtensionDescriptionDelta {
180 if (this._versionId > versionId) {
181 throw new Error(`ExtensionHostExtensions: invalid versionId ${versionId} (current: ${this._versionId})`);
245 return delta;
246 }
248 > public delta(extensionsDelta: IExtensionDescriptionDelta): IExtensionDescriptionDelta | null {
249 if (this._versionId >= extensionsDelta.versionId) {
250 // ignore older deltas
281 return extensionsDelta;
282 }
284 > public containsExtension(extensionId: ExtensionIdentifier): boolean {
285 for (const myExtensionId of this._myExtensions) {
286 if (ExtensionIdentifier.equals(myExtensionId, extensionId)) {
290 return false;
291 }
293 > public containsActivationEvent(activationEvent: string): boolean {
294 if (!this._myActivationEvents) {
295 this._myActivationEvents = this._readMyActivationEvents();
297 return this._myActivationEvents.has(activationEvent);
298 }
300 > private _readMyActivationEvents(): Set<string> {
301 const result = new Set<string>();
302
314 return result;
315 }
316 > } extensions.ts
317 >
318 function extensionDescriptionArrayToMap(extensions: IExtensionDescription[]): ExtensionIdentifierMap<IExtensionDescription> {
319 const result = new ExtensionIdentifierMap<IExtensionDescription>();
323 return result;
324 }
326 > export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean {
327 if (!extension.enabledApiProposals) {
328 return false;
339 return enabled;
340 }
342 > export interface IProposedApiUsage {
343 > /**
344 > * The identifier of the extension that attempted to use the proposal.
345 > */
346 > readonly extensionId: string;
347 > /**
348 > * The name of the API proposal that the extension is not allowed to use.
349 > */
350 > readonly proposalName: ApiProposalName;
351 > }
352 >
353 > type ProposedApiUsageReporter = (usage: IProposedApiUsage) => void;
354 >
355 > let _proposedApiUsageReporter: ProposedApiUsageReporter | undefined;
356 > const _reportedProposedApiUsages = new Set<string>();
357 >
358 > /**
359 > * Registers a reporter that is invoked whenever an extension attempts to use a proposed API
360 > * that it has not declared via its `enabledApiProposals`-property. This is used to gather
361 > * telemetry about extensions that rely on proposed API they are not entitled to use.
362 > *
363 > * Each unique extension/proposal combination is reported at most once per session in order to
364 > * avoid flooding telemetry from the (potentially hot) call sites of {@link isProposedApiEnabled}.
365 > */
366 > export function setProposedApiUsageReporter(reporter: ProposedApiUsageReporter): IDisposable {
367 _proposedApiUsageReporter = reporter;
368 return toDisposable(() => {
372 });
373 }
375 function reportDisabledProposedApiUsage(extension: IExtensionDescription, proposal: ApiProposalName): void {
376 const reporter = _proposedApiUsageReporter;
385 reporter({ extensionId: extension.identifier.value, proposalName: proposal });
386 }
388 > type ProposedApiEnabledResolver = (extension: IExtensionDescription, proposal: ApiProposalName) => boolean;
389 >
390 > let _proposedApiEnabledResolver: ProposedApiEnabledResolver | undefined;
391 >
392 > /**
393 > * The name of the experiment ("treatment") that can grant proposed API access to
394 > * extension/proposal combinations that have not declared the proposal themselves.
395 > */
396 > export const enabledApiProposalsFallbackExperimentName = 'extensionEnabledApiProposalsFallback';
397 >
398 > /**
399 > * Experiment value that explicitly blocks all proposals reaching the fallback.
400 > */
401 > export const enabledApiProposalsFallbackNone = 'none';
402 >
403 > /**
404 > * Resolves the value of the {@link enabledApiProposalsFallbackExperimentName}-experiment, or
405 > * `undefined` when it does not apply (non-`stable` quality) or cannot be read in time.
406 > */
407 export async function resolveEnabledApiProposalsFallbackExperiment(assignmentService: IAssignmentService, quality: string | undefined): Promise<string | undefined> {
408 if (quality !== 'stable') {
419 }
420 }
422 > /**
423 > * Enables the {@link enabledApiProposalsFallbackExperimentName}-experiment which can grant proposed
424 > * API access to extension/proposal combinations that have not declared the proposal via their
425 > * `enabledApiProposals`-property. It only takes effect on `stable` builds.
426 > *
427 > * Note that the experiment only applies to extensions that already declare at least one proposal:
428 > * an extension with no `enabledApiProposals` at all is never granted access.
429 > *
430 > * @param value A comma-separated list of `publisher.extension:proposalName` entries. Any combination
431 > * that appears here will have {@link isProposedApiEnabled} return `true` even when the extension has
432 > * not declared that particular proposal. When unset, all proposals are allowed;
433 > * {@link enabledApiProposalsFallbackNone} blocks all proposals that reach the fallback.
434 > * @param quality The product quality. The experiment only takes effect when this is `stable`.
435 > */
436 > export function setEnabledApiProposalsFallbackExperiment(value: string | undefined, quality: string | undefined): IDisposable {
437 if (quality !== 'stable') {
438 return Disposable.None;
463 });
464 }
466 > export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void {
467 if (!isProposedApiEnabled(extension, proposal)) {
468 throw new Error(`Extension '${extension.identifier.value}' CANNOT use API proposal: ${proposal}.\nIts package.json#enabledApiProposals-property declares: ${extension.enabledApiProposals?.join(', ') ?? '[]'} but NOT ${proposal}.\n The missing proposal MUST be added and you must start in extension development mode or use the following command line switch: --enable-proposed-api ${extension.identifier.value}`);
469 }
470 }
472 >
473 > /**
474 > * Extension id or one of the four known program states.
475 > */
476 > export type ProfileSegmentId = string | 'idle' | 'program' | 'gc' | 'self';
477 >
478 > export interface ExtensionActivationReason {
479 > readonly startup: boolean;
480 > readonly extensionId: ExtensionIdentifier;
481 > readonly activationEvent: string;
482 > }
483 >
484 > export class ActivationTimes {
485 > constructor(
486 public readonly codeLoadingTime: number,
487 public readonly activateCallTime: number,
490 ) {
491 }
492 > } extensions.ts
493 >
494 > export class ExtensionPointContribution<T> {
495 > readonly description: IExtensionDescription;
496 > readonly value: T;
497 >
498 > constructor(description: IExtensionDescription, value: T) {
499 this.description = description;
500 this.value = value;
501 }
502 > } extensions.ts
503 >
504 > export interface IWillActivateEvent {
505 > readonly event: string;
506 > readonly activation: Promise<void>;
507 > readonly activationKind: ActivationKind;
508 > }
509 >
510 > export interface IResponsiveStateChangeEvent {
511 > extensionHostKind: ExtensionHostKind;
512 > isResponsive: boolean;
513 > /**
514 > * Return the inspect port or `0`. `0` means inspection is not possible.
515 > */
516 > getInspectListener(tryEnableInspector: boolean): Promise<IExtensionInspectInfo | undefined>;
517 > }
518 >
519 > export const enum ActivationKind {
520 > Normal = 0,
521 > Immediate = 1
522 > }
523 >
524 > export interface WillStopExtensionHostsEvent {
525 >
526 > /**
527 > * A human readable reason for stopping the extension hosts
528 > * that e.g. can be shown in a confirmation dialog to the
529 > * user.
530 > */
531 > readonly reason: string;
532 >
533 > /**
534 > * A flag to indicate if the operation was triggered automatically
535 > */
536 > readonly auto: boolean;
537 >
538 > /**
539 > * Allows to veto the stopping of extension hosts. The veto can be a long running
540 > * operation.
541 > *
542 > * @param reason a human readable reason for vetoing the extension host stop in case
543 > * where the resolved `value: true`.
544 > */
545 > veto(value: boolean | Promise<boolean>, reason: string): void;
546 > }
547 >
548 > export interface IExtensionService {
549 > readonly _serviceBrand: undefined;
550 >
551 > /**
552 > * An event emitted when extensions are registered after their extension points got handled.
553 > *
554 > * This event will also fire on startup to signal the installed extensions.
555 > *
556 > * @returns the extensions that got registered
557 > */
558 > readonly onDidRegisterExtensions: Event<void>;
559 >
560 > /**
561 > * @event
562 > * Fired when extensions status changes.
563 > * The event contains the ids of the extensions that have changed.
564 > */
565 > readonly onDidChangeExtensionsStatus: Event<ExtensionIdentifier[]>;
566 >
567 > /**
568 > * Fired when the available extensions change (i.e. when extensions are added or removed).
569 > */
570 > readonly onDidChangeExtensions: Event<{ readonly added: readonly IExtensionDescription[]; readonly removed: readonly IExtensionDescription[] }>;
571 >
572 > /**
573 > * All registered extensions.
574 > * - List will be empty initially during workbench startup and will be filled with extensions as they are registered
575 > * - Listen to `onDidChangeExtensions` event for any changes to the extensions list. It will change as extensions get registered or de-reigstered.
576 > * - Listen to `onDidRegisterExtensions` event or wait for `whenInstalledExtensionsRegistered` promise to get the initial list of registered extensions.
577 > */
578 > readonly extensions: readonly IExtensionDescription[];
579 >
580 > /**
581 > * An event that is fired when activation happens.
582 > */
583 > readonly onWillActivateByEvent: Event<IWillActivateEvent>;
584 >
585 > /**
586 > * An event that is fired when an extension host changes its
587 > * responsive-state.
588 > */
589 > readonly onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent>;
590 >
591 > /**
592 > * Fired before stop of extension hosts happens. Allows listeners to veto against the
593 > * stop to prevent it from happening.
594 > */
595 > readonly onWillStop: Event<WillStopExtensionHostsEvent>;
596 >
597 > /**
598 > * Send an activation event and activate interested extensions.
599 > *
600 > * This will wait for the normal startup of the extension host(s).
601 > *
602 > * In extraordinary circumstances, if the activation event needs to activate
603 > * one or more extensions before the normal startup is finished, then you can use
604 > * `ActivationKind.Immediate`. Please do not use this flag unless really necessary
605 > * and you understand all consequences.
606 > */
607 > activateByEvent(activationEvent: string, activationKind?: ActivationKind): Promise<void>;
608 >
609 > /**
610 > * Send an activation ID and activate interested extensions.
611 > *
612 > */
613 > activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
614 >
615 > /**
616 > * Determine if `activateByEvent(activationEvent)` has resolved already.
617 > *
618 > * i.e. the activation event is finished and all interested extensions are already active.
619 > */
620 > activationEventIsDone(activationEvent: string): boolean;
621 >
622 > /**
623 > * An promise that resolves when the installed extensions are registered after
624 > * their extension points got handled.
625 > */
626 > whenInstalledExtensionsRegistered(): Promise<boolean>;
627 >
628 > /**
629 > * Return a specific extension
630 > * @param id An extension id
631 > */
632 > getExtension(id: string): Promise<IExtensionDescription | undefined>;
633 >
634 > /**
635 > * Returns `true` if the given extension can be added. Otherwise `false`.
636 > * @param extension An extension
637 > */
638 > canAddExtension(extension: IExtensionDescription): boolean;
639 >
640 > /**
641 > * Returns `true` if the given extension can be removed. Otherwise `false`.
642 > * @param extension An extension
643 > */
644 > canRemoveExtension(extension: IExtensionDescription): boolean;
645 >
646 > /**
647 > * Read all contributions to an extension point.
648 > */
649 > readExtensionPointContributions<T extends IExtensionContributions[keyof IExtensionContributions]>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]>;
650 >
651 > /**
652 > * Get information about extensions status.
653 > */
654 > getExtensionsStatus(): { [id: string]: IExtensionsStatus };
655 >
656 > /**
657 > * Return the inspect ports (if inspection is possible) for extension hosts of kind `extensionHostKind`.
658 > */
659 > getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<IExtensionInspectInfo[]>;
660 >
661 > /**
662 > * Stops the extension hosts.
663 > *
664 > * @param reason a human readable reason for stopping the extension hosts. This maybe
665 > * can be presented to the user when showing dialogs.
666 > *
667 > * @param auto indicates if the operation was triggered by an automatic action
668 > *
669 > * @returns a promise that resolves to `true` if the extension hosts were stopped, `false`
670 > * if the operation was vetoed by listeners of the `onWillStop` event.
671 > */
672 > stopExtensionHosts(reason: string, auto?: boolean): Promise<boolean>;
673 >
674 > /**
675 > * Starts the extension hosts. If updates are provided, the extension hosts are started with the given updates.
676 > */
677 > startExtensionHosts(updates?: { readonly toAdd: readonly IExtension[]; readonly toRemove: readonly string[] }): Promise<void>;
678 >
679 > /**
680 > * Modify the environment of the remote extension host
681 > * @param env New properties for the remote extension host
682 > */
683 > setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
684 > }
685 >
686 > export interface IInternalExtensionService {
687 > _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
688 > _onWillActivateExtension(extensionId: ExtensionIdentifier): void;
689 > _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
690 > _onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void;
691 > _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void;
692 > }
693 >
694 > export interface ProfileSession {
695 > stop(): Promise<IExtensionHostProfile>;
696 > }
697 >
698 > export function toExtension(extensionDescription: IExtensionDescription): IExtension {
699 return {
700 type: extensionDescription.isBuiltin ? ExtensionType.System : ExtensionType.User,
710 };
711 }
713 > export function toExtensionDescription(extension: IExtension, isUnderDevelopment?: boolean): IExtensionDescription {
714 const id = getExtensionId(extension.manifest.publisher, extension.manifest.name);
715 return {
727 };
728 }
730 >
731 > export class NullExtensionService implements IExtensionService {
732 declare readonly _serviceBrand: undefined;
733 readonly onDidRegisterExtensions: Event<void> = Event.None;
738 readonly onWillStop: Event<WillStopExtensionHostsEvent> = Event.None;
739 readonly extensions = [];
740 > activateByEvent(_activationEvent: string): Promise<void> { return Promise.resolve(undefined); } extensions.ts
741 > activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> { return Promise.resolve(undefined); }
742 > activationEventIsDone(_activationEvent: string): boolean { return false; }
743 > whenInstalledExtensionsRegistered(): Promise<boolean> { return Promise.resolve(true); }
744 > getExtension() { return Promise.resolve(undefined); }
745 > readExtensionPointContributions<T>(_extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> { return Promise.resolve(Object.create(null)); }
746 > getExtensionsStatus(): { [id: string]: IExtensionsStatus } { return Object.create(null); }
747 > getInspectPorts(_extensionHostKind: ExtensionHostKind, _tryEnableInspector: boolean): Promise<IExtensionInspectInfo[]> { return Promise.resolve([]); }
748 > async stopExtensionHosts(): Promise<boolean> { return true; }
749 > async startExtensionHosts(): Promise<void> { }
750 > async setRemoteEnvironment(_env: { [key: string]: string | null }): Promise<void> { }
751 > canAddExtension(): boolean { return false; }
752 > canRemoveExtension(): boolean { return false; }
753 > }
src/vs/platform/storage/common/storage.ts 474 covered LOC · 51 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- storage.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 { Promises, RunOnceScheduler, runWhenGlobalIdle } from '../../../base/common/async.js';
7 > import { Emitter, Event, PauseableEmitter } from '../../../base/common/event.js';
8 > import { Disposable, DisposableStore, dispose, MutableDisposable } from '../../../base/common/lifecycle.js';
9 > import { mark } from '../../../base/common/performance.js';
10 > import { isUndefinedOrNull } from '../../../base/common/types.js';
11 > import { InMemoryStorageDatabase, IStorage, IStorageChangeEvent, Storage, StorageHint, StorageValue } from '../../../base/parts/storage/common/storage.js';
12 > import { createDecorator } from '../../instantiation/common/instantiation.js';
13 > import { isUserDataProfile, IUserDataProfile } from '../../userDataProfile/common/userDataProfile.js';
14 > import { IAnyWorkspaceIdentifier } from '../../workspace/common/workspace.js';
15 >
16 > export const IS_NEW_KEY = '__$__isNewStorageMarker';
17 > export const TARGET_KEY = '__$__targetStorageMarker';
18 >
19 > export const IStorageService = createDecorator<IStorageService>('storageService');
20 >
21 > export enum WillSaveStateReason {
22 >
23 > /**
24 > * No specific reason to save state.
25 > */
26 > NONE,
27 >
28 > /**
29 > * A hint that the workbench is about to shutdown.
30 > */
31 > SHUTDOWN
32 > }
33 >
34 > export interface IWillSaveStateEvent {
35 > readonly reason: WillSaveStateReason;
36 > }
37 >
38 > export interface IStorageEntry {
39 > readonly key: string;
40 > readonly value: StorageValue;
41 > readonly scope: StorageScope;
42 > readonly target: StorageTarget;
43 > }
44 >
45 > export interface IWorkspaceStorageValueChangeEvent extends IStorageValueChangeEvent {
46 > readonly scope: StorageScope.WORKSPACE;
47 > }
48 >
49 > export interface IProfileStorageValueChangeEvent extends IStorageValueChangeEvent {
50 > readonly scope: StorageScope.PROFILE;
51 > }
52 >
53 > export interface IApplicationStorageValueChangeEvent extends IStorageValueChangeEvent {
54 > readonly scope: StorageScope.APPLICATION;
55 > }
56 >
57 > export interface IApplicationSharedStorageValueChangeEvent extends IStorageValueChangeEvent {
58 > readonly scope: StorageScope.APPLICATION_SHARED;
59 > }
60 >
61 > export interface IStorageService {
62 >
63 > readonly _serviceBrand: undefined;
64 >
65 > /**
66 > * Emitted whenever data is updated or deleted on the given
67 > * scope and optional key.
68 > *
69 > * @param scope the `StorageScope` to listen to changes
70 > * @param key the optional key to filter for or all keys of
71 > * the scope if `undefined`
72 > */
73 > onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event<IWorkspaceStorageValueChangeEvent>;
74 > onDidChangeValue(scope: StorageScope.PROFILE, key: string | undefined, disposable: DisposableStore): Event<IProfileStorageValueChangeEvent>;
75 > onDidChangeValue(scope: StorageScope.APPLICATION, key: string | undefined, disposable: DisposableStore): Event<IApplicationStorageValueChangeEvent>;
76 > onDidChangeValue(scope: StorageScope.APPLICATION_SHARED, key: string | undefined, disposable: DisposableStore): Event<IApplicationSharedStorageValueChangeEvent>;
77 > onDidChangeValue(scope: StorageScope, key: string | undefined, disposable: DisposableStore): Event<IStorageValueChangeEvent>;
78 >
79 > /**
80 > * Emitted whenever target of a storage entry changes.
81 > */
82 > readonly onDidChangeTarget: Event<IStorageTargetChangeEvent>;
83 >
84 > /**
85 > * Emitted when the storage is about to persist. This is the right time
86 > * to persist data to ensure it is stored before the application shuts
87 > * down.
88 > *
89 > * The will save state event allows to optionally ask for the reason of
90 > * saving the state, e.g. to find out if the state is saved due to a
91 > * shutdown.
92 > *
93 > * Note: this event may be fired many times, not only on shutdown to prevent
94 > * loss of state in situations where the shutdown is not sufficient to
95 > * persist the data properly.
96 > */
97 > readonly onWillSaveState: Event<IWillSaveStateEvent>;
98 >
99 > /**
100 > * Retrieve an element stored with the given key from storage. Use
101 > * the provided `defaultValue` if the element is `null` or `undefined`.
102 > *
103 > * @param scope allows to define the scope of the storage operation
104 > * to either the current workspace only, all workspaces or all profiles.
105 > */
106 > get(key: string, scope: StorageScope, fallbackValue: string): string;
107 > get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined;
108 >
109 > /**
110 > * Retrieve an element stored with the given key from storage. Use
111 > * the provided `defaultValue` if the element is `null` or `undefined`.
112 > * The element will be converted to a `boolean`.
113 > *
114 > * @param scope allows to define the scope of the storage operation
115 > * to either the current workspace only, all workspaces or all profiles.
116 > */
117 > getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
118 > getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined;
119 >
120 > /**
121 > * Retrieve an element stored with the given key from storage. Use
122 > * the provided `defaultValue` if the element is `null` or `undefined`.
123 > * The element will be converted to a `number` using `parseInt` with a
124 > * base of `10`.
125 > *
126 > * @param scope allows to define the scope of the storage operation
127 > * to either the current workspace only, all workspaces or all profiles.
128 > */
129 > getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
130 > getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined;
131 >
132 > /**
133 > * Retrieve an element stored with the given key from storage. Use
134 > * the provided `defaultValue` if the element is `null` or `undefined`.
135 > * The element will be converted to a `object` using `JSON.parse`.
136 > *
137 > * @param scope allows to define the scope of the storage operation
138 > * to either the current workspace only, all workspaces or all profiles.
139 > */
140 > getObject<T extends object>(key: string, scope: StorageScope, fallbackValue: T): T;
141 > getObject<T extends object>(key: string, scope: StorageScope, fallbackValue?: T): T | undefined;
142 >
143 > /**
144 > * Store a value under the given key to storage. The value will be
145 > * converted to a `string`. Storing either `undefined` or `null` will
146 > * remove the entry under the key.
147 > *
148 > * @param scope allows to define the scope of the storage operation
149 > * to either the current workspace only, all workspaces or all profiles.
150 > *
151 > * @param target allows to define the target of the storage operation
152 > * to either the current machine or user.
153 > */
154 > store(key: string, value: StorageValue, scope: StorageScope, target: StorageTarget): void;
155 >
156 > /**
157 > * Allows to store multiple values in a bulk operation. Events will only
158 > * be emitted when all values have been stored.
159 > *
160 > * @param external a hint to indicate the source of the operation is external,
161 > * such as settings sync or profile changes.
162 > */
163 > storeAll(entries: Array<IStorageEntry>, external: boolean): void;
164 >
165 > /**
166 > * Delete an element stored under the provided key from storage.
167 > *
168 > * The scope argument allows to define the scope of the storage
169 > * operation to either the current workspace only, all workspaces
170 > * or all profiles.
171 > */
172 > remove(key: string, scope: StorageScope): void;
173 >
174 > /**
175 > * Returns all the keys used in the storage for the provided `scope`
176 > * and `target`.
177 > *
178 > * Note: this will NOT return all keys stored in the storage layer.
179 > * Some keys may not have an associated `StorageTarget` and thus
180 > * will be excluded from the results.
181 > *
182 > * @param scope allows to define the scope for the keys
183 > * to either the current workspace only, all workspaces or all profiles.
184 > *
185 > * @param target allows to define the target for the keys
186 > * to either the current machine or user.
187 > */
188 > keys(scope: StorageScope, target: StorageTarget): string[];
189 >
190 > /**
191 > * Log the contents of the storage to the console.
192 > */
193 > log(): void;
194 >
195 > /**
196 > * Returns true if the storage service handles the provided scope.
197 > */
198 > hasScope(scope: IAnyWorkspaceIdentifier | IUserDataProfile): boolean;
199 >
200 > /**
201 > * Switch storage to another workspace or profile. Optionally preserve the
202 > * current data to the new storage.
203 > */
204 > switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise<void>;
205 >
206 > /**
207 > * Whether the storage for the given scope was created during this session or
208 > * existed before.
209 > */
210 > isNew(scope: StorageScope): boolean;
211 >
212 > /**
213 > * Attempts to reduce the DB size via optimization commands if supported.
214 > */
215 > optimize(scope: StorageScope): Promise<void>;
216 >
217 > /**
218 > * Allows to flush state, e.g. in cases where a shutdown is
219 > * imminent. This will send out the `onWillSaveState` to ask
220 > * everyone for latest state.
221 > *
222 > * @returns a `Promise` that can be awaited on when all updates
223 > * to the underlying storage have been flushed.
224 > */
225 > flush(reason?: WillSaveStateReason): Promise<void>;
226 > }
227 >
228 > export const enum StorageScope {
229 >
230 > /**
231 > * The stored data will be scoped to all workspaces across all profiles
232 > * and shared across VS Code and Sessions app.
233 > */
234 > APPLICATION_SHARED = -2,
235 >
236 > /**
237 > * The stored data will be scoped to all workspaces across all profiles.
238 > */
239 > APPLICATION = -1,
240 >
241 > /**
242 > * The stored data will be scoped to all workspaces of the same profile.
243 > */
244 > PROFILE = 0,
245 >
246 > /**
247 > * The stored data will be scoped to the current workspace.
248 > */
249 > WORKSPACE = 1
250 > }
251 >
252 > export const enum StorageTarget {
253 >
254 > /**
255 > * The stored data is user specific and applies across machines.
256 > */
257 > USER,
258 >
259 > /**
260 > * The stored data is machine specific.
261 > */
262 > MACHINE
263 > }
264 >
265 > export interface IStorageValueChangeEvent {
266 >
267 > /**
268 > * The scope for the storage entry that changed
269 > * or was removed.
270 > */
271 > readonly scope: StorageScope;
272 >
273 > /**
274 > * The `key` of the storage entry that was changed
275 > * or was removed.
276 > */
277 > readonly key: string;
278 >
279 > /**
280 > * The `target` can be `undefined` if a key is being
281 > * removed.
282 > */
283 > readonly target: StorageTarget | undefined;
284 >
285 > /**
286 > * A hint how the storage change event was triggered. If
287 > * `true`, the storage change was triggered by an external
288 > * source, such as:
289 > * - another process (for example another window)
290 > * - operations such as settings sync or profiles change
291 > */
292 > readonly external?: boolean;
293 > }
294 >
295 > export interface IStorageTargetChangeEvent {
296 >
297 > /**
298 > * The scope for the target that changed. Listeners
299 > * should use `keys(scope, target)` to get an updated
300 > * list of keys for the given `scope` and `target`.
301 > */
302 > readonly scope: StorageScope;
303 > }
304 >
305 > interface IKeyTargets {
306 > [key: string]: StorageTarget;
307 > }
308 >
309 > export interface IStorageServiceOptions {
310 > readonly flushInterval: number;
311 > }
312 >
313 > export function loadKeyTargets(storage: IStorage): IKeyTargets {
314 const keysRaw = storage.get(TARGET_KEY);
315 if (keysRaw) {
323 return Object.create(null);
324 }
325 > storage.ts
326 > export abstract class AbstractStorageService extends Disposable implements IStorageService {
327 >
328 > declare readonly _serviceBrand: undefined;
329 >
330 > private static DEFAULT_FLUSH_INTERVAL = 60 * 1000; // every minute
331 >
332 > private readonly _onDidChangeValue = this._register(new PauseableEmitter<IStorageValueChangeEvent>());
333 >
334 > private readonly _onDidChangeTarget = this._register(new PauseableEmitter<IStorageTargetChangeEvent>());
335 > readonly onDidChangeTarget = this._onDidChangeTarget.event;
336 >
337 > private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
338 > readonly onWillSaveState = this._onWillSaveState.event;
339 >
340 > private initializationPromise: Promise<void> | undefined;
341 >
342 > private readonly flushWhenIdleScheduler: RunOnceScheduler;
343 > private readonly runFlushWhenIdle = this._register(new MutableDisposable());
344 >
345 > constructor(options: IStorageServiceOptions = { flushInterval: AbstractStorageService.DEFAULT_FLUSH_INTERVAL }) {
346 > super(); storage.ts
347 >
348 > this.flushWhenIdleScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), options.flushInterval));
349 > }
350 > storage.ts
351 > onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event<IWorkspaceStorageValueChangeEvent>;
352 > onDidChangeValue(scope: StorageScope.PROFILE, key: string | undefined, disposable: DisposableStore): Event<IProfileStorageValueChangeEvent>;
353 > onDidChangeValue(scope: StorageScope.APPLICATION, key: string | undefined, disposable: DisposableStore): Event<IApplicationStorageValueChangeEvent>;
354 > onDidChangeValue(scope: StorageScope.APPLICATION_SHARED, key: string | undefined, disposable: DisposableStore): Event<IApplicationSharedStorageValueChangeEvent>;
355 > onDidChangeValue(scope: StorageScope, key: string | undefined, disposable: DisposableStore): Event<IStorageValueChangeEvent> {
356 > return Event.filter(this._onDidChangeValue.event, e => e.scope === scope && (key === undefined || e.key === key), disposable); storage.ts
357 > }
358 > storage.ts
359 > private doFlushWhenIdle(): void {
360 this.runFlushWhenIdle.value = runWhenGlobalIdle(() => {
361 if (this.shouldFlushWhenIdle()) {
367 });
368 }
369 > storage.ts
370 > protected shouldFlushWhenIdle(): boolean {
371 return true;
372 }
373 > storage.ts
374 > protected stopFlushWhenIdle(): void {
375 dispose([this.runFlushWhenIdle, this.flushWhenIdleScheduler]);
376 }
377 > storage.ts
378 > initialize(): Promise<void> {
379 if (!this.initializationPromise) {
380 this.initializationPromise = (async () => {
402 return this.initializationPromise;
403 }
404 > storage.ts
405 > protected emitDidChangeValue(scope: StorageScope, event: IStorageChangeEvent): void {
406 const { key, external } = event;
407
434 }
435 }
436 > storage.ts
437 > protected emitWillSaveState(reason: WillSaveStateReason): void {
438 this._onWillSaveState.fire({ reason });
439 }
440 > storage.ts
441 > get(key: string, scope: StorageScope, fallbackValue: string): string;
442 > get(key: string, scope: StorageScope): string | undefined;
443 > get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined {
444 > return this.getStorage(scope)?.get(key, fallbackValue); storage.ts
445 > }
446 > storage.ts
447 > getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
448 > getBoolean(key: string, scope: StorageScope): boolean | undefined;
449 > getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined {
450 return this.getStorage(scope)?.getBoolean(key, fallbackValue);
451 }
452 > storage.ts
453 > getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
454 > getNumber(key: string, scope: StorageScope): number | undefined;
455 > getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined {
456 return this.getStorage(scope)?.getNumber(key, fallbackValue);
457 }
458 > storage.ts
459 > getObject(key: string, scope: StorageScope, fallbackValue: object): object;
460 > getObject(key: string, scope: StorageScope): object | undefined;
461 > getObject(key: string, scope: StorageScope, fallbackValue?: object): object | undefined {
462 return this.getStorage(scope)?.getObject(key, fallbackValue);
463 }
464 > storage.ts
465 > storeAll(entries: Array<IStorageEntry>, external: boolean): void {
466 this.withPausedEmitters(() => {
467 for (const entry of entries) {
470 });
471 }
472 > storage.ts
473 > store(key: string, value: StorageValue, scope: StorageScope, target: StorageTarget, external = false): void {
474
475 // We remove the key for undefined/null values
489 });
490 }
491 > storage.ts
492 > remove(key: string, scope: StorageScope, external = false): void {
493
494 // Update our datastructures but send events only after
502 });
503 }
504 > storage.ts
505 > private withPausedEmitters(fn: Function): void {
506
507 // Pause emitters
518 }
519 }
520 > storage.ts
521 > keys(scope: StorageScope, target: StorageTarget): string[] {
522 const keys: string[] = [];
523
532 return keys;
533 }
534 > storage.ts
535 > private updateKeyTarget(key: string, scope: StorageScope, target: StorageTarget | undefined, external = false): void {
536
537 // Add
552 }
553 }
554 > storage.ts
555 > private _workspaceKeyTargets: IKeyTargets | undefined = undefined;
556 > private get workspaceKeyTargets(): IKeyTargets {
557 if (!this._workspaceKeyTargets) {
558 this._workspaceKeyTargets = this.loadKeyTargets(StorageScope.WORKSPACE);
561 return this._workspaceKeyTargets;
562 }
563 > storage.ts
564 > private _profileKeyTargets: IKeyTargets | undefined = undefined;
565 > private get profileKeyTargets(): IKeyTargets {
566 if (!this._profileKeyTargets) {
567 this._profileKeyTargets = this.loadKeyTargets(StorageScope.PROFILE);
570 return this._profileKeyTargets;
571 }
572 > storage.ts
573 > private _applicationKeyTargets: IKeyTargets | undefined = undefined;
574 > private get applicationKeyTargets(): IKeyTargets {
575 if (!this._applicationKeyTargets) {
576 this._applicationKeyTargets = this.loadKeyTargets(StorageScope.APPLICATION);
579 return this._applicationKeyTargets;
580 }
581 > storage.ts
582 > private _applicationSharedKeyTargets: IKeyTargets | undefined = undefined;
583 > private get applicationSharedKeyTargets(): IKeyTargets {
584 if (!this._applicationSharedKeyTargets) {
585 this._applicationSharedKeyTargets = this.loadKeyTargets(StorageScope.APPLICATION_SHARED);
588 return this._applicationSharedKeyTargets;
589 }
590 > storage.ts
591 > private getKeyTargets(scope: StorageScope): IKeyTargets {
592 switch (scope) {
593 case StorageScope.APPLICATION_SHARED:
601 }
602 }
603 > storage.ts
604 > private loadKeyTargets(scope: StorageScope): { [key: string]: StorageTarget } {
605 const storage = this.getStorage(scope);
606
607 return storage ? loadKeyTargets(storage) : Object.create(null);
608 }
609 > storage.ts
610 > isNew(scope: StorageScope): boolean {
611 return this.getBoolean(IS_NEW_KEY, scope) === true;
612 }
613 > storage.ts
614 > async flush(reason = WillSaveStateReason.NONE): Promise<void> {
615
616 // Signal event to collect changes
646 }
647 }
648 > storage.ts
649 > async log(): Promise<void> {
650 const applicationItems = this.getStorage(StorageScope.APPLICATION)?.items ?? new Map<string, string>();
651 const applicationSharedItems = this.getStorage(StorageScope.APPLICATION_SHARED)?.items ?? new Map<string, string>();
664 );
665 }
666 > storage.ts
667 > async optimize(scope: StorageScope): Promise<void> {
668
669 // Await pending data to be flushed to the DB
673 return this.getStorage(scope)?.optimize();
674 }
675 > storage.ts
676 > async switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise<void> {
677
678 // Signal as event so that clients can store data before we switch
685 return this.switchToWorkspace(to, preserveData);
686 }
687 > storage.ts
688 > protected canSwitchProfile(from: IUserDataProfile, to: IUserDataProfile): boolean {
689 if (from.id === to.id) {
690 return false; // both profiles are same
697 return true;
698 }
699 > storage.ts
700 > protected switchData(oldStorage: Map<string, string>, newStorage: IStorage, scope: StorageScope): void {
701 this.withPausedEmitters(() => {
702 // Signal storage keys that have changed
718 });
719 }
720 > storage.ts
721 > // --- abstract
722 >
723 > abstract hasScope(scope: IAnyWorkspaceIdentifier | IUserDataProfile): boolean;
724 >
725 > protected abstract doInitialize(): Promise<void>;
726 >
727 > protected abstract getStorage(scope: StorageScope): IStorage | undefined;
728 >
729 > protected abstract getLogDetails(scope: StorageScope): string | undefined;
730 >
731 > protected abstract switchToProfile(toProfile: IUserDataProfile, preserveData: boolean): Promise<void>;
732 > protected abstract switchToWorkspace(toWorkspace: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise<void>;
733 > }
734 >
735 > export function isProfileUsingDefaultStorage(profile: IUserDataProfile): boolean {
736 return profile.isDefault || !!profile.useDefaultFlags?.globalState;
737 }
738 > storage.ts
739 > export class InMemoryStorageService extends AbstractStorageService {
740 >
741 > private readonly applicationStorage = this._register(new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY }));
742 > private readonly applicationSharedStorage = this._register(new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY }));
743 > private readonly profileStorage = this._register(new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY }));
744 > private readonly workspaceStorage = this._register(new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY }));
745 >
746 > constructor() {
747 > super(); storage.ts
748 >
749 > this._register(this.workspaceStorage.onDidChangeStorage(e => this.emitDidChangeValue(StorageScope.WORKSPACE, e)));
750 > this._register(this.profileStorage.onDidChangeStorage(e => this.emitDidChangeValue(StorageScope.PROFILE, e)));
751 > this._register(this.applicationStorage.onDidChangeStorage(e => this.emitDidChangeValue(StorageScope.APPLICATION, e)));
752 > this._register(this.applicationSharedStorage.onDidChangeStorage(e => this.emitDidChangeValue(StorageScope.APPLICATION_SHARED, e)));
753 > }
754 > storage.ts
755 > protected getStorage(scope: StorageScope): IStorage {
756 > switch (scope) { storage.ts
757 > case StorageScope.APPLICATION_SHARED:
758 return this.applicationSharedStorage;
759 > case StorageScope.APPLICATION: storage.ts
760 return this.applicationStorage;
761 > case StorageScope.PROFILE: storage.ts
762 return this.profileStorage;
763 > default: storage.ts
764 > return this.workspaceStorage; storage.ts
765 > } storage.ts
766 > }
767 > storage.ts
768 > protected getLogDetails(scope: StorageScope): string | undefined {
769 switch (scope) {
770 case StorageScope.APPLICATION_SHARED:
778 }
779 }
780 > storage.ts
781 > protected async doInitialize(): Promise<void> { }
782 >
783 > protected async switchToProfile(): Promise<void> {
784 // no-op when in-memory
785 }
786 > storage.ts
787 > protected async switchToWorkspace(): Promise<void> {
788 // no-op when in-memory
789 }
790 > storage.ts
791 > protected override shouldFlushWhenIdle(): boolean {
792 return false;
793 }
794 > storage.ts
795 > hasScope(scope: IAnyWorkspaceIdentifier | IUserDataProfile): boolean {
796 return false;
797 }
798 > } storage.ts
799 >
800 export async function logStorage(application: Map<string, string>, applicationShared: Map<string, string>, profile: Map<string, string>, workspace: Map<string, string>, applicationPath: string, applicationSharedPath: string, profilePath: string, workspacePath: string): Promise<void> {
801 const safeParse = (value: string) => {
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/platform/log/common/log.ts 447 covered LOC · 73 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- log.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 { toErrorMessage } from '../../../base/common/errorMessage.js';
8 > import { Emitter, Event } from '../../../base/common/event.js';
9 > import { hash } from '../../../base/common/hash.js';
10 > import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
11 > import { ResourceMap } from '../../../base/common/map.js';
12 > import { isWindows } from '../../../base/common/platform.js';
13 > import { joinPath } from '../../../base/common/resources.js';
14 > import { Mutable, isNumber, isString } from '../../../base/common/types.js';
15 > import { URI } from '../../../base/common/uri.js';
16 > import { ILocalizedString } from '../../action/common/action.js';
17 > import { RawContextKey } from '../../contextkey/common/contextkey.js';
18 > import { IEnvironmentService } from '../../environment/common/environment.js';
19 > import { createDecorator } from '../../instantiation/common/instantiation.js';
20 >
21 > export const ILogService = createDecorator<ILogService>('logService');
22 > export const ILoggerService = createDecorator<ILoggerService>('loggerService');
23 >
24 function now(): string {
25 return new Date().toISOString();
26 }
27 > log.ts
28 > export function isLogLevel(thing: unknown): thing is LogLevel {
29 return isNumber(thing);
30 }
31 > log.ts
32 > export enum LogLevel {
33 > Off,
34 > Trace,
35 > Debug,
36 > Info,
37 > Warning,
38 > Error
39 > }
40 >
41 > export const DEFAULT_LOG_LEVEL: LogLevel = LogLevel.Info;
42 >
43 > export interface ILogger extends IDisposable {
44 > readonly onDidChangeLogLevel: Event<LogLevel>;
45 > getLevel(): LogLevel;
46 > setLevel(level: LogLevel): void;
47 >
48 > trace(message: string, ...args: unknown[]): void;
49 > debug(message: string, ...args: unknown[]): void;
50 > info(message: string, ...args: unknown[]): void;
51 > warn(message: string, ...args: unknown[]): void;
52 > error(message: string | Error, ...args: unknown[]): void;
53 >
54 > /**
55 > * An operation to flush the contents. Can be synchronous.
56 > */
57 > flush(): void;
58 > }
59 >
60 > export function canLog(loggerLevel: LogLevel, messageLevel: LogLevel): boolean {
61 return loggerLevel !== LogLevel.Off && loggerLevel <= messageLevel;
62 }
63 > log.ts
64 > export function log(logger: ILogger, level: LogLevel, message: string): void {
65 switch (level) {
66 case LogLevel.Trace: logger.trace(message); break;
73 }
74 }
75 > log.ts
76 > type ConsoleMethod = 'debug' | 'error' | 'info' | 'log' | 'warn';
77 > type ConsoleMethodFn = (...args: unknown[]) => void;
78 >
79 > /**
80 > * Flag to enable forwarding of console.* calls to the log service in development.
81 > * This is intended for the use of agents to quickly instrument the code with console.logs
82 > * which will end up in the log service's file outputs.
83 > */
84 > export const isDevConsoleLogForwardingEnabled = false
85 > // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this
86 > ;
87 >
88 > let isConsoleForwarding = false;
89 > let isLogServiceConsoleEcho = false;
90 >
91 function getConsoleMethod(method: ConsoleMethod): ConsoleMethodFn {
92 switch (method) {
98 }
99 }
100 > log.ts
101 function setConsoleMethod(method: ConsoleMethod, fn: ConsoleMethodFn): void {
102 switch (method) {
108 }
109 }
110 > log.ts
111 function logToConsole(method: ConsoleMethod, ...args: unknown[]): void {
112 if (isConsoleForwarding) {
120 }
121 }
122 > log.ts
123 > export function registerDevConsoleLogForwarder(logService: ILogService): IDisposable {
124 const originalConsoleMethods: Record<ConsoleMethod, ConsoleMethodFn> = {
125 debug: console.debug,
177 });
178 }
179 > log.ts
180 > export function format(args: any, verbose: boolean = false): string {
181 let result = '';
182
199 return result;
200 }
201 > log.ts
202 > export type LoggerGroup = {
203 > readonly id: string;
204 > readonly name: string;
205 > };
206 >
207 > export interface ILogService extends ILogger {
208 > readonly _serviceBrand: undefined;
209 > }
210 >
211 > export interface ILoggerOptions {
212 >
213 > /**
214 > * Id of the logger.
215 > */
216 > id?: string;
217 >
218 > /**
219 > * Name of the logger.
220 > */
221 > name?: string;
222 >
223 > /**
224 > * Do not create rotating files if max size exceeds.
225 > */
226 > donotRotate?: boolean;
227 >
228 > /**
229 > * Do not use formatters.
230 > */
231 > donotUseFormatters?: boolean;
232 >
233 > /**
234 > * When to log. Set to `always` to log always.
235 > */
236 > logLevel?: 'always' | LogLevel;
237 >
238 > /**
239 > * Whether the log should be hidden from the user.
240 > */
241 > hidden?: boolean;
242 >
243 > /**
244 > * Condition which must be true to show this logger
245 > */
246 > when?: string;
247 >
248 > /**
249 > * Id of the extension that created this logger.
250 > */
251 > extensionId?: string;
252 >
253 > /**
254 > * Group of the logger.
255 > */
256 > group?: LoggerGroup;
257 > }
258 >
259 > export interface ILoggerResource {
260 > readonly resource: URI;
261 > readonly id: string;
262 > readonly name?: string;
263 > readonly logLevel?: LogLevel;
264 > readonly hidden?: boolean;
265 > readonly when?: string;
266 > readonly extensionId?: string;
267 > readonly group?: LoggerGroup;
268 > }
269 >
270 > export type DidChangeLoggersEvent = {
271 > readonly added: Iterable<ILoggerResource>;
272 > readonly removed: Iterable<ILoggerResource>;
273 > };
274 >
275 > export interface ILoggerService {
276 >
277 > readonly _serviceBrand: undefined;
278 >
279 > /**
280 > * Creates a logger for the given resource, or gets one if it already exists.
281 > *
282 > * This will also register the logger with the logger service.
283 > */
284 > createLogger(resource: URI, options?: ILoggerOptions): ILogger;
285 >
286 > /**
287 > * Creates a logger with the given id in the logs folder, or gets one if it already exists.
288 > *
289 > * This will also register the logger with the logger service.
290 > */
291 > createLogger(id: string, options?: Omit<ILoggerOptions, 'id'>): ILogger;
292 >
293 > /**
294 > * Gets an existing logger, if any.
295 > */
296 > getLogger(resourceOrId: URI | string): ILogger | undefined;
297 >
298 > /**
299 > * An event which fires when the log level of a logger has changed
300 > */
301 > readonly onDidChangeLogLevel: Event<LogLevel | [URI, LogLevel]>;
302 >
303 > /**
304 > * Set default log level.
305 > */
306 > setLogLevel(level: LogLevel): void;
307 >
308 > /**
309 > * Set log level for a logger.
310 > */
311 > setLogLevel(resource: URI, level: LogLevel): void;
312 >
313 > /**
314 > * Get log level for a logger or the default log level.
315 > */
316 > getLogLevel(resource?: URI): LogLevel;
317 >
318 > /**
319 > * An event which fires when the visibility of a logger has changed
320 > */
321 > readonly onDidChangeVisibility: Event<[URI, boolean]>;
322 >
323 > /**
324 > * Set the visibility of a logger.
325 > */
326 > setVisibility(resourceOrId: URI | string, visible: boolean): void;
327 >
328 > /**
329 > * An event which fires when the logger resources are changed
330 > */
331 > readonly onDidChangeLoggers: Event<DidChangeLoggersEvent>;
332 >
333 > /**
334 > * Register a logger with the logger service.
335 > *
336 > * Note that this will not create a logger, but only register it.
337 > *
338 > * Use `createLogger` to create a logger and register it.
339 > *
340 > * Use it when you want to register a logger that is not created by the logger service.
341 > */
342 > registerLogger(resource: ILoggerResource): void;
343 >
344 > /**
345 > * Deregister the logger for the given resource.
346 > */
347 > deregisterLogger(idOrResource: URI | string): void;
348 >
349 > /**
350 > * Get all registered loggers
351 > */
352 > getRegisteredLoggers(): Iterable<ILoggerResource>;
353 >
354 > /**
355 > * Get the registered logger for the given resource.
356 > */
357 > getRegisteredLogger(resource: URI): ILoggerResource | undefined;
358 > }
359 >
360 > export abstract class AbstractLogger extends Disposable implements ILogger {
361
362 private level: LogLevel = DEFAULT_LOG_LEVEL;
363 private readonly _onDidChangeLogLevel: Emitter<LogLevel> = this._register(new Emitter<LogLevel>());
364 > get onDidChangeLogLevel(): Event<LogLevel> { return this._onDidChangeLogLevel.event; } log.ts
365 >
366 > setLevel(level: LogLevel): void {
367 if (this.level !== level) {
368 this.level = level;
370 }
371 }
372 > log.ts
373 > getLevel(): LogLevel {
374 return this.level;
375 }
376 > log.ts
377 > protected checkLogLevel(level: LogLevel): boolean {
378 return canLog(this.level, level);
379 }
380 > log.ts
381 > protected canLog(level: LogLevel): boolean {
382 if (this._store.isDisposed) {
383 return false;
385 return this.checkLogLevel(level);
386 }
387 > log.ts
388 > abstract trace(message: string, ...args: unknown[]): void;
389 > abstract debug(message: string, ...args: unknown[]): void;
390 > abstract info(message: string, ...args: unknown[]): void;
391 > abstract warn(message: string, ...args: unknown[]): void;
392 > abstract error(message: string | Error, ...args: unknown[]): void;
393 > abstract flush(): void;
394 > }
395 >
396 > export abstract class AbstractMessageLogger extends AbstractLogger implements ILogger {
397 >
398 > constructor(private readonly logAlways?: boolean) {
399 super();
400 }
401 > log.ts
402 > protected override checkLogLevel(level: LogLevel): boolean {
403 return this.logAlways || super.checkLogLevel(level);
404 }
405 > log.ts
406 > trace(message: string, ...args: unknown[]): void {
407 if (this.canLog(LogLevel.Trace)) {
408 this.log(LogLevel.Trace, format([message, ...args], true));
409 }
410 }
411 > log.ts
412 > debug(message: string, ...args: unknown[]): void {
413 if (this.canLog(LogLevel.Debug)) {
414 this.log(LogLevel.Debug, format([message, ...args]));
415 }
416 }
417 > log.ts
418 > info(message: string, ...args: unknown[]): void {
419 if (this.canLog(LogLevel.Info)) {
420 this.log(LogLevel.Info, format([message, ...args]));
421 }
422 }
423 > log.ts
424 > warn(message: string, ...args: unknown[]): void {
425 if (this.canLog(LogLevel.Warning)) {
426 this.log(LogLevel.Warning, format([message, ...args]));
427 }
428 }
429 > log.ts
430 > error(message: string | Error, ...args: unknown[]): void {
431 if (this.canLog(LogLevel.Error)) {
432 if (message instanceof Error) {
439 }
440 }
441 > log.ts
442 > flush(): void { }
443 >
444 > protected abstract log(level: LogLevel, message: string): void;
445 > }
446 >
447 >
448 > export class ConsoleMainLogger extends AbstractLogger implements ILogger {
449 >
450 > private useColors: boolean;
451 >
452 > constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
453 super();
454 this.setLevel(logLevel);
455 this.useColors = !isWindows;
456 }
457 > log.ts
458 > trace(message: string, ...args: unknown[]): void {
459 if (this.canLog(LogLevel.Trace)) {
460 if (this.useColors) {
465 }
466 }
467 > log.ts
468 > debug(message: string, ...args: unknown[]): void {
469 if (this.canLog(LogLevel.Debug)) {
470 if (this.useColors) {
475 }
476 }
477 > log.ts
478 > info(message: string, ...args: unknown[]): void {
479 if (this.canLog(LogLevel.Info)) {
480 if (this.useColors) {
485 }
486 }
487 > log.ts
488 > warn(message: string | Error, ...args: unknown[]): void {
489 if (this.canLog(LogLevel.Warning)) {
490 if (this.useColors) {
495 }
496 }
497 > log.ts
498 > error(message: string, ...args: unknown[]): void {
499 if (this.canLog(LogLevel.Error)) {
500 if (this.useColors) {
505 }
506 }
507 > log.ts
508 > flush(): void {
509 // noop
510 }
511 > log.ts
512 > }
513 >
514 > export class ConsoleLogger extends AbstractLogger implements ILogger {
515 >
516 > constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL, private readonly useColors: boolean = true) {
517 super();
518 this.setLevel(logLevel);
519 }
520 > log.ts
521 > trace(message: string, ...args: unknown[]): void {
522 if (this.canLog(LogLevel.Trace)) {
523 if (this.useColors) {
528 }
529 }
530 > log.ts
531 > debug(message: string, ...args: unknown[]): void {
532 if (this.canLog(LogLevel.Debug)) {
533 if (this.useColors) {
538 }
539 }
540 > log.ts
541 > info(message: string, ...args: unknown[]): void {
542 if (this.canLog(LogLevel.Info)) {
543 if (this.useColors) {
548 }
549 }
550 > log.ts
551 > warn(message: string | Error, ...args: unknown[]): void {
552 if (this.canLog(LogLevel.Warning)) {
553 if (this.useColors) {
558 }
559 }
560 > log.ts
561 > error(message: string, ...args: unknown[]): void {
562 if (this.canLog(LogLevel.Error)) {
563 if (this.useColors) {
568 }
569 }
570 > log.ts
571 >
572 > flush(): void {
573 // noop
574 }
575 > } log.ts
576 >
577 > export class AdapterLogger extends AbstractLogger implements ILogger {
578 >
579 > constructor(private readonly adapter: { log: (logLevel: LogLevel, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
580 super();
581 this.setLevel(logLevel);
582 }
583 > log.ts
584 > trace(message: string, ...args: unknown[]): void {
585 if (this.canLog(LogLevel.Trace)) {
586 this.adapter.log(LogLevel.Trace, [this.extractMessage(message), ...args]);
587 }
588 }
589 > log.ts
590 > debug(message: string, ...args: unknown[]): void {
591 if (this.canLog(LogLevel.Debug)) {
592 this.adapter.log(LogLevel.Debug, [this.extractMessage(message), ...args]);
593 }
594 }
595 > log.ts
596 > info(message: string, ...args: unknown[]): void {
597 if (this.canLog(LogLevel.Info)) {
598 this.adapter.log(LogLevel.Info, [this.extractMessage(message), ...args]);
599 }
600 }
601 > log.ts
602 > warn(message: string | Error, ...args: unknown[]): void {
603 if (this.canLog(LogLevel.Warning)) {
604 this.adapter.log(LogLevel.Warning, [this.extractMessage(message), ...args]);
605 }
606 }
607 > log.ts
608 > error(message: string | Error, ...args: unknown[]): void {
609 if (this.canLog(LogLevel.Error)) {
610 this.adapter.log(LogLevel.Error, [this.extractMessage(message), ...args]);
611 }
612 }
613 > log.ts
614 > private extractMessage(msg: string | Error): string {
615 if (typeof msg === 'string') {
616 return msg;
619 return toErrorMessage(msg, this.canLog(LogLevel.Trace));
620 }
621 > log.ts
622 > flush(): void {
623 // noop
624 }
625 > } log.ts
626 >
627 > export class MultiplexLogger extends AbstractLogger implements ILogger {
628 >
629 > constructor(private readonly loggers: ReadonlyArray<ILogger>) {
630 super();
631 if (loggers.length) {
633 }
634 }
635 > log.ts
636 > override setLevel(level: LogLevel): void {
637 for (const logger of this.loggers) {
638 logger.setLevel(level);
640 super.setLevel(level);
641 }
642 > log.ts
643 > trace(message: string, ...args: unknown[]): void {
644 for (const logger of this.loggers) {
645 logger.trace(message, ...args);
646 }
647 }
648 > log.ts
649 > debug(message: string, ...args: unknown[]): void {
650 for (const logger of this.loggers) {
651 logger.debug(message, ...args);
652 }
653 }
654 > log.ts
655 > info(message: string, ...args: unknown[]): void {
656 for (const logger of this.loggers) {
657 logger.info(message, ...args);
658 }
659 }
660 > log.ts
661 > warn(message: string, ...args: unknown[]): void {
662 for (const logger of this.loggers) {
663 logger.warn(message, ...args);
664 }
665 }
666 > log.ts
667 > error(message: string | Error, ...args: unknown[]): void {
668 for (const logger of this.loggers) {
669 logger.error(message, ...args);
670 }
671 }
672 > log.ts
673 > flush(): void {
674 for (const logger of this.loggers) {
675 logger.flush();
676 }
677 }
678 > log.ts
679 > override dispose(): void {
680 for (const logger of this.loggers) {
681 logger.dispose();
683 super.dispose();
684 }
685 > } log.ts
686 >
687 > type LoggerEntry = { logger: ILogger | undefined; info: Mutable<ILoggerResource> };
688 >
689 > export abstract class AbstractLoggerService extends Disposable implements ILoggerService {
690 >
691 > declare readonly _serviceBrand: undefined;
692 >
693 > private readonly _loggers = new ResourceMap<LoggerEntry>();
694 >
695 > private _onDidChangeLoggers = this._register(new Emitter<{ added: ILoggerResource[]; removed: ILoggerResource[] }>);
696 > readonly onDidChangeLoggers = this._onDidChangeLoggers.event;
697 >
698 > private _onDidChangeLogLevel = this._register(new Emitter<LogLevel | [URI, LogLevel]>);
699 > readonly onDidChangeLogLevel = this._onDidChangeLogLevel.event;
700 >
701 > private _onDidChangeVisibility = this._register(new Emitter<[URI, boolean]>);
702 > readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
703 >
704 > constructor(
705 protected logLevel: LogLevel,
706 private readonly logsHome: URI,
714 }
715 }
716 > log.ts
717 > private getLoggerEntry(resourceOrId: URI | string): LoggerEntry | undefined {
718 if (isString(resourceOrId)) {
719 return [...this._loggers.values()].find(logger => logger.info.id === resourceOrId);
721 return this._loggers.get(resourceOrId);
722 }
723 > log.ts
724 > getLogger(resourceOrId: URI | string): ILogger | undefined {
725 return this.getLoggerEntry(resourceOrId)?.logger;
726 }
727 > log.ts
728 > createLogger(idOrResource: URI | string, options?: ILoggerOptions): ILogger {
729 const resource = this.toResource(idOrResource);
730 const id = isString(idOrResource) ? idOrResource : (options?.id ?? hash(resource.toString()).toString(16));
752 return logger;
753 }
754 > log.ts
755 > protected toResource(idOrResource: string | URI): URI {
756 return isString(idOrResource) ? joinPath(this.logsHome, `${idOrResource.replace(/[\\/:\*\?"<>\|]/g, '')}.log`) : idOrResource;
757 }
758 > log.ts
759 > setLogLevel(logLevel: LogLevel): void;
760 > setLogLevel(resource: URI, logLevel: LogLevel): void;
761 > setLogLevel(arg1: any, arg2?: any): void {
762 if (URI.isUri(arg1)) {
763 const resource = arg1;
780 }
781 }
782 > log.ts
783 > setVisibility(resourceOrId: URI | string, visibility: boolean): void {
784 const logger = this.getLoggerEntry(resourceOrId);
785 if (logger && visibility !== !logger.info.hidden) {
789 }
790 }
791 > log.ts
792 > getLogLevel(resource?: URI): LogLevel {
793 let logLevel;
794 if (resource) {
797 return logLevel ?? this.logLevel;
798 }
799 > log.ts
800 > registerLogger(resource: ILoggerResource): void {
801 const existing = this._loggers.get(resource.resource);
802 if (existing) {
809 }
810 }
811 > log.ts
812 > deregisterLogger(idOrResource: URI | string): void {
813 const resource = this.toResource(idOrResource);
814 const existing = this._loggers.get(resource);
821 }
822 }
823 > log.ts
824 > *getRegisteredLoggers(): Iterable<ILoggerResource> {
825 for (const entry of this._loggers.values()) {
826 yield entry.info;
827 }
828 }
829 > log.ts
830 > getRegisteredLogger(resource: URI): ILoggerResource | undefined {
831 return this._loggers.get(resource)?.info;
832 }
833 > log.ts
834 > override dispose(): void {
835 this._loggers.forEach(logger => logger.logger?.dispose());
836 this._loggers.clear();
837 super.dispose();
838 }
839 > log.ts
840 > protected abstract doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger;
841 > }
842 >
843 > export class NullLogger implements ILogger {
844 > readonly onDidChangeLogLevel: Event<LogLevel> = new Emitter<LogLevel>().event; log.ts
845 > setLevel(level: LogLevel): void { } log.ts
846 > getLevel(): LogLevel { return LogLevel.Info; }
847 > trace(message: string, ...args: unknown[]): void { }
848 > debug(message: string, ...args: unknown[]): void { }
849 > info(message: string, ...args: unknown[]): void { }
850 > warn(message: string, ...args: unknown[]): void { }
851 > error(message: string | Error, ...args: unknown[]): void { }
852 > critical(message: string | Error, ...args: unknown[]): void { }
853 > dispose(): void { }
854 > flush(): void { }
855 > }
856 >
857 > export class NullLogService extends NullLogger implements ILogService {
858 > declare readonly _serviceBrand: undefined;
859 > }
860 >
861 > export class NullLoggerService extends AbstractLoggerService {
862 > constructor() {
863 super(LogLevel.Off, URI.parse('log:///log'));
864 }
865 > protected override doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { log.ts
866 return new NullLogger();
867 }
868 > } log.ts
869 >
870 > export function getLogLevel(environmentService: IEnvironmentService): LogLevel {
871 if (environmentService.verbose) {
872 return LogLevel.Trace;
880 return DEFAULT_LOG_LEVEL;
881 }
882 > log.ts
883 > export function LogLevelToString(logLevel: LogLevel): string {
884 > switch (logLevel) {
885 > case LogLevel.Trace: return 'trace';
886 > case LogLevel.Debug: return 'debug';
887 > case LogLevel.Info: return 'info';
888 > case LogLevel.Warning: return 'warn';
889 > case LogLevel.Error: return 'error';
890 > case LogLevel.Off: return 'off';
891 > }
892 > }
893 >
894 > export function LogLevelToLocalizedString(logLevel: LogLevel): ILocalizedString {
895 switch (logLevel) {
896 case LogLevel.Trace: return { original: 'Trace', value: nls.localize('trace', "Trace") };
902 }
903 }
904 > log.ts
905 > export function parseLogLevel(logLevel: string): LogLevel | undefined {
906 switch (logLevel) {
907 case 'trace':
922 return undefined;
923 }
924 > log.ts
925 > // Contexts
926 > export const CONTEXT_LOG_LEVEL = new RawContextKey<string>('logLevel', LogLevelToString(LogLevel.Info));
src/vs/base/common/uri.ts 433 covered LOC · 78 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 { uri.ts
16 >
17 > // scheme, must be set
18 > if (!ret.scheme && _strict) {
19 throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`);
20 }
21 > uri.ts
22 > // scheme, https://tools.ietf.org/html/rfc3986#section-3.1
23 > // ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
24 > if (ret.scheme && !_schemePattern.test(ret.scheme)) { uri.ts
25 const matches = [...ret.scheme.matchAll(/[^\w\d+.-]/gu)];
26 const detail = matches.length > 0
29 throw new Error(`[UriError]: Scheme contains illegal characters.${detail} (len:${ret.scheme.length})`);
30 }
31 > uri.ts
32 > // path, http://tools.ietf.org/html/rfc3986#section-3.3
33 > // If a URI contains an authority component, then the path component
34 > // must either be empty or begin with a slash ("/") character. If a URI
35 > // does not contain an authority component, then the path cannot begin
36 > // with two slash characters ("//").
37 > if (ret.path) {
38 > if (ret.authority) { uri.ts
39 if (!_singleSlashStart.test(ret.path)) {
40 throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character');
41 }
42 > } else { uri.ts
43 > if (_doubleSlashStart.test(ret.path)) { uri.ts
44 throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")');
45 }
46 > } uri.ts
47 > } uri.ts
48 > } uri.ts
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 { uri.ts
55 > if (!scheme && !_strict) {
56 return 'file';
57 }
58 > return scheme; uri.ts
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 { uri.ts
63 >
64 > // the slash-character is our 'default base' as we don't
65 > // support constructing URIs relative to other URIs. This
66 > // also means that we alter and potentially break paths.
67 > // see https://tools.ietf.org/html/rfc3986#section-5.1.4
68 > switch (scheme) {
69 > case 'https':
70 > case 'http':
71 > case 'file':
72 > if (!path) { uri.ts
73 path = _slash;
74 > } else if (path[0] !== _slash) { uri.ts
75 path = _slash + path;
76 }
77 > break; uri.ts
78 > } uri.ts
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 > uri.ts
163 > if (typeof schemeOrData === 'object') {
164 this.scheme = schemeOrData.scheme || _empty;
165 this.authority = schemeOrData.authority || _empty;
170 // that creates uri components.
171 // _validateUri(this);
172 > } else { uri.ts
173 > this.scheme = _schemeFix(schemeOrData, _strict);
174 > this.authority = authority || _empty;
175 > this.path = _referenceResolution(this.scheme, path || _empty);
176 > this.query = query || _empty;
177 > this.fragment = fragment || _empty;
178 >
179 > _validateUri(this, _strict);
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); uri.ts
273 > if (!match) {
274 return new Uri(_empty, _empty, _empty, _empty, _empty);
275 }
276 > return new Uri( uri.ts
277 > match[2] || _empty,
278 > percentDecode(match[4] || _empty),
279 > percentDecode(match[5] || _empty),
280 > percentDecode(match[7] || _empty),
281 > percentDecode(match[9] || _empty),
282 > _strict
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 > uri.ts
309 > let authority = _empty;
310 >
311 > // normalize to fwd-slashes on windows,
312 > // on other systems bwd-slashes are valid
313 > // filename character, eg /f\oo/ba\r.txt
314 > if (isWindows) {
315 path = path.replace(/\\/g, _slash);
316 }
317 > uri.ts
318 > // check for authority as used in UNC shares
319 > // or use the path as given
320 > if (path[0] === _slash && path[1] === _slash) {
321 const idx = path.indexOf(_slash, 2);
322 if (idx === -1) {
328 }
329 }
330 > uri.ts
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 { uri.ts
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) { uri.ts
469 > if (!this._formatted) { uri.ts
470 > this._formatted = _asFormatted(this, false);
471 > }
472 > return this._formatted;
473 > } else { uri.ts
474 // we don't cache that
475 return _asFormatted(this, true);
476 }
477 > } uri.ts
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 { uri.ts
542 > let res: string | undefined = undefined;
543 > let nativeEncodePos = -1;
544 >
545 > for (let pos = 0; pos < uriComponent.length; pos++) {
546 > const code = uriComponent.charCodeAt(pos);
547 >
548 > // unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3
549 > if (
550 > (code >= CharCode.a && code <= CharCode.z)
551 > || (code >= CharCode.A && code <= CharCode.Z) uri.ts
552 > || (code >= CharCode.Digit0 && code <= CharCode.Digit9)
553 > || code === CharCode.Dash uri.ts
554 > || code === CharCode.Period uri.ts
555 > || code === CharCode.Underline
556 > || code === CharCode.Tilde
557 > || (isPath && code === CharCode.Slash)
558 || (isAuthority && code === CharCode.OpenSquareBracket)
559 || (isAuthority && code === CharCode.CloseSquareBracket)
560 || (isAuthority && code === CharCode.Colon)
561 > ) { uri.ts
562 > // check if we are delaying native encode
563 > if (nativeEncodePos !== -1) {
564 res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos));
565 nativeEncodePos = -1;
566 }
567 > // check if we write into a new string (by default we try to return the param) uri.ts
568 > if (res !== undefined) {
569 res += uriComponent.charAt(pos);
570 }
571 > uri.ts
572 > } else {
573 // encoding needed, we need to allocate a new string
574 if (res === undefined) {
594 }
595 }
596 > } uri.ts
597 >
598 > if (nativeEncodePos !== -1) {
599 res += encodeURIComponent(uriComponent.substring(nativeEncodePos));
600 }
601 > uri.ts
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 { uri.ts
657 >
658 > const encoder = !skipEncoding
659 > ? encodeURIComponentFast uri.ts
660 : encodeURIComponentMinimal;
661 > uri.ts
662 > let res = '';
663 > let { scheme, authority, path, query, fragment } = uri;
664 > if (scheme) {
665 > res += scheme;
666 > res += ':';
667 > }
668 > if (authority || scheme === 'file') {
669 > res += _slash; uri.ts
670 > res += _slash;
671 > }
672 > if (authority) { uri.ts
673 let idx = authority.indexOf('@');
674 if (idx !== -1) {
697 }
698 }
699 > if (path) { uri.ts
700 > // lower-case windows drive letters in /C:/fff or C:/fff uri.ts
701 > if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) {
702 const code = path.charCodeAt(1);
703 if (code >= CharCode.A && code <= CharCode.Z) {
704 path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3
705 }
706 > } else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) { uri.ts
707 const code = path.charCodeAt(0);
708 if (code >= CharCode.A && code <= CharCode.Z) {
710 }
711 }
712 > // encode the rest of the path uri.ts
713 > res += encoder(path, true, false);
714 > }
715 > if (query) { uri.ts
716 res += '?';
717 res += encoder(query, false, false);
718 }
719 > if (fragment) { uri.ts
720 res += '#';
721 res += !skipEncoding ? encodeURIComponentFast(fragment, false, false) : fragment;
722 }
723 > return res; uri.ts
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 { uri.ts
743 > if (!str.match(_rEncodedAsHex)) {
744 > return str;
745 > }
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/base/common/arrays.ts 408 covered LOC · 75 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)); arrays.ts
693 > }
694 > arrays.ts
695 > export function tieBreakComparators<TItem>(...comparators: Comparator<TItem>[]): Comparator<TItem> {
696 > return (item1, item2) => { arrays.ts
697 for (const comparator of comparators) {
698 const result = comparator(item1, item2);
703 return CompareResult.neitherLessOrGreaterThan;
704 };
705 > } arrays.ts
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/workbench/services/editor/common/editorService.ts 396 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editorService.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 { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
7 > import { IResourceEditorInput, IEditorOptions, IResourceEditorInputIdentifier, ITextResourceEditorInput } from '../../../../platform/editor/common/editor.js';
8 > import { IEditorPane, GroupIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, ITextDiffEditorPane, IEditorIdentifier, ISaveOptions, IRevertOptions, EditorsOrder, IVisibleEditorPane, IEditorCloseEvent, IUntypedEditorInput, IFindEditorOptions, IEditorWillOpenEvent, ITextResourceDiffEditorInput } from '../../../common/editor.js';
9 > import { EditorInput } from '../../../common/editor/editorInput.js';
10 > import { Event } from '../../../../base/common/event.js';
11 > import { IEditor, IDiffEditor } from '../../../../editor/common/editorCommon.js';
12 > import { ICloseEditorOptions, IEditorGroup, IEditorGroupsContainer, isEditorGroup } from './editorGroupsService.js';
13 > import { URI } from '../../../../base/common/uri.js';
14 > import { IGroupModelChangeEvent } from '../../../common/editor/editorGroupModel.js';
15 > import { DisposableStore } from '../../../../base/common/lifecycle.js';
16 >
17 > export const IEditorService = createDecorator<IEditorService>('editorService');
18 >
19 > /**
20 > * Open an editor in the currently active group.
21 > */
22 > export const ACTIVE_GROUP = -1;
23 > export type ACTIVE_GROUP_TYPE = typeof ACTIVE_GROUP;
24 >
25 > /**
26 > * Open an editor to the side of the active group.
27 > */
28 > export const SIDE_GROUP = -2;
29 > export type SIDE_GROUP_TYPE = typeof SIDE_GROUP;
30 >
31 > /**
32 > * Open an editor in a new auxiliary window.
33 > */
34 > export const AUX_WINDOW_GROUP = -3;
35 > export type AUX_WINDOW_GROUP_TYPE = typeof AUX_WINDOW_GROUP;
36 >
37 > /**
38 > * Open an editor in a modal overlay on top of the workbench.
39 > */
40 > export const MODAL_GROUP = -4;
41 > export type MODAL_GROUP_TYPE = typeof MODAL_GROUP;
42 >
43 > /**
44 > * Setting that controls whether editors open in a modal editor part.
45 > */
46 > export const USE_MODAL_EDITOR_SETTING = 'workbench.editor.useModal';
47 >
48 > /**
49 > * Possible values for the `workbench.editor.useModal` setting:
50 > * - `'off'`: never open editors modal (user opt-out, honored over `RequiresModal`)
51 > * - `'some'`: open modal only for editors that request it (e.g. `RequiresModal`)
52 > * - `'all'`: open all editors modal
53 > */
54 > export type UseModalEditorMode = 'off' | 'some' | 'all';
55 >
56 > export type PreferredGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | AUX_WINDOW_GROUP_TYPE | MODAL_GROUP_TYPE;
57 >
58 > export function isPreferredGroup(obj: unknown): obj is PreferredGroup {
59 const candidate = obj as PreferredGroup | undefined;
60
61 return typeof obj === 'number' || isEditorGroup(candidate);
62 }
64 > export interface ISaveEditorsOptions extends ISaveOptions {
65 >
66 > /**
67 > * If true, will ask for a location of the editor to save to.
68 > */
69 > readonly saveAs?: boolean;
70 > }
71 >
72 > export interface ISaveEditorsResult {
73 >
74 > /**
75 > * Whether the save operation was successful.
76 > */
77 > readonly success: boolean;
78 >
79 > /**
80 > * Resulting editors after the save operation.
81 > */
82 > readonly editors: Array<EditorInput | IUntypedEditorInput>;
83 > }
84 >
85 > export interface IUntypedEditorReplacement {
86 >
87 > /**
88 > * The editor to replace.
89 > */
90 > readonly editor: EditorInput;
91 >
92 > /**
93 > * The replacement for the editor.
94 > */
95 > readonly replacement: IUntypedEditorInput;
96 >
97 > /**
98 > * Skips asking the user for confirmation and doesn't
99 > * save the document. Only use this if you really need to!
100 > */
101 > forceReplaceDirty?: boolean;
102 > }
103 >
104 > export interface IBaseSaveRevertAllEditorOptions {
105 >
106 > /**
107 > * Whether to include untitled editors as well.
108 > */
109 > readonly includeUntitled?: {
110 >
111 > /**
112 > * Whether to include scratchpad editors.
113 > * Scratchpads are not included if not specified.
114 > */
115 > readonly includeScratchpad: boolean;
116 >
117 > } | boolean;
118 >
119 > /**
120 > * Whether to exclude sticky editors.
121 > */
122 > readonly excludeSticky?: boolean;
123 > }
124 >
125 > export interface ISaveAllEditorsOptions extends ISaveEditorsOptions, IBaseSaveRevertAllEditorOptions { }
126 >
127 > export interface IRevertAllEditorsOptions extends IRevertOptions, IBaseSaveRevertAllEditorOptions { }
128 >
129 > export interface IOpenEditorsOptions {
130 >
131 > /**
132 > * Whether to validate trust when opening editors
133 > * that are potentially not inside the workspace.
134 > */
135 > readonly validateTrust?: boolean;
136 > }
137 >
138 > export interface IEditorsChangeEvent {
139 > /**
140 > * The group which had the editor change
141 > */
142 > groupId: GroupIdentifier;
143 > /*
144 > * The event fired from the model
145 > */
146 > event: IGroupModelChangeEvent;
147 > }
148 >
149 > export interface IVisibleEditorsChangeEvent {
150 >
151 > /**
152 > * Indicates whether the visibility change is the result of an explicit
153 > * user action (`true`) or happened automatically as a side effect
154 > * (e.g. the chat agent opening files it has edited).
155 > */
156 > readonly isExplicit: boolean;
157 > }
158 >
159 > export interface IEditorService {
160 >
161 > readonly _serviceBrand: undefined;
162 >
163 > /**
164 > * Emitted when the currently active editor changes.
165 > *
166 > * @see {@link IEditorService.activeEditorPane}
167 > */
168 > readonly onDidActiveEditorChange: Event<void>;
169 >
170 > /**
171 > * Emitted when any of the current visible editors changes.
172 > *
173 > * @see {@link IEditorService.visibleEditorPanes}
174 > */
175 > readonly onDidVisibleEditorsChange: Event<IVisibleEditorsChangeEvent>;
176 >
177 > /**
178 > * An aggregated event for any change to any editor across
179 > * all groups.
180 > */
181 > readonly onDidEditorsChange: Event<IEditorsChangeEvent>;
182 >
183 > /**
184 > * Emitted when an editor is about to open.
185 > */
186 > readonly onWillOpenEditor: Event<IEditorWillOpenEvent>;
187 >
188 > /**
189 > * Emitted when an editor is closed.
190 > */
191 > readonly onDidCloseEditor: Event<IEditorCloseEvent>;
192 >
193 > /**
194 > * The currently active editor pane or `undefined` if none. The editor pane is
195 > * the workbench container for editors of any kind.
196 > *
197 > * @see {@link IEditorService.activeEditor} for access to the active editor input
198 > */
199 > readonly activeEditorPane: IVisibleEditorPane | undefined;
200 >
201 > /**
202 > * The currently active editor or `undefined` if none. An editor is active when it is
203 > * located in the currently active editor group. It will be `undefined` if the active
204 > * editor group has no editors open.
205 > */
206 > readonly activeEditor: EditorInput | undefined;
207 >
208 > /**
209 > * The currently active text editor control or `undefined` if there is currently no active
210 > * editor or the active editor widget is neither a text nor a diff editor.
211 > *
212 > * @see {@link IEditorService.activeEditor}
213 > */
214 > readonly activeTextEditorControl: IEditor | IDiffEditor | undefined;
215 >
216 > /**
217 > * The currently active text editor language id or `undefined` if there is currently no active
218 > * editor or the active editor control is neither a text nor a diff editor. If the active
219 > * editor is a diff editor, the modified side's language id will be taken.
220 > */
221 > readonly activeTextEditorLanguageId: string | undefined;
222 >
223 > /**
224 > * All editor panes that are currently visible across all editor groups.
225 > *
226 > * @see {@link IEditorService.visibleEditors} for access to the visible editor inputs
227 > */
228 > readonly visibleEditorPanes: readonly IVisibleEditorPane[];
229 >
230 > /**
231 > * All editors that are currently visible. An editor is visible when it is opened in an
232 > * editor group and active in that group. Multiple editor groups can be opened at the same time.
233 > */
234 > readonly visibleEditors: readonly EditorInput[];
235 >
236 > /**
237 > * All text editor widgets that are currently visible across all editor groups. A text editor
238 > * widget is either a text or a diff editor.
239 > *
240 > * This property supports side-by-side editors as well, by returning both sides if they are
241 > * text editor widgets.
242 > */
243 > readonly visibleTextEditorControls: readonly (IEditor | IDiffEditor)[];
244 >
245 > /**
246 > * All text editor widgets that are currently visible across all editor groups. A text editor
247 > * widget is either a text or a diff editor.
248 > *
249 > * This property supports side-by-side editors as well, by returning both sides if they are
250 > * text editor widgets.
251 > *
252 > * @param order the order of the editors to use
253 > */
254 > getVisibleTextEditorControls(order: EditorsOrder): readonly (IEditor | IDiffEditor)[];
255 >
256 > /**
257 > * All editors that are opened across all editor groups in sequential order
258 > * of appearance.
259 > *
260 > * This includes active as well as inactive editors in each editor group.
261 > */
262 > readonly editors: readonly EditorInput[];
263 >
264 > /**
265 > * The total number of editors that are opened either inactive or active.
266 > */
267 > readonly count: number;
268 >
269 > /**
270 > * All editors that are opened across all editor groups with their group
271 > * identifier.
272 > *
273 > * @param order the order of the editors to use
274 > * @param options whether to exclude sticky editors or not
275 > */
276 > getEditors(order: EditorsOrder, options?: { excludeSticky?: boolean }): readonly IEditorIdentifier[];
277 >
278 > /**
279 > * Open an editor in an editor group.
280 > *
281 > * @param editor the editor to open
282 > * @param options the options to use for the editor
283 > * @param group the target group. If unspecified, the editor will open in the currently
284 > * active group. Use `SIDE_GROUP` to open the editor in a new editor group to the side
285 > * of the currently active group.
286 > *
287 > * @returns the editor that opened or `undefined` if the operation failed or the editor was not
288 > * opened to be active.
289 > */
290 > openEditor(editor: IResourceEditorInput, group?: PreferredGroup): Promise<IEditorPane | undefined>;
291 > openEditor(editor: ITextResourceEditorInput | IUntitledTextResourceEditorInput, group?: PreferredGroup): Promise<IEditorPane | undefined>;
292 > openEditor(editor: ITextResourceDiffEditorInput | IResourceDiffEditorInput, group?: PreferredGroup): Promise<ITextDiffEditorPane | undefined>;
293 > openEditor(editor: IUntypedEditorInput, group?: PreferredGroup): Promise<IEditorPane | undefined>;
294 >
295 > /**
296 > * Using this method is a sign that your editor has not adopted the editor
297 > * resolver yet. Please use `IEditorResolverService.registerEditor` to make your editor
298 > * known to the workbench and then use untyped editor inputs for opening:
299 > *
300 > * ```ts
301 > * editorService.openEditor({ resource });
302 > * ```
303 > *
304 > * If you already have an `EditorInput` in hand and must use it for opening, use `group.openEditor`
305 > * instead, via `IEditorGroupsService`.
306 > */
307 > openEditor(editor: EditorInput, options?: IEditorOptions, group?: PreferredGroup): Promise<IEditorPane | undefined>;
308 >
309 > /**
310 > * Open editors in an editor group.
311 > *
312 > * @param editors the editors to open with associated options
313 > * @param group the target group. If unspecified, the editor will open in the currently
314 > * active group. Use `SIDE_GROUP` to open the editor in a new editor group to the side
315 > * of the currently active group.
316 > *
317 > * @returns the editors that opened. The array can be empty or have less elements for editors
318 > * that failed to open or were instructed to open as inactive.
319 > */
320 > openEditors(editors: IUntypedEditorInput[], group?: PreferredGroup, options?: IOpenEditorsOptions): Promise<readonly IEditorPane[]>;
321 >
322 > /**
323 > * Replaces editors in an editor group with the provided replacement.
324 > *
325 > * @param replacements the editors to replace
326 > * @param group the editor group
327 > *
328 > * @returns a promise that is resolved when the replaced active
329 > * editor (if any) has finished loading.
330 > */
331 > replaceEditors(replacements: IUntypedEditorReplacement[], group: IEditorGroup | GroupIdentifier): Promise<void>;
332 >
333 > /**
334 > * Find out if the provided editor is opened in any editor group.
335 > *
336 > * Note: An editor can be opened but not actively visible.
337 > *
338 > * Note: This method will return `true` if a side by side editor
339 > * is opened where the `primary` editor matches too.
340 > */
341 > isOpened(editor: IResourceEditorInputIdentifier): boolean;
342 >
343 > /**
344 > * Find out if the provided editor is visible in any editor group.
345 > */
346 > isVisible(editor: EditorInput): boolean;
347 >
348 > /**
349 > * Close an editor in a specific editor group.
350 > */
351 > closeEditor(editor: IEditorIdentifier, options?: ICloseEditorOptions): Promise<void>;
352 >
353 > /**
354 > * Close multiple editors in specific editor groups.
355 > */
356 > closeEditors(editors: readonly IEditorIdentifier[], options?: ICloseEditorOptions): Promise<void>;
357 >
358 > /**
359 > * This method will return an entry for each editor that reports
360 > * a `resource` that matches the provided one in the group or
361 > * across all groups.
362 > *
363 > * It is possible that multiple editors are returned in case the
364 > * same resource is opened in different editors. To find the specific
365 > * editor, use the `IResourceEditorInputIdentifier` as input.
366 > */
367 > findEditors(resource: URI, options?: IFindEditorOptions): readonly IEditorIdentifier[];
368 > findEditors(editor: IResourceEditorInputIdentifier, options?: IFindEditorOptions): readonly IEditorIdentifier[];
369 >
370 > /**
371 > * Save the provided list of editors.
372 > */
373 > save(editors: IEditorIdentifier | readonly IEditorIdentifier[], options?: ISaveEditorsOptions): Promise<ISaveEditorsResult>;
374 >
375 > /**
376 > * Save all editors.
377 > */
378 > saveAll(options?: ISaveAllEditorsOptions): Promise<ISaveEditorsResult>;
379 >
380 > /**
381 > * Reverts the provided list of editors.
382 > *
383 > * @returns `true` if all editors reverted and `false` otherwise.
384 > */
385 > revert(editors: IEditorIdentifier | readonly IEditorIdentifier[], options?: IRevertOptions): Promise<boolean>;
386 >
387 > /**
388 > * Reverts all editors.
389 > *
390 > * @returns `true` if all editors reverted and `false` otherwise.
391 > */
392 > revertAll(options?: IRevertAllEditorsOptions): Promise<boolean>;
393 >
394 > /**
395 > * Create a scoped editor service that only operates on the provided
396 > * editor group container. Use `main` to create a scoped editor service
397 > * to the main editor group container of the main window.
398 > */
399 > createScoped(editorGroupsContainer: IEditorGroupsContainer, disposables: DisposableStore): IEditorService;
400 > }
src/vs/workbench/test/common/workbenchTestServices.ts 376 covered LOC · 76 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workbenchTestServices.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 { DeferredPromise, timeout } from '../../../base/common/async.js';
7 > import { bufferToStream, readableToBuffer, VSBuffer, VSBufferReadable } from '../../../base/common/buffer.js';
8 > import { CancellationToken } from '../../../base/common/cancellation.js';
9 > import { Emitter, Event } from '../../../base/common/event.js';
10 > import { Iterable } from '../../../base/common/iterator.js';
11 > import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
12 > import { ResourceMap, ResourceSet } from '../../../base/common/map.js';
13 > import { Schemas } from '../../../base/common/network.js';
14 > import { observableValue } from '../../../base/common/observable.js';
15 > import { join } from '../../../base/common/path.js';
16 > import { isLinux, isMacintosh } from '../../../base/common/platform.js';
17 > import { basename, isEqual, isEqualOrParent } from '../../../base/common/resources.js';
18 > import { URI } from '../../../base/common/uri.js';
19 > import { ITextResourcePropertiesService } from '../../../editor/common/services/textResourceConfiguration.js';
20 > import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';
21 > import { IResourceEditorInput } from '../../../platform/editor/common/editor.js';
22 > import { FileChangesEvent, FileOperationEvent, FileSystemProviderCapabilities, IBaseFileStat, ICreateFileOptions, IFileContent, IFileService, IFileStat, IFileStatResult, IFileStatWithMetadata, IFileStatWithPartialMetadata, IFileStreamContent, IFileSystemProvider, IFileSystemProviderActivationEvent, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemWatcher, IReadFileOptions, IReadFileStreamOptions, IResolveFileOptions, IResolveMetadataFileOptions, IWatchOptions, IWatchOptionsWithCorrelation, IWriteFileOptions } from '../../../platform/files/common/files.js';
23 > import { AbstractLoggerService, ILogger, LogLevel, NullLogger } from '../../../platform/log/common/log.js';
24 > import { IMarker, IMarkerData, IMarkerService, IResourceMarker, MarkerStatistics } from '../../../platform/markers/common/markers.js';
25 > import product from '../../../platform/product/common/product.js';
26 > import { IProgress, IProgressStep } from '../../../platform/progress/common/progress.js';
27 > import { InMemoryStorageService, WillSaveStateReason } from '../../../platform/storage/common/storage.js';
28 > import { toUserDataProfile } from '../../../platform/userDataProfile/common/userDataProfile.js';
29 > import { ISingleFolderWorkspaceIdentifier, IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, IWorkspaceFoldersWillChangeEvent, IWorkspaceIdentifier, WorkbenchState, Workspace } from '../../../platform/workspace/common/workspace.js';
30 > import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo, ResourceTrustRequestOptions, WorkspaceTrustRequestOptions, WorkspaceTrustUriResponse } from '../../../platform/workspace/common/workspaceTrust.js';
31 > import { TestWorkspace } from '../../../platform/workspace/test/common/testWorkspace.js';
32 > import { GroupIdentifier, IRevertOptions, ISaveOptions, SaveReason } from '../../common/editor.js';
33 > import { EditorInput } from '../../common/editor/editorInput.js';
34 > import { IActivity, IActivityService } from '../../services/activity/common/activity.js';
35 > import { ChatEntitlement, ChatEntitlementContext, IChatEntitlementService } from '../../services/chat/common/chatEntitlementService.js';
36 > import { Lazy } from '../../../base/common/lazy.js';
37 > import { NullExtensionService } from '../../services/extensions/common/extensions.js';
38 > import { IAutoSaveConfiguration, IAutoSaveMode, IFilesConfigurationService } from '../../services/filesConfiguration/common/filesConfigurationService.js';
39 > import { IHistoryService } from '../../services/history/common/history.js';
40 > import { BeforeShutdownErrorEvent, ILifecycleService, InternalBeforeShutdownEvent, LifecyclePhase, ShutdownReason, StartupKind, WillShutdownEvent } from '../../services/lifecycle/common/lifecycle.js';
41 > import { IResourceEncoding } from '../../services/textfile/common/textfiles.js';
42 > import { IUserDataProfileService } from '../../services/userDataProfile/common/userDataProfile.js';
43 > import { IStoredFileWorkingCopySaveEvent } from '../../services/workingCopy/common/storedFileWorkingCopy.js';
44 > import { IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from '../../services/workingCopy/common/workingCopy.js';
45 > import { ICopyOperation, ICreateFileOperation, ICreateOperation, IDeleteOperation, IFileOperationUndoRedoInfo, IMoveOperation, IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext, IWorkingCopyFileOperationParticipant, IWorkingCopyFileService, WorkingCopyFileEvent } from '../../services/workingCopy/common/workingCopyFileService.js';
46 >
47 > export class TestLoggerService extends AbstractLoggerService {
48 > constructor(logsHome?: URI) {
49 super(LogLevel.Info, logsHome ?? URI.file('tests').with({ scheme: 'vscode-tests' }));
50 }
51 > protected doCreateLogger(): ILogger { return new NullLogger(); } workbenchTestServices.ts
52 > }
53 >
54 > export class TestTextResourcePropertiesService implements ITextResourcePropertiesService {
55 >
56 > declare readonly _serviceBrand: undefined;
57 >
58 > constructor(
59 @IConfigurationService private readonly configurationService: IConfigurationService,
60 ) {
61 }
63 > getEOL(resource: URI, language?: string): string {
64 const eol = this.configurationService.getValue('files.eol', { overrideIdentifier: language, resource });
65 if (eol && typeof eol === 'string' && eol !== 'auto') {
68 return (isLinux || isMacintosh) ? '\n' : '\r\n';
69 }
71 >
72 > export class TestUserDataProfileService implements IUserDataProfileService {
73
74 readonly _serviceBrand: undefined;
75 readonly onDidChangeCurrentProfile = Event.None;
76 readonly currentProfile = toUserDataProfile('test', 'test', URI.file('tests').with({ scheme: 'vscode-tests' }), URI.file('tests').with({ scheme: 'vscode-tests' }));
77 > async updateCurrentProfile(): Promise<void> { } workbenchTestServices.ts
78 > }
79 >
80 > export class TestContextService implements IWorkspaceContextService {
81 >
82 > declare readonly _serviceBrand: undefined;
83 >
84 > private workspace: Workspace;
85 > private options: object;
86 >
87 > private readonly _onDidChangeWorkspaceName: Emitter<void>;
88 > get onDidChangeWorkspaceName(): Event<void> { return this._onDidChangeWorkspaceName.event; }
89 >
90 > private readonly _onWillChangeWorkspaceFolders: Emitter<IWorkspaceFoldersWillChangeEvent>;
91 > get onWillChangeWorkspaceFolders(): Event<IWorkspaceFoldersWillChangeEvent> { return this._onWillChangeWorkspaceFolders.event; }
92 >
93 > private readonly _onDidChangeWorkspaceFolders: Emitter<IWorkspaceFoldersChangeEvent>;
94 > get onDidChangeWorkspaceFolders(): Event<IWorkspaceFoldersChangeEvent> { return this._onDidChangeWorkspaceFolders.event; }
95 >
96 > private readonly _onDidChangeWorkbenchState: Emitter<WorkbenchState>;
97 > get onDidChangeWorkbenchState(): Event<WorkbenchState> { return this._onDidChangeWorkbenchState.event; }
98 >
99 > constructor(workspace = TestWorkspace, options = null) {
100 this.workspace = workspace;
101 this.options = options || Object.create(null);
105 this._onDidChangeWorkbenchState = new Emitter<WorkbenchState>();
106 }
108 > getFolders(): IWorkspaceFolder[] {
109 return this.workspace ? this.workspace.folders : [];
110 }
112 > getWorkbenchState(): WorkbenchState {
113 if (this.workspace.configuration) {
114 return WorkbenchState.WORKSPACE;
121 return WorkbenchState.EMPTY;
122 }
124 > hasWorkspaceData(): boolean {
125 return this.getWorkbenchState() !== WorkbenchState.EMPTY;
126 }
128 > getCompleteWorkspace(): Promise<IWorkspace> {
129 return Promise.resolve(this.getWorkspace());
130 }
132 > getWorkspace(): IWorkspace {
133 return this.workspace;
134 }
136 > getWorkspaceFolder(resource: URI): IWorkspaceFolder | null {
137 return this.workspace.getFolder(resource);
138 }
140 > setWorkspace(workspace: any): void {
141 this.workspace = workspace;
142 }
144 > getOptions() {
145 return this.options;
146 }
148 > updateOptions() { }
149 >
150 > isInsideWorkspace(resource: URI): boolean {
151 if (resource && this.workspace) {
152 return isEqualOrParent(resource, this.workspace.folders[0].uri);
155 return false;
156 }
158 > toResource(workspaceRelativePath: string): URI {
159 return URI.file(join('C:\\', workspaceRelativePath));
160 }
162 > isCurrentWorkspace(workspaceIdOrFolder: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): boolean {
163 return URI.isUri(workspaceIdOrFolder) && isEqual(this.workspace.folders[0].uri, workspaceIdOrFolder);
164 }
166 >
167 > export class TestStorageService extends InMemoryStorageService {
168 >
169 > testEmitWillSaveState(reason: WillSaveStateReason): void {
170 super.emitWillSaveState(reason);
171 }
173 >
174 > export class TestHistoryService implements IHistoryService {
175 >
176 > declare readonly _serviceBrand: undefined;
177 >
178 > constructor(private root?: URI) { }
179 >
180 > async reopenLastClosedEditor(): Promise<void> { }
181 > async goForward(): Promise<void> { }
182 > async goBack(): Promise<void> { }
183 > async goPrevious(): Promise<void> { }
184 > async goLast(): Promise<void> { }
185 > removeFromHistory(_input: EditorInput | IResourceEditorInput): void { }
186 > clear(): void { }
187 > clearRecentlyOpened(): void { }
188 > getHistory(): readonly (EditorInput | IResourceEditorInput)[] { return []; }
189 > async openNextRecentlyUsedEditor(group?: GroupIdentifier): Promise<void> { }
190 > async openPreviouslyUsedEditor(group?: GroupIdentifier): Promise<void> { }
191 > getLastActiveWorkspaceRoot(_schemeFilter: string): URI | undefined { return this.root; }
192 > getLastActiveFile(_schemeFilter: string): URI | undefined { return undefined; }
193 > }
194 >
195 > export class TestWorkingCopy extends Disposable implements IWorkingCopy {
196 >
197 > private readonly _onDidChangeDirty = this._register(new Emitter<void>());
198 > readonly onDidChangeDirty = this._onDidChangeDirty.event;
199 >
200 > private readonly _onDidChangeContent = this._register(new Emitter<void>());
201 > readonly onDidChangeContent = this._onDidChangeContent.event;
202 >
203 > private readonly _onDidSave = this._register(new Emitter<IStoredFileWorkingCopySaveEvent>());
204 > readonly onDidSave = this._onDidSave.event;
205 >
206 > readonly capabilities = WorkingCopyCapabilities.None;
207 >
208 > readonly name;
209 >
210 > private dirty = false;
211 >
212 > constructor(readonly resource: URI, isDirty = false, readonly typeId = 'testWorkingCopyType') {
213 super();
214
216 this.dirty = isDirty;
217 }
219 > setDirty(dirty: boolean): void {
220 if (this.dirty !== dirty) {
221 this.dirty = dirty;
223 }
224 }
226 > setContent(content: string): void {
227 this._onDidChangeContent.fire();
228 }
230 > isDirty(): boolean {
231 return this.dirty;
232 }
234 > isModified(): boolean {
235 return this.isDirty();
236 }
238 > async save(options?: ISaveOptions, stat?: IFileStatWithMetadata): Promise<boolean> {
239 this._onDidSave.fire({ reason: options?.reason ?? SaveReason.EXPLICIT, stat: stat ?? createFileStat(this.resource), source: options?.source });
240
241 return true;
242 }
244 > async revert(options?: IRevertOptions): Promise<void> {
245 this.setDirty(false);
246 }
248 > async backup(token: CancellationToken): Promise<IWorkingCopyBackup> {
249 return {};
250 }
252 >
253 > export function createFileStat(resource: URI, readonly = false, isFile?: boolean, isDirectory?: boolean, isSymbolicLink?: boolean, children?: { resource: URI; isFile?: boolean; isDirectory?: boolean; isSymbolicLink?: boolean; executable?: boolean }[] | undefined, executable?: boolean): IFileStatWithMetadata {
254 return {
255 resource,
268 };
269 }
271 > export class TestWorkingCopyFileService implements IWorkingCopyFileService {
272
273 declare readonly _serviceBrand: undefined;
280
281 readonly hasSaveParticipants = false;
282 > addSaveParticipant(participant: IStoredFileWorkingCopySaveParticipant): IDisposable { return Disposable.None; } workbenchTestServices.ts
283 > async runSaveParticipants(workingCopy: IWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> { }
284 >
285 > async delete(operations: IDeleteOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<void> { }
286 >
287 > registerWorkingCopyProvider(provider: (resourceOrFolder: URI) => IWorkingCopy[]): IDisposable { return Disposable.None; }
288 >
289 > getDirty(resource: URI): IWorkingCopy[] { return []; }
290 >
291 > create(operations: ICreateFileOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }
292 > createFolder(operations: ICreateOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }
293 >
294 > move(operations: IMoveOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }
295 >
296 > copy(operations: ICopyOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }
297 > }
298 >
299 > export function mock<T>(): Ctor<T> {
300 // eslint-disable-next-line local/code-no-any-casts
301 return function () { } as any;
302 }
304 > export interface Ctor<T> {
305 > new(): T;
306 > }
307 >
308 > export class TestExtensionService extends NullExtensionService { }
309 >
310 > export const TestProductService = { _serviceBrand: undefined, ...product };
311 >
312 > export class TestActivityService implements IActivityService {
313 _serviceBrand: undefined;
314 onDidChangeActivity = Event.None;
315 > getViewContainerActivities(viewContainerId: string): IActivity[] { workbenchTestServices.ts
316 return [];
317 }
318 > getActivity(id: string): IActivity[] { workbenchTestServices.ts
319 return [];
320 }
321 > showViewContainerActivity(viewContainerId: string, badge: IActivity): IDisposable { workbenchTestServices.ts
322 return this;
323 }
324 > showViewActivity(viewId: string, badge: IActivity): IDisposable { workbenchTestServices.ts
325 return this;
326 }
327 > showAccountsActivity(activity: IActivity): IDisposable { workbenchTestServices.ts
328 return this;
329 }
330 > showGlobalActivity(activity: IActivity): IDisposable { workbenchTestServices.ts
331 return this;
332 }
334 > dispose() { }
335 > }
336 >
337 > export const NullFilesConfigurationService = new class implements IFilesConfigurationService {
338 >
339 > _serviceBrand: undefined;
340 >
341 > readonly onDidChangeAutoSaveConfiguration = Event.None;
342 > readonly onDidChangeAutoSaveDisabled = Event.None;
343 > readonly onDidChangeReadonly = Event.None;
344 > readonly onDidChangeFilesAssociation = Event.None;
345 >
346 > readonly isHotExitEnabled = false;
347 > readonly hotExitConfiguration = undefined;
348 >
349 > getAutoSaveConfiguration(): IAutoSaveConfiguration { throw new Error('Method not implemented.'); }
350 > getAutoSaveMode(): IAutoSaveMode { throw new Error('Method not implemented.'); }
351 > hasShortAutoSaveDelay(): boolean { throw new Error('Method not implemented.'); }
352 > toggleAutoSave(): Promise<void> { throw new Error('Method not implemented.'); }
353 > enableAutoSaveAfterShortDelay(resourceOrEditor: URI | EditorInput): IDisposable { throw new Error('Method not implemented.'); }
354 > disableAutoSave(resourceOrEditor: URI | EditorInput): IDisposable { throw new Error('Method not implemented.'); }
355 > isReadonly(resource: URI, stat?: IBaseFileStat | undefined): boolean { return false; }
356 > async updateReadonly(_resource: URI | URI[], _readonly: boolean | 'toggle' | 'reset'): Promise<void> { }
357 > preventSaveConflicts(resource: URI, language?: string | undefined): boolean { throw new Error('Method not implemented.'); }
358 > };
359 >
360 > export class TestWorkspaceTrustEnablementService implements IWorkspaceTrustEnablementService {
361 > _serviceBrand: undefined;
362 >
363 > constructor(private isEnabled: boolean = true) { }
364 >
365 > isWorkspaceTrustEnabled(): boolean {
366 return this.isEnabled;
367 }
369 >
370 > export class TestWorkspaceTrustManagementService extends Disposable implements IWorkspaceTrustManagementService {
371 > _serviceBrand: undefined;
372 >
373 > private _onDidChangeTrust = this._register(new Emitter<boolean>());
374 > onDidChangeTrust = this._onDidChangeTrust.event;
375 >
376 > private _onDidChangeTrustedFolders = this._register(new Emitter<void>());
377 > onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event;
378 >
379 > private _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter<void>());
380 > onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event;
381 >
382 >
383 > constructor(
384 private trusted: boolean = true,
385 private trustedUris: ResourceSet = new ResourceSet()
387 super();
388 }
390 > get acceptsOutOfWorkspaceFiles(): boolean {
391 throw new Error('Method not implemented.');
392 }
394 > set acceptsOutOfWorkspaceFiles(value: boolean) {
395 throw new Error('Method not implemented.');
396 }
398 > addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable {
399 throw new Error('Method not implemented.');
400 }
402 > getTrustedUris(): URI[] {
403 throw new Error('Method not implemented.');
404 }
406 > setParentFolderTrust(trusted: boolean): Promise<void> {
407 throw new Error('Method not implemented.');
408 }
410 > getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo> {
411 return Promise.resolve({ trusted: this.trustedUris.has(uri), uri });
412 }
414 > async setTrustedUris(folders: URI[]): Promise<void> {
415 this.trustedUris = new ResourceSet(folders);
416 }
418 > async setUrisTrust(uris: URI[], trusted: boolean): Promise<void> {
419 throw new Error('Method not implemented.');
420 }
422 > canSetParentFolderTrust(): boolean {
423 throw new Error('Method not implemented.');
424 }
426 > canSetWorkspaceTrust(): boolean {
427 throw new Error('Method not implemented.');
428 }
430 > isWorkspaceTrusted(): boolean {
431 return this.trusted;
432 }
434 > isWorkspaceTrustForced(): boolean {
435 return false;
436 }
438 > get workspaceTrustInitialized(): Promise<void> {
439 return Promise.resolve();
440 }
442 > get workspaceResolved(): Promise<void> {
443 return Promise.resolve();
444 }
446 > async setWorkspaceTrust(trusted: boolean): Promise<void> {
447 if (this.trusted !== trusted) {
448 this.trusted = trusted;
450 }
451 }
453 >
454 > export class TestWorkspaceTrustRequestService extends Disposable implements IWorkspaceTrustRequestService {
455 > _serviceBrand: any;
456 >
457 > private readonly _onDidInitiateOpenFilesTrustRequest = this._register(new Emitter<void>());
458 > readonly onDidInitiateOpenFilesTrustRequest = this._onDidInitiateOpenFilesTrustRequest.event;
459 >
460 > private readonly _onDidInitiateResourcesTrustRequest = this._register(new Emitter<ResourceTrustRequestOptions>());
461 > readonly onDidInitiateResourcesTrustRequest = this._onDidInitiateResourcesTrustRequest.event;
462 >
463 > private readonly _onDidInitiateWorkspaceTrustRequest = this._register(new Emitter<WorkspaceTrustRequestOptions>());
464 > readonly onDidInitiateWorkspaceTrustRequest = this._onDidInitiateWorkspaceTrustRequest.event;
465 >
466 > private readonly _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter<void>());
467 > readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event;
468 >
469 > constructor(private readonly _trusted: boolean) {
470 super();
471 }
474 return WorkspaceTrustUriResponse.Open;
475 };
477 > requestOpenFilesTrust(uris: URI[]): Promise<WorkspaceTrustUriResponse> {
478 return this.requestOpenUrisHandler(uris);
479 }
481 > async completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse: boolean): Promise<void> {
482 throw new Error('Method not implemented.');
483 }
485 > async completeResourcesTrustRequest(uri: URI, result: WorkspaceTrustUriResponse): Promise<void> {
486 throw new Error('Method not implemented.');
487 }
489 > async requestResourcesTrust(options: ResourceTrustRequestOptions): Promise<boolean | undefined> {
490 return this._trusted;
491 }
493 > cancelWorkspaceTrustRequest(): void {
494 throw new Error('Method not implemented.');
495 }
497 > async completeWorkspaceTrustRequest(trusted?: boolean): Promise<void> {
498 throw new Error('Method not implemented.');
499 }
501 > async requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean> {
502 return this._trusted;
503 }
505 > requestWorkspaceTrustOnStartup(): void {
506 throw new Error('Method not implemented.');
507 }
509 >
510 > export class TestMarkerService implements IMarkerService {
511
512 _serviceBrand: undefined;
513
514 onMarkerChanged = Event.None;
516 > getStatistics(): MarkerStatistics { throw new Error('Method not implemented.'); }
517 > changeOne(owner: string, resource: URI, markers: IMarkerData[]): void { }
518 > changeAll(owner: string, data: IResourceMarker[]): void { }
519 > remove(owner: string, resources: URI[]): void { }
520 > read(filter?: { owner?: string | undefined; resource?: URI | undefined; severities?: number | undefined; take?: number | undefined } | undefined): IMarker[] { return []; }
521 > installResourceFilter(resource: URI, reason: string): IDisposable {
522 return { dispose: () => { /* TODO: Implement cleanup logic */ } };
523 }
525 >
526 > export class TestFileService implements IFileService {
527
528 declare readonly _serviceBrand: undefined;
695
696 readonly watches: URI[] = [];
697 > watch(_resource: URI, options: IWatchOptionsWithCorrelation): IFileSystemWatcher; workbenchTestServices.ts
698 > watch(_resource: URI): IDisposable;
699 > watch(_resource: URI): IDisposable {
700 this.watches.push(_resource);
701
702 return toDisposable(() => this.watches.splice(this.watches.indexOf(_resource), 1));
703 }
705 > getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; }
706 > dispose(): void { }
707 >
708 > async canCreateFile(source: URI, options?: ICreateFileOptions): Promise<Error | true> { return true; }
709 > async canMove(source: URI, target: URI, overwrite?: boolean | undefined): Promise<Error | true> { return true; }
710 > async canCopy(source: URI, target: URI, overwrite?: boolean | undefined): Promise<Error | true> { return true; }
711 > async canDelete(resource: URI, options?: { useTrash?: boolean | undefined; recursive?: boolean | undefined } | undefined): Promise<Error | true> { return true; }
712 > }
713 >
714 > /**
715 > * TestFileService with in-memory file storage.
716 > * Use this when your test needs to write files and read them back.
717 > */
718 > export class InMemoryTestFileService extends TestFileService {
719
720 private files = new ResourceMap<VSBuffer>();
722 > override clearTracking(): void {
723 super.clearTracking();
724 this.files.clear();
725 }
727 > override async readFile(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileContent> {
728 if (this.readShouldThrowError) {
729 throw this.readShouldThrowError;
747 };
748 }
750 > override async writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata> {
751 await timeout(0);
752
768 return createFileStat(resource, this.readonly);
769 }
771 > override async del(resource: URI, _options?: { useTrash?: boolean; recursive?: boolean }): Promise<void> {
772 this.files.delete(resource);
773 this.notExistsSet.set(resource, true);
774 }
776 > override async exists(resource: URI): Promise<boolean> {
777 const inMemory = this.files.has(resource);
778 if (inMemory) {
782 return super.exists(resource);
783 }
785 >
786 > export class TestChatEntitlementService implements IChatEntitlementService {
787
788 _serviceBrand: undefined;
824 readonly clientByokEnabled = false;
825 readonly hasByokModels = false;
827 >
828 > export class TestLifecycleService extends Disposable implements ILifecycleService {
829
830 declare readonly _serviceBrand: undefined;
884
885 shutdownJoiners: Promise<void>[] = [];
887 > fireShutdown(reason = ShutdownReason.QUIT): void {
888 this.shutdownJoiners = [];
889
898 });
899 }
901 > fireBeforeShutdown(event: InternalBeforeShutdownEvent): void { this._onBeforeShutdown.fire(event); }
902 >
903 > fireWillShutdown(event: WillShutdownEvent): void { this._onWillShutdown.fire(event); }
904 >
905 > async shutdown(): Promise<void> {
906 this.fireShutdown();
907 }
src/vs/platform/workspace/common/workspace.ts 356 covered LOC · 32 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workspace.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 { basename, extname } from '../../../base/common/path.js';
9 > import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';
10 > import { extname as resourceExtname, basenameOrAuthority, joinPath, extUriBiasedIgnorePathCase } from '../../../base/common/resources.js';
11 > import { URI, UriComponents } from '../../../base/common/uri.js';
12 > import { createDecorator } from '../../instantiation/common/instantiation.js';
13 > import { IEnvironmentService } from '../../environment/common/environment.js';
14 > import { Schemas } from '../../../base/common/network.js';
15 >
16 > export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService');
17 >
18 > export interface IWorkspaceContextService {
19 >
20 > readonly _serviceBrand: undefined;
21 >
22 > /**
23 > * An event which fires on workbench state changes.
24 > */
25 > readonly onDidChangeWorkbenchState: Event<WorkbenchState>;
26 >
27 > /**
28 > * An event which fires on workspace name changes.
29 > */
30 > readonly onDidChangeWorkspaceName: Event<void>;
31 >
32 > /**
33 > * An event which fires before workspace folders change.
34 > */
35 > readonly onWillChangeWorkspaceFolders: Event<IWorkspaceFoldersWillChangeEvent>;
36 >
37 > /**
38 > * An event which fires on workspace folders change.
39 > */
40 > readonly onDidChangeWorkspaceFolders: Event<IWorkspaceFoldersChangeEvent>;
41 >
42 > /**
43 > * Provides access to the complete workspace object.
44 > */
45 > getCompleteWorkspace(): Promise<IWorkspace>;
46 >
47 > /**
48 > * Provides access to the workspace object the window is running with.
49 > * Use `getCompleteWorkspace` to get complete workspace object.
50 > */
51 > getWorkspace(): IWorkspace;
52 >
53 > /**
54 > * Return the state of the workbench.
55 > *
56 > * WorkbenchState.EMPTY - if the workbench was opened with empty window or file
57 > * WorkbenchState.FOLDER - if the workbench was opened with a folder
58 > * WorkbenchState.WORKSPACE - if the workbench was opened with a workspace
59 > */
60 > getWorkbenchState(): WorkbenchState;
61 >
62 > /**
63 > * Returns the folder for the given resource from the workspace.
64 > * Can be null if there is no workspace or the resource is not inside the workspace.
65 > */
66 > getWorkspaceFolder(resource: URI): IWorkspaceFolder | null;
67 >
68 > /**
69 > * Return `true` if the current workspace has the given identifier or root URI otherwise `false`.
70 > */
71 > isCurrentWorkspace(workspaceIdOrFolder: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): boolean;
72 >
73 > /**
74 > * Returns if the provided resource is inside the workspace or not.
75 > */
76 > isInsideWorkspace(resource: URI): boolean;
77 >
78 > /**
79 > * Return `true` if the current workspace has data (e.g. folders or a workspace configuration) that can be sent to the extension host, otherwise `false`.
80 > */
81 > hasWorkspaceData(): boolean;
82 > }
83 >
84 > export interface IResolvedWorkspace extends IWorkspaceIdentifier, IBaseWorkspace {
85 > readonly folders: IWorkspaceFolder[];
86 > }
87 >
88 > export interface IBaseWorkspace {
89 >
90 > /**
91 > * If present, marks the window that opens the workspace
92 > * as a remote window with the given authority.
93 > */
94 > readonly remoteAuthority?: string;
95 >
96 > /**
97 > * Transient workspaces are meant to go away after being used
98 > * once, e.g. a window reload of a transient workspace will
99 > * open an empty window.
100 > *
101 > * See: https://github.com/microsoft/vscode/issues/119695
102 > */
103 > readonly transient?: boolean;
104 > }
105 >
106 > export interface IBaseWorkspaceIdentifier {
107 >
108 > /**
109 > * Every workspace (multi-root, single folder or empty)
110 > * has a unique identifier. It is not possible to open
111 > * a workspace with the same `id` in multiple windows
112 > */
113 > readonly id: string;
114 > }
115 >
116 > /**
117 > * A single folder workspace identifier is a path to a folder + id.
118 > */
119 > export interface ISingleFolderWorkspaceIdentifier extends IBaseWorkspaceIdentifier {
120 >
121 > /**
122 > * Folder path as `URI`.
123 > */
124 > readonly uri: URI;
125 > }
126 >
127 > /**
128 > * A multi-root workspace identifier is a path to a workspace file + id.
129 > */
130 > export interface IWorkspaceIdentifier extends IBaseWorkspaceIdentifier {
131 >
132 > /**
133 > * Workspace config file path as `URI`.
134 > */
135 > configPath: URI;
136 > }
137 >
138 > export interface IEmptyWorkspaceIdentifier extends IBaseWorkspaceIdentifier { }
139 >
140 > export type IAnyWorkspaceIdentifier = IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier;
141 >
142 > export function isSingleFolderWorkspaceIdentifier(obj: unknown): obj is ISingleFolderWorkspaceIdentifier {
143 const singleFolderIdentifier = obj as ISingleFolderWorkspaceIdentifier | undefined;
144
145 return typeof singleFolderIdentifier?.id === 'string' && URI.isUri(singleFolderIdentifier.uri);
146 }
147 > workspace.ts
148 > export function isEmptyWorkspaceIdentifier(obj: unknown): obj is IEmptyWorkspaceIdentifier {
149 const emptyWorkspaceIdentifier = obj as IEmptyWorkspaceIdentifier | undefined;
150 return typeof emptyWorkspaceIdentifier?.id === 'string'
152 && !isWorkspaceIdentifier(obj);
153 }
154 > workspace.ts
155 > export const EXTENSION_DEVELOPMENT_EMPTY_WINDOW_WORKSPACE: IEmptyWorkspaceIdentifier = { id: 'ext-dev' };
156 > export const UNKNOWN_EMPTY_WINDOW_WORKSPACE: IEmptyWorkspaceIdentifier = { id: 'empty-window' };
157 >
158 > export function toWorkspaceIdentifier(workspace: IWorkspace): IAnyWorkspaceIdentifier;
159 > export function toWorkspaceIdentifier(backupPath: string | undefined, isExtensionDevelopment: boolean): IEmptyWorkspaceIdentifier;
160 > export function toWorkspaceIdentifier(arg0: IWorkspace | string | undefined, isExtensionDevelopment?: boolean): IAnyWorkspaceIdentifier {
161
162 // Empty workspace
202 };
203 }
204 > workspace.ts
205 > export function isWorkspaceIdentifier(obj: unknown): obj is IWorkspaceIdentifier {
206 const workspaceIdentifier = obj as IWorkspaceIdentifier | undefined;
207
208 return typeof workspaceIdentifier?.id === 'string' && URI.isUri(workspaceIdentifier.configPath);
209 }
210 > workspace.ts
211 > export interface ISerializedSingleFolderWorkspaceIdentifier extends IBaseWorkspaceIdentifier {
212 > readonly uri: UriComponents;
213 > }
214 >
215 > export interface ISerializedWorkspaceIdentifier extends IBaseWorkspaceIdentifier {
216 > readonly configPath: UriComponents;
217 > }
218 >
219 > export function reviveIdentifier(identifier: undefined): undefined;
220 > export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier): IWorkspaceIdentifier;
221 > export function reviveIdentifier(identifier: ISerializedSingleFolderWorkspaceIdentifier): ISingleFolderWorkspaceIdentifier;
222 > export function reviveIdentifier(identifier: IEmptyWorkspaceIdentifier): IEmptyWorkspaceIdentifier;
223 > export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IAnyWorkspaceIdentifier | undefined;
224 > export function reviveIdentifier(identifier: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IAnyWorkspaceIdentifier | undefined {
225
226 // Single Folder
243 return undefined;
244 }
245 > workspace.ts
246 > export const enum WorkbenchState {
247 > EMPTY = 1,
248 > FOLDER,
249 > WORKSPACE
250 > }
251 >
252 > export interface IWorkspaceFoldersWillChangeEvent {
253 >
254 > readonly changes: IWorkspaceFoldersChangeEvent;
255 > readonly fromCache: boolean;
256 >
257 > join(promise: Promise<void>): void;
258 > }
259 >
260 > export interface IWorkspaceFoldersChangeEvent {
261 > added: IWorkspaceFolder[];
262 > removed: IWorkspaceFolder[];
263 > changed: IWorkspaceFolder[];
264 > }
265 >
266 > export interface IWorkspace {
267 >
268 > /**
269 > * the unique identifier of the workspace.
270 > */
271 > readonly id: string;
272 >
273 > /**
274 > * Folders in the workspace.
275 > */
276 > readonly folders: IWorkspaceFolder[];
277 >
278 > /**
279 > * Transient workspaces are meant to go away after being used
280 > * once, e.g. a window reload of a transient workspace will
281 > * open an empty window.
282 > */
283 > readonly transient?: boolean;
284 >
285 > /**
286 > * the location of the workspace configuration
287 > */
288 > readonly configuration?: URI | null;
289 >
290 > /**
291 > * Optional display name for the workspace.
292 > */
293 > readonly name?: string;
294 >
295 > }
296 >
297 > export function isWorkspace(thing: unknown): thing is IWorkspace {
298 const candidate = thing as IWorkspace | undefined;
299
302 && Array.isArray(candidate.folders));
303 }
304 > workspace.ts
305 > export interface IWorkspaceFolderData {
306 >
307 > /**
308 > * The associated URI for this workspace folder.
309 > */
310 > readonly uri: URI;
311 >
312 > /**
313 > * The name of this workspace folder. Defaults to
314 > * the basename of its [uri-path](#Uri.path)
315 > */
316 > readonly name: string;
317 >
318 > /**
319 > * The ordinal number of this workspace folder.
320 > */
321 > readonly index: number;
322 > }
323 >
324 > export interface IWorkspaceFolder extends IWorkspaceFolderData {
325 >
326 > /**
327 > * Given workspace folder relative path, returns the resource with the absolute path.
328 > */
329 > toResource: (relativePath: string) => URI;
330 > }
331 >
332 > export function isWorkspaceFolder(thing: unknown): thing is IWorkspaceFolder {
333 const candidate = thing as IWorkspaceFolder;
334
338 && typeof candidate.toResource === 'function');
339 }
340 > workspace.ts
341 > export class Workspace implements IWorkspace {
342 >
343 > private foldersMap: TernarySearchTree<URI, WorkspaceFolder>;
344 >
345 > private _folders!: WorkspaceFolder[];
346 > get folders(): WorkspaceFolder[] { return this._folders; }
347 > set folders(folders: WorkspaceFolder[]) {
348 > this._folders = folders; workspace.ts
349 > this.updateFoldersMap();
350 > }
351 > workspace.ts
352 > constructor(
353 > private _id: string, workspace.ts
354 > folders: WorkspaceFolder[],
355 > private _transient: boolean,
356 > private _configuration: URI | null,
357 > private ignorePathCasing: (key: URI) => boolean,
358 > private _workspaceName?: string,
359 > ) {
360 > this.foldersMap = TernarySearchTree.forUris<WorkspaceFolder>(this.ignorePathCasing, () => true);
361 > this.folders = folders;
362 > }
363 > workspace.ts
364 > update(workspace: Workspace) {
365 this._id = workspace.id;
366 this._configuration = workspace.configuration;
370 this.folders = workspace.folders;
371 }
372 > workspace.ts
373 > get id(): string {
374 return this._id;
375 }
376 > workspace.ts
377 > get transient(): boolean {
378 return this._transient;
379 }
380 > workspace.ts
381 > get configuration(): URI | null {
382 return this._configuration;
383 }
384 > workspace.ts
385 > set configuration(configuration: URI | null) {
386 this._configuration = configuration;
387 }
388 > workspace.ts
389 > get name(): string | undefined {
390 return this._workspaceName;
391 }
392 > workspace.ts
393 > getFolder(resource: URI): IWorkspaceFolder | null {
394 if (!resource) {
395 return null;
398 return this.foldersMap.findSubstr(resource) || null;
399 }
400 > workspace.ts
401 > private updateFoldersMap(): void {
402 > this.foldersMap = TernarySearchTree.forUris<WorkspaceFolder>(this.ignorePathCasing, () => true); workspace.ts
403 > for (const folder of this.folders) {
404 > this.foldersMap.set(folder.uri, folder);
405 > }
406 > }
407 > workspace.ts
408 > toJSON(): IWorkspace {
409 return { id: this.id, folders: this.folders, transient: this.transient, configuration: this.configuration, name: this.name };
410 }
411 > } workspace.ts
412 >
413 > export interface IRawFileWorkspaceFolder {
414 > readonly path: string;
415 > name?: string;
416 > }
417 >
418 > export interface IRawUriWorkspaceFolder {
419 > readonly uri: string;
420 > name?: string;
421 > }
422 >
423 > export class WorkspaceFolder implements IWorkspaceFolder {
424 >
425 > readonly uri: URI;
426 > readonly name: string;
427 > readonly index: number;
428 >
429 > constructor(
430 > data: IWorkspaceFolderData, workspace.ts
431 > /**
432 > * Provides access to the original metadata for this workspace
433 > * folder. This can be different from the metadata provided in
434 > * this class:
435 > * - raw paths can be relative
436 > * - raw paths are not normalized
437 > */
438 > readonly raw?: IRawFileWorkspaceFolder | IRawUriWorkspaceFolder
439 > ) {
440 > this.uri = data.uri;
441 > this.index = data.index;
442 > this.name = data.name;
443 > }
444 > workspace.ts
445 > toResource(relativePath: string): URI {
446 return joinPath(this.uri, relativePath);
447 }
448 > workspace.ts
449 > toJSON(): IWorkspaceFolderData {
450 return { uri: this.uri, name: this.name, index: this.index };
451 }
452 > } workspace.ts
453 >
454 > export function toWorkspaceFolder(resource: URI): WorkspaceFolder {
455 > return new WorkspaceFolder({ uri: resource, index: 0, name: basenameOrAuthority(resource) }, { uri: resource.toString() }); workspace.ts
456 > }
457 > workspace.ts
458 > export const WORKSPACE_EXTENSION = 'code-workspace';
459 > export const WORKSPACE_SUFFIX = `.${WORKSPACE_EXTENSION}`;
460 > export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }];
461 > export const UNTITLED_WORKSPACE_NAME = 'workspace.json';
462 >
463 > export function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean {
464 return extUriBiasedIgnorePathCase.isEqualOrParent(path, environmentService.untitledWorkspacesHome);
465 }
466 > workspace.ts
467 > export function isTemporaryWorkspace(workspace: IWorkspace): boolean;
468 > export function isTemporaryWorkspace(path: URI): boolean;
469 > export function isTemporaryWorkspace(arg1: IWorkspace | URI): boolean {
470 let path: URI | null | undefined;
471 if (URI.isUri(arg1)) {
477 return path?.scheme === Schemas.tmp;
478 }
479 > workspace.ts
480 > export const STANDALONE_EDITOR_WORKSPACE_ID = '4064f6ec-cb38-4ad0-af64-ee6467e63c82';
481 > export function isStandaloneEditorWorkspace(workspace: IWorkspace): boolean {
482 return workspace.id === STANDALONE_EDITOR_WORKSPACE_ID;
483 }
484 > workspace.ts
485 > export function isSavedWorkspace(path: URI, environmentService: IEnvironmentService): boolean {
486 return !isUntitledWorkspace(path, environmentService) && !isTemporaryWorkspace(path);
487 }
488 > workspace.ts
489 > export function hasWorkspaceFileExtension(path: string | URI) {
490 const ext = (typeof path === 'string') ? extname(path) : resourceExtname(path);
491
src/vs/base/common/ternarySearchTree.ts 343 covered LOC · 102 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; ternarySearchTree.ts
117 > this._to = 0;
118 > this._value = key;
119 > this._valueLen = key.length;
120 > for (let pos = key.length - 1; pos >= 0; pos--, this._valueLen--) {
121 > const ch = this._value.charCodeAt(pos);
122 > if (!(ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash)) {
123 > break;
124 > }
125 > }
126 >
127 > return this.next();
128 > }
130 > hasNext(): boolean {
131 > return this._to < this._valueLen; ternarySearchTree.ts
132 > }
134 > next(): this {
135 > // this._data = key.split(/[\\/]/).filter(s => !!s); ternarySearchTree.ts
136 > this._from = this._to;
137 > let justSeps = true;
138 > for (; this._to < this._valueLen; this._to++) {
139 > const ch = this._value.charCodeAt(this._to);
140 > if (ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash) {
141 > if (justSeps) { ternarySearchTree.ts
142 > this._from++;
143 > } else {
144 break;
145 }
146 > } else { ternarySearchTree.ts
147 > justSeps = false;
148 > }
149 > }
150 > return this;
151 > }
153 > cmp(a: string): number {
154 > return this._caseSensitive ternarySearchTree.ts
155 > ? compareSubstring(a, this._value, 0, a.length, this._from, this._to) ternarySearchTree.ts
156 : compareSubstringIgnoreCase(a, this._value, 0, a.length, this._from, this._to);
159 > value(): string {
160 > return this._value.substring(this._from, this._to); ternarySearchTree.ts
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, ternarySearchTree.ts
177 > private readonly _ignoreQueryAndFragment: (uri: URI) => boolean) { }
179 > reset(key: URI): this {
180 > this._value = key; ternarySearchTree.ts
181 > this._states = [];
182 > if (this._value.scheme) {
183 > this._states.push(UriIteratorState.Scheme);
184 > }
185 > if (this._value.authority) {
186 this._states.push(UriIteratorState.Authority);
187 }
188 > if (this._value.path) { ternarySearchTree.ts
189 > this._pathIterator = new PathIterator(false, !this._ignorePathCasing(key));
190 > this._pathIterator.reset(key.path);
191 > if (this._pathIterator.value()) {
192 > this._states.push(UriIteratorState.Path);
193 > }
194 > }
195 > if (!this._ignoreQueryAndFragment(key)) {
196 if (this._value.query) {
197 this._states.push(UriIteratorState.Query);
201 }
202 }
203 > this._stateIdx = 0; ternarySearchTree.ts
204 > return this;
205 > }
207 > next(): this {
208 > if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) { ternarySearchTree.ts
209 this._pathIterator.next();
210 > } else { ternarySearchTree.ts
211 > this._stateIdx += 1;
212 > }
213 > return this;
214 > }
216 > hasNext(): boolean {
217 > return (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) ternarySearchTree.ts
218 > || this._stateIdx < this._states.length - 1;
219 > }
221 > cmp(a: string): number {
222 > if (this._states[this._stateIdx] === UriIteratorState.Scheme) { ternarySearchTree.ts
223 > return compareIgnoreCase(a, this._value.scheme);
224 > } else if (this._states[this._stateIdx] === UriIteratorState.Authority) {
225 return compareIgnoreCase(a, this._value.authority);
226 > } else if (this._states[this._stateIdx] === UriIteratorState.Path) { ternarySearchTree.ts
227 > return this._pathIterator.cmp(a); ternarySearchTree.ts
228 > } else if (this._states[this._stateIdx] === UriIteratorState.Query) { ternarySearchTree.ts
229 return compare(a, this._value.query);
230 } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) {
232 }
233 throw new Error();
236 > value(): string {
237 > if (this._states[this._stateIdx] === UriIteratorState.Scheme) { ternarySearchTree.ts
238 > return this._value.scheme;
239 > } else if (this._states[this._stateIdx] === UriIteratorState.Authority) {
240 return this._value.authority;
241 > } else if (this._states[this._stateIdx] === UriIteratorState.Path) { ternarySearchTree.ts
242 > return this._pathIterator.value();
243 > } else if (this._states[this._stateIdx] === UriIteratorState.Query) {
244 return this._value.query;
245 } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) {
247 }
248 throw new Error();
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; ternarySearchTree.ts
258 > }
260 > static unwrap<V>(value: V | typeof Undef.Val): V | undefined {
261 > return value === Undef.Val ? undefined : value; ternarySearchTree.ts
262 > }
264 >
265 > class TernarySearchTreeNode<K, V> { ternarySearchTree.ts
266 > height: number = 1;
267 > segment!: string;
268 > value: V | typeof Undef.Val | undefined = undefined;
269 > key: K | undefined = undefined;
270 > left: TernarySearchTreeNode<K, V> | undefined = undefined;
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); ternarySearchTree.ts
298 > }
300 > balanceFactor() {
301 > return this.heightRight - this.heightLeft; ternarySearchTree.ts
302 > }
304 > get heightLeft() {
305 > return this.left?.height ?? 0; ternarySearchTree.ts
306 > }
308 > get heightRight() {
309 > return this.right?.height ?? 0; ternarySearchTree.ts
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)); ternarySearchTree.ts
323 > }
325 > static forPaths<E>(ignorePathCasing = false): TernarySearchTree<string, E> {
326 return new TernarySearchTree<string, E>(new PathIterator(undefined, !ignorePathCasing));
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); ternarySearchTree.ts
374 > let node: TernarySearchTreeNode<K, V>;
375 >
376 > if (!this._root) {
377 > this._root = new TernarySearchTreeNode<K, V>();
378 > this._root.segment = iter.value();
379 > }
380 > const stack: [Dir, TernarySearchTreeNode<K, V>][] = [];
381 >
382 > // find insert_node
383 > node = this._root;
384 > while (true) {
385 > const val = iter.cmp(node.segment);
386 > if (val > 0) {
387 // left
388 if (!node.left) {
393 node = node.left;
394
395 > } else if (val < 0) { ternarySearchTree.ts
396 // right
397 if (!node.right) {
402 node = node.right;
403
404 > } else if (iter.hasNext()) { ternarySearchTree.ts
405 > // mid ternarySearchTree.ts
406 > iter.next();
407 > if (!node.mid) {
408 > node.mid = new TernarySearchTreeNode<K, V>();
409 > node.mid.segment = iter.value();
410 > }
411 > stack.push([Dir.Mid, node]);
412 > node = node.mid;
413 > } else { ternarySearchTree.ts
414 > break;
415 > }
416 > }
417 >
418 > // set value
419 > const oldElement = Undef.unwrap(node.value);
420 > node.value = Undef.wrap(element);
421 > node.key = key;
422 >
423 > // balance
424 > for (let i = stack.length - 1; i >= 0; i--) {
425 > const node = stack[i][1]; ternarySearchTree.ts
426 >
427 > node.updateHeight();
428 > const bf = node.balanceFactor();
429 >
430 > if (bf < -1 || bf > 1) {
431 // needs rotate
432 const d1 = stack[i][0];
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/stream.ts 325 covered LOC · 29 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- stream.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 } from './cancellation.js';
7 > import { onUnexpectedError } from './errors.js';
8 > import { DisposableStore, toDisposable } from './lifecycle.js';
9 >
10 > /**
11 > * The payload that flows in readable stream events.
12 > */
13 > export type ReadableStreamEventPayload<T> = T | Error | 'end';
14 >
15 > export interface ReadableStreamEvents<T> {
16 >
17 > /**
18 > * The 'data' event is emitted whenever the stream is
19 > * relinquishing ownership of a chunk of data to a consumer.
20 > *
21 > * NOTE: PLEASE UNDERSTAND THAT ADDING A DATA LISTENER CAN
22 > * TURN THE STREAM INTO FLOWING MODE. IT IS THEREFOR THE
23 > * LAST LISTENER THAT SHOULD BE ADDED AND NOT THE FIRST
24 > *
25 > * Use `listenStream` as a helper method to listen to
26 > * stream events in the right order.
27 > */
28 > on(event: 'data', callback: (data: T) => void): void;
29 >
30 > /**
31 > * Emitted when any error occurs.
32 > */
33 > on(event: 'error', callback: (err: Error) => void): void;
34 >
35 > /**
36 > * The 'end' event is emitted when there is no more data
37 > * to be consumed from the stream. The 'end' event will
38 > * not be emitted unless the data is completely consumed.
39 > */
40 > on(event: 'end', callback: () => void): void;
41 > }
42 >
43 > /**
44 > * A interface that emulates the API shape of a node.js readable
45 > * stream for use in native and web environments.
46 > */
47 > export interface ReadableStream<T> extends ReadableStreamEvents<T> {
48 >
49 > /**
50 > * Stops emitting any events until resume() is called.
51 > */
52 > pause(): void;
53 >
54 > /**
55 > * Starts emitting events again after pause() was called.
56 > */
57 > resume(): void;
58 >
59 > /**
60 > * Destroys the stream and stops emitting any event.
61 > */
62 > destroy(): void;
63 >
64 > /**
65 > * Allows to remove a listener that was previously added.
66 > */
67 > removeListener(event: string, callback: Function): void;
68 > }
69 >
70 > /**
71 > * A interface that emulates the API shape of a node.js readable
72 > * for use in native and web environments.
73 > */
74 > export interface Readable<T> {
75 >
76 > /**
77 > * Read data from the underlying source. Will return
78 > * null to indicate that no more data can be read.
79 > */
80 > read(): T | null;
81 > }
82 >
83 > export function isReadable<T>(obj: unknown): obj is Readable<T> {
84 const candidate = obj as Readable<T> | undefined;
85 if (!candidate) {
89 return typeof candidate.read === 'function';
90 }
91 > stream.ts
92 > /**
93 > * A interface that emulates the API shape of a node.js writeable
94 > * stream for use in native and web environments.
95 > */
96 > export interface WriteableStream<T> extends ReadableStream<T> {
97 >
98 > /**
99 > * Writing data to the stream will trigger the on('data')
100 > * event listener if the stream is flowing and buffer the
101 > * data otherwise until the stream is flowing.
102 > *
103 > * If a `highWaterMark` is configured and writing to the
104 > * stream reaches this mark, a promise will be returned
105 > * that should be awaited on before writing more data.
106 > * Otherwise there is a risk of buffering a large number
107 > * of data chunks without consumer.
108 > */
109 > write(data: T): void | Promise<void>;
110 >
111 > /**
112 > * Signals an error to the consumer of the stream via the
113 > * on('error') handler if the stream is flowing.
114 > *
115 > * NOTE: call `end` to signal that the stream has ended,
116 > * this DOES NOT happen automatically from `error`.
117 > */
118 > error(error: Error): void;
119 >
120 > /**
121 > * Signals the end of the stream to the consumer. If the
122 > * result is provided, will trigger the on('data') event
123 > * listener if the stream is flowing and buffer the data
124 > * otherwise until the stream is flowing.
125 > */
126 > end(result?: T): void;
127 > }
128 >
129 > /**
130 > * A stream that has a buffer already read. Returns the original stream
131 > * that was read as well as the chunks that got read.
132 > *
133 > * The `ended` flag indicates if the stream has been fully consumed.
134 > */
135 > export interface ReadableBufferedStream<T> {
136 >
137 > /**
138 > * The original stream that is being read.
139 > */
140 > stream: ReadableStream<T>;
141 >
142 > /**
143 > * An array of chunks already read from this stream.
144 > */
145 > buffer: T[];
146 >
147 > /**
148 > * Signals if the stream has ended or not. If not, consumers
149 > * should continue to read from the stream until consumed.
150 > */
151 > ended: boolean;
152 > }
153 >
154 > export function isReadableStream<T>(obj: unknown): obj is ReadableStream<T> {
155 const candidate = obj as ReadableStream<T> | undefined;
156 if (!candidate) {
160 return [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function');
161 }
162 > stream.ts
163 > export function isReadableBufferedStream<T>(obj: unknown): obj is ReadableBufferedStream<T> {
164 const candidate = obj as ReadableBufferedStream<T> | undefined;
165 if (!candidate) {
169 return isReadableStream(candidate.stream) && Array.isArray(candidate.buffer) && typeof candidate.ended === 'boolean';
170 }
171 > stream.ts
172 > export interface IReducer<T, R = T> {
173 > (data: T[]): R;
174 > }
175 >
176 > export interface IDataTransformer<Original, Transformed> {
177 > (data: Original): Transformed;
178 > }
179 >
180 > export interface IErrorTransformer {
181 > (error: Error): Error;
182 > }
183 >
184 > export interface ITransformer<Original, Transformed> {
185 > data: IDataTransformer<Original, Transformed>;
186 > error?: IErrorTransformer;
187 > }
188 >
189 > export function newWriteableStream<T>(reducer: IReducer<T> | null, options?: WriteableStreamOptions): WriteableStream<T> {
190 return new WriteableStreamImpl<T>(reducer, options);
191 }
192 > stream.ts
193 > export interface WriteableStreamOptions {
194 >
195 > /**
196 > * The number of objects to buffer before WriteableStream#write()
197 > * signals back that the buffer is full. Can be used to reduce
198 > * the memory pressure when the stream is not flowing.
199 > */
200 > highWaterMark?: number;
201 > }
202 >
203 > class WriteableStreamImpl<T> implements WriteableStream<T> {
204 >
205 > private readonly state = {
206 > flowing: false,
207 > ended: false,
208 > destroyed: false
209 > };
210 >
211 > private readonly buffer = {
212 > data: [] as T[],
213 > error: [] as Error[]
214 > };
215 >
216 > private readonly listeners = {
217 > data: [] as { (data: T): void }[],
218 > error: [] as { (error: Error): void }[],
219 > end: [] as { (): void }[]
220 > };
221 >
222 > private readonly pendingWritePromises: Function[] = [];
223 >
224 > /**
225 > * @param reducer a function that reduces the buffered data into a single object;
226 > * because some objects can be complex and non-reducible, we also
227 > * allow passing the explicit `null` value to skip the reduce step
228 > * @param options stream options
229 > */
230 > constructor(private reducer: IReducer<T> | null, private options?: WriteableStreamOptions) { }
231 >
232 > pause(): void {
233 if (this.state.destroyed) {
234 return;
237 this.state.flowing = false;
238 }
239 > stream.ts
240 > resume(): void {
241 if (this.state.destroyed) {
242 return;
252 }
253 }
254 > stream.ts
255 > write(data: T): void | Promise<void> {
256 if (this.state.destroyed) {
257 return;
273 }
274 }
275 > stream.ts
276 > error(error: Error): void {
277 if (this.state.destroyed) {
278 return;
289 }
290 }
291 > stream.ts
292 > end(result?: T): void {
293 if (this.state.destroyed) {
294 return;
312 }
313 }
314 > stream.ts
315 > private emitData(data: T): void {
316 this.listeners.data.slice(0).forEach(listener => listener(data)); // slice to avoid listener mutation from delivering event
317 }
318 > stream.ts
319 > private emitError(error: Error): void {
320 if (this.listeners.error.length === 0) {
321 onUnexpectedError(error); // nobody listened to this error so we log it as unexpected
324 }
325 }
326 > stream.ts
327 > private emitEnd(): void {
328 this.listeners.end.slice(0).forEach(listener => listener()); // slice to avoid listener mutation from delivering event
329 }
330 > stream.ts
331 > on(event: 'data', callback: (data: T) => void): void;
332 > on(event: 'error', callback: (err: Error) => void): void;
333 > on(event: 'end', callback: () => void): void;
334 > on(event: 'data' | 'error' | 'end', callback: ((data: T) => void) | ((err: Error) => void) | (() => void)): void {
335 if (this.state.destroyed) {
336 return;
372 }
373 }
374 > stream.ts
375 > removeListener(event: string, callback: Function): void {
376 if (this.state.destroyed) {
377 return;
401 }
402 }
403 > stream.ts
404 > private flowData(): void {
405 // if buffer is empty, nothing to do
406 if (this.buffer.data.length === 0) {
428 pendingWritePromises.forEach(pendingWritePromise => pendingWritePromise());
429 }
430 > stream.ts
431 > private flowErrors(): void {
432 if (this.listeners.error.length > 0) {
433 for (const error of this.buffer.error) {
438 }
439 }
440 > stream.ts
441 > private flowEnd(): boolean {
442 if (this.state.ended) {
443 this.emitEnd();
448 return false;
449 }
450 > stream.ts
451 > destroy(): void {
452 if (!this.state.destroyed) {
453 this.state.destroyed = true;
464 }
465 }
466 > } stream.ts
467 >
468 > /**
469 > * Helper to fully read a T readable into a T.
470 > */
471 > export function consumeReadable<T>(readable: Readable<T>, reducer: IReducer<T>): T {
472 const chunks: T[] = [];
473
479 return reducer(chunks);
480 }
481 > stream.ts
482 > /**
483 > * Helper to read a T readable up to a maximum of chunks. If the limit is
484 > * reached, will return a readable instead to ensure all data can still
485 > * be read.
486 > */
487 > export function peekReadable<T>(readable: Readable<T>, reducer: IReducer<T>, maxChunks: number): T | Readable<T> {
488 const chunks: T[] = [];
489
527 };
528 }
529 > stream.ts
530 > /**
531 > * Helper to fully read a T stream into a T or consuming
532 > * a stream fully, awaiting all the events without caring
533 > * about the data.
534 > */
535 > export function consumeStream<T, R = T>(stream: ReadableStreamEvents<T>, reducer: IReducer<T, R>): Promise<R>;
536 > export function consumeStream(stream: ReadableStreamEvents<unknown>): Promise<undefined>;
537 > export function consumeStream<T, R = T>(stream: ReadableStreamEvents<T>, reducer?: IReducer<T, R>): Promise<R | undefined> {
538 return new Promise((resolve, reject) => {
539 const chunks: T[] = [];
562 });
563 }
564 > stream.ts
565 > export interface IStreamListener<T> {
566 >
567 > /**
568 > * The 'data' event is emitted whenever the stream is
569 > * relinquishing ownership of a chunk of data to a consumer.
570 > */
571 > onData(data: T): void;
572 >
573 > /**
574 > * Emitted when any error occurs.
575 > */
576 > onError(err: Error): void;
577 >
578 > /**
579 > * The 'end' event is emitted when there is no more data
580 > * to be consumed from the stream. The 'end' event will
581 > * not be emitted unless the data is completely consumed.
582 > */
583 > onEnd(): void;
584 > }
585 >
586 > /**
587 > * Helper to listen to all events of a T stream in proper order.
588 > */
589 > export function listenStream<T>(stream: ReadableStreamEvents<T>, listener: IStreamListener<T>, token?: CancellationToken): void {
590
591 stream.on('error', error => {
610 });
611 }
612 > stream.ts
613 > /**
614 > * Helper to peek up to `maxChunks` into a stream. The return type signals if
615 > * the stream has ended or not. If not, caller needs to add a `data` listener
616 > * to continue reading.
617 > */
618 > export function peekStream<T>(stream: ReadableStream<T>, maxChunks: number): Promise<ReadableBufferedStream<T>> {
619 return new Promise((resolve, reject) => {
620 const streamListeners = new DisposableStore();
666 });
667 }
668 > stream.ts
669 > /**
670 > * Helper to create a readable stream from an existing T.
671 > */
672 > export function toStream<T>(t: T, reducer: IReducer<T>): ReadableStream<T> {
673 const stream = newWriteableStream<T>(reducer);
674
677 return stream;
678 }
679 > stream.ts
680 > /**
681 > * Helper to create an empty stream
682 > */
683 > export function emptyStream(): ReadableStream<never> {
684 const stream = newWriteableStream<never>(() => { throw new Error('not supported'); });
685 stream.end();
687 return stream;
688 }
689 > stream.ts
690 > /**
691 > * Helper to convert a T into a Readable<T>.
692 > */
693 > export function toReadable<T>(t: T): Readable<T> {
694 let consumed = false;
695
706 };
707 }
708 > stream.ts
709 > /**
710 > * Helper to transform a readable stream into another stream.
711 > */
712 > export function transform<Original, Transformed>(stream: ReadableStreamEvents<Original>, transformer: ITransformer<Original, Transformed>, reducer: IReducer<Transformed>): ReadableStream<Transformed> {
713 const target = newWriteableStream<Transformed>(reducer);
714
721 return target;
722 }
723 > stream.ts
724 > /**
725 > * Helper to take an existing readable that will
726 > * have a prefix injected to the beginning.
727 > */
728 > export function prefixedReadable<T>(prefix: T, readable: Readable<T>, reducer: IReducer<T>): Readable<T> {
729 let prefixHandled = false;
730
751 };
752 }
753 > stream.ts
754 > /**
755 > * Helper to take an existing stream that will
756 > * have a prefix injected to the beginning.
757 > */
758 > export function prefixedStream<T>(prefix: T, stream: ReadableStream<T>, reducer: IReducer<T>): ReadableStream<T> {
759 let prefixHandled = false;
760
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/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/workbench/services/lifecycle/common/lifecycle.ts 292 covered LOC · 3 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 { CancellationToken } from '../../../../base/common/cancellation.js';
7 > import { Event } from '../../../../base/common/event.js';
8 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
9 >
10 > export const ILifecycleService = createDecorator<ILifecycleService>('lifecycleService');
11 >
12 > /**
13 > * An event that is send out when the window is about to close. Clients have a chance to veto
14 > * the closing by either calling veto with a boolean "true" directly or with a promise that
15 > * resolves to a boolean. Returning a promise is useful in cases of long running operations
16 > * on shutdown.
17 > *
18 > * Note: It is absolutely important to avoid long running promises if possible. Please try hard
19 > * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence!
20 > */
21 > export interface BeforeShutdownEvent {
22 >
23 > /**
24 > * The reason why the application will be shutting down.
25 > */
26 > readonly reason: ShutdownReason;
27 >
28 > /**
29 > * Allows to veto the shutdown. The veto can be a long running operation but it
30 > * will block the application from closing.
31 > *
32 > * @param id to identify the veto operation in case it takes very long or never
33 > * completes.
34 > */
35 > veto(value: boolean | Promise<boolean>, id: string): void;
36 > }
37 >
38 > export interface InternalBeforeShutdownEvent extends BeforeShutdownEvent {
39 >
40 > /**
41 > * Allows to set a veto operation to run after all other
42 > * vetos have been handled from the `BeforeShutdownEvent`
43 > *
44 > * This method is hidden from the API because it is intended
45 > * to be only used once internally.
46 > */
47 > finalVeto(vetoFn: () => boolean | Promise<boolean>, id: string): void;
48 > }
49 >
50 > /**
51 > * An event that signals an error happened during `onBeforeShutdown` veto handling.
52 > * In this case the shutdown operation will not proceed because this is an unexpected
53 > * condition that is treated like a veto.
54 > */
55 > export interface BeforeShutdownErrorEvent {
56 >
57 > /**
58 > * The reason why the application is shutting down.
59 > */
60 > readonly reason: ShutdownReason;
61 >
62 > /**
63 > * The error that happened during shutdown handling.
64 > */
65 > readonly error: Error;
66 > }
67 >
68 > export enum WillShutdownJoinerOrder {
69 >
70 > /**
71 > * Joiners to run before the `Last` joiners. This is the default order and best for
72 > * most cases. You can be sure that services are still functional at this point.
73 > */
74 > Default = 1,
75 >
76 > /**
77 > * The joiners to run last. This should ONLY be used in rare cases when you have no
78 > * dependencies to workbench services or state. The workbench may be in a state where
79 > * resources can no longer be accessed or changed.
80 > */
81 > Last
82 > }
83 >
84 > export interface IWillShutdownEventJoiner {
85 > readonly id: string;
86 > readonly label: string;
87 > readonly order?: WillShutdownJoinerOrder;
88 > }
89 >
90 > export interface IWillShutdownEventDefaultJoiner extends IWillShutdownEventJoiner {
91 > readonly order?: WillShutdownJoinerOrder.Default;
92 > }
93 >
94 > export interface IWillShutdownEventLastJoiner extends IWillShutdownEventJoiner {
95 > readonly order: WillShutdownJoinerOrder.Last;
96 > }
97 >
98 > /**
99 > * An event that is send out when the window closes. Clients have a chance to join the closing
100 > * by providing a promise from the join method. Returning a promise is useful in cases of long
101 > * running operations on shutdown.
102 > *
103 > * Note: It is absolutely important to avoid long running promises if possible. Please try hard
104 > * to return a boolean directly. Returning a promise has quite an impact on the shutdown sequence!
105 > */
106 > export interface WillShutdownEvent {
107 >
108 > /**
109 > * The reason why the application is shutting down.
110 > */
111 > readonly reason: ShutdownReason;
112 >
113 > /**
114 > * A token that will signal cancellation when the
115 > * shutdown was forced by the user.
116 > */
117 > readonly token: CancellationToken;
118 >
119 > /**
120 > * Allows to join the shutdown. The promise can be a long running operation but it
121 > * will block the application from closing.
122 > *
123 > * @param promise the promise to join the shutdown event.
124 > * @param joiner to identify the join operation in case it takes very long or never
125 > * completes.
126 > */
127 > join(promise: Promise<void>, joiner: IWillShutdownEventDefaultJoiner): void;
128 >
129 > /**
130 > * Allows to join the shutdown at the end. The promise can be a long running operation but it
131 > * will block the application from closing.
132 > *
133 > * @param promiseFn the promise to join the shutdown event.
134 > * @param joiner to identify the join operation in case it takes very long or never
135 > * completes.
136 > */
137 > join(promiseFn: (() => Promise<void>), joiner: IWillShutdownEventLastJoiner): void;
138 >
139 > /**
140 > * Allows to access the joiners that have not finished joining this event.
141 > */
142 > joiners(): IWillShutdownEventJoiner[];
143 >
144 > /**
145 > * Allows to enforce the shutdown, even when there are
146 > * pending `join` operations to complete.
147 > */
148 > force(): void;
149 > }
150 >
151 > export const enum ShutdownReason {
152 >
153 > /**
154 > * The window is closed.
155 > */
156 > CLOSE = 1,
157 >
158 > /**
159 > * The window closes because the application quits.
160 > */
161 > QUIT,
162 >
163 > /**
164 > * The window is reloaded.
165 > */
166 > RELOAD,
167 >
168 > /**
169 > * The window is loaded into a different workspace context.
170 > */
171 > LOAD
172 > }
173 >
174 > export const enum StartupKind {
175 > NewWindow = 1,
176 > ReloadedWindow = 3,
177 > ReopenedWindow = 4
178 > }
179 >
180 > export function StartupKindToString(startupKind: StartupKind): string {
181 switch (startupKind) {
182 case StartupKind.NewWindow: return 'NewWindow';
185 }
186 }
187 > lifecycle.ts
188 > export const enum LifecyclePhase {
189 >
190 > /**
191 > * The first phase signals that we are about to startup getting ready.
192 > *
193 > * Note: doing work in this phase blocks an editor from showing to
194 > * the user, so please rather consider to use `Restored` phase.
195 > */
196 > Starting = 1,
197 >
198 > /**
199 > * Services are ready and the window is about to restore its UI state.
200 > *
201 > * Note: doing work in this phase blocks an editor from showing to
202 > * the user, so please rather consider to use `Restored` phase.
203 > */
204 > Ready = 2,
205 >
206 > /**
207 > * Views, panels and editors have restored. Editors are given a bit of
208 > * time to restore their contents.
209 > */
210 > Restored = 3,
211 >
212 > /**
213 > * The last phase after views, panels and editors have restored and
214 > * some time has passed (2-5 seconds).
215 > */
216 > Eventually = 4
217 > }
218 >
219 > export function LifecyclePhaseToString(phase: LifecyclePhase): string {
220 switch (phase) {
221 case LifecyclePhase.Starting: return 'Starting';
225 }
226 }
227 > lifecycle.ts
228 > /**
229 > * A lifecycle service informs about lifecycle events of the
230 > * application, such as shutdown.
231 > */
232 > export interface ILifecycleService {
233 >
234 > readonly _serviceBrand: undefined;
235 >
236 > /**
237 > * Value indicates how this window got loaded.
238 > */
239 > readonly startupKind: StartupKind;
240 >
241 > /**
242 > * A flag indicating in what phase of the lifecycle we currently are.
243 > */
244 > phase: LifecyclePhase;
245 >
246 > /**
247 > * Fired before shutdown happens. Allows listeners to veto against the
248 > * shutdown to prevent it from happening.
249 > *
250 > * The event carries a shutdown reason that indicates how the shutdown was triggered.
251 > */
252 > readonly onBeforeShutdown: Event<BeforeShutdownEvent>;
253 >
254 > /**
255 > * Fired when the shutdown was prevented by a component giving veto.
256 > */
257 > readonly onShutdownVeto: Event<void>;
258 >
259 > /**
260 > * Fired when an error happened during `onBeforeShutdown` veto handling.
261 > * In this case the shutdown operation will not proceed because this is
262 > * an unexpected condition that is treated like a veto.
263 > *
264 > * The event carries a shutdown reason that indicates how the shutdown was triggered.
265 > */
266 > readonly onBeforeShutdownError: Event<BeforeShutdownErrorEvent>;
267 >
268 > /**
269 > * Fired when no client is preventing the shutdown from happening (from `onBeforeShutdown`).
270 > *
271 > * This event can be joined with a long running operation via `WillShutdownEvent#join()` to
272 > * handle long running shutdown operations.
273 > *
274 > * The event carries a shutdown reason that indicates how the shutdown was triggered.
275 > */
276 > readonly onWillShutdown: Event<WillShutdownEvent>;
277 >
278 > /**
279 > * A flag indicating that we are about to shutdown without further veto.
280 > */
281 > readonly willShutdown: boolean;
282 >
283 > /**
284 > * Fired when the shutdown is about to happen after long running shutdown operations
285 > * have finished (from `onWillShutdown`).
286 > *
287 > * This event should be used to dispose resources.
288 > */
289 > readonly onDidShutdown: Event<void>;
290 >
291 > /**
292 > * Returns a promise that resolves when a certain lifecycle phase
293 > * has started.
294 > */
295 > when(phase: LifecyclePhase): Promise<void>;
296 >
297 > /**
298 > * Triggers a shutdown of the workbench. Depending on native or web, this can have
299 > * different implementations and behaviour.
300 > *
301 > * **Note:** this should normally not be called. See related methods in `IHostService`
302 > * and `INativeHostService` to close a window or quit the application.
303 > */
304 > shutdown(): Promise<void>;
305 > }
src/vs/platform/request/common/request.ts 277 covered LOC · 15 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- request.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 { streamToBuffer } from '../../../base/common/buffer.js';
7 > import { CancellationToken } from '../../../base/common/cancellation.js';
8 > import { getErrorMessage } from '../../../base/common/errors.js';
9 > import { Emitter, Event } from '../../../base/common/event.js';
10 > import { Disposable } from '../../../base/common/lifecycle.js';
11 > import { IHeaders, IRequestContext, IRequestOptions } from '../../../base/parts/request/common/request.js';
12 > import { localize } from '../../../nls.js';
13 > import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from '../../configuration/common/configurationRegistry.js';
14 > import { createDecorator } from '../../instantiation/common/instantiation.js';
15 > import { ILogService } from '../../log/common/log.js';
16 > import { Registry } from '../../registry/common/platform.js';
17 >
18 > export const IRequestService = createDecorator<IRequestService>('requestService');
19 >
20 > /**
21 > * Use as the {@link IRequestOptions.callSite} value to prevent
22 > * request telemetry from being emitted. This is needed for
23 > * callers such as the telemetry sender to avoid cyclical calls.
24 > */
25 > export const NO_FETCH_TELEMETRY = 'NO_FETCH_TELEMETRY';
26 >
27 > export interface IRequestCompleteEvent {
28 > readonly callSite: string;
29 > readonly latency: number;
30 > readonly statusCode: number | undefined;
31 > }
32 >
33 > export interface AuthInfo {
34 > isProxy: boolean;
35 > scheme: string;
36 > host: string;
37 > port: number;
38 > realm: string;
39 > attempt: number;
40 > }
41 >
42 > export interface Credentials {
43 > username: string;
44 > password: string;
45 > }
46 >
47 > export interface IRequestService {
48 > readonly _serviceBrand: undefined;
49 >
50 > /**
51 > * Fires when a request completes (successfully or with an error response).
52 > */
53 > readonly onDidCompleteRequest: Event<IRequestCompleteEvent>;
54 >
55 > request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext>;
56 >
57 > resolveProxy(url: string): Promise<string | undefined>;
58 > lookupAuthorization(authInfo: AuthInfo): Promise<Credentials | undefined>;
59 > lookupKerberosAuthorization(url: string): Promise<string | undefined>;
60 > loadCertificates(): Promise<string[]>;
61 > }
62 >
63 > class LoggableHeaders {
64 >
65 > private headers: IHeaders | undefined;
66 >
67 > constructor(private readonly original: IHeaders) { }
68 >
69 > toJSON(): any {
70 if (!this.headers) {
71 const headers = Object.create(null);
81 return this.headers;
82 }
83 > request.ts
84 > }
85 >
86 > export abstract class AbstractRequestService extends Disposable implements IRequestService {
87 >
88 > declare readonly _serviceBrand: undefined;
89 >
90 > private counter = 0;
91 >
92 > private readonly _onDidCompleteRequest = this._register(new Emitter<IRequestCompleteEvent>());
93 > readonly onDidCompleteRequest = this._onDidCompleteRequest.event;
94 >
95 > constructor(protected readonly logService: ILogService) {
96 super();
97 }
98 > request.ts
99 > protected async logAndRequest(options: IRequestOptions, request: () => Promise<IRequestContext>): Promise<IRequestContext> {
100 const prefix = `#${++this.counter}: ${options.url}`;
101 this.logService.trace(`${prefix} - begin`, options.type, new LoggableHeaders(options.headers ?? {}));
115 }
116 }
117 > request.ts
118 > abstract request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext>;
119 > abstract resolveProxy(url: string): Promise<string | undefined>;
120 > abstract lookupAuthorization(authInfo: AuthInfo): Promise<Credentials | undefined>;
121 > abstract lookupKerberosAuthorization(url: string): Promise<string | undefined>;
122 > abstract loadCertificates(): Promise<string[]>;
123 > }
124 >
125 > export function isSuccess(context: IRequestContext): boolean {
126 return (context.res.statusCode && context.res.statusCode >= 200 && context.res.statusCode < 300) || context.res.statusCode === 1223;
127 }
128 > request.ts
129 > export function isClientError(context: IRequestContext): boolean {
130 return !!context.res.statusCode && context.res.statusCode >= 400 && context.res.statusCode < 500;
131 }
132 > request.ts
133 > export function isServerError(context: IRequestContext): boolean {
134 return !!context.res.statusCode && context.res.statusCode >= 500 && context.res.statusCode < 600;
135 }
136 > request.ts
137 > /**
138 > * Reads a header value from an {@link IHeaders} map, tolerating array-shaped
139 > * values and case-insensitive lookups.
140 > */
141 > export function readHeader(headers: IHeaders | undefined, name: string): string | undefined {
142 if (!headers) {
143 return undefined;
149 return value;
150 }
151 > request.ts
152 > /**
153 > * Parses the `Retry-After` header as a number of seconds. Returns `undefined`
154 > * if absent or not a finite positive number. The HTTP-date form is not parsed.
155 > */
156 > export function retryAfterFromHeaders(headers: IHeaders | undefined): number | undefined {
157 const value = readHeader(headers, 'retry-after');
158 if (!value) {
162 return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
163 }
164 > request.ts
165 > export function hasNoContent(context: IRequestContext): boolean {
166 return context.res.statusCode === 204;
167 }
168 > request.ts
169 export async function asText(context: IRequestContext): Promise<string | null> {
170 if (hasNoContent(context)) {
174 return buffer.toString();
175 }
176 > request.ts
177 export async function asTextOrError(context: IRequestContext): Promise<string | null> {
178 if (!isSuccess(context)) {
181 return asText(context);
182 }
183 > request.ts
184 export async function asJson<T = {}>(context: IRequestContext): Promise<T | null> {
185 if (!isSuccess(context)) {
198 }
199 }
200 > request.ts
201 > export function updateProxyConfigurationsScope(useHostProxy: boolean, useHostProxyDefault: boolean): void {
202 registerProxyConfigurations(useHostProxy, useHostProxyDefault);
203 }
204 > request.ts
205 > export const USER_LOCAL_AND_REMOTE_SETTINGS = [
206 > 'http.proxy',
207 > 'http.proxyStrictSSL',
208 > 'http.proxyKerberosServicePrincipal',
209 > 'http.noProxy',
210 > 'http.proxyAuthorization',
211 > 'http.proxySupport',
212 > 'http.systemCertificates',
213 > 'http.systemCertificatesNode',
214 > 'http.experimental.systemCertificatesV2',
215 > 'http.fetchAdditionalSupport',
216 > 'http.experimental.networkInterfaceCheckInterval',
217 > ];
218 >
219 > export const systemCertificatesNodeDefault = false;
220 >
221 > let proxyConfiguration: IConfigurationNode[] = [];
222 > let previousUseHostProxy: boolean | undefined = undefined;
223 > let previousUseHostProxyDefault: boolean | undefined = undefined;
224 > function registerProxyConfigurations(useHostProxy = true, useHostProxyDefault = true): void {
225 > if (previousUseHostProxy === useHostProxy && previousUseHostProxyDefault === useHostProxyDefault) {
226 return;
227 }
228 > request.ts
229 > previousUseHostProxy = useHostProxy;
230 > previousUseHostProxyDefault = useHostProxyDefault;
231 >
232 > const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
233 > const oldProxyConfiguration = proxyConfiguration;
234 > proxyConfiguration = [
235 > {
236 > id: 'http',
237 > order: 15,
238 > title: localize('httpConfigurationTitle', "HTTP"),
239 > type: 'object',
240 > scope: ConfigurationScope.MACHINE,
241 > properties: {
242 > 'http.useLocalProxyConfiguration': {
243 > type: 'boolean',
244 > default: useHostProxyDefault,
245 > markdownDescription: localize('useLocalProxy', "Controls whether in the remote extension host the local proxy configuration should be used. This setting only applies as a remote setting during [remote development](https://aka.ms/vscode-remote)."),
246 > restricted: true
247 > },
248 > }
249 > },
250 > {
251 > id: 'http',
252 > order: 15,
253 > title: localize('httpConfigurationTitle', "HTTP"),
254 > type: 'object',
255 > scope: ConfigurationScope.APPLICATION,
256 > properties: {
257 > 'http.electronFetch': {
258 > type: 'boolean',
259 > default: false,
260 > description: localize('electronFetch', "Controls whether use of Electron's fetch implementation instead of Node.js' should be enabled. All local extensions will get Electron's fetch implementation for the global fetch API."),
261 > restricted: true
262 > },
263 > }
264 > },
265 > {
266 > id: 'http',
267 > order: 15,
268 > title: localize('httpConfigurationTitle', "HTTP"),
269 > type: 'object',
270 > scope: useHostProxy ? ConfigurationScope.APPLICATION : ConfigurationScope.MACHINE,
271 > properties: {
272 > 'http.proxy': {
273 > type: 'string',
274 > pattern: '^(https?|socks|socks4a?|socks5h?)://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$',
275 > markdownDescription: localize('proxy', "The proxy setting to use. If not set, will be inherited from the `http_proxy` and `https_proxy` environment variables. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
276 > restricted: true
277 > },
278 > 'http.proxyStrictSSL': {
279 > type: 'boolean',
280 > default: true,
281 > markdownDescription: localize('strictSSL', "Controls whether the proxy server certificate should be verified against the list of supplied CAs. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
282 > restricted: true
283 > },
284 > 'http.proxyKerberosServicePrincipal': {
285 > type: 'string',
286 > markdownDescription: localize('proxyKerberosServicePrincipal', "Overrides the principal service name for Kerberos authentication with the HTTP proxy. A default based on the proxy hostname is used when this is not set. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
287 > restricted: true
288 > },
289 > 'http.noProxy': {
290 > type: 'array',
291 > items: { type: 'string' },
292 > markdownDescription: localize('noProxy', "Specifies domain names for which proxy settings should be ignored for HTTP/HTTPS requests. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
293 > restricted: true
294 > },
295 > 'http.proxyAuthorization': {
296 > type: ['null', 'string'],
297 > default: null,
298 > markdownDescription: localize('proxyAuthorization', "The value to send as the `Proxy-Authorization` header for every network request. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
299 > restricted: true
300 > },
301 > 'http.proxySupport': {
302 > type: 'string',
303 > enum: ['off', 'on', 'fallback', 'override'],
304 > enumDescriptions: [
305 > localize('proxySupportOff', "Disable proxy support for extensions."),
306 > localize('proxySupportOn', "Enable proxy support for extensions."),
307 > localize('proxySupportFallback', "Enable proxy support for extensions, fall back to request options, when no proxy found."),
308 > localize('proxySupportOverride', "Enable proxy support for extensions, override request options."),
309 > ],
310 > default: 'override',
311 > markdownDescription: localize('proxySupport', "Use the proxy support for extensions. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
312 > restricted: true
313 > },
314 > 'http.systemCertificates': {
315 > type: 'boolean',
316 > default: true,
317 > markdownDescription: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. On Windows and macOS, a reload of the window is required after turning this off. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
318 > restricted: true
319 > },
320 > 'http.systemCertificatesNode': {
321 > type: 'boolean',
322 > tags: ['experimental'],
323 > default: systemCertificatesNodeDefault,
324 > markdownDescription: localize('systemCertificatesNode', "Controls whether system certificates should be loaded using Node.js built-in support. Reload the window after changing this setting. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
325 > restricted: true,
326 > experiment: {
327 > mode: 'auto'
328 > }
329 > },
330 > 'http.experimental.systemCertificatesV2': {
331 > type: 'boolean',
332 > tags: ['experimental'],
333 > default: false,
334 > markdownDescription: localize('systemCertificatesV2', "Controls whether experimental loading of CA certificates from the OS should be enabled. This uses a more general approach than the default implementation. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
335 > restricted: true
336 > },
337 > 'http.fetchAdditionalSupport': {
338 > type: 'boolean',
339 > default: true,
340 > markdownDescription: localize('fetchAdditionalSupport', "Controls whether Node.js' fetch implementation should be extended with additional support. Currently proxy support ({1}) and system certificates ({2}) are added when the corresponding settings are enabled. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`', '`#http.proxySupport#`', '`#http.systemCertificates#`'),
341 > restricted: true
342 > },
343 > 'http.webSocketAdditionalSupport': {
344 > type: 'boolean',
345 > default: true,
346 > markdownDescription: localize('webSocketAdditionalSupport', "Controls whether the built-in WebSocket implementation should be extended with additional support. Currently proxy support ({1}) and system certificates ({2}) are added when the corresponding settings are enabled. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`', '`#http.proxySupport#`', '`#http.systemCertificates#`'),
347 > restricted: true
348 > },
349 > 'http.experimental.networkInterfaceCheckInterval': {
350 > type: 'number',
351 > default: 300,
352 > minimum: -1,
353 > tags: ['experimental'],
354 > markdownDescription: localize('networkInterfaceCheckInterval', "Controls the interval in seconds for checking network interface changes to invalidate the proxy cache. Set to -1 to disable. When during [remote development](https://aka.ms/vscode-remote) the {0} setting is disabled this setting can be configured in the local and the remote settings separately.", '`#http.useLocalProxyConfiguration#`'),
355 > restricted: true,
356 > experiment: {
357 > mode: 'auto'
358 > }
359 > }
360 > }
361 > }
362 > ];
363 > configurationRegistry.updateConfigurations({ add: proxyConfiguration, remove: oldProxyConfiguration });
364 > }
365 >
366 > registerProxyConfigurations();
src/vs/workbench/common/editor/editorInput.ts 265 covered LOC · 28 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editorInput.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 { URI } from '../../../base/common/uri.js';
8 > import { EditorInputCapabilities, Verbosity, GroupIdentifier, ISaveOptions, IRevertOptions, IMoveResult, IEditorDescriptor, IEditorPane, IUntypedEditorInput, EditorResourceAccessor, AbstractEditorInput, isEditorInput, IEditorIdentifier } from '../editor.js';
9 > import { isEqual } from '../../../base/common/resources.js';
10 > import { ConfirmResult } from '../../../platform/dialogs/common/dialogs.js';
11 > import { IMarkdownString } from '../../../base/common/htmlContent.js';
12 > import { IDisposable } from '../../../base/common/lifecycle.js';
13 > import { ThemeIcon } from '../../../base/common/themables.js';
14 >
15 > export interface IEditorCloseHandler {
16 >
17 > /**
18 > * If `true`, will call into the `confirm` method to ask for confirmation
19 > * before closing the editor.
20 > */
21 > showConfirm(): boolean;
22 >
23 > /**
24 > * Allows an editor to control what should happen when the editor
25 > * (or a list of editor of the same kind) is being closed.
26 > *
27 > * By default a file specific dialog will open if the editor is
28 > * dirty and not in the process of saving.
29 > *
30 > * If the editor is not dealing with files or another condition
31 > * should be used besides dirty state, this method should be
32 > * implemented to show a different dialog.
33 > *
34 > * @param editors All editors of the same kind that are being closed. Should be used
35 > * to show a combined dialog.
36 > */
37 > confirm(editors: ReadonlyArray<IEditorIdentifier>): Promise<ConfirmResult>;
38 > }
39 >
40 > export interface IUntypedEditorOptions {
41 >
42 > /**
43 > * Implementations should try to preserve as much
44 > * view state as possible from the typed input based
45 > * on the group the editor is opened.
46 > */
47 > readonly preserveViewState?: GroupIdentifier;
48 >
49 > /**
50 > * Implementations should preserve the original
51 > * resource of the typed input and not alter
52 > * it.
53 > */
54 > readonly preserveResource?: boolean;
55 > }
56 >
57 > /**
58 > * Editor inputs are lightweight objects that can be passed to the workbench API to open inside the editor part.
59 > * Each editor input is mapped to an editor that is capable of opening it through the Platform facade.
60 > */
61 > export abstract class EditorInput extends AbstractEditorInput {
62
63 protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
86 */
87 readonly onWillDispose = this._onWillDispose.event;
89 > /**
90 > * Optional: subclasses can override to implement
91 > * custom confirmation on close behavior.
92 > */
93 > readonly closeHandler?: IEditorCloseHandler;
94 >
95 > /**
96 > * Unique type identifier for this input. Every editor input of the
97 > * same class should share the same type identifier. The type identifier
98 > * is used for example for serialising/deserialising editor inputs
99 > * via the serialisers of the `EditorInputFactoryRegistry`.
100 > */
101 > abstract get typeId(): string;
102 >
103 > /**
104 > * Returns the optional associated resource of this input.
105 > *
106 > * This resource should be unique for all editors of the same
107 > * kind and input and is often used to identify the editor input among
108 > * others.
109 > *
110 > * **Note:** DO NOT use this property for anything but identity
111 > * checks. DO NOT use this property to present as label to the user.
112 > * Please refer to `EditorResourceAccessor` documentation in that case.
113 > */
114 > abstract get resource(): URI | undefined;
115 >
116 > /**
117 > * Identifies the type of editor this input represents
118 > * This ID is registered with the {@link EditorResolverService} to allow
119 > * for resolving an untyped input to a typed one
120 > */
121 > get editorId(): string | undefined {
122 return undefined;
123 }
125 > /**
126 > * The capabilities of the input.
127 > */
128 > get capabilities(): EditorInputCapabilities {
129 return EditorInputCapabilities.Readonly;
130 }
132 > /**
133 > * Figure out if the input has the provided capability.
134 > */
135 > hasCapability(capability: EditorInputCapabilities): boolean {
136 if (capability === EditorInputCapabilities.None) {
137 return this.capabilities === EditorInputCapabilities.None;
140 return (this.capabilities & capability) !== 0;
141 }
143 > isReadonly(): boolean | IMarkdownString {
144 return this.hasCapability(EditorInputCapabilities.Readonly);
145 }
147 > /**
148 > * Returns the display name of this input.
149 > */
150 > getName(): string {
151 return `Editor ${this.typeId}`;
152 }
154 > /**
155 > * Returns the display description of this input.
156 > */
157 > getDescription(verbosity?: Verbosity): string | undefined {
158 return undefined;
159 }
161 > /**
162 > * Returns the display title of this input.
163 > */
164 > getTitle(verbosity?: Verbosity): string {
165 return this.getName();
166 }
168 > /**
169 > * Returns the extra classes to apply to the label of this input.
170 > */
171 > getLabelExtraClasses(): string[] {
172 return [];
173 }
175 > /**
176 > * Returns the aria label to be read out by a screen reader.
177 > */
178 > getAriaLabel(): string {
179 return this.getTitle(Verbosity.SHORT);
180 }
182 > /**
183 > * Returns the icon which represents this editor input.
184 > * If undefined, the default icon will be used.
185 > */
186 > getIcon(): ThemeIcon | URI | undefined {
187 return undefined;
188 }
190 > /**
191 > * Returns a descriptor suitable for telemetry events.
192 > *
193 > * Subclasses should extend if they can contribute.
194 > */
195 > getTelemetryDescriptor(): { [key: string]: unknown } {
196 /* __GDPR__FRAGMENT__
197 "EditorTelemetryDescriptor" : {
201 return { typeId: this.typeId };
202 }
204 > /**
205 > * Returns if this input is dirty or not.
206 > */
207 > isDirty(): boolean {
208 return false;
209 }
211 > /**
212 > * Returns if the input has unsaved changes.
213 > */
214 > isModified(): boolean {
215 return this.isDirty();
216 }
218 > /**
219 > * Returns if this input is currently being saved or soon to be
220 > * saved. Based on this assumption the editor may for example
221 > * decide to not signal the dirty state to the user assuming that
222 > * the save is scheduled to happen anyway.
223 > */
224 > isSaving(): boolean {
225 return false;
226 }
228 > /**
229 > * Returns a type of `IDisposable` that represents the resolved input.
230 > * Subclasses should override to provide a meaningful model or return
231 > * `null` if the editor does not require a model.
232 > *
233 > * The `options` parameter are passed down from the editor when the
234 > * input is resolved as part of it.
235 > */
236 > async resolve(): Promise<IDisposable | null> {
237 return null;
238 }
240 > /**
241 > * Saves the editor. The provided groupId helps implementors
242 > * to e.g. preserve view state of the editor and re-open it
243 > * in the correct group after saving.
244 > *
245 > * @returns the resulting editor input (typically the same) of
246 > * this operation or `undefined` to indicate that the operation
247 > * failed or was canceled.
248 > */
249 > async save(group: GroupIdentifier, options?: ISaveOptions): Promise<EditorInput | IUntypedEditorInput | undefined> {
250 return this;
251 }
253 > /**
254 > * Saves the editor to a different location. The provided `group`
255 > * helps implementors to e.g. preserve view state of the editor
256 > * and re-open it in the correct group after saving.
257 > *
258 > * @returns the resulting editor input (typically a different one)
259 > * of this operation or `undefined` to indicate that the operation
260 > * failed or was canceled.
261 > */
262 > async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise<EditorInput | IUntypedEditorInput | undefined> {
263 return this;
264 }
266 > /**
267 > * Reverts this input from the provided group.
268 > */
269 > async revert(group: GroupIdentifier, options?: IRevertOptions): Promise<void> { }
270 >
271 > /**
272 > * Called to determine how to handle a resource that is renamed that matches
273 > * the editors resource (or is a child of).
274 > *
275 > * Implementors are free to not implement this method to signal no intent
276 > * to participate. If an editor is returned though, it will replace the
277 > * current one with that editor and optional options.
278 > */
279 > async rename(group: GroupIdentifier, target: URI): Promise<IMoveResult | undefined> {
280 return undefined;
281 }
283 > /**
284 > * Returns a copy of the current editor input. Used when we can't just reuse the input
285 > */
286 > copy(): EditorInput {
287 return this;
288 }
290 > /**
291 > * Indicates if this editor can be moved to another group. By default
292 > * editors can freely be moved around groups. If an editor cannot be
293 > * moved, a message should be returned to show to the user.
294 > *
295 > * @returns `true` if the editor can be moved to the target group, or
296 > * a string with a message to show to the user if the editor cannot be
297 > * moved.
298 > */
299 > canMove(sourceGroup: GroupIdentifier, targetGroup: GroupIdentifier): true | string {
300 return true;
301 }
303 > /**
304 > * Indicates if this editor can be reopened after being closed. By default
305 > * editors can be reopened. Subclasses can override to prevent this.
306 > *
307 > * @returns `true` if the editor can be reopened after being closed.
308 > */
309 > canReopen(): boolean {
310 return true;
311 }
313 > /**
314 > * Returns if the other object matches this input.
315 > */
316 > matches(otherInput: EditorInput | IUntypedEditorInput): boolean {
317
318 // Typed inputs: via === check
331 return isEqual(this.resource, EditorResourceAccessor.getCanonicalUri(otherInput));
332 }
334 > /**
335 > * If a editor was registered onto multiple editor panes, this method
336 > * will be asked to return the preferred one to use.
337 > *
338 > * @param editorPanes a list of editor pane descriptors that are candidates
339 > * for the editor to open in.
340 > */
341 > prefersEditorPane<T extends IEditorDescriptor<IEditorPane>>(editorPanes: T[]): T | undefined {
342 return editorPanes.at(0);
343 }
345 > /**
346 > * Returns a representation of this typed editor input as untyped
347 > * resource editor input that e.g. can be used to serialize the
348 > * editor input into a form that it can be restored.
349 > *
350 > * May return `undefined` if an untyped representation is not supported.
351 > */
352 > toUntyped(options?: IUntypedEditorOptions): IUntypedEditorInput | undefined {
353 return undefined;
354 }
356 > /**
357 > * Returns if this editor is disposed.
358 > */
359 > isDisposed(): boolean {
360 return this._store.isDisposed;
361 }
363 > override dispose(): void {
364 if (!this.isDisposed()) {
365 this._onWillDispose.fire();
src/vs/base/common/resources.ts 257 covered LOC · 25 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- resources.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 * as extpath from './extpath.js';
8 > import { Schemas } from './network.js';
9 > import * as paths from './path.js';
10 > import { isLinux, isWindows } from './platform.js';
11 > import { compare as strCompare, equalsIgnoreCase } from './strings.js';
12 > import { URI, uriToFsPath } from './uri.js';
13 >
14 > export function originalFSPath(uri: URI): string {
15 return uriToFsPath(uri, true);
16 }
18 > //#region IExtUri
19 >
20 > export interface IExtUri {
21 >
22 > // --- identity
23 >
24 > /**
25 > * Compares two uris.
26 > *
27 > * @param uri1 Uri
28 > * @param uri2 Uri
29 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
30 > */
31 > compare(uri1: URI, uri2: URI, ignoreFragment?: boolean): number;
32 >
33 > /**
34 > * Tests whether two uris are equal
35 > *
36 > * @param uri1 Uri
37 > * @param uri2 Uri
38 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
39 > */
40 > isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment?: boolean): boolean;
41 >
42 > /**
43 > * Tests whether a `candidate` URI is a parent or equal of a given `base` URI.
44 > *
45 > * @param base A uri which is "longer" or at least same length as `parentCandidate`
46 > * @param parentCandidate A uri which is "shorter" or up to same length as `base`
47 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
48 > */
49 > isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment?: boolean): boolean;
50 >
51 > /**
52 > * Creates a key from a resource URI to be used to resource comparison and for resource maps.
53 > * @see {@link ResourceMap}
54 > * @param uri Uri
55 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
56 > */
57 > getComparisonKey(uri: URI, ignoreFragment?: boolean): string;
58 >
59 > /**
60 > * Whether the casing of the path-component of the uri should be ignored.
61 > */
62 > ignorePathCasing(uri: URI): boolean;
63 >
64 > // --- path math
65 >
66 > basenameOrAuthority(resource: URI): string;
67 >
68 > /**
69 > * Returns the basename of the path component of an uri.
70 > * @param resource
71 > */
72 > basename(resource: URI): string;
73 >
74 > /**
75 > * Returns the extension of the path component of an uri.
76 > * @param resource
77 > */
78 > extname(resource: URI): string;
79 > /**
80 > * Return a URI representing the directory of a URI path.
81 > *
82 > * @param resource The input URI.
83 > * @returns The URI representing the directory of the input URI.
84 > */
85 > dirname(resource: URI): URI;
86 > /**
87 > * Join a URI path with path fragments and normalizes the resulting path.
88 > *
89 > * @param resource The input URI.
90 > * @param pathFragment The path fragment to add to the URI path.
91 > * @returns The resulting URI.
92 > */
93 > joinPath(resource: URI, ...pathFragment: string[]): URI;
94 > /**
95 > * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names.
96 > *
97 > * @param resource The URI to normalize the path.
98 > * @returns The URI with the normalized path.
99 > */
100 > normalizePath(resource: URI): URI;
101 > /**
102 > *
103 > * @param from
104 > * @param to
105 > */
106 > relativePath(from: URI, to: URI): string | undefined;
107 > /**
108 > * Resolves an absolute or relative path against a base URI.
109 > * The path can be relative or absolute posix or a Windows path
110 > */
111 > resolvePath(base: URI, path: string): URI;
112 >
113 > // --- misc
114 >
115 > /**
116 > * Returns true if the URI path is absolute.
117 > */
118 > isAbsolutePath(resource: URI): boolean;
119 > /**
120 > * Tests whether the two authorities are the same
121 > */
122 > isEqualAuthority(a1: string, a2: string): boolean;
123 > /**
124 > * Returns true if the URI path has a trailing path separator
125 > */
126 > hasTrailingPathSeparator(resource: URI, sep?: string): boolean;
127 > /**
128 > * Removes a trailing path separator, if there's one.
129 > * Important: Doesn't remove the first slash, it would make the URI invalid
130 > */
131 > removeTrailingPathSeparator(resource: URI, sep?: string): URI;
132 > /**
133 > * Adds a trailing path separator to the URI if there isn't one already.
134 > * For example, c:\ would be unchanged, but c:\users would become c:\users\
135 > */
136 > addTrailingPathSeparator(resource: URI, sep?: string): URI;
137 > }
138 >
139 > export class ExtUri implements IExtUri {
140 >
141 > constructor(private _ignorePathCasing: (uri: URI) => boolean) { }
142 >
143 > compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number {
144 if (uri1 === uri2) {
145 return 0;
147 return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment));
148 }
149 > resources.ts
150 > isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment: boolean = false): boolean {
151 if (uri1 === uri2) {
152 return true;
157 return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment);
158 }
159 > resources.ts
160 > getComparisonKey(uri: URI, ignoreFragment: boolean = false): string {
161 return uri.with({
162 path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
164 }).toString();
165 }
166 > resources.ts
167 > ignorePathCasing(uri: URI): boolean {
168 return this._ignorePathCasing(uri);
169 }
170 > resources.ts
171 > isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean {
172 if (base.scheme === parentCandidate.scheme) {
173 if (base.scheme === Schemas.file) {
180 return false;
181 }
182 > resources.ts
183 > // --- path math
184 >
185 > joinPath(resource: URI, ...pathFragment: string[]): URI {
186 return URI.joinPath(resource, ...pathFragment);
187 }
188 > resources.ts
189 > basenameOrAuthority(resource: URI): string {
190 > return basename(resource) || resource.authority; resources.ts
191 > }
192 > resources.ts
193 > basename(resource: URI, suffix?: string): string {
194 > return paths.posix.basename(resource.path, suffix); resources.ts
195 > }
196 > resources.ts
197 > extname(resource: URI): string {
198 return paths.posix.extname(resource.path);
199 }
200 > resources.ts
201 > dirname(resource: URI): URI {
202 if (resource.path.length === 0) {
203 return resource;
217 });
218 }
219 > resources.ts
220 > normalizePath(resource: URI): URI {
221 if (!resource.path.length) {
222 return resource;
232 });
233 }
234 > resources.ts
235 > relativePath(from: URI, to: URI): string | undefined {
236 if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
237 return undefined;
257 return paths.posix.relative(fromPath, toPath);
258 }
259 > resources.ts
260 > resolvePath(base: URI, path: string): URI {
261 if (base.scheme === Schemas.file) {
262 const newURI = URI.file(paths.resolve(originalFSPath(base), path));
271 });
272 }
273 > resources.ts
274 > // --- misc
275 >
276 > isAbsolutePath(resource: URI): boolean {
277 return !!resource.path && resource.path[0] === '/';
278 }
279 > resources.ts
280 > isEqualAuthority(a1: string | undefined, a2: string | undefined) {
281 return a1 === a2 || (a1 !== undefined && a2 !== undefined && equalsIgnoreCase(a1, a2));
282 }
283 > resources.ts
284 > hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean {
285 if (resource.scheme === Schemas.file) {
286 const fsp = originalFSPath(resource);
291 }
292 }
293 > resources.ts
294 > removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
295 // Make sure that the path isn't a drive letter. A trailing separator there is not removable.
296 if (hasTrailingPathSeparator(resource, sep)) {
299 return resource;
300 }
301 > resources.ts
302 > addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
303 let isRootSep: boolean = false;
304 if (resource.scheme === Schemas.file) {
315 return resource;
316 }
317 > } resources.ts
318 >
319 >
320 > /**
321 > * Unbiased utility that takes uris "as they are". This means it can be interchanged with
322 > * uri#toString() usages. The following is true
323 > * ```
324 > * assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri))
325 > * ```
326 > */
327 > export const extUri = new ExtUri(() => false);
328 >
329 > /**
330 > * BIASED utility that _mostly_ ignored the case of urs paths. ONLY use this util if you
331 > * understand what you are doing.
332 > *
333 > * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
334 > *
335 > * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
336 > * because those uris come from a "trustworthy source". When creating unknown uris it's always
337 > * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
338 > * casing matters.
339 > */
340 > export const extUriBiasedIgnorePathCase = new ExtUri(uri => {
341 // A file scheme resource is in the same platform as code, so ignore case for non linux platforms
342 // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
343 return uri.scheme === Schemas.file ? !isLinux : true;
344 });
345 > resources.ts
346 >
347 > /**
348 > * BIASED utility that always ignores the casing of uris paths. ONLY use this util if you
349 > * understand what you are doing.
350 > *
351 > * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
352 > *
353 > * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
354 > * because those uris come from a "trustworthy source". When creating unknown uris it's always
355 > * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
356 > * casing matters.
357 > */
358 > export const extUriIgnorePathCase = new ExtUri(_ => true);
359 >
360 > export const isEqual = extUri.isEqual.bind(extUri);
361 > export const isEqualOrParent = extUri.isEqualOrParent.bind(extUri);
362 > export const getComparisonKey = extUri.getComparisonKey.bind(extUri);
363 > export const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri);
364 > export const basename = extUri.basename.bind(extUri);
365 > export const extname = extUri.extname.bind(extUri);
366 > export const dirname = extUri.dirname.bind(extUri);
367 > export const joinPath = extUri.joinPath.bind(extUri);
368 > export const normalizePath = extUri.normalizePath.bind(extUri);
369 > export const relativePath = extUri.relativePath.bind(extUri);
370 > export const resolvePath = extUri.resolvePath.bind(extUri);
371 > export const isAbsolutePath = extUri.isAbsolutePath.bind(extUri);
372 > export const isEqualAuthority = extUri.isEqualAuthority.bind(extUri);
373 > export const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri);
374 > export const removeTrailingPathSeparator = extUri.removeTrailingPathSeparator.bind(extUri);
375 > export const addTrailingPathSeparator = extUri.addTrailingPathSeparator.bind(extUri);
376 >
377 > //#endregion
378 >
379 > export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => URI): T[] {
380 const distinctParents: T[] = [];
381 for (let i = 0; i < items.length; i++) {
396 return distinctParents;
397 }
398 > resources.ts
399 > /**
400 > * Data URI related helpers.
401 > */
402 > export namespace DataUri {
403 >
404 > export const META_DATA_LABEL = 'label';
405 > export const META_DATA_DESCRIPTION = 'description';
406 > export const META_DATA_SIZE = 'size';
407 > export const META_DATA_MIME = 'mime';
408 >
409 > export function parseMetaData(dataUri: URI): Map<string, string> {
410 const metadata = new Map<string, string>();
411
429 return metadata;
430 }
431 > } resources.ts
432 >
433 > export function toLocalResource(resource: URI, authority: string | undefined, localScheme: string): URI {
434 if (authority) {
435 let path = resource.path;
src/vs/platform/userDataProfile/common/userDataProfile.ts 256 covered LOC · 28 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- userDataProfile.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 { hash } from '../../../base/common/hash.js';
7 > import { Emitter, Event } from '../../../base/common/event.js';
8 > import { Disposable } from '../../../base/common/lifecycle.js';
9 > import { basename, joinPath } from '../../../base/common/resources.js';
10 > import { URI, UriDto } from '../../../base/common/uri.js';
11 > import { localize } from '../../../nls.js';
12 > import { IEnvironmentService } from '../../environment/common/environment.js';
13 > import { FileOperationResult, IFileService, toFileOperationResult } from '../../files/common/files.js';
14 > import { createDecorator } from '../../instantiation/common/instantiation.js';
15 > import { ILogService } from '../../log/common/log.js';
16 > import { IAnyWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from '../../workspace/common/workspace.js';
17 > import { IStringDictionary } from '../../../base/common/collections.js';
18 > import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js';
19 > import { Promises } from '../../../base/common/async.js';
20 > import { generateUuid } from '../../../base/common/uuid.js';
21 > import { escapeRegExpCharacters } from '../../../base/common/strings.js';
22 > import { isString, Mutable } from '../../../base/common/types.js';
23 >
24 > export const AGENTS_WINDOW_PROFILE_ID = 'agents';
25 >
26 > const AGENTS_WINDOW_PROFILE_FLAGS: UseDefaultProfileFlags = {
27 > settings: true,
28 > keybindings: true,
29 > prompts: true,
30 > mcp: true,
31 > languageModels: true,
32 > snippets: true,
33 > tasks: true,
34 > extensions: true,
35 > };
36 >
37 > export const enum ProfileResourceType {
38 > Settings = 'settings',
39 > Keybindings = 'keybindings',
40 > Snippets = 'snippets',
41 > Prompts = 'prompts',
42 > Tasks = 'tasks',
43 > Extensions = 'extensions',
44 > GlobalState = 'globalState',
45 > Mcp = 'mcp',
46 > LanguageModels = 'languageModels',
47 > }
48 >
49 > /**
50 > * Flags to indicate whether to use the default profile or not.
51 > */
52 > export type UseDefaultProfileFlags = { [key in ProfileResourceType]?: boolean };
53 > export type ProfileResourceTypeFlags = UseDefaultProfileFlags;
54 > export type SettingValue = string | boolean | number | undefined | null | object;
55 > export type ISettingsDictionary = Record<string, SettingValue>;
56 >
57 > export interface IUserDataProfile {
58 > readonly id: string;
59 > readonly isDefault: boolean;
60 > readonly name: string;
61 > readonly icon?: string;
62 > readonly location: URI;
63 > readonly globalStorageHome: URI;
64 > readonly settingsResource: URI;
65 > readonly keybindingsResource: URI;
66 > readonly tasksResource: URI;
67 > readonly snippetsHome: URI;
68 > readonly promptsHome: URI;
69 > readonly extensionsResource: URI;
70 > readonly mcpResource: URI;
71 > readonly languageModelsResource: URI;
72 > readonly agentPluginsHome: URI;
73 > readonly cacheHome: URI;
74 > readonly useDefaultFlags?: UseDefaultProfileFlags;
75 > readonly isInternal?: boolean;
76 > readonly isTransient?: boolean;
77 > readonly isAgentsWindowProfile?: boolean;
78 > readonly workspaces?: readonly URI[];
79 > }
80 >
81 > export function isUserDataProfile(thing: unknown): thing is IUserDataProfile {
82 const candidate = thing as IUserDataProfile | undefined;
83
99 );
100 }
102 > export interface IParsedUserDataProfileTemplate {
103 > readonly name: string;
104 > readonly icon?: string;
105 > readonly settings?: ISettingsDictionary;
106 > readonly globalState?: IStringDictionary<string>;
107 > }
108 >
109 > export interface ISystemProfileTemplate extends IParsedUserDataProfileTemplate {
110 > readonly id: string;
111 > }
112 >
113 > export type DidChangeProfilesEvent = { readonly added: readonly IUserDataProfile[]; readonly removed: readonly IUserDataProfile[]; readonly updated: readonly IUserDataProfile[]; readonly all: readonly IUserDataProfile[] };
114 >
115 > export type WillCreateProfileEvent = {
116 > profile: IUserDataProfile;
117 > join(promise: Promise<void>): void;
118 > };
119 >
120 > export type WillRemoveProfileEvent = {
121 > profile: IUserDataProfile;
122 > join(promise: Promise<void>): void;
123 > };
124 >
125 > export interface IUserDataProfileOptions {
126 > readonly icon?: string;
127 > readonly useDefaultFlags?: UseDefaultProfileFlags;
128 > readonly transient?: boolean;
129 > readonly workspaces?: readonly URI[];
130 > }
131 >
132 > export interface IUserDataProfileUpdateOptions extends Omit<IUserDataProfileOptions, 'icon'> {
133 > readonly name?: string;
134 > readonly icon?: string | null;
135 > }
136 >
137 > export const IUserDataProfilesService = createDecorator<IUserDataProfilesService>('IUserDataProfilesService');
138 > export interface IUserDataProfilesService {
139 > readonly _serviceBrand: undefined;
140 >
141 > readonly profilesHome: URI;
142 > readonly defaultProfile: IUserDataProfile;
143 >
144 > readonly onDidChangeProfiles: Event<DidChangeProfilesEvent>;
145 > readonly profiles: readonly IUserDataProfile[];
146 >
147 > readonly onDidResetWorkspaces: Event<void>;
148 >
149 > createNamedProfile(name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile>;
150 > createTransientProfile(workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile>;
151 > createProfile(id: string, name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile>;
152 > updateProfile(profile: IUserDataProfile, options?: IUserDataProfileUpdateOptions,): Promise<IUserDataProfile>;
153 > removeProfile(profile: IUserDataProfile): Promise<void>;
154 >
155 > setProfileForWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier, profile: IUserDataProfile): Promise<void>;
156 > resetWorkspaces(): Promise<void>;
157 >
158 > cleanUp(): Promise<void>;
159 > cleanUpTransientProfiles(): Promise<void>;
160 > }
161 >
162 > export function reviveProfile(profile: UriDto<IUserDataProfile>, scheme: string): IUserDataProfile {
163 return {
164 id: profile.id,
185 };
186 }
188 > export function toUserDataProfile(id: string, name: string, location: URI, profilesCacheHome: URI, options?: IUserDataProfileOptions, defaultProfile?: IUserDataProfile): IUserDataProfile {
189 const isAgentsWindowProfile = id === AGENTS_WINDOW_PROFILE_ID;
190 return {
212 };
213 }
215 > export type UserDataProfilesObject = {
216 > profiles: IUserDataProfile[];
217 > emptyWindows: Map<string, IUserDataProfile>;
218 > };
219 >
220 > export type StoredUserDataProfile = {
221 > name: string;
222 > location: URI;
223 > icon?: string;
224 > useDefaultFlags?: UseDefaultProfileFlags;
225 > };
226 >
227 > export type StoredProfileAssociations = {
228 > workspaces?: IStringDictionary<string>;
229 > emptyWindows?: IStringDictionary<string>;
230 > };
231 >
232 > const SYSTEM_PROFILES_HOME = 'builtin';
233 >
234 > export class UserDataProfilesService extends Disposable implements IUserDataProfilesService {
235 >
236 > readonly _serviceBrand: undefined;
237 >
238 > protected static readonly PROFILES_KEY = 'userDataProfiles';
239 > protected static readonly PROFILE_ASSOCIATIONS_KEY = 'profileAssociations';
240 >
241 > readonly profilesHome: URI;
242 > private readonly profilesCacheHome: URI;
243 >
244 > get defaultProfile(): IUserDataProfile { return this.profiles[0]; }
245 > get profiles(): IUserDataProfile[] { return [...this.profilesObject.profiles, ...this.transientProfilesObject.profiles]; }
246 >
247 > protected readonly _onDidChangeProfiles = this._register(new Emitter<DidChangeProfilesEvent>());
248 > readonly onDidChangeProfiles = this._onDidChangeProfiles.event;
249 >
250 > protected readonly _onWillCreateProfile = this._register(new Emitter<WillCreateProfileEvent>());
251 > readonly onWillCreateProfile = this._onWillCreateProfile.event;
252 >
253 > protected readonly _onWillRemoveProfile = this._register(new Emitter<WillRemoveProfileEvent>());
254 > readonly onWillRemoveProfile = this._onWillRemoveProfile.event;
255 >
256 > private readonly _onDidResetWorkspaces = this._register(new Emitter<void>());
257 > readonly onDidResetWorkspaces = this._onDidResetWorkspaces.event;
258 >
259 > private profileCreationPromises = new Map<string, Promise<IUserDataProfile>>();
260 >
261 > protected readonly transientProfilesObject: UserDataProfilesObject = {
262 > profiles: [],
263 > emptyWindows: new Map()
264 > };
265 >
266 > constructor(
267 @IEnvironmentService protected environmentService: IEnvironmentService,
268 @IFileService protected fileService: IFileService,
274 this.profilesCacheHome = joinPath(this.environmentService.cacheHome, 'CachedProfilesData');
275 }
277 > init(): void {
278 this._profilesObject = undefined;
279 }
281 > protected _profilesObject: UserDataProfilesObject | undefined;
282 > protected get profilesObject(): UserDataProfilesObject {
283 if (!this._profilesObject) {
284 const defaultProfile = this.createDefaultProfile();
336 return this._profilesObject;
337 }
339 > private isInvalidProfile(storedProfile: StoredUserDataProfile): boolean {
340 if (!storedProfile.name) {
341 return true;
349 return false;
350 }
352 > protected createDefaultProfile() {
353 const defaultProfile = toUserDataProfile('__default__profile__', localize('defaultProfile', "Default"), this.environmentService.userRoamingDataHome, this.profilesCacheHome);
354 return { ...defaultProfile, extensionsResource: this.getDefaultProfileExtensionsLocation() ?? defaultProfile.extensionsResource, isDefault: true };
355 }
357 > async createTransientProfile(workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
358 const namePrefix = `Temp`;
359 const nameRegEx = new RegExp(`${escapeRegExpCharacters(namePrefix)}\\s(\\d+)`);
367 return this.createProfile(hash(generateUuid()).toString(16), name, { transient: true }, workspaceIdentifier);
368 }
370 > async createNamedProfile(name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
371 return this.createProfile(hash(generateUuid()).toString(16), name, options, workspaceIdentifier);
372 }
374 > async createProfile(id: string, name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
375 const profile = await this.doCreateProfile(id, name, options, workspaceIdentifier);
376
377 return profile;
378 }
380 > private async doCreateProfile(id: string, name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
381 if (!isString(name) || !name) {
382 throw new Error('Name of the profile is mandatory and must be of type `string`');
428 return profileCreationPromise;
429 }
431 > async updateProfile(profile: IUserDataProfile, options: IUserDataProfileUpdateOptions): Promise<IUserDataProfile> {
432 if (profile.isAgentsWindowProfile) {
433 throw new Error('Cannot update agents window profile');
481 return updatedProfile;
482 }
484 > async removeProfile(profileToRemove: IUserDataProfile): Promise<void> {
485 if (profileToRemove.isDefault) {
486 throw new Error('Cannot remove default profile');
515 }
516 }
518 > async setProfileForWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier, profileToSet: IUserDataProfile): Promise<void> {
519 const profile = this.profiles.find(p => p.id === profileToSet.id);
520 if (!profile) {
534 }
535 }
537 > unsetWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier, transient: boolean = false): void {
538 const workspace = this.getWorkspace(workspaceIdentifier);
539 if (URI.isUri(workspace)) {
547 }
548 }
550 > async resetWorkspaces(): Promise<void> {
551 this.transientProfilesObject.emptyWindows.clear();
552 this.profilesObject.emptyWindows.clear();
557 this._onDidResetWorkspaces.fire();
558 }
560 > async cleanUp(): Promise<void> {
561 try {
562 if (await this.fileService.exists(this.profilesHome)) {
587 }
588 }
590 > async cleanUpTransientProfiles(): Promise<void> {
591 const unAssociatedTransientProfiles = this.transientProfilesObject.profiles.filter(p => !this.isProfileAssociatedToWorkspace(p));
592 await Promise.allSettled(unAssociatedTransientProfiles.map(p => this.removeProfile(p)));
593 }
595 > getProfileForWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier): IUserDataProfile | undefined {
596 const workspace = this.getWorkspace(workspaceIdentifier);
597
604 : (this.profilesObject.emptyWindows.get(workspace) ?? this.transientProfilesObject.emptyWindows.get(workspace));
605 }
607 > protected getWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier): URI | string {
608 if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
609 return workspaceIdentifier.uri;
614 return workspaceIdentifier.id;
615 }
617 > private isProfileAssociatedToWorkspace(profile: IUserDataProfile): boolean {
618 if (profile.workspaces?.length) {
619 return true;
627 return false;
628 }
630 > private updateProfiles(added: IUserDataProfile[], removed: IUserDataProfile[], updated: IUserDataProfile[], donotTrigger: boolean = false): void {
631 const allProfiles: Mutable<IUserDataProfile>[] = [...this.profiles, ...added];
632
679 }
680 }
682 > protected triggerProfilesChanges(added: IUserDataProfile[], removed: IUserDataProfile[], updated: IUserDataProfile[]) {
683 this._onDidChangeProfiles.fire({ added, removed, updated, all: this.profiles });
684 }
686 > private updateEmptyWindowAssociation(windowId: string, newProfile: IUserDataProfile | undefined, transient: boolean): void {
687 // Force transient if the new profile to associate is transient
688 transient = newProfile?.isTransient ? true : transient;
706 }
707 }
709 > private updateStoredProfiles(profiles: IUserDataProfile[]): void {
710 const storedProfiles: StoredUserDataProfile[] = [];
711 const workspaces: IStringDictionary<string> = {};
739 this._profilesObject = undefined;
740 }
742 > protected getStoredProfiles(): StoredUserDataProfile[] { return []; }
743 > protected saveStoredProfiles(storedProfiles: StoredUserDataProfile[]): void { throw new Error('not implemented'); }
744 >
745 > protected getStoredProfileAssociations(): StoredProfileAssociations { return {}; }
746 > protected saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void { throw new Error('not implemented'); }
747 > protected getDefaultProfileExtensionsLocation(): URI | undefined { return undefined; }
748 > }
749 >
750 > export class InMemoryUserDataProfilesService extends UserDataProfilesService {
751 private storedProfiles: StoredUserDataProfile[] = [];
752 protected override getStoredProfiles(): StoredUserDataProfile[] { return this.storedProfiles; }
754
755 private storedProfileAssociations: StoredProfileAssociations = {};
756 > protected override getStoredProfileAssociations(): StoredProfileAssociations { return this.storedProfileAssociations; } userDataProfile.ts
757 > protected override saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void { this.storedProfileAssociations = storedProfileAssociations; }
758 > }
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/platform/theme/common/iconRegistry.ts 247 covered LOC · 15 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- iconRegistry.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 { RunOnceScheduler } from '../../../base/common/async.js';
7 > import { Codicon } from '../../../base/common/codicons.js';
8 > import { getCodiconFontCharacters } from '../../../base/common/codiconsUtil.js';
9 > import { ThemeIcon, IconIdentifier } from '../../../base/common/themables.js';
10 > import { Emitter, Event } from '../../../base/common/event.js';
11 > import { IJSONSchema, IJSONSchemaMap } from '../../../base/common/jsonSchema.js';
12 > import { isString } from '../../../base/common/types.js';
13 > import { URI } from '../../../base/common/uri.js';
14 > import { localize } from '../../../nls.js';
15 > import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../jsonschemas/common/jsonContributionRegistry.js';
16 > import * as platform from '../../registry/common/platform.js';
17 > import { Disposable } from '../../../base/common/lifecycle.js';
18 >
19 > // ------ API types
20 >
21 >
22 > // icon registry
23 > export const Extensions = {
24 > IconContribution: 'base.contributions.icons'
25 > };
26 >
27 > export type IconDefaults = ThemeIcon | IconDefinition;
28 >
29 > export interface IconDefinition {
30 > readonly font?: IconFontContribution; // undefined for the default font (codicon)
31 > readonly fontCharacter: string;
32 > }
33 >
34 >
35 > export interface IconContribution {
36 > readonly id: string;
37 > description: string | undefined;
38 > readonly deprecationMessage?: string;
39 > readonly defaults: IconDefaults;
40 > }
41 >
42 > export namespace IconContribution {
43 > export function getDefinition(contribution: IconContribution, registry: IIconRegistry): IconDefinition | undefined {
44 let definition = contribution.defaults;
45 while (ThemeIcon.isThemeIcon(definition)) {
52 return definition;
53 }
55 >
56 > export interface IconFontContribution {
57 > readonly id: string;
58 > readonly definition: IconFontDefinition;
59 > }
60 >
61 > export interface IconFontDefinition {
62 > readonly weight?: string;
63 > readonly style?: string;
64 > readonly src: IconFontSource[];
65 > }
66 >
67 > export namespace IconFontDefinition {
68 > export function toJSONObject(iconFont: IconFontDefinition): any {
69 return {
70 weight: iconFont.weight,
73 };
74 }
75 > export function fromJSONObject(json: any): IconFontDefinition | undefined { iconRegistry.ts
76 const stringOrUndef = (s: any) => isString(s) ? s : undefined;
77 if (json && Array.isArray(json.src) && json.src.every((s: any) => isString(s.format) && isString(s.location))) {
84 return undefined;
85 }
87 >
88 >
89 > export interface IconFontSource {
90 > readonly location: URI;
91 > readonly format: string;
92 > }
93 >
94 > export interface IIconRegistry {
95 >
96 > readonly onDidChange: Event<void>;
97 >
98 > /**
99 > * Register a icon to the registry.
100 > * @param id The icon id
101 > * @param defaults The default values
102 > * @param description The description
103 > */
104 > registerIcon(id: IconIdentifier, defaults: IconDefaults, description?: string): ThemeIcon;
105 >
106 > /**
107 > * Deregister a icon from the registry.
108 > */
109 > deregisterIcon(id: IconIdentifier): void;
110 >
111 > /**
112 > * Get all icon contributions
113 > */
114 > getIcons(): IconContribution[];
115 >
116 > /**
117 > * Get the icon for the given id
118 > */
119 > getIcon(id: IconIdentifier): IconContribution | undefined;
120 >
121 > /**
122 > * JSON schema for an object to assign icon values to one of the icon contributions.
123 > */
124 > getIconSchema(): IJSONSchema;
125 >
126 > /**
127 > * JSON schema to for a reference to a icon contribution.
128 > */
129 > getIconReferenceSchema(): IJSONSchema;
130 >
131 > /**
132 > * Register a icon font to the registry.
133 > * @param id The icon font id
134 > * @param definition The icon font definition
135 > */
136 > registerIconFont(id: string, definition: IconFontDefinition): IconFontDefinition;
137 >
138 > /**
139 > * Deregister an icon font to the registry.
140 > */
141 > deregisterIconFont(id: string): void;
142 >
143 > /**
144 > * Get the icon font for the given id
145 > */
146 > getIconFont(id: string): IconFontDefinition | undefined;
147 > }
148 >
149 > // regexes for validation of font properties
150 >
151 > export const fontIdRegex = /^([\w_-]+)$/;
152 > export const fontStyleRegex = /^(normal|italic|(oblique[ \w\s-]+))$/;
153 > export const fontWeightRegex = /^(normal|bold|lighter|bolder|(\d{0-1000}))$/;
154 > export const fontSizeRegex = /^([\w_.%+-]+)$/;
155 > export const fontFormatRegex = /^woff|woff2|truetype|opentype|embedded-opentype|svg$/;
156 > export const fontColorRegex = /^#[0-9a-fA-F]{0,6}$/;
157 >
158 > export const fontIdErrorMessage = localize('schema.fontId.formatError', 'The font ID must only contain letters, numbers, underscores and dashes.');
159 >
160 > class IconRegistry extends Disposable implements IIconRegistry {
161 >
162 > private readonly _onDidChange = this._register(new Emitter<void>());
163 > readonly onDidChange: Event<void> = this._onDidChange.event;
164 >
165 > private iconsById: { [key: string]: IconContribution };
166 > private iconSchema: IJSONSchema & { properties: IJSONSchemaMap } = {
167 > definitions: {
168 > icons: {
169 > type: 'object',
170 > properties: {
171 > fontId: { type: 'string', description: localize('iconDefinition.fontId', 'The id of the font to use. If not set, the font that is defined first is used.'), pattern: fontIdRegex.source, patternErrorMessage: fontIdErrorMessage },
172 > fontCharacter: { type: 'string', description: localize('iconDefinition.fontCharacter', 'The font character associated with the icon definition.') }
173 > },
174 > additionalProperties: false,
175 > defaultSnippets: [{ body: { fontCharacter: '\\\\e030' } }]
176 > }
177 > },
178 > type: 'object',
179 > properties: {}
180 > };
181 > private iconReferenceSchema: IJSONSchema & { enum: string[]; enumDescriptions: string[] } = { type: 'string', pattern: `^${ThemeIcon.iconNameExpression}$`, enum: [], enumDescriptions: [] };
182 >
183 > private iconFontsById: { [key: string]: IconFontDefinition };
184 >
185 > constructor() {
186 > super();
187 > this.iconsById = {};
188 > this.iconFontsById = {};
189 > }
190 >
191 > public registerIcon(id: string, defaults: IconDefaults, description?: string, deprecationMessage?: string): ThemeIcon {
192 > const existing = this.iconsById[id];
193 > if (existing) {
194 if (description && !existing.description) {
195 existing.description = description;
203 return existing;
204 }
205 > const iconContribution: IconContribution = { id, description, defaults, deprecationMessage }; iconRegistry.ts
206 > this.iconsById[id] = iconContribution;
207 > const propertySchema: IJSONSchema = { $ref: '#/definitions/icons' };
208 > if (deprecationMessage) {
209 propertySchema.deprecationMessage = deprecationMessage;
210 }
211 > if (description) { iconRegistry.ts
212 > propertySchema.markdownDescription = `${description}: $(${id})`;
213 > }
214 > this.iconSchema.properties[id] = propertySchema;
215 > this.iconReferenceSchema.enum.push(id);
216 > this.iconReferenceSchema.enumDescriptions.push(description || '');
217 >
218 > this._onDidChange.fire();
219 > return { id };
220 > }
221 >
222 >
223 > public deregisterIcon(id: string): void {
224 delete this.iconsById[id];
225 delete this.iconSchema.properties[id];
231 this._onDidChange.fire();
232 }
234 > public getIcons(): IconContribution[] {
235 return Object.keys(this.iconsById).map(id => this.iconsById[id]);
236 }
238 > public getIcon(id: string): IconContribution | undefined {
239 return this.iconsById[id];
240 }
242 > public getIconSchema(): IJSONSchema {
243 > return this.iconSchema;
244 > }
245 >
246 > public getIconReferenceSchema(): IJSONSchema {
247 return this.iconReferenceSchema;
248 }
250 > public registerIconFont(id: string, definition: IconFontDefinition): IconFontDefinition {
251 const existing = this.iconFontsById[id];
252 if (existing) {
257 return definition;
258 }
260 > public deregisterIconFont(id: string): void {
261 delete this.iconFontsById[id];
262 }
264 > public getIconFont(id: string): IconFontDefinition | undefined {
265 return this.iconFontsById[id];
266 }
268 > public override toString() {
269 const sorter = (i1: IconContribution, i2: IconContribution) => {
270 return i1.id.localeCompare(i2.id);
297 return reference.join('\n');
298 }
300 > }
301 >
302 > const iconRegistry = new IconRegistry();
303 > platform.Registry.add(Extensions.IconContribution, iconRegistry);
304 >
305 > export function registerIcon(id: string, defaults: IconDefaults, description: string, deprecationMessage?: string): ThemeIcon {
306 > return iconRegistry.registerIcon(id, defaults, description, deprecationMessage);
307 > }
308 >
309 > export function getIconRegistry(): IIconRegistry {
310 return iconRegistry;
311 }
313 > function initialize() {
314 > const codiconFontCharacters = getCodiconFontCharacters();
315 > for (const icon in codiconFontCharacters) {
316 > const fontCharacter = '\\' + codiconFontCharacters[icon].toString(16);
317 > iconRegistry.registerIcon(icon, { fontCharacter });
318 > }
319 > }
320 > initialize();
321 >
322 > export const iconsSchemaId = 'vscode://schemas/icons';
323 >
324 > const schemaRegistry = platform.Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
325 > schemaRegistry.registerSchema(iconsSchemaId, iconRegistry.getIconSchema());
326 >
327 > const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(iconsSchemaId), 200);
328 > iconRegistry.onDidChange(() => {
329 > if (!delayer.isScheduled()) {
330 > delayer.schedule();
331 > }
332 > });
333 >
334 > //setTimeout(_ => console.log(iconRegistry.toString()), 5000);
335 >
336 >
337 > // common icons
338 >
339 > export const widgetClose = registerIcon('widget-close', Codicon.close, localize('widgetClose', 'Icon for the close action in widgets.'));
340 >
341 > export const gotoPreviousLocation = registerIcon('goto-previous-location', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for goto previous editor location.'));
342 > export const gotoNextLocation = registerIcon('goto-next-location', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for goto next editor location.'));
343 >
344 > export const syncing = ThemeIcon.modify(Codicon.sync, 'spin');
345 > export const spinningLoading = ThemeIcon.modify(Codicon.loading, 'spin');
src/vs/base/parts/storage/common/storage.ts 231 covered LOC · 39 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- storage.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 { ThrottledDelayer } from '../../../common/async.js';
7 > import { Event, PauseableEmitter } from '../../../common/event.js';
8 > import { Disposable, IDisposable } from '../../../common/lifecycle.js';
9 > import { parse, stringify } from '../../../common/marshalling.js';
10 > import { isObject, isUndefined, isUndefinedOrNull } from '../../../common/types.js';
11 >
12 > export enum StorageHint {
13 >
14 > // A hint to the storage that the storage
15 > // does not exist on disk yet. This allows
16 > // the storage library to improve startup
17 > // time by not checking the storage for data.
18 > STORAGE_DOES_NOT_EXIST,
19 >
20 > // A hint to the storage that the storage
21 > // is backed by an in-memory storage.
22 > STORAGE_IN_MEMORY
23 > }
24 >
25 > export interface IStorageOptions {
26 > readonly hint?: StorageHint;
27 > }
28 >
29 > export interface IUpdateRequest {
30 > readonly insert?: Map<string, string>;
31 > readonly delete?: Set<string>;
32 > }
33 >
34 > export interface IStorageItemsChangeEvent {
35 > readonly changed?: Map<string, string>;
36 > readonly deleted?: Set<string>;
37 > }
38 >
39 > export function isStorageItemsChangeEvent(thing: unknown): thing is IStorageItemsChangeEvent {
40 const candidate = thing as IStorageItemsChangeEvent | undefined;
41
42 return candidate?.changed instanceof Map || candidate?.deleted instanceof Set;
43 }
44 > storage.ts
45 > export interface IStorageDatabase {
46 >
47 > readonly onDidChangeItemsExternal: Event<IStorageItemsChangeEvent>;
48 >
49 > getItems(): Promise<Map<string, string>>;
50 > updateItems(request: IUpdateRequest): Promise<void>;
51 >
52 > optimize(): Promise<void>;
53 >
54 > close(recovery?: () => Map<string, string>): Promise<void>;
55 > }
56 >
57 > export interface IStorageChangeEvent {
58 >
59 > /**
60 > * The `key` of the storage entry that was changed
61 > * or was removed.
62 > */
63 > readonly key: string;
64 >
65 > /**
66 > * A hint how the storage change event was triggered. If
67 > * `true`, the storage change was triggered by an external
68 > * source, such as:
69 > * - another process (for example another window)
70 > * - operations such as settings sync or profiles change
71 > */
72 > readonly external?: boolean;
73 > }
74 >
75 > export type StorageValue = string | boolean | number | undefined | null | object;
76 >
77 > export interface IStorage extends IDisposable {
78 >
79 > readonly onDidChangeStorage: Event<IStorageChangeEvent>;
80 >
81 > readonly items: Map<string, string>;
82 > readonly size: number;
83 >
84 > init(): Promise<void>;
85 >
86 > get(key: string, fallbackValue: string): string;
87 > get(key: string, fallbackValue?: string): string | undefined;
88 >
89 > getBoolean(key: string, fallbackValue: boolean): boolean;
90 > getBoolean(key: string, fallbackValue?: boolean): boolean | undefined;
91 >
92 > getNumber(key: string, fallbackValue: number): number;
93 > getNumber(key: string, fallbackValue?: number): number | undefined;
94 >
95 > getObject<T extends object>(key: string, fallbackValue: T): T;
96 > getObject<T extends object>(key: string, fallbackValue?: T): T | undefined;
97 >
98 > set(key: string, value: StorageValue, external?: boolean): Promise<void>;
99 > delete(key: string, external?: boolean): Promise<void>;
100 >
101 > flush(delay?: number): Promise<void>;
102 > whenFlushed(): Promise<void>;
103 >
104 > optimize(): Promise<void>;
105 >
106 > close(): Promise<void>;
107 > }
108 >
109 > export enum StorageState {
110 > None,
111 > Initialized,
112 > Closed
113 > }
114 >
115 > export class Storage extends Disposable implements IStorage {
116 >
117 > private static readonly DEFAULT_FLUSH_DELAY = 100;
118 >
119 > private readonly _onDidChangeStorage = this._register(new PauseableEmitter<IStorageChangeEvent>());
120 > readonly onDidChangeStorage = this._onDidChangeStorage.event;
121 >
122 > private state = StorageState.None;
123 >
124 > private cache = new Map<string, string>();
125 >
126 > private readonly flushDelayer = this._register(new ThrottledDelayer<void>(Storage.DEFAULT_FLUSH_DELAY));
127 >
128 > private pendingDeletes = new Set<string>();
129 > private pendingInserts = new Map<string, string>();
130 >
131 > private pendingClose: Promise<void> | undefined = undefined;
132 >
133 > private readonly whenFlushedCallbacks: Function[] = [];
134 >
135 > constructor(
136 > protected readonly database: IStorageDatabase, storage.ts
137 > private readonly options: IStorageOptions = Object.create(null)
138 > ) {
139 > super();
140 >
141 > this.registerListeners();
142 > }
143 > storage.ts
144 > private registerListeners(): void {
145 > this._register(this.database.onDidChangeItemsExternal(e => this.onDidChangeItemsExternal(e))); storage.ts
146 > }
147 > storage.ts
148 > private onDidChangeItemsExternal(e: IStorageItemsChangeEvent): void {
149 this._onDidChangeStorage.pause();
150
161 }
162 }
163 > storage.ts
164 > private acceptExternal(key: string, value: string | undefined): void {
165 if (this.state === StorageState.Closed) {
166 return; // Return early if we are already closed
188 }
189 }
190 > storage.ts
191 > get items(): Map<string, string> {
192 return this.cache;
193 }
194 > storage.ts
195 > get size(): number {
196 return this.cache.size;
197 }
198 > storage.ts
199 > async init(): Promise<void> {
200 if (this.state !== StorageState.None) {
201 return; // either closed or already initialized
213 this.cache = await this.database.getItems();
214 }
215 > storage.ts
216 > get(key: string, fallbackValue: string): string;
217 > get(key: string, fallbackValue?: string): string | undefined;
218 > get(key: string, fallbackValue?: string): string | undefined {
219 > const value = this.cache.get(key); storage.ts
220 >
221 > if (isUndefinedOrNull(value)) {
222 > return fallbackValue; storage.ts
223 > }
224
225 return value;
226 > } storage.ts
227 > storage.ts
228 > getBoolean(key: string, fallbackValue: boolean): boolean;
229 > getBoolean(key: string, fallbackValue?: boolean): boolean | undefined;
230 > getBoolean(key: string, fallbackValue?: boolean): boolean | undefined {
231 const value = this.get(key);
232
237 return value === 'true';
238 }
239 > storage.ts
240 > getNumber(key: string, fallbackValue: number): number;
241 > getNumber(key: string, fallbackValue?: number): number | undefined;
242 > getNumber(key: string, fallbackValue?: number): number | undefined {
243 const value = this.get(key);
244
249 return parseInt(value, 10);
250 }
251 > storage.ts
252 > getObject(key: string, fallbackValue: object): object;
253 > getObject(key: string, fallbackValue?: object | undefined): object | undefined;
254 > getObject(key: string, fallbackValue?: object): object | undefined {
255 const value = this.get(key);
256
261 return parse(value);
262 }
263 > storage.ts
264 > async set(key: string, value: string | boolean | number | null | undefined | object, external = false): Promise<void> {
265 if (this.state === StorageState.Closed) {
266 return; // Return early if we are already closed
292 return this.doFlush();
293 }
294 > storage.ts
295 > async delete(key: string, external = false): Promise<void> {
296 if (this.state === StorageState.Closed) {
297 return; // Return early if we are already closed
316 return this.doFlush();
317 }
318 > storage.ts
319 > async optimize(): Promise<void> {
320 if (this.state === StorageState.Closed) {
321 return; // Return early if we are already closed
328 return this.database.optimize();
329 }
330 > storage.ts
331 > async close(): Promise<void> {
332 if (!this.pendingClose) {
333 this.pendingClose = this.doClose();
336 return this.pendingClose;
337 }
338 > storage.ts
339 > private async doClose(): Promise<void> {
340
341 // Update state
356 await this.database.close(() => this.cache);
357 }
358 > storage.ts
359 > private get hasPending() {
360 return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0;
361 }
362 > storage.ts
363 > private async flushPending(): Promise<void> {
364 if (!this.hasPending) {
365 return; // return early if nothing to do
383 });
384 }
385 > storage.ts
386 > async flush(delay?: number): Promise<void> {
387 if (
388 this.state === StorageState.Closed || // Return early if we are already closed
394 return this.doFlush(delay);
395 }
396 > storage.ts
397 > private async doFlush(delay?: number): Promise<void> {
398 if (this.options.hint === StorageHint.STORAGE_IN_MEMORY) {
399 return this.flushPending(); // return early if in-memory
402 return this.flushDelayer.trigger(() => this.flushPending(), delay);
403 }
404 > storage.ts
405 > async whenFlushed(): Promise<void> {
406 if (!this.hasPending) {
407 return; // return early if nothing to do
410 return new Promise(resolve => this.whenFlushedCallbacks.push(resolve));
411 }
412 > storage.ts
413 > isInMemory(): boolean {
414 return this.options.hint === StorageHint.STORAGE_IN_MEMORY;
415 }
416 > } storage.ts
417 >
418 > export class InMemoryStorageDatabase implements IStorageDatabase {
419 > storage.ts
420 > readonly onDidChangeItemsExternal = Event.None;
421 >
422 > private readonly items = new Map<string, string>();
423 > storage.ts
424 > async getItems(): Promise<Map<string, string>> {
425 return this.items;
426 }
427 > storage.ts
428 > async updateItems(request: IUpdateRequest): Promise<void> {
429 request.insert?.forEach((value, key) => this.items.set(key, value));
430
431 request.delete?.forEach(key => this.items.delete(key));
432 }
433 > storage.ts
434 > async optimize(): Promise<void> { }
435 > async close(): Promise<void> { }
436 > }
437 >
438 >
439 > export const MIGRATED_KEY = '__$__migratedStorageMarker';
440 >
441 > export class MigratingStorage extends Storage {
442
443 private migratedKeys: Set<string> = new Set();
444 private fallbackStorage: IStorage | undefined = undefined;
445 private isFallbackStorageReadonly: boolean = false;
446 > storage.ts
447 > override async init(): Promise<void> {
448 await super.init();
449
451 this.migratedKeys = this.loadMigratedKeys();
452 }
453 > storage.ts
454 > public setFallbackStorage(storage: IStorage, isReadonly: boolean): void {
455 this.fallbackStorage = storage;
456 this.isFallbackStorageReadonly = isReadonly;
457 }
458 > storage.ts
459 > private static readonly INTERNAL_KEY_PREFIX = '__$__';
460 >
461 > override get(key: string, fallbackValue: string): string;
462 > override get(key: string, fallbackValue?: string): string | undefined;
463 > override get(key: string, fallbackValue?: string): string | undefined {
464 if (!key.startsWith(MigratingStorage.INTERNAL_KEY_PREFIX) && !this.migratedKeys.has(key) && isUndefined(super.get(key))) {
465 // Check fallback storage and auto-migrate on hit.
480 return super.get(key, fallbackValue);
481 }
482 > storage.ts
483 > private loadMigratedKeys(): Set<string> {
484 const raw = super.get(MIGRATED_KEY);
485 if (raw) {
492 return new Set();
493 }
494 > storage.ts
495 > private persistMigratedKeys(): void {
496 this.set(MIGRATED_KEY, JSON.stringify([...this.migratedKeys]));
497 }
498 > } storage.ts
499
src/vs/nls.ts 224 covered LOC · 19 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);
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; nls.ts
143 > if (typeof data === 'number') {
144 message = lookupMessage(data, originalMessage);
145 > } else { nls.ts
146 > message = originalMessage;
147 > }
148 >
149 > const value = _format(message, args);
150 >
151 > return {
152 > value,
153 > original: originalMessage === message ? value : _format(originalMessage, args)
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/workbench/services/workingCopy/common/workingCopy.ts 221 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workingCopy.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 { URI } from '../../../../base/common/uri.js';
8 > import { ISaveOptions, IRevertOptions, SaveReason, SaveSource } from '../../../common/editor.js';
9 > import { CancellationToken } from '../../../../base/common/cancellation.js';
10 > import { VSBufferReadable, VSBufferReadableStream } from '../../../../base/common/buffer.js';
11 >
12 > export const enum WorkingCopyCapabilities {
13 >
14 > /**
15 > * Signals no specific capability for the working copy.
16 > */
17 > None = 0,
18 >
19 > /**
20 > * Signals that the working copy requires
21 > * additional input when saving, e.g. an
22 > * associated path to save to.
23 > */
24 > Untitled = 1 << 1,
25 >
26 > /**
27 > * The working copy will not indicate that
28 > * it is dirty and unsaved content will be
29 > * discarded without prompting if closed.
30 > */
31 > Scratchpad = 1 << 2
32 > }
33 >
34 > /**
35 > * Data to be associated with working copy backups. Use
36 > * `IWorkingCopyBackupService.resolve(workingCopy)` to
37 > * retrieve the backup when loading the working copy.
38 > */
39 > export interface IWorkingCopyBackup {
40 >
41 > /**
42 > * Any serializable metadata to be associated with the backup.
43 > */
44 > meta?: IWorkingCopyBackupMeta;
45 >
46 > /**
47 > * The actual snapshot of the contents of the working copy at
48 > * the time the backup was made.
49 > */
50 > content?: VSBufferReadable | VSBufferReadableStream;
51 > }
52 >
53 > /**
54 > * Working copy backup metadata that can be associated
55 > * with the backup.
56 > *
57 > * Some properties may be reserved as outlined here and
58 > * cannot be used.
59 > */
60 > export interface IWorkingCopyBackupMeta {
61 >
62 > /**
63 > * Any property needs to be serializable through JSON.
64 > */
65 > [key: string]: unknown;
66 >
67 > /**
68 > * `typeId` is a reserved property that cannot be used
69 > * as backup metadata.
70 > */
71 > typeId?: never;
72 > }
73 >
74 > /**
75 > * @deprecated it is important to provide a type identifier
76 > * for working copies to enable all capabilities.
77 > */
78 > export const NO_TYPE_ID = '';
79 >
80 > /**
81 > * Every working copy has in common that it is identified by
82 > * a resource `URI` and a `typeId`. There can only be one
83 > * working copy registered with the same `URI` and `typeId`.
84 > */
85 > export interface IWorkingCopyIdentifier {
86 >
87 > /**
88 > * The type identifier of the working copy for grouping
89 > * working copies of the same domain together.
90 > *
91 > * There can only be one working copy for a given resource
92 > * and type identifier.
93 > */
94 > readonly typeId: string;
95 >
96 > /**
97 > * The resource of the working copy must be unique for
98 > * working copies of the same `typeId`.
99 > */
100 > readonly resource: URI;
101 > }
102 >
103 > export interface IWorkingCopySaveEvent {
104 >
105 > /**
106 > * The reason why the working copy was saved.
107 > */
108 > readonly reason?: SaveReason;
109 >
110 > /**
111 > * The source of the working copy save request.
112 > */
113 > readonly source?: SaveSource;
114 > }
115 >
116 > /**
117 > * A working copy is an abstract concept to unify handling of
118 > * data that can be worked on (e.g. edited) in an editor.
119 > *
120 > *
121 > * A working copy resource may be the backing store of the data
122 > * (e.g. a file on disk), but that is not a requirement. If
123 > * your working copy is file based, consider to use the
124 > * `IFileWorkingCopy` instead that simplifies a lot of things
125 > * when working with file based working copies.
126 > */
127 > export interface IWorkingCopy extends IWorkingCopyIdentifier {
128 >
129 > /**
130 > * Human readable name of the working copy.
131 > */
132 > readonly name: string;
133 >
134 > /**
135 > * The capabilities of the working copy.
136 > */
137 > readonly capabilities: WorkingCopyCapabilities;
138 >
139 >
140 > //#region Events
141 >
142 > /**
143 > * Used by the workbench to signal if the working copy
144 > * is dirty or not. Typically a working copy is dirty
145 > * once changed until saved or reverted.
146 > */
147 > readonly onDidChangeDirty: Event<void>;
148 >
149 > /**
150 > * Used by the workbench e.g. to trigger auto-save
151 > * (unless this working copy is untitled) and backups.
152 > */
153 > readonly onDidChangeContent: Event<void>;
154 >
155 > /**
156 > * Used by the workbench e.g. to track local history
157 > * (unless this working copy is untitled).
158 > */
159 > readonly onDidSave: Event<IWorkingCopySaveEvent>;
160 >
161 > //#endregion
162 >
163 >
164 > //#region Dirty Tracking
165 >
166 > /**
167 > * Indicates that the file has unsaved changes
168 > * and should confirm before closing.
169 > */
170 > isDirty(): boolean;
171 >
172 > /**
173 > * Indicates that the file has unsaved changes.
174 > * Used for backup tracking and accounts for
175 > * working copies that are never dirty e.g.
176 > * scratchpads.
177 > */
178 > isModified(): boolean;
179 >
180 > //#endregion
181 >
182 >
183 > //#region Save / Backup
184 >
185 > /**
186 > * The delay in milliseconds to wait before triggering
187 > * a backup after the content of the model has changed.
188 > *
189 > * If not configured, a sensible default will be taken
190 > * based on user settings.
191 > */
192 > readonly backupDelay?: number;
193 >
194 > /**
195 > * The workbench may call this method often after it receives
196 > * the `onDidChangeContent` event for the working copy. The motivation
197 > * is to allow to quit VSCode with dirty working copies present.
198 > *
199 > * Providers of working copies should use `IWorkingCopyBackupService.resolve(workingCopy)`
200 > * to retrieve the backup metadata associated when loading the working copy.
201 > *
202 > * @param token support for cancellation
203 > */
204 > backup(token: CancellationToken): Promise<IWorkingCopyBackup>;
205 >
206 > /**
207 > * Asks the working copy to save. If the working copy was dirty, it is
208 > * expected to be non-dirty after this operation has finished.
209 > *
210 > * @returns `true` if the operation was successful and `false` otherwise.
211 > */
212 > save(options?: ISaveOptions): Promise<boolean>;
213 >
214 > /**
215 > * Asks the working copy to revert. If the working copy was dirty, it is
216 > * expected to be non-dirty after this operation has finished.
217 > */
218 > revert(options?: IRevertOptions): Promise<void>;
219 >
220 > //#endregion
221 > }
src/vs/base/test/common/virtualScheduling/processor.ts 215 covered LOC · 19 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- processor.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 } from '../../../common/cancellation.js';
7 > import { Disposable, DisposableStore, IDisposable } from '../../../common/lifecycle.js';
8 > import { Embedding, nextMacrotask } from './embedding.js';
9 > import { TimeApi } from './timeApi.js';
10 > import { ROOT_TRACE, TraceContext } from './trace.js';
11 > import { EventSource, VirtualClock, VirtualEvent, VirtualTime } from './virtualClock.js';
12 >
13 > // ============================================================================
14 > // Termination policy
15 > // ============================================================================
16 >
17 > /**
18 > * When a {@link Run} should terminate.
19 > *
20 > * Greenfield design choice: termination is *always* explicit. There is no
21 > * "bare run()" that terminates on first empty queue, because that creates a
22 > * race with the caller's microtask chain (the run can resolve before the
23 > * caller's `.then` has had a chance to schedule).
24 > */
25 > export type TerminationPolicy =
26 > /** Resolve as soon as the virtual queue is empty. */
27 > | { readonly kind: 'idle' }
28 > /** Resolve when the token is cancelled AND the queue is empty. */
29 > | { readonly kind: 'token'; readonly token: CancellationToken }
30 > /** Resolve when virtual time has reached `time` and all events scheduled
31 > * at or before `time` have been processed. A sentinel event at `time`
32 > * is scheduled by the processor so virtual time always reaches it. */
33 > | { readonly kind: 'time'; readonly time: VirtualTime };
34 >
35 > export const untilIdle: TerminationPolicy = { kind: 'idle' };
36 > export function untilToken(token: CancellationToken): TerminationPolicy { return { kind: 'token', token }; }
37 > export function untilTime(time: VirtualTime): TerminationPolicy { return { kind: 'time', time }; }
38 >
39 > export interface RunOptions {
40 > readonly until: TerminationPolicy;
41 > /** Maximum number of virtual events this run will execute. Default: 100. */
42 > readonly maxEvents?: number;
43 > /** Maximum causal-trace depth this run will tolerate. Useful for catching
44 > * runaway self-rescheduling timers. */
45 > readonly maxTraceDepth?: number;
46 > }
47 >
48 > // ============================================================================
49 > // Run — internal state for a single processor.run() invocation
50 > // ============================================================================
51 >
52 > type RunStatus = 'continue' | 'done' | { readonly error: Error };
53 >
54 > class Run {
55 > private static _idCounter = 0;
56 > public readonly id = ++Run._idCounter;
57 >
58 > public readonly promise: Promise<void>;
59 > private _resolve!: () => void;
60 > private _reject!: (e: Error) => void;
61 > private _settled = false;
62 > public get settled(): boolean { return this._settled; }
63 >
64 > constructor(
65 public readonly options: RunOptions,
66 public readonly executedAtStart: number,
69 this.promise = new Promise<void>((res, rej) => { this._resolve = res; this._reject = rej; });
70 }
72 > settle(error?: Error): void {
73 if (this._settled) { return; }
74 this._settled = true;
75 if (error) { this._reject(error); } else { this._resolve(); }
76 }
78 > evaluate(clock: VirtualClock, executedTotal: number, makeOverflow: () => Error): RunStatus {
79 const local = executedTotal - this.executedAtStart;
80 if (local >= this.maxEvents && clock.hasEvents) {
98 }
99 }
100 > } processor.ts
101 >
102 > // ============================================================================
103 > // Step outcome — what the pure state machine tells the trampoline
104 > // ============================================================================
105 >
106 > type StepOutcome =
107 > /** Either a virtual event was executed, or a run was rejected for a
108 > * bookkeeping reason (depth/event overflow). The trampoline should let
109 > * the embedding decide how to reach the next step. */
110 > | 'progress'
111 > /** No actionable event under any active deadline. The trampoline should
112 > * park until something wakes the processor. */
113 > | 'park'
114 > /** No active runs. The trampoline should stop driving. */
115 > | 'quiesce';
116 >
117 > // ============================================================================
118 > // VirtualTimeProcessor
119 > // ============================================================================
120 >
121 > export interface VirtualTimeProcessorOptions {
122 > readonly defaultMaxEvents?: number;
123 > }
124 >
125 > /**
126 > * # VirtualTimeProcessor
127 > *
128 > * Drives a {@link VirtualClock} from the host event loop. This is the
129 > * **embedding** of a small virtual event loop into the host event loop.
130 > *
131 > * ## Responsibilities, separated
132 > *
133 > * - {@link _step} is a *pure* state-machine advance. It reads the clock,
134 > * decides what to do, optionally executes one virtual event, and returns
135 > * a {@link StepOutcome}. It never touches host time.
136 > *
137 > * - {@link _drive} is the *trampoline*. It calls `_step` and lets the
138 > * {@link Embedding} decide whether to loop in place (`'continueSync'`)
139 > * or schedule the next iteration on the host (`'cbScheduled'`). It is
140 > * the only code that touches host time.
141 > *
142 > * - {@link Run} carries the user's termination predicate. Runs are pure
143 > * over `_step`'s observations; they never schedule.
144 > *
145 > * ## Invariants
146 > *
147 > * 1. **Single driver.** At any moment at most one `_drive` invocation is
148 > * active per processor (the `_inDrive` guard).
149 > *
150 > * 2. **Step is pure w.r.t. host time.** `_step` only reads the clock,
151 > * mutates the run set via settling, and synchronously runs at most one
152 > * virtual event. It never calls into a host time API.
153 > *
154 > * 3. **Embedding chooses the host primitive.** Whether the next step runs
155 > * inline, after a microtask drain, or on a paint frame is entirely the
156 > * embedding's decision — *per event*.
157 > *
158 > * 4. **Park is breakable.** While parked, the processor wakes on
159 > * {@link VirtualClock.onEventScheduled}, on a token cancellation, and
160 > * on a new run being added.
161 > *
162 > * 5. **Disposal is terminal.** After dispose, all runs are rejected and
163 > * `_step`/`_drive` short-circuit to `'quiesce'`.
164 > *
165 > * ## On the trace-reset sink
166 > *
167 > * The trace context's deferred reset (see {@link TraceContext.runAsHandler})
168 > * needs a "fire after the microtask closure" primitive. The processor passes
169 > * its *own* {@link nextMacrotask} as that sink, so the reset goes through
170 > * the same primitive the embedding uses for its own host hops. This removes
171 > * any race between the processor's hops and the trace-reset timer.
172 > */
173 > export class VirtualTimeProcessor extends Disposable {
174 >
175 > private readonly _runs = new Map<Run, IDisposable>();
176 > private readonly _history: VirtualEvent[] = [];
177 > private _executedTotal = 0;
178 > private _disposed = false;
179 >
180 > private _inDrive = false;
181 > private _parkCleanup: IDisposable | undefined;
182 >
183 > private readonly _defaultMaxEvents: number;
184 >
185 > public get history(): readonly VirtualEvent[] { return this._history; }
186 > public get executedTotal(): number { return this._executedTotal; }
187 >
188 > constructor(
189 private readonly _clock: VirtualClock,
190 private readonly _embedding: Embedding,
196 this._register({ dispose: () => this._onDispose() });
197 }
198 > processor.ts
199 > // ---- Public API -----------------------------------------------------
200 >
201 > /** Start a run with the given termination policy. */
202 > run(options: RunOptions): Promise<void> {
203 const run = new Run(options, this._executedTotal, options.maxEvents ?? this._defaultMaxEvents);
204 const cleanup = new DisposableStore();
226 return run.promise;
227 }
228 > processor.ts
229 > // ---- The pure step --------------------------------------------------
230 >
231 > private _step(): StepOutcome {
232 if (this._disposed) { return 'quiesce'; }
233
254 return 'progress';
255 }
256 > processor.ts
257 > private _executeOne(event: VirtualEvent): void {
258 try {
259 TraceContext.instance.runAsHandler(
280 }
281 }
282 > processor.ts
283 > // ---- The trampoline -------------------------------------------------
284 >
285 > private readonly _drive = (): void => {
286 > if (this._inDrive) { return; }
287 > this._inDrive = true;
288 > try {
289 > while (true) {
290 > const outcome = this._step();
291 > if (outcome === 'quiesce') { return; }
292 > if (outcome === 'park') { this._park(); return; } processor.ts
293 >
294 > // 'progress': read the next event so the embedding can pick a
295 > // per-event primitive. If there is none, loop and let the next
296 > // `_step` decide between 'park' and 'quiesce'.
297 > const next = this._clock.peekNext();
298 > if (next === undefined) { continue; }
299 > processor.ts
300 > const choice = this._embedding(next, this._drive);
301 > if (choice === 'cbScheduled') { return; }
302 > // 'continueSync': loop in place. processor.ts
303 > }
304 > } finally {
305 > this._inDrive = false;
306 > }
307 > };
308 >
309 > // ---- Park & wake ----------------------------------------------------
310 >
311 > private _park(): void {
312 this._unpark();
313 const store = new DisposableStore();
315 this._parkCleanup = store;
316 }
317 > processor.ts
318 > private _unpark(): void {
319 this._parkCleanup?.dispose();
320 this._parkCleanup = undefined;
321 }
322 > processor.ts
323 > private _wake(): void {
324 if (this._disposed) { return; }
325 this._unpark();
337 nextMacrotask(this._realApi, this._drive);
338 }
339 > processor.ts
340 > // ---- Run lifecycle --------------------------------------------------
341 >
342 > private _settleFinishedRuns(): void {
343 for (const run of [...this._runs.keys()]) {
344 if (run.settled) { continue; }
351 }
352 }
353 > processor.ts
354 > private _settleRun(run: Run, error?: Error): void {
355 const cleanup = this._runs.get(run);
356 if (!cleanup) { return; }
359 run.settle(error);
360 }
361 > processor.ts
362 > private _buildOverflow(run: Run): Error {
363 const local = this._executedTotal - run.executedAtStart;
364 return new Error(
367 );
368 }
369 > processor.ts
370 > private _buildDepthOverflow(run: Run, depth: number): Error {
371 return new Error(
372 `[VirtualTimeProcessor] Run #${run.id} exceeded maxTraceDepth (${run.options.maxTraceDepth}) — ` +
375 );
376 }
377 > processor.ts
378 > private _onDispose(): void {
379 this._disposed = true;
380 this._unpark();
382 for (const run of [...this._runs.keys()]) { this._settleRun(run, err); }
383 }
384 > } processor.ts
src/vs/workbench/contrib/debug/test/common/mockDebug.ts 215 covered LOC · 100 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- mockDebug.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 { DeferredPromise } from '../../../../../base/common/async.js';
7 > import { CancellationToken } from '../../../../../base/common/cancellation.js';
8 > import { Event } from '../../../../../base/common/event.js';
9 > import { URI as uri } from '../../../../../base/common/uri.js';
10 > import { IPosition, Position } from '../../../../../editor/common/core/position.js';
11 > import { ITextModel } from '../../../../../editor/common/model.js';
12 > import { NullLogService } from '../../../../../platform/log/common/log.js';
13 > import { IStorageService } from '../../../../../platform/storage/common/storage.js';
14 > import { IWorkspaceFolder } from '../../../../../platform/workspace/common/workspace.js';
15 > import { AbstractDebugAdapter } from '../../common/abstractDebugAdapter.js';
16 > import { AdapterEndEvent, IAdapterManager, IBreakpoint, IBreakpointData, IBreakpointUpdateData, IConfig, IConfigurationManager, IDataBreakpoint, IDataBreakpointInfoResponse, IDebugLocationReferenced, IDebugModel, IDebugService, IDebugSession, IDebugSessionOptions, IDebugger, IExceptionBreakpoint, IExceptionInfo, IFunctionBreakpoint, IInstructionBreakpoint, ILaunch, IMemoryRegion, INewReplElementData, IRawModelUpdate, IRawStoppedDetails, IReplElement, IStackFrame, IThread, IViewModel, LoadedSourceEvent, State } from '../../common/debug.js';
17 > import { DebugCompoundRoot } from '../../common/debugCompoundRoot.js';
18 > import { IInstructionBreakpointOptions } from '../../common/debugModel.js';
19 > import { Source } from '../../common/debugSource.js';
20 > import { DebugStorage } from '../../common/debugStorage.js';
21 >
22 > export class MockDebugService implements IDebugService {
23 > _serviceBrand: undefined;
24 >
25 > get state(): State {
26 throw new Error('not implemented');
27 }
29 > get onWillNewSession(): Event<IDebugSession> {
30 throw new Error('not implemented');
31 }
33 > get onDidNewSession(): Event<IDebugSession> {
34 throw new Error('not implemented');
35 }
37 > get onDidEndSession(): Event<{ session: IDebugSession; restart: boolean }> {
38 throw new Error('not implemented');
39 }
41 > get onDidChangeState(): Event<State> {
42 throw new Error('not implemented');
43 }
45 > getConfigurationManager(): IConfigurationManager {
46 throw new Error('not implemented');
47 }
49 > getAdapterManager(): IAdapterManager {
50 throw new Error('Method not implemented.');
51 }
53 > canSetBreakpointsIn(model: ITextModel): boolean {
54 throw new Error('Method not implemented.');
55 }
57 > focusStackFrame(focusedStackFrame: IStackFrame): Promise<void> {
58 throw new Error('not implemented');
59 }
61 > sendAllBreakpoints(session?: IDebugSession): Promise<any> {
62 throw new Error('not implemented');
63 }
65 > sendBreakpoints(modelUri: uri, sourceModified?: boolean | undefined, session?: IDebugSession | undefined): Promise<any> {
66 throw new Error('not implemented');
67 }
69 > addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): Promise<IBreakpoint[]> {
70 throw new Error('not implemented');
71 }
73 > updateBreakpoints(uri: uri, data: Map<string, IBreakpointUpdateData>, sendOnResourceSaved: boolean): Promise<void> {
74 throw new Error('not implemented');
75 }
77 > enableOrDisableBreakpoints(enabled: boolean): Promise<void> {
78 throw new Error('not implemented');
79 }
81 > setBreakpointsActivated(): Promise<void> {
82 throw new Error('not implemented');
83 }
85 > removeBreakpoints(): Promise<any> {
86 throw new Error('not implemented');
87 }
89 > addInstructionBreakpoint(opts: IInstructionBreakpointOptions): Promise<void> {
90 throw new Error('Method not implemented.');
91 }
93 > removeInstructionBreakpoints(address?: string): Promise<void> {
94 throw new Error('Method not implemented.');
95 }
97 > setExceptionBreakpointCondition(breakpoint: IExceptionBreakpoint, condition: string): Promise<void> {
98 throw new Error('Method not implemented.');
99 }
100 > mockDebug.ts
101 > setExceptionBreakpointsForSession(session: IDebugSession, data: DebugProtocol.ExceptionBreakpointsFilter[]): void {
102 throw new Error('Method not implemented.');
103 }
104 > mockDebug.ts
105 > addFunctionBreakpoint(): void { }
106 >
107 > moveWatchExpression(id: string, position: number): void { }
108 >
109 > updateFunctionBreakpoint(id: string, update: { name?: string; hitCondition?: string; condition?: string }): Promise<void> {
110 throw new Error('not implemented');
111 }
112 > mockDebug.ts
113 > removeFunctionBreakpoints(id?: string): Promise<void> {
114 throw new Error('not implemented');
115 }
116 > mockDebug.ts
117 > addDataBreakpoint(): Promise<void> {
118 throw new Error('Method not implemented.');
119 }
120 > mockDebug.ts
121 > updateDataBreakpoint(id: string, update: { hitCondition?: string; condition?: string }): Promise<void> {
122 throw new Error('not implemented');
123 }
124 > mockDebug.ts
125 > removeDataBreakpoints(id?: string | undefined): Promise<void> {
126 throw new Error('Method not implemented.');
127 }
128 > mockDebug.ts
129 > addReplExpression(name: string): Promise<void> {
130 throw new Error('not implemented');
131 }
132 > mockDebug.ts
133 > removeReplExpressions(): void { }
134 >
135 > addWatchExpression(name?: string): Promise<void> {
136 throw new Error('not implemented');
137 }
138 > mockDebug.ts
139 > renameWatchExpression(id: string, newName: string): Promise<void> {
140 throw new Error('not implemented');
141 }
142 > mockDebug.ts
143 > removeWatchExpressions(id?: string): void { }
144 >
145 > startDebugging(launch: ILaunch, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise<boolean> {
146 return Promise.resolve(true);
147 }
148 > mockDebug.ts
149 > restartSession(): Promise<any> {
150 throw new Error('not implemented');
151 }
152 > mockDebug.ts
153 > stopSession(): Promise<any> {
154 throw new Error('not implemented');
155 }
156 > mockDebug.ts
157 > getModel(): IDebugModel {
158 throw new Error('not implemented');
159 }
160 > mockDebug.ts
161 > getViewModel(): IViewModel {
162 throw new Error('not implemented');
163 }
164 > mockDebug.ts
165 > sourceIsNotAvailable(uri: uri): void { }
166 >
167 > tryToAutoFocusStackFrame(thread: IThread): Promise<any> {
168 throw new Error('not implemented');
169 }
170 > mockDebug.ts
171 > runTo(uri: uri, lineNumber: number, column?: number): Promise<void> {
172 throw new Error('Method not implemented.');
173 }
174 > } mockDebug.ts
175 >
176 > export class MockSession implements IDebugSession {
177 readonly suppressDebugToolbar = false;
178 readonly suppressDebugStatusbar = false;
282 root!: IWorkspaceFolder;
283 capabilities: DebugProtocol.Capabilities = {};
284 > mockDebug.ts
285 > getId(): string {
286 return 'mock';
287 }
288 > mockDebug.ts
289 > getLabel(): string {
290 return 'mockname';
291 }
292 > mockDebug.ts
293 > get name(): string {
294 return 'mockname';
295 }
296 > mockDebug.ts
297 > setName(name: string): void {
298 throw new Error('not implemented');
299 }
300 > mockDebug.ts
301 > getSourceForUri(modelUri: uri): Source {
302 throw new Error('not implemented');
303 }
304 > mockDebug.ts
305 > getThread(threadId: number): IThread {
306 throw new Error('not implemented');
307 }
308 > mockDebug.ts
309 > getStoppedDetails(): IRawStoppedDetails {
310 throw new Error('not implemented');
311 }
312 > mockDebug.ts
313 > get onDidCustomEvent(): Event<DebugProtocol.Event> {
314 throw new Error('not implemented');
315 }
316 > mockDebug.ts
317 > get onDidLoadedSource(): Event<LoadedSourceEvent> {
318 throw new Error('not implemented');
319 }
320 > mockDebug.ts
321 > get onDidChangeState(): Event<void> {
322 throw new Error('not implemented');
323 }
324 > mockDebug.ts
325 > get onDidEndAdapter(): Event<AdapterEndEvent | undefined> {
326 throw new Error('not implemented');
327 }
328 > mockDebug.ts
329 > get onDidChangeName(): Event<string> {
330 throw new Error('not implemented');
331 }
332 > mockDebug.ts
333 > get onDidProgressStart(): Event<DebugProtocol.ProgressStartEvent> {
334 throw new Error('not implemented');
335 }
336 > mockDebug.ts
337 > get onDidProgressUpdate(): Event<DebugProtocol.ProgressUpdateEvent> {
338 throw new Error('not implemented');
339 }
340 > mockDebug.ts
341 > get onDidProgressEnd(): Event<DebugProtocol.ProgressEndEvent> {
342 throw new Error('not implemented');
343 }
344 > mockDebug.ts
345 > setConfiguration(configuration: { resolved: IConfig; unresolved: IConfig }) { }
346 >
347 > getAllThreads(): IThread[] {
348 return [];
349 }
350 > mockDebug.ts
351 > getSource(raw: DebugProtocol.Source): Source {
352 throw new Error('not implemented');
353 }
354 > mockDebug.ts
355 > getLoadedSources(): Promise<Source[]> {
356 return Promise.resolve([]);
357 }
358 > mockDebug.ts
359 > completions(frameId: number, threadId: number, text: string, position: Position): Promise<DebugProtocol.CompletionsResponse> {
360 throw new Error('not implemented');
361 }
362 > mockDebug.ts
363 > clearThreads(removeThreads: boolean, reference?: number): void { }
364 >
365 > rawUpdate(data: IRawModelUpdate): void { }
366 >
367 > initialize(dbgr: IDebugger): Promise<void> {
368 throw new Error('Method not implemented.');
369 }
370 > launchOrAttach(config: IConfig): Promise<void> { mockDebug.ts
371 throw new Error('Method not implemented.');
372 }
373 > restart(): Promise<void> { mockDebug.ts
374 throw new Error('Method not implemented.');
375 }
376 > sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise<void> { mockDebug.ts
377 throw new Error('Method not implemented.');
378 }
379 > sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise<void> { mockDebug.ts
380 throw new Error('Method not implemented.');
381 }
382 > sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void> { mockDebug.ts
383 throw new Error('Method not implemented.');
384 }
385 > sendInstructionBreakpoints(dbps: IInstructionBreakpoint[]): Promise<void> { mockDebug.ts
386 throw new Error('Method not implemented.');
387 }
388 > getDebugProtocolBreakpoint(breakpointId: string): DebugProtocol.Breakpoint | undefined { mockDebug.ts
389 throw new Error('Method not implemented.');
390 }
391 > customRequest(request: string, args: any): Promise<DebugProtocol.Response> { mockDebug.ts
392 throw new Error('Method not implemented.');
393 }
394 > stackTrace(threadId: number, startFrame: number, levels: number, token: CancellationToken): Promise<DebugProtocol.StackTraceResponse> { mockDebug.ts
395 throw new Error('Method not implemented.');
396 }
397 > exceptionInfo(threadId: number): Promise<IExceptionInfo> { mockDebug.ts
398 throw new Error('Method not implemented.');
399 }
400 > scopes(frameId: number): Promise<DebugProtocol.ScopesResponse> { mockDebug.ts
401 throw new Error('Method not implemented.');
402 }
403 > variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named', start: number, count: number): Promise<DebugProtocol.VariablesResponse> { mockDebug.ts
404 throw new Error('Method not implemented.');
405 }
406 > evaluate(expression: string, frameId: number, context?: string): Promise<DebugProtocol.EvaluateResponse> { mockDebug.ts
407 throw new Error('Method not implemented.');
408 }
409 > restartFrame(frameId: number, threadId: number): Promise<void> { mockDebug.ts
410 throw new Error('Method not implemented.');
411 }
412 > next(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> { mockDebug.ts
413 throw new Error('Method not implemented.');
414 }
415 > stepIn(threadId: number, targetId?: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> { mockDebug.ts
416 throw new Error('Method not implemented.');
417 }
418 > stepOut(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> { mockDebug.ts
419 throw new Error('Method not implemented.');
420 }
421 > stepBack(threadId: number, granularity?: DebugProtocol.SteppingGranularity): Promise<void> { mockDebug.ts
422 throw new Error('Method not implemented.');
423 }
424 > continue(threadId: number): Promise<void> { mockDebug.ts
425 throw new Error('Method not implemented.');
426 }
427 > reverseContinue(threadId: number): Promise<void> { mockDebug.ts
428 throw new Error('Method not implemented.');
429 }
430 > pause(threadId: number): Promise<void> { mockDebug.ts
431 throw new Error('Method not implemented.');
432 }
433 > terminateThreads(threadIds: number[]): Promise<void> { mockDebug.ts
434 throw new Error('Method not implemented.');
435 }
436 > setVariable(variablesReference: number, name: string, value: string): Promise<DebugProtocol.SetVariableResponse> { mockDebug.ts
437 throw new Error('Method not implemented.');
438 }
439 > setExpression(frameId: number, expression: string, value: string): Promise<DebugProtocol.SetExpressionResponse | undefined> { mockDebug.ts
440 throw new Error('Method not implemented.');
441 }
442 > loadSource(resource: uri): Promise<DebugProtocol.SourceResponse> { mockDebug.ts
443 throw new Error('Method not implemented.');
444 }
445 > disassemble(memoryReference: string, offset: number, instructionOffset: number, instructionCount: number): Promise<DebugProtocol.DisassembledInstruction[] | undefined> { mockDebug.ts
446 throw new Error('Method not implemented.');
447 }
448 > mockDebug.ts
449 > terminate(restart = false): Promise<void> {
450 throw new Error('Method not implemented.');
451 }
452 > disconnect(restart = false): Promise<void> { mockDebug.ts
453 throw new Error('Method not implemented.');
454 }
455 > mockDebug.ts
456 > gotoTargets(source: DebugProtocol.Source, line: number, column?: number | undefined): Promise<DebugProtocol.GotoTargetsResponse> {
457 throw new Error('Method not implemented.');
458 }
459 > goto(threadId: number, targetId: number): Promise<DebugProtocol.GotoResponse> { mockDebug.ts
460 throw new Error('Method not implemented.');
461 }
462 > resolveLocationReference(locationReference: number): Promise<IDebugLocationReferenced> { mockDebug.ts
463 throw new Error('Method not implemented.');
464 }
465 > } mockDebug.ts
466 >
467 > export class MockRawSession {
468
469 capabilities: DebugProtocol.Capabilities = {};
597
598 readonly onDidStop: Event<DebugProtocol.StoppedEvent> = null!;
599 > } mockDebug.ts
600 >
601 > export class MockDebugAdapter extends AbstractDebugAdapter {
602 private seq = 0;
603
604 private pendingResponses = new Map<string, DeferredPromise<DebugProtocol.Response>>();
605 > mockDebug.ts
606 > startSession(): Promise<void> {
607 return Promise.resolve();
608 }
609 > mockDebug.ts
610 > stopSession(): Promise<void> {
611 return Promise.resolve();
612 }
613 > mockDebug.ts
614 > sendMessage(message: DebugProtocol.ProtocolMessage): void {
615 if (message.type === 'request') {
616 setTimeout(() => {
631 }
632 }
633 > mockDebug.ts
634 > sendResponseBody(request: DebugProtocol.Request, body: any) {
635 const response: DebugProtocol.Response = {
636 seq: ++this.seq,
643 this.acceptMessage(response);
644 }
645 > mockDebug.ts
646 > sendEventBody(event: string, body: any) {
647 const response: DebugProtocol.Event = {
648 seq: ++this.seq,
653 this.acceptMessage(response);
654 }
655 > mockDebug.ts
656 > waitForResponseFromClient(command: string): Promise<DebugProtocol.Response> {
657 const deferred = new DeferredPromise<DebugProtocol.Response>();
658 if (this.pendingResponses.has(command)) {
663 return deferred.p;
664 }
665 > mockDebug.ts
666 > sendRequestBody(command: string, args: any) {
667 const response: DebugProtocol.Request = {
668 seq: ++this.seq,
673 this.acceptMessage(response);
674 }
675 > mockDebug.ts
676 > evaluate(request: DebugProtocol.Request, args: DebugProtocol.EvaluateArguments) {
677 if (args.expression.indexOf('before.') === 0) {
678 this.sendEventBody('output', { output: args.expression });
688 }
689 }
690 > } mockDebug.ts
691 >
692 > export class MockDebugStorage extends DebugStorage {
693 >
694 > constructor(storageService: IStorageService) {
695 > super(storageService, undefined!, undefined!, new NullLogService()); mockDebug.ts
696 > }
697 > } mockDebug.ts
src/vs/base/common/path.ts 208 covered LOC · 42 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) { path.ts
77 > if (typeof value !== 'string') {
78 throw new ErrorInvalidArgType(name, 'string', value);
79 }
80 > } path.ts
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) { path.ts
1351 validateString(suffix, 'suffix');
1352 }
1353 > validateString(path, 'path'); path.ts
1354 >
1355 > let start = 0;
1356 > let end = -1;
1357 > let matchedSlash = true;
1358 > let i;
1359 >
1360 > if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {
1361 if (suffix === path) {
1362 return '';
1405 return path.slice(start, end);
1406 }
1407 > for (i = path.length - 1; i >= 0; --i) { path.ts
1408 > if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { path.ts
1409 > // If we reached a path separator that was not part of a set of path path.ts
1410 > // separators at the end of the string, stop now
1411 > if (!matchedSlash) {
1412 > start = i + 1;
1413 > break;
1414 > }
1415 > } else if (end === -1) { path.ts
1416 > // We saw the first non-path separator, mark this as the end of our
1417 > // path component
1418 > matchedSlash = false;
1419 > end = i + 1;
1420 > }
1421 > }
1422 > path.ts
1423 > if (end === -1) {
1424 return '';
1425 }
1426 > return path.slice(start, end); path.ts
1427 > }, path.ts
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/observableInternal/base.ts 206 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- base.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, onUnexpectedError } from './commonFacade/deps.js';
7 >
8 > /**
9 > * Represents an observable value.
10 > *
11 > * @template T The type of the values the observable can hold.
12 > */
13 > // This interface exists so that, for example for string observables,
14 > // typescript renders the type as `IObservable<string>` instead of `IObservable<string, unknown>`.
15 > export interface IObservable<T> extends IObservableWithChange<T, unknown> { }
16 >
17 > /**
18 > * Represents an observable value.
19 > *
20 > * @template T The type of the values the observable can hold.
21 > * @template TChange The type used to describe value changes
22 > * (usually `void` and only used in advanced scenarios).
23 > * While observers can miss temporary values of an observable,
24 > * they will receive all change values (as long as they are subscribed)!
25 > */
26 > export interface IObservableWithChange<T, TChange = unknown> {
27 > /**
28 > * Returns the current value.
29 > *
30 > * Calls {@link IObserver.handleChange} if the observable notices that the value changed.
31 > * Must not be called from {@link IObserver.handleChange}!
32 > */
33 > get(): T;
34 >
35 > /**
36 > * Forces the observable to check for changes and report them.
37 > *
38 > * Has the same effect as calling {@link IObservable.get}, but does not force the observable
39 > * to actually construct the value, e.g. if change deltas are used.
40 > * Calls {@link IObserver.handleChange} if the observable notices that the value changed.
41 > * Must not be called from {@link IObserver.handleChange}!
42 > */
43 > reportChanges(): void;
44 >
45 > /**
46 > * Adds the observer to the set of subscribed observers.
47 > * This method is idempotent.
48 > */
49 > addObserver(observer: IObserver): void;
50 >
51 > /**
52 > * Removes the observer from the set of subscribed observers.
53 > * This method is idempotent.
54 > */
55 > removeObserver(observer: IObserver): void;
56 >
57 > // #region These members have a standard implementation and are only part of the interface for convenience.
58 >
59 > /**
60 > * Reads the current value and subscribes the reader to this observable.
61 > *
62 > * Calls {@link IReader.readObservable} if a reader is given, otherwise {@link IObservable.get}
63 > * (see {@link ConvenientObservable.read} for the implementation).
64 > */
65 > read(reader: IReader | undefined): T;
66 >
67 > /**
68 > * Makes sure this value is computed eagerly.
69 > */
70 > recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T>;
71 >
72 > /**
73 > * Makes sure this value is cached.
74 > */
75 > keepObserved(store: DisposableStore): IObservable<T>;
76 >
77 > /**
78 > * Creates a derived observable that depends on this observable.
79 > * Use the reader to read other observables
80 > * (see {@link ConvenientObservable.map} for the implementation).
81 > */
82 > map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
83 > map<TNew>(owner: object, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
84 >
85 > flatten<TNew>(this: IObservable<IObservable<TNew>>): IObservable<TNew>;
86 >
87 > /**
88 > * ONLY FOR DEBUGGING!
89 > * Logs computations of this derived.
90 > */
91 > log(): IObservableWithChange<T, TChange>;
92 >
93 > /**
94 > * A human-readable name for debugging purposes.
95 > */
96 > readonly debugName: string;
97 >
98 > /**
99 > * This property captures the type of the change object. Do not use it at runtime!
100 > */
101 > readonly TChange: TChange;
102 >
103 > // #endregion
104 > }
105 >
106 > /**
107 > * Represents an observer that can be subscribed to an observable.
108 > *
109 > * If an observer is subscribed to an observable and that observable didn't signal
110 > * a change through one of the observer methods, the observer can assume that the
111 > * observable didn't change.
112 > * If an observable reported a possible change, {@link IObservable.reportChanges} forces
113 > * the observable to report an actual change if there was one.
114 > */
115 > export interface IObserver {
116 > /**
117 > * Signals that the given observable might have changed and a transaction potentially modifying that observable started.
118 > * Before the given observable can call this method again, is must call {@link IObserver.endUpdate}.
119 > *
120 > * Implementations must not get/read the value of other observables, as they might not have received this event yet!
121 > * The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
122 > */
123 > beginUpdate<T>(observable: IObservable<T>): void;
124 >
125 > /**
126 > * Signals that the transaction that potentially modified the given observable ended.
127 > * This is a good place to react to (potential) changes.
128 > */
129 > endUpdate<T>(observable: IObservable<T>): void;
130 >
131 > /**
132 > * Signals that the given observable might have changed.
133 > * The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
134 > *
135 > * Implementations must not get/read the value of other observables, as they might not have received this event yet!
136 > * The change should be processed lazily or in {@link IObserver.endUpdate}.
137 > */
138 > handlePossibleChange<T>(observable: IObservable<T>): void;
139 >
140 > /**
141 > * Signals that the given {@link observable} changed.
142 > *
143 > * Implementations must not get/read the value of other observables, as they might not have received this event yet!
144 > * The change should be processed lazily or in {@link IObserver.endUpdate}.
145 > *
146 > * @param change Indicates how or why the value changed.
147 > */
148 > handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void;
149 > }
150 >
151 > /**
152 > * A reader allows code to track what it depends on, so the caller knows when the computed value or produced side-effect is no longer valid.
153 > * Use `derived(reader => ...)` to turn code that needs a reader into an observable value.
154 > */
155 > export interface IReader {
156 > /**
157 > * Reads the value of an observable and subscribes to it.
158 > */
159 > readObservable<T>(observable: IObservableWithChange<T, any>): T;
160 > }
161 >
162 > export interface ISettable<T, TChange = void> {
163 > /**
164 > * Sets the value of the observable.
165 > * Use a transaction to batch multiple changes (with a transaction, observers only react at the end of the transaction).
166 > *
167 > * @param transaction When given, value changes are handled on demand or when the transaction ends.
168 > * @param change Describes how or why the value changed.
169 > */
170 > set(value: T, transaction: ITransaction | undefined, change: TChange): void;
171 > }
172 >
173 > export interface ITransaction {
174 > /**
175 > * Calls {@link Observer.beginUpdate} immediately
176 > * and {@link Observer.endUpdate} when the transaction ends.
177 > */
178 > updateObserver(observer: IObserver, observable: IObservableWithChange<any, any>): void;
179 > }
180 >
181 > /**
182 > * This function is used to indicate that the caller recovered from an error that indicates a bug.
183 > */
184 > export function handleBugIndicatingErrorRecovery(message: string) {
185 const err = new Error('BugIndicatingErrorRecovery: ' + message);
186 onUnexpectedError(err);
187 console.error('recovered from an error that indicates a bug', err);
188 }
189 > base.ts
190 > /**
191 > * A settable observable.
192 > */
193 > export interface ISettableObservable<T, TChange = void> extends IObservableWithChange<T, TChange>, ISettable<T, TChange> {
194 > }
195 >
196 > export interface IReaderWithStore extends IReader {
197 > /**
198 > * Items in this store get disposed just before the observable recomputes/reruns or when it becomes unobserved.
199 > */
200 > get store(): DisposableStore;
201 >
202 > /**
203 > * Items in this store get disposed just after the observable recomputes/reruns or when it becomes unobserved.
204 > * This is important if the current run needs the undisposed result from the last run.
205 > *
206 > * Warning: Items in this store might still get disposed before dependents (that read the now disposed value in the past) are recomputed with the new (undisposed) value!
207 > * A clean solution for this is ref counting.
208 > */
209 > get delayedStore(): DisposableStore;
210 > }
src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts 204 covered LOC · 24 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- devToolsLogger.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 { AutorunObserver, AutorunState } from '../../reactions/autorunImpl.js';
7 > import { TransactionImpl } from '../../transaction.js';
8 > import { IChangeInformation, IObservableLogger } from '../logging.js';
9 > import { formatValue } from '../consoleObservableLogger.js';
10 > import { ObsDebuggerApi, IObsDeclaration, ObsInstanceId, ObsStateUpdate, ITransactionState, ObserverInstanceState } from './debuggerApi.js';
11 > import { registerDebugChannel } from './debuggerRpc.js';
12 > import { deepAssign, deepAssignDeleteNulls, Throttler } from './utils.js';
13 > import { isDefined } from '../../../types.js';
14 > import { FromEventObservable } from '../../observables/observableFromEvent.js';
15 > import { BugIndicatingError, onUnexpectedError } from '../../../errors.js';
16 > import { IObservable, IObserver } from '../../base.js';
17 > import { BaseObservable } from '../../observables/baseObservable.js';
18 > import { Derived, DerivedState } from '../../observables/derivedImpl.js';
19 > import { ObservableValue } from '../../observables/observableValue.js';
20 > import { DebugLocation } from '../../debugLocation.js';
21 >
22 > interface IInstanceInfo {
23 > declarationId: number;
24 > instanceId: number;
25 > }
26 >
27 > interface IObservableInfo extends IInstanceInfo {
28 > listenerCount: number;
29 > lastValue: string | undefined;
30 > updateCount: number;
31 > changedObservables: Set<IObservable<any>>;
32 > }
33 >
34 > interface IAutorunInfo extends IInstanceInfo {
35 > updateCount: number;
36 > changedObservables: Set<IObservable<any>>;
37 > }
38 >
39 > export class DevToolsLogger implements IObservableLogger {
40 > private static _instance: DevToolsLogger | undefined = undefined;
41 > public static getInstance(): DevToolsLogger {
42 if (DevToolsLogger._instance === undefined) {
43 DevToolsLogger._instance = new DevToolsLogger();
45 return DevToolsLogger._instance;
46 }
48 > private _declarationId = 0;
49 > private _instanceId = 0;
50 >
51 > private readonly _declarations = new Map</* declarationId + type */string, IObsDeclaration>();
52 > private readonly _instanceInfos = new WeakMap<object, IObservableInfo | IAutorunInfo>();
53 > private readonly _aliveInstances = new Map<ObsInstanceId, IObservable<any> | AutorunObserver>();
54 > private readonly _activeTransactions = new Set<TransactionImpl>();
55 >
56 > private readonly _channel = registerDebugChannel<ObsDebuggerApi>('observableDevTools', () => {
57 > return {
58 > notifications: {
59 > setDeclarationIdFilter: declarationIds => {
60 >
61 > },
62 > logObservableValue: (observableId) => {
63 > console.log('logObservableValue', observableId);
64 > },
65 > flushUpdates: () => {
66 > this._flushUpdates();
67 > },
68 > resetUpdates: () => {
69 > this._pendingChanges = null;
70 > this._channel.api.notifications.handleChange(this._fullState, true);
71 > },
72 > },
73 > requests: {
74 > getDeclarations: () => {
75 > const result: Record<string, IObsDeclaration> = {};
76 > for (const decl of this._declarations.values()) {
77 > result[decl.id] = decl;
78 > }
79 > return { decls: result };
80 > },
81 > getSummarizedInstances: () => {
82 > return null!;
83 > },
84 > getObservableValueInfo: instanceId => {
85 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
86 > return {
87 > observers: [...obs.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
88 > };
89 > },
90 > getDerivedInfo: instanceId => {
91 > const d = this._aliveInstances.get(instanceId) as Derived<any>;
92 > return {
93 > dependencies: [...d.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
94 > observers: [...d.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined),
95 > };
96 > },
97 > getAutorunInfo: instanceId => {
98 > const obs = this._aliveInstances.get(instanceId) as AutorunObserver;
99 > return {
100 > dependencies: [...obs.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined),
101 > };
102 > },
103 > getTransactionState: () => {
104 > return this.getTransactionState();
105 > },
106 > setValue: (instanceId, jsonValue) => {
107 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
108 >
109 > if (obs instanceof Derived) {
110 > obs.debugSetValue(jsonValue);
111 > } else if (obs instanceof ObservableValue) {
112 > obs.debugSetValue(jsonValue);
113 > } else if (obs instanceof FromEventObservable) {
114 > obs.debugSetValue(jsonValue);
115 > } else {
116 > throw new BugIndicatingError('Observable is not supported');
117 > }
118 >
119 > const observers = [...obs.debugGetObservers()];
120 > for (const d of observers) {
121 > d.beginUpdate(obs);
122 > }
123 > for (const d of observers) {
124 > d.handleChange(obs, undefined);
125 > }
126 > for (const d of observers) {
127 > d.endUpdate(obs);
128 > }
129 > },
130 > getValue: instanceId => {
131 > const obs = this._aliveInstances.get(instanceId) as BaseObservable<any>;
132 > if (obs instanceof Derived) {
133 > return formatValue(obs.debugGetState().value, 200);
134 > } else if (obs instanceof ObservableValue) {
135 > return formatValue(obs.debugGetState().value, 200);
136 > }
137 >
138 > return undefined;
139 > },
140 > logValue: (instanceId) => {
141 > const obs = this._aliveInstances.get(instanceId);
142 > if (obs && 'get' in obs) {
143 > console.log('Logged Value:', obs.get());
144 > } else {
145 > throw new BugIndicatingError('Observable is not supported');
146 > }
147 > },
148 > rerun: (instanceId) => {
149 > const obs = this._aliveInstances.get(instanceId);
150 > if (obs instanceof Derived) {
151 > obs.debugRecompute();
152 > } else if (obs instanceof AutorunObserver) {
153 > obs.debugRerun();
154 > } else {
155 > throw new BugIndicatingError('Observable is not supported');
156 > }
157 > },
158 > }
159 > };
160 > });
161 >
162 > private getTransactionState(): ITransactionState | undefined {
163 const affected: ObserverInstanceState[] = [];
164 const txs = [...this._activeTransactions];
188 return { names: txs.map(t => t.getDebugName() ?? 'tx'), affected };
189 }
191 > private _getObservableInfo(observable: IObservable<any>): IObservableInfo | undefined {
192 const info = this._instanceInfos.get(observable);
193 if (!info) {
197 return info as IObservableInfo;
198 }
200 > private _getAutorunInfo(autorun: AutorunObserver): IAutorunInfo | undefined {
201 const info = this._instanceInfos.get(autorun);
202 if (!info) {
206 return info as IAutorunInfo;
207 }
209 > private _getInfo(observer: IObserver, queue: (observer: IObserver) => void): ObserverInstanceState | undefined {
210 if (observer instanceof Derived) {
211 const observersToUpdate = [...observer.debugGetObservers()];
255 return undefined;
256 }
258 > private _formatObservable(obs: IObservable<any>): { name: string; instanceId: ObsInstanceId } | undefined {
259 const info = this._getObservableInfo(obs);
260 if (!info) { return undefined; }
261 return { name: obs.debugName, instanceId: info.instanceId };
262 }
264 > private _formatObserver(obs: IObserver): { name: string; instanceId: ObsInstanceId } | undefined {
265 if (obs instanceof Derived) {
266 return { name: obs.toString(), instanceId: this._getObservableInfo(obs)?.instanceId! };
273 return undefined;
274 }
276 > private constructor() {
277 DebugLocation.enable();
278 }
280 > private _pendingChanges: ObsStateUpdate | null = null;
281 > private readonly _changeThrottler = new Throttler();
282 >
283 > private readonly _fullState = {};
284 >
285 > private _handleChange(update: ObsStateUpdate): void {
286 deepAssignDeleteNulls(this._fullState, update);
287
294 this._changeThrottler.throttle(this._flushUpdates, 10);
295 }
297 > private readonly _flushUpdates = () => {
298 > if (this._pendingChanges !== null) {
299 > this._channel.api.notifications.handleChange(this._pendingChanges, false);
300 > this._pendingChanges = null;
301 > }
302 > };
303 >
304 > private _getDeclarationId(type: IObsDeclaration['type'], location: DebugLocation): number {
305 if (!location) {
306 return -1;
322 return decInfo.id;
323 }
325 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
326 const declarationId = this._getDeclarationId('observable/value', location);
327
336 this._instanceInfos.set(observable, info);
337 }
339 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void {
340 const info = this._getObservableInfo(observable);
341 if (!info) { return; }
364 info.listenerCount = newCount;
365 }
367 > handleObservableUpdated(observable: IObservable<any>, changeInfo: IChangeInformation): void {
368 if (observable instanceof Derived) {
369 this._handleDerivedRecomputed(observable, changeInfo);
383 }
384 }
386 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void {
387 const declarationId = this._getDeclarationId('autorun', location);
388 const info: IAutorunInfo = {
408 }
409 }
410 > handleAutorunDisposed(autorun: AutorunObserver): void { devToolsLogger.ts
411 const info = this._getAutorunInfo(autorun);
412 if (!info) { return; }
418 this._aliveInstances.delete(info.instanceId);
419 }
420 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void { devToolsLogger.ts
421 const info = this._getAutorunInfo(autorun);
422 if (!info) { return; }
424 info.changedObservables.add(observable);
425 }
426 > handleAutorunStarted(autorun: AutorunObserver): void { devToolsLogger.ts
427
428 }
429 > handleAutorunFinished(autorun: AutorunObserver): void { devToolsLogger.ts
430 const info = this._getAutorunInfo(autorun);
431 if (!info) { return; }
437 });
438 }
440 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void {
441 const info = this._getObservableInfo(derived);
442 if (info) {
444 }
445 }
446 > _handleDerivedRecomputed(observable: Derived<any>, changeInfo: IChangeInformation): void { devToolsLogger.ts
447 const info = this._getObservableInfo(observable);
448 if (!info) { return; }
459 }
460 }
461 > handleDerivedCleared(observable: Derived<any>): void { devToolsLogger.ts
462 const info = this._getObservableInfo(observable);
463 if (!info) { return; }
475 }
476 }
477 > handleBeginTransaction(transaction: TransactionImpl): void { devToolsLogger.ts
478 this._activeTransactions.add(transaction);
479 }
480 > handleEndTransaction(transaction: TransactionImpl): void { devToolsLogger.ts
481 this._activeTransactions.delete(transaction);
482 }
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/platform/contextkey/common/scanner.ts 203 covered LOC · 58 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- scanner.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 '../../../base/common/charCode.js';
7 > import { illegalState } from '../../../base/common/errors.js';
8 > import { localize } from '../../../nls.js';
9 >
10 > export const enum TokenType {
11 > LParen,
12 > RParen,
13 > Neg,
14 > Eq,
15 > NotEq,
16 > Lt,
17 > LtEq,
18 > Gt,
19 > GtEq,
20 > RegexOp,
21 > RegexStr,
22 > True,
23 > False,
24 > In,
25 > Not,
26 > And,
27 > Or,
28 > Str,
29 > QuotedStr,
30 > Error,
31 > EOF,
32 > }
33 >
34 > export type Token =
35 > | { type: TokenType.LParen; offset: number }
36 > | { type: TokenType.RParen; offset: number }
37 > | { type: TokenType.Neg; offset: number }
38 > | { type: TokenType.Eq; offset: number; isTripleEq: boolean }
39 > | { type: TokenType.NotEq; offset: number; isTripleEq: boolean }
40 > | { type: TokenType.Lt; offset: number }
41 > | { type: TokenType.LtEq; offset: number }
42 > | { type: TokenType.Gt; offset: number }
43 > | { type: TokenType.GtEq; offset: number }
44 > | { type: TokenType.RegexOp; offset: number }
45 > | { type: TokenType.RegexStr; offset: number; lexeme: string }
46 > | { type: TokenType.True; offset: number }
47 > | { type: TokenType.False; offset: number }
48 > | { type: TokenType.In; offset: number }
49 > | { type: TokenType.Not; offset: number }
50 > | { type: TokenType.And; offset: number }
51 > | { type: TokenType.Or; offset: number }
52 > | { type: TokenType.Str; offset: number; lexeme: string }
53 > | { type: TokenType.QuotedStr; offset: number; lexeme: string }
54 > | { type: TokenType.Error; offset: number; lexeme: string }
55 > | { type: TokenType.EOF; offset: number };
56 >
57 > type KeywordTokenType = TokenType.Not | TokenType.In | TokenType.False | TokenType.True;
58 > type TokenTypeWithoutLexeme =
59 > TokenType.LParen |
60 > TokenType.RParen |
61 > TokenType.Neg |
62 > TokenType.Lt |
63 > TokenType.LtEq |
64 > TokenType.Gt |
65 > TokenType.GtEq |
66 > TokenType.RegexOp |
67 > TokenType.True |
68 > TokenType.False |
69 > TokenType.In |
70 > TokenType.Not |
71 > TokenType.And |
72 > TokenType.Or |
73 > TokenType.EOF;
74 >
75 > /**
76 > * Example:
77 > * `foo == bar'` - note how single quote doesn't have a corresponding closing quote,
78 > * so it's reported as unexpected
79 > */
80 > export type LexingError = {
81 > offset: number; /** note that this doesn't take into account escape characters from the original encoding of the string, e.g., within an extension manifest file's JSON encoding */
82 > lexeme: string;
83 > additionalInfo?: string;
84 > };
85 >
86 function hintDidYouMean(...meant: string[]) {
87 switch (meant.length) {
96 }
97 }
98 > scanner.ts
99 > const hintDidYouForgetToOpenOrCloseQuote = localize('contextkey.scanner.hint.didYouForgetToOpenOrCloseQuote', "Did you forget to open or close the quote?");
100 > const hintDidYouForgetToEscapeSlash = localize('contextkey.scanner.hint.didYouForgetToEscapeSlash', "Did you forget to escape the '/' (slash) character? Put two backslashes before it to escape, e.g., '\\\\/\'.");
101 >
102 > /**
103 > * A simple scanner for context keys.
104 > *
105 > * Example:
106 > *
107 > * ```ts
108 > * const scanner = new Scanner().reset('resourceFileName =~ /docker/ && !config.docker.enabled');
109 > * const tokens = [...scanner];
110 > * if (scanner.errorTokens.length > 0) {
111 > * scanner.errorTokens.forEach(err => console.error(`Unexpected token at ${err.offset}: ${err.lexeme}\nHint: ${err.additional}`));
112 > * } else {
113 > * // process tokens
114 > * }
115 > * ```
116 > */
117 > export class Scanner {
118 >
119 > static getLexeme(token: Token): string {
120 > switch (token.type) {
121 > case TokenType.LParen:
122 > return '('; scanner.ts
123 > case TokenType.RParen: scanner.ts
124 > return ')'; scanner.ts
125 > case TokenType.Neg: scanner.ts
126 > return '!'; scanner.ts
127 > case TokenType.Eq: scanner.ts
128 > return token.isTripleEq ? '===' : '=='; scanner.ts
129 > case TokenType.NotEq: scanner.ts
130 > return token.isTripleEq ? '!==' : '!='; scanner.ts
131 > case TokenType.Lt: scanner.ts
132 > return '<'; scanner.ts
133 > case TokenType.LtEq: scanner.ts
134 > return '<='; scanner.ts
135 > case TokenType.Gt: scanner.ts
136 > return '>'; scanner.ts
137 > case TokenType.GtEq: scanner.ts
138 > return '>='; scanner.ts
139 > case TokenType.RegexOp: scanner.ts
140 > return '=~'; scanner.ts
141 > case TokenType.RegexStr: scanner.ts
142 > return token.lexeme; scanner.ts
143 > case TokenType.True: scanner.ts
144 > return 'true'; scanner.ts
145 > case TokenType.False: scanner.ts
146 > return 'false'; scanner.ts
147 > case TokenType.In: scanner.ts
148 > return 'in'; scanner.ts
149 > case TokenType.Not: scanner.ts
150 > return 'not'; scanner.ts
151 > case TokenType.And: scanner.ts
152 > return '&&'; scanner.ts
153 > case TokenType.Or: scanner.ts
154 > return '||'; scanner.ts
155 > case TokenType.Str: scanner.ts
156 > return token.lexeme; scanner.ts
157 > case TokenType.QuotedStr: scanner.ts
158 > return token.lexeme; scanner.ts
159 > case TokenType.Error: scanner.ts
160 > return token.lexeme; scanner.ts
161 > case TokenType.EOF: scanner.ts
162 > return 'EOF'; scanner.ts
163 > default: scanner.ts
164 > throw illegalState(`unhandled token type: ${JSON.stringify(token)}; have you forgotten to add a case?`); scanner.ts
165 > } scanner.ts
166 > }
167 >
168 > private static _regexFlags = new Set(['i', 'g', 's', 'm', 'y', 'u'].map(ch => ch.charCodeAt(0)));
169 >
170 > private static _keywords = new Map<string, KeywordTokenType>([
171 > ['not', TokenType.Not],
172 > ['in', TokenType.In],
173 > ['false', TokenType.False],
174 > ['true', TokenType.True],
175 > ]);
176 >
177 > private _input: string = '';
178 > private _start: number = 0;
179 > private _current: number = 0;
180 > private _tokens: Token[] = [];
181 > private _errors: LexingError[] = [];
182 >
183 > get errors(): Readonly<LexingError[]> {
184 return this._errors;
185 }
186 > scanner.ts
187 > reset(value: string) {
188 this._input = value;
189
195 return this;
196 }
197 > scanner.ts
198 > scan() {
199 while (!this._isAtEnd()) {
200
267 return Array.from(this._tokens);
268 }
269 > scanner.ts
270 > private _match(expected: number): boolean {
271 if (this._isAtEnd()) {
272 return false;
278 return true;
279 }
280 > scanner.ts
281 > private _advance(): number {
282 return this._input.charCodeAt(this._current++);
283 }
284 > scanner.ts
285 > private _peek(): number {
286 return this._isAtEnd() ? CharCode.Null : this._input.charCodeAt(this._current);
287 }
288 > scanner.ts
289 > private _addToken(type: TokenTypeWithoutLexeme) {
290 this._tokens.push({ type, offset: this._start });
291 }
292 > scanner.ts
293 > private _error(additional?: string) {
294 const offset = this._start;
295 const lexeme = this._input.substring(this._start, this._current);
298 this._tokens.push(errToken);
299 }
300 > scanner.ts
301 > // u - unicode, y - sticky // TODO@ulugbekna: we accept double quotes as part of the string rather than as a delimiter (to preserve old parser's behavior)
302 > private stringRe = /[a-zA-Z0-9_<>\-\./\\:\*\?\+\[\]\^,#@;"%\$\p{L}-]+/uy;
303 > private _string() {
304 this.stringRe.lastIndex = this._start;
305 const match = this.stringRe.exec(this._input);
315 }
316 }
317 > scanner.ts
318 > // captures the lexeme without the leading and trailing '
319 > private _quotedString() {
320 while (this._peek() !== CharCode.SingleQuote && !this._isAtEnd()) { // TODO@ulugbekna: add support for escaping ' ?
321 this._advance();
332 this._tokens.push({ type: TokenType.QuotedStr, lexeme: this._input.substring(this._start + 1, this._current - 1), offset: this._start + 1 });
333 }
334 > scanner.ts
335 > /*
336 > * Lexing a regex expression: /.../[igsmyu]*
337 > * Based on https://github.com/microsoft/TypeScript/blob/9247ef115e617805983740ba795d7a8164babf89/src/compiler/scanner.ts#L2129-L2181
338 > *
339 > * Note that we want slashes within a regex to be escaped, e.g., /file:\\/\\/\\// should match `file:///`
340 > */
341 > private _regex() {
342 let p = this._current;
343
378 this._tokens.push({ type: TokenType.RegexStr, lexeme, offset: this._start });
379 }
380 > scanner.ts
381 > private _isAtEnd() {
382 return this._current >= this._input.length;
383 }
384 > } scanner.ts
src/vs/platform/telemetry/common/telemetryUtils.ts 199 covered LOC · 15 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- telemetryUtils.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 { cloneAndChange, safeStringify } from '../../../base/common/objects.js';
7 > import { isObject } from '../../../base/common/types.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { localize } from '../../../nls.js';
10 > import { IConfigurationService } from '../../configuration/common/configuration.js';
11 > import { IEnvironmentService } from '../../environment/common/environment.js';
12 > import { LoggerGroup } from '../../log/common/log.js';
13 > import { IProductService } from '../../product/common/productService.js';
14 > import { getRemoteName } from '../../remote/common/remoteHosts.js';
15 > import { verifyMicrosoftInternalDomain } from './commonProperties.js';
16 > import { ICustomEndpointTelemetryService, ITelemetryData, ITelemetryEndpoint, ITelemetryService, TelemetryConfiguration, TelemetryLevel, TELEMETRY_CRASH_REPORTER_SETTING_ID, TELEMETRY_OLD_SETTING_ID, TELEMETRY_SETTING_ID } from './telemetry.js';
17 >
18 > /**
19 > * A special class used to denoting a telemetry value which should not be clean.
20 > * This is because that value is "Trusted" not to contain identifiable information such as paths.
21 > * NOTE: This is used as an API type as well, and should not be changed.
22 > */
23 > export class TelemetryTrustedValue<T> {
24 > // This is merely used as an identifier as the instance will be lost during serialization over the exthost
25 > public readonly isTrustedTelemetryValue = true;
26 > constructor(public readonly value: T) { }
27 > }
28 >
29 > export class NullTelemetryServiceShape implements ITelemetryService {
30 > declare readonly _serviceBrand: undefined;
31 > readonly telemetryLevel = TelemetryLevel.NONE;
32 > readonly sessionId = 'someValue.sessionId';
33 > readonly machineId = 'someValue.machineId';
34 > readonly sqmId = 'someValue.sqmId';
35 > readonly devDeviceId = 'someValue.devDeviceId';
36 > readonly firstSessionDate = 'someValue.firstSessionDate';
37 > readonly sendErrorTelemetry = false;
38 > publicLog() { }
39 > publicLog2() { }
40 > publicLogError() { }
41 > publicLogError2() { }
42 > setExperimentProperty() { }
43 > setCommonProperty() { }
44 > }
45 >
46 > export const NullTelemetryService = new NullTelemetryServiceShape();
47 >
48 > export class NullEndpointTelemetryService implements ICustomEndpointTelemetryService {
49 > _serviceBrand: undefined;
50 >
51 > async publicLog(_endpoint: ITelemetryEndpoint, _eventName: string, _data?: ITelemetryData): Promise<void> {
52 // noop
53 }
55 > async publicLogError(_endpoint: ITelemetryEndpoint, _errorEventName: string, _data?: ITelemetryData): Promise<void> {
56 // noop
57 }
59 >
60 > export const telemetryLogId = 'telemetry';
61 > export const TelemetryLogGroup: LoggerGroup = { id: telemetryLogId, name: localize('telemetryLogName', "Telemetry") };
62 >
63 > export interface ITelemetryAppender {
64 > log(eventName: string, data: ITelemetryData): void;
65 > flush(): Promise<void>;
66 > }
67 >
68 > export const NullAppender: ITelemetryAppender = { log: () => null, flush: () => Promise.resolve(undefined) };
69 >
70 >
71 > /* __GDPR__FRAGMENT__
72 > "URIDescriptor" : {
73 > "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
74 > "scheme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
75 > "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
76 > "path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
77 > }
78 > */
79 > export interface URIDescriptor {
80 > mimeType?: string;
81 > scheme?: string;
82 > ext?: string;
83 > path?: string;
84 > }
85 >
86 > /**
87 > * Determines whether or not we support logging telemetry.
88 > * This checks if the product is capable of collecting telemetry but not whether or not it can send it
89 > * For checking the user setting and what telemetry you can send please check `getTelemetryLevel`.
90 > * This returns true if `--disable-telemetry` wasn't used, the product.json allows for telemetry, and we're not testing an extension
91 > * If false telemetry is disabled throughout the product
92 > * @param productService
93 > * @param environmentService
94 > * @returns false - telemetry is completely disabled, true - telemetry is logged locally, but may not be sent
95 > */
96 > export function supportsTelemetry(productService: IProductService, environmentService: IEnvironmentService): boolean {
97 // If it's OSS and telemetry isn't disabled via the CLI we will allow it for logging only purposes
98 if (!environmentService.isBuilt && !environmentService.disableTelemetry) {
101 return !(environmentService.disableTelemetry || !productService.enableTelemetry);
102 }
104 > /**
105 > * Checks to see if we're in logging only mode to debug telemetry.
106 > * This is if telemetry is enabled and we're in OSS, but no telemetry key is provided so it's not being sent just logged.
107 > * @param productService
108 > * @param environmentService
109 > * @returns True if telemetry is actually disabled and we're only logging for debug purposes
110 > */
111 > export function isLoggingOnly(productService: IProductService, environmentService: IEnvironmentService): boolean {
112 // If we're testing an extension, log telemetry for debug purposes
113 if (environmentService.extensionTestsLocationURI) {
129 return true;
130 }
132 > /**
133 > * Determines how telemetry is handled based on the user's configuration.
134 > *
135 > * @param configurationService
136 > * @returns OFF, ERROR, ON
137 > */
138 > export function getTelemetryLevel(configurationService: IConfigurationService): TelemetryLevel {
139 const newConfig = configurationService.getValue<TelemetryConfiguration>(TELEMETRY_SETTING_ID);
140 const crashReporterConfig = configurationService.getValue<boolean | undefined>(TELEMETRY_CRASH_REPORTER_SETTING_ID);
158 }
159 }
161 > export interface Properties {
162 > [key: string]: string;
163 > }
164 >
165 > export interface Measurements {
166 > [key: string]: number;
167 > }
168 >
169 > export function validateTelemetryData(data?: unknown): { properties: Properties; measurements: Measurements } {
170
171 const properties: Properties = {};
204 };
205 }
207 > interface IRemoteAuthoringConfig {
208 > remoteExtensionTips?: { readonly [remoteName: string]: unknown };
209 > virtualWorkspaceExtensionTips?: { readonly [remoteName: string]: unknown };
210 > }
211 >
212 > export function cleanRemoteAuthority(remoteAuthority: string | undefined, config: IRemoteAuthoringConfig): string {
213 if (!remoteAuthority) {
214 return 'none';
229 return 'other';
230 }
232 function flatten(obj: unknown, result: Record<string, unknown>, order: number = 0, prefix?: string): void {
233 if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
258 }
259 }
261 > /**
262 > * Whether or not this is an internal user
263 > * @param productService The product service
264 > * @param configService The config servivce
265 > * @returns true if internal, false otherwise
266 > */
267 > export function isInternalTelemetry(productService: IProductService, configService: IConfigurationService) {
268 const msftInternalDomains = productService.msftInternalDomains || [];
269 const internalTesting = configService.getValue<boolean>('telemetry.internalTesting');
270 return verifyMicrosoftInternalDomain(msftInternalDomains) || internalTesting;
271 }
273 > interface IPathEnvironment {
274 > appRoot: string;
275 > extensionsPath: string;
276 > userDataPath: string;
277 > userHome: URI;
278 > tmpDir: URI;
279 > }
280 >
281 > export function getPiiPathsFromEnvironment(paths: IPathEnvironment): string[] {
282 return [paths.appRoot, paths.extensionsPath, paths.userHome.fsPath, paths.tmpDir.fsPath, paths.userDataPath];
283 }
285 > //#region Telemetry Cleaning
286 >
287 > /**
288 > * Cleans a given stack of possible paths
289 > * @param stack The stack to sanitize
290 > * @param cleanupPatterns Cleanup patterns to remove from the stack
291 > * @returns The cleaned stack
292 > */
293 function anonymizeFilePaths(stack: string, cleanupPatterns: RegExp[]): string {
294
356 return updatedStack;
357 }
359 > const userDataRegexes = [
360 > { label: 'URL', regex: /[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s]*/ },
361 > { label: 'Google API Key', regex: /AIza[A-Za-z0-9_\\\-]{35}/ },
362 > { label: 'JWT', regex: /eyJ[0eXAiOiJKV1Qi|hbGci|a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+/ },
363 > { label: 'Slack Token', regex: /xox[pbar]\-[A-Za-z0-9]/ },
364 > { label: 'GitHub Token', regex: /(gh[psuro]_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})/ },
365 > { label: 'Generic Secret', regex: /(key|token|sig|secret|signature|password|passwd|pwd|android:value)[^a-zA-Z0-9]/i },
366 > { label: 'CLI Credentials', regex: /((login|psexec|(certutil|psexec)\.exe).{1,50}(\s-u(ser(name)?)?\s+.{3,100})?\s-(admin|user|vm|root)?p(ass(word)?)?\s+["']?[^$\-\/\s]|(^|[\s\r\n\\])net(\.exe)?.{1,5}(user\s+|share\s+\/user:| user -? secrets ? set) \s + [^ $\s \/])/ },
367 > { label: 'Microsoft Entra ID', regex: /eyJ(?:0eXAiOiJKV1Qi|hbGci|[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.)/ },
368 > { label: 'Email', regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/ }
369 > ];
370 >
371 > /**
372 > * Redacts a value if it contains commonly leaked PII.
373 > * @param value The value returned (as-is) when no PII is detected
374 > * @param probe The string actually matched against the PII heuristics. Defaults
375 > * to `value`; callers may pass a value that includes a trailing delimiter (e.g. a
376 > * newline) so that heuristics relying on a non-alphanumeric boundary match the
377 > * same way they would against the original whole string.
378 > * @returns A `<REDACTED: ...>` marker if the probe matched, otherwise `value`
379 > */
380 function redactIfPossibleUserInfo(value: string, probe: string = value): string {
381 for (const secretRegex of userDataRegexes) {
386 return value;
387 }
389 > /**
390 > * Attempts to remove commonly leaked PII.
391 > *
392 > * When a match is found the check is applied per line so that a single suspicious
393 > * frame (e.g. a stack frame containing a function name such as `getStorageKey`
394 > * which matches the broad `Generic Secret` heuristic) only redacts that line —
395 > * replacing it with a `<REDACTED: ...>` marker — instead of wiping the entire
396 > * multi-line value such as a whole callstack.
397 > * @param property The property whose offending lines will be replaced with a redaction marker if they contain user data
398 > * @returns The new value for the property
399 > */
400 function removePropertiesWithPossibleUserInfo(property: string): string {
401 // If for some reason it is undefined we skip it (this shouldn't be possible);
436 return lines.join('\n');
437 }
439 >
440 > /**
441 > * Does a best possible effort to clean a data object from any possible PII.
442 > * @param data The data object to clean
443 > * @param paths Any additional patterns that should be removed from the data set
444 > * @returns A new object with the PII removed
445 > */
446 > export function cleanData(data: ITelemetryData | undefined, cleanUpPatterns: RegExp[]): Record<string, unknown> {
447 if (!data) {
448 return {};
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/platform/environment/common/environment.ts 165 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- environment.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 '../../../base/common/uri.js';
7 > import { NativeParsedArgs } from './argv.js';
8 > import { createDecorator, refineServiceDecorator } from '../../instantiation/common/instantiation.js';
9 >
10 > export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService');
11 > export const INativeEnvironmentService = refineServiceDecorator<IEnvironmentService, INativeEnvironmentService>(IEnvironmentService);
12 >
13 > export interface IDebugParams {
14 > port: number | null;
15 > break: boolean;
16 > }
17 >
18 > export interface IExtensionHostDebugParams extends IDebugParams {
19 > debugId?: string;
20 > env?: Record<string, string>;
21 > }
22 >
23 > /**
24 > * Type of extension.
25 > *
26 > * **NOTE**: This is defined in `platform/environment` because it can appear as a CLI argument.
27 > */
28 > export type ExtensionKind = 'ui' | 'workspace' | 'web';
29 >
30 > /**
31 > * A basic environment service that can be used in various processes,
32 > * such as main, renderer and shared process. Use subclasses of this
33 > * service for specific environment.
34 > */
35 > export interface IEnvironmentService {
36 >
37 > readonly _serviceBrand: undefined;
38 >
39 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
40 > //
41 > // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
42 > //
43 > // AS SUCH:
44 > // - PUT NON-WEB PROPERTIES INTO NATIVE ENVIRONMENT SERVICE
45 > // - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
46 > // - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
47 > //
48 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
49 >
50 > // --- user roaming data
51 > stateResource: URI;
52 > userRoamingDataHome: URI;
53 > keyboardLayoutResource: URI;
54 > argvResource: URI;
55 >
56 > // --- data paths
57 > untitledWorkspacesHome: URI;
58 > workspaceStorageHome: URI;
59 > localHistoryHome: URI;
60 > cacheHome: URI;
61 > appSharedDataHome: URI;
62 >
63 > // --- settings sync
64 > userDataSyncHome: URI;
65 > sync: 'on' | 'off' | undefined;
66 >
67 > // --- continue edit session
68 > continueOn?: string;
69 > editSessionId?: string;
70 >
71 > // --- extension development
72 > debugExtensionHost: IExtensionHostDebugParams;
73 > isExtensionDevelopment: boolean;
74 > disableExtensions: boolean | string[];
75 > skipBuiltinExtensions?: readonly string[];
76 > enableExtensions?: readonly string[];
77 > extensionDevelopmentLocationURI?: URI[];
78 > extensionDevelopmentKind?: ExtensionKind[];
79 > extensionTestsLocationURI?: URI;
80 >
81 > // --- logging
82 > logsHome: URI;
83 > logLevel?: string;
84 > extensionLogLevel?: [string, string][];
85 > verbose: boolean;
86 > isBuilt: boolean;
87 >
88 > // --- telemetry/exp
89 > disableTelemetry: boolean;
90 > disableExperiments: boolean;
91 > serviceMachineIdResource: URI;
92 >
93 > // --- agent sessions workspace
94 > agentSessionsWorkspace: URI;
95 > // --- Policy
96 > policyFile?: URI;
97 >
98 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
99 > //
100 > // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
101 > //
102 > // AS SUCH:
103 > // - PUT NON-WEB PROPERTIES INTO NATIVE ENVIRONMENT SERVICE
104 > // - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
105 > // - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
106 > //
107 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
108 > }
109 >
110 > /**
111 > * A subclass of the `IEnvironmentService` to be used only in native
112 > * environments (Windows, Linux, macOS) but not e.g. web.
113 > */
114 > export interface INativeEnvironmentService extends IEnvironmentService {
115 >
116 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
117 > //
118 > // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
119 > //
120 > // AS SUCH:
121 > // - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
122 > // - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
123 > //
124 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
125 >
126 > // --- CLI Arguments
127 > args: NativeParsedArgs;
128 >
129 > // --- data paths
130 > /**
131 > * Root path of the JavaScript sources.
132 > *
133 > * Note: This is NOT the installation root
134 > * directory itself but contained in it at
135 > * a level that is platform dependent.
136 > */
137 > appRoot: string;
138 > userHome: URI;
139 > appSettingsHome: URI;
140 > tmpDir: URI;
141 > userDataPath: string;
142 >
143 > // --- extensions
144 > extensionsPath: string;
145 > extensionsDownloadLocation: URI;
146 > builtinExtensionsPath: string;
147 >
148 > // --- use in-memory Secret Storage
149 > useInMemorySecretStorage?: boolean;
150 >
151 > crossOriginIsolated?: boolean;
152 > exportPolicyData?: string;
153 > exportDefaultKeybindings?: string;
154 >
155 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
156 > //
157 > // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE.
158 > //
159 > // AS SUCH:
160 > // - PUT NON-WEB PROPERTIES INTO NATIVE ENVIRONMENT SERVICE
161 > // - PUT WORKBENCH ONLY PROPERTIES INTO WORKBENCH ENVIRONMENT SERVICE
162 > // - PUT ELECTRON-MAIN ONLY PROPERTIES INTO MAIN ENVIRONMENT SERVICE
163 > //
164 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
165 > }
src/vs/base/common/policy.ts 161 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- policy.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 { IPolicyData } from './defaultAccount.js';
8 >
9 > /**
10 > * System-wide policy file path for Linux systems.
11 > */
12 > export const LINUX_SYSTEM_POLICY_FILE_PATH = '/etc/vscode/policy.json';
13 >
14 > export type PolicyName = string;
15 > export type LocalizedValue = {
16 > key: string;
17 > value: string;
18 > };
19 >
20 > export type PolicyValue = string | number | boolean;
21 > export type ManagedSettingValue = PolicyValue;
22 > export type ManagedSettingsData = Readonly<Record<string, ManagedSettingValue>>;
23 >
24 > export interface IManagedSettingPolicyDefinition {
25 > readonly type: 'string' | 'number' | 'boolean';
26 > }
27 >
28 > export type IManagedSettingsPolicyDefinitions = Readonly<Record<string, IManagedSettingPolicyDefinition>>;
29 >
30 > export enum PolicyCategory {
31 > Extensions = 'Extensions',
32 > IntegratedTerminal = 'IntegratedTerminal',
33 > InteractiveSession = 'InteractiveSession',
34 > Telemetry = 'Telemetry',
35 > Update = 'Update',
36 > }
37 >
38 > export const PolicyCategoryData: {
39 > [key in PolicyCategory]: { name: LocalizedValue }
40 > } = {
41 > [PolicyCategory.Extensions]: {
42 > name: {
43 > key: 'extensionsConfigurationTitle', value: localize('extensionsConfigurationTitle', "Extensions"),
44 > }
45 > },
46 > [PolicyCategory.IntegratedTerminal]: {
47 > name: {
48 > key: 'terminalIntegratedConfigurationTitle', value: localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"),
49 > }
50 > },
51 > [PolicyCategory.InteractiveSession]: {
52 > name: {
53 > key: 'interactiveSessionConfigurationTitle', value: localize('interactiveSessionConfigurationTitle', "Chat"),
54 > }
55 > },
56 > [PolicyCategory.Telemetry]: {
57 > name: {
58 > key: 'telemetryConfigurationTitle', value: localize('telemetryConfigurationTitle', "Telemetry"),
59 > }
60 > },
61 > [PolicyCategory.Update]: {
62 > name: {
63 > key: 'updateConfigurationTitle', value: localize('updateConfigurationTitle', "Update"),
64 > }
65 > }
66 > };
67 >
68 > export interface IPolicy {
69 >
70 > /**
71 > * The policy name.
72 > */
73 > readonly name: PolicyName;
74 >
75 > /**
76 > * The policy category.
77 > */
78 > readonly category: PolicyCategory;
79 >
80 > /**
81 > * The Code version in which this policy was introduced.
82 > */
83 > readonly minimumVersion: `${number}.${number}`;
84 >
85 > /**
86 > * Localization info for the policy.
87 > *
88 > * IMPORTANT: the key values for these must be unique to avoid collisions, as during the export time the module information is not available.
89 > */
90 > readonly localization: {
91 > /** The localization key or key value pair. If only a key is provided, the default value will fallback to the parent configuration's description property. */
92 > description: LocalizedValue;
93 > /** List of localization key or key value pair. If only a key is provided, the default value will fallback to the parent configuration's enumDescriptions property. */
94 > enumDescriptions?: LocalizedValue[];
95 > };
96 >
97 > /**
98 > * The value that an ACCOUNT-based feature will use when its corresponding policy is active.
99 > *
100 > * Only applicable when policy is tagged with ACCOUNT. When an account-based feature's policy is enabled,
101 > * this value determines what value the feature receives.
102 > *
103 > * For example:
104 > * - If evaluated value is `true`, the feature's setting is locked to `true` WHEN the policy is in effect.
105 > * - If evaluated value is `foo`, the feature's setting is locked to 'foo' WHEN the policy is in effect.
106 > *
107 > * If `undefined`, the feature's setting is not locked and can be overridden by other means.
108 > */
109 > readonly value?: (policyData: IPolicyData) => string | number | boolean | undefined;
110 >
111 > /**
112 > * Declares Copilot managed-settings keys this policy's value callback reads.
113 > * Keys are dot-separated managed-settings paths, for example
114 > * `permissions.disableBypassPermissionsMode`.
115 > */
116 > readonly managedSettings?: IManagedSettingsPolicyDefinitions;
117 >
118 > /**
119 > * The most-restrictive value that should be applied when the user is subject to the
120 > * "Require Approved Account" gate but the gate is not yet satisfied (i.e. no approved
121 > * GitHub account is signed in or the account-side policy data has not yet resolved).
122 > *
123 > * If omitted, the gate falls back to a type-driven safe default
124 > * (`false` for boolean, `0` for number, `''` for string).
125 > *
126 > * Only consulted while the gate is active and unsatisfied; ignored otherwise.
127 > */
128 > readonly restrictedValue?: string | number | boolean;
129 > }
130 >
131 > /**
132 > * A subordinate attachment to an existing {@link IPolicy} (the "owner"). A setting may declare a
133 > * `policyReference` instead of a full `policy` to be governed by a policy owned by another setting,
134 > * letting a single enterprise policy lock more than one setting (e.g. gating an agent in both the
135 > * editor window and the Agents window).
136 > *
137 > * A reference is a pure pointer: it carries no policy semantics of its own. The owner is the single
138 > * source of truth for the policy's catalog metadata *and* its runtime behaviour (type, value
139 > * callback, etc.); a reference only contributes the policy name so the setting is gated and the OS
140 > * policy watcher observes the name in processes where the owner is not loaded.
141 > */
142 > export interface IPolicyReference {
143 >
144 > /** The name of the owning {@link IPolicy} this setting attaches to. */
145 > readonly name: PolicyName;
146 > }
147 >
148 > /**
149 > * A `product.json` `extensionConfigurationPolicy` entry that attaches its setting to a policy
150 > * *owned* by an in-code setting, instead of declaring a full owner {@link IPolicy}. This mirrors the
151 > * in-code `policyReference` configuration field, so the same indirection can be expressed from
152 > * `product.json` — where the owner's runtime behaviour (notably its `value` callback) cannot live.
153 > *
154 > * An `extensionConfigurationPolicy` entry is therefore either a full {@link IPolicy} (the setting
155 > * "parents"/owns the policy, the current syntax) or this reference wrapper.
156 > */
157 > export interface IExtensionConfigurationPolicyReference {
158 >
159 > /** Pointer to the owning {@link IPolicy} declared by an in-code setting. */
160 > readonly policyReference: IPolicyReference;
161 > }
src/vs/base/common/buffer.ts 158 covered LOC · 42 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- buffer.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 { Lazy } from './lazy.js';
7 > import * as streams from './stream.js';
8 >
9 > interface NodeBuffer {
10 > allocUnsafe(size: number): Uint8Array;
11 > isBuffer(obj: unknown): obj is NodeBuffer;
12 > from(arrayBuffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8Array;
13 > from(data: string): Uint8Array;
14 > }
15 >
16 > declare const Buffer: NodeBuffer;
17 >
18 > const hasBuffer = (typeof Buffer !== 'undefined');
19 > const indexOfTable = new Lazy(() => new Uint8Array(256));
20 >
21 > let textEncoder: { encode: (input: string) => Uint8Array } | null;
22 > let textDecoder: { decode: (input: Uint8Array) => string } | null;
23 >
24 > export class VSBuffer {
25 >
26 > /**
27 > * When running in a nodejs context, the backing store for the returned `VSBuffer` instance
28 > * might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
29 > */
30 > static alloc(byteLength: number): VSBuffer {
31 if (hasBuffer) {
32 return new VSBuffer(Buffer.allocUnsafe(byteLength));
35 }
36 }
37 > buffer.ts
38 > /**
39 > * When running in a nodejs context, if `actual` is not a nodejs Buffer, the backing store for
40 > * the returned `VSBuffer` instance might use a nodejs Buffer allocated from node's Buffer pool,
41 > * which is not transferrable.
42 > */
43 > static wrap(actual: Uint8Array): VSBuffer {
44 if (hasBuffer && !(Buffer.isBuffer(actual))) {
45 // https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
49 return new VSBuffer(actual);
50 }
51 > buffer.ts
52 > /**
53 > * When running in a nodejs context, the backing store for the returned `VSBuffer` instance
54 > * might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
55 > */
56 > static fromString(source: string, options?: { dontUseNodeBuffer?: boolean }): VSBuffer {
57 const dontUseNodeBuffer = options?.dontUseNodeBuffer || false;
58 if (!dontUseNodeBuffer && hasBuffer) {
65 }
66 }
67 > buffer.ts
68 > /**
69 > * When running in a nodejs context, the backing store for the returned `VSBuffer` instance
70 > * might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
71 > */
72 > static fromByteArray(source: number[]): VSBuffer {
73 const result = VSBuffer.alloc(source.length);
74 for (let i = 0, len = source.length; i < len; i++) {
77 return result;
78 }
79 > buffer.ts
80 > /**
81 > * When running in a nodejs context, the backing store for the returned `VSBuffer` instance
82 > * might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
83 > */
84 > static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
85 if (typeof totalLength === 'undefined') {
86 totalLength = 0;
100 return ret;
101 }
102 > buffer.ts
103 > static isNativeBuffer(buffer: unknown): boolean {
104 return hasBuffer && Buffer.isBuffer(buffer);
105 }
106 > buffer.ts
107 > readonly buffer: Uint8Array;
108 > readonly byteLength: number;
109 >
110 > private constructor(buffer: Uint8Array) {
111 this.buffer = buffer;
112 this.byteLength = this.buffer.byteLength;
113 }
114 > buffer.ts
115 > /**
116 > * When running in a nodejs context, the backing store for the returned `VSBuffer` instance
117 > * might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
118 > */
119 > clone(): VSBuffer {
120 const result = VSBuffer.alloc(this.byteLength);
121 result.set(this);
122 return result;
123 }
124 > buffer.ts
125 > toString(): string {
126 if (hasBuffer) {
127 return this.buffer.toString();
133 }
134 }
135 > buffer.ts
136 > slice(start?: number, end?: number): VSBuffer {
137 // IMPORTANT: use subarray instead of slice because TypedArray#slice
138 // creates shallow copy and NodeBuffer#slice doesn't. The use of subarray
140 return new VSBuffer(this.buffer.subarray(start, end));
141 }
142 > buffer.ts
143 > set(array: VSBuffer, offset?: number): void;
144 > set(array: Uint8Array, offset?: number): void;
145 > set(array: ArrayBuffer, offset?: number): void;
146 > set(array: ArrayBufferView, offset?: number): void;
147 > set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void;
148 > set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void {
149 if (array instanceof VSBuffer) {
150 this.buffer.set(array.buffer, offset);
159 }
160 }
161 > buffer.ts
162 > readUInt32BE(offset: number): number {
163 return readUInt32BE(this.buffer, offset);
164 }
165 > buffer.ts
166 > writeUInt32BE(value: number, offset: number): void {
167 writeUInt32BE(this.buffer, value, offset);
168 }
169 > buffer.ts
170 > readUInt32LE(offset: number): number {
171 return readUInt32LE(this.buffer, offset);
172 }
173 > buffer.ts
174 > writeUInt32LE(value: number, offset: number): void {
175 writeUInt32LE(this.buffer, value, offset);
176 }
177 > buffer.ts
178 > readUInt8(offset: number): number {
179 return readUInt8(this.buffer, offset);
180 }
181 > buffer.ts
182 > writeUInt8(value: number, offset: number): void {
183 writeUInt8(this.buffer, value, offset);
184 }
185 > buffer.ts
186 > indexOf(subarray: VSBuffer | Uint8Array, offset = 0) {
187 return binaryIndexOf(this.buffer, subarray instanceof VSBuffer ? subarray.buffer : subarray, offset);
188 }
189 > buffer.ts
190 > equals(other: VSBuffer): boolean {
191 if (this === other) {
192 return true;
199 return this.buffer.every((value, index) => value === other.buffer[index]);
200 }
201 > } buffer.ts
202 >
203 > /**
204 > * Like String.indexOf, but works on Uint8Arrays.
205 > * Uses the boyer-moore-horspool algorithm to be reasonably speedy.
206 > */
207 > export function binaryIndexOf(haystack: Uint8Array, needle: Uint8Array, offset = 0): number {
208 const needleLen = needle.byteLength;
209 const haystackLen = haystack.byteLength;
248 return result;
249 }
250 > buffer.ts
251 > export function readUInt16LE(source: Uint8Array, offset: number): number {
252 return (
253 ((source[offset + 0] << 0) >>> 0) |
255 );
256 }
257 > buffer.ts
258 > export function writeUInt16LE(destination: Uint8Array, value: number, offset: number): void {
259 destination[offset + 0] = (value & 0b11111111);
260 value = value >>> 8;
261 destination[offset + 1] = (value & 0b11111111);
262 }
263 > buffer.ts
264 > export function readUInt32BE(source: Uint8Array, offset: number): number {
265 return (
266 source[offset] * 2 ** 24
270 );
271 }
272 > buffer.ts
273 > export function writeUInt32BE(destination: Uint8Array, value: number, offset: number): void {
274 destination[offset + 3] = value;
275 value = value >>> 8;
280 destination[offset] = value;
281 }
282 > buffer.ts
283 > export function readUInt32LE(source: Uint8Array, offset: number): number {
284 return (
285 ((source[offset + 0] << 0) >>> 0) |
289 );
290 }
291 > buffer.ts
292 > export function writeUInt32LE(destination: Uint8Array, value: number, offset: number): void {
293 destination[offset + 0] = (value & 0b11111111);
294 value = value >>> 8;
299 destination[offset + 3] = (value & 0b11111111);
300 }
301 > buffer.ts
302 > export function readUInt8(source: Uint8Array, offset: number): number {
303 return source[offset];
304 }
305 > buffer.ts
306 > export function writeUInt8(destination: Uint8Array, value: number, offset: number): void {
307 destination[offset] = value;
308 }
309 > buffer.ts
310 > export interface VSBufferReadable extends streams.Readable<VSBuffer> { }
311 >
312 > export interface VSBufferReadableStream extends streams.ReadableStream<VSBuffer> { }
313 >
314 > export interface VSBufferWriteableStream extends streams.WriteableStream<VSBuffer> { }
315 >
316 > export interface VSBufferReadableBufferedStream extends streams.ReadableBufferedStream<VSBuffer> { }
317 >
318 > export function readableToBuffer(readable: VSBufferReadable): VSBuffer {
319 return streams.consumeReadable<VSBuffer>(readable, chunks => VSBuffer.concat(chunks));
320 }
321 > buffer.ts
322 > export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
323 return streams.toReadable<VSBuffer>(buffer);
324 }
325 > buffer.ts
326 > export function streamToBuffer(stream: streams.ReadableStream<VSBuffer>): Promise<VSBuffer> {
327 return streams.consumeStream<VSBuffer>(stream, chunks => VSBuffer.concat(chunks));
328 }
329 > buffer.ts
330 export async function bufferedStreamToBuffer(bufferedStream: streams.ReadableBufferedStream<VSBuffer>): Promise<VSBuffer> {
331 if (bufferedStream.ended) {
342 ]);
343 }
344 > buffer.ts
345 > export function bufferToStream(buffer: VSBuffer): streams.ReadableStream<VSBuffer> {
346 return streams.toStream<VSBuffer>(buffer, chunks => VSBuffer.concat(chunks));
347 }
348 > buffer.ts
349 > export function streamToBufferReadableStream(stream: streams.ReadableStreamEvents<Uint8Array | string>): streams.ReadableStream<VSBuffer> {
350 return streams.transform<Uint8Array | string, VSBuffer>(stream, { data: data => typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data) }, chunks => VSBuffer.concat(chunks));
351 }
352 > buffer.ts
353 > export function newWriteableBufferStream(options?: streams.WriteableStreamOptions): streams.WriteableStream<VSBuffer> {
354 return streams.newWriteableStream<VSBuffer>(chunks => VSBuffer.concat(chunks), options);
355 }
356 > buffer.ts
357 > export function prefixedBufferReadable(prefix: VSBuffer, readable: VSBufferReadable): VSBufferReadable {
358 return streams.prefixedReadable(prefix, readable, chunks => VSBuffer.concat(chunks));
359 }
360 > buffer.ts
361 > export function prefixedBufferStream(prefix: VSBuffer, stream: VSBufferReadableStream): VSBufferReadableStream {
362 return streams.prefixedStream(prefix, stream, chunks => VSBuffer.concat(chunks));
363 }
364 > buffer.ts
365 > /** Decodes base64 to a uint8 array. URL-encoded and unpadded base64 is allowed. */
366 > export function decodeBase64(encoded: string) {
367 let building = 0;
368 let remainder = 0;
424 return VSBuffer.wrap(buffer).slice(0, unpadded);
425 }
426 > buffer.ts
427 > const base64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
428 > const base64UrlSafeAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
429 >
430 > /** Encodes a buffer to a base64 string. */
431 > export function encodeBase64({ buffer }: VSBuffer, padded = true, urlSafe = false) {
432 const dictionary = urlSafe ? base64UrlSafeAlphabet : base64Alphabet;
433 let output = '';
463 return output;
464 }
465 > buffer.ts
466 > const hexChars = '0123456789abcdef';
467 > export function encodeHex({ buffer }: VSBuffer): string {
468 let result = '';
469 for (let i = 0; i < buffer.length; i++) {
474 return result;
475 }
476 > buffer.ts
477 > export function decodeHex(hex: string): VSBuffer {
478 if (hex.length % 2 !== 0) {
479 throw new SyntaxError('Hex string must have an even length');
485 return VSBuffer.wrap(out);
486 }
487 > buffer.ts
488 function decodeHexChar(str: string, position: number) {
489 const s = str.charCodeAt(position);
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/base/common/observableInternal/reactions/autorunImpl.ts 149 covered LOC · 33 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- autorunImpl.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 { IObservable, IObservableWithChange, IObserver, IReaderWithStore } from '../base.js';
7 > import { DebugNameData } from '../debugName.js';
8 > import { assertFn, BugIndicatingError, DisposableStore, IDisposable, markAsDisposed, onBugIndicatingError, trackDisposable } from '../commonFacade/deps.js';
9 > import { getLogger } from '../logging/logging.js';
10 > import { IChangeTracker } from '../changeTracker.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 >
13 > export const enum AutorunState {
14 > /**
15 > * A dependency could have changed.
16 > * We need to explicitly ask them if at least one dependency changed.
17 > */
18 > dependenciesMightHaveChanged = 1,
19 >
20 > /**
21 > * A dependency changed and we need to recompute.
22 > */
23 > stale = 2,
24 > upToDate = 3,
25 > }
26 >
27 function autorunStateToString(state: AutorunState): string {
28 switch (state) {
33 }
34 }
36 > export class AutorunObserver<TChangeSummary = any> implements IObserver, IReaderWithStore, IDisposable {
37 > private _state = AutorunState.stale;
38 > private _updateCount = 0;
39 > private _disposed = false;
40 > private _dependencies = new Set<IObservable<any>>();
41 > private _dependenciesToBeRemoved = new Set<IObservable<any>>();
42 > private _changeSummary: TChangeSummary | undefined;
43 > private _isRunning = false;
44 > private _iteration = 0;
45 >
46 > public get debugName(): string {
47 > return this._debugNameData.getDebugName(this) ?? '(anonymous)';
48 > }
49 >
50 > constructor(
51 > public readonly _debugNameData: DebugNameData, autorunImpl.ts
52 > public readonly _runFn: (reader: IReaderWithStore, changeSummary: TChangeSummary) => void,
53 > private readonly _changeTracker: IChangeTracker<TChangeSummary> | undefined,
54 > debugLocation: DebugLocation
55 > ) {
56 > this._changeSummary = this._changeTracker?.createChangeSummary(undefined);
57 > getLogger()?.handleAutorunCreated(this, debugLocation);
58 > this._run();
59 >
60 > trackDisposable(this);
61 > }
63 > public dispose(): void {
64 > if (this._disposed) { autorunImpl.ts
65 return;
66 }
67 > this._disposed = true; autorunImpl.ts
68 > for (const o of this._dependencies) {
69 > o.removeObserver(this); // Warning: external call!
70 > }
71 > this._dependencies.clear();
72 >
73 > if (this._store !== undefined) {
74 this._store.dispose();
75 }
76 > if (this._delayedStore !== undefined) { autorunImpl.ts
77 this._delayedStore.dispose();
78 }
80 > getLogger()?.handleAutorunDisposed(this);
81 > markAsDisposed(this);
82 > }
84 > private _run() {
85 > const emptySet = this._dependenciesToBeRemoved; autorunImpl.ts
86 > this._dependenciesToBeRemoved = this._dependencies;
87 > this._dependencies = emptySet;
88 >
89 > this._state = AutorunState.upToDate;
90 >
91 > try {
92 > if (!this._disposed) {
93 > getLogger()?.handleAutorunStarted(this);
94 > const changeSummary = this._changeSummary!;
95 > const delayedStore = this._delayedStore;
96 > if (delayedStore !== undefined) {
97 this._delayedStore = undefined;
98 }
99 > try { autorunImpl.ts
100 > this._isRunning = true;
101 > if (this._changeTracker) {
102 this._changeTracker.beforeUpdate?.(this, changeSummary);
103 this._changeSummary = this._changeTracker.createChangeSummary(changeSummary); // Warning: external call!
104 }
105 > if (this._store !== undefined) { autorunImpl.ts
106 this._store.dispose();
107 this._store = undefined;
108 }
110 > this._runFn(this, changeSummary); // Warning: external call!
111 > } catch (e) {
112 onBugIndicatingError(e);
113 > } finally { autorunImpl.ts
114 > this._isRunning = false;
115 > if (delayedStore !== undefined) {
116 delayedStore.dispose();
117 }
118 > } autorunImpl.ts
119 > }
120 > } finally {
121 > if (!this._disposed) {
122 > getLogger()?.handleAutorunFinished(this);
123 > }
124 > // We don't want our observed observables to think that they are (not even temporarily) not being observed.
125 > // Thus, we only unsubscribe from observables that are definitely not read anymore.
126 > for (const o of this._dependenciesToBeRemoved) {
127 o.removeObserver(this); // Warning: external call!
128 }
129 > this._dependenciesToBeRemoved.clear(); autorunImpl.ts
130 > }
131 > }
133 > public toString(): string {
134 return `Autorun<${this.debugName}>`;
135 }
137 > // IObserver implementation
138 > public beginUpdate(_observable: IObservable<any>): void {
139 if (this._state === AutorunState.upToDate) {
140 this._checkIterations();
143 this._updateCount++;
144 }
146 > public endUpdate(_observable: IObservable<any>): void {
147 try {
148 if (this._updateCount === 1) {
175 assertFn(() => this._updateCount >= 0);
176 }
178 > public handlePossibleChange(observable: IObservable<any>): void {
179 if (this._state === AutorunState.upToDate && this._isDependency(observable)) {
180 this._checkIterations();
182 }
183 }
185 > public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
186 if (this._isDependency(observable)) {
187 getLogger()?.handleAutorunDependencyChanged(this, observable, change);
203 }
204 }
206 > private _isDependency(observable: IObservableWithChange<any, any>): boolean {
207 return this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable);
208 }
210 > // IReader implementation
211 >
212 > private _ensureNoRunning(): void {
213 > if (!this._isRunning) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); } autorunImpl.ts
214 > }
216 > public readObservable<T>(observable: IObservable<T>): T {
217 > this._ensureNoRunning(); autorunImpl.ts
218 >
219 > // In case the run action disposes the autorun
220 > if (this._disposed) {
221 return observable.get(); // warning: external call!
222 }
224 > observable.addObserver(this); // warning: external call!
225 > const value = observable.get(); // warning: external call!
226 > this._dependencies.add(observable);
227 > this._dependenciesToBeRemoved.delete(observable);
228 > return value;
229 > }
231 > private _store: DisposableStore | undefined = undefined;
232 > get store(): DisposableStore {
233 this._ensureNoRunning();
234 if (this._disposed) {
241 return this._store;
242 }
244 > private _delayedStore: DisposableStore | undefined = undefined;
245 > get delayedStore(): DisposableStore {
246 this._ensureNoRunning();
247 if (this._disposed) {
254 return this._delayedStore;
255 }
257 > public debugGetState() {
258 return {
259 isRunning: this._isRunning,
264 };
265 }
267 > public debugRerun(): void {
268 if (!this._isRunning) {
269 this._run();
272 }
273 }
275 > private _checkIterations(): boolean {
276 if (this._iteration > 100) {
277 onBugIndicatingError(new BugIndicatingError(`Autorun '${this.debugName}' is stuck in an infinite update loop.`));
src/vs/platform/opener/common/opener.ts 139 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- opener.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 } from '../../../base/common/cancellation.js';
7 > import { IDisposable } from '../../../base/common/lifecycle.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { IEditorOptions, ITextEditorSelection } from '../../editor/common/editor.js';
10 > import { createDecorator } from '../../instantiation/common/instantiation.js';
11 >
12 > export const IOpenerService = createDecorator<IOpenerService>('openerService');
13 >
14 > export type OpenInternalOptions = {
15 >
16 > /**
17 > * Signals that the intent is to open an editor to the side
18 > * of the currently active editor.
19 > */
20 > readonly openToSide?: boolean;
21 >
22 > /**
23 > * Extra editor options to apply in case an editor is used to open.
24 > */
25 > readonly editorOptions?: IEditorOptions;
26 >
27 > /**
28 > * Signals that the editor to open was triggered through a user
29 > * action, such as keyboard or mouse usage.
30 > */
31 > readonly fromUserGesture?: boolean;
32 >
33 > /**
34 > * Allow command links to be handled.
35 > *
36 > * If this is an array, then only the commands included in the array can be run.
37 > */
38 > readonly allowCommands?: boolean | readonly string[];
39 > };
40 >
41 > export type OpenExternalOptions = {
42 > readonly openExternal?: boolean;
43 > readonly allowTunneling?: boolean;
44 > readonly allowContributedOpeners?: boolean | string;
45 > readonly fromWorkspace?: boolean;
46 > readonly skipValidation?: boolean;
47 > };
48 >
49 > export type OpenOptions = OpenInternalOptions & OpenExternalOptions;
50 >
51 > export type ResolveExternalUriOptions = { readonly allowTunneling?: boolean };
52 >
53 > export interface IResolvedExternalUri extends IDisposable {
54 > resolved: URI;
55 > }
56 >
57 > export interface IOpener {
58 > open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
59 > }
60 >
61 > export interface IExternalOpener {
62 > openExternal(href: string, ctx: { sourceUri: URI; preferredOpenerId?: string }, token: CancellationToken): Promise<boolean>;
63 > dispose?(): void;
64 > }
65 >
66 > export interface IValidator {
67 > shouldOpen(resource: URI | string, openOptions?: OpenOptions): Promise<boolean>;
68 > }
69 >
70 > export interface IExternalUriResolver {
71 > resolveExternalUri(resource: URI, options?: OpenOptions): Promise<{ resolved: URI; dispose(): void } | undefined>;
72 > }
73 >
74 > export interface IOpenerService {
75 >
76 > readonly _serviceBrand: undefined;
77 >
78 > /**
79 > * Register a participant that can handle the open() call.
80 > */
81 > registerOpener(opener: IOpener): IDisposable;
82 >
83 > /**
84 > * Register a participant that can validate if the URI resource be opened.
85 > * Validators are run before openers.
86 > */
87 > registerValidator(validator: IValidator): IDisposable;
88 >
89 > /**
90 > * Register a participant that can resolve an external URI resource to be opened.
91 > */
92 > registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable;
93 >
94 > /**
95 > * Sets the handler for opening externally. If not provided,
96 > * a default handler will be used.
97 > */
98 > setDefaultExternalOpener(opener: IExternalOpener): void;
99 >
100 > /**
101 > * Registers a new opener external resources openers.
102 > */
103 > registerExternalOpener(opener: IExternalOpener): IDisposable;
104 >
105 > /**
106 > * Opens a resource, like a webaddress, a document uri, or executes command.
107 > *
108 > * @param resource A resource
109 > * @return A promise that resolves when the opening is done.
110 > */
111 > open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
112 >
113 > /**
114 > * Resolve a resource to its external form.
115 > * @throws whenever resolvers couldn't resolve this resource externally.
116 > */
117 > resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise<IResolvedExternalUri>;
118 > }
119 >
120 > /**
121 > * Encodes selection into the `URI`.
122 > *
123 > * IMPORTANT: you MUST use `extractSelection` to separate the selection
124 > * again from the original `URI` before passing the `URI` into any
125 > * component that is not aware of selections.
126 > */
127 > export function withSelection(uri: URI, selection: ITextEditorSelection): URI {
128 return uri.with({ fragment: `${selection.startLineNumber},${selection.startColumn}${selection.endLineNumber ? `-${selection.endLineNumber}${selection.endColumn ? `,${selection.endColumn}` : ''}` : ''}` });
129 }
130 > opener.ts
131 > /**
132 > * file:///some/file.js#73
133 > * file:///some/file.js#L73
134 > * file:///some/file.js#73,84
135 > * file:///some/file.js#L73,84
136 > * file:///some/file.js#73-83
137 > * file:///some/file.js#L73-L83
138 > * file:///some/file.js#73,84-83,52
139 > * file:///some/file.js#L73,84-L83,52
140 > */
141 > export function extractSelection(uri: URI): { selection: ITextEditorSelection | undefined; uri: URI } {
142 let selection: ITextEditorSelection | undefined = undefined;
143 const match = /^L?(\d+)(?:,(\d+))?(-L?(\d+)(?:,(\d+))?)?/.exec(uri.fragment);
src/vs/base/common/observableInternal/observables/baseObservable.ts 128 covered LOC · 24 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- baseObservable.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 { IObservableWithChange, IObserver, IReader, IObservable } from '../base.js';
7 > import { DisposableStore } from '../commonFacade/deps.js';
8 > import { DebugLocation } from '../debugLocation.js';
9 > import { DebugOwner, getFunctionName } from '../debugName.js';
10 > import { debugGetObservableGraph } from '../logging/debugGetDependencyGraph.js';
11 > import { getLogger, logObservable } from '../logging/logging.js';
12 > import type { keepObserved, recomputeInitiallyAndOnChange } from '../utils/utils.js';
13 > import { derivedOpts } from './derived.js';
14 >
15 > let _derived: typeof derivedOpts;
16 > /**
17 > * @internal
18 > * This is to allow splitting files.
19 > */
20 > export function _setDerivedOpts(derived: typeof _derived) {
21 > _derived = derived;
22 > }
23 >
24 > let _recomputeInitiallyAndOnChange: typeof recomputeInitiallyAndOnChange;
25 > export function _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange: typeof _recomputeInitiallyAndOnChange) {
26 > _recomputeInitiallyAndOnChange = recomputeInitiallyAndOnChange;
27 > }
28 >
29 > let _keepObserved: typeof keepObserved;
30 > export function _setKeepObserved(keepObserved: typeof _keepObserved) {
31 > _keepObserved = keepObserved;
32 > }
33 >
34 > let _debugGetObservableGraph: typeof debugGetObservableGraph;
35 > export function _setDebugGetObservableGraph(debugGetObservableGraph: typeof _debugGetObservableGraph) {
36 > _debugGetObservableGraph = debugGetObservableGraph;
37 > }
38 >
39 > export abstract class ConvenientObservable<T, TChange> implements IObservableWithChange<T, TChange> {
40 > get TChange(): TChange { return null!; }
41 >
42 > public abstract get(): T;
43 >
44 > public reportChanges(): void {
45 this.get();
46 }
48 > public abstract addObserver(observer: IObserver): void;
49 > public abstract removeObserver(observer: IObserver): void;
50 >
51 > /** @sealed */
52 > public read(reader: IReader | undefined): T {
53 > if (reader) { baseObservable.ts
54 > return reader.readObservable(this); baseObservable.ts
55 > } else { baseObservable.ts
56 return this.get();
57 }
60 > /** @sealed */
61 > public map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
62 > public map<TNew>(owner: DebugOwner, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
63 > public map<TNew>(fnOrOwner: DebugOwner | ((value: T, reader: IReader) => TNew), fnOrUndefined?: (value: T, reader: IReader) => TNew, debugLocation: DebugLocation = DebugLocation.ofCaller()): IObservable<TNew> {
64 const owner = fnOrUndefined === undefined ? undefined : fnOrOwner as DebugOwner;
65 const fn = fnOrUndefined === undefined ? fnOrOwner as (value: T, reader: IReader) => TNew : fnOrUndefined;
91 );
92 }
94 > public abstract log(): IObservableWithChange<T, TChange>;
95 >
96 > /**
97 > * @sealed
98 > * Converts an observable of an observable value into a direct observable of the value.
99 > */
100 > public flatten<TNew>(this: IObservable<IObservableWithChange<TNew, any>>): IObservable<TNew> {
101 return _derived(
102 {
107 );
108 }
110 > public recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T> {
111 store.add(_recomputeInitiallyAndOnChange!(this, handleValue));
112 return this;
113 }
115 > /**
116 > * Ensures that this observable is observed. This keeps the cache alive.
117 > * However, in case of deriveds, it does not force eager evaluation (only when the value is read/get).
118 > * Use `recomputeInitiallyAndOnChange` for eager evaluation.
119 > */
120 > public keepObserved(store: DisposableStore): IObservable<T> {
121 store.add(_keepObserved!(this));
122 return this;
123 }
125 > public abstract get debugName(): string;
126 >
127 > protected get debugValue() {
128 return this.get();
129 }
131 > get debug(): DebugHelper {
132 return new DebugHelper(this);
133 }
135 >
136 > class DebugHelper {
137 > constructor(public readonly observable: IObservableWithChange<any, any>) {
138 }
140 > getDependencyGraph(): string {
141 return _debugGetObservableGraph(this.observable, { type: 'dependencies' });
142 }
144 > getObserverGraph(): string {
145 return _debugGetObservableGraph(this.observable, { type: 'observers' });
146 }
148 >
149 > export abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
150 > protected readonly _observers = new Set<IObserver>();
151 >
152 > constructor(debugLocation: DebugLocation) {
153 > super(); baseObservable.ts
154 > getLogger()?.handleObservableCreated(this, debugLocation);
155 > }
157 > public addObserver(observer: IObserver): void {
158 > const len = this._observers.size; baseObservable.ts
159 > this._observers.add(observer);
160 > if (len === 0) {
161 > this.onFirstObserverAdded();
162 > }
163 > if (len !== this._observers.size) {
164 > getLogger()?.handleOnListenerCountChanged(this, this._observers.size);
165 > }
166 > }
168 > public removeObserver(observer: IObserver): void {
169 > const deleted = this._observers.delete(observer); baseObservable.ts
170 > if (deleted && this._observers.size === 0) {
171 > this.onLastObserverRemoved();
172 > }
173 > if (deleted) {
174 > getLogger()?.handleOnListenerCountChanged(this, this._observers.size);
175 > }
176 > }
178 > protected onFirstObserverAdded(): void { }
179 > protected onLastObserverRemoved(): void { }
180 >
181 > public override log(): IObservableWithChange<T, TChange> {
182 const hadLogger = !!getLogger();
183 logObservable(this);
src/vs/platform/instantiation/common/instantiation.ts 124 covered LOC · 11 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 }); instantiation.ts
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/base/test/common/virtualScheduling/trace.ts 122 covered LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- trace.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 } from '../../../common/errors.js';
7 >
8 > /**
9 > * # Trace — causal-chain attribution for scheduled work
10 > *
11 > * A {@link Trace} is an immutable value identifying a causal chain. Every
12 > * non-root trace carries a `parent`; the head of the chain has no parent.
13 > * Use {@link child} to extend a chain when scheduling follow-up work.
14 > *
15 > * Traces are used to answer "who caused this?" for any virtual event:
16 > * useful for debugging, for per-owner termination, and for attribution in
17 > * error messages.
18 > */
19 > export class Trace {
20 > private static _idCounter = 0;
21 > public readonly id: number = ++Trace._idCounter;
22 > public readonly root: Trace;
23 > public readonly depth: number;
24 >
25 > constructor(
26 > public readonly parent: Trace | undefined,
27 > public readonly label: string,
28 > public readonly stack: string | undefined = undefined,
29 > ) {
30 > this.root = parent?.root ?? this;
31 > this.depth = (parent?.depth ?? -1) + 1;
32 > }
33 >
34 > child(label: string, stack?: string): Trace {
35 return new Trace(this, label, stack);
36 }
37 > trace.ts
38 > /** "#id label ← #id label ← … ← #id label" */
39 > describe(): string {
40 const parts: string[] = [];
41 for (let t: Trace | undefined = this; t; t = t.parent) {
44 return parts.join(' ← ');
45 }
46 > trace.ts
47 > toString(): string { return this.describe(); }
48 > }
49 >
50 > /** Sentinel for "no known causal predecessor". */
51 > export const ROOT_TRACE: Trace = new Trace(undefined, '<root>');
52 >
53 > export function createTraceRoot(label: string, stack?: string): Trace {
54 return new Trace(undefined, label, stack);
55 }
56 > trace.ts
57 > interface Frame {
58 > readonly trace: Trace;
59 > readonly prev: Frame | undefined;
60 > }
61 >
62 > const ROOT_FRAME: Frame = { trace: ROOT_TRACE, prev: undefined };
63 >
64 > /**
65 > * Options for {@link TraceContext.runAsHandler}.
66 > *
67 > * # Why this is a per-call option
68 > *
69 > * `runAsHandler` cannot restore the previous trace synchronously: microtasks
70 > * enqueued by `fn` (including awaited continuations) must observe the new
71 > * trace. So the reset is deferred — but it must fire after the *closure* of
72 > * the microtask queue (the current microtask plus every microtask it
73 > * recursively enqueues), not just one drain.
74 > *
75 > * Per spec, the host doesn't run a macrotask until the microtask queue is
76 > * empty, so any macrotask primitive (`setTimeout(0)`, `setImmediate`, the
77 > * `setTimeout0` shim) achieves this. Letting the *caller* supply the sink
78 > * means:
79 > *
80 > * - the {@link VirtualTimeProcessor} can route the reset through the same
81 > * primitive its embedding uses for its own host hops, eliminating any
82 > * race between the processor's hops and the trace-reset timer;
83 > *
84 > * - production code without a processor can still use a real
85 > * `setTimeout(0)`-based sink and get the same semantics;
86 > *
87 > * - tests can install a deterministic sink (e.g. a hand-driven queue) for
88 > * fully synchronous assertions.
89 > */
90 > export interface RunAsHandlerOptions {
91 > /**
92 > * Sink for the deferred trace-reset.
93 > *
94 > * Must invoke `reset` after the microtask closure that follows the
95 > * `runAsHandler` call returns — i.e. on the next host macrotask.
96 > */
97 > readonly afterMicrotaskClosure: (reset: () => void) => void;
98 > }
99 >
100 > /**
101 > * Holds the mutable "current trace frame" slot. Construct fresh instances
102 > * for test isolation, or use {@link TraceContext.instance} for shared state.
103 > */
104 > export class TraceContext {
105 > public static readonly instance = new TraceContext();
106 >
107 > private _current: Frame = ROOT_FRAME;
108 > private _isHandlerRunning = false;
109 >
110 > currentTrace(): Trace { return this._current.trace; }
111 >
112 > /**
113 > * Install `t` as current for the synchronous duration of `fn`, then
114 > * restore. Nestable. Microtasks enqueued by fn that run after fn returns
115 > * see the *restored* trace — use {@link runAsHandler} when continuation
116 > * inheritance is wanted.
117 > */
118 > runWithTrace<T>(t: Trace, fn: () => T): T {
119 const prev = this._current;
120 const next: Frame = { trace: t, prev };
132 }
133 }
134 > trace.ts
135 > /**
136 > * Install `t` as current and run `fn`. The trace stays current through
137 > * the microtask closure that follows `fn`, so awaited continuations
138 > * inside fn observe `t`. The reset is dispatched via
139 > * `opts.afterMicrotaskClosure`.
140 > *
141 > * Throws on synchronous re-entry: timer callbacks never nest on the
142 > * same JS stack frame, so this only fires for misuse.
143 > */
144 > runAsHandler<T>(t: Trace, fn: () => T, opts: RunAsHandlerOptions): T {
145 if (this._isHandlerRunning) {
146 throw new Error(
165 }
166 }
167 > trace.ts
168 > _resetForTesting(): void {
169 this._current = ROOT_FRAME;
170 this._isHandlerRunning = false;
171 }
172 > } trace.ts
src/vs/platform/telemetry/common/telemetry.ts 115 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- telemetry.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 { createDecorator } from '../../instantiation/common/instantiation.js';
7 > import { ClassifiedEvent, IGDPRProperty, OmitMetadata, StrictPropertyCheck } from './gdprTypings.js';
8 >
9 > export const ITelemetryService = createDecorator<ITelemetryService>('telemetryService');
10 >
11 > export interface ITelemetryData {
12 > from?: string;
13 > target?: string;
14 > [key: string]: string | unknown | undefined;
15 > }
16 >
17 > export interface ITelemetryService {
18 >
19 > readonly _serviceBrand: undefined;
20 >
21 > readonly telemetryLevel: TelemetryLevel;
22 >
23 > readonly sessionId: string;
24 > readonly machineId: string;
25 > readonly sqmId: string;
26 > readonly devDeviceId: string;
27 > readonly firstSessionDate: string;
28 > readonly msftInternal?: boolean;
29 >
30 > /**
31 > * Whether error telemetry will get sent. If false, `publicLogError` will no-op.
32 > */
33 > readonly sendErrorTelemetry: boolean;
34 >
35 > /**
36 > * @deprecated Use publicLog2 and the typescript GDPR annotation where possible
37 > */
38 > publicLog(eventName: string, data?: ITelemetryData): void;
39 >
40 > /**
41 > * Sends a telemetry event that has been privacy approved.
42 > * Do not call this unless you have been given approval.
43 > */
44 > publicLog2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void;
45 >
46 > /**
47 > * @deprecated Use publicLogError2 and the typescript GDPR annotation where possible
48 > */
49 > publicLogError(errorEventName: string, data?: ITelemetryData): void;
50 >
51 > publicLogError2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void;
52 >
53 > setExperimentProperty(name: string, value: string): void;
54 >
55 > /**
56 > * Sets a common property that will be attached to all telemetry events.
57 > * Common properties are added after PII cleaning and cannot be overridden by event data.
58 > */
59 > setCommonProperty(name: string, value: string | boolean): void;
60 > }
61 >
62 > export function telemetryLevelEnabled(service: ITelemetryService, level: TelemetryLevel): boolean {
63 return service.telemetryLevel >= level;
64 }
66 > /**
67 > * Replaces `/` and `\` with `|` in model identifiers to prevent the
68 > * telemetry pipeline from redacting them as file paths.
69 > */
70 > export function escapeModelIdForTelemetry(modelId: string | undefined): string | undefined {
71 return modelId?.replace(/[\/\\]/g, '|');
72 }
74 > export interface ITelemetryEndpoint {
75 > id: string;
76 > aiKey: string;
77 > sendErrorTelemetry: boolean;
78 > }
79 >
80 > export const ICustomEndpointTelemetryService = createDecorator<ICustomEndpointTelemetryService>('customEndpointTelemetryService');
81 >
82 > export interface ICustomEndpointTelemetryService {
83 > readonly _serviceBrand: undefined;
84 >
85 > publicLog(endpoint: ITelemetryEndpoint, eventName: string, data?: ITelemetryData): void;
86 > publicLogError(endpoint: ITelemetryEndpoint, errorEventName: string, data?: ITelemetryData): void;
87 > }
88 >
89 > // Keys
90 > export const currentSessionDateStorageKey = 'telemetry.currentSessionDate';
91 > export const firstSessionDateStorageKey = 'telemetry.firstSessionDate';
92 > export const lastSessionDateStorageKey = 'telemetry.lastSessionDate';
93 > export const machineIdKey = 'telemetry.machineId';
94 > export const sqmIdKey = 'telemetry.sqmId';
95 > export const devDeviceIdKey = 'telemetry.devDeviceId';
96 >
97 > // Configuration Keys
98 > export const TELEMETRY_SECTION_ID = 'telemetry';
99 > export const TELEMETRY_SETTING_ID = 'telemetry.telemetryLevel';
100 > export const TELEMETRY_CRASH_REPORTER_SETTING_ID = 'telemetry.enableCrashReporter';
101 > export const TELEMETRY_OLD_SETTING_ID = 'telemetry.enableTelemetry';
102 >
103 > export const enum TelemetryLevel {
104 > NONE = 0,
105 > CRASH = 1,
106 > ERROR = 2,
107 > USAGE = 3
108 > }
109 >
110 > export const enum TelemetryConfiguration {
111 > OFF = 'off',
112 > CRASH = 'crash',
113 > ERROR = 'error',
114 > ON = 'all'
115 > }
116 >
117 > export interface ICommonProperties {
118 > [name: string]: string | boolean | undefined;
119 > }
src/vs/workbench/contrib/debug/common/debugStorage.ts 115 covered LOC · 30 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugStorage.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 { Disposable } from '../../../../base/common/lifecycle.js';
7 > import { ISettableObservable, observableValue } from '../../../../base/common/observable.js';
8 > import { URI } from '../../../../base/common/uri.js';
9 > import { ILogService } from '../../../../platform/log/common/log.js';
10 > import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
11 > import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
12 > import { IDebugModel, IEvaluate, IExpression } from './debug.js';
13 > import { Breakpoint, DataBreakpoint, ExceptionBreakpoint, Expression, FunctionBreakpoint } from './debugModel.js';
14 > import { ITextFileService } from '../../../services/textfile/common/textfiles.js';
15 > import { mapValues } from '../../../../base/common/objects.js';
16 >
17 > const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
18 > const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
19 > const DEBUG_DATA_BREAKPOINTS_KEY = 'debug.databreakpoint';
20 > const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint';
21 > const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
22 > const DEBUG_CHOSEN_ENVIRONMENTS_KEY = 'debug.chosenenvironment';
23 > const DEBUG_UX_STATE_KEY = 'debug.uxstate';
24 >
25 > export interface IChosenEnvironment {
26 > type: string;
27 > dynamicLabel?: string;
28 > }
29 >
30 > export class DebugStorage extends Disposable {
31 > public readonly breakpoints: ISettableObservable<Breakpoint[]>;
32 > public readonly functionBreakpoints: ISettableObservable<FunctionBreakpoint[]>;
33 > public readonly exceptionBreakpoints: ISettableObservable<ExceptionBreakpoint[]>;
34 > public readonly dataBreakpoints: ISettableObservable<DataBreakpoint[]>;
35 > public readonly watchExpressions: ISettableObservable<Expression[]>;
36 >
37 > constructor(
38 > @IStorageService private readonly storageService: IStorageService, debugStorage.ts
39 > @ITextFileService private readonly textFileService: ITextFileService,
40 > @IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
41 > @ILogService private readonly logService: ILogService
42 > ) {
43 > super();
44 > this.breakpoints = observableValue(this, this.loadBreakpoints());
45 > this.functionBreakpoints = observableValue(this, this.loadFunctionBreakpoints());
46 > this.exceptionBreakpoints = observableValue(this, this.loadExceptionBreakpoints());
47 > this.dataBreakpoints = observableValue(this, this.loadDataBreakpoints());
48 > this.watchExpressions = observableValue(this, this.loadWatchExpressions());
49 >
50 > this._register(storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, this._store)(e => {
51 if (e.external) {
52 switch (e.key) {
63 }
64 }
65 > })); debugStorage.ts
66 > }
68 > loadDebugUxState(): 'simple' | 'default' {
69 return this.storageService.get(DEBUG_UX_STATE_KEY, StorageScope.WORKSPACE, 'default') as 'simple' | 'default';
70 }
72 > storeDebugUxState(value: 'simple' | 'default'): void {
73 this.storageService.store(DEBUG_UX_STATE_KEY, value, StorageScope.WORKSPACE, StorageTarget.MACHINE);
74 }
76 > private loadBreakpoints(): Breakpoint[] {
77 > let result: Breakpoint[] | undefined; debugStorage.ts
78 > try {
79 > result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: ReturnType<Breakpoint['toJSON']>) => {
80 breakpoint.uri = URI.revive(breakpoint.uri);
81 return new Breakpoint(breakpoint, this.textFileService, this.uriIdentityService, this.logService, breakpoint.id);
82 > }); debugStorage.ts
83 > } catch (e) {
84 this.logService.error('Failed to load breakpoints from storage', e);
85 }
87 > return result || [];
88 > }
90 > private loadFunctionBreakpoints(): FunctionBreakpoint[] {
91 > let result: FunctionBreakpoint[] | undefined; debugStorage.ts
92 > try {
93 > result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: ReturnType<FunctionBreakpoint['toJSON']>) => {
94 return new FunctionBreakpoint(fb, fb.id);
95 > }); debugStorage.ts
96 > } catch (e) {
97 this.logService.error('Failed to load function breakpoints from storage', e);
98 }
100 > return result || [];
101 > }
103 > private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
104 > let result: ExceptionBreakpoint[] | undefined; debugStorage.ts
105 > try {
106 > result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: ReturnType<ExceptionBreakpoint['toJSON']>) => {
107 return new ExceptionBreakpoint(exBreakpoint, exBreakpoint.id);
108 > }); debugStorage.ts
109 > } catch (e) {
110 this.logService.error('Failed to load exception breakpoints from storage', e);
111 }
113 > return result || [];
114 > }
116 > private loadDataBreakpoints(): DataBreakpoint[] {
117 > let result: DataBreakpoint[] | undefined; debugStorage.ts
118 > try {
119 > result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: ReturnType<DataBreakpoint['toJSON']>) => {
120 return new DataBreakpoint(dbp, dbp.id);
121 > }); debugStorage.ts
122 > } catch (e) {
123 this.logService.error('Failed to load data breakpoints from storage', e);
124 }
126 > return result || [];
127 > }
129 > private loadWatchExpressions(): Expression[] {
130 > let result: Expression[] | undefined; debugStorage.ts
131 > try {
132 > result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string; id: string }) => {
133 return new Expression(watchStoredData.name, watchStoredData.id);
134 > }); debugStorage.ts
135 > } catch (e) {
136 this.logService.error('Failed to load watch expressions from storage', e);
137 }
139 > return result || [];
140 > }
142 > loadChosenEnvironments(): Record<string, IChosenEnvironment> {
143 const obj = JSON.parse(this.storageService.get(DEBUG_CHOSEN_ENVIRONMENTS_KEY, StorageScope.WORKSPACE, '{}'));
144 // back compat from when this was a string map:
145 return mapValues(obj, (value): IChosenEnvironment => typeof value === 'string' ? { type: value } : value);
146 }
148 > storeChosenEnvironments(environments: Record<string, IChosenEnvironment>): void {
149 this.storageService.store(DEBUG_CHOSEN_ENVIRONMENTS_KEY, JSON.stringify(environments), StorageScope.WORKSPACE, StorageTarget.MACHINE);
150 }
152 > storeWatchExpressions(watchExpressions: (IExpression & IEvaluate)[]): void {
153 if (watchExpressions.length) {
154 this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE, StorageTarget.MACHINE);
157 }
158 }
160 > storeBreakpoints(debugModel: IDebugModel): void {
161 const breakpoints = debugModel.getBreakpoints();
162 if (breakpoints.length) {
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/base/common/observableInternal/observables/derivedImpl.ts 112 covered LOC · 23 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- derivedImpl.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 { IObservable, IObservableWithChange, IObserver, IReaderWithStore, ISettableObservable, ITransaction, } from '../base.js';
7 > import { BaseObservable } from './baseObservable.js';
8 > import { DebugNameData } from '../debugName.js';
9 > import { BugIndicatingError, DisposableStore, EqualityComparer, assertFn, onBugIndicatingError } from '../commonFacade/deps.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { IChangeTracker } from '../changeTracker.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > export interface IDerivedReader<TChange = void> extends IReaderWithStore {
15 > /**
16 > * Call this to report a change delta or to force report a change, even if the new value is the same as the old value.
17 > */
18 > reportChange(change: TChange): void;
19 > }
20 >
21 > export const enum DerivedState {
22 > /** Initial state, no previous value, recomputation needed */
23 > initial = 0,
24 >
25 > /**
26 > * A dependency could have changed.
27 > * We need to explicitly ask them if at least one dependency changed.
28 > */
29 > dependenciesMightHaveChanged = 1,
30 >
31 > /**
32 > * A dependency changed and we need to recompute.
33 > * After recomputation, we need to check the previous value to see if we changed as well.
34 > */
35 > stale = 2,
36 >
37 > /**
38 > * No change reported, our cached value is up to date.
39 > */
40 > upToDate = 3,
41 > }
42 >
43 function derivedStateToString(state: DerivedState): string {
44 switch (state) {
50 }
51 }
53 > export class Derived<T, TChangeSummary = any, TChange = void> extends BaseObservable<T, TChange> implements IDerivedReader<TChange>, IObserver {
54 > private _state = DerivedState.initial;
55 > private _value: T | undefined = undefined;
56 > private _updateCount = 0;
57 > private _dependencies = new Set<IObservable<any>>();
58 > private _dependenciesToBeRemoved = new Set<IObservable<any>>();
59 > private _changeSummary: TChangeSummary | undefined = undefined;
60 > private _isUpdating = false;
61 > private _isComputing = false;
62 > private _didReportChange = false;
63 > private _isInBeforeUpdate = false;
64 > private _isReaderValid = false;
65 > private _store: DisposableStore | undefined = undefined;
66 > private _delayedStore: DisposableStore | undefined = undefined;
67 > private _removedObserverToCallEndUpdateOn: Set<IObserver> | null = null;
68 >
69 > public override get debugName(): string {
70 > return this._debugNameData.getDebugName(this) ?? '(anonymous)';
71 > }
72 >
73 > constructor(
74 public readonly _debugNameData: DebugNameData,
75 public readonly _computeFn: (reader: IDerivedReader<TChange>, changeSummary: TChangeSummary) => T,
82 this._changeSummary = this._changeTracker?.createChangeSummary(undefined);
83 }
85 > protected override onLastObserverRemoved(): void {
86 /**
87 * We are not tracking changes anymore, thus we have to assume
107 this._handleLastObserverRemoved?.();
108 }
110 > public override get(): T {
111 const checkEnabled = false; // TODO set to true
112 if (this._isComputing && checkEnabled) {
164 }
165 }
167 > private _recompute() {
168 let didChange = false;
169 this._isComputing = true;
238 }
239 }
241 > public override toString(): string {
242 return `LazyDerived<${this.debugName}>`;
243 }
245 > // IObserver Implementation
246 >
247 > public beginUpdate<T>(_observable: IObservable<T>): void {
248 if (this._isUpdating) {
249 throw new BugIndicatingError('Cyclic deriveds are not supported yet!');
272 }
273 }
275 > public endUpdate<T>(_observable: IObservable<T>): void {
276 this._updateCount--;
277 if (this._updateCount === 0) {
291 assertFn(() => this._updateCount >= 0);
292 }
294 > public handlePossibleChange<T>(observable: IObservable<T>): void {
295 // In all other states, observers already know that we might have changed.
296 if (this._state === DerivedState.upToDate && this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) {
301 }
302 }
304 > public handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
305 if (this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable) || this._isInBeforeUpdate) {
306 getLogger()?.handleDerivedDependencyChanged(this, observable, change);
329 }
330 }
332 > // IReader Implementation
333 >
334 > private _ensureReaderValid(): void {
335 if (!this._isReaderValid) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); }
336 }
338 > public readObservable<T>(observable: IObservable<T>): T {
339 this._ensureReaderValid();
340
348 return value;
349 }
351 > public reportChange(change: TChange): void {
352 this._ensureReaderValid();
353
358 }
359 }
361 > get store(): DisposableStore {
362 this._ensureReaderValid();
363
367 return this._store;
368 }
370 > get delayedStore(): DisposableStore {
371 this._ensureReaderValid();
372
376 return this._delayedStore;
377 }
379 > public override addObserver(observer: IObserver): void {
380 const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCount > 0;
381 super.addObserver(observer);
387 }
388 }
390 > public override removeObserver(observer: IObserver): void {
391 if (this._observers.has(observer) && this._updateCount > 0) {
392 if (!this._removedObserverToCallEndUpdateOn) {
397 super.removeObserver(observer);
398 }
400 > public debugGetState() {
401 return {
402 state: this._state,
408 };
409 }
411 > public debugSetValue(newValue: unknown) {
412 // eslint-disable-next-line local/code-no-any-casts
413 this._value = newValue as any;
414 }
416 > public debugRecompute(): void {
417 this.beginUpdate(this);
418 try {
426 }
427 }
429 > public setValue(newValue: T, tx: ITransaction, change: TChange): void {
430 this._value = newValue;
431 const observers = this._observers;
435 }
436 }
437 > } derivedImpl.ts
438 >
439 >
440 > export class DerivedWithSetter<T, TChangeSummary = any, TOutChanges = any> extends Derived<T, TChangeSummary, TOutChanges> implements ISettableObservable<T, TOutChanges> {
441 > constructor(
442 debugNameData: DebugNameData,
443 computeFn: (reader: IDerivedReader<TOutChanges>, changeSummary: TChangeSummary) => T,
src/vs/base/common/equals.ts 108 covered LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- equals.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 >
8 > /*
9 > * Each function in this file which offers an equality comparison, has an accompanying
10 > * `*C` variant which returns an EqualityComparer function.
11 > *
12 > * The `*C` variant allows for easier composition of equality comparers and improved type-inference.
13 > */
14 >
15 >
16 > /** Represents a function that decides if two values are equal. */
17 > export type EqualityComparer<T> = (a: T, b: T) => boolean;
18 >
19 > export interface IEquatable<T> {
20 > equals(other: T): boolean;
21 > }
22 >
23 > /**
24 > * Compares two items for equality using strict equality.
25 > */
26 > export function strictEquals<T>(a: T, b: T): boolean {
27 return a === b;
28 }
29 > equals.ts
30 > export function strictEqualsC<T>(): EqualityComparer<T> {
31 return (a, b) => a === b;
32 }
33 > equals.ts
34 > /**
35 > * Checks if the items of two arrays are equal.
36 > * By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
37 > */
38 > export function arrayEquals<T>(a: readonly T[], b: readonly T[], itemEquals?: EqualityComparer<T>): boolean {
39 return arrays.equals(a, b, itemEquals ?? strictEquals);
40 }
41 > equals.ts
42 > /**
43 > * Checks if the items of two arrays are equal.
44 > * By default, strict equality is used to compare elements, but a custom equality comparer can be provided.
45 > */
46 > export function arrayEqualsC<T>(itemEquals?: EqualityComparer<T>): EqualityComparer<readonly T[]> {
47 return (a, b) => arrays.equals(a, b, itemEquals ?? strictEquals);
48 }
49 > equals.ts
50 > /**
51 > * Drills into arrays (items ordered) and objects (keys unordered) and uses strict equality on everything else.
52 > */
53 > export function structuralEquals<T>(a: T, b: T): boolean {
54 if (a === b) {
55 return true;
95 return false;
96 }
97 > equals.ts
98 > export function structuralEqualsC<T>(): EqualityComparer<T> {
99 return (a, b) => structuralEquals(a, b);
100 }
101 > equals.ts
102 > /**
103 > * `getStructuralKey(a) === getStructuralKey(b) <=> structuralEquals(a, b)`
104 > * (assuming that a and b are not cyclic structures and nothing extends globalThis Array).
105 > */
106 > export function getStructuralKey(t: unknown): string {
107 return JSON.stringify(toNormalizedJsonStructure(t));
108 }
109 > equals.ts
110 > let objectId = 0;
111 > const objIds = new WeakMap<object, number>();
112 >
113 function toNormalizedJsonStructure(t: unknown): unknown {
114 if (Array.isArray(t)) {
136 return t;
137 }
138 > equals.ts
139 >
140 > /**
141 > * Two items are considered equal, if their stringified representations are equal.
142 > */
143 > export function jsonStringifyEquals<T>(a: T, b: T): boolean {
144 return JSON.stringify(a) === JSON.stringify(b);
145 }
146 > equals.ts
147 > /**
148 > * Two items are considered equal, if their stringified representations are equal.
149 > */
150 > export function jsonStringifyEqualsC<T>(): EqualityComparer<T> {
151 return (a, b) => JSON.stringify(a) === JSON.stringify(b);
152 }
153 > equals.ts
154 > /**
155 > * Uses `item.equals(other)` to determine equality.
156 > */
157 > export function thisEqualsC<T extends IEquatable<T>>(): EqualityComparer<T> {
158 return (a, b) => a.equals(b);
159 }
160 > equals.ts
161 > /**
162 > * Checks if two items are both null or undefined, or are equal according to the provided equality comparer.
163 > */
164 > export function equalsIfDefined<T>(v1: T | undefined | null, v2: T | undefined | null, equals: EqualityComparer<T>): boolean {
165 if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
166 return v2 === v1;
168 return equals(v1, v2);
169 }
170 > equals.ts
171 > /**
172 > * Returns an equality comparer that checks if two items are both null or undefined, or are equal according to the provided equality comparer.
173 > */
174 > export function equalsIfDefinedC<T>(equals: EqualityComparer<T>): EqualityComparer<T | undefined | null> {
175 return (v1, v2) => {
176 if (v1 === undefined || v1 === null || v2 === undefined || v2 === null) {
180 };
181 }
182 > equals.ts
183 > /**
184 > * Each function in this file which offers an equality comparison, has an accompanying
185 > * `*C` variant which returns an EqualityComparer function.
186 > *
187 > * The `*C` variant allows for easier composition of equality comparers and improved type-inference.
188 > */
189 > export namespace equals {
190 > export const strict = strictEquals;
191 > export const strictC = strictEqualsC;
192 >
193 > export const array = arrayEquals;
194 > export const arrayC = arrayEqualsC;
195 >
196 > export const structural = structuralEquals;
197 > export const structuralC = structuralEqualsC;
198 >
199 > export const jsonStringify = jsonStringifyEquals;
200 > export const jsonStringifyC = jsonStringifyEqualsC;
201 >
202 > export const thisC = thisEqualsC;
203 >
204 > export const ifDefined = equalsIfDefined;
205 > export const ifDefinedC = equalsIfDefinedC;
206 > }
src/vs/platform/workspace/common/workspaceTrust.ts 108 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- workspaceTrust.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 { IDisposable } from '../../../base/common/lifecycle.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { createDecorator } from '../../instantiation/common/instantiation.js';
10 >
11 > export enum WorkspaceTrustScope {
12 > Local = 0,
13 > Remote = 1
14 > }
15 >
16 > export interface WorkspaceTrustRequestButton {
17 > readonly label: string;
18 > readonly type: 'ContinueWithTrust' | 'ContinueWithoutTrust' | 'Manage' | 'Cancel';
19 > }
20 >
21 > export interface ResourceTrustRequestOptions {
22 > readonly uri: URI;
23 > readonly message?: string;
24 > }
25 >
26 > export interface WorkspaceTrustRequestOptions {
27 > readonly buttons?: WorkspaceTrustRequestButton[];
28 > readonly message?: string;
29 > }
30 >
31 > export const IWorkspaceTrustEnablementService = createDecorator<IWorkspaceTrustEnablementService>('workspaceTrustEnablementService');
32 >
33 > export interface IWorkspaceTrustEnablementService {
34 > readonly _serviceBrand: undefined;
35 >
36 > isWorkspaceTrustEnabled(): boolean;
37 > }
38 >
39 > export const IWorkspaceTrustManagementService = createDecorator<IWorkspaceTrustManagementService>('workspaceTrustManagementService');
40 >
41 > export interface IWorkspaceTrustManagementService {
42 > readonly _serviceBrand: undefined;
43 >
44 > readonly onDidChangeTrust: Event<boolean>;
45 > readonly onDidChangeTrustedFolders: Event<void>;
46 >
47 > readonly workspaceResolved: Promise<void>;
48 > readonly workspaceTrustInitialized: Promise<void>;
49 > acceptsOutOfWorkspaceFiles: boolean;
50 >
51 > isWorkspaceTrusted(): boolean;
52 > isWorkspaceTrustForced(): boolean;
53 >
54 > canSetParentFolderTrust(): boolean;
55 > setParentFolderTrust(trusted: boolean): Promise<void>;
56 >
57 > canSetWorkspaceTrust(): boolean;
58 > setWorkspaceTrust(trusted: boolean): Promise<void>;
59 >
60 > getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo>;
61 > setUrisTrust(uri: URI[], trusted: boolean): Promise<void>;
62 >
63 > getTrustedUris(): URI[];
64 > setTrustedUris(uris: URI[]): Promise<void>;
65 >
66 > addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable;
67 > }
68 >
69 > export const enum WorkspaceTrustUriResponse {
70 > Open = 1,
71 > OpenInNewWindow = 2,
72 > Cancel = 3
73 > }
74 >
75 > export const IWorkspaceTrustRequestService = createDecorator<IWorkspaceTrustRequestService>('workspaceTrustRequestService');
76 >
77 > export interface IWorkspaceTrustRequestService {
78 > readonly _serviceBrand: undefined;
79 >
80 > readonly onDidInitiateOpenFilesTrustRequest: Event<void>;
81 > readonly onDidInitiateWorkspaceTrustRequest: Event<WorkspaceTrustRequestOptions | undefined>;
82 > readonly onDidInitiateWorkspaceTrustRequestOnStartup: Event<void>;
83 > readonly onDidInitiateResourcesTrustRequest: Event<ResourceTrustRequestOptions>;
84 >
85 > completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse?: boolean): Promise<void>;
86 > requestOpenFilesTrust(openFiles: URI[]): Promise<WorkspaceTrustUriResponse>;
87 >
88 > completeResourcesTrustRequest(uri: URI, result: WorkspaceTrustUriResponse): Promise<void>;
89 > requestResourcesTrust(options: ResourceTrustRequestOptions): Promise<boolean | undefined>;
90 >
91 > cancelWorkspaceTrustRequest(): void;
92 > completeWorkspaceTrustRequest(trusted?: boolean): Promise<void>;
93 > requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<boolean | undefined>;
94 > requestWorkspaceTrustOnStartup(): void;
95 > }
96 >
97 > export interface IWorkspaceTrustTransitionParticipant {
98 > participate(trusted: boolean): Promise<void>;
99 > }
100 >
101 > export interface IWorkspaceTrustUriInfo {
102 > uri: URI;
103 > trusted: boolean;
104 > }
105 >
106 > export interface IWorkspaceTrustInfo {
107 > uriTrustInfo: IWorkspaceTrustUriInfo[];
108 > }
src/vs/base/common/observableInternal/utils/promise.ts 104 covered LOC · 12 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- promise.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 > import { DisposableStore } from '../../lifecycle.js';
6 > import { IObservable, ISettableObservable } from '../base.js';
7 > import { autorun } from '../reactions/autorun.js';
8 > import { transaction } from '../transaction.js';
9 > import { derived } from '../observables/derived.js';
10 > import { observableValue } from '../observables/observableValue.js';
11 >
12 > export class ObservableLazy<T> {
13 > private readonly _value = observableValue<T | undefined>(this, undefined);
14 >
15 > /**
16 > * The cached value.
17 > * Does not force a computation of the value.
18 > */
19 > public get cachedValue(): IObservable<T | undefined> { return this._value; }
20 >
21 > constructor(private readonly _computeValue: () => T) {
22 }
23 > promise.ts
24 > /**
25 > * Returns the cached value.
26 > * Computes the value if the value has not been cached yet.
27 > */
28 > public getValue(): T {
29 let v = this._value.get();
30 if (!v) {
34 return v;
35 }
36 > } promise.ts
37 >
38 > /**
39 > * A promise whose state is observable.
40 > */
41 > export class ObservablePromise<T> {
42 > public static fromFn<T>(fn: () => Promise<T>): ObservablePromise<T> {
43 > return new ObservablePromise(fn());
44 > }
45 >
46 > public static resolved<T>(value: T): ObservablePromise<T> {
47 return new ObservablePromise(Promise.resolve(value));
48 }
49 > promise.ts
50 > private readonly _value = observableValue<PromiseResult<T> | undefined>(this, undefined);
51 >
52 > /**
53 > * The promise that this object wraps.
54 > */
55 > public readonly promise: Promise<T>;
56 >
57 > /**
58 > * The current state of the promise.
59 > * Is `undefined` if the promise didn't resolve yet.
60 > */
61 > public readonly promiseResult: IObservable<PromiseResult<T> | undefined> = this._value;
62 >
63 > constructor(promise: Promise<T>) {
64 this.promise = promise.then(value => {
65 transaction(tx => {
76 });
77 }
78 > promise.ts
79 > public readonly resolvedValue = derived(this, reader => {
80 > const result = this.promiseResult.read(reader); promise.ts
81 > if (!result) {
82 > return undefined;
83 > }
84 > return result.getDataOrThrow();
85 > }); promise.ts
86 > }
87 >
88 > export class PromiseResult<T> {
89 > constructor(
90 /**
91 * The value of the resolved promise.
101 ) {
102 }
103 > promise.ts
104 > /**
105 > * Returns the value if the promise resolved, otherwise throws the error.
106 > */
107 > public getDataOrThrow(): T {
108 if (this.error) {
109 throw this.error;
111 return this.data!;
112 }
113 > } promise.ts
114 >
115 > /**
116 > * Tracks a changing {@link ObservablePromise}, exposing the last resolved value
117 > * and whether a newer promise is still pending.
118 > */
119 > export class ObservableResolvedPromise<T> {
120 > private readonly _lastResolved: ISettableObservable<T>;
121 > public readonly lastResolved: IObservable<T>;
122 >
123 > private readonly _isResolving = observableValue<boolean>(this, false);
124 > public readonly isResolving: IObservable<boolean> = this._isResolving;
125 >
126 > private _runningPromise: ObservablePromise<T> | undefined;
127 >
128 > constructor(
129 source: IObservable<ObservablePromise<T>>,
130 initialValue: T,
149 }));
150 }
151 > } promise.ts
152 >
153 > /**
154 > * A lazy promise whose state is observable.
155 > */
156 > export class ObservableLazyPromise<T> {
157 > private readonly _lazyValue = new ObservableLazy(() => new ObservablePromise(this._computePromise()));
158 >
159 > /**
160 > * Does not enforce evaluation of the promise compute function.
161 > * Is undefined if the promise has not been computed yet.
162 > */
163 > public readonly cachedPromiseResult = derived(this, reader => this._lazyValue.cachedValue.read(reader)?.promiseResult.read(reader));
164 >
165 > constructor(private readonly _computePromise: () => Promise<T>) {
166 }
167 > promise.ts
168 > public getPromise(): Promise<T> {
169 return this._lazyValue.getValue().promise;
170 }
171 > } promise.ts
src/vs/base/common/cancellation.ts 100 covered LOC · 23 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); cancellation.ts
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) { cancellation.ts
117 > // save an object by returning the default cancellation.ts
118 > // cancelled token when cancellation happens
119 > // before someone asks for the token
120 > this._token = CancellationToken.Cancelled;
121 >
122 > } else if (this._token instanceof MutableToken) { cancellation.ts
123 // actually cancel
124 this._token.cancel();
125 }
126 > } cancellation.ts
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/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/observableInternal/reactions/autorun.ts 98 covered LOC · 11 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- autorun.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 { IReaderWithStore, IReader, IObservable, ISettableObservable } from '../base.js';
7 > import { IChangeTracker } from '../changeTracker.js';
8 > import { DisposableStore, IDisposable, toDisposable } from '../commonFacade/deps.js';
9 > import { DebugNameData, IDebugNameData } from '../debugName.js';
10 > import { AutorunObserver } from './autorunImpl.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 > import { observableValue } from '../observables/observableValue.js';
13 > import { transaction } from '../transaction.js';
14 >
15 > /**
16 > * Runs immediately and whenever a transaction ends and an observed observable changed.
17 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
18 > */
19 > export function autorun(fn: (reader: IReaderWithStore) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
20 > return new AutorunObserver( autorun.ts
21 > new DebugNameData(undefined, undefined, fn),
22 > fn,
23 > undefined,
24 > debugLocation
25 > );
26 > }
27 > autorun.ts
28 > /**
29 > * Runs immediately and whenever a transaction ends and an observed observable changed.
30 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
31 > */
32 > export function autorunOpts(options: IDebugNameData & {}, fn: (reader: IReaderWithStore) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
33 return new AutorunObserver(
34 new DebugNameData(options.owner, options.debugName, options.debugReferenceFn ?? fn),
38 );
39 }
40 > autorun.ts
41 > /**
42 > * Runs immediately and whenever a transaction ends and an observed observable changed.
43 > * {@link fn} should start with a JS Doc using `@description` to name the autorun.
44 > *
45 > * Use `changeTracker.createChangeSummary` to create a "change summary" that can collect the changes.
46 > * Use `changeTracker.handleChange` to add a reported change to the change summary.
47 > * The run function is given the last change summary.
48 > * The change summary is discarded after the run function was called.
49 > *
50 > * @see autorun
51 > */
52 > export function autorunHandleChanges<TChangeSummary>(
53 options: IDebugNameData & {
54 changeTracker: IChangeTracker<TChangeSummary>;
64 );
65 }
66 > autorun.ts
67 > /**
68 > * @see autorunHandleChanges (but with a disposable store that is cleared before the next run or on dispose)
69 > */
70 > export function autorunWithStoreHandleChanges<TChangeSummary>(
71 options: IDebugNameData & {
72 changeTracker: IChangeTracker<TChangeSummary>;
92 });
93 }
94 > autorun.ts
95 > /**
96 > * @see autorun (but with a disposable store that is cleared before the next run or on dispose)
97 > *
98 > * @deprecated Use `autorun(reader => { reader.store.add(...) })` instead!
99 > */
100 > export function autorunWithStore(fn: (reader: IReader, store: DisposableStore) => void): IDisposable {
101 const store = new DisposableStore();
102 const disposable = autorunOpts(
116 });
117 }
118 > autorun.ts
119 > export function autorunDelta<T>(
120 observable: IObservable<T>,
121 handler: (args: { lastValue: T | undefined; newValue: T }) => void
129 });
130 }
131 > autorun.ts
132 > export function autorunIterableDelta<T>(
133 getValue: (reader: IReader) => Iterable<T>,
134 handler: (args: { addedValues: T[]; removedValues: T[] }) => void,
157 });
158 }
159 > autorun.ts
160 > /**
161 > * For each key-stable item in {@link items}, runs {@link setup} once when the
162 > * key is first observed and disposes the per-key {@link DisposableStore} when
163 > * the key is no longer present in the array (or when the returned disposable
164 > * is disposed).
165 > *
166 > * The {@link IObservable} handed to {@link setup} fires whenever the array
167 > * still contains an item with the same key but the item value itself has
168 > * changed (e.g. because the upstream state is immutable and produced a new
169 > * object with the same id). All per-key value updates triggered by a single
170 > * change to {@link items} are batched into one transaction, so dependent
171 > * autoruns observe a consistent snapshot.
172 > *
173 > * Per-key state should be stored in closures or in disposables registered
174 > * against the per-key {@link DisposableStore}. {@link setup} should not call
175 > * `.read()` on the outer {@link items} observable from its body (use the
176 > * provided per-key value observable, or create inner autoruns).
177 > */
178 > export function autorunPerKeyedItem<TIn, TKey>(
179 items: IObservable<readonly TIn[]>,
180 keyFn: (input: TIn) => TKey,
227 });
228 }
229 > autorun.ts
230 > export interface IReaderWithDispose extends IReaderWithStore, IDisposable { }
231 >
232 > /**
233 > * An autorun with a `dispose()` method on its `reader` which cancels the autorun.
234 > * It it safe to call `dispose()` synchronously.
235 > * @deprecated Use autorunSelfDisposable2
236 > */
237 > export function autorunSelfDisposable(fn: (reader: IReaderWithDispose) => void, debugLocation = DebugLocation.ofCaller()): IDisposable {
238 let ar: IDisposable | undefined;
239 let disposed = false;
258 return ar;
259 }
260 > autorun.ts
261 >
262 > /**
263 > * An autorun with a `dispose()` method on its `reader` which cancels the autorun.
264 > * It it safe to call `dispose()` synchronously.
265 > * TODO@hediet/copilot: rename to delete autorunSelfDisposable, and rename autorunSelfDisposable2 to autorunSelfDisposable.
266 > */
267 > export function registerAutorunSelfDisposable(store: DisposableStore, fn: (reader: IReaderWithDispose) => void, debugLocation = DebugLocation.ofCaller()): void {
268 let ar: IDisposable | undefined;
269 let disposeSync = false;
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/test/common/virtualScheduling/virtualClock.ts 94 covered LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- virtualClock.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, tieBreakComparators } from '../../../common/arrays.js';
7 > import { Emitter } from '../../../common/event.js';
8 > import { IDisposable } from '../../../common/lifecycle.js';
9 > import { Trace } from './trace.js';
10 >
11 > export type VirtualTime = number;
12 >
13 > /** Debug source description for an event. */
14 > export interface EventSource {
15 > toString(): string;
16 > readonly stackTrace?: string;
17 > }
18 >
19 > /**
20 > * A unit of work scheduled at a point in virtual time.
21 > *
22 > * Timer callbacks are events. External completions (e.g. fake fs reads) can
23 > * also be modelled as events whose virtual completion time is chosen by a
24 > * scheduling policy — to the {@link VirtualClock} they are indistinguishable.
25 > */
26 > export interface VirtualEvent {
27 > readonly time: VirtualTime;
28 > readonly source: EventSource;
29 > readonly trace?: Trace;
30 > /**
31 > * Hint for the {@link Embedding}: this event prefers to run on a real
32 > * animation frame (e.g. so DOM measurements after it observe a real
33 > * reflow). Pure-time tests can ignore the hint.
34 > */
35 > readonly preferRealAnimationFrame?: boolean;
36 > run(): void;
37 > }
38 >
39 > interface QueuedEvent extends VirtualEvent { readonly id: number }
40 >
41 > const eventComparator = tieBreakComparators<QueuedEvent>(
42 > compareBy(e => e.time, numberComparator),
43 > compareBy(e => e.id, numberComparator),
44 > );
45 >
46 > /**
47 > * A pure data structure: a virtual clock + a priority queue of events.
48 > *
49 > * The clock has no concept of "real time". It is advanced exclusively by
50 > * {@link runNext}, which sets `now` to the next event's `time` before running
51 > * it. The {@link VirtualTimeProcessor} is the only intended driver, but the
52 > * clock is useful in isolation (e.g. for unit-testing a scheduler or for
53 > * stepping a scenario manually).
54 > */
55 > export class VirtualClock {
56 > private _now: VirtualTime;
57 > private _idCounter = 0;
58 > private readonly _queue = new SimplePriorityQueue<QueuedEvent>(eventComparator);
59 > private readonly _onEventScheduled = new Emitter<VirtualEvent>();
60 >
61 > public readonly onEventScheduled = this._onEventScheduled.event;
62 >
63 > constructor(startTime: VirtualTime = 0) {
64 this._now = startTime;
65 }
67 > get now(): VirtualTime { return this._now; }
68 > get hasEvents(): boolean { return this._queue.length > 0; }
69 >
70 > schedule(event: VirtualEvent): IDisposable {
71 if (event.time < this._now) {
72 throw new Error(`Scheduled time (${event.time}) must be >= now (${this._now}).`);
77 return { dispose: () => this._queue.remove(queued) };
78 }
80 > peekNext(): VirtualEvent | undefined { return this._queue.getMin(); }
81 >
82 > runNext(): VirtualEvent | undefined {
83 const e = this._queue.removeMin();
84 if (e) {
88 return e;
89 }
91 > getEvents(): readonly VirtualEvent[] { return this._queue.toSortedArray(); }
92 > }
93 >
94 > class SimplePriorityQueue<T> {
95 > private _items: T[] = [];
96 > private _sorted = true;
97 >
98 > constructor(private readonly _compare: (a: T, b: T) => number) { }
99 >
100 > get length(): number { return this._items.length; }
101 >
102 > add(value: T): void {
103 this._items.push(value);
104 this._sorted = false;
105 }
107 > remove(value: T): void {
108 const i = this._items.indexOf(value);
109 if (i !== -1) { this._items.splice(i, 1); }
110 }
112 > getMin(): T | undefined { this._ensureSorted(); return this._items[0]; }
113 > removeMin(): T | undefined { this._ensureSorted(); return this._items.shift(); }
114 > toSortedArray(): T[] { this._ensureSorted(); return [...this._items]; }
115 >
116 > private _ensureSorted(): void {
117 if (this._sorted) { return; }
118 this._items.sort(this._compare);
119 this._sorted = true;
120 }
121 > } virtualClock.ts
src/vs/base/common/extpath.ts 91 covered LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extpath.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 { isAbsolute, join, normalize, posix, sep } from './path.js';
8 > import { isWindows } from './platform.js';
9 > import { equalsIgnoreCase, rtrim, startsWithIgnoreCase } from './strings.js';
10 > import { isNumber } from './types.js';
11 >
12 > export function isPathSeparator(code: number) {
13 return code === CharCode.Slash || code === CharCode.Backslash;
14 }
15 > extpath.ts
16 > /**
17 > * Takes a Windows OS path and changes backward slashes to forward slashes.
18 > * This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
19 > * Using it on a Linux or MaxOS path might change it.
20 > */
21 > export function toSlashes(osPath: string) {
22 return osPath.replace(/[\\/]/g, posix.sep);
23 }
24 > extpath.ts
25 > /**
26 > * Takes a Windows OS path (using backward or forward slashes) and turns it into a posix path:
27 > * - turns backward slashes into forward slashes
28 > * - makes it absolute if it starts with a drive letter
29 > * This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
30 > * Using it on a Linux or MaxOS path might change it.
31 > */
32 > export function toPosixPath(osPath: string) {
33 if (osPath.indexOf('/') === -1) {
34 osPath = toSlashes(osPath);
39 return osPath;
40 }
41 > extpath.ts
42 > /**
43 > * Computes the _root_ this path, like `getRoot('c:\files') === c:\`,
44 > * `getRoot('files:///files/path') === files:///`,
45 > * or `getRoot('\\server\shares\path') === \\server\shares\`
46 > */
47 > export function getRoot(path: string, sep: string = posix.sep): string {
48 if (!path) {
49 return '';
111 return '';
112 }
113 > extpath.ts
114 > /**
115 > * Check if the path follows this pattern: `\\hostname\sharename`.
116 > *
117 > * @see https://msdn.microsoft.com/en-us/library/gg465305.aspx
118 > * @return A boolean indication if the path is a UNC path, on none-windows
119 > * always false.
120 > */
121 > export function isUNC(path: string): boolean {
122 if (!isWindows) {
123 // UNC is a windows concept
162 return true;
163 }
164 > extpath.ts
165 > // Reference: https://en.wikipedia.org/wiki/Filename
166 > const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g;
167 > const UNIX_INVALID_FILE_CHARS = /[/]/g;
168 > const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])(\.(.*?))?$/i;
169 > export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean {
170 const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
171
201 return true;
202 }
203 > extpath.ts
204 > /**
205 > * @deprecated please use `IUriIdentityService.extUri.isEqual` instead. If you are
206 > * in a context without services, consider to pass down the `extUri` from the outside
207 > * or use `extUriBiasedIgnorePathCase` if you know what you are doing.
208 > */
209 > export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boolean {
210 const identityEquals = (pathA === pathB);
211 if (!ignoreCase || identityEquals) {
219 return equalsIgnoreCase(pathA, pathB);
220 }
221 > extpath.ts
222 > /**
223 > * @deprecated please use `IUriIdentityService.extUri.isEqualOrParent` instead. If
224 > * you are in a context without services, consider to pass down the `extUri` from the
225 > * outside, or use `extUriBiasedIgnorePathCase` if you know what you are doing.
226 > */
227 > export function isEqualOrParent(base: string, parentCandidate: string, ignoreCase?: boolean, forcePosixSemantics = false): boolean {
228 const separator = forcePosixSemantics ? posix.sep : sep;
229
272 return base.indexOf(parentCandidate) === 0;
273 }
274 > extpath.ts
275 > export function isWindowsDriveLetter(char0: number): boolean {
276 return char0 >= CharCode.A && char0 <= CharCode.Z || char0 >= CharCode.a && char0 <= CharCode.z;
277 }
278 > extpath.ts
279 > export function sanitizeFilePath(candidate: string, cwd: string): string {
280
281 // Special case: allow to open a drive letter without trailing backslash
295 return removeTrailingPathSeparator(candidate);
296 }
297 > extpath.ts
298 > export function removeTrailingPathSeparator(candidate: string): string {
299 if (isWindows) {
300 candidate = rtrim(candidate, sep);
316 return candidate;
317 }
318 > extpath.ts
319 > export function isRootOrDriveLetter(path: string): boolean {
320 const pathNormalized = normalize(path);
321
331 return pathNormalized === posix.sep;
332 }
333 > extpath.ts
334 > export function hasDriveLetter(path: string, isWindowsOS: boolean = isWindows): boolean {
335 if (isWindowsOS) {
336 return isWindowsDriveLetter(path.charCodeAt(0)) && path.charCodeAt(1) === CharCode.Colon;
339 return false;
340 }
341 > extpath.ts
342 > export function getDriveLetter(path: string, isWindowsOS: boolean = isWindows): string | undefined {
343 return hasDriveLetter(path, isWindowsOS) ? path[0] : undefined;
344 }
345 > extpath.ts
346 > export function indexOfPath(path: string, candidate: string, ignoreCase?: boolean): number {
347 if (candidate.length > path.length) {
348 return -1;
360 return path.indexOf(candidate);
361 }
362 > extpath.ts
363 > export interface IPathWithLineAndColumn {
364 > path: string;
365 > line?: number;
366 > column?: number;
367 > }
368 >
369 > export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn {
370 const segments = rawPath.split(':'); // C:\file.txt:<line>:<column>
371
395 };
396 }
397 > extpath.ts
398 > const pathChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
399 > const windowsSafePathFirstChars = 'BDEFGHIJKMOQRSTUVWXYZbdefghijkmoqrstuvwxyz0123456789';
400 >
401 > export function randomPath(parent?: string, prefix?: string, randomLength = 8): string {
402 let suffix = '';
403 for (let i = 0; i < randomLength; i++) {
src/vs/base/common/observableInternal/utils/utils.ts 89 covered LOC · 21 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 { autorun } from '../reactions/autorun.js';
7 > import { IObservable, IObservableWithChange, IObserver, IReader, ITransaction } from '../base.js';
8 > import { observableValue } from '../observables/observableValue.js';
9 > import { DebugOwner } from '../debugName.js';
10 > import { DisposableStore, Event, IDisposable, toDisposable } from '../commonFacade/deps.js';
11 > import { derived, derivedOpts } from '../observables/derived.js';
12 > import { observableFromEvent } from '../observables/observableFromEvent.js';
13 > import { observableSignal } from '../observables/observableSignal.js';
14 > import { _setKeepObserved, _setRecomputeInitiallyAndOnChange } from '../observables/baseObservable.js';
15 > import { DebugLocation } from '../debugLocation.js';
16 >
17 > export function observableFromPromise<T>(promise: Promise<T>): IObservable<{ value?: T }> {
18 const observable = observableValue<{ value?: T }>('promiseValue', {});
19 promise.then((value) => {
22 return observable;
23 }
24 > utils.ts
25 > export function signalFromObservable<T>(owner: DebugOwner | undefined, observable: IObservable<T>): IObservable<void> {
26 return derivedOpts({
27 owner,
31 });
32 }
33 > utils.ts
34 > /**
35 > * Creates an observable that debounces the input observable.
36 > */
37 > export function debouncedObservable<T>(observable: IObservable<T>, debounceMs: number | ((lastValue: T | undefined, newValue: T) => number), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
38 let hasValue = false;
39 let lastValue: T | undefined;
79 }, debugLocation);
80 }
81 > utils.ts
82 > /**
83 > * Creates an observable that throttles the input observable.
84 > * Unlike {@link debouncedObservable}, the timer starts on the first change
85 > * and is not reset by subsequent changes, preventing starvation.
86 > */
87 > export function throttledObservable<T>(observable: IObservable<T>, throttleMs: number, debugLocation = DebugLocation.ofCaller()): IObservable<T> {
88 let hasValue = false;
89 let lastValue: T | undefined;
126 }, debugLocation);
127 }
128 > utils.ts
129 > /**
130 > * Creates an observable that debounces the input observable.
131 > */
132 > export function debouncedObservable2<T>(observable: IObservable<T>, debounceMs: number | ((currentValue: T | undefined, newValue: T) => number), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
133 const s = observableSignal('handleTimeout');
134
167 return d;
168 }
169 > utils.ts
170 > export function wasEventTriggeredRecently(event: Event<any>, timeoutMs: number, disposableStore: DisposableStore): IObservable<boolean> {
171 const observable = observableValue('triggeredRecently', false);
172
186 return observable;
187 }
188 > utils.ts
189 > /**
190 > * This makes sure the observable is being observed and keeps its cache alive.
191 > */
192 > export function keepObserved<T>(observable: IObservable<T>): IDisposable {
193 const o = new KeepAliveObserver(false, undefined);
194 observable.addObserver(o);
197 });
198 }
199 > utils.ts
200 > _setKeepObserved(keepObserved);
201 >
202 > /**
203 > * This converts the given observable into an autorun.
204 > */
205 > export function recomputeInitiallyAndOnChange<T>(observable: IObservable<T>, handleValue?: (value: T) => void): IDisposable {
206 const o = new KeepAliveObserver(true, handleValue);
207 observable.addObserver(o);
216 });
217 }
218 > utils.ts
219 > _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange);
220 >
221 > export class KeepAliveObserver implements IObserver {
222 > private _counter = 0;
223 >
224 > constructor(
225 private readonly _forceRecompute: boolean,
226 private readonly _handleValue: ((value: any) => void) | undefined,
227 ) { }
228 > utils.ts
229 > beginUpdate<T>(observable: IObservable<T>): void {
230 this._counter++;
231 }
232 > utils.ts
233 > endUpdate<T>(observable: IObservable<T>): void {
234 if (this._counter === 1 && this._forceRecompute) {
235 if (this._handleValue) {
241 this._counter--;
242 }
243 > utils.ts
244 > handlePossibleChange<T>(observable: IObservable<T>): void {
245 // NO OP
246 }
247 > utils.ts
248 > handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void {
249 // NO OP
250 }
251 > } utils.ts
252 >
253 > export function derivedObservableWithCache<T>(owner: DebugOwner, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T> {
254 let lastValue: T | undefined = undefined;
255 const observable = derivedOpts({ owner, debugReferenceFn: computeFn }, reader => {
259 return observable;
260 }
261 > utils.ts
262 > export function derivedObservableWithWritableCache<T>(owner: object, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable<T>
263 & { clearCache(transaction: ITransaction): void; setCache(newValue: T | undefined, tx: ITransaction | undefined): void } {
264 let lastValue: T | undefined = undefined;
280 });
281 }
282 > utils.ts
283 > /**
284 > * When the items array changes, referential equal items are not mapped again.
285 > */
286 > export function mapObservableArrayCached<TIn, TOut, TKey = TIn>(owner: DebugOwner, items: IObservable<readonly TIn[]>, map: (input: TIn, store: DisposableStore) => TOut, keySelector?: (input: TIn) => TKey): IObservable<readonly TOut[]> {
287 let m = new ArrayMap(map, keySelector);
288 const self = derivedOpts({
300 return self;
301 }
302 > utils.ts
303 > class ArrayMap<TIn, TOut, TKey> implements IDisposable {
304 > private readonly _cache = new Map<TKey, { out: TOut; store: DisposableStore }>();
305 > private _items: TOut[] = [];
306 > constructor(
307 private readonly _map: (input: TIn, store: DisposableStore) => TOut,
308 private readonly _keySelector?: (input: TIn) => TKey,
309 ) {
310 }
311 > utils.ts
312 > public dispose(): void {
313 this._cache.forEach(entry => entry.store.dispose());
314 this._cache.clear();
315 }
316 > utils.ts
317 > public setItems(items: readonly TIn[]): void {
318 const newItems: TOut[] = [];
319 const itemsToRemove = new Set(this._cache.keys());
342 this._items = newItems;
343 }
344 > utils.ts
345 > public getItems(): TOut[] {
346 return this._items;
347 }
348 > } utils.ts
349 >
350 > export function isObservable<T>(obj: unknown): obj is IObservable<T> {
351 return !!obj && (<IObservable<T>>obj).read !== undefined && (<IObservable<T>>obj).reportChanges !== undefined;
352 }
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/platform/defaultAccount/common/defaultAccount.ts 88 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- defaultAccount.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 { ICopilotTokenInfo, IDefaultAccount, IDefaultAccountAuthenticationProvider, IPolicyData } from '../../../base/common/defaultAccount.js';
7 > import { Event } from '../../../base/common/event.js';
8 > import { createDecorator } from '../../instantiation/common/instantiation.js';
9 >
10 > /**
11 > * Well-known GitHub URL paths used with {@link IDefaultAccountService.resolveGitHubUrl}.
12 > */
13 > export const GitHubPaths = {
14 > copilotSettings: 'settings/copilot/features',
15 > billingBudgets: 'settings/copilot/features?utm_source=vscode',
16 > copilotUpgrade: 'github-copilot/upgrade?utm_source=vscode',
17 > } as const;
18 >
19 > /**
20 > * Outcome of the last `/copilot_internal/managed_settings` fetch.
21 > * - A numeric HTTP status code indicates the server responded with that code.
22 > * - `'ok'`: response parsed and adapted successfully (including an empty `{}` body).
23 > * - `'no-url'`: no `managedSettingsUrl` configured in product.json.
24 > * - `'no-response'`: network error, all sessions rejected, or active rate-limit backoff.
25 > * - `'parse-error'`: response received but JSON parsing failed.
26 > * - `null`: never fetched.
27 > */
28 > export type ManagedSettingsFetchStatus = number | 'ok' | 'no-url' | 'no-response' | 'parse-error' | null;
29 >
30 > export interface IDefaultAccountProvider {
31 > readonly defaultAccount: IDefaultAccount | null;
32 > readonly onDidChangeDefaultAccount: Event<IDefaultAccount | null>;
33 > readonly policyData: IPolicyData | null;
34 > readonly onDidChangePolicyData: Event<IPolicyData | null>;
35 > readonly copilotTokenInfo: ICopilotTokenInfo | null;
36 > readonly onDidChangeCopilotTokenInfo: Event<ICopilotTokenInfo | null>;
37 > readonly managedSettingsFetchStatus: ManagedSettingsFetchStatus;
38 > /** Timestamp (ms) of the last managed-settings fetch, or `null` if never fetched. */
39 > readonly managedSettingsFetchedAt: number | null;
40 > /** The raw JSON response from the managed-settings endpoint, for diagnostics. */
41 > readonly managedSettingsRawResponse: unknown;
42 > getDefaultAccountAuthenticationProvider(): IDefaultAccountAuthenticationProvider;
43 >
44 > /**
45 > * Resolves a GitHub URL path to a full URL, using the GitHub Enterprise
46 > * base URL when the user is authenticated via a GHE provider, or
47 > * `https://github.com` otherwise.
48 > *
49 > * @param path The path portion of the URL (e.g. `settings/copilot/features`).
50 > */
51 > resolveGitHubUrl(path: string): string;
52 >
53 > refresh(options?: { forceRefresh?: boolean }): Promise<IDefaultAccount | null>;
54 > signIn(options?: { additionalScopes?: readonly string[];[key: string]: unknown }): Promise<IDefaultAccount | null>;
55 > signOut(): Promise<void>;
56 > }
57 >
58 > export const IDefaultAccountService = createDecorator<IDefaultAccountService>('defaultAccountService');
59 >
60 > export interface IDefaultAccountService {
61 > readonly _serviceBrand: undefined;
62 > readonly onDidChangeDefaultAccount: Event<IDefaultAccount | null>;
63 > readonly onDidChangePolicyData: Event<IPolicyData | null>;
64 > readonly policyData: IPolicyData | null;
65 > readonly currentDefaultAccount: IDefaultAccount | null;
66 > readonly copilotTokenInfo: ICopilotTokenInfo | null;
67 > readonly onDidChangeCopilotTokenInfo: Event<ICopilotTokenInfo | null>;
68 > readonly managedSettingsFetchStatus: ManagedSettingsFetchStatus;
69 > /** Timestamp (ms) of the last managed-settings fetch, or `null` if never fetched. */
70 > readonly managedSettingsFetchedAt: number | null;
71 > /** The raw JSON response from the managed-settings endpoint, for diagnostics. */
72 > readonly managedSettingsRawResponse: unknown;
73 > getDefaultAccount(): Promise<IDefaultAccount | null>;
74 > getDefaultAccountAuthenticationProvider(): IDefaultAccountAuthenticationProvider;
75 > setDefaultAccountProvider(provider: IDefaultAccountProvider): void;
76 > refresh(options?: { forceRefresh?: boolean }): Promise<IDefaultAccount | null>;
77 > signIn(options?: { additionalScopes?: readonly string[];[key: string]: unknown }): Promise<IDefaultAccount | null>;
78 > signOut(): Promise<void>;
79 >
80 > /**
81 > * Resolves a GitHub URL path to a full URL, using the GitHub Enterprise
82 > * base URL when the user is authenticated via a GHE provider, or
83 > * `https://github.com` otherwise.
84 > *
85 > * @param path The path portion of the URL (e.g. `settings/copilot/features`).
86 > */
87 > resolveGitHubUrl(path: string): string;
88 > }
src/vs/workbench/contrib/debug/common/debugUtils.ts 86 covered LOC · 21 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugUtils.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 { equalsIgnoreCase } from '../../../../base/common/strings.js';
7 > import { IDebuggerContribution, IDebugSession, IConfig, IConfigPresentation, State } from './debug.js';
8 > import { URI as uri } from '../../../../base/common/uri.js';
9 > import { isAbsolute } from '../../../../base/common/path.js';
10 > import { deepClone } from '../../../../base/common/objects.js';
11 > import { Schemas } from '../../../../base/common/network.js';
12 > import { IEditorService } from '../../../services/editor/common/editorService.js';
13 > import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
14 > import { ITextModel } from '../../../../editor/common/model.js';
15 > import { Position } from '../../../../editor/common/core/position.js';
16 > import { IRange, Range } from '../../../../editor/common/core/range.js';
17 > import { CancellationToken } from '../../../../base/common/cancellation.js';
18 > import { coalesce } from '../../../../base/common/arrays.js';
19 > import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
20 > import { OperatingSystem, OS } from '../../../../base/common/platform.js';
21 >
22 > const _formatPIIRegexp = /{([^}]+)}/g;
23 >
24 > export function formatPII(value: string, excludePII: boolean, args: { [key: string]: string } | undefined): string {
25 return value.replace(_formatPIIRegexp, function (match, group) {
26 if (excludePII && group.length > 0 && group[0] !== '_') {
33 });
34 }
36 > /**
37 > * Filters exceptions (keys marked with "!") from the given object. Used to
38 > * ensure exception data is not sent on web remotes, see #97628.
39 > */
40 > export function filterExceptionsFromTelemetry<T extends { [key: string]: unknown }>(data: T): Partial<T> {
41 const output: Partial<T> = {};
42 for (const key of Object.keys(data) as (keyof T & string)[]) {
48 return output;
49 }
51 >
52 > export function isSessionAttach(session: IDebugSession): boolean {
53 return session.configuration.request === 'attach' && !getExtensionHostDebugSession(session) && (!session.parentSession || isSessionAttach(session.parentSession));
54 }
56 > /**
57 > * Returns the session or any parent which is an extension host debug session.
58 > * Returns undefined if there's none.
59 > */
60 > export function getExtensionHostDebugSession(session: IDebugSession): IDebugSession | void {
61 let type = session.configuration.type;
62 if (!type) {
74 return session.parentSession ? getExtensionHostDebugSession(session.parentSession) : undefined;
75 }
77 > // only a debugger contributions with a label, program, or runtime attribute is considered a "defining" or "main" debugger contribution
78 > export function isDebuggerMainContribution(dbg: IDebuggerContribution) {
79 return dbg.type && (dbg.label || dbg.program || dbg.runtime);
80 }
82 > /**
83 > * Note- uses 1-indexed numbers
84 > */
85 > export function getExactExpressionStartAndEnd(lineContent: string, looseStart: number, looseEnd: number): { start: number; end: number } {
86 let matchingExpression: string | undefined = undefined;
87 let startOffset = 0;
134 { start: 0, end: 0 };
135 }
137 export async function getEvaluatableExpressionAtPosition(languageFeaturesService: ILanguageFeaturesService, model: ITextModel, position: Position, token?: CancellationToken): Promise<{ range: IRange; matchingExpression: string } | null> {
138 if (languageFeaturesService.evaluatableExpressionProvider.has(model)) {
172 return null;
173 }
175 > // RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt
176 > const _schemePattern = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/;
177 >
178 > export function isUriString(s: string | undefined): boolean {
179 // heuristics: a valid uri starts with a scheme and
180 // the scheme has at least 2 characters so that it doesn't look like a drive letter.
181 return !!(s && s.match(_schemePattern));
182 }
184 function stringToUri(source: PathContainer): string | undefined {
185 if (typeof source.path === 'string') {
201 return source.path;
202 }
204 function uriToString(source: PathContainer): string | undefined {
205 if (typeof source.path === 'object') {
215 return source.path;
216 }
218 > // path hooks helpers
219 >
220 > interface PathContainer {
221 > path?: string;
222 > sourceReference?: number;
223 > }
224 >
225 > export function convertToDAPaths(message: DebugProtocol.ProtocolMessage, toUri: boolean): DebugProtocol.ProtocolMessage {
226
227 const fixPath = toUri ? stringToUri : uriToString;
237 return msg;
238 }
240 > export function convertToVSCPaths(message: DebugProtocol.ProtocolMessage, toUri: boolean): DebugProtocol.ProtocolMessage {
241
242 const fixPath = toUri ? stringToUri : uriToString;
252 return msg;
253 }
255 function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA: boolean, source: PathContainer | undefined) => void): void {
256
332 }
333 }
334 > export function getVisibleAndSorted<T extends { presentation?: IConfigPresentation }>(array: T[]): T[] { debugUtils.ts
335 return array.filter(config => !config.presentation?.hidden).sort((first, second) => {
336 if (!first.presentation) {
359 });
360 }
362 function compareOrders(first: number | undefined, second: number | undefined): number {
363 if (typeof first !== 'number') {
374 return first - second;
375 }
377 export async function saveAllBeforeDebugStart(configurationService: IConfigurationService, editorService: IEditorService): Promise<void> {
378 const saveBeforeStartConfig: string = configurationService.getValue('debug.saveBeforeStart', { overrideIdentifier: editorService.activeTextEditorLanguageId });
389 await configurationService.reloadConfiguration();
390 }
392 > export const sourcesEqual = (a: DebugProtocol.Source | undefined, b: DebugProtocol.Source | undefined): boolean =>
393 !a || !b ? a === b : a.name === b.name && a.path === b.path && a.sourceReference === b.sourceReference;
395 > /**
396 > * Resolves the best child session to focus when a parent session is selected.
397 > * Always prefer child sessions over parent wrapper sessions to ensure console responsiveness.
398 > * Fixes issue #152407: Using debug console picker when not paused leaves console unresponsive.
399 > */
400 > export function resolveChildSession(session: IDebugSession, allSessions: readonly IDebugSession[]): IDebugSession {
401 // Always focus child session instead of parent wrapper session #152407
402 const childSessions = allSessions.filter(s => s.parentSession === session);
414 return session;
415 }
417 > type IPlatformSpecificConfig = NonNullable<IConfig['windows']>;
418 >
419 function getPlatformSpecificConfig(config: IConfig, os: OperatingSystem): IPlatformSpecificConfig | undefined {
420 switch (os) {
427 }
428 }
430 > export function getEffectiveConfigForPlatform(config: IConfig, os: OperatingSystem = OS): IConfig {
431 const platformConfig = getPlatformSpecificConfig(config, os);
432 if (!platformConfig) {
440 };
441 }
443 > export function getEffectivePresentationForConfig(config: IConfig, os: OperatingSystem = OS): IConfigPresentation | undefined {
444 return getEffectiveConfigForPlatform(config, os).presentation;
445 }
src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts 85 covered LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- abstractDebugAdapter.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 { IDebugAdapter } from './debug.js';
8 > import { timeout } from '../../../../base/common/async.js';
9 > import { localize } from '../../../../nls.js';
10 >
11 > /**
12 > * Abstract implementation of the low level API for a debug adapter.
13 > * Missing is how this API communicates with the debug adapter.
14 > */
15 > export abstract class AbstractDebugAdapter implements IDebugAdapter {
16 > private sequence: number;
17 > private pendingRequests = new Map<number, (e: DebugProtocol.Response) => void>();
18 > private pendingRequestTimers = new Map<number, Timeout>();
19 > private requestCallback: ((request: DebugProtocol.Request) => void) | undefined;
20 > private eventCallback: ((request: DebugProtocol.Event) => void) | undefined;
21 > private messageCallback: ((message: DebugProtocol.ProtocolMessage) => void) | undefined;
22 > private queue: DebugProtocol.ProtocolMessage[] = [];
23 > protected readonly _onError = new Emitter<Error>();
24 > protected readonly _onExit = new Emitter<number | null>();
25 >
26 > constructor() {
27 this.sequence = 1;
28 }
30 > abstract startSession(): Promise<void>;
31 >
32 > abstract stopSession(): Promise<void>;
33 >
34 > abstract sendMessage(message: DebugProtocol.ProtocolMessage): void;
35 >
36 > get onError(): Event<Error> {
37 return this._onError.event;
38 }
40 > get onExit(): Event<number | null> {
41 return this._onExit.event;
42 }
44 > onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void {
45 if (this.messageCallback) {
46 this._onError.fire(new Error(`attempt to set more than one 'Message' callback`));
48 this.messageCallback = callback;
49 }
51 > onEvent(callback: (event: DebugProtocol.Event) => void): void {
52 if (this.eventCallback) {
53 this._onError.fire(new Error(`attempt to set more than one 'Event' callback`));
55 this.eventCallback = callback;
56 }
58 > onRequest(callback: (request: DebugProtocol.Request) => void): void {
59 if (this.requestCallback) {
60 this._onError.fire(new Error(`attempt to set more than one 'Request' callback`));
62 this.requestCallback = callback;
63 }
65 > sendResponse(response: DebugProtocol.Response): void {
66 if (response.seq > 0) {
67 this._onError.fire(new Error(`attempt to send more than one response for command ${response.command}`));
70 }
71 }
73 > sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): number {
74 const request: any = {
75 command: command
105 return request.seq;
106 }
108 > acceptMessage(message: DebugProtocol.ProtocolMessage): void {
109 if (this.messageCallback) {
110 this.messageCallback(message);
117 }
118 }
120 > /**
121 > * Returns whether we should insert a timeout between processing messageA
122 > * and messageB. Artificially queueing protocol messages guarantees that any
123 > * microtasks for previous message finish before next message is processed.
124 > * This is essential ordering when using promises anywhere along the call path.
125 > *
126 > * For example, take the following, where `chooseAndSendGreeting` returns
127 > * a person name and then emits a greeting event:
128 > *
129 > * ```
130 > * let person: string;
131 > * adapter.onGreeting(() => console.log('hello', person));
132 > * person = await adapter.chooseAndSendGreeting();
133 > * ```
134 > *
135 > * Because the event is dispatched synchronously, it may fire before person
136 > * is assigned if they're processed in the same task. Inserting a task
137 > * boundary avoids this issue.
138 > */
139 > protected needsTaskBoundaryBetween(messageA: DebugProtocol.ProtocolMessage, messageB: DebugProtocol.ProtocolMessage) {
140 return messageA.type !== 'event' || messageB.type !== 'event';
141 }
143 > /**
144 > * Reads and dispatches items from the queue until it is empty.
145 > */
146 > private async processQueue() {
147 let message: DebugProtocol.ProtocolMessage | undefined;
148 while (this.queue.length) {
176 }
177 }
179 > private internalSend(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void {
180 message.type = typ;
181 message.seq = this.sequence++;
182 this.sendMessage(message);
183 }
185 > protected async cancelPendingRequests(): Promise<void> {
186 if (this.pendingRequests.size === 0) {
187 return Promise.resolve();
205 });
206 }
208 > private clearPendingRequestTimer(requestSeq: number): void {
209 clearTimeout(this.pendingRequestTimers.get(requestSeq));
210 this.pendingRequestTimers.delete(requestSeq);
211 }
213 > getPendingRequestIds(): number[] {
214 return Array.from(this.pendingRequests.keys());
215 }
217 > dispose(): void {
218 for (const timer of this.pendingRequestTimers.values()) {
219 clearTimeout(timer);
src/vs/base/common/performance.ts 84 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- performance.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 type { INodeProcess } from './platform.js';
7 >
8 > function _definePolyfillMarks(timeOrigin?: number) {
9 > const _data: [string?, number?] = [];
10 > if (typeof timeOrigin === 'number') {
11 > _data.push('code/timeOrigin', timeOrigin);
12 > }
13 >
14 > function mark(name: string, markOptions?: { startTime?: number }) {
15 _data.push(name, markOptions?.startTime ?? Date.now());
16 }
17 > function getMarks() { performance.ts
18 const result = [];
19 for (let i = 0; i < _data.length; i += 2) {
25 return result;
26 }
27 > function clearMarks(name?: string) { performance.ts
28 if (typeof name === 'undefined') {
29 const hasTimeOrigin = _data.length >= 2 && _data[0] === 'code/timeOrigin';
41 }
42 }
43 > return { mark, getMarks, clearMarks }; performance.ts
44 > }
45 >
46 > declare const process: INodeProcess;
47 >
48 > interface IPerformanceEntry {
49 > readonly name: string;
50 > readonly startTime: number;
51 > }
52 >
53 > interface IPerformanceTiming {
54 > readonly navigationStart?: number;
55 > readonly redirectStart?: number;
56 > readonly fetchStart?: number;
57 > }
58 >
59 > interface IPerformance {
60 > mark(name: string, markOptions?: { startTime?: number }): void;
61 > clearMarks(name?: string): void;
62 > getEntriesByType(type: string): IPerformanceEntry[];
63 > readonly timeOrigin: number;
64 > readonly timing: IPerformanceTiming;
65 > readonly nodeTiming?: any;
66 > }
67 >
68 > declare const performance: IPerformance;
69 >
70 > function _define() {
71 >
72 > // Identify browser environment when following property is not present
73 > // https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#performancenodetiming
74 > // @ts-ignore
75 > if (typeof performance === 'object' && typeof performance.mark === 'function' && !performance.nodeTiming) {
76 // in a browser context, reuse performance-util
77
109 }
110
111 > } else if (typeof process === 'object') { performance.ts
112 > // node.js: use the normal polyfill but add the timeOrigin
113 > // from the node perf_hooks API as very first mark
114 > const timeOrigin = performance?.timeOrigin;
115 > return _definePolyfillMarks(timeOrigin);
116 >
117 > } else {
118 // unknown environment
119 console.trace('perf-util loaded in UNKNOWN environment');
120 return _definePolyfillMarks();
121 }
122 > } performance.ts
123 >
124 > function _factory(sharedObj: any) {
125 > if (!sharedObj.MonacoPerformanceMarks) {
126 > sharedObj.MonacoPerformanceMarks = _define();
127 > }
128 > return sharedObj.MonacoPerformanceMarks;
129 > }
130 >
131 > const perf = _factory(globalThis);
132 >
133 > export const mark: (name: string, markOptions?: { startTime?: number }) => void = perf.mark;
134 >
135 > /**
136 > * Clears performance marks. If a name is given, only marks with that exact
137 > * name are removed. If no name is given, all marks are removed.
138 > */
139 > export const clearMarks: (name?: string) => void = perf.clearMarks;
140 >
141 > export interface PerformanceMark {
142 > readonly name: string;
143 > readonly startTime: number;
144 > }
145 >
146 > /**
147 > * Returns all marks, sorted by `startTime`.
148 > */
149 > export const getMarks: () => PerformanceMark[] = perf.getMarks;
src/vs/platform/extensionManagement/common/extensionManagementUtil.ts 82 covered LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensionManagementUtil.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 { compareIgnoreCase } from '../../../base/common/strings.js';
7 > import { IExtensionIdentifier, IGalleryExtension, ILocalExtension, MaliciousExtensionInfo, getTargetPlatform } from './extensionManagement.js';
8 > import { ExtensionIdentifier, IExtension, TargetPlatform, UNDEFINED_PUBLISHER } from '../../extensions/common/extensions.js';
9 > import { IFileService } from '../../files/common/files.js';
10 > import { isLinux, platform } from '../../../base/common/platform.js';
11 > import { URI } from '../../../base/common/uri.js';
12 > import { getErrorMessage } from '../../../base/common/errors.js';
13 > import { ILogService } from '../../log/common/log.js';
14 > import { arch } from '../../../base/common/process.js';
15 > import { TelemetryTrustedValue } from '../../telemetry/common/telemetryUtils.js';
16 > import { isString } from '../../../base/common/types.js';
17 >
18 > export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean {
19 if (a.uuid && b.uuid) {
20 return a.uuid === b.uuid;
25 return compareIgnoreCase(a.id, b.id) === 0;
26 }
28 > const ExtensionKeyRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)(-(.+))?$/;
29 >
30 > export class ExtensionKey {
31 >
32 > static create(extension: IExtension | IGalleryExtension): ExtensionKey {
33 > const version = (extension as IExtension).manifest ? (extension as IExtension).manifest.version : (extension as IGalleryExtension).version;
34 > const targetPlatform = (extension as IExtension).manifest ? (extension as IExtension).targetPlatform : (extension as IGalleryExtension).properties.targetPlatform;
35 > return new ExtensionKey(extension.identifier, version, targetPlatform);
36 > }
37 >
38 > static parse(key: string): ExtensionKey | null {
39 const matches = ExtensionKeyRegex.exec(key);
40 return matches && matches[1] && matches[2] ? new ExtensionKey({ id: matches[1] }, matches[2], matches[4] as TargetPlatform || undefined) : null;
41 }
43 > readonly id: string;
44 >
45 > constructor(
46 readonly identifier: IExtensionIdentifier,
47 readonly version: string,
50 this.id = identifier.id;
51 }
53 > toString(): string {
54 return `${this.id}-${this.version}${this.targetPlatform !== TargetPlatform.UNDEFINED ? `-${this.targetPlatform}` : ''}`;
55 }
57 > equals(o: unknown): boolean {
58 if (!(o instanceof ExtensionKey)) {
59 return false;
61 return areSameExtensions(this, o) && this.version === o.version && this.targetPlatform === o.targetPlatform;
62 }
64 >
65 > const EXTENSION_IDENTIFIER_WITH_VERSION_REGEX = /^([^.]+\..+)@((prerelease)|(\d+\.\d+\.\d+(-.*)?))$/;
66 > export function getIdAndVersion(id: string): [string, string | undefined] {
67 const matches = EXTENSION_IDENTIFIER_WITH_VERSION_REGEX.exec(id);
68 if (matches && matches[1]) {
71 return [adoptToGalleryExtensionId(id), undefined];
72 }
74 > export function getExtensionId(publisher: string, name: string): string {
75 return `${publisher}.${name}`;
76 }
78 > export function adoptToGalleryExtensionId(id: string): string {
79 return id.toLowerCase();
80 }
82 > export function getGalleryExtensionId(publisher: string | undefined, name: string): string {
83 return adoptToGalleryExtensionId(getExtensionId(publisher ?? UNDEFINED_PUBLISHER, name));
84 }
86 > export function groupByExtension<T>(extensions: T[], getExtensionIdentifier: (t: T) => IExtensionIdentifier): T[][] {
87 const byExtension: T[][] = [];
88 const findGroup = (extension: T) => {
104 return byExtension;
105 }
107 > export function getLocalExtensionTelemetryData(extension: ILocalExtension) {
108 return {
109 id: extension.identifier.id,
116 };
117 }
119 >
120 > /* __GDPR__FRAGMENT__
121 > "GalleryExtensionTelemetryData" : {
122 > "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
123 > "name": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
124 > "extensionVersion": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
125 > "galleryId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
126 > "publisherId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
127 > "publisherName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
128 > "publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
129 > "isPreReleaseVersion": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
130 > "dependencies": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
131 > "isSigned": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
132 > "${include}": [
133 > "${GalleryExtensionTelemetryData2}"
134 > ]
135 > }
136 > */
137 > export function getGalleryExtensionTelemetryData(extension: IGalleryExtension) {
138 return {
139 id: new TelemetryTrustedValue(extension.identifier.id),
150 };
151 }
153 > export const BetterMergeId = new ExtensionIdentifier('pprice.better-merge');
154 >
155 > export function getExtensionDependencies(installedExtensions: ReadonlyArray<IExtension>, extension: IExtension): IExtension[] {
156 const dependencies: IExtension[] = [];
157 const extensions = extension.manifest.extensionDependencies?.slice(0) ?? [];
171 return dependencies;
172 }
174 async function isAlpineLinux(fileService: IFileService, logService: ILogService): Promise<boolean> {
175 if (!isLinux) {
191 return !!content && (content.match(/^ID=([^\u001b\r\n]*)/m) || [])[1] === 'alpine';
192 }
194 export async function computeTargetPlatform(fileService: IFileService, logService: ILogService): Promise<TargetPlatform> {
195 const alpineLinux = await isAlpineLinux(fileService, logService);
198 return targetPlatform;
199 }
201 > export function isMalicious(identifier: IExtensionIdentifier, malicious: ReadonlyArray<MaliciousExtensionInfo>): boolean {
202 return findMatchingMaliciousEntry(identifier, malicious) !== undefined;
203 }
205 > export function findMatchingMaliciousEntry(identifier: IExtensionIdentifier, malicious: ReadonlyArray<MaliciousExtensionInfo>): MaliciousExtensionInfo | undefined {
206 return malicious.find(({ extensionOrPublisher }) => {
207 if (isString(extensionOrPublisher)) {
src/vs/base/common/observableInternal/observables/observableValue.ts 77 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableValue.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 { ISettableObservable, ITransaction } from '../base.js';
7 > import { TransactionImpl } from '../transaction.js';
8 > import { BaseObservable } from './baseObservable.js';
9 > import { EqualityComparer, IDisposable, strictEquals } from '../commonFacade/deps.js';
10 > import { DebugNameData } from '../debugName.js';
11 > import { getLogger } from '../logging/logging.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Creates an observable value.
16 > * Observers get informed when the value changes.
17 > * @template TChange An arbitrary type to describe how or why the value changed. Defaults to `void`.
18 > * Observers will receive every single change value.
19 > */
20 >
21 > export function observableValue<T, TChange = void>(name: string, initialValue: T): ISettableObservable<T, TChange>;
22 > export function observableValue<T, TChange = void>(owner: object, initialValue: T): ISettableObservable<T, TChange>;
23 > export function observableValue<T, TChange = void>(nameOrOwner: string | object, initialValue: T, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T, TChange> {
24 > let debugNameData: DebugNameData; observableValue.ts
25 > if (typeof nameOrOwner === 'string') {
26 debugNameData = new DebugNameData(undefined, nameOrOwner, undefined);
27 > } else { observableValue.ts
28 > debugNameData = new DebugNameData(nameOrOwner, undefined, undefined); observableValue.ts
29 > }
30 > return new ObservableValue(debugNameData, initialValue, strictEquals, debugLocation); observableValue.ts
31 > }
33 > export class ObservableValue<T, TChange = void>
34 > extends BaseObservable<T, TChange>
35 > implements ISettableObservable<T, TChange> {
36 > protected _value: T;
37 >
38 > get debugName() {
39 > return this._debugNameData.getDebugName(this) ?? 'ObservableValue';
40 > }
41 >
42 > constructor(
43 > private readonly _debugNameData: DebugNameData, observableValue.ts
44 > initialValue: T,
45 > private readonly _equalityComparator: EqualityComparer<T>,
46 > debugLocation: DebugLocation
47 > ) {
48 > super(debugLocation);
49 > this._value = initialValue;
50 >
51 > getLogger()?.handleObservableUpdated(this, { hadValue: false, newValue: initialValue, change: undefined, didChange: true, oldValue: undefined });
52 > }
53 > public override get(): T { observableValue.ts
54 > return this._value; observableValue.ts
55 > }
57 > public set(value: T, tx: ITransaction | undefined, change: TChange): void {
58 if (change === undefined && this._equalityComparator(this._value, value)) {
59 return;
79 }
80 }
82 > override toString(): string {
83 return `${this.debugName}: ${this._value}`;
84 }
86 > protected _setValue(newValue: T): void {
87 this._value = newValue;
88 }
90 > public debugGetState() {
91 return {
92 value: this._value,
93 };
94 }
96 > public debugSetValue(value: unknown) {
97 this._value = value as T;
98 }
100 > /**
101 > * A disposable observable. When disposed, its value is also disposed.
102 > * When a new value is set, the previous value is disposed.
103 > */
104 >
105 > export function disposableObservableValue<T extends IDisposable | undefined, TChange = void>(nameOrOwner: string | object, initialValue: T, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T, TChange> & IDisposable {
106 let debugNameData: DebugNameData;
107 if (typeof nameOrOwner === 'string') {
112 return new DisposableObservableValue(debugNameData, initialValue, strictEquals, debugLocation);
113 }
115 > export class DisposableObservableValue<T extends IDisposable | undefined, TChange = void> extends ObservableValue<T, TChange> implements IDisposable {
116 > protected override _setValue(newValue: T): void {
117 if (this._value === newValue) {
118 return;
src/vs/base/test/common/virtualScheduling/embedding.ts 76 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- embedding.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 { setTimeout0, setTimeout0IsFaster } from '../../../common/platform.js';
7 > import { TimeApi } from './timeApi.js';
8 > import { VirtualEvent } from './virtualClock.js';
9 >
10 > /**
11 > * # The processor/host embedding
12 > *
13 > * An {@link Embedding} is the contract between the processor's pure state
14 > * machine and the host event loop. It is invoked once per virtual step that
15 > * produced progress, and decides *how* the processor reaches the host before
16 > * the next step.
17 > *
18 > * ## Contract
19 > *
20 > * On each invocation the embedding MUST do exactly one of:
21 > *
22 > * 1. Return `'continueSync'` **without** calling `then`. The processor will
23 > * loop in place on the same host stack frame.
24 > *
25 > * 2. Schedule `then` on a host primitive (microtask, macrotask, paint frame)
26 > * and return `'cbScheduled'`. The processor will return and wait for the
27 > * callback to re-enter the trampoline.
28 > *
29 > * The embedding MUST NOT call `then` synchronously and also return
30 > * `'cbScheduled'` (that would re-enter the trampoline before this call
31 > * completed). Likewise, returning `'continueSync'` while having scheduled
32 > * `then` async would cause `then` to fire after the trampoline already
33 > * looped — also a bug.
34 > *
35 > * ## Why a callback contract instead of async/await
36 > *
37 > * Every `await` is an implicit microtask hop. For code whose job is to
38 > * decide host hops, that's the wrong abstraction: the reader has to mentally
39 > * compile the `await` to a boundary. With this contract, every host hop is
40 > * a named call to a single primitive (`api.setTimeout`, `setTimeout0`,
41 > * `api.requestAnimationFrame`, …) at exactly one site in this file.
42 > */
43 > export type Embedding = (
44 > nextEvent: VirtualEvent,
45 > then: () => void,
46 > ) => 'continueSync' | 'cbScheduled';
47 >
48 > /**
49 > * Tasks never schedule via promise chains. The processor runs virtual events
50 > * back-to-back on a single host stack frame — fastest possible, but starves
51 > * the host event loop for the duration of the run.
52 > *
53 > * Use only for tests where no `await` / `.then` chains are involved between
54 > * scheduling and execution of virtual events.
55 > */
56 > export const syncEmbedding: Embedding = () => 'continueSync';
57 >
58 > /**
59 > * Tasks may schedule via `await` / `.then`. Between virtual events, yield to
60 > * the host so the *microtask closure* — the current microtask plus every
61 > * microtask it transitively enqueues — drains before the next event runs.
62 > *
63 > * This is the embedding to use for almost all integration-style tests.
64 > */
65 > export function drainMicrotasksEmbedding(realApi: TimeApi): Embedding {
66 return (next, then) => {
67 if (next.preferRealAnimationFrame && realApi.requestAnimationFrame) {
73 };
74 }
76 > /**
77 > * Schedule `cb` after the closure of the current microtask queue: `cb`
78 > * fires only after the current microtask AND every microtask it
79 > * (recursively, transitively) enqueues has settled.
80 > *
81 > * Per the HTML spec, a macrotask runs only when the microtask queue is
82 > * empty, so any macrotask primitive achieves this. We pick the fastest
83 > * one available on the host.
84 > */
85 > export function nextMacrotask(api: TimeApi, cb: () => void): void {
86 if (setTimeout0IsFaster) { setTimeout0(cb); return; }
87 if (api.setImmediate) { api.setImmediate(cb); return; }
src/vs/base/common/observableInternal/logging/debugGetDependencyGraph.ts 75 covered LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugGetDependencyGraph.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 { IObservable, IObserver } from '../base.js';
7 > import { Derived } from '../observables/derivedImpl.js';
8 > import { FromEventObservable } from '../observables/observableFromEvent.js';
9 > import { ObservableValue } from '../observables/observableValue.js';
10 > import { AutorunObserver } from '../reactions/autorunImpl.js';
11 > import { formatValue } from './consoleObservableLogger.js';
12 >
13 > interface IOptions {
14 > type: 'dependencies' | 'observers';
15 > debugNamePostProcessor?: (name: string) => string;
16 > }
17 >
18 > export function debugGetObservableGraph(obs: IObservable<any> | IObserver, options: IOptions): string {
19 const debugNamePostProcessor = options?.debugNamePostProcessor ?? ((str: string) => str);
20 const info = Info.from(obs, debugNamePostProcessor);
31 }
32 }
34 function formatObservableInfoWithDependencies(info: Info, indentLevel: number, alreadyListed: Set<IObservable<any> | IObserver>, options: IOptions): string {
35 const indent = '\t\t'.repeat(indentLevel);
58 return lines.join('\n');
59 }
61 function formatObservableInfoWithObservers(info: Info, indentLevel: number, alreadyListed: Set<IObservable<any> | IObserver>, options: IOptions): string {
62 const indent = '\t\t'.repeat(indentLevel);
85 return lines.join('\n');
86 }
88 > class Info {
89 > public static from(obs: IObservable<any> | IObserver, debugNamePostProcessor: (name: string) => string): Info | undefined {
90 > if (obs instanceof AutorunObserver) {
91 > const state = obs.debugGetState(); debugGetDependencyGraph.ts
92 > return new Info(
93 > obs,
94 > debugNamePostProcessor(obs.debugName),
95 > 'autorun',
96 > undefined,
97 > state.stateStr,
98 > Array.from(state.dependencies),
99 > []
100 > );
101 > } else if (obs instanceof Derived) { debugGetDependencyGraph.ts
102 > const state = obs.debugGetState();
103 > return new Info(
104 > obs,
105 > debugNamePostProcessor(obs.debugName),
106 > 'derived',
107 > state.value,
108 > state.stateStr,
109 > Array.from(state.dependencies),
110 > Array.from(obs.debugGetObservers())
111 > );
112 > } else if (obs instanceof ObservableValue) {
113 > const state = obs.debugGetState();
114 > return new Info(
115 > obs,
116 > debugNamePostProcessor(obs.debugName),
117 > 'observableValue',
118 > state.value,
119 > 'upToDate',
120 > [],
121 > Array.from(obs.debugGetObservers())
122 > );
123 > } else if (obs instanceof FromEventObservable) {
124 > const state = obs.debugGetState(); debugGetDependencyGraph.ts
125 > return new Info(
126 > obs,
127 > debugNamePostProcessor(obs.debugName),
128 > 'fromEvent',
129 > state.value,
130 > state.hasValue ? 'upToDate' : 'initial',
131 > [],
132 > Array.from(obs.debugGetObservers())
133 > );
134 > }
135 > return undefined;
137 >
138 > public static unknown(obs: IObservable<any> | IObserver): Info {
139 return new Info(
140 obs,
147 );
148 }
150 > constructor(
151 public readonly sourceObj: IObservable<any> | IObserver,
152 public readonly name: string,
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/hash.ts 74 covered LOC · 17 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- hash.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 { encodeHex, VSBuffer } from './buffer.js';
7 > import * as strings from './strings.js';
8 >
9 > type NotSyncHashable = ArrayBufferLike | ArrayBufferView;
10 >
11 > /**
12 > * Return a hash value for an object.
13 > *
14 > * Note that this should not be used for binary data types. Instead,
15 > * prefer {@link hashAsync}.
16 > */
17 > export function hash<T>(obj: T extends NotSyncHashable ? never : T): number {
18 return doHash(obj, 0);
19 }
20 > hash.ts
21 > export function doHash(obj: unknown, hashVal: number): number {
22 switch (typeof obj) {
23 case 'object':
40 }
41 }
42 > hash.ts
43 > export function numberHash(val: number, initialHashVal: number): number {
44 return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32
45 }
46 > hash.ts
47 function booleanHash(b: boolean, initialHashVal: number): number {
48 return numberHash(b ? 433 : 863, initialHashVal);
49 }
50 > hash.ts
51 > export function stringHash(s: string, hashVal: number) {
52 hashVal = numberHash(149417, hashVal);
53 for (let i = 0, length = s.length; i < length; i++) {
56 return hashVal;
57 }
58 > hash.ts
59 function arrayHash(arr: unknown[], initialHashVal: number): number {
60 initialHashVal = numberHash(104579, initialHashVal);
61 return arr.reduce<number>((hashVal, item) => doHash(item, hashVal), initialHashVal);
62 }
63 > hash.ts
64 function objectHash(obj: object, initialHashVal: number): number {
65 initialHashVal = numberHash(181387, initialHashVal);
69 }, initialHashVal);
70 }
71 > hash.ts
72 >
73 >
74 > /** Hashes the input as SHA-1, returning a hex-encoded string. */
75 > export const hashAsync = (input: string | ArrayBufferView | VSBuffer) => {
76 // Note: I would very much like to expose a streaming interface for hashing
77 // generally, but this is not available in web crypto yet, see
96 return crypto.subtle.digest('sha-1', buff as ArrayBufferView<ArrayBuffer>).then(toHexString); // CodeQL [SM04514] we use sha1 here for validating old stored client state, not for security
97 };
98 > hash.ts
99 > const enum SHA1Constant {
100 > BLOCK_SIZE = 64, // 512 / 8
101 > UNICODE_REPLACEMENT = 0xFFFD,
102 > }
103 >
104 function leftRotate(value: number, bits: number, totalBits: number = 32): number {
105 // delta + bits = totalBits
112 return ((value << bits) | ((mask & value) >>> delta)) >>> 0;
113 }
114 > hash.ts
115 > function toHexString(buffer: ArrayBuffer): string;
116 > function toHexString(value: number, bitsize?: number): string;
117 function toHexString(bufferOrValue: ArrayBuffer | number, bitsize: number = 32): string {
118 if (bufferOrValue instanceof ArrayBuffer) {
122 return (bufferOrValue >>> 0).toString(16).padStart(bitsize / 4, '0');
123 }
124 > hash.ts
125 > /**
126 > * A SHA1 implementation that works with strings and does not allocate.
127 > *
128 > * Prefer to use {@link hashAsync} in async contexts
129 > */
130 > export class StringSHA1 {
131 > private static _bigBlock32 = new DataView(new ArrayBuffer(320)); // 80 * 4 = 320
132 >
133 > private _h0 = 0x67452301;
134 > private _h1 = 0xEFCDAB89;
135 > private _h2 = 0x98BADCFE;
136 > private _h3 = 0x10325476;
137 > private _h4 = 0xC3D2E1F0;
138 >
139 > private readonly _buff: Uint8Array;
140 > private readonly _buffDV: DataView;
141 > private _buffLen: number;
142 > private _totalLen: number;
143 > private _leftoverHighSurrogate: number;
144 > private _finished: boolean;
145 >
146 > constructor() {
147 this._buff = new Uint8Array(SHA1Constant.BLOCK_SIZE + 3 /* to fit any utf-8 */);
148 this._buffDV = new DataView(this._buff.buffer);
152 this._finished = false;
153 }
154 > hash.ts
155 > public update(str: string): void {
156 const strLen = str.length;
157 if (strLen === 0) {
208 this._leftoverHighSurrogate = leftoverHighSurrogate;
209 }
210 > hash.ts
211 > private _push(buff: Uint8Array, buffLen: number, codePoint: number): number {
212 if (codePoint < 0x0080) {
213 buff[buffLen++] = codePoint;
238 return buffLen;
239 }
240 > hash.ts
241 > public digest(): string {
242 if (!this._finished) {
243 this._finished = true;
253 return toHexString(this._h0) + toHexString(this._h1) + toHexString(this._h2) + toHexString(this._h3) + toHexString(this._h4);
254 }
255 > hash.ts
256 > private _wrapUp(): void {
257 this._buff[this._buffLen++] = 0x80;
258 this._buff.subarray(this._buffLen).fill(0);
271 this._step();
272 }
273 > hash.ts
274 > private _step(): void {
275 const bigBlock32 = StringSHA1._bigBlock32;
276 const data = this._buffDV;
322 this._h4 = (this._h4 + e) & 0xffffffff;
323 }
324 > } hash.ts
src/vs/base/common/themables.ts 73 covered LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- themables.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 { Codicon } from './codicons.js';
7 >
8 > export type ColorIdentifier = string;
9 >
10 > export type IconIdentifier = string;
11 >
12 > export interface ThemeColor {
13 > id: string;
14 > }
15 >
16 > export namespace ThemeColor {
17 > export function isThemeColor(obj: unknown): obj is ThemeColor {
18 return !!obj && typeof obj === 'object' && typeof (<ThemeColor>obj).id === 'string';
19 }
20 > } themables.ts
21 >
22 > export function themeColorFromId(id: ColorIdentifier) {
23 return { id };
24 }
26 >
27 > export interface ThemeIcon {
28 > readonly id: string;
29 > readonly color?: ThemeColor;
30 > }
31 >
32 > export namespace ThemeIcon {
33 > export const iconNameSegment = '[A-Za-z0-9]+';
34 > export const iconNameExpression = '[A-Za-z0-9-]+';
35 > export const iconModifierExpression = '~[A-Za-z]+';
36 > export const iconNameCharacter = '[A-Za-z0-9~-]';
37 >
38 > const ThemeIconIdRegex = new RegExp(`^(${iconNameExpression})(${iconModifierExpression})?$`);
39 >
40 > export function asClassNameArray(icon: ThemeIcon): string[] {
41 const match = ThemeIconIdRegex.exec(icon.id);
42 if (!match) {
50 return classNames;
51 }
53 > export function asClassName(icon: ThemeIcon): string {
54 return asClassNameArray(icon).join(' ');
55 }
57 > export function asCSSSelector(icon: ThemeIcon): string {
58 return '.' + asClassNameArray(icon).join('.');
59 }
61 > export function isThemeIcon(obj: unknown): obj is ThemeIcon {
62 return !!obj && typeof obj === 'object' && typeof (<ThemeIcon>obj).id === 'string' && (typeof (<ThemeIcon>obj).color === 'undefined' || ThemeColor.isThemeColor((<ThemeIcon>obj).color));
63 }
65 > const _regexFromString = new RegExp(`^\\$\\((${ThemeIcon.iconNameExpression}(?:${ThemeIcon.iconModifierExpression})?)\\)$`);
66 >
67 > export function fromString(str: string): ThemeIcon | undefined {
68 const match = _regexFromString.exec(str);
69 if (!match) {
73 return { id: name };
74 }
76 > export function fromId(id: string): ThemeIcon {
77 return { id };
78 }
80 > export function modify(icon: ThemeIcon, modifier: 'disabled' | 'spin' | undefined): ThemeIcon {
81 > let id = icon.id; themables.ts
82 > const tildeIndex = id.lastIndexOf('~');
83 > if (tildeIndex !== -1) {
84 id = id.substring(0, tildeIndex);
85 }
86 > if (modifier) { themables.ts
87 > id = `${id}~${modifier}`;
88 > }
89 > return { id };
90 > }
92 > export function getModifier(icon: ThemeIcon): string | undefined {
93 const tildeIndex = icon.id.lastIndexOf('~');
94 if (tildeIndex !== -1) {
97 return undefined;
98 }
100 > export function isEqual(ti1: ThemeIcon, ti2: ThemeIcon): boolean {
101 return ti1.id === ti2.id && ti1.color?.id === ti2.color?.id;
102 }
103 > themables.ts
104 > /**
105 > * Returns whether specified icon is defined and has 'file' ID.
106 > */
107 > export function isFile(icon: ThemeIcon | undefined): boolean {
108 return icon?.id === Codicon.file.id;
109 }
110 > themables.ts
111 > /**
112 > * Returns whether specified icon is defined and has 'folder' ID.
113 > */
114 > export function isFolder(icon: ThemeIcon | undefined): boolean {
115 return icon?.id === Codicon.folder.id;
116 }
117 > } themables.ts
src/vs/base/common/observableInternal/logging/logging.ts 69 covered LOC · 18 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- logging.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 { AutorunObserver } from '../reactions/autorunImpl.js';
7 > import { IObservable } from '../base.js';
8 > import { TransactionImpl } from '../transaction.js';
9 > import type { Derived } from '../observables/derivedImpl.js';
10 > import { DebugLocation } from '../debugLocation.js';
11 >
12 > let globalObservableLogger: IObservableLogger | undefined;
13 >
14 > export function addLogger(logger: IObservableLogger): void {
15 if (!globalObservableLogger) {
16 globalObservableLogger = logger;
21 }
22 }
23 > logging.ts
24 > export function getLogger(): IObservableLogger | undefined {
25 > return globalObservableLogger; logging.ts
26 > }
27 > logging.ts
28 > let globalObservableLoggerFn: ((obs: IObservable<any>) => void) | undefined = undefined;
29 > export function setLogObservableFn(fn: (obs: IObservable<any>) => void): void {
30 > globalObservableLoggerFn = fn;
31 > }
32 >
33 > export function logObservable(obs: IObservable<any>): void {
34 if (globalObservableLoggerFn) {
35 globalObservableLoggerFn(obs);
36 }
37 }
38 > logging.ts
39 > export interface IChangeInformation {
40 > oldValue: unknown;
41 > newValue: unknown;
42 > change: unknown;
43 > didChange: boolean;
44 > hadValue: boolean;
45 > }
46 >
47 > export interface IObservableLogger {
48 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void;
49 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void;
50 >
51 > handleObservableUpdated(observable: IObservable<any>, info: IChangeInformation): void;
52 >
53 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void;
54 > handleAutorunDisposed(autorun: AutorunObserver): void;
55 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void;
56 > handleAutorunStarted(autorun: AutorunObserver): void;
57 > handleAutorunFinished(autorun: AutorunObserver): void;
58 >
59 > handleDerivedDependencyChanged(derived: Derived<any, any, any>, observable: IObservable<any>, change: unknown): void;
60 > handleDerivedCleared(observable: Derived<any, any, any>): void;
61 >
62 > handleBeginTransaction(transaction: TransactionImpl): void;
63 > handleEndTransaction(transaction: TransactionImpl): void;
64 > }
65 >
66 > class ComposedLogger implements IObservableLogger {
67 > constructor(
68 public readonly loggers: IObservableLogger[],
69 ) { }
70 > logging.ts
71 > handleObservableCreated(observable: IObservable<any>, location: DebugLocation): void {
72 for (const logger of this.loggers) {
73 logger.handleObservableCreated(observable, location);
74 }
75 }
76 > handleOnListenerCountChanged(observable: IObservable<any>, newCount: number): void { logging.ts
77 for (const logger of this.loggers) {
78 logger.handleOnListenerCountChanged(observable, newCount);
79 }
80 }
81 > handleObservableUpdated(observable: IObservable<any>, info: IChangeInformation): void { logging.ts
82 for (const logger of this.loggers) {
83 logger.handleObservableUpdated(observable, info);
84 }
85 }
86 > handleAutorunCreated(autorun: AutorunObserver, location: DebugLocation): void { logging.ts
87 for (const logger of this.loggers) {
88 logger.handleAutorunCreated(autorun, location);
89 }
90 }
91 > handleAutorunDisposed(autorun: AutorunObserver): void { logging.ts
92 for (const logger of this.loggers) {
93 logger.handleAutorunDisposed(autorun);
94 }
95 }
96 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void { logging.ts
97 for (const logger of this.loggers) {
98 logger.handleAutorunDependencyChanged(autorun, observable, change);
99 }
100 }
101 > handleAutorunStarted(autorun: AutorunObserver): void { logging.ts
102 for (const logger of this.loggers) {
103 logger.handleAutorunStarted(autorun);
104 }
105 }
106 > handleAutorunFinished(autorun: AutorunObserver): void { logging.ts
107 for (const logger of this.loggers) {
108 logger.handleAutorunFinished(autorun);
109 }
110 }
111 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void { logging.ts
112 for (const logger of this.loggers) {
113 logger.handleDerivedDependencyChanged(derived, observable, change);
114 }
115 }
116 > handleDerivedCleared(observable: Derived<any>): void { logging.ts
117 for (const logger of this.loggers) {
118 logger.handleDerivedCleared(observable);
119 }
120 }
121 > handleBeginTransaction(transaction: TransactionImpl): void { logging.ts
122 for (const logger of this.loggers) {
123 logger.handleBeginTransaction(transaction);
124 }
125 }
126 > handleEndTransaction(transaction: TransactionImpl): void { logging.ts
127 for (const logger of this.loggers) {
128 logger.handleEndTransaction(transaction);
129 }
130 }
131 > } logging.ts
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/common/objects.ts 65 covered LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- objects.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 { isTypedArray, isObject, isUndefinedOrNull } from './types.js';
7 >
8 > export function deepClone<T>(obj: T): T {
9 if (!obj || typeof obj !== 'object') {
10 return obj;
19 return result;
20 }
21 > objects.ts
22 > export function deepFreeze<T>(obj: T): T {
23 if (!obj || typeof obj !== 'object') {
24 return obj;
39 return obj;
40 }
41 > objects.ts
42 > const _hasOwnProperty = Object.prototype.hasOwnProperty;
43 >
44 >
45 > export function cloneAndChange(obj: any, changer: (orig: any) => any): any {
46 return _cloneAndChange(obj, changer, new Set());
47 }
48 > objects.ts
49 function _cloneAndChange(obj: any, changer: (orig: any) => any, seen: Set<any>): any {
50 if (isUndefinedOrNull(obj)) {
82 return obj;
83 }
84 > objects.ts
85 > /**
86 > * Copies all properties of source into destination. The optional parameter "overwrite" allows to control
87 > * if existing properties on the destination should be overwritten or not. Defaults to true (overwrite).
88 > */
89 > export function mixin(destination: any, source: any, overwrite: boolean = true): any {
90 if (!isObject(destination)) {
91 return source;
109 return destination;
110 }
111 > objects.ts
112 > export function equals(one: any, other: any): boolean {
113 if (one === other) {
114 return true;
162 return true;
163 }
164 > objects.ts
165 > /**
166 > * Calls `JSON.Stringify` with a replacer to break apart any circular references.
167 > * This prevents `JSON`.stringify` from throwing the exception
168 > * "Uncaught TypeError: Converting circular structure to JSON"
169 > */
170 > export function safeStringify(obj: any): string {
171 const seen = new Set<any>();
172 return JSON.stringify(obj, (key, value) => {
184 });
185 }
186 > objects.ts
187 > /**
188 > * Like `JSON.stringify`, but with deterministic ordering of object keys so that
189 > * structurally equal inputs always produce the same string. Useful for cache
190 > * keys derived from arbitrary object payloads.
191 > *
192 > * - Object keys are sorted at every level of nesting.
193 > * - Properties whose value is `undefined` are omitted (matching `JSON.stringify`).
194 > * - Circular references are replaced with the string `"[Circular]"` to avoid
195 > * throwing.
196 > * - A top-level `undefined` returns the string `'undefined'`; any other
197 > * stringification failure returns the empty string.
198 > */
199 > export function stableStringify(value: unknown): string {
200 if (value === undefined) {
201 return 'undefined';
207 }
208 }
209 > objects.ts
210 function _stableStringify(value: unknown, seen: WeakSet<object>): string {
211 if (value === null || typeof value !== 'object') {
230 return '{' + parts.join(',') + '}';
231 }
232 > objects.ts
233 > type obj = { [key: string]: any };
234 > /**
235 > * Returns an object that has keys for each value that is different in the base object. Keys
236 > * that do not exist in the target but in the base object are not considered.
237 > *
238 > * Note: This is not a deep-diffing method, so the values are strictly taken into the resulting
239 > * object if they differ.
240 > *
241 > * @param base the object to diff against
242 > * @param obj the object to use for diffing
243 > */
244 > export function distinct(base: obj, target: obj): obj {
245 const result = Object.create(null);
246
261 return result;
262 }
263 > objects.ts
264 > export function getCaseInsensitive(target: obj, key: string): unknown {
265 const lowercaseKey = key.toLowerCase();
266 const equivalentKey = Object.keys(target).find(k => k.toLowerCase() === lowercaseKey);
267 return equivalentKey ? target[equivalentKey] : target[key];
268 }
269 > objects.ts
270 > export function filter(obj: obj, predicate: (key: string, value: any) => boolean): obj {
271 const result = Object.create(null);
272 for (const [key, value] of Object.entries(obj)) {
277 return result;
278 }
279 > objects.ts
280 > export function mapValues<T extends {}, R>(obj: T, fn: (value: T[keyof T], key: string) => R): { [K in keyof T]: R } {
281 const result: { [key: string]: R } = {};
282 for (const [key, value] of Object.entries(obj)) {
src/vs/base/common/codicons.ts 64 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- codicons.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 > import { ThemeIcon } from './themables.js';
6 > import { register } from './codiconsUtil.js';
7 > import { codiconsLibrary } from './codiconsLibrary.js';
8 >
9 >
10 > /**
11 > * Only to be used by the iconRegistry.
12 > */
13 > export function getAllCodicons(): ThemeIcon[] {
14 return Object.values(Codicon);
15 }
17 > /**
18 > * Derived icons, that could become separate icons.
19 > * These mappings should be moved into the mapping file in the vscode-codicons repo at some point.
20 > */
21 > export const codiconsDerived = {
22 > dialogError: register('dialog-error', 'error'),
23 > dialogWarning: register('dialog-warning', 'warning'),
24 > dialogInfo: register('dialog-info', 'info'),
25 > dialogClose: register('dialog-close', 'close'),
26 > treeItemExpanded: register('tree-item-expanded', 'chevron-down'), // collapsed is done with rotation
27 > treeFilterOnTypeOn: register('tree-filter-on-type-on', 'list-filter'),
28 > treeFilterOnTypeOff: register('tree-filter-on-type-off', 'list-selection'),
29 > treeFilterClear: register('tree-filter-clear', 'close'),
30 > treeItemLoading: register('tree-item-loading', 'loading'),
31 > menuSelection: register('menu-selection', 'check'),
32 > menuSubmenu: register('menu-submenu', 'chevron-right'),
33 > menuBarMore: register('menubar-more', 'more'),
34 > scrollbarButtonLeft: register('scrollbar-button-left', 'triangle-left'),
35 > scrollbarButtonRight: register('scrollbar-button-right', 'triangle-right'),
36 > scrollbarButtonUp: register('scrollbar-button-up', 'triangle-up'),
37 > scrollbarButtonDown: register('scrollbar-button-down', 'triangle-down'),
38 > toolBarMore: register('toolbar-more', 'more'),
39 > quickInputBack: register('quick-input-back', 'arrow-left'),
40 > dropDownButton: register('drop-down-button', 0xeab4),
41 > symbolCustomColor: register('symbol-customcolor', 0xeb5c),
42 > exportIcon: register('export', 0xebac),
43 > workspaceUnspecified: register('workspace-unspecified', 0xebc3),
44 > newLine: register('newline', 0xebea),
45 > thumbsDownFilled: register('thumbsdown-filled', 0xec13),
46 > thumbsUpFilled: register('thumbsup-filled', 0xec14),
47 > gitFetch: register('git-fetch', 0xec1d),
48 > lightbulbSparkleAutofix: register('lightbulb-sparkle-autofix', 0xec1f),
49 > debugBreakpointPending: register('debug-breakpoint-pending', 0xebd9),
50 > chatImport: register('chat-import', 0xec86),
51 > chatExport: register('chat-export', 0xec87),
52 >
53 > } as const;
54 >
55 > /**
56 > * The Codicon library is a set of default icons that are built-in in VS Code.
57 > *
58 > * In the product (outside of base) Codicons should only be used as defaults. In order to have all icons in VS Code
59 > * themeable, component should define new, UI component specific icons using `iconRegistry.registerIcon`.
60 > * In that call a Codicon can be named as default.
61 > */
62 > export const Codicon = {
63 > ...codiconsLibrary,
64 > ...codiconsDerived
65 >
66 > } as const;
src/vs/workbench/contrib/debug/common/debugSource.ts 58 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugSource.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 { URI } from '../../../../base/common/uri.js';
8 > import { normalize, isAbsolute } from '../../../../base/common/path.js';
9 > import * as resources from '../../../../base/common/resources.js';
10 > import { DEBUG_SCHEME } from './debug.js';
11 > import { IRange } from '../../../../editor/common/core/range.js';
12 > import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js';
13 > import { Schemas } from '../../../../base/common/network.js';
14 > import { isUriString } from './debugUtils.js';
15 > import { IEditorPane } from '../../../common/editor.js';
16 > import { TextEditorSelectionRevealType } from '../../../../platform/editor/common/editor.js';
17 > import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
18 > import { ILogService } from '../../../../platform/log/common/log.js';
19 >
20 > export const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
21 >
22 > /**
23 > * Debug URI format
24 > *
25 > * a debug URI represents a Source object and the debug session where the Source comes from.
26 > *
27 > * debug:arbitrary_path?session=123e4567-e89b-12d3-a456-426655440000&ref=1016
28 > * \___/ \____________/ \__________________________________________/ \______/
29 > * | | | |
30 > * scheme source.path session id source.reference
31 > *
32 > *
33 > */
34 >
35 > export class Source {
36 >
37 > readonly uri: URI;
38 > available: boolean;
39 > raw: DebugProtocol.Source;
40 >
41 > constructor(raw_: DebugProtocol.Source | undefined, sessionId: string, uriIdentityService: IUriIdentityService, logService: ILogService) {
42 let path: string;
43 if (raw_) {
53 this.uri = getUriFromSource(this.raw, path, sessionId, uriIdentityService, logService);
54 }
56 > get name() {
57 return this.raw.name || resources.basenameOrAuthority(this.uri);
58 }
60 > get origin() {
61 return this.raw.origin;
62 }
64 > get presentationHint() {
65 return this.raw.presentationHint;
66 }
68 > get reference() {
69 return this.raw.sourceReference;
70 }
72 > get inMemory() {
73 return this.uri.scheme === DEBUG_SCHEME;
74 }
76 > openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<IEditorPane | undefined> {
77 return !this.available ? Promise.resolve(undefined) : editorService.openEditor({
78 resource: this.uri,
87 }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
88 }
90 > static getEncodedDebugData(modelUri: URI): { name: string; path: string; sessionId?: string; sourceReference?: number } {
91 let path: string;
92 let sourceReference: number | undefined;
128 };
129 }
130 > } debugSource.ts
131 >
132 > export function getUriFromSource(raw: DebugProtocol.Source, path: string | undefined, sessionId: string, uriIdentityService: IUriIdentityService, logService: ILogService): URI {
133 const _getUriFromSource = (path: string | undefined) => {
134 if (typeof raw.sourceReference === 'number' && raw.sourceReference > 0) {
src/vs/base/common/observableInternal/index.ts 57 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- index.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 > // This is a facade for the observable implementation. Only import from here!
7 >
8 > export { observableValueOpts } from './observables/observableValueOpts.js';
9 > export { autorun, autorunDelta, autorunHandleChanges, autorunOpts, autorunWithStore, autorunWithStoreHandleChanges, autorunIterableDelta, autorunPerKeyedItem, autorunSelfDisposable, registerAutorunSelfDisposable } from './reactions/autorun.js';
10 > export { type IObservable, type IObservableWithChange, type IObserver, type IReader, type ISettable, type IReaderWithStore, type ISettableObservable, type ITransaction } from './base.js';
11 > export { disposableObservableValue } from './observables/observableValue.js';
12 > export { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter, derivedWithStore } from './observables/derived.js';
13 > export { type IDerivedReader } from './observables/derivedImpl.js';
14 > export { ObservableLazy, ObservableLazyPromise, ObservablePromise, ObservableResolvedPromise, PromiseResult, } from './utils/promise.js';
15 > export { derivedWithCancellationToken, waitForState } from './utils/utilsCancellation.js';
16 > export {
17 > debouncedObservable, debouncedObservable2, derivedObservableWithCache,
18 > derivedObservableWithWritableCache, keepObserved, mapObservableArrayCached, observableFromPromise,
19 > recomputeInitiallyAndOnChange,
20 > signalFromObservable, throttledObservable, wasEventTriggeredRecently,
21 > isObservable,
22 > } from './utils/utils.js';
23 > export { type DebugOwner } from './debugName.js';
24 > export { type IChangeContext, type IChangeTracker, recordChanges, recordChangesLazy } from './changeTracker.js';
25 > export { constObservable } from './observables/constObservable.js';
26 > export { type IObservableSignal, observableSignal } from './observables/observableSignal.js';
27 > export { observableFromEventOpts } from './observables/observableFromEvent.js';
28 > export { observableSignalFromEvent } from './observables/observableSignalFromEvent.js';
29 > export { asyncTransaction, globalTransaction, subtransaction, transaction, TransactionImpl } from './transaction.js';
30 > export { observableFromValueWithChangeEvent, ValueWithChangeEventFromObservable } from './utils/valueWithChangeEvent.js';
31 > export { runOnChange, runOnChangeWithCancellationToken, runOnChangeWithStore, type RemoveUndefined } from './utils/runOnChange.js';
32 > export { derivedConstOnceDefined, latestChangedValue } from './experimental/utils.js';
33 > export { observableFromEvent } from './observables/observableFromEvent.js';
34 > export { observableValue } from './observables/observableValue.js';
35 >
36 > export { ObservableSet } from './set.js';
37 > export { ObservableMap } from './map.js';
38 > export { DebugLocation } from './debugLocation.js';
39 >
40 > import { addLogger, setLogObservableFn } from './logging/logging.js';
41 > import { ConsoleObservableLogger, logObservableToConsole } from './logging/consoleObservableLogger.js';
42 > import { DevToolsLogger } from './logging/debugger/devToolsLogger.js';
43 > import { env } from '../process.js';
44 > import { _setDebugGetObservableGraph } from './observables/baseObservable.js';
45 > import { debugGetObservableGraph } from './logging/debugGetDependencyGraph.js';
46 >
47 > _setDebugGetObservableGraph(debugGetObservableGraph);
48 > setLogObservableFn(logObservableToConsole);
49 >
50 > // Remove "//" in the next line to enable logging
51 > const enableLogging = false
52 > // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this
53 > ;
54 >
55 > if (enableLogging) {
56 addLogger(new ConsoleObservableLogger());
57 }
58 > index.ts
59 > if (env && env['VSCODE_DEV_DEBUG_OBSERVABLES']) {
60 // To debug observables you also need the extension "ms-vscode.debug-value-editor"
61 addLogger(DevToolsLogger.getInstance());
src/vs/base/common/observableInternal/observables/derived.ts 57 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- derived.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 { IObservable, IReader, ITransaction, ISettableObservable, IObservableWithChange } from '../base.js';
7 > import { IChangeTracker } from '../changeTracker.js';
8 > import { DisposableStore, EqualityComparer, IDisposable, strictEquals } from '../commonFacade/deps.js';
9 > import { DebugLocation } from '../debugLocation.js';
10 > import { DebugOwner, DebugNameData, IDebugNameData } from '../debugName.js';
11 > import { _setDerivedOpts } from './baseObservable.js';
12 > import { IDerivedReader, Derived, DerivedWithSetter } from './derivedImpl.js';
13 >
14 > /**
15 > * Creates an observable that is derived from other observables.
16 > * The value is only recomputed when absolutely needed.
17 > *
18 > * {@link computeFn} should start with a JS Doc using `@description` to name the derived.
19 > */
20 > export function derived<T, TChange = void>(computeFn: (reader: IDerivedReader<TChange>, debugLocation?: DebugLocation) => T): IObservableWithChange<T, TChange>;
21 > export function derived<T, TChange = void>(owner: DebugOwner, computeFn: (reader: IDerivedReader<TChange>) => T, debugLocation?: DebugLocation): IObservableWithChange<T, TChange>;
22 > export function derived<T, TChange = void>(
23 computeFnOrOwner: ((reader: IDerivedReader<TChange>) => T) | DebugOwner,
24 computeFn?: ((reader: IDerivedReader<TChange>) => T) | undefined,
46 );
47 }
48 > derived.ts
49 > export function derivedWithSetter<T>(owner: DebugOwner | undefined, computeFn: (reader: IReader) => T, setter: (value: T, transaction: ITransaction | undefined) => void, debugLocation = DebugLocation.ofCaller()): ISettableObservable<T> {
50 return new DerivedWithSetter(
51 new DebugNameData(owner, undefined, computeFn),
58 );
59 }
60 > derived.ts
61 > export function derivedOpts<T>(
62 options: IDebugNameData & {
63 equalsFn?: EqualityComparer<T>;
76 );
77 }
78 > _setDerivedOpts(derivedOpts); derived.ts
79 >
80 > /**
81 > * Represents an observable that is derived from other observables.
82 > * The value is only recomputed when absolutely needed.
83 > *
84 > * {@link computeFn} should start with a JS Doc using `@description` to name the derived.
85 > *
86 > * Use `createEmptyChangeSummary` to create a "change summary" that can collect the changes.
87 > * Use `handleChange` to add a reported change to the change summary.
88 > * The compute function is given the last change summary.
89 > * The change summary is discarded after the compute function was called.
90 > *
91 > * @see derived
92 > */
93 > export function derivedHandleChanges<T, TDelta, TChangeSummary>(
94 options: IDebugNameData & {
95 changeTracker: IChangeTracker<TChangeSummary>;
108 );
109 }
110 > derived.ts
111 > /**
112 > * @deprecated Use `derived(reader => { reader.store.add(...) })` instead!
113 > */
114 > export function derivedWithStore<T>(computeFn: (reader: IReader, store: DisposableStore) => T): IObservable<T>;
115 >
116 > /**
117 > * @deprecated Use `derived(reader => { reader.store.add(...) })` instead!
118 > */
119 > export function derivedWithStore<T>(owner: DebugOwner, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable<T>;
120 > export function derivedWithStore<T>(computeFnOrOwner: ((reader: IReader, store: DisposableStore) => T) | DebugOwner, computeFnOrUndefined?: ((reader: IReader, store: DisposableStore) => T), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
121 let computeFn: (reader: IReader, store: DisposableStore) => T;
122 let owner: DebugOwner;
151 );
152 }
153 > derived.ts
154 > export function derivedDisposable<T extends IDisposable | undefined>(computeFn: (reader: IReader) => T): IObservable<T>;
155 > export function derivedDisposable<T extends IDisposable | undefined>(owner: DebugOwner, computeFn: (reader: IReader) => T): IObservable<T>;
156 > export function derivedDisposable<T extends IDisposable | undefined>(computeFnOrOwner: ((reader: IReader) => T) | DebugOwner, computeFnOrUndefined?: ((reader: IReader) => T), debugLocation = DebugLocation.ofCaller()): IObservable<T> {
157 let computeFn: (reader: IReader) => T;
158 let owner: DebugOwner;
src/vs/base/common/observableInternal/debugName.ts 56 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugName.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 IDebugNameData {
7 > /**
8 > * The owner object of an observable.
9 > * Used for debugging only, such as computing a name for the observable by iterating over the fields of the owner.
10 > */
11 > readonly owner?: DebugOwner | undefined;
12 >
13 > /**
14 > * A string or function that returns a string that represents the name of the observable.
15 > * Used for debugging only.
16 > */
17 > readonly debugName?: DebugNameSource | undefined;
18 >
19 > /**
20 > * A function that points to the defining function of the object.
21 > * Used for debugging only.
22 > */
23 > readonly debugReferenceFn?: Function | undefined;
24 > }
25 >
26 > export class DebugNameData {
27 > constructor(
28 > public readonly owner: DebugOwner | undefined, debugName.ts
29 > public readonly debugNameSource: DebugNameSource | undefined,
30 > public readonly referenceFn: Function | undefined,
31 > ) { }
33 > public getDebugName(target: object): string | undefined {
34 return getDebugName(target, this);
35 }
36 > } debugName.ts
37 >
38 > /**
39 > * The owning object of an observable.
40 > * Is only used for debugging purposes, such as computing a name for the observable by iterating over the fields of the owner.
41 > */
42 > export type DebugOwner = object | undefined;
43 > export type DebugNameSource = string | (() => string | undefined);
44 >
45 > const countPerName = new Map<string, number>();
46 > const cachedDebugName = new WeakMap<object, string>();
47 >
48 > export function getDebugName(target: object, data: DebugNameData): string | undefined {
49 const cached = cachedDebugName.get(target);
50 if (cached) {
63 return undefined;
64 }
66 function computeDebugName(self: object, data: DebugNameData): string | undefined {
67 const cached = cachedDebugName.get(self);
101 return undefined;
102 }
103 > debugName.ts
104 function findKey(obj: object, value: object): string | undefined {
105 for (const key in obj) {
110 return undefined;
111 }
112 > debugName.ts
113 > const countPerClassName = new Map<string, number>();
114 > const ownerId = new WeakMap<object, string>();
115 >
116 function formatOwner(owner: object): string {
117 const id = ownerId.get(owner);
127 return result;
128 }
129 > debugName.ts
130 > export function getClassName(obj: object): string | undefined {
131 const ctor = obj.constructor;
132 if (ctor) {
138 return undefined;
139 }
140 > debugName.ts
141 > export function getFunctionName(fn: Function): string | undefined {
142 const fnSrc = fn.toString();
143 // Pattern: /** @description ... */
src/vs/base/test/common/utils.ts 56 covered LOC · 13 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);
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/workbench/services/environment/common/environmentService.ts 56 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- environmentService.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 { refineServiceDecorator } from '../../../../platform/instantiation/common/instantiation.js';
7 > import { IPath } from '../../../../platform/window/common/window.js';
8 > import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
9 > import { URI } from '../../../../base/common/uri.js';
10 >
11 > export const IWorkbenchEnvironmentService = refineServiceDecorator<IEnvironmentService, IWorkbenchEnvironmentService>(IEnvironmentService);
12 >
13 > /**
14 > * A workbench specific environment service that is only present in workbench
15 > * layer.
16 > */
17 > export interface IWorkbenchEnvironmentService extends IEnvironmentService {
18 >
19 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20 > // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. AS SUCH:
21 > // PUT NON-WEB PROPERTIES INTO THE NATIVE WORKBENCH
22 > // ENVIRONMENT SERVICE
23 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
24 >
25 > // --- Paths
26 > readonly logFile: URI;
27 > readonly windowLogsPath: URI;
28 > readonly extHostLogsPath: URI;
29 >
30 > // --- Extensions
31 > readonly extensionEnabledProposedApi?: string[];
32 >
33 > // --- Config
34 > readonly remoteAuthority?: string;
35 > readonly skipReleaseNotes: boolean;
36 > readonly skipWelcome: boolean;
37 > readonly disableWorkspaceTrust: boolean;
38 > readonly isSessionsWindow: boolean;
39 > readonly webviewExternalEndpoint: string;
40 >
41 > // --- Development
42 > readonly debugRenderer: boolean;
43 > readonly logExtensionHostCommunication?: boolean;
44 > readonly enableSmokeTestDriver?: boolean;
45 > readonly profDurationMarkers?: string[];
46 >
47 > // --- Editors to open
48 > readonly filesToOpenOrCreate?: IPath[] | undefined;
49 > readonly filesToDiff?: IPath[] | undefined;
50 > readonly filesToMerge?: IPath[] | undefined;
51 >
52 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
53 > // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. AS SUCH:
54 > // - PUT NON-WEB PROPERTIES INTO NATIVE WB ENV SERVICE
55 > // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
56 > }
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/observableInternal/logging/debugger/rpc.ts 53 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- rpc.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 ChannelFactory = (handler: IChannelHandler) => IChannel;
7 >
8 > export interface IChannel {
9 > sendNotification(data: unknown): void;
10 > sendRequest(data: unknown): Promise<RpcRequestResult>;
11 > }
12 >
13 > export interface IChannelHandler {
14 > handleNotification(notificationData: unknown): void;
15 > handleRequest(requestData: unknown): Promise<RpcRequestResult> | RpcRequestResult;
16 > }
17 >
18 > export type RpcRequestResult = { type: 'result'; value: unknown } | { type: 'error'; value: unknown };
19 >
20 > export type API = {
21 > host: Side;
22 > client: Side;
23 > };
24 >
25 > export type Side = {
26 > notifications: Record<string, (...args: any[]) => void>;
27 > requests: Record<string, (...args: any[]) => Promise<unknown> | unknown>;
28 > };
29 >
30 > type MakeAsyncIfNot<TFn> = TFn extends (...args: infer TArgs) => infer TResult ? TResult extends Promise<unknown> ? TFn : (...args: TArgs) => Promise<TResult> : never;
31 >
32 > export type MakeSideAsync<T extends Side> = {
33 > notifications: T['notifications'];
34 > requests: { [K in keyof T['requests']]: MakeAsyncIfNot<T['requests'][K]> };
35 > };
36 >
37 > export class SimpleTypedRpcConnection<T extends Side> {
38 > public static createHost<T extends API>(channelFactory: ChannelFactory, getHandler: () => T['host']): SimpleTypedRpcConnection<MakeSideAsync<T['client']>> {
39 > return new SimpleTypedRpcConnection(channelFactory, getHandler);
40 > }
41 >
42 > public static createClient<T extends API>(channelFactory: ChannelFactory, getHandler: () => T['client']): SimpleTypedRpcConnection<MakeSideAsync<T['host']>> {
43 return new SimpleTypedRpcConnection(channelFactory, getHandler);
44 }
45 > rpc.ts
46 > public readonly api: T;
47 > private readonly _channel: IChannel;
48 >
49 > private constructor(
50 private readonly _channelFactory: ChannelFactory,
51 private readonly _getHandler: () => Side,
95 this.api = { notifications: notifications, requests: requests } as any;
96 }
97 > } rpc.ts
98 >
99 > type OutgoingMessage = [
100 > method: string,
101 > args: unknown[],
102 > ];
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/observableInternal/logging/consoleObservableLogger.ts 52 covered LOC · 20 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- consoleObservableLogger.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 { IObservable } from '../base.js';
7 > import { TransactionImpl } from '../transaction.js';
8 > import { IObservableLogger, IChangeInformation, addLogger } from './logging.js';
9 > import { FromEventObservable } from '../observables/observableFromEvent.js';
10 > import { getClassName } from '../debugName.js';
11 > import { Derived } from '../observables/derivedImpl.js';
12 > import { AutorunObserver } from '../reactions/autorunImpl.js';
13 >
14 > let consoleObservableLogger: ConsoleObservableLogger | undefined;
15 >
16 > export function logObservableToConsole(obs: IObservable<any>): void {
17 if (!consoleObservableLogger) {
18 consoleObservableLogger = new ConsoleObservableLogger();
21 consoleObservableLogger.addFilteredObj(obs);
22 }
24 > export class ConsoleObservableLogger implements IObservableLogger {
25 private indentation = 0;
26
118
119 private readonly changedObservablesSets = new WeakMap<object, Set<IObservable<any>>>();
121 > formatChanges(changes: Set<IObservable<any>>): ConsoleText | undefined {
122 if (changes.size === 0) {
123 return undefined;
130 );
131 }
133 > handleDerivedDependencyChanged(derived: Derived<any>, observable: IObservable<any>, change: unknown): void {
134 if (!this._isIncluded(derived)) { return; }
135
136 this.changedObservablesSets.get(derived)?.add(observable);
137 }
139 > _handleDerivedRecomputed(derived: Derived<unknown>, info: IChangeInformation): void {
140 if (!this._isIncluded(derived)) { return; }
141
151 changedObservables.clear();
152 }
154 > handleDerivedCleared(derived: Derived<unknown>): void {
155 if (!this._isIncluded(derived)) { return; }
156
160 ]));
161 }
163 > handleFromEventObservableTriggered(observable: FromEventObservable<any, any>, info: IChangeInformation): void {
164 if (!this._isIncluded(observable)) { return; }
165
171 ]));
172 }
174 > handleAutorunCreated(autorun: AutorunObserver): void {
175 if (!this._isIncluded(autorun)) { return; }
176
177 this.changedObservablesSets.set(autorun, new Set());
178 }
180 > handleAutorunDisposed(autorun: AutorunObserver): void {
181 }
183 > handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable<any>, change: unknown): void {
184 if (!this._isIncluded(autorun)) { return; }
185
186 this.changedObservablesSets.get(autorun)!.add(observable);
187 }
189 > handleAutorunStarted(autorun: AutorunObserver): void {
190 const changedObservables = this.changedObservablesSets.get(autorun);
191 if (!changedObservables) { return; }
202 this.indentation++;
203 }
205 > handleAutorunFinished(autorun: AutorunObserver): void {
206 this.indentation--;
207 }
209 > handleBeginTransaction(transaction: TransactionImpl): void {
210 let transactionName = transaction.getDebugName();
211 if (transactionName === undefined) {
221 this.indentation++;
222 }
224 > handleEndTransaction(): void {
225 this.indentation--;
226 }
228 > type ConsoleText = (ConsoleText | undefined)[] |
229 > { text: string; style: string; data?: unknown[] } |
230 > { data: unknown[] };
231 function consoleTextToArgs(text: ConsoleText): unknown[] {
232 const styles = new Array<any>();
294 };
295 }
297 > export function formatValue(value: unknown, availableLen: number): string {
298 switch (typeof value) {
299 case 'number':
325 }
326 }
328 function formatArray(value: unknown[], availableLen: number): string {
329 let result = '[ ';
343 return result;
344 }
346 function formatObject(value: object, availableLen: number): string {
347 if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
371 return result;
372 }
374 function repeat(str: string, count: number): string {
375 let result = '';
379 return result;
380 }
382 function padStr(str: string, length: number): string {
383 while (str.length < length) {
src/vs/platform/remote/common/remoteHosts.ts 52 covered LOC · 7 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- remoteHosts.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 { Schemas } from '../../../base/common/network.js';
7 > import { URI } from '../../../base/common/uri.js';
8 >
9 > export function getRemoteAuthority(uri: URI): string | undefined {
10 return uri.scheme === Schemas.vscodeRemote ? uri.authority : undefined;
11 }
13 > export function getRemoteName(authority: string): string;
14 > export function getRemoteName(authority: undefined): undefined;
15 > export function getRemoteName(authority: string | undefined): string | undefined;
16 > export function getRemoteName(authority: string | undefined): string | undefined {
17 if (!authority) {
18 return undefined;
25 return authority.substr(0, pos);
26 }
28 > /**
29 > * Returns the suffix part of the authority after the '+' character.
30 > * For remote connections, this is typically the server/tunnel identifier.
31 > * Examples:
32 > * - For tunnels: `tunnel+myTunnel` returns `myTunnel`
33 > * - For SSH: `ssh+myserver` returns `myserver`
34 > * - For localhost: `localhost:8000` returns `undefined`
35 > * @param authority The remote authority string.
36 > * @returns The suffix after the '+' character, or undefined if there is no '+' character.
37 > */
38 > export function getRemoteServerRootPath(authority: string): string | undefined;
39 > export function getRemoteServerRootPath(authority: undefined): undefined;
40 > export function getRemoteServerRootPath(authority: string | undefined): string | undefined;
41 > export function getRemoteServerRootPath(authority: string | undefined): string | undefined {
42 if (!authority) {
43 return undefined;
49 return authority.substring(pos + 1);
50 }
52 > export function parseAuthorityWithPort(authority: string): { host: string; port: number } {
53 const { host, port } = parseAuthority(authority);
54 if (typeof port === 'undefined') {
57 return { host, port };
58 }
60 > export function parseAuthorityWithOptionalPort(authority: string, defaultPort: number): { host: string; port: number } {
61 let { host, port } = parseAuthority(authority);
62 if (typeof port === 'undefined') {
65 return { host, port };
66 }
68 function parseAuthority(authority: string): { host: string; port: number | undefined } {
69 // check for ipv6 with port
88 return { host: authority, port: undefined };
89 }
91 > const loopbackHosts = new Set([
92 > 'localhost',
93 > '127.0.0.1',
94 > '::1',
95 > '[::1]',
96 > '0000:0000:0000:0000:0000:0000:0000:0001',
97 > '[0000:0000:0000:0000:0000:0000:0000:0001]'
98 > ]);
99 >
100 > /**
101 > * Returns whether the given host (as found in a direct `<host>:<port>` remote
102 > * authority) refers to the local loopback interface. The check is intentionally
103 > * strict: only `localhost` and the IPv4/IPv6 loopback literals are considered
104 > * local. Any other host (a routable IP address or a hostname) is treated as a
105 > * connection that leaves the local machine.
106 > */
107 > export function isLoopbackHost(host: string): boolean {
108 return loopbackHosts.has(host.toLowerCase());
109 }
src/vs/base/common/observableInternal/observables/observableFromEvent.ts 51 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableFromEvent.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 { IObservable, ITransaction } from '../base.js';
7 > import { subtransaction } from '../transaction.js';
8 > import { EqualityComparer, Event, IDisposable, strictEquals } from '../commonFacade/deps.js';
9 > import { DebugOwner, DebugNameData, IDebugNameData } from '../debugName.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { BaseObservable } from './baseObservable.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 >
15 > export function observableFromEvent<T, TArgs = unknown>(
16 > owner: DebugOwner,
17 > event: Event<TArgs>,
18 > getValue: (args: TArgs | undefined) => T,
19 > debugLocation?: DebugLocation,
20 > ): IObservable<T>;
21 > export function observableFromEvent<T, TArgs = unknown>(
22 > event: Event<TArgs>,
23 > getValue: (args: TArgs | undefined) => T,
24 > ): IObservable<T>;
25 > export function observableFromEvent(...args:
26 [owner: DebugOwner, event: Event<any>, getValue: (args: any | undefined) => any, debugLocation?: DebugLocation] |
27 [event: Event<any>, getValue: (args: any | undefined) => any]
45 );
46 }
48 > export function observableFromEventOpts<T, TArgs = unknown>(
49 options: IDebugNameData & {
50 equalsFn?: EqualityComparer<T>;
64 );
65 }
67 > export class FromEventObservable<TArgs, T> extends BaseObservable<T> {
68 > public static globalTransaction: ITransaction | undefined;
69 >
70 > private _value: T | undefined;
71 > private _hasValue = false;
72 > private _subscription: IDisposable | undefined;
73 >
74 > constructor(
75 private readonly _debugNameData: DebugNameData,
76 private readonly event: Event<TArgs>,
131 }
132 };
134 > protected override onLastObserverRemoved(): void {
135 this._subscription!.dispose();
136 this._subscription = undefined;
138 this._value = undefined;
139 }
141 > public get(): T {
142 if (this._subscription) {
143 if (!this._hasValue) {
151 }
152 }
154 > public debugSetValue(value: unknown): void {
155 // eslint-disable-next-line local/code-no-any-casts
156 this._value = value as any;
157 }
159 > public debugGetState() {
160 return { value: this._value, hasValue: this._hasValue };
161 }
163 >
164 > export namespace observableFromEvent {
165 > export const Observer = FromEventObservable;
166 >
167 > export function batchEventsGlobally(tx: ITransaction, fn: () => void): void {
168 let didSet = false;
169 if (FromEventObservable.globalTransaction === undefined) {
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/base/common/observableInternal/observables/lazyObservableValue.ts 49 covered LOC · 11 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lazyObservableValue.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 { EqualityComparer } from '../commonFacade/deps.js';
7 > import { IObserver, ISettableObservable, ITransaction } from '../base.js';
8 > import { TransactionImpl } from '../transaction.js';
9 > import { DebugNameData } from '../debugName.js';
10 > import { getLogger } from '../logging/logging.js';
11 > import { BaseObservable } from './baseObservable.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Holds off updating observers until the value is actually read.
16 > */
17 > export class LazyObservableValue<T, TChange = void>
18 > extends BaseObservable<T, TChange>
19 > implements ISettableObservable<T, TChange> {
20 > protected _value: T;
21 > private _isUpToDate = true;
22 > private readonly _deltas: TChange[] = [];
23 >
24 > get debugName() {
25 > return this._debugNameData.getDebugName(this) ?? 'LazyObservableValue';
26 > }
27 >
28 > constructor(
29 private readonly _debugNameData: DebugNameData,
30 initialValue: T,
35 this._value = initialValue;
36 }
38 > public override get(): T {
39 this._update();
40 return this._value;
41 }
43 > private _update(): void {
44 if (this._isUpToDate) {
45 return;
62 }
63 }
65 > private _updateCounter = 0;
66 >
67 > private _beginUpdate(): void {
68 this._updateCounter++;
69 if (this._updateCounter === 1) {
73 }
74 }
76 > private _endUpdate(): void {
77 this._updateCounter--;
78 if (this._updateCounter === 0) {
86 }
87 }
89 > public override addObserver(observer: IObserver): void {
90 const shouldCallBeginUpdate = !this._observers.has(observer) && this._updateCounter > 0;
91 super.addObserver(observer);
95 }
96 }
98 > public override removeObserver(observer: IObserver): void {
99 const shouldCallEndUpdate = this._observers.has(observer) && this._updateCounter > 0;
100 super.removeObserver(observer);
105 }
106 }
108 > public set(value: T, tx: ITransaction | undefined, change: TChange): void {
109 if (change === undefined && this._equalityComparator(this._value, value)) {
110 return;
src/vs/base/common/linkedList.ts 48 covered LOC · 14 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> {
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/test/common/virtualScheduling/timeApi.ts 48 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- timeApi.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 TimeoutId { readonly _timeoutIdBrand: void }
7 > export interface IntervalId { readonly _intervalIdBrand: void }
8 > export interface ImmediateId { readonly _immediateIdBrand: void }
9 > export type AnimationFrameId = number & { readonly _animationFrameIdBrand: void };
10 >
11 > /**
12 > * The subset of host time APIs the processor and embeddings need.
13 > *
14 > * Used both for the real host API (captured via {@link captureGlobalTimeApi})
15 > * and for the virtual replacement that runs through a {@link VirtualClock}.
16 > *
17 > * Keeping this as a plain interface means the processor never reaches into
18 > * `globalThis` directly: the boundary between "real time" and "virtual time"
19 > * is exactly which `TimeApi` instance is in use.
20 > */
21 > export interface TimeApi {
22 > setTimeout(handler: () => void, timeout?: number): TimeoutId;
23 > clearTimeout(id: TimeoutId): void;
24 > setInterval(handler: () => void, interval: number): IntervalId;
25 > clearInterval(id: IntervalId): void;
26 > setImmediate?: ((handler: () => void) => ImmediateId);
27 > clearImmediate?: ((id: ImmediateId) => void);
28 > requestAnimationFrame?: ((cb: (time: number) => void) => AnimationFrameId);
29 > cancelAnimationFrame?: ((id: AnimationFrameId) => void);
30 > Date: DateConstructor;
31 > }
32 >
33 > export function captureGlobalTimeApi(): TimeApi {
34 > return {
35 > setTimeout: globalThis.setTimeout.bind(globalThis) as unknown as TimeApi['setTimeout'],
36 > clearTimeout: globalThis.clearTimeout.bind(globalThis) as unknown as TimeApi['clearTimeout'],
37 > setInterval: globalThis.setInterval.bind(globalThis) as unknown as TimeApi['setInterval'],
38 > clearInterval: globalThis.clearInterval.bind(globalThis) as unknown as TimeApi['clearInterval'],
39 > setImmediate: globalThis.setImmediate?.bind(globalThis) as unknown as TimeApi['setImmediate'],
40 > clearImmediate: globalThis.clearImmediate?.bind(globalThis) as unknown as TimeApi['clearImmediate'],
41 > requestAnimationFrame: globalThis.requestAnimationFrame?.bind(globalThis) as unknown as TimeApi['requestAnimationFrame'],
42 > cancelAnimationFrame: globalThis.cancelAnimationFrame?.bind(globalThis) as unknown as TimeApi['cancelAnimationFrame'],
43 > Date: globalThis.Date,
44 > };
45 > }
46 >
47 > /** A snapshot of the real host time API at module-load time. */
48 > export const realTimeApi: TimeApi = captureGlobalTimeApi();
src/vs/base/test/common/virtualScheduling/traceLogger.ts 47 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- traceLogger.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 { LogEntryLike } from '../executionGraph.js';
7 > import { Trace, TraceContext } from './trace.js';
8 >
9 > /**
10 > * A minimal logger for tests that captures the active {@link Trace} at log
11 > * time so messages can later be woven into a swimlane diagram next to the
12 > * timer events that produced them.
13 > */
14 > export interface ITraceLogger {
15 > log(message: string): void;
16 > warn(message: string): void;
17 > error(message: string): void;
18 > /**
19 > * Run `fn` synchronously and log it as a marker in the trace. The
20 > * marker text is `fn.toString()` so call sites read naturally as e.g.
21 > * `logger.logRun(() => model.trigger())`. Returns whatever `fn`
22 > * returns.
23 > */
24 > logRun<T>(fn: () => T): T;
25 > }
26 >
27 > /**
28 > * One log entry produced by an {@link ITraceLogger}. Extends
29 > * {@link LogEntryLike} (consumed by `buildHistoryFromTasks`) with a level so
30 > * renderers can differentiate `log` / `warn` / `error`.
31 > */
32 > export interface ITraceLogEntry extends LogEntryLike {
33 > readonly trace: Trace;
34 > readonly level: 'log' | 'warn' | 'error';
35 > }
36 >
37 > /**
38 > * Build an {@link ITraceLogger} that pushes every call into `buffer`,
39 > * tagging each entry with the trace that's current at call time.
40 > *
41 > * Pass the same `buffer` to `buildHistoryFromTasks(history, startTime,
42 > * buffer)` to interleave log lines with the timer swimlane.
43 > */
44 > export function createTraceLogger(buffer: ITraceLogEntry[]): ITraceLogger {
45 const make = (level: 'log' | 'warn' | 'error') => (message: string) => {
46 buffer.push({
61 };
62 }
64 > /** Best-effort one-line description of `fn` for trace log markers. */
65 function _describeFn(fn: () => unknown): string {
66 const src = fn.toString();
72 return _collapseWhitespace(src);
73 }
75 function _collapseWhitespace(s: string): string {
76 return s.replace(/\s+/g, ' ').trim();
src/vs/base/common/observableInternal/debugLocation.ts 46 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debugLocation.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 DebugLocation = DebugLocationImpl | undefined;
7 >
8 > export namespace DebugLocation {
9 > let enabled = false;
10 >
11 > export function enable(): void {
12 enabled = true;
13 }
15 > export function ofCaller(): DebugLocation {
16 > if (!enabled) { debugLocation.ts
17 > return undefined;
18 > }
19 const Err = Error as ErrorConstructor & { stackTraceLimit: number };
20
25
26 return DebugLocationImpl.fromStack(stack, 2);
29 >
30 > class DebugLocationImpl implements ILocation {
31 > public static fromStack(stack: string, parentIdx: number): DebugLocationImpl | undefined {
32 > const lines = stack.split('\n');
33 > const location = parseLine(lines[parentIdx + 1]);
34 > if (location) {
35 > return new DebugLocationImpl(
36 > location.fileName,
37 > location.line,
38 > location.column,
39 > location.id
40 > );
41 > } else {
42 > return undefined;
43 > }
44 > }
45 >
46 > constructor(
47 public readonly fileName: string,
48 public readonly line: number,
51 ) {
52 }
54 >
55 >
56 > export interface ILocation {
57 > fileName: string;
58 > line: number;
59 > column: number;
60 > id: string;
61 > }
62 >
63 function parseLine(stackLine: string): ILocation | undefined {
64 const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
src/vs/platform/uriIdentity/common/uriIdentity.ts 46 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- uriIdentity.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 '../../../base/common/uri.js';
7 > import { createDecorator } from '../../instantiation/common/instantiation.js';
8 > import { IExtUri } from '../../../base/common/resources.js';
9 >
10 >
11 > export const IUriIdentityService = createDecorator<IUriIdentityService>('IUriIdentityService');
12 >
13 > export interface IUriIdentityService {
14 >
15 > readonly _serviceBrand: undefined;
16 >
17 > /**
18 > * Uri extensions that are aware of casing.
19 > */
20 > readonly extUri: IExtUri;
21 >
22 > /**
23 > * Returns a canonical uri for the given resource. Different uris can point to the same
24 > * resource. That's because of casing or missing normalization, e.g the following uris
25 > * are different but refer to the same document (because windows paths are not case-sensitive)
26 > *
27 > * ```txt
28 > * file:///c:/foo/bar.txt
29 > * file:///c:/FOO/BAR.txt
30 > * ```
31 > *
32 > * This function should be invoked when feeding uris into the system that represent the truth,
33 > * e.g document uris or marker-to-document associations etc. This function should NOT be called
34 > * to pretty print a label nor to sanitize a uri.
35 > *
36 > * Samples:
37 > *
38 > * | in | out | |
39 > * |---|---|---|
40 > * | `file:///foo/bar/../bar` | `file:///foo/bar` | n/a |
41 > * | `file:///foo/bar/../bar#frag` | `file:///foo/bar#frag` | keep fragment |
42 > * | `file:///foo/BAR` | `file:///foo/bar` | assume ignore case |
43 > * | `file:///foo/bar/../BAR?q=2` | `file:///foo/BAR?q=2` | query makes it a different document |
44 > */
45 > asCanonicalUri(uri: URI): URI;
46 > }
src/vs/base/test/common/virtualScheduling/virtualTimeApi.ts 45 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- virtualTimeApi.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 { IDisposable } from '../../../common/lifecycle.js';
7 > import { realTimeApi, TimeApi } from './timeApi.js';
8 > import { ROOT_TRACE, TraceContext } from './trace.js';
9 > import { VirtualClock } from './virtualClock.js';
10 >
11 > // V8 default `Error.stackTraceLimit` of 10 swallows everything past the
12 > // first async boundary in the stacks we capture for trace diagnostics.
13 > // Bump it so swimlane callers actually see the user code that scheduled a
14 > // timer rather than just the Promise wrapper.
15 > if (typeof Error.stackTraceLimit === 'number' && Error.stackTraceLimit < 50) {
16 > Error.stackTraceLimit = 50;
17 > }
18 >
19 > /** Virtual timer IDs are `IDisposable`s. Recover one from an opaque id. */
20 function asDisposable(id: unknown): IDisposable | undefined {
21 if (id === null || typeof id !== 'object') { return undefined; }
23 return typeof maybe.dispose === 'function' ? id as IDisposable : undefined;
24 }
26 > export interface CreateVirtualTimeApiOptions {
27 > /**
28 > * If `true`, `requestAnimationFrame` is faked: callbacks are scheduled
29 > * onto the virtual queue at `now + 16ms` and the resulting event hints
30 > * the embedding to use a real `requestAnimationFrame` so the host can
31 > * reflow before the callback runs. Useful for fixtures that need DOM
32 > * measurements after rAF callbacks.
33 > *
34 > * If `false` (default), `requestAnimationFrame` is left to the host.
35 > */
36 > readonly fakeRequestAnimationFrame?: boolean;
37 > }
38 >
39 > /**
40 > * Build a {@link TimeApi} that schedules every timer call into `clock`'s
41 > * virtual queue, capturing the current trace at schedule time so that
42 > * causal chains (`setTimeout` → `setTimeout`, etc.) are preserved.
43 > *
44 > * The returned API is suitable to install with {@link pushGlobalTimeApi},
45 > * which is what {@link runWithFakedTimers} does internally.
46 > */
47 > export function createVirtualTimeApi(
48 clock: VirtualClock,
49 options?: CreateVirtualTimeApiOptions,
190 return api;
191 }
193 > // Re-exported for convenience: many tests want to install both at once.
194 > export { pushGlobalTimeApi } from './globalTimeApi.js';
src/vs/base/test/common/virtualScheduling/runWithFakedTimers.ts 44 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- runWithFakedTimers.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 { CancellationTokenSource } from '../../../common/cancellation.js';
7 > import { drainMicrotasksEmbedding } from './embedding.js';
8 > import { pushGlobalTimeApi } from './globalTimeApi.js';
9 > import { realTimeApi } from './timeApi.js';
10 > import { untilToken, VirtualTimeProcessor } from './processor.js';
11 > import { createRecordingRealTimeApi, RecordedTimerEvent } from './recordingTimeApi.js';
12 > import { VirtualClock } from './virtualClock.js';
13 > import { createVirtualTimeApi } from './virtualTimeApi.js';
14 >
15 > export interface RunWithFakedTimersOptions {
16 > readonly startTime?: number;
17 > /** Default `true`. Set `false` to bypass virtual time entirely (for
18 > * cases where the same test is parameterised over real/virtual time). */
19 > readonly useFakeTimers?: boolean;
20 > /** No effect in the new processor; accepted for legacy compatibility.
21 > * The drain-microtasks embedding picks the fastest available macrotask
22 > * primitive automatically. */
23 > readonly useSetImmediate?: boolean;
24 > /** Maximum number of virtual events the run is allowed to execute
25 > * before being rejected. Default 100. */
26 > readonly maxTaskCount?: number;
27 > /**
28 > * If set, called once `fn` resolves with the recorded timer events.
29 > * In virtual mode the events come from the {@link VirtualTimeProcessor}'s
30 > * own history; in real mode a recording wrapper around the host time
31 > * API is installed for the duration of `fn`. Useful for swimlane
32 > * diagnostics.
33 > */
34 > readonly onHistory?: (history: readonly RecordedTimerEvent[]) => void;
35 > }
36 >
37 > /**
38 > * Run `fn` with a virtual clock installed as the global time API.
39 > *
40 > * After `fn` resolves, the virtual queue is drained (so any timers `fn`
41 > * scheduled and `await`ed for, transitively, complete deterministically).
42 > * If `fn` throws, the queue is *not* drained — the original error is
43 > * re-thrown immediately.
44 > */
45 export async function runWithFakedTimers<T>(
46 options: RunWithFakedTimersOptions,
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/test/common/virtualScheduling/globalTimeApi.ts 42 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- globalTimeApi.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 { IDisposable } from '../../../common/lifecycle.js';
7 > import { captureGlobalTimeApi, realTimeApi, TimeApi } from './timeApi.js';
8 >
9 > /** Cast through `unknown` so we don't widen our typed `TimeApi` shapes to `any`. */
10 > type AsGlobal<K extends keyof typeof globalThis> = (typeof globalThis)[K];
11 >
12 > /**
13 > * Ensure `fn` carries an `originalFn` back-door pointing at the real
14 > * (non-virtual) `setTimeout`. We prefer the existing tag on `fn`, then a tag
15 > * inherited from `previousFn` (which may itself be a wrapper that already
16 > * carried the back-door), and finally fall back to `realTimeApi.setTimeout`
17 > * — which has its own `originalFn` set at module load.
18 > */
19 function ensureSetTimeoutOriginalFn(fn: TimeApi['setTimeout'], previousFn: TimeApi['setTimeout']): TimeApi['setTimeout'] {
20 const tagged = fn as TimeApi['setTimeout'] & { originalFn?: TimeApi['setTimeout'] };
26 return tagged;
27 }
29 > /**
30 > * Replace the global time APIs (`setTimeout`, `setInterval`, …, `Date`,
31 > * optionally `requestAnimationFrame`) with the ones from `api`. Returns a
32 > * disposable that restores the previous globals.
33 > *
34 > * The previous globals are captured *at install time*, so nested installs
35 > * compose correctly (the disposable restores to whatever was current when
36 > * this call was made, not to the original real values).
37 > *
38 > * `setTimeout.originalFn` is preserved on the installed function so callers
39 > * like the component-explorer host can escape virtual time when polling.
40 > * If `api.setTimeout` does not already carry `originalFn`, it is copied from
41 > * the previous global (or defaulted to the real `setTimeout`) so wrapping
42 > * APIs such as a logging wrapper don't drop the back-door.
43 > */
44 > export function pushGlobalTimeApi(api: TimeApi): IDisposable {
45 const previous = captureGlobalTimeApi();
46
74 };
75 }
77 > // One-shot tag on the *real* setTimeout: lets callers (e.g. the
78 > // component-explorer host's polling loop) escape virtual time even after
79 > // pushGlobalTimeApi has installed a virtual version on top. The `originalFn`
80 > // property is not on the `setTimeout` signature by design — it's a back-door
81 > // convention shared with the polling code.
82 > (realTimeApi.setTimeout as unknown as { originalFn: TimeApi['setTimeout'] }).originalFn = realTimeApi.setTimeout;
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/base/common/observableInternal/transaction.ts 39 covered LOC · 10 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- transaction.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 { handleBugIndicatingErrorRecovery, IObservable, IObserver, ITransaction } from './base.js';
7 > import { getFunctionName } from './debugName.js';
8 > import { getLogger } from './logging/logging.js';
9 >
10 > /**
11 > * Starts a transaction in which many observables can be changed at once.
12 > * {@link fn} should start with a JS Doc using `@description` to give the transaction a debug name.
13 > * Reaction run on demand or when the transaction ends.
14 > */
15 >
16 > export function transaction(fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
17 const tx = new TransactionImpl(fn, getDebugName);
18 try {
22 }
23 }
24 > let _globalTransaction: ITransaction | undefined = undefined; transaction.ts
25 >
26 > export function globalTransaction(fn: (tx: ITransaction) => void) {
27 if (_globalTransaction) {
28 fn(_globalTransaction);
40 }
41 }
42 > /** @deprecated */ transaction.ts
43 >
44 export async function asyncTransaction(fn: (tx: ITransaction) => Promise<void>, getDebugName?: () => string): Promise<void> {
45 const tx = new TransactionImpl(fn, getDebugName);
50 }
51 }
52 > /** transaction.ts
53 > * Allows to chain transactions.
54 > */
55 >
56 > export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransaction) => void, getDebugName?: () => string): void {
57 if (!tx) {
58 transaction(fn, getDebugName);
60 fn(tx);
61 }
62 > } export class TransactionImpl implements ITransaction { transaction.ts
63 > private _updatingObservers: { observer: IObserver; observable: IObservable<any> }[] | null = [];
64 >
65 > constructor(public readonly _fn: Function, private readonly _getDebugName?: () => string) {
66 getLogger()?.handleBeginTransaction(this);
67 }
69 > public getDebugName(): string | undefined {
70 if (this._getDebugName) {
71 return this._getDebugName();
73 return getFunctionName(this._fn);
74 }
76 > public updateObserver(observer: IObserver, observable: IObservable<any>): void {
77 if (!this._updatingObservers) {
78 // This happens when a transaction is used in a callback or async function.
90 observer.beginUpdate(observable);
91 }
93 > public finish(): void {
94 const updatingObservers = this._updatingObservers;
95 if (!updatingObservers) {
106 getLogger()?.handleEndTransaction(this);
107 }
109 > public debugGetUpdatingObservers() {
110 return this._updatingObservers;
111 }
112 > } transaction.ts
113
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/base/common/observableInternal/observables/observableSignalFromEvent.ts 38 covered LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableSignalFromEvent.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 { IObservable } from '../base.js';
7 > import { transaction } from '../transaction.js';
8 > import { Event, IDisposable } from '../commonFacade/deps.js';
9 > import { DebugOwner, DebugNameData } from '../debugName.js';
10 > import { BaseObservable } from './baseObservable.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 >
13 > export function observableSignalFromEvent(
14 owner: DebugOwner | string,
15 event: Event<any>,
18 return new FromEventObservableSignal(typeof owner === 'string' ? owner : new DebugNameData(owner, undefined, undefined), event, debugLocation);
19 }
21 > class FromEventObservableSignal extends BaseObservable<void> {
22 > private subscription: IDisposable | undefined;
23 >
24 > public readonly debugName: string;
25 > constructor(
26 debugNameDataOrName: DebugNameData | string,
27 private readonly event: Event<any>,
33 : debugNameDataOrName.getDebugName(this) ?? 'Observable Signal From Event';
34 }
36 > protected override onFirstObserverAdded(): void {
37 this.subscription = this.event(this.handleEvent);
38 }
40 > private readonly handleEvent = () => {
41 > transaction( observableSignalFromEvent.ts
42 > (tx) => {
43 > for (const o of this._observers) {
44 > tx.updateObserver(o, this);
45 > o.handleChange(this, undefined);
46 > }
47 > },
48 > () => this.debugName
49 > );
50 > };
52 > protected override onLastObserverRemoved(): void {
53 this.subscription!.dispose();
54 this.subscription = undefined;
55 }
57 > public override get(): void {
58 // NO OP
59 }
src/vs/base/common/observableInternal/observables/observableSignal.ts 37 covered LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableSignal.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 { IObservableWithChange, ITransaction } from '../base.js';
7 > import { transaction } from '../transaction.js';
8 > import { DebugNameData } from '../debugName.js';
9 > import { BaseObservable } from './baseObservable.js';
10 > import { DebugLocation } from '../debugLocation.js';
11 >
12 > /**
13 > * Creates a signal that can be triggered to invalidate observers.
14 > * Signals don't have a value - when they are triggered they indicate a change.
15 > * However, signals can carry a delta that is passed to observers.
16 > */
17 > export function observableSignal<TDelta = void>(debugName: string): IObservableSignal<TDelta>;
18 > export function observableSignal<TDelta = void>(owner: object): IObservableSignal<TDelta>;
19 > export function observableSignal<TDelta = void>(debugNameOrOwner: string | object, debugLocation = DebugLocation.ofCaller()): IObservableSignal<TDelta> {
20 if (typeof debugNameOrOwner === 'string') {
21 return new ObservableSignal<TDelta>(debugNameOrOwner, undefined, debugLocation);
24 }
25 }
27 > export interface IObservableSignal<TChange> extends IObservableWithChange<void, TChange> {
28 > trigger(tx: ITransaction | undefined, change: TChange): void;
29 > }
30 >
31 > class ObservableSignal<TChange> extends BaseObservable<void, TChange> implements IObservableSignal<TChange> {
32 > public get debugName() {
33 > return new DebugNameData(this._owner, this._debugName, undefined).getDebugName(this) ?? 'Observable Signal';
34 > }
35 >
36 > public override toString(): string {
37 return this.debugName;
38 }
40 > constructor(
41 private readonly _debugName: string | undefined,
42 private readonly _owner: object | undefined,
45 super(debugLocation);
46 }
48 > public trigger(tx: ITransaction | undefined, change: TChange): void {
49 if (!tx) {
50 transaction(tx => {
src/vs/base/test/common/virtualScheduling/index.ts 36 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- index.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 > // Greenfield virtual scheduling primitives.
7 > //
8 > // This folder is the new home for virtual-time scheduling. It supersedes
9 > // `timeTravelScheduler.ts` and `traceableTimeApi.ts`, both of which are
10 > // retained as @deprecated re-export shims.
11 >
12 > export type { TimeApi } from './timeApi.js';
13 > export { captureGlobalTimeApi, realTimeApi } from './timeApi.js';
14 >
15 > export type { EventSource, VirtualEvent, VirtualTime } from './virtualClock.js';
16 > export { VirtualClock } from './virtualClock.js';
17 >
18 > export type { RunAsHandlerOptions } from './trace.js';
19 > export { ROOT_TRACE, Trace, TraceContext, createTraceRoot } from './trace.js';
20 >
21 > export type { Embedding } from './embedding.js';
22 > export { drainMicrotasksEmbedding, nextMacrotask, syncEmbedding } from './embedding.js';
23 >
24 > export type { RunOptions, TerminationPolicy, VirtualTimeProcessorOptions } from './processor.js';
25 > export { VirtualTimeProcessor, untilIdle, untilTime, untilToken } from './processor.js';
26 >
27 > export { pushGlobalTimeApi } from './globalTimeApi.js';
28 > export type { CreateVirtualTimeApiOptions } from './virtualTimeApi.js';
29 > export { createVirtualTimeApi } from './virtualTimeApi.js';
30 > export { createLoggingTimeApi } from './loggingTimeApi.js';
31 > export type { RecordedTimerEvent } from './recordingTimeApi.js';
32 > export { createRecordingRealTimeApi } from './recordingTimeApi.js';
33 > export type { ITraceLogEntry, ITraceLogger } from './traceLogger.js';
34 > export { createTraceLogger } from './traceLogger.js';
35 > export type { RunWithFakedTimersOptions } from './runWithFakedTimers.js';
36 > export { runWithFakedTimers } from './runWithFakedTimers.js';
src/vs/platform/extensionManagement/common/implicitActivationEvents.ts 36 covered LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- implicitActivationEvents.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 { onUnexpectedError } from '../../../base/common/errors.js';
8 > import { ExtensionIdentifier, IExtensionDescription } from '../../extensions/common/extensions.js';
9 >
10 > export interface IActivationEventsGenerator<T> {
11 > (contributions: readonly T[]): Iterable<string>;
12 > }
13 >
14 > export class ImplicitActivationEventsImpl {
15 >
16 > private readonly _generators = new Map<string, IActivationEventsGenerator<unknown>>();
17 > private readonly _cache = new WeakMap<IExtensionDescription, string[]>();
18 >
19 > public register<T>(extensionPointName: string, generator: IActivationEventsGenerator<T>): void {
20 this._generators.set(extensionPointName, generator as IActivationEventsGenerator<unknown>);
21 }
23 > /**
24 > * This can run correctly only on the renderer process because that is the only place
25 > * where all extension points and all implicit activation events generators are known.
26 > */
27 > public readActivationEvents(extensionDescription: IExtensionDescription): string[] {
28 if (!this._cache.has(extensionDescription)) {
29 this._cache.set(extensionDescription, this._readActivationEvents(extensionDescription));
31 return this._cache.get(extensionDescription)!;
32 }
34 > /**
35 > * This can run correctly only on the renderer process because that is the only place
36 > * where all extension points and all implicit activation events generators are known.
37 > */
38 > public createActivationEventsMap(extensionDescriptions: IExtensionDescription[]): { [extensionId: string]: string[] } {
39 const result: { [extensionId: string]: string[] } = Object.create(null);
40 for (const extensionDescription of extensionDescriptions) {
46 return result;
47 }
49 > private _readActivationEvents(desc: IExtensionDescription): string[] {
50 if (typeof desc.main === 'undefined' && typeof desc.browser === 'undefined') {
51 return [];
83 return activationEvents;
84 }
86 >
87 > export const ImplicitActivationEvents: ImplicitActivationEventsImpl = new ImplicitActivationEventsImpl();
src/vs/base/common/observableInternal/changeTracker.ts 35 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- changeTracker.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 } from './commonFacade/deps.js';
7 > import { IObservableWithChange, IReader } from './base.js';
8 >
9 > export interface IChangeTracker<TChangeSummary> {
10 > createChangeSummary(previousChangeSummary: TChangeSummary | undefined): TChangeSummary;
11 > handleChange(ctx: IChangeContext, change: TChangeSummary): boolean;
12 > beforeUpdate?(reader: IReader, change: TChangeSummary): void;
13 > }
14 >
15 > export interface IChangeContext {
16 > readonly changedObservable: IObservableWithChange<any, any>;
17 > readonly change: unknown;
18 >
19 > /**
20 > * Returns if the given observable caused the change.
21 > */
22 > didChange<T, TChange>(observable: IObservableWithChange<T, TChange>): this is { change: TChange };
23 > }
24 >
25 > /**
26 > * Subscribes to and records changes and the last value of the given observables.
27 > * Don't use the key "changes", as it is reserved for the changes array!
28 > */
29 > export function recordChanges<TObs extends Record<any, IObservableWithChange<any, any>>>(obs: TObs):
30 IChangeTracker<{ [TKey in keyof TObs]: ReturnType<TObs[TKey]['get']> }
31 & { changes: readonly ({ [TKey in keyof TObs]: { key: TKey; change: TObs[TKey]['TChange'] } }[keyof TObs])[] }> {
56 };
57 }
59 > /**
60 > * Subscribes to and records changes and the last value of the given observables.
61 > * Don't use the key "changes", as it is reserved for the changes array!
62 > */
63 > export function recordChangesLazy<TObs extends Record<any, IObservableWithChange<any, any>>>(getObs: () => TObs):
64 IChangeTracker<{ [TKey in keyof TObs]: ReturnType<TObs[TKey]['get']> }
65 & { changes: readonly ({ [TKey in keyof TObs]: { key: TKey; change: TObs[TKey]['TChange'] } }[keyof TObs])[] }> {
src/vs/base/common/observableInternal/map.ts 35 covered LOC · 14 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 { IObservable, ITransaction } from '../observable.js';
7 > import { observableValueOpts } from './observables/observableValueOpts.js';
8 >
9 >
10 > export class ObservableMap<K, V> implements Map<K, V> {
11 private readonly _data = new Map<K, V>();
12
14
15 readonly observable: IObservable<Map<K, V>> = this._obs;
16 > map.ts
17 > get size(): number {
18 return this._data.size;
19 }
20 > map.ts
21 > has(key: K): boolean {
22 return this._data.has(key);
23 }
24 > map.ts
25 > get(key: K): V | undefined {
26 return this._data.get(key);
27 }
28 > map.ts
29 > set(key: K, value: V, tx?: ITransaction): this {
30 const hadKey = this._data.has(key);
31 const oldValue = this._data.get(key);
36 return this;
37 }
38 > map.ts
39 > delete(key: K, tx?: ITransaction): boolean {
40 const result = this._data.delete(key);
41 if (result) {
44 return result;
45 }
46 > map.ts
47 > clear(tx?: ITransaction): void {
48 if (this._data.size > 0) {
49 this._data.clear();
51 }
52 }
53 > map.ts
54 > forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: unknown): void {
55 this._data.forEach((value, key, _map) => {
56 callbackfn.call(thisArg, value, key, this);
57 });
58 }
59 > map.ts
60 > *entries(): MapIterator<[K, V]> {
61 yield* this._data.entries();
62 }
63 > map.ts
64 > *keys(): MapIterator<K> {
65 yield* this._data.keys();
66 }
67 > map.ts
68 > *values(): MapIterator<V> {
69 yield* this._data.values();
70 }
71 > map.ts
72 > [Symbol.iterator](): MapIterator<[K, V]> {
73 return this.entries();
74 }
75 > map.ts
76 > get [Symbol.toStringTag](): string {
77 return 'ObservableMap';
78 }
79 > } map.ts
src/vs/base/test/common/timeTravelScheduler.ts 35 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- timeTravelScheduler.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 > * @deprecated The contents of this file have moved to
8 > * `./virtualScheduling/index.js`. This re-export is kept for backwards
9 > * compatibility and will be removed once all callers have migrated.
10 > *
11 > * Notes for migration:
12 > * - `TimeTravelScheduler` is now {@link VirtualClock} (same constructor).
13 > * - `AsyncSchedulerProcessor` is now {@link VirtualTimeProcessor}; its
14 > * constructor takes an explicit {@link Embedding}, and {@link Run.options}
15 > * use `until` (a {@link TerminationPolicy}) instead of the implicit
16 > * "drain queue" behaviour. See {@link runWithFakedTimers} for a
17 > * drop-in helper.
18 > * - `originalGlobalValues` is now {@link realTimeApi}.
19 > */
20 >
21 > export {
22 > captureGlobalTimeApi,
23 > createLoggingTimeApi,
24 > createVirtualTimeApi,
25 > pushGlobalTimeApi,
26 > realTimeApi as originalGlobalValues,
27 > runWithFakedTimers,
28 > VirtualClock as TimeTravelScheduler,
29 > } from './virtualScheduling/index.js';
30 >
31 > export type {
32 > CreateVirtualTimeApiOptions,
33 > RunWithFakedTimersOptions,
34 > TimeApi,
35 > } from './virtualScheduling/index.js';
src/vs/platform/instantiation/common/extensions.ts 35 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- extensions.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 { SyncDescriptor } from './descriptors.js';
7 > import { BrandedService, ServiceIdentifier } from './instantiation.js';
8 >
9 > const _registry: [ServiceIdentifier<any>, SyncDescriptor<any>][] = [];
10 >
11 > export const enum InstantiationType {
12 > /**
13 > * Instantiate this service as soon as a consumer depends on it. _Note_ that this
14 > * is more costly as some upfront work is done that is likely not needed
15 > */
16 > Eager = 0,
17 >
18 > /**
19 > * Instantiate this service as soon as a consumer uses it. This is the _better_
20 > * way of registering a service.
21 > */
22 > Delayed = 1
23 > }
24 >
25 > export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctor: new (...services: Services) => T, supportsDelayedInstantiation: InstantiationType): void;
26 > export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, descriptor: SyncDescriptor<any>): void;
27 > export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctorOrDescriptor: { new(...services: Services): T } | SyncDescriptor<any>, supportsDelayedInstantiation?: boolean | InstantiationType): void {
28 > if (!(ctorOrDescriptor instanceof SyncDescriptor)) {
29 > ctorOrDescriptor = new SyncDescriptor<T>(ctorOrDescriptor as new (...args: unknown[]) => T, [], Boolean(supportsDelayedInstantiation));
30 > }
31 >
32 > _registry.push([id, ctorOrDescriptor]);
33 > }
34 >
35 > export function getSingletonServiceDescriptors(): [ServiceIdentifier<any>, SyncDescriptor<any>][] {
36 return _registry;
37 }
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/test/common/virtualScheduling/recordingTimeApi.ts 33 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- recordingTimeApi.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 { realTimeApi, TimeApi } from './timeApi.js';
7 > import { Trace, TraceContext } from './trace.js';
8 > import { EventSource } from './virtualClock.js';
9 >
10 > /**
11 > * One entry in a real-time trace recording. Structurally compatible with
12 > * `VirtualEvent` (and `ScheduledTaskLike` consumed by
13 > * `buildHistoryFromTasks`), so the same swimlane renderer can plot both.
14 > */
15 > export interface RecordedTimerEvent {
16 > readonly time: number;
17 > readonly source: EventSource;
18 > readonly trace?: Trace;
19 > }
20 >
21 > /**
22 > * Wrap the real host time API so every `setTimeout` / `setInterval` /
23 > * `requestAnimationFrame` call is tagged with a child {@link Trace} and
24 > * pushes a {@link RecordedTimerEvent} into `history` when the handler
25 > * actually runs.
26 > *
27 > * Handlers are invoked through {@link TraceContext.runAsHandler} so causal
28 > * chains carry across awaits inside a handler. Note: because each handler's
29 > * deferred trace-reset fires as its own real macrotask, attribution can
30 > * drift slightly when many handlers fire in quick succession — accurate
31 > * enough for diagnostics, not for assertions.
32 > */
33 > export function createRecordingRealTimeApi(history: RecordedTimerEvent[]): TimeApi {
34 const realSetTimeout = realTimeApi.setTimeout;
35
src/vs/base/common/observableInternal/set.ts 32 covered LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- set.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 { IObservable, ITransaction } from '../observable.js';
7 > import { observableValueOpts } from './observables/observableValueOpts.js';
8 >
9 > export class ObservableSet<T> implements Set<T> {
10
11 private readonly _data = new Set<T>();
14
15 readonly observable: IObservable<Set<T>> = this._obs;
16 > set.ts
17 > get size(): number {
18 return this._data.size;
19 }
20 > set.ts
21 > has(value: T): boolean {
22 return this._data.has(value);
23 }
24 > set.ts
25 > add(value: T, tx?: ITransaction): this {
26 const hadValue = this._data.has(value);
27 if (!hadValue) {
31 return this;
32 }
33 > set.ts
34 > delete(value: T, tx?: ITransaction): boolean {
35 const result = this._data.delete(value);
36 if (result) {
39 return result;
40 }
41 > set.ts
42 > clear(tx?: ITransaction): void {
43 if (this._data.size > 0) {
44 this._data.clear();
46 }
47 }
48 > set.ts
49 > forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: unknown): void {
50 this._data.forEach((value, value2, _set) => {
51 callbackfn.call(thisArg, value, value2, this);
52 });
53 }
54 > set.ts
55 > *entries(): SetIterator<[T, T]> {
56 for (const value of this._data) {
57 yield [value, value];
58 }
59 }
60 > set.ts
61 > *keys(): SetIterator<T> {
62 yield* this._data.keys();
63 }
64 > set.ts
65 > *values(): SetIterator<T> {
66 yield* this._data.values();
67 }
68 > set.ts
69 > [Symbol.iterator](): SetIterator<T> {
70 return this.values();
71 }
72 > set.ts
73 > get [Symbol.toStringTag](): string {
74 return 'ObservableSet';
75 }
76 > } set.ts
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/marshalling.ts 28 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- marshalling.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 } from './buffer.js';
7 > import { URI, UriComponents } from './uri.js';
8 > import { MarshalledId } from './marshallingIds.js';
9 >
10 > export function stringify(obj: unknown): string {
11 return JSON.stringify(obj, replacer);
12 }
14 > export function parse(text: string): any {
15 let data = JSON.parse(text);
16 data = revive(data);
17 return data;
18 }
20 > export interface MarshalledObject {
21 > $mid: MarshalledId;
22 > }
23 >
24 function replacer(key: string, value: any): any {
25 // URI is done via toJSON-member
33 return value;
34 }
36 >
37 > type Deserialize<T> = T extends UriComponents ? URI
38 > : T extends VSBuffer ? VSBuffer
39 > : T extends object
40 > ? Revived<T>
41 > : T;
42 >
43 > export type Revived<T> = { [K in keyof T]: Deserialize<T[K]> };
44 >
45 > export function revive<T = any>(obj: any, depth = 0): Revived<T> {
46 if (!obj || depth > 200) {
47 return obj;
src/vs/base/test/common/mock.ts 27 covered LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- mock.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 { SinonStub, stub } from 'sinon';
7 > import { DeepPartial } from '../../common/types.js';
8 >
9 > export interface Ctor<T> {
10 > new(): T;
11 > }
12 >
13 > export function mock<T>(): Ctor<T> {
14 // eslint-disable-next-line local/code-no-any-casts
15 return function () { } as any;
16 }
17 > mock.ts
18 > export type MockObject<T, ExceptProps = never> = { [K in keyof T]: K extends ExceptProps ? T[K] : SinonStub };
19 >
20 > // Creates an object object that returns sinon mocks for every property. Optionally
21 > // takes base properties.
22 > export const mockObject = <T extends object>() => <TP extends Partial<T> = {}>(properties?: TP): MockObject<T, keyof TP> => {
23 // eslint-disable-next-line local/code-no-any-casts
24 return new Proxy({ ...properties } as any, {
36 });
37 };
38 > mock.ts
39 > /**
40 > * Shortcut for type-safe partials in mocks. A shortcut for `obj as Partial<T> as T`.
41 > */
42 > export function upcastPartial<T>(partial: Partial<T>): T {
43 > return partial as T; mock.ts
44 > }
45 > export function upcastDeepPartial<T>(partial: DeepPartial<T>): T { mock.ts
46 return partial as T;
47 }
src/vs/base/common/codiconsUtil.ts 26 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- codiconsUtil.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 > import { ThemeIcon } from './themables.js';
6 > import { isString } from './types.js';
7 >
8 >
9 > const _codiconFontCharacters: { [id: string]: number } = Object.create(null);
10 >
11 > export function register(id: string, fontCharacter: number | string): ThemeIcon {
12 > if (isString(fontCharacter)) {
13 > const val = _codiconFontCharacters[fontCharacter];
14 > if (val === undefined) {
15 throw new Error(`${id} references an unknown codicon: ${fontCharacter}`);
16 }
17 > fontCharacter = val; codiconsUtil.ts
18 > }
19 > _codiconFontCharacters[id] = fontCharacter;
20 > return { id };
21 > }
22 >
23 > /**
24 > * Only to be used by the iconRegistry.
25 > */
26 > export function getCodiconFontCharacters(): { [id: string]: number } {
27 > return _codiconFontCharacters; codiconsUtil.ts
28 > }
src/vs/base/common/observableInternal/experimental/utils.ts 26 covered LOC · 2 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 { IObservable, IReader } from '../base.js';
7 > import { BugIndicatingError, DisposableStore } from '../commonFacade/deps.js';
8 > import { DebugOwner, getDebugName, DebugNameData } from '../debugName.js';
9 > import { observableFromEvent } from '../observables/observableFromEvent.js';
10 > import { autorunOpts } from '../reactions/autorun.js';
11 > import { derivedObservableWithCache } from '../utils/utils.js';
12 >
13 > /**
14 > * Creates an observable that has the latest changed value of the given observables.
15 > * Initially (and when not observed), it has the value of the last observable.
16 > * When observed and any of the observables change, it has the value of the last changed observable.
17 > * If multiple observables change in the same transaction, the last observable wins.
18 > */
19 > export function latestChangedValue<T extends IObservable<any>[]>(owner: DebugOwner, observables: T): IObservable<ReturnType<T[number]['get']>> {
20 if (observables.length === 0) {
21 throw new BugIndicatingError();
50 return result;
51 }
52 > utils.ts
53 > /**
54 > * Works like a derived.
55 > * However, if the value is not undefined, it is cached and will not be recomputed anymore.
56 > * In that case, the derived will unsubscribe from its dependencies.
57 > */
58 > export function derivedConstOnceDefined<T>(owner: DebugOwner, fn: (reader: IReader) => T): IObservable<T | undefined> {
59 return derivedObservableWithCache<T | undefined>(owner, (reader, lastValue) => lastValue ?? fn(reader));
60 }
src/vs/base/common/observableInternal/observables/constObservable.ts 26 covered LOC · 9 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- constObservable.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 { IObservable, IObserver, IObservableWithChange } from '../base.js';
7 > import { ConvenientObservable } from './baseObservable.js';
8 >
9 > /**
10 > * Represents an efficient observable whose value never changes.
11 > */
12 >
13 > export function constObservable<T>(value: T): IObservable<T> {
14 return new ConstObservable(value);
15 }
16 > class ConstObservable<T> extends ConvenientObservable<T, void> { constObservable.ts
17 > constructor(private readonly value: T) {
18 super();
19 }
21 > public override get debugName(): string {
22 return this.toString();
23 }
25 > public get(): T {
26 return this.value;
27 }
28 > public addObserver(observer: IObserver): void { constObservable.ts
29 // NO OP
30 }
31 > public removeObserver(observer: IObserver): void { constObservable.ts
32 // NO OP
33 }
35 > override log(): IObservableWithChange<T, void> {
36 return this;
37 }
39 > override toString(): string {
40 return `Const: ${this.value}`;
41 }
src/vs/platform/workspace/test/common/testWorkspace.ts 26 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- testWorkspace.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 { isLinux, isWindows } from '../../../../base/common/platform.js';
7 > import { URI } from '../../../../base/common/uri.js';
8 > import { toWorkspaceFolder, Workspace as BaseWorkspace, WorkspaceFolder } from '../../common/workspace.js';
9 >
10 > export class Workspace extends BaseWorkspace {
11 > constructor(
12 > id: string,
13 > folders: WorkspaceFolder[] = [],
14 > configuration: URI | null = null,
15 > ignorePathCasing: (key: URI) => boolean = () => !isLinux
16 > ) {
17 > super(id, folders, false, configuration, ignorePathCasing);
18 > }
19 > }
20 >
21 > const wsUri = URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace');
22 > export const TestWorkspace = testWorkspace(wsUri);
23 >
24 > export function testWorkspace(...resource: URI[]): Workspace {
25 > return new Workspace('test-workspace', resource.map(toWorkspaceFolder));
26 > }
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/uuid.ts 25 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- uuid.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 > const _UUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8 >
9 > export function isUUID(value: string): boolean {
10 return _UUIDPattern.test(value);
11 }
12 > uuid.ts
13 > export const generateUuid = (function (): () => string {
14 >
15 > // use `randomUUID` if possible
16 > if (typeof crypto.randomUUID === 'function') {
17 > // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto
18 > // > Although crypto is available on all windows, the returned Crypto object only has one
19 > // > usable feature in insecure contexts: the getRandomValues() method.
20 > // > In general, you should use this API only in secure contexts.
21 >
22 > return crypto.randomUUID.bind(crypto);
23 > }
24
25 // prep-work
63 return result;
64 };
65 > })(); uuid.ts
66 >
67 > /** Namespace should be 3 letters, e.g. `abc-<uuid>`. */
68 > export function prefixedUuid(namespace: string): string {
69 return `${namespace}-${generateUuid()}`;
70 }
src/vs/base/common/observableInternal/logging/debugger/utils.ts 24 covered LOC · 8 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 { IDisposable } from '../../../lifecycle.js';
7 >
8 > export class Debouncer implements IDisposable {
9 private _timeout: Timeout | undefined = undefined;
10 > utils.ts
11 > public debounce(fn: () => void, timeoutMs: number): void {
12 if (this._timeout !== undefined) {
13 clearTimeout(this._timeout);
18 }, timeoutMs);
19 }
20 > utils.ts
21 > dispose(): void {
22 if (this._timeout !== undefined) {
23 clearTimeout(this._timeout);
24 }
25 }
26 > } utils.ts
27 >
28 > export class Throttler implements IDisposable {
29 private _timeout: Timeout | undefined = undefined;
30 > utils.ts
31 > public throttle(fn: () => void, timeoutMs: number): void {
32 if (this._timeout === undefined) {
33 this._timeout = setTimeout(() => {
37 }
38 }
39 > utils.ts
40 > dispose(): void {
41 if (this._timeout !== undefined) {
42 clearTimeout(this._timeout);
43 }
44 }
45 > } utils.ts
46 >
47 > export function deepAssign<T>(target: T, source: T): void {
48 for (const key in source) {
49 if (!!target[key] && typeof target[key] === 'object' && !!source[key] && typeof source[key] === 'object') {
54 }
55 }
56 > utils.ts
57 > export function deepAssignDeleteNulls<T>(target: T, source: T): void {
58 for (const key in source) {
59 if (source[key] === null) {
src/vs/base/common/observableInternal/utils/utilsCancellation.ts 24 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- utilsCancellation.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 { IReader, IObservable } from '../base.js';
7 > import { DebugOwner, DebugNameData } from '../debugName.js';
8 > import { CancellationError, CancellationToken, CancellationTokenSource } from '../commonFacade/cancellation.js';
9 > import { strictEquals } from '../commonFacade/deps.js';
10 > import { autorun } from '../reactions/autorun.js';
11 > import { Derived } from '../observables/derivedImpl.js';
12 > import { DebugLocation } from '../debugLocation.js';
13 >
14 > /**
15 > * Resolves the promise when the observables state matches the predicate.
16 > */
17 > export function waitForState<T>(observable: IObservable<T | null | undefined>): Promise<T>;
18 > export function waitForState<T, TState extends T>(observable: IObservable<T>, predicate: (state: T) => state is TState, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise<TState>;
19 > export function waitForState<T>(observable: IObservable<T>, predicate: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise<T>;
20 > export function waitForState<T>(observable: IObservable<T>, predicate?: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise<T> {
21 if (!predicate) {
22 predicate = state => state !== null && state !== undefined;
69 });
70 }
72 > export function derivedWithCancellationToken<T>(computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable<T>;
73 > export function derivedWithCancellationToken<T>(owner: object, computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable<T>;
74 > export function derivedWithCancellationToken<T>(computeFnOrOwner: ((reader: IReader, cancellationToken: CancellationToken) => T) | object, computeFnOrUndefined?: ((reader: IReader, cancellationToken: CancellationToken) => T)): IObservable<T> {
75 let computeFn: (reader: IReader, store: CancellationToken) => T;
76 let owner: DebugOwner;
src/vs/workbench/contrib/debug/common/disassemblyViewInput.ts 22 covered LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- disassemblyViewInput.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 { EditorInput } from '../../../common/editor/editorInput.js';
7 > import { localize } from '../../../../nls.js';
8 > import { ThemeIcon } from '../../../../base/common/themables.js';
9 > import { Codicon } from '../../../../base/common/codicons.js';
10 > import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js';
11 >
12 > const DisassemblyEditorIcon = registerIcon('disassembly-editor-label-icon', Codicon.debug, localize('disassemblyEditorLabelIcon', 'Icon of the disassembly editor label.'));
13 >
14 > export class DisassemblyViewInput extends EditorInput {
15
16 static readonly ID = 'debug.disassemblyView.input';
src/vs/platform/instantiation/common/descriptors.ts 21 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- descriptors.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 class SyncDescriptor<T> {
7 >
8 > readonly ctor: any;
9 > readonly staticArguments: unknown[];
10 > readonly supportsDelayedInstantiation: boolean;
11 >
12 > constructor(ctor: new (...args: any[]) => T, staticArguments: unknown[] = [], supportsDelayedInstantiation: boolean = false) {
13 > this.ctor = ctor; descriptors.ts
14 > this.staticArguments = staticArguments;
15 > this.supportsDelayedInstantiation = supportsDelayedInstantiation;
16 > }
18 >
19 > export interface SyncDescriptor0<T> {
20 > readonly ctor: new () => T;
21 > }
src/vs/base/common/observableInternal/utils/valueWithChangeEvent.ts 19 covered LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- valueWithChangeEvent.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 { IObservable } from '../base.js';
7 > import { Event, IValueWithChangeEvent } from '../commonFacade/deps.js';
8 > import { DebugOwner } from '../debugName.js';
9 > import { observableFromEvent } from '../observables/observableFromEvent.js';
10 >
11 > export class ValueWithChangeEventFromObservable<T> implements IValueWithChangeEvent<T> {
12 > constructor(public readonly observable: IObservable<T>) {
13 }
15 > get onDidChange(): Event<void> {
16 return Event.fromObservableLight(this.observable);
17 }
19 > get value(): T {
20 return this.observable.get();
21 }
23 >
24 > export function observableFromValueWithChangeEvent<T>(owner: DebugOwner, value: IValueWithChangeEvent<T>): IObservable<T> {
25 if (value instanceof ValueWithChangeEventFromObservable) {
26 return value.observable;
src/vs/base/common/observableInternal/logging/debugger/debuggerRpc.ts 17 covered LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- debuggerRpc.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 { ChannelFactory, IChannelHandler, API, SimpleTypedRpcConnection, MakeSideAsync } from './rpc.js';
7 >
8 > export function registerDebugChannel<T extends { channelId: string } & API>(
9 channelId: T['channelId'],
10 createClient: () => T['client'],
43 });
44 }
46 > interface GlobalObj {
47 > $$debugValueEditor_debugChannels: Record<string, (host: IHost) => { handleRequest: (data: unknown) => unknown }>;
48 > }
49 >
50 > interface IHost {
51 > sendNotification: (data: unknown) => void;
52 > }
53 >
54 function createChannelFactoryFromDebugChannel(host: IHost): { channel: ChannelFactory; handler: { handleRequest: (data: unknown) => unknown } } {
55 let h: IChannelHandler | undefined;
src/vs/base/common/observableInternal/utils/runOnChange.ts 17 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- runOnChange.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 { IObservableWithChange } from '../base.js';
7 > import { CancellationToken, cancelOnDispose } from '../commonFacade/cancellation.js';
8 > import { DisposableStore, IDisposable } from '../commonFacade/deps.js';
9 > import { autorunWithStoreHandleChanges } from '../reactions/autorun.js';
10 >
11 > export type RemoveUndefined<T> = T extends undefined ? never : T;
12 >
13 > export function runOnChange<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: T, deltas: RemoveUndefined<TChange>[]) => void): IDisposable {
14 let _previousValue: T | undefined;
15 let _firstRun = true;
42 });
43 }
45 > export function runOnChangeWithStore<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: T, deltas: RemoveUndefined<TChange>[], store: DisposableStore) => void): IDisposable {
46 const store = new DisposableStore();
47 const disposable = runOnChange(observable, (value, previousValue: T, deltas) => {
56 };
57 }
59 > export function runOnChangeWithCancellationToken<T, TChange>(observable: IObservableWithChange<T, TChange>, cb: (value: T, previousValue: T, deltas: RemoveUndefined<TChange>[], token: CancellationToken) => Promise<void>): IDisposable {
60 return runOnChangeWithStore(observable, (value, previousValue, deltas, store) => {
61 cb(value, previousValue, deltas, cancelOnDispose(store));
src/vs/platform/product/common/productService.ts 17 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- productService.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 { IProductConfiguration } from '../../../base/common/product.js';
7 > import { createDecorator } from '../../instantiation/common/instantiation.js';
8 >
9 > export const IProductService = createDecorator<IProductService>('productService');
10 >
11 > export interface IProductService extends Readonly<IProductConfiguration> {
12 >
13 > readonly _serviceBrand: undefined;
14 >
15 > }
16 >
17 > export const productSchemaId = 'vscode://schemas/vscode-product';
src/vs/base/test/common/virtualScheduling/loggingTimeApi.ts 15 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- loggingTimeApi.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 { TimeApi } from './timeApi.js';
7 >
8 > /**
9 > * Wrap `underlying` so that every call to `setTimeout`, `setInterval`,
10 > * `setImmediate` or `requestAnimationFrame` invokes `onCall` first.
11 > *
12 > * Useful for diagnostics — e.g. logging timer registrations made outside of
13 > * virtual time, to find leaks of real-time scheduling into a fixture.
14 > */
15 > export function createLoggingTimeApi(
16 underlying: TimeApi,
17 onCall: (name: string, stack: string | undefined, handler?: () => void) => void,
src/vs/platform/telemetry/common/commonProperties.ts 14 covered LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- commonProperties.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 { isLinuxSnap, platform, Platform, PlatformToString } from '../../../base/common/platform.js';
7 > import { env, platform as nodePlatform } from '../../../base/common/process.js';
8 > import { generateUuid } from '../../../base/common/uuid.js';
9 > import { ICommonProperties } from './telemetry.js';
10 >
11 function getPlatformDetail(hostname: string): string | undefined {
12 if (platform === Platform.Linux && /^penguin(\.|$)/i.test(hostname)) {
16 return undefined;
17 }
19 > export function resolveCommonProperties(
20 release: string,
21 hostname: string,
97 return result;
98 }
100 > export function verifyMicrosoftInternalDomain(domainList: readonly string[]): boolean {
101 const userDnsDomain = env['USERDNSDOMAIN'];
102 if (!userDnsDomain) {
src/vs/base/common/observableInternal/observables/observableValueOpts.ts 13 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observableValueOpts.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 { ISettableObservable } from '../base.js';
7 > import { DebugNameData, IDebugNameData } from '../debugName.js';
8 > import { EqualityComparer, strictEquals } from '../commonFacade/deps.js';
9 > import { ObservableValue } from './observableValue.js';
10 > import { LazyObservableValue } from './lazyObservableValue.js';
11 > import { DebugLocation } from '../debugLocation.js';
12 >
13 > export function observableValueOpts<T, TChange = void>(
14 options: IDebugNameData & {
15 equalsFn?: EqualityComparer<T>;
src/vs/base/common/observableInternal/commonFacade/deps.ts 10 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- deps.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 { assertFn } from '../../assert.js';
7 > export { type EqualityComparer, strictEquals } from '../../equals.js';
8 > export { BugIndicatingError, onBugIndicatingError, onUnexpectedError } from '../../errors.js';
9 > export { Event, type IValueWithChangeEvent } from '../../event.js';
10 > export { DisposableStore, type IDisposable, markAsDisposed, toDisposable, trackDisposable } from '../../lifecycle.js';
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;
src/vs/base/common/symbols.ts 9 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- symbols.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 > * Can be passed into the Delayed to defer using a microtask
8 > * */
9 > export const MicrotaskDelay = Symbol('MicrotaskDelay');
src/vs/base/common/observable.ts 8 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- observable.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 > // This is a facade for the observable implementation. Only import from here!
7 >
8 > export * from './observableInternal/index.js';
src/vs/base/common/observableInternal/commonFacade/cancellation.ts 7 covered LOC · 1 range

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 > export { CancellationError } from '../../errors.js';
7 > export { CancellationToken, CancellationTokenSource, cancelOnDispose } from '../../cancellation.js';