@microsoft/inshellisense 0.0.1-rc.4 → 0.0.1-rc.6

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.
Files changed (43) hide show
  1. package/README.md +23 -7
  2. package/build/commands/complete.js +15 -0
  3. package/build/commands/root.js +20 -29
  4. package/build/commands/uninstall.js +1 -1
  5. package/build/index.js +5 -6
  6. package/build/isterm/commandManager.js +217 -0
  7. package/build/isterm/index.js +4 -0
  8. package/build/isterm/pty.js +248 -0
  9. package/build/runtime/generator.js +21 -10
  10. package/build/runtime/parser.js +2 -2
  11. package/build/runtime/runtime.js +37 -23
  12. package/build/runtime/suggestion.js +34 -14
  13. package/build/runtime/template.js +24 -18
  14. package/build/runtime/utils.js +42 -12
  15. package/build/ui/input.js +7 -53
  16. package/build/ui/suggestionManager.js +139 -0
  17. package/build/ui/ui-root.js +120 -64
  18. package/build/ui/ui-uninstall.js +1 -3
  19. package/build/ui/utils.js +41 -0
  20. package/build/utils/ansi.js +56 -0
  21. package/build/utils/config.js +68 -0
  22. package/build/utils/log.js +30 -0
  23. package/build/utils/shell.js +84 -1
  24. package/package.json +12 -5
  25. package/shell/bash-preexec.sh +380 -0
  26. package/shell/shellIntegration-env.zsh +9 -0
  27. package/shell/shellIntegration-login.zsh +4 -0
  28. package/shell/shellIntegration-profile.zsh +4 -0
  29. package/shell/shellIntegration-rc.zsh +52 -0
  30. package/shell/shellIntegration.bash +65 -0
  31. package/shell/shellIntegration.fish +14 -0
  32. package/shell/shellIntegration.ps1 +17 -0
  33. package/build/commands/bind.js +0 -12
  34. package/build/ui/suggestions.js +0 -84
  35. package/build/ui/ui-bind.js +0 -69
  36. package/build/ui/ui-init.js +0 -44
  37. package/build/utils/bindings.js +0 -236
  38. package/build/utils/cache.js +0 -21
  39. package/shell/key-bindings-powershell.ps1 +0 -27
  40. package/shell/key-bindings-pwsh.ps1 +0 -27
  41. package/shell/key-bindings.bash +0 -7
  42. package/shell/key-bindings.fish +0 -8
  43. package/shell/key-bindings.zsh +0 -10
@@ -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: process.cwd(),
9
+ currentWorkingDirectory: cwd,
9
10
  currentProcess: "",
10
11
  sshPrefix: "",
11
12
  isDangerous: false,
@@ -13,30 +14,40 @@ const getGeneratorContext = () => {
13
14
  };
14
15
  };
15
16
  // TODO: add support for caching, trigger, & getQueryTerm
16
- export const runGenerator = async (generator, tokens) => {
17
- const { script, postProcess, scriptTimeout, splitOn, custom, template } = generator;
17
+ export const runGenerator = async (generator, tokens, cwd) => {
18
+ // TODO: support trigger
19
+ const { script, postProcess, scriptTimeout, splitOn, custom, template, filterTemplateSuggestions } = generator;
18
20
  const executeShellCommand = buildExecuteShellCommand(scriptTimeout ?? 5000);
19
21
  const suggestions = [];
20
22
  try {
21
23
  if (script) {
22
- const scriptOutput = typeof script === "function" ? script(tokens) : script != null ? await executeShellCommand(script) : "";
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) })
27
+ : await executeShellCommand(shellInput);
23
28
  if (postProcess) {
24
- suggestions.push(...postProcess(scriptOutput, tokens));
29
+ suggestions.push(...postProcess(scriptOutput.stdout, tokens));
25
30
  }
26
31
  else if (splitOn) {
27
- suggestions.push(...scriptOutput.split(splitOn).map((s) => ({ name: s })));
32
+ suggestions.push(...scriptOutput.stdout.split(splitOn).map((s) => ({ name: s })));
28
33
  }
29
34
  }
