@microsoft/inshellisense 0.0.1-rc.2 → 0.0.1-rc.20
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 +290 -0
- package/build/isterm/index.js +4 -0
- package/build/isterm/pty.js +372 -0
- package/build/runtime/alias.js +61 -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/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 +130 -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 +132 -0
- package/build/utils/log.js +39 -0
- package/build/utils/shell.js +316 -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 +125 -0
- package/shell/shellIntegration.fish +28 -0
- package/shell/shellIntegration.nu +36 -0
- package/shell/shellIntegration.ps1 +27 -0
- package/shell/shellIntegration.xsh +37 -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
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import log from "../utils/log.js";
|
|
4
|
+
import { gitBashPath, Shell } from "../utils/shell.js";
|
|
5
|
+
import { parseCommand } from "./parser.js";
|
|
6
|
+
import { buildExecuteShellCommand } from "./utils.js";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
const loadedAliases = {};
|
|
9
|
+
const platform = os.platform();
|
|
10
|
+
const executeShellCommand = await buildExecuteShellCommand(5000);
|
|
11
|
+
const loadBashAliases = async () => {
|
|
12
|
+
const shellTarget = platform == "win32" ? await gitBashPath() : Shell.Bash;
|
|
13
|
+
const { stdout, stderr, status } = await executeShellCommand({ command: shellTarget, args: ["-i", "-c", "alias"], cwd: process.cwd(), env: { ISTERM: "1" } });
|
|
14
|
+
if (status !== 0) {
|
|
15
|
+
log.debug({ msg: "failed to load bash aliases", stderr, status });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
return stdout
|
|
19
|
+
.trim()
|
|
20
|
+
.split("\n")
|
|
21
|
+
.forEach((line) => {
|
|
22
|
+
const [alias, ...commandSegments] = line.replace("alias ", "").replaceAll("'\\''", "'").split("=");
|
|
23
|
+
loadedAliases[alias] = parseCommand(commandSegments.join("=").slice(1, -1) + " ", Shell.Bash);
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
const loadZshAliases = async () => {
|
|
27
|
+
const { stdout, stderr, status } = await executeShellCommand({ command: Shell.Zsh, args: ["-i", "-c", "alias"], cwd: process.cwd(), env: { ISTERM: "1" } });
|
|
28
|
+
if (status !== 0) {
|
|
29
|
+
log.debug({ msg: "failed to load zsh aliases", stderr, status });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
return stdout
|
|
33
|
+
.trim()
|
|
34
|
+
.split("\n")
|
|
35
|
+
.forEach((line) => {
|
|
36
|
+
const [alias, ...commandSegments] = line.replaceAll("'\\''", "'").split("=");
|
|
37
|
+
loadedAliases[alias] = parseCommand(commandSegments.join("=").slice(1, -1) + " ", Shell.Zsh);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
export const loadAliases = async (shell) => {
|
|
41
|
+
switch (shell) {
|
|
42
|
+
case Shell.Bash:
|
|
43
|
+
await loadBashAliases();
|
|
44
|
+
break;
|
|
45
|
+
case Shell.Zsh:
|
|
46
|
+
await loadZshAliases();
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
return [];
|
|
50
|
+
};
|
|
51
|
+
export const getAliasNames = () => Object.keys(loadedAliases);
|
|
52
|
+
export const aliasExpand = (command) => {
|
|
53
|
+
if (!command.at(0)?.complete)
|
|
54
|
+
return command;
|
|
55
|
+
const alias = loadedAliases[command.at(0)?.token ?? ""];
|
|
56
|
+
if (alias) {
|
|
57
|
+
log.debug({ msg: "expanding alias", alias, command: command.slice(1) });
|
|
58
|
+
return [...alias, ...command.slice(1)];
|
|
59
|
+
}
|
|
60
|
+
return command;
|
|
61
|
+
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
+
import log from "../utils/log.js";
|
|
3
4
|
import { runTemplates } from "./template.js";
|
|
4
5
|
import { buildExecuteShellCommand } from "./utils.js";
|
|
5
|
-
const getGeneratorContext = () => {
|
|
6
|
+
const getGeneratorContext = (cwd) => {
|
|
6
7
|
return {
|
|
7
8
|
environmentVariables: Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] != null)),
|
|
8
|
-
currentWorkingDirectory:
|
|
9
|
+
currentWorkingDirectory: cwd,
|
|
9
10
|
currentProcess: "",
|
|
10
11
|
sshPrefix: "",
|
|
11
12
|
isDangerous: false,
|
|
@@ -13,30 +14,42 @@ const getGeneratorContext = () => {
|
|
|
13
14
|
};
|
|
14
15
|
};
|
|
15
16
|
// TODO: add support for caching, trigger, & getQueryTerm
|
|
16
|
-
export const runGenerator = async (generator, tokens) => {
|
|
17
|
-
|
|
18
|
-
const
|
|
17
|
+
export const runGenerator = async (generator, tokens, cwd) => {
|
|
18
|
+
// TODO: support trigger
|
|
19
|
+
const { script, postProcess, scriptTimeout, splitOn, custom, template, filterTemplateSuggestions } = generator;
|
|
20
|
+
const executeShellCommand = await buildExecuteShellCommand(scriptTimeout ?? 5000);
|
|
19
21
|
const suggestions = [];
|
|
20
22
|
try {
|
|
21
23
|
if (script) {
|
|
22
|
-
const
|
|
24
|
+
const shellInput = typeof script === "function" ? script(tokens) : script;
|
|
25
|
+
const scriptOutput = Array.isArray(shellInput)
|
|
26
|
+
? await executeShellCommand({ command: shellInput.at(0) ?? "", args: shellInput.slice(1), cwd })
|
|
27
|
+
: await executeShellCommand({ ...shellInput, cwd });
|
|
28
|
+
const scriptStdout = scriptOutput.stdout.trim();
|
|
23
29
|
if (postProcess) {
|
|
24
|
-
suggestions.push(...postProcess(
|
|
30
|
+
suggestions.push(...postProcess(scriptStdout, tokens));
|
|
25
31
|
}
|
|
26
32
|
else if (splitOn) {
|
|
27
|
-
suggestions.push(...
|
|
33
|
+
suggestions.push(...scriptStdout.split(splitOn).map((s) => ({ name: s })));
|
|
28
34
|
}
|
|
29
35
|
}
|
|
30
36
|
if (custom) {
|
|
31
|
-
suggestions.push(...(await custom(tokens, executeShellCommand, getGeneratorContext())));
|
|
37
|
+
suggestions.push(...(await custom(tokens, executeShellCommand, getGeneratorContext(cwd))));
|
|
32
38
|
}
|
|
33
39
|
if (template != null) {
|
|
34
|
-
|
|
40
|
+
const templateSuggestions = await runTemplates(template, cwd);
|
|
41
|
+
if (filterTemplateSuggestions) {
|
|
42
|
+
suggestions.push(...filterTemplateSuggestions(templateSuggestions));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
suggestions.push(...templateSuggestions);
|
|
46
|
+
}
|
|
35
47
|
}
|
|
36
48
|
return suggestions;
|
|
37
49
|
}
|
|
38
50
|
catch (e) {
|
|
39
|
-
|
|
51
|
+
const err = typeof e === "string" ? e : e instanceof Error ? e.message : e;
|
|
52
|
+
log.debug({ msg: "generator failed", err, script, splitOn, template });
|
|
40
53
|
}
|
|
41
54
|
return suggestions;
|
|
42
55
|
};
|
package/build/runtime/parser.js
CHANGED
|
@@ -1,19 +1,48 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
|
|
3
|
+
import wcwidth from "wcwidth";
|
|
4
|
+
import { getShellWhitespaceEscapeChar } from "./utils.js";
|
|
5
|
+
const cmdDelim = /(\|\|)|(&&)|(;)|(\|)/;
|
|
4
6
|
const spaceRegex = /\s/;
|
|
5
|
-
export const parseCommand = (command) => {
|
|
7
|
+
export const parseCommand = (command, shell) => {
|
|
6
8
|
const lastCommand = command.split(cmdDelim).at(-1)?.trimStart();
|
|
7
|
-
|
|
9
|
+
const tokens = lastCommand ? lex(lastCommand, shell) : [];
|
|
10
|
+
return sanitizeTokens(tokens, shell);
|
|
8
11
|
};
|
|
9
|
-
const
|
|
12
|
+
const sanitizeTokens = (cmdTokens, shell) => unwrapQuotedTokens(unescapeSpaceTokens(cmdTokens, shell), shell);
|
|
13
|
+
// remove escapes around spaces
|
|
14
|
+
const unescapeSpaceTokens = (cmdTokens, shell) => {
|
|
15
|
+
const escapeChar = getShellWhitespaceEscapeChar(shell);
|
|
16
|
+
return cmdTokens.map((cmdToken) => {
|
|
17
|
+
const { token, isQuoted } = cmdToken;
|
|
18
|
+
if (!isQuoted && token.includes(`${escapeChar} `)) {
|
|
19
|
+
return { ...cmdToken, token: token.replaceAll(`${escapeChar} `, " ") };
|
|
20
|
+
}
|
|
21
|
+
return cmdToken;
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
// need to unwrap tokens that are quoted with content after the quotes like `"hello"world`
|
|
25
|
+
const unwrapQuotedTokens = (cmdTokens, shell) => {
|
|
26
|
+
const escapeChar = getShellWhitespaceEscapeChar(shell);
|
|
27
|
+
return cmdTokens.map((cmdToken) => {
|
|
28
|
+
const { token, isQuoteContinued } = cmdToken;
|
|
29
|
+
if (isQuoteContinued) {
|
|
30
|
+
const quoteChar = token[0];
|
|
31
|
+
const unquotedToken = token.replaceAll(`${escapeChar}${quoteChar}`, "\u001B").replaceAll(quoteChar, "").replaceAll("\u001B", quoteChar);
|
|
32
|
+
return { ...cmdToken, token: unquotedToken };
|
|
33
|
+
}
|
|
34
|
+
return cmdToken;
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
const lex = (command, shell) => {
|
|
10
38
|
const tokens = [];
|
|
11
|
-
|
|
39
|
+
const escapeChar = getShellWhitespaceEscapeChar(shell);
|
|
40
|
+
let [readingQuotedString, readingQuoteContinuedString, readingFlag, readingCmd] = [false, false, false, false];
|
|
12
41
|
let readingIdx = 0;
|
|
13
42
|
let readingQuoteChar = "";
|
|
14
43
|
[...command].forEach((char, idx) => {
|
|
15
|
-
const reading = readingQuotedString || readingFlag || readingCmd;
|
|
16
|
-
if (!reading && (char === `'` || char === `"`)) {
|
|
44
|
+
const reading = readingQuotedString || readingQuoteContinuedString || readingFlag || readingCmd;
|
|
45
|
+
if (!reading && (char === `'` || char === `"` || char == "`")) {
|
|
17
46
|
[readingQuotedString, readingIdx, readingQuoteChar] = [true, idx, char];
|
|
18
47
|
return;
|
|
19
48
|
}
|
|
@@ -25,39 +54,80 @@ const lex = (command) => {
|
|
|
25
54
|
[readingCmd, readingIdx] = [true, idx];
|
|
26
55
|
return;
|
|
27
56
|
}
|
|
28
|
-
if (readingQuotedString && char === readingQuoteChar && command.at(idx - 1) !== "
|
|
57
|
+
if (readingQuotedString && char === readingQuoteChar && command.at(idx - 1) !== escapeChar && !spaceRegex.test(command.at(idx + 1) ?? " ")) {
|
|
58
|
+
readingQuotedString = false;
|
|
59
|
+
readingQuoteContinuedString = true;
|
|
60
|
+
}
|
|
61
|
+
else if (readingQuotedString && char === readingQuoteChar && command.at(idx - 1) !== escapeChar) {
|
|
29
62
|
readingQuotedString = false;
|
|
30
63
|
const complete = idx + 1 < command.length && spaceRegex.test(command[idx + 1]);
|
|
31
64
|
tokens.push({
|
|
32
|
-
token: command.slice(readingIdx
|
|
65
|
+
token: command.slice(readingIdx + 1, idx),
|
|
66
|
+
tokenLength: wcwidth(command.slice(readingIdx + 1, idx)) + 2,
|
|
33
67
|
complete,
|
|
34
68
|
isOption: false,
|
|
69
|
+
isQuoted: true,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else if (readingQuoteContinuedString && spaceRegex.test(char) && command.at(idx - 1) !== escapeChar) {
|
|
73
|
+
readingQuoteContinuedString = false;
|
|
74
|
+
tokens.push({
|
|
75
|
+
token: command.slice(readingIdx, idx),
|
|
76
|
+
tokenLength: wcwidth(command.slice(readingIdx, idx)),
|
|
77
|
+
complete: true,
|
|
78
|
+
isOption: false,
|
|
79
|
+
isQuoted: true,
|
|
80
|
+
isQuoteContinued: true,
|
|
35
81
|
});
|
|
36
82
|
}
|
|
37
83
|
else if ((readingFlag && spaceRegex.test(char)) || char === "=") {
|
|
38
84
|
readingFlag = false;
|
|
39
85
|
tokens.push({
|
|
40
86
|
token: command.slice(readingIdx, idx),
|
|
87
|
+
tokenLength: wcwidth(command.slice(readingIdx, idx)),
|
|
41
88
|
complete: true,
|
|
42
89
|
isOption: true,
|
|
43
90
|
});
|
|
44
91
|
}
|
|
45
|
-
else if (readingCmd && spaceRegex.test(char)) {
|
|
92
|
+
else if (readingCmd && spaceRegex.test(char) && command.at(idx - 1) !== escapeChar) {
|
|
46
93
|
readingCmd = false;
|
|
47
94
|
tokens.push({
|
|
48
95
|
token: command.slice(readingIdx, idx),
|
|
96
|
+
tokenLength: wcwidth(command.slice(readingIdx, idx)),
|
|
49
97
|
complete: true,
|
|
50
98
|
isOption: false,
|
|
51
99
|
});
|
|
52
100
|
}
|
|
53
101
|
});
|
|
54
|
-
const reading = readingQuotedString || readingFlag || readingCmd;
|
|
102
|
+
const reading = readingQuotedString || readingQuoteContinuedString || readingFlag || readingCmd;
|
|
55
103
|
if (reading) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
if (readingQuotedString) {
|
|
105
|
+
tokens.push({
|
|
106
|
+
token: command.slice(readingIdx + 1),
|
|
107
|
+
tokenLength: wcwidth(command.slice(readingIdx + 1)) + 1,
|
|
108
|
+
complete: false,
|
|
109
|
+
isOption: false,
|
|
110
|
+
isQuoted: true,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else if (readingQuoteContinuedString) {
|
|
114
|
+
tokens.push({
|
|
115
|
+
token: command.slice(readingIdx),
|
|
116
|
+
tokenLength: wcwidth(command.slice(readingIdx)),
|
|
117
|
+
complete: false,
|
|
118
|
+
isOption: false,
|
|
119
|
+
isQuoted: true,
|
|
120
|
+
isQuoteContinued: true,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
tokens.push({
|
|
125
|
+
token: command.slice(readingIdx),
|
|
126
|
+
tokenLength: wcwidth(command.slice(readingIdx)),
|
|
127
|
+
complete: false,
|
|
128
|
+
isOption: readingFlag,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
61
131
|
}
|
|
62
132
|
return tokens;
|
|
63
133
|
};
|
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
|
};
|