Atlas › Test

agentHostSchema.test|title=agentHostSchema normalizeAgentHostTerminalAutoApproveRulesConfig removes default-only entries when default rules are ignored|occurrence=1

Exact test identity: mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/common/agentHostSchema.test|title=agentHostSchema normalizeAgentHostTerminalAutoApproveRulesConfig removes default-only entries when default rules are ignored|occurrence=1

Package
mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/agentHost/test/common
Suite / test hierarchy
agentHostSchema.test|title=agentHostSchema normalizeAgentHostTerminalAutoApproveRulesConfig removes default-only entries when default rules are ignored|occurrence=1
Test
agentHostSchema.test|title=agentHostSchema normalizeAgentHostTerminalAutoApproveRulesConfig removes default-only entries when default rules are ignored|occurrence=1
Introduced at
equals.ts ×1 Frontier kind: Joint frontier
Covered ranges
578
Covered lines
6671
Covered files
33

Covered source

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

src/vs/platform/agentHost/common/state/protocol/common/commands.ts 1071 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- commands.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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > import type { URI, Snapshot } from './state.js';
10 > import type { ActionEnvelope, StateAction } from './actions.js';
11 > import type { TelemetryCapabilities } from '../channels-otlp/state.js';
12 >
13 > // ─── BaseParams ──────────────────────────────────────────────────────────────
14 >
15 > /**
16 > * Base shape every command's params extends.
17 > *
18 > * `channel` identifies the channel the command targets, mirroring the
19 > * `channel` field on every protocol notification. For commands that operate
20 > * on a specific channel (a session, terminal, or changeset), `channel` is
21 > * that channel's URI. For commands that are connection-level rather than
22 > * channel-scoped (e.g. {@link InitializeParams | `initialize`},
23 > * {@link PingParams | `ping`}, {@link ListSessionsParams | `listSessions`},
24 > * the `resource*` filesystem commands, and {@link AuthenticateParams |
25 > * `authenticate`}), the params type narrows `channel` to the literal
26 > * root URI `'ahp-root://'`.
27 > *
28 > * This invariant lets implementations route every incoming message —
29 > * request, response, or notification — by inspecting `params.channel`
30 > * without needing to know the per-method param shape.
31 > *
32 > * @category Commands
33 > */
34 > export interface BaseParams {
35 > /** Channel URI this command targets. */
36 > channel: URI;
37 > }
38 >
39 > // ─── Pagination ──────────────────────────────────────────────────────────────
40 >
41 > /**
42 > * Cursor-based pagination inputs, mixed into the params of any list command
43 > * that can page a large result set (e.g. {@link ListSessionsParams |
44 > * `listSessions`}). The paired output is {@link PaginatedResult}.
45 > *
46 > * Pagination is **opaque and cursor-based**, mirroring the shape `fetchTurns`
47 > * already uses for chat history: the server owns the ordering and keyset, and
48 > * the client walks pages by echoing the cursor from the previous
49 > * {@link PaginatedResult.nextCursor} back on the next request.
50 > *
51 > * The contract every paginated command shares:
52 > *
53 > * - To fetch the first page, omit `cursor`. Supply `limit` to bound the page.
54 > * - If the result carries a {@link PaginatedResult.nextCursor}, more entries
55 > * exist — pass it back as `cursor` to fetch the following page. A missing
56 > * `nextCursor` signals the end of the collection.
57 > * - Cursors are **server-defined and opaque**: clients MUST NOT parse, modify,
58 > * or persist them across connections. An unrecognised cursor SHOULD be
59 > * rejected with an `InvalidParams` error.
60 > * - Pagination is **fully additive**: a client that omits `limit`/`cursor` and
61 > * ignores `nextCursor` sees the pre-pagination behaviour (subject to any
62 > * server-imposed cap), and a server that does not paginate ignores the inputs
63 > * and returns everything in a single page.
64 > *
65 > * @category Commands
66 > */
67 > export interface PaginatedParams {
68 > /**
69 > * Maximum number of entries to return in this page. The server SHOULD respect
70 > * this bound but MAY return fewer entries and MAY impose its own upper cap.
71 > * Omit to let the server choose the page size.
72 > */
73 > limit?: number;
74 > /**
75 > * Opaque pagination cursor from a previous {@link PaginatedResult.nextCursor}.
76 > * Omit to fetch the first page. Cursors are server-defined and MUST be treated
77 > * as opaque — do not parse, modify, or persist them across connections. An
78 > * unrecognised cursor SHOULD be rejected with an `InvalidParams` error.
79 > */
80 > cursor?: string;
81 > }
82 >
83 > /**
84 > * Cursor-based pagination output, extended by the result of any list command
85 > * that can page a large result set (e.g. {@link ListSessionsResult |
86 > * `listSessions`}). See {@link PaginatedParams} for the full pagination
87 > * contract shared by every paginated command.
88 > *
89 > * @category Commands
90 > */
91 > export interface PaginatedResult {
92 > /**
93 > * Opaque cursor for the next page. Present when more entries exist beyond the
94 > * returned page; absent signals the end of the collection. Pass it back as
95 > * {@link PaginatedParams.cursor} to fetch the following page.
96 > */
97 > nextCursor?: string;
98 > }
99 >
100 > // ─── initialize ──────────────────────────────────────────────────────────────
101 >
102 > /**
103 > * Identifies a protocol implementation — the software (and build) on one end
104 > * of the connection, as distinct from the {@link AgentInfo | agent persona} it
105 > * hosts. Carried as {@link InitializeParams.clientInfo | `clientInfo`} on the
106 > * client side and {@link InitializeResult.serverInfo | `serverInfo`} on the
107 > * server side, mirroring LSP's `clientInfo`/`serverInfo` and MCP's
108 > * `Implementation`.
109 > *
110 > * This is **informational only**: it exists for logging, telemetry, an
111 > * about/status affordance, and — as a last resort — a known-issue workaround
112 > * for a specific buggy build. It is **not** a feature-detection mechanism.
113 > * Feature availability stays with the capability model
114 > * ({@link ClientCapabilities} and the various `*.capabilities` declarations);
115 > * implementations SHOULD NOT gate protocol behaviour on parsing
116 > * {@link Implementation.version | `version`}.
117 > *
118 > * @category Commands
119 > */
120 > export interface Implementation {
121 > /** Implementation name, e.g. a product or package identifier. */
122 > name: string;
123 > /**
124 > * Implementation version. A [SemVer](https://semver.org) string is
125 > * recommended but not required.
126 > */
127 > version?: string;
128 > /** Optional human-readable display name. */
129 > title?: string;
130 > }
131 >
132 > /**
133 > * Establishes a new connection and negotiates the protocol version.
134 > * This MUST be the first message sent by the client.
135 > *
136 > * @category Commands
137 > * @method initialize
138 > * @direction Client → Server
139 > * @messageType Request
140 > * @version 1
141 > * @see {@link /specification/lifecycle | Lifecycle} for the full handshake flow.
142 > */
143 > export interface InitializeParams extends BaseParams {
144 > channel: 'ahp-root://';
145 > /**
146 > * Protocol versions the client is willing to speak, ordered from most
147 > * preferred to least preferred. Each entry is a [SemVer](https://semver.org)
148 > * `MAJOR.MINOR.PATCH` string (e.g. `"0.1.0"`).
149 > *
150 > * The server selects one entry and returns it as `InitializeResult.protocolVersion`.
151 > * If the server cannot speak any of the offered versions, it MUST return
152 > * error code `-32005` (`UnsupportedProtocolVersion`).
153 > */
154 > protocolVersions: string[];
155 > /** Unique client identifier */
156 > clientId: string;
157 > /**
158 > * Optional identity of the client implementation (name and version).
159 > * Informational only — see {@link Implementation} for how it may and may not
160 > * be used. Distinct from {@link InitializeParams.clientId | `clientId`},
161 > * which is an opaque per-connection identifier used for reconnection, not a
162 > * human-readable implementation name.
163 > */
164 > clientInfo?: Implementation;
165 > /** URIs to subscribe to during handshake */
166 > initialSubscriptions?: URI[];
167 > /**
168 > * IETF BCP 47 language tag indicating the client's preferred locale
169 > * (e.g. `"en-US"`, `"ja"`). The server SHOULD use this to localise
170 > * user-facing strings such as confirmation option labels.
171 > */
172 > locale?: string;
173 > /**
174 > * Optional client capability declarations.
175 > *
176 > * Servers SHOULD only advertise features whose corresponding client
177 > * capability is set here. Absent means "not declared" — the server
178 > * MUST assume the client does not support the feature.
179 > */
180 > capabilities?: ClientCapabilities;
181 > }
182 >
183 > /**
184 > * Optional capabilities a client declares during `initialize`.
185 > *
186 > * Each field is a presence flag: an empty object `{}` means "supported",
187 > * absence means "not supported". Sub-fields on individual capabilities
188 > * are reserved for future per-capability options.
189 > *
190 > * @category Commands
191 > */
192 > export interface ClientCapabilities {
193 > /**
194 > * Client can render
195 > * [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) — i.e.
196 > * it can host the View sandbox, run the `ui/*` protocol against it,
197 > * and forward `mcp://`-channel traffic on the App's behalf.
198 > *
199 > * Hosts SHOULD only populate
200 > * {@link McpServerCustomization.mcpApp | `McpServerCustomization.mcpApp`}
201 > * (and expose the corresponding
202 > * {@link McpServerCustomization.channel | `mcp://` channel}) when this
203 > * capability is declared. Clients that omit it MUST treat
204 > * App-bearing tool calls as ordinary MCP tool calls.
205 > */
206 > mcpApps?: Record<string, never>;
207 > }
208 >
209 > /**
210 > * Result of the `initialize` command.
211 > *
212 > * `protocolVersion` is the version the server has selected from the client's
213 > * `protocolVersions` list. The client and server MUST use this version for
214 > * the rest of the connection. If the server cannot speak any of the offered
215 > * versions it MUST return error code `-32005` (`UnsupportedProtocolVersion`)
216 > * instead of a result.
217 > */
218 > export interface InitializeResult {
219 > /**
220 > * Protocol version selected by the server. MUST be one of the entries in
221 > * `InitializeParams.protocolVersions`. Formatted as a [SemVer](https://semver.org)
222 > * `MAJOR.MINOR.PATCH` string (e.g. `"0.1.0"`).
223 > */
224 > protocolVersion: string;
225 > /** Current server sequence number */
226 > serverSeq: number;
227 > /**
228 > * Optional identity of the server implementation (name and version).
229 > * Informational only — see {@link Implementation} for how it may and may not
230 > * be used. Whereas {@link InitializeResult.protocolVersion | `protocolVersion`}
231 > * identifies the negotiated protocol, `serverInfo` identifies the host
232 > * software behind it.
233 > */
234 > serverInfo?: Implementation;
235 > /** Snapshots for each `initialSubscriptions` URI */
236 > snapshots: Snapshot[];
237 > /** Suggested default directory for remote filesystem browsing */
238 > defaultDirectory?: URI;
239 > /**
240 > * Characters that, when typed in a {@link Message} input, SHOULD cause
241 > * the client to issue a `completions` request with
242 > * {@link CompletionItemKind.UserMessage}. Typically includes characters like
243 > * `'@'` or `'/'`.
244 > */
245 > completionTriggerCharacters?: string[];
246 > /**
247 > * Prefix that the host recognizes at the start of a user {@link Message.text}
248 > * as a shorthand for executing the remainder as a terminal command. Currently
249 > * the standardized convention is `"!"`; absence means the host does not
250 > * support command prefixes.
251 > */
252 > terminalCommandPrefix?: string;
253 > /**
254 > * OTLP telemetry channels the host emits, if any. Each populated field is
255 > * either a literal `ahp-otlp:` channel URI or an RFC 6570 URI template a
256 > * client expands before subscribing (currently only the `logs` channel
257 > * defines a template variable, `{level}`, for subscriber-side severity
258 > * filtering). Clients MAY ignore signals they cannot process.
259 > *
260 > * @see {@link /specification/telemetry-channel | Telemetry Channel}
261 > */
262 > telemetry?: TelemetryCapabilities;
263 > }
264 >
265 > // ─── ping ────────────────────────────────────────────────────────────────────
266 >
267 > /**
268 > * Verifies that the AHP connection is still alive and keeps it from being
269 > * closed by idle-timeout intermediaries (proxies, load balancers, etc.).
270 > *
271 > * The server MUST respond regardless of whether the client has completed
272 > * `initialize` or holds any subscriptions. Ping carries no payload in either
273 > * direction; the response itself is the signal.
274 > *
275 > * @category Commands
276 > * @method ping
277 > * @direction Client → Server
278 > * @messageType Request
279 > * @version 1
280 > */
281 > export interface PingParams extends BaseParams {
282 > channel: 'ahp-root://';
283 > }
284 >
285 > // ─── reconnect ───────────────────────────────────────────────────────────────
286 >
287 > /**
288 > * Discriminant for reconnect result types.
289 > *
290 > * @category Commands
291 > */
292 > export const enum ReconnectResultType {
293 > Replay = 'replay',
294 > Snapshot = 'snapshot',
295 > }
296 >
297 > /**
298 > * Re-establishes a dropped connection. The server replays missed actions or
299 > * provides fresh snapshots.
300 > *
301 > * @category Commands
302 > * @method reconnect
303 > * @direction Client → Server
304 > * @messageType Request
305 > * @version 1
306 > * @see {@link /specification/lifecycle | Lifecycle} for details.
307 > */
308 > export interface ReconnectParams extends BaseParams {
309 > channel: 'ahp-root://';
310 > /** Client identifier from the original connection */
311 > clientId: string;
312 > /** Last `serverSeq` the client received */
313 > lastSeenServerSeq: number;
314 > /** URIs the client was subscribed to */
315 > subscriptions: URI[];
316 > }
317 >
318 > /**
319 > * Reconnect result when the server can replay from the requested sequence.
320 > *
321 > * The server MUST include all replayed data in the response.
322 > */
323 > export interface ReconnectReplayResult {
324 > /** Discriminant */
325 > type: ReconnectResultType.Replay;
326 > /** Missed action envelopes since `lastSeenServerSeq` */
327 > actions: ActionEnvelope[];
328 > /**
329 > * URIs from `ReconnectParams.subscriptions` that the server cannot resume.
330 > * This includes resources that no longer exist (e.g. disposed sessions or
331 > * terminals) as well as resources the client is no longer permitted to
332 > * observe. Clients SHOULD drop these from their local subscription set.
333 > */
334 > missing: URI[];
335 > }
336 >
337 > /**
338 > * Reconnect result when the gap exceeds the replay buffer.
339 > */
340 > export interface ReconnectSnapshotResult {
341 > /** Discriminant */
342 > type: ReconnectResultType.Snapshot;
343 > /** Fresh snapshots for each subscription */
344 > snapshots: Snapshot[];
345 > }
346 >
347 > /** Result of the `reconnect` command. */
348 > export type ReconnectResult = ReconnectReplayResult | ReconnectSnapshotResult;
349 >
350 > // ─── subscribe ───────────────────────────────────────────────────────────────
351 >
352 > /**
353 > * Subscribe to a URI-identified channel.
354 > *
355 > * A channel MAY have state associated with it (e.g. root, sessions,
356 > * terminals) or be stateless (pure pub/sub for streaming data). For
357 > * state-bearing channels the result includes a snapshot; for stateless
358 > * channels `snapshot` is omitted.
359 > *
360 > * @category Commands
361 > * @method subscribe
362 > * @direction Client → Server
363 > * @messageType Request
364 > * @version 1
365 > * @see {@link /specification/subscriptions | Subscriptions}
366 > */
367 > export interface SubscribeParams extends BaseParams {
368 > /**
369 > * Optional delivery preferences for this subscription.
370 > *
371 > * Servers MAY use these preferences to buffer and coalesce high-frequency
372 > * updates while preserving the same reduced state. Omit this field for the
373 > * server's default delivery behavior.
374 > */
375 > delivery?: SubscriptionDeliveryOptions;
376 > /**
377 > * Optional client-requested shape for the returned snapshot.
378 > *
379 > * Servers that do not understand a requested view ignore it and return their
380 > * default snapshot. Clients MUST tolerate receiving more state than requested.
381 > */
382 > view?: SubscribeView;
383 > }
384 >
385 > /**
386 > * Optional client-requested shape for a subscription snapshot.
387 > *
388 > * @category Commands
389 > */
390 > export interface SubscribeView {
391 > /**
392 > * Advisory number of most-recent completed turns to expose in a chat
393 > * snapshot.
394 > *
395 > * Servers MAY return more or fewer turns than requested. When omitted, the
396 > * host MUST return all retained turns. When older turns remain available, the
397 > * returned {@link ChatState} carries `turnsNextCursor`; clients pass that
398 > * cursor to `fetchTurns` to ask the host to page more turns into the chat
399 > * state.
400 > */
401 > turns?: number;
402 > }
403 >
404 > /**
405 > * Advisory delivery preferences for a single subscription.
406 > *
407 > * @category Commands
408 > */
409 > export interface SubscriptionDeliveryOptions {
410 > /**
411 > * Maximum time, in milliseconds, that the server may intentionally delay
412 > * delivery while buffering/coalescing updates for this subscription.
413 > *
414 > * A value of `0` requests immediate delivery with no intentional coalescing.
415 > */
416 > maxLatencyMs?: number;
417 > }
418 >
419 > /**
420 > * Result of the `subscribe` command.
421 > *
422 > * `snapshot` is present when the subscribed channel has associated state, and
423 > * absent for stateless channels.
424 > */
425 > export interface SubscribeResult {
426 > /** Snapshot of the subscribed channel's state (omitted for stateless channels) */
427 > snapshot?: Snapshot;
428 > }
429 >
430 > // ─── unsubscribe ─────────────────────────────────────────────────────────────
431 >
432 > /**
433 > * Stop receiving updates for a channel.
434 > *
435 > * @category Commands
436 > * @method unsubscribe
437 > * @direction Client → Server
438 > * @messageType Notification
439 > * @version 1
440 > * @see {@link /specification/subscriptions | Subscriptions}
441 > */
442 > export interface UnsubscribeParams {
443 > /** Channel URI to unsubscribe from */
444 > channel: URI;
445 > }
446 >
447 > // ─── dispatchAction ──────────────────────────────────────────────────────────
448 >
449 > /**
450 > * Fire-and-forget action dispatch (write-ahead). The client applies actions
451 > * optimistically to local state and the server echoes them back as an
452 > * {@link ActionEnvelope} once accepted.
453 > *
454 > * The client → server method is named `dispatchAction`; the server's reply
455 > * arrives on the server → client `action` notification (params:
456 > * {@link ActionEnvelope}).
457 > *
458 > * @category Commands
459 > * @method dispatchAction
460 > * @direction Client → Server
461 > * @messageType Notification
462 > * @version 1
463 > * @see {@link /guide/actions | Actions} for the full list of client-dispatchable actions.
464 > */
465 > export interface DispatchActionParams {
466 > /** Channel URI this action targets */
467 > channel: URI;
468 > /** Client sequence number */
469 > clientSeq: number;
470 > /** The action to dispatch */
471 > action: StateAction;
472 > }
473 >
474 > // ─── resourceRead ────────────────────────────────────────────────────────
475 >
476 > /**
477 > * Encoding of fetched content data.
478 > *
479 > * @category Commands
480 > */
481 > export const enum ContentEncoding {
482 > Base64 = 'base64',
483 > Utf8 = 'utf-8',
484 > }
485 >
486 > /**
487 > * Reads the content of a resource by URI.
488 > *
489 > * Content references keep the state tree small by storing large data (images,
490 > * long tool outputs) by reference rather than inline.
491 > *
492 > * Binary content (images, etc.) MUST use `base64` encoding. Text content MAY
493 > * use `utf-8` encoding.
494 > *
495 > * Like all `resource*` methods, `resourceRead` is symmetrical and MAY be
496 > * sent in either direction. Hosts use it to fetch content from a
497 > * client-published URI (e.g. `virtual://my-client/...` plugins); clients
498 > * use it to read host-side files. The receiver enforces access via the
499 > * same permission/`resourceRequest` flow regardless of which peer initiated.
500 > *
501 > * @category Commands
502 > * @method resourceRead
503 > * @direction Client ↔ Server
504 > * @messageType Request
505 > * @version 1
506 > * @throws `NotFound` (`-32008`) if the URI does not exist.
507 > * @throws `PermissionDenied` (`-32009`) if the client is not permitted to read the URI.
508 > * @example
509 > * ```jsonc
510 > * // Client → Server
511 > * { "jsonrpc": "2.0", "id": 10, "method": "resourceRead",
512 > * "params": { "uri": "ahp-session:/<uuid>/content/img-1" } }
513 > *
514 > * // Server → Client
515 > * { "jsonrpc": "2.0", "id": 10, "result": {
516 > * "data": "iVBORw0KGgo...",
517 > * "encoding": "base64",
518 > * "contentType": "image/png"
519 > * }}
520 > * ```
521 > */
522 > export interface ResourceReadParams extends BaseParams {
523 > channel: 'ahp-root://';
524 > /** Content URI from a `ContentRef` */
525 > uri: string;
526 > /** Preferred encoding for the returned data (default: server-chosen) */
527 > encoding?: ContentEncoding;
528 > }
529 >
530 > /**
531 > * Result of the `resourceRead` command.
532 > *
533 > * The server SHOULD honor the `encoding` requested in the params. If the
534 > * server cannot provide the requested encoding, it MUST fall back to either
535 > * `base64` or `utf-8`.
536 > */
537 > export interface ResourceReadResult {
538 > /** Content encoded as a string */
539 > data: string;
540 > /** How `data` is encoded */
541 > encoding: ContentEncoding;
542 > /** Content type (e.g. `"image/png"`, `"text/plain"`) */
543 > contentType?: string;
544 > }
545 >
546 > // ─── resourceWrite ───────────────────────────────────────────────────────────
547 >
548 > /**
549 > * How {@link ResourceWriteParams.data} is placed within the target file.
550 > *
551 > * Each mode interprets {@link ResourceWriteParams.position} differently:
552 > *
553 > * - `truncate` (default): rooted at the **start** of the file. The file is
554 > * truncated at `position` (0 by default) and `data` is written from that
555 > * offset, so the resulting file is `existing[0..position] + data`. With
556 > * `position` omitted this is a full overwrite.
557 > * - `append`: rooted at the **end** of the file. `position` counts bytes
558 > * backwards from EOF, so `position: 0` (the default) writes at EOF —
559 > * POSIX append — and `position: 5` inserts `data` 5 bytes before the
560 > * current EOF, shifting those trailing 5 bytes after the inserted region.
561 > * The server MUST evaluate the effective EOF and write atomically with
562 > * respect to other appenders so concurrent `append` writes do not
563 > * clobber each other.
564 > * - `insert`: rooted at the **start** of the file. `position` (0 by default)
565 > * is the byte offset at which `data` is spliced in; bytes at or after
566 > * `position` are shifted right by `data.length`. `insert` always grows
567 > * the file — use `truncate` to overwrite bytes in place.
568 > *
569 > * @category Commands
570 > */
571 > export const enum ResourceWriteMode {
572 > Truncate = 'truncate',
573 > Append = 'append',
574 > Insert = 'insert',
575 > }
576 >
577 > /**
578 > * Writes content to a file on the server's filesystem.
579 > *
580 > * Binary content (images, etc.) MUST use `base64` encoding. Text content MAY
581 > * use `utf-8` encoding.
582 > *
583 > * If the file does not exist, it is created. If the file already exists, the
584 > * effect on existing bytes depends on {@link ResourceWriteParams.mode}:
585 > * `truncate` (default) overwrites from the chosen offset onward, `append`
586 > * preserves all existing bytes and adds `data` at a position rooted at EOF,
587 > * and `insert` preserves all existing bytes and splices `data` in at an
588 > * offset rooted at the start of the file.
589 > *
590 > * Like all `resource*` methods, `resourceWrite` is symmetrical and MAY be
591 > * sent in either direction.
592 > *
593 > * @category Commands
594 > * @method resourceWrite
595 > * @direction Client ↔ Server
596 > * @messageType Request
597 > * @version 1
598 > * @throws `NotFound` (`-32008`) if the parent directory does not exist.
599 > * @throws `PermissionDenied` (`-32009`) if the client is not permitted to write to the path.
600 > * @throws `AlreadyExists` (`-32010`) if `createOnly` is set and the file already exists.
601 > * @throws `Conflict` (`-32011`) if `ifMatch` is set and the current `etag` does not match.
602 > * @example
603 > * ```jsonc
604 > * // Client → Server
605 > * { "jsonrpc": "2.0", "id": 11, "method": "resourceWrite",
606 > * "params": { "uri": "file:///workspace/hello.txt", "data": "SGVsbG8=",
607 > * "encoding": "base64", "contentType": "text/plain" } }
608 > *
609 > * // Server → Client
610 > * { "jsonrpc": "2.0", "id": 11, "result": {} }
611 > * ```
612 > */
613 > export interface ResourceWriteParams extends BaseParams {
614 > channel: 'ahp-root://';
615 > /** Target file URI on the server filesystem */
616 > uri: URI;
617 > /** Content encoded as a string */
618 > data: string;
619 > /** How `data` is encoded */
620 > encoding: ContentEncoding;
621 > /** Content type (e.g. `"text/plain"`, `"image/png"`) */
622 > contentType?: string;
623 > /**
624 > * If `true`, the server MUST fail if the file already exists instead of
625 > * overwriting it. Useful for safe creation of new files.
626 > */
627 > createOnly?: boolean;
628 > /**
629 > * How `data` is placed within the target file. Defaults to `'truncate'`
630 > * (full overwrite) when omitted. See {@link ResourceWriteMode} for the
631 > * meaning of each mode and how it interprets {@link position}.
632 > */
633 > mode?: ResourceWriteMode;
634 > /**
635 > * Byte offset interpreted according to {@link mode}. Defaults to `0`.
636 > * - `truncate`: offset from the start of the file at which to truncate
637 > * before writing.
638 > * - `append`: bytes back from EOF at which to insert `data`.
639 > * - `insert`: offset from the start of the file at which to splice in
640 > * `data`.
641 > */
642 > position?: number;
643 > /**
644 > * Optimistic-concurrency token previously returned by
645 > * {@link ResourceResolveResult.etag}. When set, the server MUST fail with
646 > * `Conflict` if the current `etag` does not match — preventing lost
647 > * updates between a `resourceResolve` and a subsequent `resourceWrite`.
648 > */
649 > ifMatch?: string;
650 > }
651 >
652 > /**
653 > * Result of the `resourceWrite` command.
654 > *
655 > * An empty object on success.
656 > */
657 > export interface ResourceWriteResult {
658 > }
659 >
660 > // ─── resourceList ────────────────────────────────────────────────────────
661 >
662 > /**
663 > * Lists directory entries at a file URI on the server's filesystem.
664 > *
665 > * This is intended for remote folder pickers and similar UI that needs to let
666 > * users navigate the server's local filesystem.
667 > *
668 > * The server MUST return success only if the target exists and is a directory.
669 > * If the target does not exist, is not a directory, or cannot be accessed, the
670 > * server MUST return a JSON-RPC error.
671 > *
672 > * Like all `resource*` methods, `resourceList` is symmetrical and MAY be
673 > * sent in either direction.
674 > *
675 > * @category Commands
676 > * @method resourceList
677 > * @direction Client ↔ Server
678 > * @messageType Request
679 > * @version 1
680 > * @throws `NotFound` (`-32008`) if the directory does not exist.
681 > * @throws `PermissionDenied` (`-32009`) if the client is not permitted to browse the directory.
682 > */
683 > export interface ResourceListParams extends BaseParams {
684 > channel: 'ahp-root://';
685 > /** Directory URI on the server filesystem */
686 > uri: URI;
687 > }
688 >
689 > /**
690 > * Directory entry returned by `resourceList`.
691 > */
692 > export interface DirectoryEntry {
693 > /** Base name of the entry */
694 > name: string;
695 > /** Whether the entry is a file or directory */
696 > type: 'file' | 'directory';
697 > }
698 >
699 > /**
700 > * Result of the `resourceList` command.
701 > */
702 > export interface ResourceListResult {
703 > /** Entries directly contained in the requested directory */
704 > entries: DirectoryEntry[];
705 > }
706 >
707 > // ─── resourceCopy ────────────────────────────────────────────────────────────
708 >
709 > /**
710 > * Copies a resource from one URI to another on the server's filesystem.
711 > *
712 > * If the destination already exists, it is overwritten unless `failIfExists`
713 > * is set.
714 > *
715 > * Like all `resource*` methods, `resourceCopy` is symmetrical and MAY be
716 > * sent in either direction.
717 > *
718 > * @category Commands
719 > * @method resourceCopy
720 > * @direction Client ↔ Server
721 > * @messageType Request
722 > * @version 1
723 > * @throws `NotFound` (`-32008`) if the source does not exist.
724 > * @throws `PermissionDenied` (`-32009`) if the client is not permitted to read the source or write to the destination.
725 > * @throws `AlreadyExists` (`-32010`) if `failIfExists` is set and the destination already exists.
726 > */
727 > export interface ResourceCopyParams extends BaseParams {
728 > channel: 'ahp-root://';
729 > /** Source URI to copy from */
730 > source: URI;
731 > /** Destination URI to copy to */
732 > destination: URI;
733 > /**
734 > * If `true`, the server MUST fail if the destination already exists instead
735 > * of overwriting it.
736 > */
737 > failIfExists?: boolean;
738 > }
739 >
740 > /**
741 > * Result of the `resourceCopy` command.
742 > *
743 > * An empty object on success.
744 > */
745 > export interface ResourceCopyResult {
746 > }
747 >
748 > // ─── resourceDelete ──────────────────────────────────────────────────────────
749 >
750 > /**
751 > * Deletes a resource at a URI on the server's filesystem.
752 > *
753 > * Like all `resource*` methods, `resourceDelete` is symmetrical and MAY be
754 > * sent in either direction.
755 > *
756 > * @category Commands
757 > * @method resourceDelete
758 > * @direction Client ↔ Server
759 > * @messageType Request
760 > * @version 1
761 > * @throws `NotFound` (`-32008`) if the resource does not exist.
762 > * @throws `PermissionDenied` (`-32009`) if the client is not permitted to delete the resource.
763 > */
764 > export interface ResourceDeleteParams extends BaseParams {
765 > channel: 'ahp-root://';
766 > /** URI of the resource to delete */
767 > uri: URI;
768 > /**
769 > * If `true` and the target is a directory, delete it and all its contents
770 > * recursively. If `false` (default), deleting a non-empty directory MUST fail.
771 > */
772 > recursive?: boolean;
773 > }
774 >
775 > /**
776 > * Result of the `resourceDelete` command.
777 > *
778 > * An empty object on success.
779 > */
780 > export interface ResourceDeleteResult {
781 > }
782 >
783 > // ─── resourceRequest ─────────────────────────────────────────────────────────
784 >
785 > /**
786 > * Requests permission to access a resource on the receiver's filesystem.
787 > *
788 > * `resourceRequest` is symmetrical and MAY be sent in either direction: a
789 > * client asks the server to grant access to a server-side resource, or a
790 > * server asks the client to grant access to a client-side resource. The
791 > * receiver decides whether to allow, deny, or prompt the user for the
792 > * requested access.
793 > *
794 > * If the receiver denies access, it MUST respond with `PermissionDenied`
795 > * (-32009). The error data MAY include a `ResourceRequestParams` value
796 > * describing the access the caller would need to be granted for the
797 > * operation to succeed; see `PermissionDeniedErrorData` in
798 > * `types/errors.ts`.
799 > *
800 > * After a successful `resourceRequest`, the caller MAY use the corresponding
801 > * `resource*` commands (e.g. `resourceRead`, `resourceWrite`) to perform the
802 > * operation. Receivers MAY rescind access at any time by returning
803 > * `PermissionDenied` on subsequent operations.
804 > *
805 > * Either `read`, `write`, or both SHOULD be set to `true`. A request with
806 > * neither flag set is treated as `read: true` by receivers.
807 > *
808 > * @category Commands
809 > * @method resourceRequest
810 > * @direction Client ↔ Server
811 > * @messageType Request
812 > * @version 1
813 > * @throws `PermissionDenied` (`-32009`) if access is denied.
814 > */
815 > export interface ResourceRequestParams extends BaseParams {
816 > channel: 'ahp-root://';
817 > /**
818 > * Resource URI being requested. Typically a `file:` URI on the receiver's
819 > * filesystem, but any URI scheme that the receiver mediates access to is
820 > * allowed.
821 > */
822 > uri: URI;
823 > /** Whether the caller needs read access to the resource. */
824 > read?: boolean;
825 > /** Whether the caller needs write access to the resource. */
826 > write?: boolean;
827 > }
828 >
829 > /**
830 > * Result of the `resourceRequest` command.
831 > *
832 > * An empty object on success.
833 > */
834 > export interface ResourceRequestResult {
835 > }
836 >
837 > // ─── resourceMove ────────────────────────────────────────────────────────────
838 >
839 > /**
840 > * Moves (renames) a resource from one URI to another on the server's filesystem.
841 > *
842 > * If the destination already exists, it is overwritten unless `failIfExists`
843 > * is set.
844 > *
845 > * Like all `resource*` methods, `resourceMove` is symmetrical and MAY be
846 > * sent in either direction.
847 > *
848 > * @category Commands
849 > * @method resourceMove
850 > * @direction Client ↔ Server
851 > * @messageType Request
852 > * @version 1
853 > * @throws `NotFound` (`-32008`) if the source does not exist.
854 > * @throws `PermissionDenied` (`-32009`) if the client is not permitted to move the resource.
855 > * @throws `AlreadyExists` (`-32010`) if `failIfExists` is set and the destination already exists.
856 > */
857 > export interface ResourceMoveParams extends BaseParams {
858 > channel: 'ahp-root://';
859 > /** Source URI to move from */
860 > source: URI;
861 > /** Destination URI to move to */
862 > destination: URI;
863 > /**
864 > * If `true`, the server MUST fail if the destination already exists instead
865 > * of overwriting it.
866 > */
867 > failIfExists?: boolean;
868 > }
869 >
870 > /**
871 > * Result of the `resourceMove` command.
872 > *
873 > * An empty object on success.
874 > */
875 > export interface ResourceMoveResult {
876 > }
877 >
878 > // ─── resourceResolve ─────────────────────────────────────────────────────────
879 >
880 > /**
881 > * Discriminant for {@link ResourceResolveResult.type}.
882 > *
883 > * @category Commands
884 > */
885 > export const enum ResourceType {
886 > File = 'file',
887 > Directory = 'directory',
888 > Symlink = 'symlink',
889 > }
890 >
891 > /**
892 > * Resolves a resource — the combination of POSIX `stat` and `realpath`.
893 > *
894 > * `resourceResolve` returns metadata about the resource together with its
895 > * canonical URI after symlink resolution. Use this in place of any
896 > * `resourceExists` shim: a missing resource MUST surface as a `NotFound`
897 > * JSON-RPC error rather than a success with a sentinel value. Callers that
898 > * truly need a boolean check should attempt `resourceResolve` and treat
899 > * `NotFound` as "does not exist".
900 > *
901 > * Like all `resource*` methods, `resourceResolve` is symmetrical and MAY be
902 > * sent in either direction.
903 > *
904 > * @category Commands
905 > * @method resourceResolve
906 > * @direction Client ↔ Server
907 > * @messageType Request
908 > * @version 1
909 > * @throws `NotFound` (`-32008`) if the resource does not exist.
910 > * @throws `PermissionDenied` (`-32009`) if the caller is not permitted to stat the URI.
911 > * @example
912 > * ```jsonc
913 > * // Client → Server
914 > * { "jsonrpc": "2.0", "id": 20, "method": "resourceResolve",
915 > * "params": { "channel": "ahp-root://", "uri": "file:///workspace/hello.txt" } }
916 > *
917 > * // Server → Client
918 > * { "jsonrpc": "2.0", "id": 20, "result": {
919 > * "uri": "file:///workspace/hello.txt",
920 > * "type": "file",
921 > * "size": 5,
922 > * "mtime": "2026-01-15T12:34:56.789Z",
923 > * "etag": "W/\"5-abc123\""
924 > * }}
925 > * ```
926 > */
927 > export interface ResourceResolveParams extends BaseParams {
928 > channel: 'ahp-root://';
929 > /** URI to resolve */
930 > uri: URI;
931 > /**
932 > * When `true` (default), follow symlinks and report the metadata of the
933 > * link target — and set `uri` in the result to the canonical (realpath)
934 > * URI. When `false`, stat the link itself (lstat semantics) and report
935 > * `type: 'symlink'`.
936 > */
937 > followSymlinks?: boolean;
938 > }
939 >
940 > /**
941 > * Result of the `resourceResolve` command.
942 > */
943 > export interface ResourceResolveResult {
944 > /**
945 > * Canonical URI after symlink resolution. Equal to the requested URI when
946 > * `followSymlinks` is `false` or the URI does not traverse a symlink.
947 > */
948 > uri: URI;
949 > /** Resource kind. */
950 > type: ResourceType;
951 > /**
952 > * Size in bytes. Omitted for directories when the provider cannot
953 > * cheaply compute it.
954 > */
955 > size?: number;
956 > /** Last-modified time in ISO 8601 format, when known. */
957 > mtime?: string;
958 > /** Creation time in ISO 8601 format, when known. */
959 > ctime?: string;
960 > /** Sniffed MIME type, when known (e.g. `"text/plain"`, `"image/png"`). */
961 > contentType?: string;
962 > /**
963 > * Opaque per-provider version token. When present, pass it as
964 > * {@link ResourceWriteParams.ifMatch} on a subsequent `resourceWrite` to
965 > * detect concurrent modifications.
966 > */
967 > etag?: string;
968 > }
969 >
970 > // ─── resourceMkdir ───────────────────────────────────────────────────────────
971 >
972 > /**
973 > * Creates a directory on the server's filesystem with `mkdir -p` semantics.
974 > *
975 > * The server MUST create any missing parent directories. Creating a
976 > * directory that already exists is a no-op success. If `uri` already
977 > * exists but is **not** a directory, the server MUST fail with
978 > * `AlreadyExists`.
979 > *
980 > * Like all `resource*` methods, `resourceMkdir` is symmetrical and MAY be
981 > * sent in either direction.
982 > *
983 > * @category Commands
984 > * @method resourceMkdir
985 > * @direction Client ↔ Server
986 > * @messageType Request
987 > * @version 1
988 > * @throws `PermissionDenied` (`-32009`) if the caller is not permitted to create the directory.
989 > * @throws `AlreadyExists` (`-32010`) if `uri` already exists as a non-directory.
990 > */
991 > export interface ResourceMkdirParams extends BaseParams {
992 > channel: 'ahp-root://';
993 > /** Directory URI to create (parents created as needed). */
994 > uri: URI;
995 > }
996 >
997 > /**
998 > * Result of the `resourceMkdir` command.
999 > *
1000 > * An empty object on success.
1001 > */
1002 > export interface ResourceMkdirResult {
1003 > }
1004 >
1005 > // ─── authenticate ────────────────────────────────────────────────────────────
1006 >
1007 > /**
1008 > * Pushes a ****** for a protected resource. The `resource` field MUST
1009 > * match a protected-resource identifier the client has discovered from the
1010 > * server — whether declared statically in `AgentInfo.protectedResources`,
1011 > * or discovered dynamically from a live `McpServerAuthRequiredState.resource`
1012 > * or `ToolCallAuthRequiredState.auth.resource` (both surfaced only once the
1013 > * corresponding MCP server or tool call actually challenges for auth).
1014 > * Servers MUST accept any `resource` value they have themselves advertised
1015 > * through one of these three mechanisms.
1016 > *
1017 > * Tokens are delivered using [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750)
1018 > * (****** Usage) semantics. The client obtains the token from the
1019 > * authorization server(s) listed in the resource's metadata and pushes it
1020 > * to the server via this command.
1021 > *
1022 > * @category Commands
1023 > * @method authenticate
1024 > * @direction Client → Server
1025 > * @messageType Request
1026 > * @version 1
1027 > * @see {@link /specification/authentication | Authentication}
1028 > * @example
1029 > * ```jsonc
1030 > * // Client → Server
1031 > * { "jsonrpc": "2.0", "id": 3, "method": "authenticate",
1032 > * "params": { "channel": "ahp-root://", "resource": "https://api.github.com", "token": "gho_xxxx" } }
1033 > *
1034 > * // Server → Client (success)
1035 > * { "jsonrpc": "2.0", "id": 3, "result": {} }
1036 > *
1037 > * // Server → Client (failure — invalid token)
1038 > * { "jsonrpc": "2.0", "id": 3, "error": { "code": -32007, "message": "Invalid token" } }
1039 > * ```
1040 > */
1041 > export interface AuthenticateParams extends BaseParams {
1042 > channel: 'ahp-root://';
1043 > /**
1044 > * The protected resource identifier. MUST match a `resource` value the
1045 > * server has advertised — via `ProtectedResourceMetadata` in
1046 > * `AgentInfo.protectedResources`, or via a live
1047 > * `McpServerAuthRequiredState.resource` / `ToolCallAuthRequiredState.auth.resource`.
1048 > */
1049 > resource: string;
1050 > /** ****** obtained from the resource's authorization server */
1051 > token: string;
1052 > /**
1053 > * OAuth scopes the token grants, when known. Lets the server determine
1054 > * whether a specific challenge — e.g. the `requiredScopes` on a live
1055 > * `McpServerAuthRequiredState` or `ToolCallAuthRequiredState.auth` — is
1056 > * satisfied without decoding the (opaque, server-specific) token itself.
1057 > * Omit when the client doesn't track granted scopes separately from the
1058 > * token.
1059 > */
1060 > scopes?: string[];
1061 > }
1062 >
1063 > /**
1064 > * Result of the `authenticate` command.
1065 > *
1066 > * An empty object on success. If the token is invalid or the resource is
1067 > * unrecognized, the server MUST return a JSON-RPC error (e.g. `AuthRequired`
1068 > * `-32007` or `InvalidParams` `-32602`).
1069 > */
1070 > export interface AuthenticateResult {
1071 > }
src/vs/platform/agentHost/common/agentHostSchema.ts 580 covered LOC · 36 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- agentHostSchema.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 { structuralEquals } from '../../../base/common/equals.js';
8 > import { ConfigurationTarget, type IConfigurationService, type IConfigurationValue } from '../../configuration/common/configuration.js';
9 > import type { IMcpServerConfiguration } from '../../mcp/common/mcpPlatformTypes.js';
10 > import { TelemetryConfiguration, TelemetryLevel } from '../../telemetry/common/telemetry.js';
11 > import { SessionConfigKey } from './sessionConfigKeys.js';
12 > import type { SessionConfigPropertySchema, SessionConfigSchema } from './state/protocol/commands.js';
13 > import { JsonRpcErrorCodes, ProtocolError } from './state/sessionProtocol.js';
14 >
15 > // ---- Schema builder --------------------------------------------------------
16 >
17 > /**
18 > * A schema property with a phantom TypeScript type and a precomputed
19 > * runtime validator.
20 > *
21 > * The `<T>` type parameter is the developer's assertion about the
22 > * property's runtime shape; the validator derived from `protocol`
23 > * (`type`, `enum`, `items`, `properties`, `required`) enforces it at
24 > * runtime.
25 > */
26 > export interface ISchemaProperty<T> {
27 > readonly protocol: SessionConfigPropertySchema;
28 > /**
29 > * Returns `true` iff `value` conforms to {@link protocol}. Narrows
30 > * the type to `T` for callers. The boolean form is preferred for
31 > * control flow; use {@link assertValid} when you want a descriptive
32 > * error for the offending path.
33 > */
34 > validate(value: unknown): value is T;
35 > /**
36 > * Throws a {@link ProtocolError} with `JsonRpcErrorCodes.InvalidParams`
37 > * describing the offending path (e.g. `'permissions.allow[2]'`) when
38 > * `value` does not conform to {@link protocol}. Otherwise returns and
39 > * narrows the type to `T`.
40 > *
41 > * @param path Dotted path prefix to embed in error messages. Defaults
42 > * to empty (the value itself).
43 > */
44 > assertValid(value: unknown, path?: string): asserts value is T;
45 > }
46 >
47 > /**
48 > * Defines a strongly-typed schema property whose runtime validator is
49 > * derived from the supplied JSON-schema descriptor.
50 > */
51 > export function schemaProperty<T>(protocol: SessionConfigPropertySchema): ISchemaProperty<T> {
52 > const assertFn = buildAssert(protocol);
53 > const assertValid = (value: unknown, path: string = ''): asserts value is T => assertFn(value, path);
54 > const validate = (value: unknown): value is T => {
55 try {
56 assertFn(value, '');
60 }
61 };
62 > return { protocol, validate, assertValid }; agentHostSchema.ts
63 > }
64 >
65 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
66 > export type SchemaDefinition = Record<string, ISchemaProperty<any>>;
67 >
68 > export type SchemaValue<P> = P extends ISchemaProperty<infer T> ? T : never;
69 >
70 > export type SchemaValues<D extends SchemaDefinition> = {
71 > [K in keyof D]?: SchemaValue<D[K]>;
72 > };
73 >
74 > /**
75 > * A bundle of named schema properties plus helpers for serializing to the
76 > * protocol shape, validating a values bag at write sites, and validating
77 > * a single key at read sites.
78 > */
79 > export interface ISchema<D extends SchemaDefinition> {
80 > readonly definition: D;
81 > /** Returns the protocol-serializable schema for this bundle. */
82 > toProtocol(): SessionConfigSchema;
83 > /**
84 > * Validates each known key in `values` against its schema and returns
85 > * a new plain record. Throws a {@link ProtocolError} with a path like
86 > * `'permissions.allow[2]'` when any supplied value fails validation.
87 > * Unknown keys are passed through untouched for forward-compatibility.
88 > */
89 > values(values: SchemaValues<D>): Record<string, unknown>;
90 > /**
91 > * Returns `true` iff `value` validates against the schema for `key`.
92 > * Unknown keys return `false`.
93 > */
94 > validate<K extends keyof D & string>(key: K, value: unknown): value is SchemaValue<D[K]>;
95 > /**
96 > * Throws a {@link ProtocolError} describing the offending path when
97 > * `value` does not validate against the schema for `key`, or when
98 > * `key` is not defined in the schema.
99 > */
100 > assertValid<K extends keyof D & string>(key: K, value: unknown): asserts value is SchemaValue<D[K]>;
101 > /**
102 > * Returns a fully-typed values bag by validating each key of the
103 > * schema against `values` and falling back to the default when
104 > * the incoming value is missing or fails validation.
105 > *
106 > * Semantics: for every key declared in the schema `definition`:
107 > * - if `values[key]` validates, it is kept;
108 > * - else if `key` is present in `defaults`, the default is used;
109 > * - else the key is omitted from the result.
110 > *
111 > * This means callers MAY supply defaults for only a subset of the
112 > * schema — keys not present in `defaults` are simply left unset
113 > * when the incoming value is missing or invalid. This is useful
114 > * when some properties (e.g. per-session `permissions`) should be
115 > * inherited from a higher scope rather than materialized on every
116 > * new session.
117 > *
118 > * Intended for sanitizing untrusted input at protocol boundaries
119 > * (e.g. `resolveSessionConfig`). Keys that fail validation are
120 > * silently replaced with their default or dropped; use
121 > * {@link values} or {@link assertValid} when you want a descriptive
122 > * {@link ProtocolError} instead.
123 > */
124 > validateOrDefault<T extends Partial<{ [K in keyof D]: SchemaValue<D[K]> }>>(values: { [K in keyof T]?: unknown } | undefined, defaults: T): T;
125 > }
126 >
127 > export function createSchema<D extends SchemaDefinition>(definition: D): ISchema<D> {
128 > return {
129 > definition,
130 > toProtocol(): SessionConfigSchema {
131 const properties: Record<string, SessionConfigPropertySchema> = {};
132 for (const key of Object.keys(definition)) {
135 return { type: 'object', properties };
136 },
137 > values(values) { agentHostSchema.ts
138 const raw = values as Record<string, unknown>;
139 for (const key of Object.keys(definition)) {
149 return { ...raw };
150 },
151 > validate<K extends keyof D & string>(key: K, value: unknown): value is SchemaValue<D[K]> { agentHostSchema.ts
152 const prop = definition[key];
153 return prop ? prop.validate(value) : false;
154 },
155 > assertValid<K extends keyof D & string>(key: K, value: unknown): asserts value is SchemaValue<D[K]> { agentHostSchema.ts
156 const prop: ISchemaProperty<unknown> | undefined = definition[key];
157 if (!prop) {
163 narrowed.assertValid(value, key);
164 },
165 > validateOrDefault<T extends Partial<{ [K in keyof D]: SchemaValue<D[K]> }>>(values: { [K in keyof T]?: unknown } | undefined, defaults: T): T { agentHostSchema.ts
166 const result: Record<string, unknown> = {};
167 const raw: { [K in keyof T]?: unknown } = values ?? {};
179 return result as T;
180 },
182 > }
183 >
184 > // ---- Validator derivation --------------------------------------------------
185 >
186 > /**
187 > * A validator that throws a {@link ProtocolError} annotated with the
188 > * offending path when `value` does not conform, or returns normally
189 > * when it does.
190 > */
191 > type AssertValidator = (value: unknown, path: string) => void;
192 >
193 > function buildAssert(schema: SessionConfigPropertySchema): AssertValidator {
194 > if (schema.type === 'object' && schema.properties) {
195 > const propAsserts: Record<string, AssertValidator> = {};
196 > for (const key of Object.keys(schema.properties)) {
197 > propAsserts[key] = buildAssert(schema.properties[key] as SessionConfigPropertySchema);
198 > }
199 > const required = new Set(schema.required ?? []);
200 > return (value, path) => {
201 if (typeof value !== 'object' || value === null || Array.isArray(value)) {
202 throw invalidParams(path, 'object', value);
214 }
215 };
217 > if (schema.type === 'array' && schema.items) {
218 > const itemAssert = buildAssert(schema.items as SessionConfigPropertySchema);
219 > return (value, path) => {
220 if (!Array.isArray(value)) {
221 throw invalidParams(path, 'array', value);
225 }
226 };
228 > return buildPrimitiveAssert(schema);
229 > }
230 >
231 > function buildPrimitiveAssert(schema: SessionConfigPropertySchema): AssertValidator {
232 > const enumDynamic = schema.enumDynamic === true;
233 > return (value, path) => {
234 switch (schema.type) {
235 case 'string': if (typeof value !== 'string') { throw invalidParams(path, 'string', value); } break;
243 }
244 };
246 >
247 function invalidParams(path: string, expected: string, value: unknown): ProtocolError {
248 return new ProtocolError(JsonRpcErrorCodes.InvalidParams, `Invalid value at '${path || '<root>'}': expected ${expected}, got ${safeStringify(value)}`);
249 }
251 function joinPath(parent: string, key: string): string {
252 return parent ? `${parent}.${key}` : key;
253 }
255 function safeStringify(value: unknown): string {
256 try {
260 }
261 }
263 > // ---- Platform-owned schema -------------------------------------------------
264 >
265 > export type AutoApproveLevel = 'default' | 'assisted' | 'autoApprove';
266 >
267 > export type SessionMode = 'interactive' | 'plan' | 'autopilot';
268 >
269 > export interface IPermissionsValue {
270 > readonly allow: readonly string[];
271 > readonly deny: readonly string[];
272 > }
273 >
274 > const permissionsProperty = schemaProperty<IPermissionsValue>({
275 > type: 'object',
276 > title: localize('agentHost.sessionConfig.permissions', "Permissions"),
277 > description: localize('agentHost.sessionConfig.permissionsDescription', "Per-tool session permissions. Updated automatically when approving a tool \"in this Session\"."),
278 > properties: {
279 > allow: {
280 > type: 'array',
281 > title: localize('agentHost.sessionConfig.permissions.allow', "Allowed tools"),
282 > items: {
283 > type: 'string',
284 > title: localize('agentHost.sessionConfig.permissions.toolName', "Tool name"),
285 > },
286 > },
287 > deny: {
288 > type: 'array',
289 > title: localize('agentHost.sessionConfig.permissions.deny', "Denied tools"),
290 > items: {
291 > type: 'string',
292 > title: localize('agentHost.sessionConfig.permissions.toolName', "Tool name"),
293 > },
294 > },
295 > },
296 > default: { allow: [], deny: [] },
297 > sessionMutable: true,
298 > });
299 >
300 > /**
301 > * Session-config properties owned by the platform itself — i.e. consumed
302 > * by the agent host rather than by any particular agent.
303 > *
304 > * Agents extend this schema by spreading `platformSessionSchema.definition`
305 > * into their own {@link createSchema} call together with any
306 > * provider-specific properties.
307 > */
308 > export const platformSessionSchema = createSchema({
309 > [SessionConfigKey.AutoApprove]: schemaProperty<AutoApproveLevel>({
310 > type: 'string',
311 > title: localize('agentHost.sessionConfig.autoApprove', "Approvals"),
312 > description: localize('agentHost.sessionConfig.autoApproveDescription', "Tool approval behavior for this session"),
313 > enum: ['default', 'assisted', 'autoApprove'],
314 > enumLabels: [
315 > localize('agentHost.sessionConfig.autoApprove.default', "Default approvals"),
316 > localize('agentHost.sessionConfig.autoApprove.assisted', "Assisted permissions"),
317 > localize('agentHost.sessionConfig.autoApprove.bypass', "Allow all"),
318 > ],
319 > enumDescriptions: [
320 > localize('agentHost.sessionConfig.autoApprove.defaultDescription', "Asks when approval settings don't apply"),
321 > localize('agentHost.sessionConfig.autoApprove.assistedDescription', "Evaluates risk before running tools"),
322 > localize('agentHost.sessionConfig.autoApprove.bypassDescription', "Runs tool calls without asking"),
323 > ],
324 > default: 'default',
325 > sessionMutable: true,
326 > }),
327 > [SessionConfigKey.Permissions]: permissionsProperty,
328 > [SessionConfigKey.Mode]: schemaProperty<SessionMode>({
329 > type: 'string',
330 > title: localize('agentHost.sessionConfig.mode', "Agent Mode"),
331 > description: localize('agentHost.sessionConfig.modeDescription', "How the agent should approach this turn"),
332 > enum: ['interactive', 'plan', 'autopilot'],
333 > enumLabels: [
334 > localize('agentHost.sessionConfig.mode.interactive', "Interactive"),
335 > localize('agentHost.sessionConfig.mode.plan', "Plan"),
336 > localize('agentHost.sessionConfig.mode.autopilot', "Autopilot"),
337 > ],
338 > enumDescriptions: [
339 > localize('agentHost.sessionConfig.mode.interactiveDescription', "Step-by-step collaboration"),
340 > localize('agentHost.sessionConfig.mode.planDescription', "Plan first, execute when ready"),
341 > localize('agentHost.sessionConfig.mode.autopilotDescription', "Autonomously iterates from start to finish"),
342 > ],
343 > default: 'interactive',
344 > sessionMutable: true,
345 > }),
346 > });
347 >
348 > /**
349 > * Rewrites a legacy `autoApprove='autopilot'` config value — used before
350 > * Autopilot moved from the `autoApprove` axis onto the orthogonal `mode`
351 > * axis — into the current two-axis shape:
352 > *
353 > * - `autoApprove='autopilot'` + `mode='plan'` → `mode='plan'`, `autoApprove='default'`
354 > * (legacy `plan` took precedence over autopilot when resolving the SDK mode).
355 > * - `autoApprove='autopilot'` + any other mode → `mode='autopilot'`, `autoApprove='default'`.
356 > *
357 > * Returns a shallow copy with the migration applied, or the original
358 > * reference unchanged when no legacy value is present. Safe to call on
359 > * `undefined`.
360 > *
361 > * Without this, a session persisted (or a "remembered" picker value seeded)
362 > * with `autoApprove='autopilot'` would fail the new schema's enum validation
363 > * and silently fall back to `default`, downgrading the session from
364 > * autonomous Autopilot to manual per-tool confirmation.
365 > */
366 > export function migrateLegacyAutopilotConfig<T extends Record<string, unknown> | undefined>(config: T): T {
367 if (!config || config[SessionConfigKey.AutoApprove] !== 'autopilot') {
368 return config;
375 return migrated as T;
376 }
378 > /**
379 > * Root (agent host) config properties owned by the platform itself.
380 > *
381 > * Root config acts as the baseline that applies to every session:
382 > *
383 > * - {@link SessionConfigKey.Permissions} — host-wide allow/deny lists
384 > * unioned with each session's own permissions when evaluating tool
385 > * auto-approval. See `SessionPermissionManager` for the evaluation
386 > * rules.
387 > */
388 > export const AgentHostTelemetryLevelConfigKey = 'telemetryLevel';
389 >
390 > /** Legacy Copilot Chat debug switch that disables `request.repoInfo` collection. */
391 > export const AgentHostDisableRepoInfoTelemetryConfigKey = 'disableRepoInfoTelemetry';
392 >
393 > /** VS Code setting forwarded into {@link AgentHostDisableRepoInfoTelemetryConfigKey}. */
394 > export const DISABLE_REPO_INFO_TELEMETRY_SETTING_ID = 'chat.advanced.debug.disableRepoInfoTelemetry';
395 >
396 > /**
397 > * Root config key forwarded from the renderer when VS Code's
398 > * `chat.sessionSync.enabled` setting changes. Controls the `remote` flag
399 > * passed to the copilot-sdk `CopilotClientOptions`.
400 > */
401 > export const AgentHostSessionSyncEnabledConfigKey = 'sessionSyncEnabled';
402 >
403 > /**
404 > * Root config key forwarded from the renderer carrying the experiment-aware
405 > * value of `chat.agentHost.codexAgent.enabled`. The host registers the Codex
406 > * provider when this is `true`; disabling requires an agent host restart.
407 > */
408 > export const AgentHostCodexEnabledConfigKey = 'codexAgentEnabled';
409 >
410 > /**
411 > * Root config key forwarded from the renderer when VS Code's
412 > * `chat.tools.terminal.enableAutoApprove` setting changes. Controls whether
413 > * agent-host shell permission checks may apply terminal auto-approve rules.
414 > */
415 > export const AgentHostTerminalAutoApproveEnabledConfigKey = 'terminalAutoApproveEnabled';
416 >
417 > /**
418 > * The VS Code setting ID for terminal auto approve enablement. Defined here so
419 > * renderer-side agent-host clients can forward it without importing from
420 > * workbench terminal contributions.
421 > */
422 > export const TERMINAL_AUTO_APPROVE_ENABLED_SETTING_ID = 'chat.tools.terminal.enableAutoApprove';
423 >
424 > /**
425 > * Root config key forwarded from the renderer when VS Code's
426 > * `chat.tools.global.autoApprove` setting changes. When `true`, the global
427 > * auto-approve ("approve everything") setting is enabled and the agent host
428 > * treats every tool call as auto-approved — equivalent to a session running
429 > * with Allow all.
430 > */
431 > export const AgentHostGlobalAutoApproveEnabledConfigKey = 'globalAutoApproveEnabled';
432 >
433 > /**
434 > * The VS Code setting ID for global auto approve. Defined here so renderer-side
435 > * agent-host clients can forward it without importing from `workbench/contrib/chat`.
436 > */
437 > export const GLOBAL_AUTO_APPROVE_SETTING_ID = 'chat.tools.global.autoApprove';
438 >
439 > /**
440 > * Root config key forwarded from the renderer when VS Code's `chat.autoReply`
441 > * setting changes. When `true`, the agent host auto-answers `ask_user`
442 > * questions instead of blocking on the user — the user is treated as
443 > * unavailable and the agent is told to use its best judgment, mirroring the
444 > * behavior of `autopilot` mode.
445 > */
446 > export const AgentHostAutoReplyEnabledConfigKey = 'autoReplyEnabled';
447 >
448 > /**
449 > * The VS Code setting ID for auto-reply. Defined here so renderer-side
450 > * agent-host clients can forward it without importing from `workbench/contrib/chat`.
451 > */
452 > export const AUTO_REPLY_SETTING_ID = 'chat.autoReply';
453 >
454 > // Root config key forwarded from the renderer when Copilot Chat's `github.copilot.chat.preferLongContext.enabled` setting changes.
455 > export const AgentHostPreferLongContextEnabledConfigKey = 'preferLongContextEnabled';
456 >
457 > // The Copilot Chat setting ID for preferring long context, forwarded into the agent host root config.
458 > export const PREFER_LONG_CONTEXT_SETTING_ID = 'github.copilot.chat.preferLongContext.enabled';
459 >
460 > /** Root config key forwarded from the renderer for automatic OS system proxy discovery. */
461 > export const AgentHostSystemProxyEnabledConfigKey = 'systemProxyEnabled';
462 >
463 > /**
464 > * Root config key forwarded from the renderer when VS Code's
465 > * `chat.tools.terminal.autoApprove` setting changes. Holds the effective
466 > * terminal auto-approve rule object for agent-host shell permission checks.
467 > */
468 > export const AgentHostTerminalAutoApproveRulesConfigKey = 'terminalAutoApproveRules';
469 >
470 > export interface IAgentHostTerminalAutoApproveRule {
471 > readonly approve: boolean;
472 > readonly matchCommandLine?: boolean;
473 > }
474 >
475 > export type AgentHostTerminalAutoApproveRuleValue = boolean | null | IAgentHostTerminalAutoApproveRule;
476 > export type AgentHostTerminalAutoApproveRules = Record<string, AgentHostTerminalAutoApproveRuleValue>;
477 >
478 > /**
479 > * The VS Code setting IDs for terminal auto approve rules. Defined here so
480 > * renderer-side agent-host clients can forward them without importing from
481 > * workbench terminal contributions.
482 > */
483 > export const TERMINAL_AUTO_APPROVE_SETTING_ID = 'chat.tools.terminal.autoApprove';
484 > export const TERMINAL_IGNORE_DEFAULT_AUTO_APPROVE_RULES_SETTING_ID = 'chat.tools.terminal.ignoreDefaultAutoApproveRules';
485 >
486 > export function getAgentHostTerminalAutoApproveRulesConfig(configurationService: IConfigurationService): AgentHostTerminalAutoApproveRules {
487 const config = configurationService.getValue<AgentHostTerminalAutoApproveRules | undefined>(TERMINAL_AUTO_APPROVE_SETTING_ID);
488 const configInspectValue = configurationService.inspect<Readonly<AgentHostTerminalAutoApproveRules>>(TERMINAL_AUTO_APPROVE_SETTING_ID);
490 return normalizeAgentHostTerminalAutoApproveRulesConfig(config, configInspectValue, ignoreDefaults);
491 }
493 > export function normalizeAgentHostTerminalAutoApproveRulesConfig(config: AgentHostTerminalAutoApproveRules | undefined, configInspectValue: IConfigurationValue<Readonly<AgentHostTerminalAutoApproveRules>>, ignoreDefaults: boolean): AgentHostTerminalAutoApproveRules {
494 > if (!config) { agentHostSchema.ts
495 return {};
496 }
498 > const rules: AgentHostTerminalAutoApproveRules = {};
499 > for (const [key, value] of Object.entries(config)) {
500 > if (ignoreDefaults && isDefaultOnlyAutoApproveRule(key, value, configInspectValue)) {
501 > continue; agentHostSchema.ts
502 > }
503 > rules[key] = value; agentHostSchema.ts
504 > }
505 > return rules;
506 > }
508 > function isDefaultOnlyAutoApproveRule(key: string, value: AgentHostTerminalAutoApproveRuleValue, configInspectValue: IConfigurationValue<Readonly<AgentHostTerminalAutoApproveRules>>): boolean { agentHostSchema.ts
509 > const defaultValue = configInspectValue.default?.value;
510 > const isDefaultRule = hasMatchingRule(defaultValue, key, value);
511 > if (!isDefaultRule) {
512 > return false; agentHostSchema.ts
513 > }
515 > const sourceTarget = getAutoApproveRuleSourceTarget(key, value, configInspectValue);
516 >
517 > return sourceTarget === ConfigurationTarget.DEFAULT;
518 > }
520 > function getAutoApproveRuleSourceTarget(key: string, value: AgentHostTerminalAutoApproveRuleValue, configInspectValue: IConfigurationValue<Readonly<AgentHostTerminalAutoApproveRules>>): ConfigurationTarget { agentHostSchema.ts
521 > if (hasMatchingRule(configInspectValue.workspaceFolderValue, key, value)) {
522 return ConfigurationTarget.WORKSPACE_FOLDER;
523 }
524 > if (hasMatchingRule(configInspectValue.workspaceValue, key, value)) { agentHostSchema.ts
525 return ConfigurationTarget.WORKSPACE;
526 }
527 > if (hasMatchingRule(configInspectValue.userRemoteValue, key, value)) { agentHostSchema.ts
528 return ConfigurationTarget.USER_REMOTE;
529 }
530 > if (hasMatchingRule(configInspectValue.userLocalValue, key, value)) { agentHostSchema.ts
531 return ConfigurationTarget.USER_LOCAL;
532 }
533 > if (hasMatchingRule(configInspectValue.userValue, key, value)) { agentHostSchema.ts
534 return ConfigurationTarget.USER;
535 }
536 > if (hasMatchingRule(configInspectValue.applicationValue, key, value)) { agentHostSchema.ts
537 return ConfigurationTarget.APPLICATION;
538 }
539 > return ConfigurationTarget.DEFAULT; agentHostSchema.ts
540 > }
542 > function hasMatchingRule(config: Readonly<AgentHostTerminalAutoApproveRules> | undefined, key: string, value: AgentHostTerminalAutoApproveRuleValue): boolean { agentHostSchema.ts
543 > return !!config && Object.prototype.hasOwnProperty.call(config, key) && structuralEquals(config[key], value);
544 > }
546 > /**
547 > * Root config key holding agent-host-level MCP server definitions.
548 > *
549 > * The value is a map of server name → {@link IMcpServerConfiguration}
550 > * (the same `servers` shape used by `mcp.json`). These servers are
551 > * exposed to every session created by the host, merged with any
552 > * plugin-provided MCP servers when launching the copilot-sdk client.
553 > */
554 > export const AgentHostMcpServersConfigKey = 'mcpServers';
555 >
556 > /**
557 > * Map of server name → MCP server configuration, as stored in the
558 > * {@link AgentHostMcpServersConfigKey} root config value.
559 > */
560 > export type AgentHostMcpServers = Record<string, IMcpServerConfiguration>;
561 >
562 > /**
563 > * The VS Code setting ID for session sync. Defined here so the platform
564 > * layer (renderer-side forwarding) can reference it without importing from
565 > * `workbench/contrib/chat`.
566 > */
567 > export const SESSION_SYNC_ENABLED_SETTING_ID = 'chat.sessionSync.enabled';
568 >
569 > export function telemetryLevelToAgentHostConfigValue(telemetryLevel: TelemetryLevel): TelemetryConfiguration {
570 switch (telemetryLevel) {
571 case TelemetryLevel.NONE:
579 }
580 }
582 > export function agentHostConfigValueToTelemetryLevel(value: unknown): TelemetryLevel | undefined {
583 switch (value) {
584 case TelemetryConfiguration.OFF:
594 }
595 }
597 > /**
598 > * Field descriptors for a single MCP server entry, shared by the stdio and
599 > * http shapes. The agent-host config schema has no `oneOf`, so both variants'
600 > * fields are described together; `type` selects which fields apply
601 > * (`stdio` uses `command`/`args`/`env`/`cwd`, `http` uses `url`/`headers`).
602 > */
603 > const mcpServerConfigProperties: Record<string, SessionConfigPropertySchema> = {
604 > type: {
605 > type: 'string',
606 > title: localize('agentHost.config.mcpServers.type.title', "Server Type"),
607 > description: localize('agentHost.config.mcpServers.type.description', "The transport used to reach the server: `stdio` for a local command, `http` for a remote endpoint."),
608 > enum: ['stdio', 'http'],
609 > },
610 > command: {
611 > type: 'string',
612 > title: localize('agentHost.config.mcpServers.command.title', "Command"),
613 > description: localize('agentHost.config.mcpServers.command.description', "For `stdio` servers, the executable to spawn."),
614 > },
615 > args: {
616 > type: 'array',
617 > title: localize('agentHost.config.mcpServers.args.title', "Arguments"),
618 > description: localize('agentHost.config.mcpServers.args.description', "For `stdio` servers, the arguments passed to the command."),
619 > items: { type: 'string', title: localize('agentHost.config.mcpServers.arg.title', "Argument") },
620 > },
621 > env: {
622 > type: 'object',
623 > title: localize('agentHost.config.mcpServers.env.title', "Environment"),
624 > description: localize('agentHost.config.mcpServers.env.description', "For `stdio` servers, environment variables set on the spawned process."),
625 > },
626 > cwd: {
627 > type: 'string',
628 > title: localize('agentHost.config.mcpServers.cwd.title', "Working Directory"),
629 > description: localize('agentHost.config.mcpServers.cwd.description', "For `stdio` servers, the working directory the command runs in."),
630 > },
631 > url: {
632 > type: 'string',
633 > title: localize('agentHost.config.mcpServers.url.title', "URL"),
634 > description: localize('agentHost.config.mcpServers.url.description', "For `http` servers, the endpoint URL of the MCP server."),
635 > },
636 > headers: {
637 > type: 'object',
638 > title: localize('agentHost.config.mcpServers.headers.title', "Headers"),
639 > description: localize('agentHost.config.mcpServers.headers.description', "For `http` servers, HTTP headers sent with every request."),
640 > },
641 > };
642 >
643 > /**
644 > * Documents the value shape of the {@link AgentHostMcpServersConfigKey} map.
645 > *
646 > * The config value is a map of server name → server config. The schema
647 > * language has no `additionalProperties`, so the per-entry shape is attached
648 > * under a placeholder key (`<serverName>`) rather than at the map level —
649 > * this keeps the field descriptions discoverable without the runtime
650 > * validator mistaking a real server named e.g. `command` for the `command`
651 > * field. Real entries (keyed by actual server names) are passed through.
652 > */
653 > const mcpServersValueProperties: Record<string, SessionConfigPropertySchema> = {
654 > '<serverName>': {
655 > type: 'object',
656 > title: localize('agentHost.config.mcpServers.entry.title', "MCP Server"),
657 > description: localize('agentHost.config.mcpServers.entry.description', "A single MCP server entry. The property key is the server name."),
658 > properties: mcpServerConfigProperties,
659 > },
660 > };
661 >
662 > export const platformRootSchema = createSchema({
663 > [SessionConfigKey.Permissions]: permissionsProperty,
664 > [AgentHostDisableRepoInfoTelemetryConfigKey]: schemaProperty<boolean>({
665 > type: 'boolean',
666 > title: localize('agentHost.config.disableRepoInfoTelemetry.title', "Disable Repository Information Telemetry"),
667 > description: localize('agentHost.config.disableRepoInfoTelemetry.description', "Whether repository information telemetry is disabled for Agent Host sessions."),
668 > default: false,
669 > }),
670 > [AgentHostTelemetryLevelConfigKey]: schemaProperty<TelemetryConfiguration>({
671 > type: 'string',
672 > title: localize('agentHost.config.telemetryLevel.title', "Telemetry Level"),
673 > description: localize('agentHost.config.telemetryLevel.description', "Most restrictive telemetry level requested by connected clients."),
674 > enum: [TelemetryConfiguration.ON, TelemetryConfiguration.ERROR, TelemetryConfiguration.CRASH, TelemetryConfiguration.OFF],
675 > default: TelemetryConfiguration.ON,
676 > }),
677 > [AgentHostSessionSyncEnabledConfigKey]: schemaProperty<boolean>({
678 > type: 'boolean',
679 > title: localize('agentHost.config.sessionSyncEnabled.title', "Session Sync"),
680 > description: localize('agentHost.config.sessionSyncEnabled.description', "Whether remote session sync is enabled for the copilot-sdk CLI."),
681 > default: false,
682 > }),
683 > [AgentHostCodexEnabledConfigKey]: schemaProperty<boolean>({
684 > type: 'boolean',
685 > title: localize('agentHost.config.codexAgentEnabled.title', "Codex Agent"),
686 > description: localize('agentHost.config.codexAgentEnabled.description', "Whether the Codex provider is enabled."),
687 > default: false,
688 > }),
689 > [AgentHostTerminalAutoApproveEnabledConfigKey]: schemaProperty<boolean>({
690 > type: 'boolean',
691 > title: localize('agentHost.config.terminalAutoApproveEnabled.title', "Terminal Auto Approve"),
692 > description: localize('agentHost.config.terminalAutoApproveEnabled.description', "Whether terminal auto-approve rules forwarded by the connected client are allowed to apply to agent-host shell permission requests."),
693 > default: true,
694 > }),
695 > [AgentHostGlobalAutoApproveEnabledConfigKey]: schemaProperty<boolean>({
696 > type: 'boolean',
697 > title: localize('agentHost.config.globalAutoApproveEnabled.title', "Global Auto Approve"),
698 > description: localize('agentHost.config.globalAutoApproveEnabled.description', "Whether VS Code's global auto-approve setting is enabled. When `true`, every tool call is auto-approved, equivalent to a session using Allow all."),
699 > default: false,
700 > }),
701 > [AgentHostAutoReplyEnabledConfigKey]: schemaProperty<boolean>({
702 > type: 'boolean',
703 > title: localize('agentHost.config.autoReplyEnabled.title', "Auto Reply"),
704 > description: localize('agentHost.config.autoReplyEnabled.description', "Whether VS Code's auto-reply setting is enabled. When `true`, `ask_user` questions are auto-answered instead of blocking on the user, mirroring autopilot mode."),
705 > default: false,
706 > }),
707 > [AgentHostPreferLongContextEnabledConfigKey]: schemaProperty<boolean>({
708 > type: 'boolean',
709 > title: localize('agentHost.config.preferLongContextEnabled.title', "Prefer Long Context"),
710 > description: localize('agentHost.config.preferLongContextEnabled.description', "Whether Copilot Chat's prefer-long-context setting is enabled. When `true`, models with a free long context window only show the long context option in the picker. When `false` (default), the smaller default context option stays selectable."),
711 > default: false,
712 > }),
713 > [AgentHostSystemProxyEnabledConfigKey]: schemaProperty<boolean>({
714 > type: 'boolean',
715 > title: localize('agentHost.config.systemProxyEnabled.title', "System Proxy Discovery"),
716 > description: localize('agentHost.config.systemProxyEnabled.description', "Whether Copilot sessions automatically discover and use the operating system's proxy configuration."),
717 > default: true,
718 > }),
719 > [AgentHostTerminalAutoApproveRulesConfigKey]: schemaProperty<AgentHostTerminalAutoApproveRules>({
720 > type: 'object',
721 > title: localize('agentHost.config.terminalAutoApproveRules.title', "Terminal Auto Approve Rules"),
722 > description: localize('agentHost.config.terminalAutoApproveRules.description', "Terminal auto-approve rules forwarded by the connected client for agent-host shell permission checks."),
723 > default: {},
724 > }),
725 > [AgentHostMcpServersConfigKey]: schemaProperty<AgentHostMcpServers>({
726 > type: 'object',
727 > title: localize('agentHost.config.mcpServers.title', "MCP Servers"),
728 > description: localize('agentHost.config.mcpServers.description', "Agent-host-level MCP servers exposed to every session, keyed by server name. Each value is a server configuration (see `<serverName>`)."),
729 > properties: mcpServersValueProperties,
730 > default: {},
731 > }),
732 > });
src/vs/base/common/lifecycle.ts 479 covered LOC · 98 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);
92 if (!val) {
96 return val;
97 }
99 > trackDisposable(d: IDisposable): void {
100 const data = this.getDisposableData(d);
101 if (!data.source) {
104 }
105 }
106 > lifecycle.ts
107 > setParent(child: IDisposable, parent: IDisposable | null): void {
108 const data = this.getDisposableData(child);
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 {
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)) {
334 const errors: any[] = [];
356 }
357 }
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;
383 this._fn = fn;
384 trackDisposable(this);
385 }
386 > lifecycle.ts
387 > dispose() {
388 if (this._isDisposed) {
389 return;
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);
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
457 try {
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) {
469 return o;
484 return o;
485 }
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);
539 setParentOfDisposable(this._store, this);
540 }
541 > lifecycle.ts
542 > public dispose(): void {
543 markAsDisposed(this);
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) {
553 throw new Error('Cannot register a disposable on itself!');
555 return this._store.add(o);
556 }
557 > } lifecycle.ts
558 >
559 > /**
560 > * Manages the lifecycle of a disposable value that may be changed.
561 > *
562 > * This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can
563 > * also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up.
564 > */
565 > export class MutableDisposable<T extends IDisposable> implements IDisposable {
566 > private _value?: T;
567 > private _isDisposed = false;
568 >
569 > constructor() {
570 trackDisposable(this);
571 }
572 > lifecycle.ts
573 > /**
574 > * Get the currently held disposable value, or `undefined` if this MutableDisposable has been disposed
575 > */
576 > get value(): T | undefined {
577 return this._isDisposed ? undefined : this._value;
578 }
579 > lifecycle.ts
580 > /**
581 > * Set a new disposable value.
582 > *
583 > * Behaviour:
584 > * - If the MutableDisposable has been disposed, the setter is a no-op.
585 > * - If the new value is strictly equal to the current value, the setter is a no-op.
586 > * - Otherwise the previous value (if any) is disposed and the new value is stored.
587 > *
588 > * Related helpers:
589 > * - clear() resets the value to `undefined` (and disposes the previous value).
590 > * - clearAndLeak() returns the old value without disposing it and removes its parent.
591 > */
592 > set value(value: T | undefined) {
593 if (this._isDisposed || value === this._value) {
594 return;
601 this._value = value;
602 }
603 > lifecycle.ts
604 > /**
605 > * Resets the stored value and disposed of the previously stored value.
606 > */
607 > clear(): void {
608 this.value = undefined;
609 }
610 > lifecycle.ts
611 > dispose(): void {
612 this._isDisposed = true;
613 markAsDisposed(this);
615 this._value = undefined;
616 }
617 > lifecycle.ts
618 > /**
619 > * Clears the value, but does not dispose it.
620 > * The old value is returned.
621 > */
622 > clearAndLeak(): T | undefined {
623 const oldValue = this._value;
624 this._value = undefined;
628 return oldValue;
629 }
630 > } lifecycle.ts
631 >
632 > /**
633 > * Manages the lifecycle of a disposable value that may be changed like {@link MutableDisposable}, but the value must
634 > * exist and cannot be undefined.
635 > */
636 > export class MandatoryMutableDisposable<T extends IDisposable> implements IDisposable {
637 > private readonly _disposable = new MutableDisposable<T>();
638 > private _isDisposed = false;
639 >
640 > constructor(initialValue: T) {
641 this._disposable.value = initialValue;
642 }
643 > lifecycle.ts
644 > get value(): T {
645 return this._disposable.value!;
646 }
647 > lifecycle.ts
648 > set value(value: T) {
649 if (this._isDisposed || value === this._disposable.value) {
650 return;
652 this._disposable.value = value;
653 }
654 > lifecycle.ts
655 > dispose() {
656 this._isDisposed = true;
657 this._disposable.dispose();
658 }
659 > } lifecycle.ts
660 >
661 > export class RefCountedDisposable {
662 >
663 > private _counter: number = 1;
664 >
665 > constructor(
666 private readonly _disposable: IDisposable,
667 ) { }
668 > lifecycle.ts
669 > acquire() {
670 this._counter++;
671 return this;
672 }
673 > lifecycle.ts
674 > release() {
675 if (--this._counter === 0) {
676 this._disposable.dispose();
678 return this;
679 }
680 > } lifecycle.ts
681 >
682 > export interface IReference<T> extends IDisposable {
683 > readonly object: T;
684 > }
685 >
686 > export abstract class ReferenceCollection<T> {
687
688 private readonly references: Map<string, { readonly object: T; counter: number }> = new Map();
689 > lifecycle.ts
690 > acquire(key: string, ...args: unknown[]): IReference<T> {
691 let reference = this.references.get(key);
692
708 return { object, dispose };
709 }
710 > lifecycle.ts
711 > protected abstract createReferencedObject(key: string, ...args: unknown[]): T;
712 > protected abstract destroyReferencedObject(key: string, object: T): void;
713 > }
714 >
715 > /**
716 > * Unwraps a reference collection of promised values. Makes sure
717 > * references are disposed whenever promises get rejected.
718 > */
719 > export class AsyncReferenceCollection<T> {
720 >
721 > constructor(private referenceCollection: ReferenceCollection<Promise<T>>) { }
722 >
723 > async acquire(key: string, ...args: unknown[]): Promise<IReference<T>> {
724 const ref = this.referenceCollection.acquire(key, ...args);
725
736 }
737 }
738 > } lifecycle.ts
739 >
740 > export class ImmortalReference<T> implements IReference<T> {
741 > constructor(public object: T) { }
742 > dispose(): void { /* noop */ }
743 > }
744 >
745 > export function disposeOnReturn(fn: (store: DisposableStore) => void): void {
746 const store = new DisposableStore();
747 try {
751 }
752 }
753 > lifecycle.ts
754 > /**
755 > * A map the manages the lifecycle of the values that it stores.
756 > */
757 > export class DisposableMap<K, V extends IDisposable = IDisposable> implements IDisposable {
758 >
759 > private readonly _store: Map<K, V>;
760 > private _isDisposed = false;
761 >
762 > constructor(store: Map<K, V> = new Map<K, V>()) {
763 this._store = store;
764 trackDisposable(this);
765 }
766 > lifecycle.ts
767 > /**
768 > * Disposes of all stored values and mark this object as disposed.
769 > *
770 > * Trying to use this object after it has been disposed of is an error.
771 > */
772 > dispose(): void {
773 markAsDisposed(this);
774 this._isDisposed = true;
775 this.clearAndDisposeAll();
776 }
777 > lifecycle.ts
778 > /**
779 > * Disposes of all stored values and clear the map, but DO NOT mark this object as disposed.
780 > */
781 > clearAndDisposeAll(): void {
782 if (!this._store.size) {
783 return;
790 }
791 }
792 > lifecycle.ts
793 > has(key: K): boolean {
794 return this._store.has(key);
795 }
796 > lifecycle.ts
797 > get size(): number {
798 return this._store.size;
799 }
800 > lifecycle.ts
801 > get(key: K): V | undefined {
802 return this._store.get(key);
803 }
804 > lifecycle.ts
805 > set(key: K, value: V, skipDisposeOnOverwrite = false): void {
806 if (this._isDisposed) {
807 console.warn(new Error('Trying to add a disposable to a DisposableMap that has already been disposed of. The added object will be leaked!').stack);
815 setParentOfDisposable(value, this);
816 }
817 > lifecycle.ts
818 > /**
819 > * Delete the value stored for `key` from this map and also dispose of it.
820 > */
821 > deleteAndDispose(key: K): void {
822 this._store.get(key)?.dispose();
823 this._store.delete(key);
824 }
825 > lifecycle.ts
826 > /**
827 > * Delete the value stored for `key` from this map but return it. The caller is
828 > * responsible for disposing of the value.
829 > */
830 > deleteAndLeak(key: K): V | undefined {
831 const value = this._store.get(key);
832 if (value) {
836 return value;
837 }
838 > lifecycle.ts
839 > keys(): IterableIterator<K> {
840 return this._store.keys();
841 }
842 > lifecycle.ts
843 > values(): IterableIterator<V> {
844 return this._store.values();
845 }
846 > lifecycle.ts
847 > [Symbol.iterator](): IterableIterator<[K, V]> {
848 return this._store[Symbol.iterator]();
849 }
850 > } lifecycle.ts
851 >
852 > /**
853 > * A set that manages the lifecycle of the values that it stores.
854 > */
855 > export class DisposableSet<V extends IDisposable = IDisposable> implements IDisposable {
856 >
857 > private readonly _store: Set<V>;
858 > private _isDisposed = false;
859 >
860 > constructor(store: Set<V> = new Set<V>()) {
861 this._store = store;
862 trackDisposable(this);
863 }
864 > lifecycle.ts
865 > /**
866 > * Disposes of all stored values and mark this object as disposed.
867 > *
868 > * Trying to use this object after it has been disposed of is an error.
869 > */
870 > dispose(): void {
871 markAsDisposed(this);
872 this._isDisposed = true;
873 this.clearAndDisposeAll();
874 }
875 > lifecycle.ts
876 > /**
877 > * Disposes of all stored values and clear the set, but DO NOT mark this object as disposed.
878 > */
879 > clearAndDisposeAll(): void {
880 if (!this._store.size) {
881 return;
888 }
889 }
890 > lifecycle.ts
891 > has(value: V): boolean {
892 return this._store.has(value);
893 }
894 > lifecycle.ts
895 > get size(): number {
896 return this._store.size;
897 }
898 > lifecycle.ts
899 > add(value: V): void {
900 if (this._isDisposed) {
901 console.warn(new Error('Trying to add a disposable to a DisposableSet that has already been disposed of. The added object will be leaked!').stack);
905 setParentOfDisposable(value, this);
906 }
907 > lifecycle.ts
908 > /**
909 > * Delete the value from this set and also dispose of it.
910 > */
911 > deleteAndDispose(value: V): void {
912 if (this._store.delete(value)) {
913 value.dispose();
914 }
915 }
916 > lifecycle.ts
917 > /**
918 > * Delete the value from this set but return it. The caller is
919 > * responsible for disposing of the value.
920 > */
921 > deleteAndLeak(value: V): V | undefined {
922 if (this._store.delete(value)) {
923 setParentOfDisposable(value, null);
926 return undefined;
927 }
928 > lifecycle.ts
929 > values(): IterableIterator<V> {
930 return this._store.values();
931 }
932 > lifecycle.ts
933 > [Symbol.iterator](): IterableIterator<V> {
934 return this._store[Symbol.iterator]();
935 }
936 > } lifecycle.ts
937 >
938 > /**
939 > * Call `then` on a Promise, unless the returned disposable is disposed.
940 > */
941 > export function thenIfNotDisposed<T>(promise: Promise<T>, then: (result: T) => void): IDisposable {
942 let disposed = false;
943 promise.then(result => {
951 });
952 }
953 > lifecycle.ts
954 > /**
955 > * Call `then` on a promise that resolves to a {@link IDisposable}, then either register the
956 > * disposable or register it to the {@link DisposableStore}, depending on whether the store is
957 > * disposed or not.
958 > */
959 > export function thenRegisterOrDispose<T extends IDisposable>(promise: Promise<T>, store: DisposableStore): Promise<T> {
960 return promise.then(disposable => {
961 if (store.isDisposed) {
967 });
968 }
969 > lifecycle.ts
970 > export class DisposableResourceMap<V extends IDisposable = IDisposable> extends DisposableMap<URI, V> {
971 > constructor() {
972 super(new ResourceMap());
973 }
974 > } lifecycle.ts
src/vs/base/common/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/base/common/arrays.ts 404 covered LOC · 72 ranges

Open complete file

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

Open complete file

1 > /*--------------------------------------------------------------------------------------------- commands.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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > import type { URI } from '../common/state.js';
10 > import type { BaseParams } from '../common/commands.js';
11 > import type { SessionActiveClient } from './state.js';
12 > import type { MessageAttachment } from '../channels-chat/state.js';
13 >
14 > // ─── createSession ───────────────────────────────────────────────────────────
15 >
16 > /**
17 > * Creates a new session with the specified agent provider.
18 > *
19 > * If the session URI already exists, the server MUST return an error with code
20 > * `-32003` (`SessionAlreadyExists`).
21 > *
22 > * After creation, the client should subscribe to the session URI to receive state
23 > * updates. The server also broadcasts a `root/sessionAdded` notification to all
24 > * clients.
25 > *
26 > * @category Commands
27 > * @method createSession
28 > * @direction Client → Server
29 > * @messageType Request
30 > * @version 1
31 > * @example
32 > * ```jsonc
33 > * // Client → Server
34 > * { "jsonrpc": "2.0", "id": 2, "method": "createSession",
35 > * "params": { "channel": "ahp-session:/<uuid>", "provider": "copilot" } }
36 > *
37 > * // Server → Client (success)
38 > * { "jsonrpc": "2.0", "id": 2, "result": null }
39 > *
40 > * // Server → Client (failure — provider not found)
41 > * { "jsonrpc": "2.0", "id": 2, "error": { "code": -32002, "message": "No agent for provider" } }
42 > *
43 > * // Server → Client (failure — session already exists)
44 > * { "jsonrpc": "2.0", "id": 2, "error": { "code": -32003, "message": "Session already exists" } }
45 > * ```
46 > */
47 > /**
48 > * Identifies a source session and turn to fork from.
49 > *
50 > * When provided in `createSession`, the server populates the new session with
51 > * content from the source session up to and including the response of the
52 > * specified turn.
53 > */
54 > export interface SessionForkSource {
55 > /** URI of the existing session to fork from */
56 > session: URI;
57 > /** Turn ID in the source session; content up to and including this turn's response is copied */
58 > turnId: string;
59 > }
60 >
61 > export interface CreateSessionParams extends BaseParams {
62 > /** Session URI (client-chosen, e.g. `ahp-session:/<uuid>`) */
63 > channel: URI;
64 > /** Agent provider ID */
65 > provider?: string;
66 > /**
67 > * The working directories the session's agent is granted tool access to.
68 > * A session may span multiple directories; they are equal peers except when
69 > * the agent advertises
70 > * {@link MultipleWorkingDirectoriesCapability.requiresPrimary}, in which case
71 > * one of them should be designated the primary via
72 > * {@link primaryWorkingDirectory}.
73 > *
74 > * A client MUST NOT supply more than one entry unless the agent advertises
75 > * {@link AgentCapabilities.multipleWorkingDirectories}; a server without that
76 > * capability treats only the first entry as the session's working directory
77 > * and ignores the rest. Dispatch `session/workingDirectorySet` /
78 > * `session/workingDirectoryRemoved` to change the set after the session has
79 > * started.
80 > *
81 > * Ignored for forked sessions — a fork inherits its working directories
82 > * from the source session identified by `fork`.
83 > */
84 > workingDirectories?: URI[];
85 > /**
86 > * The primary working directory for the session's **default chat**.
87 > *
88 > * A session has no primary of its own — primary is a per-chat notion (see
89 > * {@link ChatState.primaryWorkingDirectory}). But `createSession` implicitly
90 > * creates the session's default chat, and there is no separate `createChat`
91 > * call to carry that chat's create-time fields. This field is therefore the
92 > * only place a client can designate the **default chat's** primary at birth;
93 > * it is copied into that chat's read-only `primaryWorkingDirectory`. For any
94 > * non-default chat, pass {@link CreateChatParams.primaryWorkingDirectory}
95 > * instead.
96 > *
97 > * When set, it MUST be one of {@link workingDirectories}. A client SHOULD
98 > * supply this when the agent advertises
99 > * {@link MultipleWorkingDirectoriesCapability.requiresPrimary}; a host MAY
100 > * reject creation that omits it, or fall back to the first entry of
101 > * `workingDirectories`. Ignored for forked sessions (a fork inherits the
102 > * source session's chats and their primaries).
103 > */
104 > primaryWorkingDirectory?: URI;
105 > /**
106 > * Fork from an existing session. The new session is populated with content
107 > * from the source session up to and including the specified turn's response.
108 > */
109 > fork?: SessionForkSource;
110 > /**
111 > * Agent-specific configuration values collected via `resolveSessionConfig`.
112 > * Keys and values correspond to the schema returned by the server.
113 > */
114 > config?: Record<string, unknown>;
115 > /**
116 > * Eagerly claim an active client role for the new session.
117 > *
118 > * When provided, the server initializes the session with this client as an
119 > * active client, equivalent to dispatching a `session/activeClientSet`
120 > * action immediately after creation. The `clientId` MUST match the
121 > * `clientId` the creating client supplied in `initialize`.
122 > */
123 > activeClient?: SessionActiveClient;
124 > /**
125 > * Opt-in progress token. When set, the client is offering to receive
126 > * `progress` notifications (see `ProgressParams`) for any long-running work
127 > * the server does to bring this session up — most notably the lazy,
128 > * first-use download of the provider's native SDK. The server echoes this
129 > * exact token on every `progress` frame so the client can correlate it to
130 > * this `createSession` call (and the UI awaiting it).
131 > *
132 > * The token MUST be unique across the client's active requests. The server
133 > * MAY ignore it (e.g. when nothing long-running is needed), in which case no
134 > * `progress` notifications are emitted.
135 > */
136 > progressToken?: string;
137 > }
138 >
139 > // ─── disposeSession ──────────────────────────────────────────────────────────
140 >
141 > /**
142 > * Disposes a session and cleans up server-side resources.
143 > *
144 > * The server broadcasts a `root/sessionRemoved` notification to all clients.
145 > *
146 > * @category Commands
147 > * @method disposeSession
148 > * @direction Client → Server
149 > * @messageType Request
150 > * @version 1
151 > */
152 > export interface DisposeSessionParams extends BaseParams { }
153 >
154 > // ─── fetchTurns ──────────────────────────────────────────────────────────────
155 >
156 > /**
157 > * Requests that the host load older historical turns into a chat state.
158 > *
159 > * The command result does not carry turns. Instead, before responding, the host
160 > * MUST dispatch `chat/turnsLoaded` to insert any loaded turns into the chat
161 > * channel's `turns` state, ahead of the already-loaded window, and update or
162 > * clear `turnsNextCursor`.
163 > *
164 > * Before applying any operation that references a turn outside the currently
165 > * loaded window, the host MUST eagerly load enough older turns into state for
166 > * that operation to reduce against valid state.
167 > *
168 > * @category Commands
169 > * @method fetchTurns
170 > * @direction Client → Server
171 > * @messageType Request
172 > * @version 1
173 > * @example
174 > * ```jsonc
175 > * // Client → Server (load the next page indicated by ChatState.turnsNextCursor)
176 > * { "jsonrpc": "2.0", "id": 8, "method": "fetchTurns",
177 > * "params": { "channel": "ahp-chat:/<uuid>", "cursor": "opaque-cursor" } }
178 > *
179 > * // Server updates chat state, then responds
180 > * { "jsonrpc": "2.0", "id": 8, "result": {} }
181 > * ```
182 > */
183 > export interface FetchTurnsParams extends BaseParams {
184 > /** Chat URI */
185 > channel: URI;
186 > /**
187 > * Opaque cursor from `ChatState.turnsNextCursor`.
188 > *
189 > * The host MUST reject unrecognised cursors with `InvalidParams`. Omit only
190 > * when asking the host to opportunistically load its next older page for the
191 > * chat, if any.
192 > */
193 > cursor?: string;
194 > }
195 >
196 > /**
197 > * Result of the `fetchTurns` command.
198 > */
199 > export interface FetchTurnsResult { }
200 >
201 > // ─── completions ─────────────────────────────────────────────────────────────
202 >
203 > /**
204 > * The kind of completion items being requested.
205 > *
206 > * @category Commands
207 > */
208 > export const enum CompletionItemKind {
209 > /**
210 > * Completions for the text of a {@link Message} the user is composing.
211 > * Each returned item carries an attachment that gets associated with the
212 > * message when accepted.
213 > */
214 > UserMessage = 'userMessage',
215 > }
216 >
217 > /**
218 > * Requests completion items for a partially-typed input (e.g. a user message
219 > * the user is currently composing). Used to power `@`-mention pickers,
220 > * file/symbol references, and similar inline-completion experiences.
221 > *
222 > * Servers SHOULD treat this command as best-effort and return promptly. The
223 > * client SHOULD debounce calls to avoid flooding the server with requests on
224 > * every keystroke.
225 > *
226 > * @category Commands
227 > * @method completions
228 > * @direction Client → Server
229 > * @messageType Request
230 > * @version 1
231 > * @example
232 > * ```jsonc
233 > * // User has typed "look at @foo" and the cursor is just after "@foo".
234 > * // Client → Server
235 > * { "jsonrpc": "2.0", "id": 12, "method": "completions",
236 > * "params": { "kind": "userMessage", "channel": "ahp-chat:/<uuid>",
237 > * "text": "look at @foo", "offset": 12 } }
238 > *
239 > * // Server → Client
240 > * { "jsonrpc": "2.0", "id": 12, "result": {
241 > * "items": [
242 > * {
243 > * "insertText": "@foo.ts",
244 > * "rangeStart": 8,
245 > * "rangeEnd": 12,
246 > * "attachment": {
247 > * "type": "resource",
248 > * "label": "foo.ts",
249 > * "displayKind": "document",
250 > * "uri": "file:///workspace/foo.ts"
251 > * }
252 > * }
253 > * ]
254 > * }}
255 > * ```
256 > */
257 > export interface CompletionsParams extends BaseParams {
258 > /** What kind of completion is being requested. */
259 > kind: CompletionItemKind;
260 > /** The chat URI the completion is being requested for. */
261 > channel: URI;
262 > /**
263 > * The complete text of the input being completed (e.g. the full user
264 > * message text typed so far).
265 > */
266 > text: string;
267 > /**
268 > * The character offset within `text` at which the completion is requested,
269 > * measured in UTF-16 code units. MUST satisfy `0 <= offset <= text.length`.
270 > */
271 > offset: number;
272 > }
273 >
274 > /**
275 > * A single completion item returned by the `completions` command.
276 > *
277 > * When the user accepts an item, the client SHOULD:
278 > * 1. Replace the range `[rangeStart, rangeEnd)` in the input with `insertText`
279 > * (or insert `insertText` at the cursor when the range is omitted).
280 > * 2. Associate the item's `attachment` with the resulting {@link Message}.
281 > *
282 > * @category Commands
283 > */
284 > export interface CompletionItem {
285 > /**
286 > * The text inserted into the input when this item is accepted.
287 > */
288 > insertText: string;
289 >
290 > /**
291 > * If defined, the start of the range in the input's `text` that is replaced
292 > * by `insertText`. The range is the half-open interval
293 > * `[rangeStart, rangeEnd)` of character offsets, measured in UTF-16 code
294 > * units.
295 > *
296 > * When omitted, the client SHOULD insert `insertText` at the cursor.
297 > *
298 > * Note: this range refers to positions in the *current* input. The
299 > * attachment's own `rangeStart`/`rangeEnd` (when present) refer to
300 > * positions in the final {@link Message.text} after the item is
301 > * accepted.
302 > */
303 > rangeStart?: number;
304 >
305 > /**
306 > * The end of the range in the input's `text` that is replaced by
307 > * `insertText`. See {@link rangeStart}.
308 > */
309 > rangeEnd?: number;
310 >
311 > /**
312 > * The attachment associated with this completion item.
313 > */
314 > attachment: MessageAttachment;
315 > }
316 >
317 > /**
318 > * Result of the `completions` command.
319 > */
320 > export interface CompletionsResult {
321 > /** The completion items, in the order the server suggests displaying them. */
322 > items: CompletionItem[];
323 > }
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 284 covered LOC · 24 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');
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
35 // are subclasses of any put not positvely matched by the function. Hence type
41 && !(obj instanceof Date);
42 }
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');
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);
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/uri.ts 283 covered LOC · 27 ranges

Open complete file

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

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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > import type { ProtectedResourceMetadata } from './state.js';
10 > import type { ResourceRequestParams } from './commands.js';
11 >
12 > // ─── Standard JSON-RPC Codes ─────────────────────────────────────────────────
13 >
14 > /**
15 > * Standard JSON-RPC 2.0 error codes.
16 > *
17 > * @category Standard JSON-RPC Codes
18 > */
19 > export const JsonRpcErrorCodes = {
20 > /** Invalid JSON */
21 > ParseError: -32700,
22 > /** Not a valid JSON-RPC request */
23 > InvalidRequest: -32600,
24 > /** Unknown method name */
25 > MethodNotFound: -32601,
26 > /** Invalid method parameters */
27 > InvalidParams: -32602,
28 > /** Unspecified server error */
29 > InternalError: -32603,
30 > } as const;
31 >
32 > // ─── AHP Application Codes ──────────────────────────────────────────────────
33 >
34 > /**
35 > * AHP application-specific error codes.
36 > *
37 > * @category AHP Application Codes
38 > * @version 1
39 > */
40 > export const AhpErrorCodes = {
41 > /** The referenced session URI does not exist */
42 > SessionNotFound: -32001,
43 > /** The requested agent provider is not registered */
44 > ProviderNotFound: -32002,
45 > /** A session with the given URI already exists */
46 > SessionAlreadyExists: -32003,
47 > /** The operation requires no active turn, but one is in progress */
48 > TurnInProgress: -32004,
49 > /**
50 > * The server cannot speak any of the protocol versions offered by the
51 > * client in `InitializeParams.protocolVersions`. The `data` field of the
52 > * JSON-RPC error MAY be an `UnsupportedProtocolVersionErrorData` advertising
53 > * the protocol versions the server is willing to speak.
54 > */
55 > UnsupportedProtocolVersion: -32005,
56 > /** The requested content URI does not exist */
57 > ContentNotFound: -32006,
58 > /**
59 > * A command failed because the client has not authenticated for a required
60 > * protected resource. The `data` field of the JSON-RPC error MUST be an
61 > * `AuthRequiredErrorData` describing the resources that require
62 > * authentication.
63 > *
64 > * @see {@link /specification/authentication | Authentication}
65 > */
66 > AuthRequired: -32007,
67 > /** The requested file, folder, or URI does not exist */
68 > NotFound: -32008,
69 > /**
70 > * The client is not permitted to access the requested resource.
71 > *
72 > * Servers SHOULD return this when a client attempts to read or browse
73 > * a path outside the allowed set (e.g. outside the session's working
74 > * directory or workspace roots).
75 > *
76 > * The `data` field of the JSON-RPC error MAY be a
77 > * `PermissionDeniedErrorData` advertising a `resourceRequest` that, if
78 > * granted, would unlock the operation.
79 > */
80 > PermissionDenied: -32009,
81 > /**
82 > * The target resource already exists and the operation does not allow
83 > * overwriting (e.g. `resourceWrite` with `createOnly: true`).
84 > */
85 > AlreadyExists: -32010,
86 > /**
87 > * An optimistic-concurrency precondition failed.
88 > *
89 > * Returned when a request carries a precondition token that no longer
90 > * matches the receiver's current state — for example, `resourceWrite`
91 > * with an `ifMatch` etag that has been superseded by a concurrent
92 > * write. Callers SHOULD re-read the resource (e.g. via
93 > * `resourceResolve`) and decide whether to retry the operation with the
94 > * fresh token or surface the conflict to the user.
95 > */
96 > Conflict: -32011,
97 > } as const;
98 >
99 > /** Union type of all AHP application error codes. */
100 > export type AhpErrorCode = (typeof AhpErrorCodes)[keyof typeof AhpErrorCodes];
101 >
102 > /** Union type of all JSON-RPC error codes. */
103 > export type JsonRpcErrorCode = (typeof JsonRpcErrorCodes)[keyof typeof JsonRpcErrorCodes];
104 >
105 > // ─── Error Detail Types ──────────────────────────────────────────────────────
106 >
107 > /**
108 > * Details carried in the `data` field of an `AuthRequired` (-32007) error.
109 > *
110 > * Wraps the protected resource list in `{ resources: [...] }` rather than
111 > * returning a bare array, so additional fields can be added in future
112 > * versions without breaking the wire shape.
113 > *
114 > * @category Error Details
115 > * @version 1
116 > */
117 > export interface AuthRequiredErrorData {
118 > /** Protected resources that require authentication. */
119 > resources: ProtectedResourceMetadata[];
120 > }
121 >
122 > /**
123 > * Details carried in the `data` field of a `PermissionDenied` (-32009) error.
124 > *
125 > * The receiver MAY advertise a `resourceRequest` payload describing the
126 > * access that, if granted, would unlock the operation. The caller MAY then
127 > * issue `resourceRequest` with that payload to negotiate access.
128 > *
129 > * @category Error Details
130 > * @version 1
131 > */
132 > export interface PermissionDeniedErrorData {
133 > /**
134 > * The resource access that, if granted via `resourceRequest`, would unlock
135 > * the operation. Omitted when no specific access grant would resolve the
136 > * denial (for example, when the resource is fundamentally inaccessible).
137 > */
138 > request?: ResourceRequestParams;
139 > }
140 >
141 > /**
142 > * Details carried in the `data` field of an `UnsupportedProtocolVersion`
143 > * (-32005) error.
144 > *
145 > * @category Error Details
146 > * @version 1
147 > */
148 > export interface UnsupportedProtocolVersionErrorData {
149 > /**
150 > * Protocol versions the server is willing to speak.
151 > *
152 > * Each entry is either a [SemVer](https://semver.org) `MAJOR.MINOR.PATCH`
153 > * string (e.g. `"0.1.0"`) or a [SemVer range](https://semver.org/#spec-item-11)
154 > * constraint (e.g. `">=0.1.0 <0.3.0"` or `"^0.2.0"`).
155 > */
156 > supportedVersions: string[];
157 > }
158 >
159 > /**
160 > * Maps each AHP error code that carries structured `data` to the type of
161 > * that data.
162 > *
163 > * Error codes not present in this map either have no `data` payload or
164 > * carry an unspecified payload that callers SHOULD treat as `unknown`.
165 > *
166 > * @category Error Details
167 > * @version 1
168 > */
169 > export interface AhpErrorDetailsMap {
170 > [AhpErrorCodes.AuthRequired]: AuthRequiredErrorData;
171 > [AhpErrorCodes.PermissionDenied]: PermissionDeniedErrorData;
172 > [AhpErrorCodes.UnsupportedProtocolVersion]: UnsupportedProtocolVersionErrorData;
173 > }
174 >
175 > /** AHP error codes that carry a structured `data` payload. */
176 > export type AhpErrorCodeWithData = keyof AhpErrorDetailsMap;
177 >
178 > /**
179 > * A typed JSON-RPC error object whose `data` is narrowed by `code`.
180 > *
181 > * Distributes over the `AhpErrorCode` union so narrowing on `code` reveals
182 > * the precise `data` type. For codes listed in {@link AhpErrorDetailsMap}
183 > * `data` is required; for all other codes `data` is an optional `unknown`.
184 > *
185 > * ```ts
186 > * function handle(err: AhpError) {
187 > * if (err.code === AhpErrorCodes.PermissionDenied) {
188 > * err.data.request; // typed as ResourceRequestParams | undefined
189 > * }
190 > * }
191 > * ```
192 > *
193 > * @category Error Details
194 > * @version 1
195 > */
196 > export type AhpError<C extends AhpErrorCode = AhpErrorCode> =
197 > C extends AhpErrorCode
198 > ? C extends keyof AhpErrorDetailsMap
199 > ? {
200 > /** The error code. */
201 > readonly code: C;
202 > /** Human-readable error message. */
203 > readonly message: string;
204 > /** Structured detail payload mandated by `AhpErrorDetailsMap`. */
205 > readonly data: AhpErrorDetailsMap[C];
206 > }
207 > : {
208 > /** The error code. */
209 > readonly code: C;
210 > /** Human-readable error message. */
211 > readonly message: string;
212 > /** Optional, unspecified detail payload. */
213 > readonly data?: unknown;
214 > }
215 > : never;
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/nls.ts 201 covered LOC · 13 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) => {
34 const index = rest[0];
43 });
44 }
45 > nls.ts
46 > if (isPseudo) {
47 // FF3B and FF3D is the Unicode zenkaku representation for [ and ]
48 result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D';
49 }
50 > nls.ts
51 > return result;
52 > }
53 > nls.ts
54 > /**
55 > * Marks a string to be localized. Returns the localized string.
56 > *
57 > * @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
58 > * @param message The string to localize
59 > * @param args The arguments to the string
60 > *
61 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
62 > * @example `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
63 > *
64 > * @returns string The localized string.
65 > */
66 > export function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
67 >
68 > /**
69 > * Marks a string to be localized. Returns the localized string.
70 > *
71 > * @param key The key to use for localizing the string
72 > * @param message The string to localize
73 > * @param args The arguments to the string
74 > *
75 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
76 > * @example For example, `localize('sayHello', 'hello {0}', name)`
77 > *
78 > * @returns string The localized string.
79 > */
80 > export function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string;
81 >
82 > /**
83 > * @skipMangle
84 > */
85 > export function localize(data: ILocalizeInfo | string /* | number when built */, message: string /* | null when built */, ...args: (string | number | boolean | undefined | null)[]): string {
86 > if (typeof data === 'number') { nls.ts
87 return _format(lookupMessage(data, message), args);
88 }
89 > return _format(message, args); nls.ts
90 > }
91 > nls.ts
92 > /**
93 > * Only used when built: Looks up the message in the global NLS table.
94 > * This table is being made available as a global through bootstrapping
95 > * depending on the target context.
96 > */
97 function lookupMessage(index: number, fallback: string | null): string {
98 const message = getNLSMessages()?.[index];
105 return message;
106 }
107 > nls.ts
108 > /**
109 > * Marks a string to be localized. Returns an {@linkcode ILocalizedString}
110 > * which contains the localized string and the original string.
111 > *
112 > * @param info The {@linkcode ILocalizeInfo} which describes the id and comments associated with the localized string.
113 > * @param message The string to localize
114 > * @param args The arguments to the string
115 > *
116 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
117 > * @example `localize2({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)`
118 > *
119 > * @returns ILocalizedString which contains the localized string and the original string.
120 > */
121 > export function localize2(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
122 >
123 > /**
124 > * Marks a string to be localized. Returns an {@linkcode ILocalizedString}
125 > * which contains the localized string and the original string.
126 > *
127 > * @param key The key to use for localizing the string
128 > * @param message The string to localize
129 > * @param args The arguments to the string
130 > *
131 > * @note `message` can contain `{n}` notation where it is replaced by the nth value in `...args`
132 > * @example `localize('sayHello', 'hello {0}', name)`
133 > *
134 > * @returns ILocalizedString which contains the localized string and the original string.
135 > */
136 > export function localize2(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString;
137 >
138 > /**
139 > * @skipMangle
140 > */
141 > export function localize2(data: ILocalizeInfo | string /* | number when built */, originalMessage: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString {
142 let message: string;
143 if (typeof data === 'number') {
154 };
155 }
156 > nls.ts
157 > export interface INLSLanguagePackConfiguration {
158 >
159 > /**
160 > * The path to the translations config file that contains pointers to
161 > * all message bundles for `main` and extensions.
162 > */
163 > readonly translationsConfigFile: string;
164 >
165 > /**
166 > * The path to the file containing the translations for this language
167 > * pack as flat string array.
168 > */
169 > readonly messagesFile: string;
170 >
171 > /**
172 > * The path to the file that can be used to signal a corrupt language
173 > * pack, for example when reading the `messagesFile` fails. This will
174 > * instruct the application to re-create the cache on next startup.
175 > */
176 > readonly corruptMarkerFile: string;
177 > }
178 >
179 > export interface INLSConfiguration {
180 >
181 > /**
182 > * Locale as defined in `argv.json` or `app.getLocale()`.
183 > */
184 > readonly userLocale: string;
185 >
186 > /**
187 > * Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`).
188 > */
189 > readonly osLocale: string;
190 >
191 > /**
192 > * The actual language of the UI that ends up being used considering `userLocale`
193 > * and `osLocale`.
194 > */
195 > readonly resolvedLanguage: string;
196 >
197 > /**
198 > * Defined if a language pack is used that is not the
199 > * default english language pack. This requires a language
200 > * pack to be installed as extension.
201 > */
202 > readonly languagePack?: INLSLanguagePackConfiguration;
203 >
204 > /**
205 > * The path to the file containing the default english messages
206 > * as flat string array. The file is only present in built
207 > * versions of the application.
208 > */
209 > readonly defaultMessagesFile: string;
210 >
211 > /**
212 > * Below properties are deprecated and only there to continue support
213 > * for `vscode-nls` module that depends on them.
214 > * Refs https://github.com/microsoft/vscode-nls/blob/main/src/node/main.ts#L36-L46
215 > */
216 > /** @deprecated */
217 > readonly locale: string;
218 > /** @deprecated */
219 > readonly availableLanguages: Record<string, string>;
220 > /** @deprecated */
221 > readonly _languagePackSupport?: boolean;
222 > /** @deprecated */
223 > readonly _languagePackId?: string;
224 > /** @deprecated */
225 > readonly _translationsConfigFile?: string;
226 > /** @deprecated */
227 > readonly _cacheRoot?: string;
228 > /** @deprecated */
229 > readonly _resolvedLanguagePackCoreLocation?: string;
230 > /** @deprecated */
231 > readonly _corruptedFile?: string;
232 > }
233 >
234 > export interface ILanguagePack {
235 > readonly hash: string;
236 > readonly label: string | undefined;
237 > readonly extensions: {
238 > readonly extensionIdentifier: { readonly id: string; readonly uuid?: string };
239 > readonly version: string;
240 > }[];
241 > readonly translations: Record<string, string | undefined>;
242 > }
243 >
244 > export type ILanguagePacks = Record<string, ILanguagePack | undefined>;
src/vs/base/common/platform.ts 189 covered LOC · 12 ranges

Open complete file

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

Open complete file

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

Open complete file

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

Open complete file

1 > /*--------------------------------------------------------------------------------------------- commands.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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > import type { URI } from '../common/state.js';
10 > import type { BaseParams } from '../common/commands.js';
11 > import type { Message, SideChatSelection } from './state.js';
12 >
13 > // ─── createChat ──────────────────────────────────────────────────────────────
14 >
15 > /**
16 > * How a new chat uses its source chat and turn.
17 > */
18 > export const enum ChatSourceKind {
19 > /** Copy source history through the referenced turn into the new chat. */
20 > Fork = 'fork',
21 > /** Supply source context without copying it into the new chat's visible history. */
22 > SideChat = 'sideChat',
23 > }
24 >
25 > /**
26 > * Copies source history through a completed turn into the new chat.
27 > */
28 > export interface ForkChatSource {
29 > /** Discriminant */
30 > kind: ChatSourceKind.Fork;
31 > /** URI of the existing source chat. */
32 > chat: URI;
33 > /**
34 > * Completed turn identifier in the source chat.
35 > *
36 > * Content through this turn is copied into the new chat's visible `turns`.
37 > */
38 > turnId: string;
39 > }
40 >
41 > /**
42 > * Supplies source context to a new side chat without copying it into the side
43 > * chat's visible history.
44 > */
45 > export interface SideChatSource {
46 > /** Discriminant */
47 > kind: ChatSourceKind.SideChat;
48 > /** URI of the existing source chat. */
49 > chat: URI;
50 > /**
51 > * Stable source-turn identifier in the source chat.
52 > *
53 > * Hosts resolve this id against the source chat's current `activeTurn` or its
54 > * retained `turns` when accepting `createChat`. If it names the current
55 > * active turn, the host snapshots the source chat's retained history plus
56 > * that turn's current user message and any partial assistant response already
57 > * available. Once that turn later becomes historical, it is still referenced
58 > * by this same identifier.
59 > */
60 > turnId: string;
61 > /**
62 > * Optional immutable selected-text snapshot to carry into the created side
63 > * chat's origin.
64 > *
65 > * When present, the host MUST snapshot and preserve this exact selection when
66 > * it accepts `createChat`; later source-turn deltas do not alter it.
67 > */
68 > selection?: SideChatSelection;
69 > }
70 >
71 > /**
72 > * Identifies a source chat for a new chat.
73 > */
74 > export type ChatSource =
75 > | ForkChatSource
76 > | SideChatSource;
77 >
78 > /**
79 > * Creates a new chat within a session.
80 > *
81 > * @category Commands
82 > * @method createChat
83 > * @direction Client → Server
84 > * @messageType Request
85 > * @version 1
86 > */
87 > export interface CreateChatParams extends BaseParams {
88 > /** Session URI containing the new chat. */
89 > channel: URI;
90 > /** Chat URI (client-chosen, e.g. `ahp-chat:/<uuid>`). */
91 > chat: URI;
92 > /** Optional initial message for the new chat. */
93 > initialMessage?: Message;
94 > /**
95 > * Optional source chat and source turn.
96 > *
97 > * The source chat MUST belong to this session. Clients MUST only request
98 > * `kind: "fork"` when the selected agent advertises
99 > * `capabilities.multipleChats.fork`, and `kind: "sideChat"` when the
100 > * selected agent advertises `capabilities.multipleChats.sideChat`. Both
101 > * source forms carry a stable top-level `turnId`. Forks target completed
102 > * turns. Side chats also carry a stable `turnId`, which the host resolves
103 > * against the source chat's current active turn or retained history. If it
104 > * resolves to the active turn, the host snapshots the currently available
105 > * partial response when accepting `createChat`. When
106 > * `source.kind === "sideChat"` and `source.selection` is present, the host
107 > * also snapshots and preserves that exact selected text in the created chat's
108 > * origin; any `responsePartId` there is provenance only, not a live range.
109 > */
110 > source?: ChatSource;
111 > /**
112 > * Initial working-directory subset for this chat. Every entry MUST be
113 > * present in the owning session's `workingDirectories`; the server MUST
114 > * reject any entry that is not. When absent, the chat inherits the full
115 > * session set. Forked chats (those whose `source.kind` is `"fork"`) inherit
116 > * the source chat's `workingDirectories`; this field is ignored for forks.
117 > *
118 > * A client MUST NOT supply this field unless the agent advertises
119 > * {@link AgentCapabilities.multipleWorkingDirectories}.
120 > */
121 > workingDirectories?: URI[];
122 > /**
123 > * The chat's primary working directory — the distinguished root this chat is
124 > * centered on. When set, it MUST be one of the chat's effective working
125 > * directories ({@link workingDirectories}, or the session's set when that is
126 > * omitted). A client SHOULD supply this when the agent advertises
127 > * {@link MultipleWorkingDirectoriesCapability.requiresPrimary}; a host MAY
128 > * reject creation that omits it, or fall back to the first of the chat's
129 > * directories. Fixed at creation and reported (read-only) on
130 > * {@link ChatState.primaryWorkingDirectory}. Ignored for forks (a chat whose
131 > * `source.kind` is `"fork"` inherits the source chat's primary).
132 > */
133 > primaryWorkingDirectory?: URI;
134 > }
135 >
136 > // ─── disposeChat ─────────────────────────────────────────────────────────────
137 >
138 > /**
139 > * Disposes a chat and cleans up server-side resources.
140 > *
141 > * @category Commands
142 > * @method disposeChat
143 > * @direction Client → Server
144 > * @messageType Request
145 > * @version 1
146 > */
147 > export interface DisposeChatParams extends BaseParams { }
src/vs/platform/agentHost/common/state/sessionProtocol.ts 140 covered LOC · 5 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- sessionProtocol.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 > // Protocol messages using JSON-RPC 2.0 framing for the sessions process.
7 > // See protocol.md for the full design.
8 > //
9 > // Most types are re-exported from the auto-generated protocol layer.
10 > // This file adds VS Code-specific additions (ISetAuthTokenParams, ProtocolError)
11 > // and backward-compatible aliases.
12 >
13 > // ---- Re-exports from protocol -----------------------------------------------
14 >
15 > // JSON-RPC base types
16 > export type {
17 > JsonRpcErrorResponse,
18 > JsonRpcNotification,
19 > JsonRpcParseErrorResponse,
20 > JsonRpcRequest,
21 > JsonRpcResponse,
22 > JsonRpcSuccessResponse,
23 > } from './protocol/messages.js';
24 >
25 > // Typed message unions
26 > export type {
27 > AhpClientNotification,
28 > AhpNotification,
29 > AhpRequest,
30 > AhpResponse,
31 > AhpServerNotification,
32 > AhpSuccessResponse,
33 > CommandMap,
34 > ClientNotificationMap,
35 > ProtocolMessage,
36 > ServerNotificationMap,
37 > } from './protocol/messages.js';
38 >
39 > // Command params and results
40 > export type {
41 > CreateSessionParams,
42 > DirectoryEntry,
43 > DispatchActionParams,
44 > DisposeSessionParams,
45 > FetchTurnsParams,
46 > FetchTurnsResult,
47 > InitializeParams,
48 > InitializeResult,
49 > ListSessionsParams,
50 > ListSessionsResult,
51 > ReconnectParams,
52 > ReconnectReplayResult,
53 > ReconnectResult,
54 > ReconnectSnapshotResult,
55 > ResourceCopyParams,
56 > ResourceCopyResult,
57 > ResourceDeleteParams,
58 > ResourceDeleteResult,
59 > ResourceListParams,
60 > ResourceListResult,
61 > ResourceMkdirParams,
62 > ResourceMkdirResult,
63 > ResourceMoveParams,
64 > ResourceMoveResult,
65 > ResourceReadParams,
66 > ResourceReadResult,
67 > ResourceResolveParams,
68 > ResourceResolveResult,
69 > ResourceWriteParams,
70 > ResourceWriteResult,
71 > SubscribeParams,
72 > SubscribeResult,
73 > UnsubscribeParams,
74 > } from './protocol/commands.js';
75 >
76 > export type {
77 > CreateResourceWatchParams,
78 > CreateResourceWatchResult,
79 > } from './protocol/channels-resource-watch/commands.js';
80 >
81 > export { ContentEncoding, ReconnectResultType, ResourceType, ResourceWriteMode } from './protocol/commands.js';
82 > export { ResourceChangeType } from './protocol/channels-resource-watch/state.js';
83 > export type { ResourceChange, ResourceWatchState } from './protocol/channels-resource-watch/state.js';
84 >
85 > // Error codes
86 > export { AhpErrorCodes, JsonRpcErrorCodes } from './protocol/errors.js';
87 > export type { AhpErrorCode, JsonRpcErrorCode } from './protocol/errors.js';
88 >
89 > // Snapshot type (re-exported from state). The generated `Snapshot.state`
90 > // union now includes `ChatState`, so per-chat snapshots type-check directly.
91 > import type { Snapshot as ProtocolSnapshot } from './protocol/state.js';
92 > export type IStateSnapshot = ProtocolSnapshot;
93 >
94 > // ---- Backward-compatible error code aliases ---------------------------------
95 >
96 > export const JSON_RPC_PARSE_ERROR = -32700 as const;
97 > export const JSON_RPC_INTERNAL_ERROR = -32603 as const;
98 > export const AHP_SESSION_NOT_FOUND = -32001 as const;
99 > export const AHP_PROVIDER_NOT_FOUND = -32002 as const;
100 > export const AHP_SESSION_ALREADY_EXISTS = -32003 as const;
101 > export const AHP_TURN_IN_PROGRESS = -32004 as const;
102 > export const AHP_UNSUPPORTED_PROTOCOL_VERSION = -32005 as const;
103 > export const AHP_CONTENT_NOT_FOUND = -32006 as const;
104 > export const AHP_AUTH_REQUIRED = -32007 as const;
105 >
106 > // ---- Type guards -----------------------------------------------------------
107 >
108 > import type { AhpRequest, AhpNotification, AhpSuccessResponse, ProtocolMessage, JsonRpcErrorResponse } from './protocol/messages.js';
109 >
110 > export function isJsonRpcRequest(msg: ProtocolMessage): msg is AhpRequest {
111 return 'method' in msg && 'id' in msg;
112 }
114 > export function isJsonRpcNotification(msg: ProtocolMessage): msg is AhpNotification {
115 return 'method' in msg && !('id' in msg);
116 }
118 > export function isJsonRpcResponse(msg: ProtocolMessage): msg is AhpSuccessResponse | JsonRpcErrorResponse {
119 return 'id' in msg && !('method' in msg);
120 }
122 > // ---- VS Code-specific types ------------------------------------------------
123 >
124 > /**
125 > * Error with a JSON-RPC error code for protocol-level failures.
126 > * Optionally carries a `data` payload for structured error details.
127 > */
128 > export class ProtocolError extends Error {
129 > constructor(readonly code: number, message: string, readonly data?: unknown) {
130 super(message);
131 }
133 >
134 > /**
135 > * VS Code-specific extension: set the auth token on the server.
136 > * Not yet part of the official protocol.
137 > */
138 > export interface ISetAuthTokenParams {
139 > readonly token: string;
140 > }
141 >
142 > // ---- Server → Client notification param aliases (backward compat) -----------
143 >
144 > import type { INotification } from './sessionActions.js';
145 >
146 > export interface INotificationBroadcastParams {
147 > readonly notification: INotification;
148 > }
src/vs/base/common/equals.ts 118 covered LOC · 20 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) { equals.ts
55 > return true;
56 > }
57 > equals.ts
58 > if (Array.isArray(a) && Array.isArray(b)) { equals.ts
59 if (a.length !== b.length) {
60 return false;
67 return true;
68 }
69 > equals.ts
70 > if (a && typeof a === 'object' && b && typeof b === 'object') { equals.ts
71 if (Object.getPrototypeOf(a) === Object.prototype && Object.getPrototypeOf(b) === Object.prototype) {
72 const aObj = a as Record<string, unknown>;
92 }
93 }
94 > equals.ts
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/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/platform/instantiation/common/instantiation.ts 111 covered LOC · 5 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 {
98 if ((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) {
117 throw new Error('@IServiceName-decorator can only be used to decorate a parameter');
119 storeServiceDependency(id, target, index);
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;
130 }
src/vs/platform/agentHost/common/state/protocol/channels-changeset/commands.ts 105 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- commands.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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > import type { URI, ContentRef, StringOrMarkdown, TextRange } from '../common/state.js';
10 > import type { BaseParams } from '../common/commands.js';
11 >
12 > // ─── invokeChangesetOperation ────────────────────────────────────────────────
13 >
14 > /**
15 > * Discriminator for {@link ChangesetOperationTarget}. Mirrors the
16 > * non-`Changeset` members of {@link ChangesetOperationScope} — the
17 > * `Changeset` scope has no target.
18 > *
19 > * @category Commands
20 > */
21 > export const enum ChangesetOperationTargetKind {
22 > /** Operation acts on a single file. */
23 > Resource = 'resource',
24 > /** Operation acts on a line range within a single file. */
25 > Range = 'range',
26 > }
27 >
28 > /**
29 > * Identifies the file or range a {@link ChangesetOperation} should act on.
30 > *
31 > * The `kind` MUST match one of the operation's declared
32 > * {@link ChangesetOperation.scopes}.
33 > *
34 > * @category Commands
35 > */
36 > export type ChangesetOperationTarget =
37 > | { kind: ChangesetOperationTargetKind.Resource; resource: URI; side?: 'before' | 'after' }
38 > | { kind: ChangesetOperationTargetKind.Range; resource: URI; side?: 'before' | 'after'; range: TextRange };
39 >
40 > /**
41 > * Optional follow-up surfaced by the server after an operation completes —
42 > * a {@link ContentRef} the client can fetch and display.
43 > *
44 > * Set `external` to `true` to open the content in the user's preferred
45 > * external handler (e.g. browser); otherwise the client is expected to
46 > * surface it inline.
47 > *
48 > * @category Commands
49 > */
50 > export interface ChangesetOperationFollowUp {
51 > content: ContentRef;
52 > /** When `true`, open in an external handler rather than inline. */
53 > external?: boolean;
54 > }
55 >
56 > /**
57 > * Invokes a server-defined {@link ChangesetOperation} against a changeset,
58 > * a single file, or a line range.
59 > *
60 > * The server validates that `operationId` exists in the changeset's
61 > * current `operations` list and that the requested `target.kind` is
62 > * contained in the operation's `scopes`. Invalid combinations result in a
63 > * JSON-RPC error.
64 > *
65 > * State changes resulting from invocation flow back through the normal
66 > * `changeset/*` action stream on the relevant changeset URIs. Clients
67 > * SHOULD NOT synthesise local optimistic changes for invocations unless
68 > * the server explicitly opts in via a future capability.
69 > *
70 > * @category Commands
71 > * @method invokeChangesetOperation
72 > * @direction Client → Server
73 > * @messageType Request
74 > * @version 2
75 > */
76 > export interface InvokeChangesetOperationParams extends BaseParams {
77 > /** The expanded changeset URI. */
78 > channel: URI;
79 > /** Matches {@link ChangesetOperation.id} from the changeset's `operations` list. */
80 > operationId: string;
81 > /**
82 > * Target of the operation. Required iff the chosen scope is
83 > * `'resource'` or `'range'`. Omit for changeset-scoped operations.
84 > */
85 > target?: ChangesetOperationTarget;
86 > }
87 >
88 > /**
89 > * Result of the {@link InvokeChangesetOperationParams | `invokeChangesetOperation`}
90 > * command.
91 > *
92 > * Success is implicit: the server returns this result when it accepted
93 > * the operation. Failure is signalled by rejecting the JSON-RPC request
94 > * with an appropriate error code, not by any field on this result. The
95 > * operation MAY still produce subsequent failure feedback through the
96 > * {@link ChangesetStatusChangedAction | `changeset/statusChanged`} stream.
97 > *
98 > * @category Commands
99 > */
100 > export interface InvokeChangesetOperationResult {
101 > /** Optional human-readable message describing the result. */
102 > message?: StringOrMarkdown;
103 > /** Optional follow-up: a URI to open (e.g. a PR), a content ref, etc. */
104 > followUp?: ChangesetOperationFollowUp;
105 > }
src/vs/base/common/arraysFind.ts 95 covered LOC · 16 ranges

Open complete file

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

Open complete file

1 > /*--------------------------------------------------------------------------------------------- state.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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > import type { URI } from '../common/state.js';
10 >
11 > // ─── Resource Watch Types ────────────────────────────────────────────────────
12 >
13 > /**
14 > * Full state for a single resource watch, returned when a client subscribes
15 > * to an `ahp-resource-watch:` URI.
16 > *
17 > * Watches are otherwise stateless: the watcher exists to deliver
18 > * {@link ResourceWatchChangedAction} events. The state carries only the
19 > * descriptor of what is being watched so a re-subscribing client can
20 > * recover the watch configuration after reconnecting.
21 > *
22 > * @category Resource Watch Types
23 > */
24 > export interface ResourceWatchState {
25 > /**
26 > * The URI being watched. For recursive watches this is the root of the
27 > * subtree; for non-recursive watches this is the single file or
28 > * directory.
29 > */
30 > root: URI;
31 > /**
32 > * `true` if the watcher reports changes for descendants of `root`;
33 > * `false` if it only reports changes to `root` itself (and, when
34 > * `root` is a directory, its direct children).
35 > */
36 > recursive: boolean;
37 > /**
38 > * Optional glob patterns or paths relative to `root` to exclude from
39 > * change reporting.
40 > */
41 > excludes?: { items: string[] };
42 > /**
43 > * Optional glob patterns or paths relative to `root` to restrict
44 > * change reporting to. Omit to report every change under `root`
45 > * subject to `excludes`.
46 > */
47 > includes?: { items: string[] };
48 > }
49 >
50 > // ─── Resource Change ─────────────────────────────────────────────────────────
51 >
52 > /**
53 > * Discriminant for {@link ResourceChange.type}.
54 > *
55 > * @category Resource Watch Types
56 > */
57 > export const enum ResourceChangeType {
58 > Added = 'added',
59 > Updated = 'updated',
60 > Deleted = 'deleted',
61 > }
62 >
63 > /**
64 > * A single change observed by a resource watcher.
65 > *
66 > * @category Resource Watch Types
67 > */
68 > export interface ResourceChange {
69 > /** The URI of the resource that changed. */
70 > uri: URI;
71 > /** The kind of change observed. */
72 > type: ResourceChangeType;
73 > }
src/vs/base/common/iterator.ts 63 covered LOC · 22 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';
12 }
14 > const _empty: Iterable<never> = Object.freeze([]);
15 > export function empty<T = never>(): readonly never[] {
16 return _empty as readonly never[];
17 }
19 > export function* single<T>(element: T): Iterable<T> {
20 yield element;
21 }
23 > export function wrap<T>(iterableOrElement: Iterable<T> | T): Iterable<T> {
24 if (is(iterableOrElement)) {
25 return iterableOrElement;
28 }
29 }
31 > export function from<T>(iterable: Iterable<T> | undefined | null): Iterable<T> {
32 return iterable ?? (_empty as Iterable<T>);
33 }
35 > export function* reverse<T>(array: ReadonlyArray<T>): Iterable<T> {
36 for (let i = array.length - 1; i >= 0; i--) {
37 yield array[i];
38 }
39 }
41 > export function isEmpty<T>(iterable: Iterable<T> | undefined | null): boolean {
42 return !iterable || iterable[Symbol.iterator]().next().done === true;
43 }
45 > export function first<T>(iterable: Iterable<T>): T | undefined {
46 return iterable[Symbol.iterator]().next().value;
47 }
49 > export function some<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
50 let i = 0;
51 for (const element of iterable) {
56 return false;
57 }
59 > export function every<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
60 let i = 0;
61 for (const element of iterable) {
66 return true;
67 }
69 > export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): R | undefined;
70 > export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
71 > export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
72 for (const element of iterable) {
73 if (predicate(element)) {
78 return undefined;
79 }
81 > export function filter<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): Iterable<R>;
82 > export function filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T>;
83 > export function* filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T> {
84 for (const element of iterable) {
85 if (predicate(element)) {
88 }
89 }
91 > export function* map<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => R): Iterable<R> {
92 let index = 0;
93 for (const element of iterable) {
95 }
96 }
98 > export function* flatMap<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => Iterable<R>): Iterable<R> {
99 let index = 0;
100 for (const element of iterable) {
102 }
103 }
104 > iterator.ts
105 > export function* concat<T>(...iterables: (Iterable<T> | T)[]): Iterable<T> {
106 for (const item of iterables) {
107 if (isIterable(item)) {
112 }
113 }
114 > iterator.ts
115 > export function reduce<T, R>(iterable: Iterable<T>, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R {
116 let value = initialValue;
117 for (const element of iterable) {
120 return value;
121 }
122 > iterator.ts
123 > export function length<T>(iterable: Iterable<T>): number {
124 let count = 0;
125 for (const _ of iterable) {
128 return count;
129 }
130 > iterator.ts
131 > /**
132 > * Returns an iterable slice of the array, with the same semantics as `array.slice()`.
133 > */
134 > export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
135 if (from < -arr.length) {
136 from = 0;
150 }
151 }
152 > iterator.ts
153 > /**
154 > * Consumes `atMost` elements from iterable and returns the consumed elements,
155 > * and an iterable for the rest of the elements.
156 > */
157 > export function consume<T>(iterable: Iterable<T>, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable<T>] {
158 const consumed: T[] = [];
159
176 return [consumed, { [Symbol.iterator]() { return iterator; } }];
177 }
178 > iterator.ts
179 > export async function asyncToArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
180 const result: T[] = [];
181 for await (const item of iterable) {
184 return result;
185 }
186 > iterator.ts
187 > export async function asyncToArrayFlat<T>(iterable: AsyncIterable<T[]>): Promise<T[]> {
188 let result: T[] = [];
189 for await (const item of iterable) {
src/vs/base/test/common/utils.ts 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/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/platform/agentHost/common/sessionConfigKeys.ts 52 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- sessionConfigKeys.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 > * Well-known keys used in the agent-host configuration value bag.
8 > *
9 > * The Agent Host Protocol's config schema is intentionally generic — agents
10 > * are free to advertise any property names. These constants capture the
11 > * names that the platform itself consumes (e.g. {@link SessionConfigKey.AutoApprove}
12 > * drives tool auto-approval) or that clients interpret via convention
13 > * (e.g. {@link SessionConfigKey.Branch}, {@link SessionConfigKey.Isolation}).
14 > *
15 > * Provider-owned platform properties use these names in an agent's
16 > * `resolveSessionConfig` response. Worktree properties are owned and
17 > * contributed by the host and are not passed to agents.
18 > */
19 > export const enum SessionConfigKey {
20 > /** `'autoApprove'` — tool auto-approval level. */
21 > AutoApprove = 'autoApprove',
22 > /** `'permissions'` — per-tool session allow/deny lists. */
23 > Permissions = 'permissions',
24 > /** `'isolation'` — host-owned `'folder'` or `'worktree'` selection. */
25 > Isolation = 'isolation',
26 > /** `'branch'` — host-owned base branch to work from. */
27 > Branch = 'branch',
28 > /** `'mode'` — agent execution mode (interactive / plan / autopilot). */
29 > Mode = 'mode',
30 > /** `'worktreeBranchPrefix'` — host-owned prefix for the worktree branch name. */
31 > WorktreeBranchPrefix = 'worktreeBranchPrefix',
32 > /** `'worktreeIncludeFiles'` — host-owned glob patterns for files copied into a new worktree. */
33 > WorktreeIncludeFiles = 'worktreeIncludeFiles',
34 > }
35 >
36 > /**
37 > * The set of enum values the unified permission picker *tolerates* for the
38 > * {@link SessionConfigKey.AutoApprove} property when deciding whether a
39 > * session's schema is "well-known" (and therefore handled by the dedicated
40 > * permission picker rather than the generic per-property fallback).
41 > *
42 > * `default` is the required baseline level; `assisted` and `autoApprove` are
43 > * offered elevated levels. `autopilot` is retained for backward compatibility
44 > * with sessions created before it moved onto the mode axis.
45 > */
46 > export const KNOWN_AUTO_APPROVE_VALUES: ReadonlySet<string> = new Set(['default', 'assisted', 'autoApprove', 'autopilot']);
47 >
48 > /**
49 > * The set of enum values understood for the {@link SessionConfigKey.Mode}
50 > * property: the agent execution mode axis.
51 > */
52 > export const KNOWN_MODE_VALUES: ReadonlySet<string> = new Set(['interactive', 'plan', 'autopilot']);
src/vs/base/common/assert.ts 48 covered LOC · 7 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) {
24 throw new Error(message ? `Assertion failed (${message})` : 'Assertion Failed');
25 }
26 }
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/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/platform/agentHost/common/state/protocol/commands.ts 15 covered LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- commands.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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > export * from './common/commands.js';
10 > export * from './channels-root/commands.js';
11 > export * from './channels-session/commands.js';
12 > export * from './channels-chat/commands.js';
13 > export * from './channels-terminal/commands.js';
14 > export * from './channels-changeset/commands.js';
15 > export * from './channels-resource-watch/commands.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/platform/agentHost/common/state/protocol/errors.ts 9 covered LOC · 1 range

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 > // allow-any-unicode-comment-file
7 > // DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8 >
9 > export * from './common/errors.js';