configurationResolverExpression.ts ×13

Frontier kind: Code frontier

unlabeled · c_503de25f9d0b

100 tests · 33444 LOC · 151 files · introduces 0 tests · 105 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
13 ranges105 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2828 ranges33444 lines · 151 files · Browse complete extent
All tests (intent)
100 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 105 introduced LOC across 13 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/services/configurationResolver/common/configurationResolverExpression.ts 105 introduced LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- configurationResolverExpression.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 { Iterable } from '../../../../base/common/iterator.js';
7 > import { isLinux, isMacintosh, isWindows } from '../../../../base/common/platform.js';
8 > import { ConfiguredInput } from './configurationResolver.js';
9 >
10 > /** A replacement found in the object, as ${name} or ${name:arg} */
11 > export type Replacement = {
12 > /** ${name:arg} */
13 > id: string;
14 > /** The `name:arg` in ${name:arg} */
15 > inner: string;
16 > /** The `name` in ${name:arg} */
17 > name: string;
18 > /** The `arg` in ${name:arg} */
19 > arg?: string;
20 > };
21 >
22 > interface IConfigurationResolverExpression<T> {
23 > /**
24 > * Gets the replacements which have not yet been
25 > * resolved.
26 > */
27 > unresolved(): Iterable<Replacement>;
28 >
29 > /**
30 > * Gets the replacements which have been resolved.
31 > */
32 > resolved(): Iterable<[Replacement, IResolvedValue]>;
33 >
34 > /**
35 > * Resolves a replacement into the string value.
36 > * If the value is undefined, the original variable text will be preserved.
37 > */
38 > resolve(replacement: Replacement, data: string | IResolvedValue): void;
39 >
40 > /**
41 > * Returns the complete object. Any unresolved replacements are left intact.
42 > */
43 > toObject(): T;
44 > }
45 >
46 > type PropertyLocation = {
47 > object: any;
48 > propertyName: string | number;
49 > replaceKeyName?: boolean;
50 > };
51 >
52 > export interface IResolvedValue {
53 > value: string | undefined;
54 >
55 > /** Present when the variable is resolved from an input field. */
56 > input?: ConfiguredInput;
57 > }
58 >
59 > interface IReplacementLocation {
60 > replacement: Replacement;
61 > locations: PropertyLocation[];
62 > resolved?: IResolvedValue;
63 > }
64 >
65 > export class ConfigurationResolverExpression<T> implements IConfigurationResolverExpression<T> {
66 > public static readonly VARIABLE_LHS = '${';
67 >
68 > private readonly locations = new Map<string, IReplacementLocation>();
69 > private root: T;
70 > private stringRoot: boolean;
71 > /**
72 > * Callbacks when a new replacement is made, so that nested resolutions from
73 > * `expr.unresolved()` can be fulfilled in the same iteration.
74 > */
75 > private newReplacementNotifiers = new Set<(r: Replacement) => void>();
76 >
77 > private constructor(object: T) {
78 // If the input is a string, wrap it in an object so we can use the same logic
79 if (typeof object === 'string') {
86 }
87 }
89 > /**
90 > * Creates a new {@link ConfigurationResolverExpression} from an object.
91 > * Note that platform-specific keys (i.e. `windows`, `osx`, `linux`) are
92 > * applied during parsing.
93 > */
94 > public static parse<T>(object: T): ConfigurationResolverExpression<T> {
95 if (object instanceof ConfigurationResolverExpression) {
96 return object;
102 return expr;
103 }
105 > private applyPlatformSpecificKeys() {
106 // eslint-disable-next-line local/code-no-any-casts
107 const config = this.root as any; // already cloned by ctor, safe to change
116 delete config.linux;
117 }
119 > private parseVariable(str: string, start: number): { replacement: Replacement; end: number } | undefined {
120 if (str[start] !== '$' || str[start + 1] !== '{') {
121 return undefined;
185 }
186 }
188 > private parseString(object: any, propertyName: string | number, value: string, replaceKeyName?: boolean, replacementPath?: string[]): void {
189 let pos = 0;
190 while (pos < value.length) {
215 }
216 }
218 > public *unresolved(): Iterable<Replacement> {
219 const newReplacements = new Map<string, Replacement>();
220 const notifier = (replacement: Replacement) => {
243 this.newReplacementNotifiers.delete(notifier);
244 }
246 > public resolved(): Iterable<[Replacement, IResolvedValue]> {
247 return Iterable.map(Iterable.filter(this.locations.values(), l => !!l.resolved), l => [l.replacement, l.resolved!]);
248 }
250 > public resolve(replacement: Replacement, data: string | IResolvedValue): void {
251 if (typeof data !== 'object') {
252 data = { value: String(data) };
266 }
267 }
269 > private _resolveAtLocation(replacement: Replacement, { replaceKeyName, propertyName, object }: PropertyLocation, data: IResolvedValue, path: string[] = []) {
270 if (data.value === undefined) {
271 return;
290 path.pop();
291 }
293 > private _renameKeyInLocations(obj: object, oldKey: string, newKey: string) {
294 for (const location of this.locations.values()) {
295 for (const loc of location.locations) {