@microsoft/inshellisense 0.0.1-rc.2 → 0.0.1-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +80 -6
- package/SECURITY.md +41 -41
- package/build/commands/complete.js +16 -0
- package/build/commands/doctor.js +11 -0
- package/build/commands/init.js +24 -0
- package/build/commands/root.js +27 -30
- package/build/commands/specs/list.js +26 -0
- package/build/commands/specs/root.js +8 -0
- package/build/commands/uninstall.js +1 -1
- package/build/index.js +20 -7
- package/build/isterm/commandManager.js +184 -0
- package/build/isterm/index.js +4 -0
- package/build/isterm/pty.js +361 -0
- package/build/runtime/alias.js +66 -0
- package/build/runtime/generator.js +24 -11
- package/build/runtime/parser.js +86 -16
- package/build/runtime/runtime.js +103 -45
- package/build/runtime/spec.js +36 -0
- package/build/runtime/suggestion.js +70 -22
- package/build/runtime/template.js +33 -18
- package/build/runtime/utils.js +111 -12
- package/build/ui/suggestionManager.js +162 -0
- package/build/ui/ui-doctor.js +69 -0
- package/build/ui/ui-root.js +134 -64
- package/build/ui/ui-uninstall.js +3 -5
- package/build/ui/utils.js +57 -0
- package/build/utils/ansi.js +37 -0
- package/build/utils/config.js +99 -0
- package/build/utils/log.js +39 -0
- package/build/utils/shell.js +318 -0
- package/package.json +39 -6
- package/scripts/postinstall.js +9 -0
- package/shell/bash-preexec.sh +380 -0
- package/shell/shellIntegration-env.zsh +12 -0
- package/shell/shellIntegration-login.zsh +9 -0
- package/shell/shellIntegration-profile.zsh +9 -0
- package/shell/shellIntegration-rc.zsh +66 -0
- package/shell/shellIntegration.bash +114 -0
- package/shell/shellIntegration.fish +27 -0
- package/shell/shellIntegration.nu +29 -0
- package/shell/shellIntegration.ps1 +26 -0
- package/shell/shellIntegration.xsh +31 -0
- package/todo.md +17 -0
- package/build/commands/bind.js +0 -12
- package/build/ui/input.js +0 -55
- package/build/ui/suggestions.js +0 -84
- package/build/ui/ui-bind.js +0 -69
- package/build/utils/bindings.js +0 -216
- package/build/utils/cache.js +0 -21
- package/shell/key-bindings-powershell.ps1 +0 -27
- package/shell/key-bindings-pwsh.ps1 +0 -27
- package/shell/key-bindings.bash +0 -7
- package/shell/key-bindings.fish +0 -8
- package/shell/key-bindings.zsh +0 -10
package/build/runtime/runtime.js
CHANGED
|
@@ -4,25 +4,35 @@ import speclist, { diffVersionedCompletions as versionedSpeclist,
|
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
} from "@withfig/autocomplete/build/index.js";
|
|
7
|
+
import path from "node:path";
|
|
7
8
|
import { parseCommand } from "./parser.js";
|
|
8
9
|
import { getArgDrivenRecommendation, getSubcommandDrivenRecommendation } from "./suggestion.js";
|
|
9
|
-
import { buildExecuteShellCommand } from "./utils.js";
|
|
10
|
+
import { buildExecuteShellCommand, resolveCwd } from "./utils.js";
|
|
11
|
+
import { aliasExpand } from "./alias.js";
|
|
12
|
+
import { getConfig } from "../utils/config.js";
|
|
13
|
+
import log from "../utils/log.js";
|
|
10
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- recursive type, setting as any
|
|
11
15
|
const specSet = {};
|
|
12
|
-
speclist
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
function loadSpecsSet(speclist, versionedSpeclist, specsPath) {
|
|
17
|
+
speclist.forEach((s) => {
|
|
18
|
+
let activeSet = specSet;
|
|
19
|
+
const specRoutes = s.split("/");
|
|
20
|
+
specRoutes.forEach((route, idx) => {
|
|
21
|
+
if (typeof activeSet !== "object") {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (idx === specRoutes.length - 1) {
|
|
25
|
+
const prefix = versionedSpeclist.includes(s) ? "/index.js" : `.js`;
|
|
26
|
+
activeSet[route] = `${specsPath}/${s}${prefix}`;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
activeSet[route] = activeSet[route] || {};
|
|
30
|
+
activeSet = activeSet[route];
|
|
31
|
+
}
|
|
32
|
+
});
|
|
24
33
|
});
|
|
25
|
-
}
|
|
34
|
+
}
|
|
35
|
+
loadSpecsSet(speclist, versionedSpeclist, `@withfig/autocomplete/build`);
|
|
26
36
|
const loadedSpecs = {};
|
|
27
37
|
const loadSpec = async (cmd) => {
|
|
28
38
|
const rootToken = cmd.at(0);
|
|
@@ -46,25 +56,53 @@ const lazyLoadSpec = async (key) => {
|
|
|
46
56
|
const lazyLoadSpecLocation = async (location) => {
|
|
47
57
|
return; //TODO: implement spec location loading
|
|
48
58
|
};
|
|
49
|
-
export const
|
|
50
|
-
const
|
|
59
|
+
export const loadLocalSpecsSet = async () => {
|
|
60
|
+
const specsPath = getConfig()?.specs?.path;
|
|
61
|
+
if (!specsPath) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await Promise.allSettled(specsPath.map((specPath) => import(path.join(specPath, "index.js"))
|
|
66
|
+
.then((res) => {
|
|
67
|
+
const { default: speclist, diffVersionedCompletions: versionedSpeclist } = res;
|
|
68
|
+
loadSpecsSet(speclist, versionedSpeclist, specPath);
|
|
69
|
+
})
|
|
70
|
+
.catch((e) => {
|
|
71
|
+
log.debug({ msg: "load local spec failed", e: e.message, specPath });
|
|
72
|
+
})));
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
log.debug({ msg: "load local specs failed", e: e.message, specsPath });
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
export const getSuggestions = async (cmd, cwd, shell) => {
|
|
79
|
+
let activeCmd = parseCommand(cmd, shell);
|
|
51
80
|
const rootToken = activeCmd.at(0);
|
|
52
81
|
if (activeCmd.length === 0 || !rootToken?.complete) {
|
|
53
82
|
return;
|
|
54
83
|
}
|
|
84
|
+
activeCmd = aliasExpand(activeCmd);
|
|
55
85
|
const spec = await loadSpec(activeCmd);
|
|
56
86
|
if (spec == null)
|
|
57
87
|
return;
|
|
58
88
|
const subcommand = getSubcommand(spec);
|
|
59
89
|
if (subcommand == null)
|
|
60
90
|
return;
|
|
61
|
-
const
|
|
91
|
+
const lastCommand = activeCmd.at(-1);
|
|
92
|
+
const { cwd: resolvedCwd, pathy, complete: pathyComplete } = await resolveCwd(lastCommand, cwd, shell);
|
|
93
|
+
if (pathy && lastCommand) {
|
|
94
|
+
lastCommand.isPath = true;
|
|
95
|
+
lastCommand.isPathComplete = pathyComplete;
|
|
96
|
+
}
|
|
97
|
+
const result = await runSubcommand(activeCmd.slice(1), subcommand, resolvedCwd, shell);
|
|
62
98
|
if (result == null)
|
|
63
99
|
return;
|
|
64
|
-
const
|
|
65
|
-
const charactersToDrop = lastCommand?.complete ? 0 : lastCommand?.token.length ?? 0;
|
|
100
|
+
const charactersToDrop = lastCommand?.complete ? 0 : lastCommand?.tokenLength;
|
|
66
101
|
return { ...result, charactersToDrop };
|
|
67
102
|
};
|
|
103
|
+
export const getSpecNames = () => {
|
|
104
|
+
return Object.keys(specSet).filter((spec) => !spec.startsWith("@") && spec != "-");
|
|
105
|
+
};
|
|
68
106
|
const getPersistentOptions = (persistentOptions, options) => {
|
|
69
107
|
const persistentOptionNames = new Set(persistentOptions.map((o) => (typeof o.name === "string" ? [o.name] : o.name)).flat());
|
|
70
108
|
return persistentOptions.concat((options ?? []).filter((o) => (typeof o.name == "string" ? !persistentOptionNames.has(o.name) : o.name.some((n) => !persistentOptionNames.has(n))) && o.isPersistent === true));
|
|
@@ -82,14 +120,14 @@ const getSubcommand = (spec) => {
|
|
|
82
120
|
}
|
|
83
121
|
return spec;
|
|
84
122
|
};
|
|
85
|
-
const executeShellCommand = buildExecuteShellCommand(5000);
|
|
123
|
+
const executeShellCommand = await buildExecuteShellCommand(5000);
|
|
86
124
|
const genSubcommand = async (command, parentCommand) => {
|
|
87
|
-
|
|
88
|
-
if (subcommandIdx == null)
|
|
125
|
+
if (!parentCommand.subcommands || parentCommand.subcommands.length === 0)
|
|
89
126
|
return;
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
127
|
+
const subcommandIdx = parentCommand.subcommands.findIndex((s) => (Array.isArray(s.name) ? s.name.includes(command) : s.name === command));
|
|
128
|
+
if (subcommandIdx === -1)
|
|
92
129
|
return;
|
|
130
|
+
const subcommand = parentCommand.subcommands[subcommandIdx];
|
|
93
131
|
// this pulls in the spec from the load spec and overwrites the subcommand in the parent with the loaded spec.
|
|
94
132
|
// then it returns the subcommand and clears the loadSpec field so that it doesn't get called again
|
|
95
133
|
switch (typeof subcommand.loadSpec) {
|
|
@@ -115,17 +153,29 @@ const genSubcommand = async (command, parentCommand) => {
|
|
|
115
153
|
return parentCommand.subcommands[subcommandIdx];
|
|
116
154
|
}
|
|
117
155
|
else {
|
|
118
|
-
parentCommand.subcommands[subcommandIdx] = {
|
|
156
|
+
parentCommand.subcommands[subcommandIdx] = {
|
|
157
|
+
...subcommand,
|
|
158
|
+
...partSpec,
|
|
159
|
+
loadSpec: undefined,
|
|
160
|
+
};
|
|
119
161
|
return parentCommand.subcommands[subcommandIdx];
|
|
120
162
|
}
|
|
121
163
|
}
|
|
122
164
|
case "string": {
|
|
123
165
|
const spec = await lazyLoadSpec(subcommand.loadSpec);
|
|
124
|
-
parentCommand.subcommands[subcommandIdx] = {
|
|
166
|
+
parentCommand.subcommands[subcommandIdx] = {
|
|
167
|
+
...subcommand,
|
|
168
|
+
...(getSubcommand(spec) ?? []),
|
|
169
|
+
loadSpec: undefined,
|
|
170
|
+
};
|
|
125
171
|
return parentCommand.subcommands[subcommandIdx];
|
|
126
172
|
}
|
|
127
173
|
case "object": {
|
|
128
|
-
parentCommand.subcommands[subcommandIdx] = {
|
|
174
|
+
parentCommand.subcommands[subcommandIdx] = {
|
|
175
|
+
...subcommand,
|
|
176
|
+
...(subcommand.loadSpec ?? {}),
|
|
177
|
+
loadSpec: undefined,
|
|
178
|
+
};
|
|
129
179
|
return parentCommand.subcommands[subcommandIdx];
|
|
130
180
|
}
|
|
131
181
|
case "undefined": {
|
|
@@ -142,7 +192,7 @@ const getPersistentTokens = (tokens) => {
|
|
|
142
192
|
const getArgs = (args) => {
|
|
143
193
|
return args instanceof Array ? args : args != null ? [args] : [];
|
|
144
194
|
};
|
|
145
|
-
const runOption = async (tokens, option, subcommand, persistentOptions, acceptedTokens) => {
|
|
195
|
+
const runOption = async (tokens, option, subcommand, cwd, shell, persistentOptions, acceptedTokens) => {
|
|
146
196
|
if (tokens.length === 0) {
|
|
147
197
|
throw new Error("invalid state reached, option expected but no tokens found");
|
|
148
198
|
}
|
|
@@ -150,37 +200,40 @@ const runOption = async (tokens, option, subcommand, persistentOptions, accepted
|
|
|
150
200
|
const isPersistent = persistentOptions.some((o) => (typeof o.name === "string" ? o.name === activeToken.token : o.name.includes(activeToken.token)));
|
|
151
201
|
if ((option.args instanceof Array && option.args.length > 0) || option.args != null) {
|
|
152
202
|
const args = option.args instanceof Array ? option.args : [option.args];
|
|
153
|
-
return runArg(tokens.slice(1), args, subcommand, persistentOptions, acceptedTokens.concat(activeToken), true, false);
|
|
203
|
+
return runArg(tokens.slice(1), args, subcommand, cwd, shell, persistentOptions, acceptedTokens.concat(activeToken), true, false);
|
|
154
204
|
}
|
|
155
|
-
return runSubcommand(tokens.slice(1), subcommand, persistentOptions, acceptedTokens.concat({
|
|
205
|
+
return runSubcommand(tokens.slice(1), subcommand, cwd, shell, persistentOptions, acceptedTokens.concat({
|
|
206
|
+
...activeToken,
|
|
207
|
+
isPersistent,
|
|
208
|
+
}));
|
|
156
209
|
};
|
|
157
|
-
const runArg = async (tokens, args, subcommand, persistentOptions, acceptedTokens, fromOption, fromVariadic) => {
|
|
210
|
+
const runArg = async (tokens, args, subcommand, cwd, shell, persistentOptions, acceptedTokens, fromOption, fromVariadic) => {
|
|
158
211
|
if (args.length === 0) {
|
|
159
|
-
return runSubcommand(tokens, subcommand, persistentOptions, acceptedTokens, true, !fromOption);
|
|
212
|
+
return runSubcommand(tokens, subcommand, cwd, shell, persistentOptions, acceptedTokens, true, !fromOption);
|
|
160
213
|
}
|
|
161
214
|
else if (tokens.length === 0) {
|
|
162
|
-
return await getArgDrivenRecommendation(args, subcommand, persistentOptions, undefined, acceptedTokens, fromVariadic);
|
|
215
|
+
return await getArgDrivenRecommendation(args, subcommand, persistentOptions, undefined, acceptedTokens, fromVariadic, cwd, shell);
|
|
163
216
|
}
|
|
164
217
|
else if (!tokens.at(0)?.complete) {
|
|
165
|
-
return await getArgDrivenRecommendation(args, subcommand, persistentOptions, tokens[0]
|
|
218
|
+
return await getArgDrivenRecommendation(args, subcommand, persistentOptions, tokens[0], acceptedTokens, fromVariadic, cwd, shell);
|
|
166
219
|
}
|
|
167
220
|
const activeToken = tokens[0];
|
|
168
221
|
if (args.every((a) => a.isOptional)) {
|
|
169
222
|
if (activeToken.isOption) {
|
|
170
223
|
const option = getOption(activeToken, persistentOptions.concat(subcommand.options ?? []));
|
|
171
224
|
if (option != null) {
|
|
172
|
-
return runOption(tokens, option, subcommand, persistentOptions, acceptedTokens);
|
|
225
|
+
return runOption(tokens, option, subcommand, cwd, shell, persistentOptions, acceptedTokens);
|
|
173
226
|
}
|
|
174
227
|
return;
|
|
175
228
|
}
|
|
176
229
|
const nextSubcommand = await genSubcommand(activeToken.token, subcommand);
|
|
177
230
|
if (nextSubcommand != null) {
|
|
178
|
-
return runSubcommand(tokens.slice(1), nextSubcommand, persistentOptions, getPersistentTokens(acceptedTokens.concat(activeToken)));
|
|
231
|
+
return runSubcommand(tokens.slice(1), nextSubcommand, cwd, shell, persistentOptions, getPersistentTokens(acceptedTokens.concat(activeToken)));
|
|
179
232
|
}
|
|
180
233
|
}
|
|
181
234
|
const activeArg = args[0];
|
|
182
235
|
if (activeArg.isVariadic) {
|
|
183
|
-
return runArg(tokens.slice(1), args, subcommand, persistentOptions, acceptedTokens.concat(activeToken), fromOption, true);
|
|
236
|
+
return runArg(tokens.slice(1), args, subcommand, cwd, shell, persistentOptions, acceptedTokens.concat(activeToken), fromOption, true);
|
|
184
237
|
}
|
|
185
238
|
else if (activeArg.isCommand) {
|
|
186
239
|
if (tokens.length <= 0) {
|
|
@@ -192,16 +245,16 @@ const runArg = async (tokens, args, subcommand, persistentOptions, acceptedToken
|
|
|
192
245
|
const subcommand = getSubcommand(spec);
|
|
193
246
|
if (subcommand == null)
|
|
194
247
|
return;
|
|
195
|
-
return runSubcommand(tokens.slice(1), subcommand);
|
|
248
|
+
return runSubcommand(tokens.slice(1), subcommand, cwd, shell);
|
|
196
249
|
}
|
|
197
|
-
return runArg(tokens.slice(1), args.slice(1), subcommand, persistentOptions, acceptedTokens.concat(activeToken), fromOption, false);
|
|
250
|
+
return runArg(tokens.slice(1), args.slice(1), subcommand, cwd, shell, persistentOptions, acceptedTokens.concat(activeToken), fromOption, false);
|
|
198
251
|
};
|
|
199
|
-
const runSubcommand = async (tokens, subcommand, persistentOptions = [], acceptedTokens = [], argsDepleted = false, argsUsed = false) => {
|
|
252
|
+
const runSubcommand = async (tokens, subcommand, cwd, shell, persistentOptions = [], acceptedTokens = [], argsDepleted = false, argsUsed = false) => {
|
|
200
253
|
if (tokens.length === 0) {
|
|
201
|
-
return getSubcommandDrivenRecommendation(subcommand, persistentOptions, undefined, argsDepleted, argsUsed, acceptedTokens);
|
|
254
|
+
return getSubcommandDrivenRecommendation(subcommand, persistentOptions, undefined, argsDepleted, argsUsed, acceptedTokens, cwd, shell);
|
|
202
255
|
}
|
|
203
256
|
else if (!tokens.at(0)?.complete) {
|
|
204
|
-
return getSubcommandDrivenRecommendation(subcommand, persistentOptions, tokens[0]
|
|
257
|
+
return getSubcommandDrivenRecommendation(subcommand, persistentOptions, tokens[0], argsDepleted, argsUsed, acceptedTokens, cwd, shell);
|
|
205
258
|
}
|
|
206
259
|
const activeToken = tokens[0];
|
|
207
260
|
const activeArgsLength = subcommand.args instanceof Array ? subcommand.args.length : 1;
|
|
@@ -209,16 +262,21 @@ const runSubcommand = async (tokens, subcommand, persistentOptions = [], accepte
|
|
|
209
262
|
if (activeToken.isOption) {
|
|
210
263
|
const option = getOption(activeToken, allOptions);
|
|
211
264
|
if (option != null) {
|
|
212
|
-
return runOption(tokens, option, subcommand, persistentOptions, acceptedTokens);
|
|
265
|
+
return runOption(tokens, option, subcommand, cwd, shell, persistentOptions, acceptedTokens);
|
|
213
266
|
}
|
|
214
267
|
return;
|
|
215
268
|
}
|
|
216
269
|
const nextSubcommand = await genSubcommand(activeToken.token, subcommand);
|
|
217
270
|
if (nextSubcommand != null) {
|
|
218
|
-
return runSubcommand(tokens.slice(1), nextSubcommand, getPersistentOptions(persistentOptions, subcommand.options), getPersistentTokens(acceptedTokens.concat(activeToken)));
|
|
271
|
+
return runSubcommand(tokens.slice(1), nextSubcommand, cwd, shell, getPersistentOptions(persistentOptions, subcommand.options), getPersistentTokens(acceptedTokens.concat(activeToken)));
|
|
219
272
|
}
|
|
220
273
|
if (activeArgsLength <= 0) {
|
|
221
274
|
return; // not subcommand or option & no args exist
|
|
222
275
|
}
|
|
223
|
-
|
|
276
|
+
const args = getArgs(subcommand.args);
|
|
277
|
+
if (args.length != 0) {
|
|
278
|
+
return runArg(tokens, args, subcommand, cwd, shell, allOptions, acceptedTokens, false, false);
|
|
279
|
+
}
|
|
280
|
+
// if the subcommand has no args specified, fallback to the subcommand and ignore this item
|
|
281
|
+
return runSubcommand(tokens.slice(1), subcommand, cwd, shell, persistentOptions, acceptedTokens.concat(activeToken));
|
|
224
282
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
5
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
6
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
7
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
8
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
9
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
10
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.getDiffVersionedSpeclist = exports.getSpeclist = void 0;
|
|
15
|
+
let speclist = undefined;
|
|
16
|
+
let versionedSpeclist = undefined;
|
|
17
|
+
const getSpeclist = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
if (!speclist) {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
const autocomplete = yield import("@withfig/autocomplete/build/index.js");
|
|
22
|
+
speclist = autocomplete.default;
|
|
23
|
+
}
|
|
24
|
+
return speclist;
|
|
25
|
+
});
|
|
26
|
+
exports.getSpeclist = getSpeclist;
|
|
27
|
+
const getDiffVersionedSpeclist = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
if (!versionedSpeclist) {
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
const autocomplete = yield import("@withfig/autocomplete/build/index.js");
|
|
32
|
+
versionedSpeclist = autocomplete.diffVersionedCompletions;
|
|
33
|
+
}
|
|
34
|
+
return versionedSpeclist;
|
|
35
|
+
});
|
|
36
|
+
exports.getDiffVersionedSpeclist = getDiffVersionedSpeclist;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
import { runGenerator } from "./generator.js";
|
|
4
5
|
import { runTemplates } from "./template.js";
|
|
5
|
-
|
|
6
|
+
import log from "../utils/log.js";
|
|
7
|
+
import { escapePath } from "./utils.js";
|
|
8
|
+
import { addPathSeparator, getPathDirname, removePathSeparator } from "../utils/shell.js";
|
|
9
|
+
export var SuggestionIcons;
|
|
6
10
|
(function (SuggestionIcons) {
|
|
7
11
|
SuggestionIcons["File"] = "\uD83D\uDCC4";
|
|
8
12
|
SuggestionIcons["Folder"] = "\uD83D\uDCC1";
|
|
@@ -14,7 +18,11 @@ var SuggestionIcons;
|
|
|
14
18
|
SuggestionIcons["Special"] = "\u2B50";
|
|
15
19
|
SuggestionIcons["Default"] = "\uD83D\uDCC0";
|
|
16
20
|
})(SuggestionIcons || (SuggestionIcons = {}));
|
|
17
|
-
const getIcon = (suggestionType) => {
|
|
21
|
+
const getIcon = (icon, suggestionType) => {
|
|
22
|
+
// eslint-disable-next-line no-control-regex
|
|
23
|
+
if (icon && /[^\u0000-\u00ff]/.test(icon)) {
|
|
24
|
+
return icon;
|
|
25
|
+
}
|
|
18
26
|
switch (suggestionType) {
|
|
19
27
|
case "arg":
|
|
20
28
|
return SuggestionIcons.Argument;
|
|
@@ -38,16 +46,20 @@ const getIcon = (suggestionType) => {
|
|
|
38
46
|
const getLong = (suggestion) => {
|
|
39
47
|
return suggestion instanceof Array ? suggestion.reduce((p, c) => (p.length > c.length ? p : c)) : suggestion;
|
|
40
48
|
};
|
|
49
|
+
const getPathy = (type) => {
|
|
50
|
+
return type === "file" || type === "folder";
|
|
51
|
+
};
|
|
41
52
|
const toSuggestion = (suggestion, name, type) => {
|
|
42
53
|
if (suggestion.name == null)
|
|
43
54
|
return;
|
|
44
55
|
return {
|
|
45
56
|
name: name ?? getLong(suggestion.name),
|
|
46
57
|
description: suggestion.description,
|
|
47
|
-
icon: getIcon(type ?? suggestion.type),
|
|
58
|
+
icon: getIcon(suggestion.icon, type ?? suggestion.type),
|
|
48
59
|
allNames: suggestion.name instanceof Array ? suggestion.name : [suggestion.name],
|
|
49
60
|
priority: suggestion.priority ?? 50,
|
|
50
61
|
insertValue: suggestion.insertValue,
|
|
62
|
+
type: suggestion.type,
|
|
51
63
|
};
|
|
52
64
|
};
|
|
53
65
|
function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
|
|
@@ -65,10 +77,11 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
|
|
|
65
77
|
? {
|
|
66
78
|
name: matchedName,
|
|
67
79
|
description: s.description,
|
|
68
|
-
icon: getIcon(s.type ?? suggestionType),
|
|
80
|
+
icon: getIcon(s.icon, s.type ?? suggestionType),
|
|
69
81
|
allNames: s.name,
|
|
70
82
|
priority: s.priority ?? 50,
|
|
71
83
|
insertValue: s.insertValue,
|
|
84
|
+
type: s.type,
|
|
72
85
|
}
|
|
73
86
|
: undefined;
|
|
74
87
|
}
|
|
@@ -76,10 +89,11 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
|
|
|
76
89
|
? {
|
|
77
90
|
name: s.name,
|
|
78
91
|
description: s.description,
|
|
79
|
-
icon: getIcon(s.type ?? suggestionType),
|
|
92
|
+
icon: getIcon(s.icon, s.type ?? suggestionType),
|
|
80
93
|
allNames: [s.name],
|
|
81
94
|
priority: s.priority ?? 50,
|
|
82
95
|
insertValue: s.insertValue,
|
|
96
|
+
type: s.type,
|
|
83
97
|
}
|
|
84
98
|
: undefined;
|
|
85
99
|
})
|
|
@@ -95,10 +109,11 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
|
|
|
95
109
|
? {
|
|
96
110
|
name: matchedName,
|
|
97
111
|
description: s.description,
|
|
98
|
-
icon: getIcon(s.type ?? suggestionType),
|
|
112
|
+
icon: getIcon(s.icon, s.type ?? suggestionType),
|
|
99
113
|
allNames: s.name,
|
|
100
114
|
insertValue: s.insertValue,
|
|
101
115
|
priority: s.priority ?? 50,
|
|
116
|
+
type: s.type,
|
|
102
117
|
}
|
|
103
118
|
: undefined;
|
|
104
119
|
}
|
|
@@ -106,24 +121,27 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
|
|
|
106
121
|
? {
|
|
107
122
|
name: s.name,
|
|
108
123
|
description: s.description,
|
|
109
|
-
icon: getIcon(s.type ?? suggestionType),
|
|
124
|
+
icon: getIcon(s.icon, s.type ?? suggestionType),
|
|
110
125
|
allNames: [s.name],
|
|
111
126
|
insertValue: s.insertValue,
|
|
112
127
|
priority: s.priority ?? 50,
|
|
128
|
+
type: s.type,
|
|
113
129
|
}
|
|
114
130
|
: undefined;
|
|
115
131
|
})
|
|
116
132
|
.filter((s) => s != null);
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
|
-
const generatorSuggestions = async (generator, acceptedTokens, filterStrategy, partialCmd) => {
|
|
135
|
+
const generatorSuggestions = async (generator, acceptedTokens, filterStrategy, partialCmd, cwd) => {
|
|
120
136
|
const generators = generator instanceof Array ? generator : generator ? [generator] : [];
|
|
121
137
|
const tokens = acceptedTokens.map((t) => t.token);
|
|
122
|
-
|
|
123
|
-
|
|
138
|
+
if (partialCmd)
|
|
139
|
+
tokens.push(partialCmd);
|
|
140
|
+
const suggestions = (await Promise.all(generators.map((gen) => runGenerator(gen, tokens, cwd)))).flat();
|
|
141
|
+
return filter(suggestions.map((suggestion) => ({ ...suggestion, priority: suggestion.priority ?? 60 })), filterStrategy, partialCmd, undefined);
|
|
124
142
|
};
|
|
125
|
-
const templateSuggestions = async (templates, filterStrategy, partialCmd) => {
|
|
126
|
-
return filter(await runTemplates(templates ?? []), filterStrategy, partialCmd, undefined);
|
|
143
|
+
const templateSuggestions = async (templates, filterStrategy, partialCmd, cwd) => {
|
|
144
|
+
return filter(await runTemplates(templates ?? [], cwd), filterStrategy, partialCmd, undefined);
|
|
127
145
|
};
|
|
128
146
|
const suggestionSuggestions = (suggestions, filterStrategy, partialCmd) => {
|
|
129
147
|
const cleanedSuggestions = suggestions?.map((s) => (typeof s === "string" ? { name: s } : s)) ?? [];
|
|
@@ -137,17 +155,43 @@ const optionSuggestions = (options, acceptedTokens, filterStrategy, partialCmd)
|
|
|
137
155
|
const validOptions = options?.filter((o) => o.exclusiveOn?.every((exclusiveOption) => !usedOptions.has(exclusiveOption)) ?? true);
|
|
138
156
|
return filter(validOptions ?? [], filterStrategy, partialCmd, "option");
|
|
139
157
|
};
|
|
140
|
-
|
|
158
|
+
function adjustPathSuggestions(suggestions, partialToken, shell) {
|
|
159
|
+
return suggestions.map((s) => {
|
|
160
|
+
const pathy = getPathy(s.type);
|
|
161
|
+
const rawInsertValue = removePathSeparator(s.insertValue ?? s.name ?? "");
|
|
162
|
+
const insertValue = s.type == "folder" ? addPathSeparator(rawInsertValue, shell) : rawInsertValue;
|
|
163
|
+
const partialDir = getPathDirname(partialToken?.token ?? "", shell);
|
|
164
|
+
const fullPath = partialToken?.isPath ? `${partialDir}${insertValue}` : insertValue;
|
|
165
|
+
return pathy ? { ...s, insertValue: escapePath(fullPath, shell), name: removePathSeparator(s.name) } : s;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const removeAcceptedSuggestions = (suggestions, acceptedTokens) => {
|
|
141
169
|
const seen = new Set(acceptedTokens.map((t) => t.token));
|
|
142
170
|
return suggestions.filter((s) => s.allNames.every((n) => !seen.has(n)));
|
|
143
171
|
};
|
|
172
|
+
const removeDuplicateSuggestion = (suggestions) => {
|
|
173
|
+
const seen = new Set();
|
|
174
|
+
return suggestions
|
|
175
|
+
.map((s) => {
|
|
176
|
+
if (seen.has(s.name))
|
|
177
|
+
return null;
|
|
178
|
+
seen.add(s.name);
|
|
179
|
+
return s;
|
|
180
|
+
})
|
|
181
|
+
.filter((s) => s != null);
|
|
182
|
+
};
|
|
144
183
|
const removeEmptySuggestion = (suggestions) => {
|
|
145
184
|
return suggestions.filter((s) => s.name.length > 0);
|
|
146
185
|
};
|
|
147
|
-
export const getSubcommandDrivenRecommendation = async (subcommand, persistentOptions,
|
|
186
|
+
export const getSubcommandDrivenRecommendation = async (subcommand, persistentOptions, partialToken, argsDepleted, argsFromSubcommand, acceptedTokens, cwd, shell) => {
|
|
187
|
+
log.debug({ msg: "suggestion point", subcommand, persistentOptions, partialToken, argsDepleted, argsFromSubcommand, acceptedTokens, cwd });
|
|
148
188
|
if (argsDepleted && argsFromSubcommand) {
|
|
149
189
|
return;
|
|
150
190
|
}
|
|
191
|
+
let partialCmd = partialToken?.token;
|
|
192
|
+
if (partialToken?.isPath) {
|
|
193
|
+
partialCmd = partialToken.isPathComplete ? "" : path.basename(partialCmd ?? "");
|
|
194
|
+
}
|
|
151
195
|
const suggestions = [];
|
|
152
196
|
const argLength = subcommand.args instanceof Array ? subcommand.args.length : subcommand.args ? 1 : 0;
|
|
153
197
|
const allOptions = persistentOptions.concat(subcommand.options ?? []);
|
|
@@ -157,28 +201,32 @@ export const getSubcommandDrivenRecommendation = async (subcommand, persistentOp
|
|
|
157
201
|
}
|
|
158
202
|
if (argLength != 0) {
|
|
159
203
|
const activeArg = subcommand.args instanceof Array ? subcommand.args[0] : subcommand.args;
|
|
160
|
-
suggestions.push(...(await generatorSuggestions(activeArg?.generators, acceptedTokens, activeArg?.filterStrategy, partialCmd)));
|
|
204
|
+
suggestions.push(...(await generatorSuggestions(activeArg?.generators, acceptedTokens, activeArg?.filterStrategy, partialCmd, cwd)));
|
|
161
205
|
suggestions.push(...suggestionSuggestions(activeArg?.suggestions, activeArg?.filterStrategy, partialCmd));
|
|
162
|
-
suggestions.push(...(await templateSuggestions(activeArg?.template, activeArg?.filterStrategy, partialCmd)));
|
|
206
|
+
suggestions.push(...(await templateSuggestions(activeArg?.template, activeArg?.filterStrategy, partialCmd, cwd)));
|
|
163
207
|
}
|
|
164
208
|
return {
|
|
165
|
-
suggestions: removeEmptySuggestion(
|
|
209
|
+
suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(adjustPathSuggestions(suggestions.sort((a, b) => b.priority - a.priority), partialToken, shell), acceptedTokens))),
|
|
166
210
|
};
|
|
167
211
|
};
|
|
168
|
-
export const getArgDrivenRecommendation = async (args, subcommand, persistentOptions,
|
|
212
|
+
export const getArgDrivenRecommendation = async (args, subcommand, persistentOptions, partialToken, acceptedTokens, variadicArgBound, cwd, shell) => {
|
|
213
|
+
let partialCmd = partialToken?.token;
|
|
214
|
+
if (partialToken?.isPath) {
|
|
215
|
+
partialCmd = partialToken.isPathComplete ? "" : path.basename(partialCmd ?? "");
|
|
216
|
+
}
|
|
169
217
|
const activeArg = args[0];
|
|
170
218
|
const allOptions = persistentOptions.concat(subcommand.options ?? []);
|
|
171
219
|
const suggestions = [
|
|
172
|
-
...(await generatorSuggestions(args[0].generators, acceptedTokens, activeArg?.filterStrategy, partialCmd)),
|
|
220
|
+
...(await generatorSuggestions(args[0].generators, acceptedTokens, activeArg?.filterStrategy, partialCmd, cwd)),
|
|
173
221
|
...suggestionSuggestions(args[0].suggestions, activeArg?.filterStrategy, partialCmd),
|
|
174
|
-
...(await templateSuggestions(args[0].template, activeArg?.filterStrategy, partialCmd)),
|
|
222
|
+
...(await templateSuggestions(args[0].template, activeArg?.filterStrategy, partialCmd, cwd)),
|
|
175
223
|
];
|
|
176
|
-
if (
|
|
224
|
+
if (activeArg.isOptional || (activeArg.isVariadic && variadicArgBound)) {
|
|
177
225
|
suggestions.push(...subcommandSuggestions(subcommand.subcommands, activeArg?.filterStrategy, partialCmd));
|
|
178
226
|
suggestions.push(...optionSuggestions(allOptions, acceptedTokens, activeArg?.filterStrategy, partialCmd));
|
|
179
227
|
}
|
|
180
228
|
return {
|
|
181
|
-
suggestions: removeEmptySuggestion(
|
|
229
|
+
suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(adjustPathSuggestions(suggestions.sort((a, b) => b.priority - a.priority), partialToken, shell), acceptedTokens))),
|
|
182
230
|
argumentDescription: activeArg.description ?? activeArg.name,
|
|
183
231
|
};
|
|
184
232
|
};
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import fsAsync from "fs/promises";
|
|
4
|
-
import
|
|
5
|
-
const filepathsTemplate = async () => {
|
|
6
|
-
const files = await fsAsync.readdir(
|
|
7
|
-
return files
|
|
3
|
+
import fsAsync from "node:fs/promises";
|
|
4
|
+
import log from "../utils/log.js";
|
|
5
|
+
const filepathsTemplate = async (cwd) => {
|
|
6
|
+
const files = await fsAsync.readdir(cwd, { withFileTypes: true });
|
|
7
|
+
return files
|
|
8
|
+
.filter((f) => f.isFile() || f.isDirectory())
|
|
9
|
+
.map((f) => ({ name: f.name, priority: 55, context: { templateType: "filepaths" }, type: f.isDirectory() ? "folder" : "file" }));
|
|
8
10
|
};
|
|
9
|
-
const foldersTemplate = async () => {
|
|
10
|
-
const files = await fsAsync.readdir(
|
|
11
|
-
return files
|
|
11
|
+
const foldersTemplate = async (cwd) => {
|
|
12
|
+
const files = await fsAsync.readdir(cwd, { withFileTypes: true });
|
|
13
|
+
return files
|
|
14
|
+
.filter((f) => f.isDirectory())
|
|
15
|
+
.map((f) => ({
|
|
16
|
+
name: f.name,
|
|
17
|
+
priority: 55,
|
|
18
|
+
context: { templateType: "folders" },
|
|
19
|
+
type: "folder",
|
|
20
|
+
}));
|
|
12
21
|
};
|
|
13
22
|
// TODO: implement history template
|
|
14
23
|
const historyTemplate = () => {
|
|
@@ -18,18 +27,24 @@ const historyTemplate = () => {
|
|
|
18
27
|
const helpTemplate = () => {
|
|
19
28
|
return [];
|
|
20
29
|
};
|
|
21
|
-
export const runTemplates = async (template) => {
|
|
30
|
+
export const runTemplates = async (template, cwd) => {
|
|
22
31
|
const templates = template instanceof Array ? template : [template];
|
|
23
32
|
return (await Promise.all(templates.map(async (t) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
try {
|
|
34
|
+
switch (t) {
|
|
35
|
+
case "filepaths":
|
|
36
|
+
return await filepathsTemplate(cwd);
|
|
37
|
+
case "folders":
|
|
38
|
+
return await foldersTemplate(cwd);
|
|
39
|
+
case "history":
|
|
40
|
+
return historyTemplate();
|
|
41
|
+
case "help":
|
|
42
|
+
return helpTemplate();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
log.debug({ msg: "template failed", e, template: t, cwd });
|
|
47
|
+
return [];
|
|
33
48
|
}
|
|
34
49
|
}))).flat();
|
|
35
50
|
};
|