Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/terminal-suggest/src/fig/fig-autocomplete-shared/mixins.ts
3520 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { makeArray } from './utils';
7
8
export type SpecMixin =
9
| Fig.Subcommand
10
| ((currentSpec: Fig.Subcommand, context: Fig.ShellContext) => Fig.Subcommand);
11
12
type NamedObject = { name: Fig.SingleOrArray<string> };
13
14
const concatArrays = <T>(a: T[] | undefined, b: T[] | undefined): T[] | undefined =>
15
a && b ? [...a, ...b] : a || b;
16
17
const mergeNames = <T = string>(a: T | T[], b: T | T[]): T | T[] => [
18
...new Set(concatArrays(makeArray(a), makeArray(b))),
19
];
20
21
const mergeArrays = <T>(a: T[] | undefined, b: T[] | undefined): T[] | undefined =>
22
a && b ? [...new Set(concatArrays(makeArray(a), makeArray(b)))] : a || b;
23
24
const mergeArgs = (arg: Fig.Arg, partial: Fig.Arg): Fig.Arg => ({
25
...arg,
26
...partial,
27
suggestions: concatArrays<Fig.Suggestion | string>(arg.suggestions, partial.suggestions),
28
generators:
29
arg.generators && partial.generators
30
? concatArrays(makeArray(arg.generators), makeArray(partial.generators))
31
: arg.generators || partial.generators,
32
template:
33
arg.template && partial.template
34
? mergeNames<Fig.TemplateStrings>(arg.template, partial.template)
35
: arg.template || partial.template,
36
});
37
38
const mergeArgArrays = (
39
args: Fig.SingleOrArray<Fig.Arg> | undefined,
40
partials: Fig.SingleOrArray<Fig.Arg> | undefined
41
): Fig.SingleOrArray<Fig.Arg> | undefined => {
42
if (!args || !partials) {
43
return args || partials;
44
}
45
const argArray = makeArray(args);
46
const partialArray = makeArray(partials);
47
const result = [];
48
for (let i = 0; i < Math.max(argArray.length, partialArray.length); i += 1) {
49
const arg = argArray[i];
50
const partial = partialArray[i];
51
if (arg !== undefined && partial !== undefined) {
52
result.push(mergeArgs(arg, partial));
53
} else if (partial !== undefined || arg !== undefined) {
54
result.push(arg || partial);
55
}
56
}
57
return result.length === 1 ? result[0] : result;
58
};
59
60
const mergeOptions = (option: Fig.Option, partial: Fig.Option): Fig.Option => ({
61
...option,
62
...partial,
63
name: mergeNames(option.name, partial.name),
64
args: mergeArgArrays(option.args, partial.args),
65
exclusiveOn: mergeArrays(option.exclusiveOn, partial.exclusiveOn),
66
dependsOn: mergeArrays(option.dependsOn, partial.dependsOn),
67
});
68
69
const mergeNamedObjectArrays = <T extends NamedObject>(
70
objects: T[] | undefined,
71
partials: T[] | undefined,
72
mergeItems: (a: T, b: T) => T
73
): T[] | undefined => {
74
if (!objects || !partials) {
75
return objects || partials;
76
}
77
const mergedObjects = objects ? [...objects] : [];
78
79
const existingNameIndexMap: Record<string, number> = {};
80
for (let i = 0; i < objects.length; i += 1) {
81
makeArray(objects[i].name).forEach((name) => {
82
existingNameIndexMap[name] = i;
83
});
84
}
85
86
for (let i = 0; i < partials.length; i += 1) {
87
const partial = partials[i];
88
if (!partial) {
89
throw new Error('Invalid object passed to merge');
90
}
91
const existingNames = makeArray(partial.name).filter((name) => name in existingNameIndexMap);
92
if (existingNames.length === 0) {
93
mergedObjects.push(partial);
94
} else {
95
const index = existingNameIndexMap[existingNames[0]];
96
if (existingNames.some((name) => existingNameIndexMap[name] !== index)) {
97
throw new Error('Names provided for option matched multiple existing options');
98
}
99
mergedObjects[index] = mergeItems(mergedObjects[index], partial);
100
}
101
}
102
return mergedObjects;
103
};
104
105
function mergeOptionArrays(
106
options: Fig.Option[] | undefined,
107
partials: Fig.Option[] | undefined
108
): Fig.Option[] | undefined {
109
return mergeNamedObjectArrays(options, partials, mergeOptions);
110
}
111
112
function mergeSubcommandArrays(
113
subcommands: Fig.Subcommand[] | undefined,
114
partials: Fig.Subcommand[] | undefined
115
): Fig.Subcommand[] | undefined {
116
return mergeNamedObjectArrays(subcommands, partials, mergeSubcommands);
117
}
118
119
export function mergeSubcommands(
120
subcommand: Fig.Subcommand,
121
partial: Fig.Subcommand
122
): Fig.Subcommand {
123
return {
124
...subcommand,
125
...partial,
126
name: mergeNames(subcommand.name, partial.name),
127
args: mergeArgArrays(subcommand.args, partial.args),
128
additionalSuggestions: concatArrays<Fig.Suggestion | string>(
129
subcommand.additionalSuggestions,
130
partial.additionalSuggestions
131
),
132
subcommands: mergeSubcommandArrays(subcommand.subcommands, partial.subcommands),
133
options: mergeOptionArrays(subcommand.options, partial.options),
134
parserDirectives:
135
subcommand.parserDirectives && partial.parserDirectives
136
? { ...subcommand.parserDirectives, ...partial.parserDirectives }
137
: subcommand.parserDirectives || partial.parserDirectives,
138
};
139
}
140
141
export const applyMixin = (
142
spec: Fig.Subcommand,
143
context: Fig.ShellContext,
144
mixin: SpecMixin
145
): Fig.Subcommand => {
146
if (typeof mixin === 'function') {
147
return mixin(spec, context);
148
}
149
const partial = mixin;
150
return mergeSubcommands(spec, partial);
151
};
152
153