30
35
  if (custom) {
31
- suggestions.push(...(await custom(tokens, executeShellCommand, getGeneratorContext())));
36
+ suggestions.push(...(await custom(tokens, executeShellCommand, getGeneratorContext(cwd))));
32
37
  }
33
38
  if (template != null) {
34
- suggestions.push(...(await runTemplates(template)));
39
+ const templateSuggestions = await runTemplates(template, cwd);
40
+ if (filterTemplateSuggestions) {
41
+ suggestions.push(...filterTemplateSuggestions(templateSuggestions));
42
+ }
43
+ else {
44
+ suggestions.push(...templateSuggestions);
45
+ }
35
46
  }
36
47
  return suggestions;
37
48
  }
38
49
  catch (e) {
39
- /* empty */
50
+ log.debug({ msg: "generator failed", e, script, splitOn, template });
40
51
  }
41
52
  return suggestions;
42
53
  };
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- const cmdDelim = /(\|\|)|(&&)|(;)/;
3
+ const cmdDelim = /(\|\|)|(&&)|(;)|(\|)/;
4
4
  const spaceRegex = /\s/;
5
5
  export const parseCommand = (command) => {
6
6
  const lastCommand = command.split(cmdDelim).at(-1)?.trimStart();
@@ -56,7 +56,7 @@ const lex = (command) => {
56
56
  tokens.push({
57
57
  token: command.slice(readingIdx),
58
58
  complete: false,
59
- isOption: false,
59
+ isOption: readingFlag,
60
60
  });
61
61
  }
62
62
  return tokens;
@@ -4,9 +4,10 @@ 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";
10
11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- recursive type, setting as any
11
12
  const specSet = {};
12
13
  speclist.forEach((s) => {
@@ -46,7 +47,7 @@ const lazyLoadSpec = async (key) => {
46
47
  const lazyLoadSpecLocation = async (location) => {
47
48
  return; //TODO: implement spec location loading
48
49
  };
49
- export const getSuggestions = async (cmd) => {
50
+ export const getSuggestions = async (cmd, cwd, shell) => {
50
51
  const activeCmd = parseCommand(cmd);
51
52
  const rootToken = activeCmd.at(0);
52
53
  if (activeCmd.length === 0 || !rootToken?.complete) {
@@ -58,11 +59,19 @@ export const getSuggestions = async (cmd) => {
58
59
  const subcommand = getSubcommand(spec);
59
60
  if (subcommand == null)
60
61
  return;
61
- const result = await runSubcommand(activeCmd.slice(1), subcommand);
62
+ const lastCommand = activeCmd.at(-1);
63
+ const { cwd: resolvedCwd, pathy, complete: pathyComplete } = await resolveCwd(lastCommand, cwd, shell);
64
+ if (pathy && lastCommand) {
65
+ lastCommand.isPath = true;
66
+ lastCommand.isPathComplete = pathyComplete;
67
+ }
68
+ const result = await runSubcommand(activeCmd.slice(1), subcommand, resolvedCwd);
62
69
  if (result == null)
63
70
  return;
64
- const lastCommand = activeCmd.at(-1);
65
- const charactersToDrop = lastCommand?.complete ? 0 : lastCommand?.token.length ?? 0;
71
+ let charactersToDrop = lastCommand?.complete ? 0 : lastCommand?.token.length ?? 0;
72
+ if (pathy) {
73
+ charactersToDrop = pathyComplete ? 0 : path.basename(lastCommand?.token ?? "").length;
74
+ }
66
75
  return { ...result, charactersToDrop };
67
76
  };
68
77
  const getPersistentOptions = (persistentOptions, options) => {
@@ -142,7 +151,7 @@ const getPersistentTokens = (tokens) => {
142
151
  const getArgs = (args) => {
143
152
  return args instanceof Array ? args : args != null ? [args] : [];
144
153
  };
145
- const runOption = async (tokens, option, subcommand, persistentOptions, acceptedTokens) => {
154
+ const runOption = async (tokens, option, subcommand, cwd, persistentOptions, acceptedTokens) => {
146
155
  if (tokens.length === 0) {
147
156
  throw new Error("invalid state reached, option expected but no tokens found");
148
157
  }
@@ -150,37 +159,37 @@ const runOption = async (tokens, option, subcommand, persistentOptions, accepted
150
159
  const isPersistent = persistentOptions.some((o) => (typeof o.name === "string" ? o.name === activeToken.token : o.name.includes(activeToken.token)));
151
160
  if ((option.args instanceof Array && option.args.length > 0) || option.args != null) {
152
161
  const args = option.args instanceof Array ? option.args : [option.args];
153
- return runArg(tokens.slice(1), args, subcommand, persistentOptions, acceptedTokens.concat(activeToken), true, false);
162
+ return runArg(tokens.slice(1), args, subcommand, cwd, persistentOptions, acceptedTokens.concat(activeToken), true, false);
154
163
  }
155
- return runSubcommand(tokens.slice(1), subcommand, persistentOptions, acceptedTokens.concat({ ...activeToken, isPersistent }));
164
+ return runSubcommand(tokens.slice(1), subcommand, cwd, persistentOptions, acceptedTokens.concat({ ...activeToken, isPersistent }));
156
165
  };
157
- const runArg = async (tokens, args, subcommand, persistentOptions, acceptedTokens, fromOption, fromVariadic) => {
166
+ const runArg = async (tokens, args, subcommand, cwd, persistentOptions, acceptedTokens, fromOption, fromVariadic) => {
158
167
  if (args.length === 0) {
159
- return runSubcommand(tokens, subcommand, persistentOptions, acceptedTokens, true, !fromOption);
168
+ return runSubcommand(tokens, subcommand, cwd, persistentOptions, acceptedTokens, true, !fromOption);
160
169
  }
161
170
  else if (tokens.length === 0) {
162
- return await getArgDrivenRecommendation(args, subcommand, persistentOptions, undefined, acceptedTokens, fromVariadic);
171
+ return await getArgDrivenRecommendation(args, subcommand, persistentOptions, undefined, acceptedTokens, fromVariadic, cwd);
163
172
  }
164
173
  else if (!tokens.at(0)?.complete) {
165
- return await getArgDrivenRecommendation(args, subcommand, persistentOptions, tokens[0].token, acceptedTokens, fromVariadic);
174
+ return await getArgDrivenRecommendation(args, subcommand, persistentOptions, tokens[0], acceptedTokens, fromVariadic, cwd);
166
175
  }
167
176
  const activeToken = tokens[0];
168
177
  if (args.every((a) => a.isOptional)) {
169
178
  if (activeToken.isOption) {
170
179
  const option = getOption(activeToken, persistentOptions.concat(subcommand.options ?? []));
171
180
  if (option != null) {
172
- return runOption(tokens, option, subcommand, persistentOptions, acceptedTokens);
181
+ return runOption(tokens, option, subcommand, cwd, persistentOptions, acceptedTokens);
173
182
  }
174
183
  return;
175
184
  }
176
185
  const nextSubcommand = await genSubcommand(activeToken.token, subcommand);
177
186
  if (nextSubcommand != null) {
178
- return runSubcommand(tokens.slice(1), nextSubcommand, persistentOptions, getPersistentTokens(acceptedTokens.concat(activeToken)));
187
+ return runSubcommand(tokens.slice(1), nextSubcommand, cwd, persistentOptions, getPersistentTokens(acceptedTokens.concat(activeToken)));
179
188
  }
180
189
  }
181
190
  const activeArg = args[0];
182
191
  if (activeArg.isVariadic) {
183
- return runArg(tokens.slice(1), args, subcommand, persistentOptions, acceptedTokens.concat(activeToken), fromOption, true);
192
+ return runArg(tokens.slice(1), args, subcommand, cwd, persistentOptions, acceptedTokens.concat(activeToken), fromOption, true);
184
193
  }
185
194
  else if (activeArg.isCommand) {
186
195
  if (tokens.length <= 0) {
@@ -192,16 +201,16 @@ const runArg = async (tokens, args, subcommand, persistentOptions, acceptedToken
192
201
  const subcommand = getSubcommand(spec);
193
202
  if (subcommand == null)
194
203
  return;
195
- return runSubcommand(tokens.slice(1), subcommand);
204
+ return runSubcommand(tokens.slice(1), subcommand, cwd);
196
205
  }
197
- return runArg(tokens.slice(1), args.slice(1), subcommand, persistentOptions, acceptedTokens.concat(activeToken), fromOption, false);
206
+ return runArg(tokens.slice(1), args.slice(1), subcommand, cwd, persistentOptions, acceptedTokens.concat(activeToken), fromOption, false);
198
207
  };
199
- const runSubcommand = async (tokens, subcommand, persistentOptions = [], acceptedTokens = [], argsDepleted = false, argsUsed = false) => {
208
+ const runSubcommand = async (tokens, subcommand, cwd, persistentOptions = [], acceptedTokens = [], argsDepleted = false, argsUsed = false) => {
200
209
  if (tokens.length === 0) {
201
- return getSubcommandDrivenRecommendation(subcommand, persistentOptions, undefined, argsDepleted, argsUsed, acceptedTokens);
210
+ return getSubcommandDrivenRecommendation(subcommand, persistentOptions, undefined, argsDepleted, argsUsed, acceptedTokens, cwd);
202
211
  }
203
212
  else if (!tokens.at(0)?.complete) {
204
- return getSubcommandDrivenRecommendation(subcommand, persistentOptions, tokens[0].token, argsDepleted, argsUsed, acceptedTokens);
213
+ return getSubcommandDrivenRecommendation(subcommand, persistentOptions, tokens[0], argsDepleted, argsUsed, acceptedTokens, cwd);
205
214
  }
206
215
  const activeToken = tokens[0];
207
216
  const activeArgsLength = subcommand.args instanceof Array ? subcommand.args.length : 1;
@@ -209,16 +218,21 @@ const runSubcommand = async (tokens, subcommand, persistentOptions = [], accepte
209
218
  if (activeToken.isOption) {
210
219
  const option = getOption(activeToken, allOptions);
211
220
  if (option != null) {
212
- return runOption(tokens, option, subcommand, persistentOptions, acceptedTokens);
221
+ return runOption(tokens, option, subcommand, cwd, persistentOptions, acceptedTokens);
213
222
  }
214
223
  return;
215
224
  }
216
225
  const nextSubcommand = await genSubcommand(activeToken.token, subcommand);
217
226
  if (nextSubcommand != null) {
218
- return runSubcommand(tokens.slice(1), nextSubcommand, getPersistentOptions(persistentOptions, subcommand.options), getPersistentTokens(acceptedTokens.concat(activeToken)));
227
+ return runSubcommand(tokens.slice(1), nextSubcommand, cwd, getPersistentOptions(persistentOptions, subcommand.options), getPersistentTokens(acceptedTokens.concat(activeToken)));
219
228
  }
220
229
  if (activeArgsLength <= 0) {
221
230
  return; // not subcommand or option & no args exist
222
231
  }
223
- return runArg(tokens, getArgs(subcommand.args), subcommand, allOptions, acceptedTokens, false, false);
232
+ const args = getArgs(subcommand.args);
233
+ if (args.length != 0) {
234
+ return runArg(tokens, args, subcommand, cwd, allOptions, acceptedTokens, false, false);
235
+ }
236
+ // if the subcommand has no args specified, fallback to the subcommand and ignore this item
237
+ return runSubcommand(tokens.slice(1), subcommand, cwd, persistentOptions, acceptedTokens.concat(activeToken));
224
238
  };
@@ -1,5 +1,6 @@
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
  var SuggestionIcons;
@@ -116,14 +117,14 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
116
117
  .filter((s) => s != null);
117
118
  }
118
119
  }
119
- const generatorSuggestions = async (generator, acceptedTokens, filterStrategy, partialCmd) => {
120
+ const generatorSuggestions = async (generator, acceptedTokens, filterStrategy, partialCmd, cwd) => {
120
121
  const generators = generator instanceof Array ? generator : generator ? [generator] : [];
121
122
  const tokens = acceptedTokens.map((t) => t.token);
122
- const suggestions = (await Promise.all(generators.map((gen) => runGenerator(gen, tokens)))).flat();
123
+ const suggestions = (await Promise.all(generators.map((gen) => runGenerator(gen, tokens, cwd)))).flat();
123
124
  return filter(suggestions, filterStrategy, partialCmd, undefined);
124
125
  };
125
- const templateSuggestions = async (templates, filterStrategy, partialCmd) => {
126
- return filter(await runTemplates(templates ?? []), filterStrategy, partialCmd, undefined);
126
+ const templateSuggestions = async (templates, filterStrategy, partialCmd, cwd) => {
127
+ return filter(await runTemplates(templates ?? [], cwd), filterStrategy, partialCmd, undefined);
127
128
  };
128
129
  const suggestionSuggestions = (suggestions, filterStrategy, partialCmd) => {
129
130
  const cleanedSuggestions = suggestions?.map((s) => (typeof s === "string" ? { name: s } : s)) ?? [];
@@ -137,17 +138,32 @@ const optionSuggestions = (options, acceptedTokens, filterStrategy, partialCmd)
137
138
  const validOptions = options?.filter((o) => o.exclusiveOn?.every((exclusiveOption) => !usedOptions.has(exclusiveOption)) ?? true);
138
139
  return filter(validOptions ?? [], filterStrategy, partialCmd, "option");
139
140
  };
140
- const removeDuplicateSuggestions = (suggestions, acceptedTokens) => {
141
+ const removeAcceptedSuggestions = (suggestions, acceptedTokens) => {
141
142
  const seen = new Set(acceptedTokens.map((t) => t.token));
142
143
  return suggestions.filter((s) => s.allNames.every((n) => !seen.has(n)));
143
144
  };
145
+ const removeDuplicateSuggestion = (suggestions) => {
146
+ const seen = new Set();
147
+ return suggestions
148
+ .map((s) => {
149
+ if (seen.has(s.name))
150
+ return null;
151
+ seen.add(s.name);
152
+ return s;
153
+ })
154
+ .filter((s) => s != null);
155
+ };
144
156
  const removeEmptySuggestion = (suggestions) => {
145
157
  return suggestions.filter((s) => s.name.length > 0);
146
158
  };
147
- export const getSubcommandDrivenRecommendation = async (subcommand, persistentOptions, partialCmd, argsDepleted, argsFromSubcommand, acceptedTokens) => {
159
+ export const getSubcommandDrivenRecommendation = async (subcommand, persistentOptions, partialToken, argsDepleted, argsFromSubcommand, acceptedTokens, cwd) => {
148
160
  if (argsDepleted && argsFromSubcommand) {
149
161
  return;
150
162
  }
163
+ let partialCmd = partialToken?.token;
164
+ if (partialToken?.isPath) {
165
+ partialCmd = partialToken.isPathComplete ? "" : path.basename(partialCmd ?? "");
166
+ }
151
167
  const suggestions = [];
152
168
  const argLength = subcommand.args instanceof Array ? subcommand.args.length : subcommand.args ? 1 : 0;
153
169
  const allOptions = persistentOptions.concat(subcommand.options ?? []);
@@ -157,28 +173,32 @@ export const getSubcommandDrivenRecommendation = async (subcommand, persistentOp
157
173
  }
158
174
  if (argLength != 0) {
159
175
  const activeArg = subcommand.args instanceof Array ? subcommand.args[0] : subcommand.args;
160
- suggestions.push(...(await generatorSuggestions(activeArg?.generators, acceptedTokens, activeArg?.filterStrategy, partialCmd)));
176
+ suggestions.push(...(await generatorSuggestions(activeArg?.generators, acceptedTokens, activeArg?.filterStrategy, partialCmd, cwd)));
161
177
  suggestions.push(...suggestionSuggestions(activeArg?.suggestions, activeArg?.filterStrategy, partialCmd));
162
- suggestions.push(...(await templateSuggestions(activeArg?.template, activeArg?.filterStrategy, partialCmd)));
178
+ suggestions.push(...(await templateSuggestions(activeArg?.template, activeArg?.filterStrategy, partialCmd, cwd)));
163
179
  }
164
180
  return {
165
- suggestions: removeEmptySuggestion(removeDuplicateSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens)),
181
+ suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens))),
166
182
  };
167
183
  };
168
- export const getArgDrivenRecommendation = async (args, subcommand, persistentOptions, partialCmd, acceptedTokens, variadicArgBound) => {
184
+ export const getArgDrivenRecommendation = async (args, subcommand, persistentOptions, partialToken, acceptedTokens, variadicArgBound, cwd) => {
185
+ let partialCmd = partialToken?.token;
186
+ if (partialToken?.isPath) {
187
+ partialCmd = partialToken.isPathComplete ? "" : path.basename(partialCmd ?? "");
188
+ }
169
189
  const activeArg = args[0];
170
190
  const allOptions = persistentOptions.concat(subcommand.options ?? []);
171
191
  const suggestions = [
172
- ...(await generatorSuggestions(args[0].generators, acceptedTokens, activeArg?.filterStrategy, partialCmd)),
192
+ ...(await generatorSuggestions(args[0].generators, acceptedTokens, activeArg?.filterStrategy, partialCmd, cwd)),
173
193
  ...suggestionSuggestions(args[0].suggestions, activeArg?.filterStrategy, partialCmd),
174
- ...(await templateSuggestions(args[0].template, activeArg?.filterStrategy, partialCmd)),
194
+ ...(await templateSuggestions(args[0].template, activeArg?.filterStrategy, partialCmd, cwd)),
175
195
  ];
176
- if ((activeArg.isOptional && !activeArg.isVariadic) || (activeArg.isVariadic && activeArg.isOptional && !variadicArgBound)) {
196
+ if (activeArg.isOptional || (activeArg.isVariadic && variadicArgBound)) {
177
197
  suggestions.push(...subcommandSuggestions(subcommand.subcommands, activeArg?.filterStrategy, partialCmd));
178
198
  suggestions.push(...optionSuggestions(allOptions, acceptedTokens, activeArg?.filterStrategy, partialCmd));
179
199
  }
180
200
  return {
181
- suggestions: removeEmptySuggestion(removeDuplicateSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens)),
201
+ suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens))),
182
202
  argumentDescription: activeArg.description ?? activeArg.name,
183
203
  };
184
204
  };
@@ -1,14 +1,14 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- import fsAsync from "fs/promises";
4
- import process from "node:process";
5
- const filepathsTemplate = async () => {
6
- const files = await fsAsync.readdir(process.cwd(), { withFileTypes: true });
7
- return files.filter((f) => f.isFile() || f.isDirectory()).map((f) => ({ name: f.name, priority: 90 }));
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.filter((f) => f.isFile() || f.isDirectory()).map((f) => ({ name: f.name, priority: 90, context: { templateType: "filepaths" } }));
8
8
  };
9
- const foldersTemplate = async () => {
10
- const files = await fsAsync.readdir(process.cwd(), { withFileTypes: true });
11
- return files.filter((f) => f.isDirectory()).map((f) => ({ name: f.name, priority: 90 }));
9
+ const foldersTemplate = async (cwd) => {
10
+ const files = await fsAsync.readdir(cwd, { withFileTypes: true });
11
+ return files.filter((f) => f.isDirectory()).map((f) => ({ name: f.name, priority: 90, context: { templateType: "folders" } }));
12
12
  };
13
13
  // TODO: implement history template
14
14
  const historyTemplate = () => {
@@ -18,18 +18,24 @@ const historyTemplate = () => {
18
18
  const helpTemplate = () => {
19
19
  return [];
20
20
  };
21
- export const runTemplates = async (template) => {
21
+ export const runTemplates = async (template, cwd) => {
22
22
  const templates = template instanceof Array ? template : [template];
23
23
  return (await Promise.all(templates.map(async (t) => {
24
- switch (t) {
25
- case "filepaths":
26
- return await filepathsTemplate();
27
- case "folders":
28
- return await foldersTemplate();
29
- case "history":
30
- return historyTemplate();
31
- case "help":
32
- return helpTemplate();
24
+ try {
25
+ switch (t) {
26
+ case "filepaths":
27
+ return await filepathsTemplate(cwd);
28
+ case "folders":
29
+ return await foldersTemplate(cwd);
30
+ case "history":
31
+ return historyTemplate();
32
+ case "help":
33
+ return helpTemplate();
34
+ }
35
+ }
36
+ catch (e) {
37
+ log.debug({ msg: "template failed", e, template: t, cwd });
38
+ return [];
33
39
  }
34
40
  }))).flat();
35
41
  };
@@ -1,22 +1,52 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- import { exec, spawn } from "node:child_process";
4
- export const buildExecuteShellCommand = (timeout) =>
5
- // eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use cwd in the future
6
- async (command, cwd) => {
7
- return new Promise((resolve) => {
8
- exec(command, { timeout }, (_, stdout, stderr) => {
9
- resolve(stdout || stderr);
10
- });
3
+ import path from "node:path";
4
+ import { spawn } from "node:child_process";
5
+ import fsAsync from "node:fs/promises";
6
+ import { Shell } from "../utils/shell.js";
7
+ import log from "../utils/log.js";
8
+ export const buildExecuteShellCommand = (timeout) => async ({ command, env, args, cwd }) => {
9
+ const child = spawn(command, args, { cwd, env });
10
+ setTimeout(() => child.kill("SIGKILL"), timeout);
11
+ let stdout = "";
12
+ let stderr = "";
13
+ child.stdout.on("data", (data) => (stdout += data));
14
+ child.stderr.on("data", (data) => (stderr += data));
15
+ child.on("error", (err) => {
16
+ log.debug({ msg: "shell command failed", e: err.message });
11
17
  });
12
- };
13
- export const executeShellCommandTTY = async (shell, command) => {
14
- const child = spawn(shell, ["-c", command.trim()], { stdio: "inherit" });
15
18
  return new Promise((resolve) => {
16
19
  child.on("close", (code) => {
17
20
  resolve({
18
- code,
21
+ status: code ?? 0,
22
+ stderr,
23
+ stdout,
19
24
  });
20
25
  });
21
26
  });
22
27
  };
28
+ export const resolveCwd = async (cmdToken, cwd, shell) => {
29
+ if (cmdToken == null)
30
+ return { cwd, pathy: false, complete: false };
31
+ const { token } = cmdToken;
32
+ const sep = shell == Shell.Bash ? "/" : path.sep;
33
+ if (!token.includes(sep))
34
+ return { cwd, pathy: false, complete: false };
35
+ const resolvedCwd = path.isAbsolute(token) ? token : path.join(cwd, token);
36
+ try {
37
+ await fsAsync.access(resolvedCwd, fsAsync.constants.R_OK);
38
+ return { cwd: resolvedCwd, pathy: true, complete: token.endsWith(sep) };
39
+ }
40
+ catch {
41
+ // fallback to the parent folder if possible
42
+ const baselessCwd = resolvedCwd.substring(0, resolvedCwd.length - path.basename(resolvedCwd).length);
43
+ try {
44
+ await fsAsync.access(baselessCwd, fsAsync.constants.R_OK);
45
+ return { cwd: baselessCwd, pathy: true, complete: token.endsWith(sep) };
46
+ }
47
+ catch {
48
+ /*empty*/
49
+ }
50
+ return { cwd, pathy: false, complete: false };
51
+ }
52
+ };
package/build/ui/input.js CHANGED
@@ -1,55 +1,9 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- import React, { useState, useEffect } from "react";
4
- import { useInput, Text } from "ink";
5
- import chalk from "chalk";
6
- const BlinkSpeed = 530;
7
- const CursorColor = "#FFFFFF";
8
- export default function Input({ value, setValue, prompt, activeSuggestion, tabCompletionDropSize, }) {
9
- const [cursorLocation, setCursorLocation] = useState(value.length);
10
- const [cursorBlink, setCursorBlink] = useState(true);
11
- useEffect(() => {
12
- setTimeout(() => {
13
- setCursorBlink(!cursorBlink);
14
- }, BlinkSpeed);
15
- }, [cursorBlink]);
16
- // TODO: arrow key navigation shortcuts (emacs & vim modes)
17
- useInput((input, key) => {
18
- // TODO: handle delete better on unix systems: https://github.com/vadimdemedes/ink/issues/634
19
- const windows = process.platform === "win32";
20
- const backspaceKey = windows ? key.backspace : key.backspace || key.delete;
21
- const deleteKey = windows ? key.delete : false;
22
- if (backspaceKey) {
23
- setValue([...value].slice(0, Math.max(cursorLocation - 1, 0)).join("") + [...value].slice(cursorLocation).join(""));
24
- setCursorLocation(Math.max(cursorLocation - 1, 0));
25
- }
26
- else if (deleteKey) {
27
- setValue([...value].slice(0, cursorLocation).join("") + [...value].slice(Math.min(value.length, cursorLocation + 1)).join(""));
28
- }
29
- else if (key.leftArrow) {
30
- setCursorLocation(Math.max(cursorLocation - 1, 0));
31
- }
32
- else if (key.rightArrow) {
33
- setCursorLocation(Math.min(cursorLocation + 1, value.length));
34
- }
35
- else if (key.tab) {
36
- if (activeSuggestion) {
37
- // TOOD: support insertValue
38
- const newValue = [...value].slice(0, cursorLocation - tabCompletionDropSize).join("") + activeSuggestion.name + " ";
39
- setValue(newValue);
40
- setCursorLocation(newValue.length);
41
- }
42
- }
43
- else if (input) {
44
- setValue([...value].slice(0, cursorLocation).join("") + input + [...value].slice(cursorLocation).join(""));
45
- setCursorLocation(cursorLocation + input.length);
46
- }
47
- });
48
- const cursoredCommand = value + " ";
49
- const cursoredText = [...cursoredCommand].slice(0, cursorLocation).join("") +
50
- (cursorBlink ? chalk.bgHex(CursorColor).inverse([...cursoredCommand].at(cursorLocation)) : [...cursoredCommand].at(cursorLocation)) +
51
- [...cursoredCommand].slice(cursorLocation + 1).join("");
52
- return (React.createElement(Text, null,
53
- prompt,
54
- cursoredText));
55
- }
3
+ export const inputModifier = (input) => {
4
+ switch (input.toString()) {
5
+ case "\b":
6
+ return "\u007F"; // DEL
7
+ }
8
+ return input.toString();
9
+ };