@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.
Files changed (55) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +80 -6
  3. package/SECURITY.md +41 -41
  4. package/build/commands/complete.js +16 -0
  5. package/build/commands/doctor.js +11 -0
  6. package/build/commands/init.js +24 -0
  7. package/build/commands/root.js +27 -30
  8. package/build/commands/specs/list.js +26 -0
  9. package/build/commands/specs/root.js +8 -0
  10. package/build/commands/uninstall.js +1 -1
  11. package/build/index.js +20 -7
  12. package/build/isterm/commandManager.js +184 -0
  13. package/build/isterm/index.js +4 -0
  14. package/build/isterm/pty.js +361 -0
  15. package/build/runtime/alias.js +66 -0
  16. package/build/runtime/generator.js +24 -11
  17. package/build/runtime/parser.js +86 -16
  18. package/build/runtime/runtime.js +103 -45
  19. package/build/runtime/spec.js +36 -0
  20. package/build/runtime/suggestion.js +70 -22
  21. package/build/runtime/template.js +33 -18
  22. package/build/runtime/utils.js +111 -12
  23. package/build/ui/suggestionManager.js +162 -0
  24. package/build/ui/ui-doctor.js +69 -0
  25. package/build/ui/ui-root.js +134 -64
  26. package/build/ui/ui-uninstall.js +3 -5
  27. package/build/ui/utils.js +57 -0
  28. package/build/utils/ansi.js +37 -0
  29. package/build/utils/config.js +99 -0
  30. package/build/utils/log.js +39 -0
  31. package/build/utils/shell.js +318 -0
  32. package/package.json +39 -6
  33. package/scripts/postinstall.js +9 -0
  34. package/shell/bash-preexec.sh +380 -0
  35. package/shell/shellIntegration-env.zsh +12 -0
  36. package/shell/shellIntegration-login.zsh +9 -0
  37. package/shell/shellIntegration-profile.zsh +9 -0
  38. package/shell/shellIntegration-rc.zsh +66 -0
  39. package/shell/shellIntegration.bash +114 -0
  40. package/shell/shellIntegration.fish +27 -0
  41. package/shell/shellIntegration.nu +29 -0
  42. package/shell/shellIntegration.ps1 +26 -0
  43. package/shell/shellIntegration.xsh +31 -0
  44. package/todo.md +17 -0
  45. package/build/commands/bind.js +0 -12
  46. package/build/ui/input.js +0 -55
  47. package/build/ui/suggestions.js +0 -84
  48. package/build/ui/ui-bind.js +0 -69
  49. package/build/utils/bindings.js +0 -216
  50. package/build/utils/cache.js +0 -21
  51. package/shell/key-bindings-powershell.ps1 +0 -27
  52. package/shell/key-bindings-pwsh.ps1 +0 -27
  53. package/shell/key-bindings.bash +0 -7
  54. package/shell/key-bindings.fish +0 -8
  55. package/shell/key-bindings.zsh +0 -10
@@ -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.forEach((s) => {
13
- let activeSet = specSet;
14
- const specRoutes = s.split("/");
15
- specRoutes.forEach((route, idx) => {
16
- if (idx === specRoutes.length - 1) {
17
- const prefix = versionedSpeclist.includes(s) ? "/index.js" : `.js`;
18
- activeSet[route] = `@withfig/autocomplete/build/${s}${prefix}`;
19
- }
20
- else {
21
- activeSet[route] = activeSet[route] || {};
22
- activeSet = activeSet[route];
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 getSuggestions = async (cmd) => {
50
- const activeCmd = parseCommand(cmd);
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 result = await runSubcommand(activeCmd.slice(1), subcommand);
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 lastCommand = activeCmd.at(-1);
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
- const subcommandIdx = parentCommand.subcommands?.findIndex((s) => s.name === command);
88
- if (subcommandIdx == null)
125
+ if (!parentCommand.subcommands || parentCommand.subcommands.length === 0)
89
126
  return;
90
- const subcommand = parentCommand.subcommands?.at(subcommandIdx);
91
- if (subcommand == null)
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] = { ...subcommand, ...partSpec, loadSpec: undefined };
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] = { ...subcommand, ...(getSubcommand(spec) ?? []), loadSpec: undefined };
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] = { ...subcommand, ...(subcommand.loadSpec ?? {}), loadSpec: undefined };
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({ ...activeToken, isPersistent }));
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].token, acceptedTokens, fromVariadic);
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].token, argsDepleted, argsUsed, acceptedTokens);
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
- return runArg(tokens, getArgs(subcommand.args), subcommand, allOptions, acceptedTokens, false, false);
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
- var SuggestionIcons;
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
- const suggestions = (await Promise.all(generators.map((gen) => runGenerator(gen, tokens)))).flat();
123
- return filter(suggestions, filterStrategy, partialCmd, undefined);
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
- const removeDuplicateSuggestions = (suggestions, acceptedTokens) => {
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, partialCmd, argsDepleted, argsFromSubcommand, acceptedTokens) => {
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(removeDuplicateSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens)),
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, partialCmd, acceptedTokens, variadicArgBound) => {
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 ((activeArg.isOptional && !activeArg.isVariadic) || (activeArg.isVariadic && activeArg.isOptional && !variadicArgBound)) {
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(removeDuplicateSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens)),
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 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
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(process.cwd(), { withFileTypes: true });
11
- return files.filter((f) => f.isDirectory()).map((f) => ({ name: f.name, priority: 90 }));
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
- 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();
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
  };