@microsoft/inshellisense 0.0.1-rc.11 → 0.0.1-rc.13

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/README.md CHANGED
@@ -20,6 +20,33 @@ npm install -g @microsoft/inshellisense
20
20
 
21
21
  After completing the installation, you can run `is` to start the autocomplete session for your desired shell. Additionally, inshellisense is also aliased under `inshellisense` after installation.
22
22
 
23
+ ### Shell Plugin
24
+
25
+ If you'd like to automatically start inshellisense when you open your shell, run the respective command for your shell. After running the command, inshellisense will automatically open when you start any new shell session:
26
+
27
+ ```shell
28
+ # bash
29
+ is init bash >> ~/.bashrc
30
+
31
+ # zsh
32
+ is init zsh >> ~/.zshrc
33
+
34
+ # fish
35
+ is init fish >> ~/.config/fish/config.fish
36
+
37
+ # pwsh
38
+ is init pwsh >> $profile
39
+
40
+ # powershell
41
+ is init powershell >> $profile
42
+
43
+ # xonsh
44
+ is init xonsh >> ~/.xonshrc
45
+
46
+ # nushell
47
+ is init nu >> $nu.env-path
48
+ ```
49
+
23
50
  ### Usage
24
51
 
25
52
  | Action | Command | Description |
@@ -0,0 +1,18 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { Command } from "commander";
4
+ import { initSupportedShells as shells, getShellConfig } from "../utils/shell.js";
5
+ const supportedShells = shells.join(", ");
6
+ const action = (program) => async (shell) => {
7
+ if (!shells.map((s) => s.valueOf()).includes(shell)) {
8
+ program.error(`Unsupported shell: '${shell}', supported shells: ${supportedShells}`, { exitCode: 1 });
9
+ }
10
+ const config = getShellConfig(shell);
11
+ process.stdout.write(`\n\n# ---------------- inshellisense shell plugin ----------------\n${config}`);
12
+ process.exit(0);
13
+ };
14
+ const cmd = new Command("init");
15
+ cmd.description(`generates shell configurations for the provided shell`);
16
+ cmd.argument("<shell>", `shell to generate configuration for, supported shells: ${supportedShells}`);
17
+ cmd.action(action(cmd));
18
+ export default cmd;
@@ -5,6 +5,8 @@ import { Shell, supportedShells as shells, setupZshDotfiles, setupBashPreExec }
5
5
  import { inferShell } from "../utils/shell.js";
6
6
  import { loadConfig } from "../utils/config.js";
7
7
  import log from "../utils/log.js";
8
+ import { loadAliases } from "../runtime/alias.js";
9
+ import { loadLocalSpecsSet } from "../runtime/runtime.js";
8
10
  export const supportedShells = shells.join(", ");
9
11
  export const action = (program) => async (options) => {
10
12
  const inISTerm = process.env.ISTERM === "1";
@@ -15,6 +17,7 @@ export const action = (program) => async (options) => {
15
17
  if (options.verbose)
16
18
  await log.enable();
17
19
  await loadConfig(program);
20
+ await loadLocalSpecsSet();
18
21
  const shell = options.shell ?? (await inferShell());
19
22
  if (shell == null) {
20
23
  program.error(`Unable to identify shell, use the -s/--shell option to provide your shell`, { exitCode: 1 });
@@ -28,5 +31,6 @@ export const action = (program) => async (options) => {
28
31
  else if (shell == Shell.Bash) {
29
32
  await setupBashPreExec();
30
33
  }
31
- await render(shell, options.test ?? false, options.parentTermExit ?? false);
34
+ await loadAliases(shell);
35
+ await render(shell, options.test ?? false);
32
36
  };
package/build/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  import { Command, Option } from "commander";
6
6
  import complete from "./commands/complete.js";
7
7
  import uninstall from "./commands/uninstall.js";
8
+ import init from "./commands/init.js";
8
9
  import { action, supportedShells } from "./commands/root.js";
9
10
  import { getVersion } from "./utils/version.js";
10
11
  const program = new Command();
@@ -20,10 +21,10 @@ program
20
21
  .action(action(program))
21
22
  .option("-s, --shell <shell>", `shell to use for command execution, supported shells: ${supportedShells}`)
22
23
  .option("-c, --check", `check if shell is in an inshellisense session`)
23
- .option("--parent-term-exit", `when inshellisense is closed, kill the parent process`)
24
24
  .addOption(hiddenOption("-T, --test", "used to make e2e tests reproducible across machines"))
25
25
  .option("-V, --verbose", `enable verbose logging`)
26
26
  .showHelpAfterError("(add --help for additional information)");
27
27
  program.addCommand(complete);
28
28
  program.addCommand(uninstall);
29
+ program.addCommand(init);
29
30
  program.parse();
@@ -32,6 +32,8 @@ export class CommandManager {
32
32
  this.#activeCommand = { promptStartMarker: this.#terminal.registerMarker(0), hasOutput: false, cursorTerminated: false };
33
33
  }
34
34
  handlePromptEnd() {
35
+ if (this.#activeCommand.promptEndMarker != null)
36
+ return;
35
37
  this.#activeCommand.promptEndMarker = this.#terminal.registerMarker(0);
36
38
  if (this.#activeCommand.promptEndMarker?.line === this.#terminal.buffer.active.cursorY) {
37
39
  this.#activeCommand.promptEndX = this.#terminal.buffer.active.cursorX;
@@ -218,8 +220,10 @@ export class CommandManager {
218
220
  let lineY = this.#activeCommand.promptEndMarker.line;
219
221
  let line = this.#terminal.buffer.active.getLine(this.#activeCommand.promptEndMarker.line);
220
222
  let command = "";
223
+ let wrappedCommand = "";
221
224
  let suggestions = "";
222
- for (;;) {
225
+ let isWrapped = false;
226
+ for (; lineY < this.#terminal.buffer.active.baseY + this.#terminal.rows;) {
223
227
  for (let i = lineY == this.#activeCommand.promptEndMarker.line ? this.#activeCommand.promptText.length : 0; i < this.#terminal.cols; i++) {
224
228
  if (command.endsWith(" "))
225
229
  break; // assume that a command that ends with 4 spaces is terminated, avoids capturing right prompts
@@ -230,6 +234,7 @@ export class CommandManager {
230
234
  const cleanedChars = chars == "" ? " " : chars;
231
235
  if (!this._isSuggestion(cell) && suggestions.length == 0) {
232
236
  command += cleanedChars;
237
+ wrappedCommand += cleanedChars;
233
238
  }
234
239
  else {
235
240
  suggestions += cleanedChars;
@@ -237,11 +242,16 @@ export class CommandManager {
237
242
  }
238
243
  lineY += 1;
239
244
  line = this.#terminal.buffer.active.getLine(lineY);
240
- if (!line?.isWrapped) {
245
+ const wrapped = line?.isWrapped || this.#terminal.buffer.active.cursorY + this.#terminal.buffer.active.baseY != lineY - 1;
246
+ isWrapped = isWrapped || wrapped;
247
+ if (!wrapped) {
241
248
  break;
242
249
  }
250
+ wrappedCommand = "";
243
251
  }
244
- const cursorAtEndOfInput = (this.#activeCommand.promptText.length + command.trim().length) % this.#terminal.cols <= this.#terminal.buffer.active.cursorX;
252
+ const cursorAtEndOfInput = isWrapped
253
+ ? wrappedCommand.trim().length % this.#terminal.cols <= this.#terminal.buffer.active.cursorX
254
+ : (this.#activeCommand.promptText.length + command.trimEnd().length) % this.#terminal.cols <= this.#terminal.buffer.active.cursorX;
245
255
  let hasOutput = false;
246
256
  let cell = undefined;
247
257
  for (let i = 0; i < this.#terminal.cols; i++) {
@@ -253,7 +263,10 @@ export class CommandManager {
253
263
  break;
254
264
  }
255
265
  }
256
- const commandPostfix = this.#activeCommand.promptText.length + command.trim().length < this.#terminal.buffer.active.cursorX ? " " : "";
266
+ const postfixActive = isWrapped
267
+ ? wrappedCommand.trim().length < this.#terminal.buffer.active.cursorX
268
+ : this.#activeCommand.promptText.length + command.trimEnd().length < this.#terminal.buffer.active.cursorX;
269
+ const commandPostfix = postfixActive ? " " : "";
257
270
  this.#activeCommand.persistentOutput = this.#activeCommand.hasOutput && hasOutput;
258
271
  this.#activeCommand.hasOutput = hasOutput;
259
272
  this.#activeCommand.suggestionsText = suggestions.trim();
@@ -0,0 +1,60 @@
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 = 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() });
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) + " ");
24
+ });
25
+ };
26
+ const loadZshAliases = async () => {
27
+ const { stdout, stderr, status } = await executeShellCommand({ command: Shell.Zsh, args: ["-i", "-c", "alias"], cwd: process.cwd() });
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) + " ");
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 aliasExpand = (command) => {
52
+ if (!command.at(0)?.complete)
53
+ return command;
54
+ const alias = loadedAliases[command.at(0)?.token ?? ""];
55
+ if (alias) {
56
+ log.debug({ msg: "expanding alias", alias, command: command.slice(1) });
57
+ return [...alias, ...command.slice(1)];
58
+ }
59
+ return command;
60
+ };
@@ -29,9 +29,10 @@ const lex = (command) => {
29
29
  readingQuotedString = false;
30
30
  const complete = idx + 1 < command.length && spaceRegex.test(command[idx + 1]);
31
31
  tokens.push({
32
- token: command.slice(readingIdx, idx + 1),
32
+ token: command.slice(readingIdx + 1, idx),
33
33
  complete,
34
34
  isOption: false,
35
+ isQuoted: true,
35
36
  });
36
37
  }
37
38
  else if ((readingFlag && spaceRegex.test(char)) || char === "=") {
@@ -42,7 +43,7 @@ const lex = (command) => {
42
43
  isOption: true,
43
44
  });
44
45
  }
45
- else if (readingCmd && spaceRegex.test(char)) {
46
+ else if (readingCmd && spaceRegex.test(char) && command.at(idx - 1) !== "\\") {
46
47
  readingCmd = false;
47
48
  tokens.push({
48
49
  token: command.slice(readingIdx, idx),
@@ -53,11 +54,21 @@ const lex = (command) => {
53
54
  });
54
55
  const reading = readingQuotedString || readingFlag || readingCmd;
55
56
  if (reading) {
56
- tokens.push({
57
- token: command.slice(readingIdx),
58
- complete: false,
59
- isOption: readingFlag,
60
- });
57
+ if (readingQuotedString) {
58
+ tokens.push({
59
+ token: command.slice(readingIdx + 1),
60
+ complete: false,
61
+ isOption: false,
62
+ isQuoted: true,
63
+ });
64
+ }
65
+ else {
66
+ tokens.push({
67
+ token: command.slice(readingIdx),
68
+ complete: false,
69
+ isOption: readingFlag,
70
+ });
71
+ }
61
72
  }
62
73
  return tokens;
63
74
  };
@@ -8,25 +8,31 @@ import path from "node:path";
8
8
  import { parseCommand } from "./parser.js";
9
9
  import { getArgDrivenRecommendation, getSubcommandDrivenRecommendation } from "./suggestion.js";
10
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";
11
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- recursive type, setting as any
12
15
  const specSet = {};
13
- speclist.forEach((s) => {
14
- let activeSet = specSet;
15
- const specRoutes = s.split("/");
16
- specRoutes.forEach((route, idx) => {
17
- if (typeof activeSet !== "object") {
18
- return;
19
- }
20
- if (idx === specRoutes.length - 1) {
21
- const prefix = versionedSpeclist.includes(s) ? "/index.js" : `.js`;
22
- activeSet[route] = `@withfig/autocomplete/build/${s}${prefix}`;
23
- }
24
- else {
25
- activeSet[route] = activeSet[route] || {};
26
- activeSet = activeSet[route];
27
- }
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
+ });
28
33
  });
29
- });
34
+ }
35
+ loadSpecsSet(speclist, versionedSpeclist, `@withfig/autocomplete/build`);
30
36
  const loadedSpecs = {};
31
37
  const loadSpec = async (cmd) => {
32
38
  const rootToken = cmd.at(0);
@@ -50,12 +56,32 @@ const lazyLoadSpec = async (key) => {
50
56
  const lazyLoadSpecLocation = async (location) => {
51
57
  return; //TODO: implement spec location loading
52
58
  };
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
+ };
53
78
  export const getSuggestions = async (cmd, cwd, shell) => {
54
- const activeCmd = parseCommand(cmd);
79
+ let activeCmd = parseCommand(cmd);
55
80
  const rootToken = activeCmd.at(0);
56
81
  if (activeCmd.length === 0 || !rootToken?.complete) {
57
82
  return;
58
83
  }
84
+ activeCmd = aliasExpand(activeCmd);
59
85
  const spec = await loadSpec(activeCmd);
60
86
  if (spec == null)
61
87
  return;
@@ -127,17 +153,29 @@ const genSubcommand = async (command, parentCommand) => {
127
153
  return parentCommand.subcommands[subcommandIdx];
128
154
  }
129
155
  else {
130
- parentCommand.subcommands[subcommandIdx] = { ...subcommand, ...partSpec, loadSpec: undefined };
156
+ parentCommand.subcommands[subcommandIdx] = {
157
+ ...subcommand,
158
+ ...partSpec,
159
+ loadSpec: undefined,
160
+ };
131
161
  return parentCommand.subcommands[subcommandIdx];
132
162
  }
133
163
  }
134
164
  case "string": {
135
165
  const spec = await lazyLoadSpec(subcommand.loadSpec);
136
- parentCommand.subcommands[subcommandIdx] = { ...subcommand, ...(getSubcommand(spec) ?? []), loadSpec: undefined };
166
+ parentCommand.subcommands[subcommandIdx] = {
167
+ ...subcommand,
168
+ ...(getSubcommand(spec) ?? []),
169
+ loadSpec: undefined,
170
+ };
137
171
  return parentCommand.subcommands[subcommandIdx];
138
172
  }
139
173
  case "object": {
140
- parentCommand.subcommands[subcommandIdx] = { ...subcommand, ...(subcommand.loadSpec ?? {}), loadSpec: undefined };
174
+ parentCommand.subcommands[subcommandIdx] = {
175
+ ...subcommand,
176
+ ...(subcommand.loadSpec ?? {}),
177
+ loadSpec: undefined,
178
+ };
141
179
  return parentCommand.subcommands[subcommandIdx];
142
180
  }
143
181
  case "undefined": {
@@ -164,7 +202,10 @@ const runOption = async (tokens, option, subcommand, cwd, persistentOptions, acc
164
202
  const args = option.args instanceof Array ? option.args : [option.args];
165
203
  return runArg(tokens.slice(1), args, subcommand, cwd, persistentOptions, acceptedTokens.concat(activeToken), true, false);
166
204
  }
167
- return runSubcommand(tokens.slice(1), subcommand, cwd, persistentOptions, acceptedTokens.concat({ ...activeToken, isPersistent }));
205
+ return runSubcommand(tokens.slice(1), subcommand, cwd, persistentOptions, acceptedTokens.concat({
206
+ ...activeToken,
207
+ isPersistent,
208
+ }));
168
209
  };
169
210
  const runArg = async (tokens, args, subcommand, cwd, persistentOptions, acceptedTokens, fromOption, fromVariadic) => {
170
211
  if (args.length === 0) {
@@ -3,6 +3,7 @@
3
3
  import path from "node:path";
4
4
  import { runGenerator } from "./generator.js";
5
5
  import { runTemplates } from "./template.js";
6
+ import log from "../utils/log.js";
6
7
  var SuggestionIcons;
7
8
  (function (SuggestionIcons) {
8
9
  SuggestionIcons["File"] = "\uD83D\uDCC4";
@@ -43,6 +44,9 @@ const getIcon = (icon, suggestionType) => {
43
44
  const getLong = (suggestion) => {
44
45
  return suggestion instanceof Array ? suggestion.reduce((p, c) => (p.length > c.length ? p : c)) : suggestion;
45
46
  };
47
+ const getPathy = (type) => {
48
+ return type === "file" || type === "folder";
49
+ };
46
50
  const toSuggestion = (suggestion, name, type) => {
47
51
  if (suggestion.name == null)
48
52
  return;
@@ -53,6 +57,7 @@ const toSuggestion = (suggestion, name, type) => {
53
57
  allNames: suggestion.name instanceof Array ? suggestion.name : [suggestion.name],
54
58
  priority: suggestion.priority ?? 50,
55
59
  insertValue: suggestion.insertValue,
60
+ pathy: getPathy(suggestion.type),
56
61
  };
57
62
  };
58
63
  function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
@@ -74,6 +79,7 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
74
79
  allNames: s.name,
75
80
  priority: s.priority ?? 50,
76
81
  insertValue: s.insertValue,
82
+ pathy: getPathy(s.type),
77
83
  }
78
84
  : undefined;
79
85
  }
@@ -85,6 +91,7 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
85
91
  allNames: [s.name],
86
92
  priority: s.priority ?? 50,
87
93
  insertValue: s.insertValue,
94
+ pathy: getPathy(s.type),
88
95
  }
89
96
  : undefined;
90
97
  })
@@ -104,6 +111,7 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
104
111
  allNames: s.name,
105
112
  insertValue: s.insertValue,
106
113
  priority: s.priority ?? 50,
114
+ pathy: getPathy(s.type),
107
115
  }
108
116
  : undefined;
109
117
  }
@@ -115,6 +123,7 @@ function filter(suggestions, filterStrategy, partialCmd, suggestionType) {
115
123
  allNames: [s.name],
116
124
  insertValue: s.insertValue,
117
125
  priority: s.priority ?? 50,
126
+ pathy: getPathy(s.type),
118
127
  }
119
128
  : undefined;
120
129
  })
@@ -144,6 +153,14 @@ const optionSuggestions = (options, acceptedTokens, filterStrategy, partialCmd)
144
153
  const validOptions = options?.filter((o) => o.exclusiveOn?.every((exclusiveOption) => !usedOptions.has(exclusiveOption)) ?? true);
145
154
  return filter(validOptions ?? [], filterStrategy, partialCmd, "option");
146
155
  };
156
+ const getEscapedPath = (value) => {
157
+ return value?.replaceAll(" ", "\\ ");
158
+ };
159
+ function adjustPathSuggestions(suggestions, partialToken) {
160
+ if (partialToken == null || partialToken.isQuoted)
161
+ return suggestions;
162
+ return suggestions.map((s) => s.pathy ? { ...s, insertValue: getEscapedPath(s.insertValue), name: s.insertValue == null ? getEscapedPath(s.name) : s.name } : s);
163
+ }
147
164
  const removeAcceptedSuggestions = (suggestions, acceptedTokens) => {
148
165
  const seen = new Set(acceptedTokens.map((t) => t.token));
149
166
  return suggestions.filter((s) => s.allNames.every((n) => !seen.has(n)));
@@ -163,6 +180,7 @@ const removeEmptySuggestion = (suggestions) => {
163
180
  return suggestions.filter((s) => s.name.length > 0);
164
181
  };
165
182
  export const getSubcommandDrivenRecommendation = async (subcommand, persistentOptions, partialToken, argsDepleted, argsFromSubcommand, acceptedTokens, cwd) => {
183
+ log.debug({ msg: "suggestion point", subcommand, persistentOptions, partialToken, argsDepleted, argsFromSubcommand, acceptedTokens, cwd });
166
184
  if (argsDepleted && argsFromSubcommand) {
167
185
  return;
168
186
  }
@@ -184,7 +202,7 @@ export const getSubcommandDrivenRecommendation = async (subcommand, persistentOp
184
202
  suggestions.push(...(await templateSuggestions(activeArg?.template, activeArg?.filterStrategy, partialCmd, cwd)));
185
203
  }
186
204
  return {
187
- suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens))),
205
+ suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(adjustPathSuggestions(suggestions.sort((a, b) => b.priority - a.priority), partialToken), acceptedTokens))),
188
206
  };
189
207
  };
190
208
  export const getArgDrivenRecommendation = async (args, subcommand, persistentOptions, partialToken, acceptedTokens, variadicArgBound, cwd) => {
@@ -204,7 +222,7 @@ export const getArgDrivenRecommendation = async (args, subcommand, persistentOpt
204
222
  suggestions.push(...optionSuggestions(allOptions, acceptedTokens, activeArg?.filterStrategy, partialCmd));
205
223
  }
206
224
  return {
207
- suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(suggestions.sort((a, b) => b.priority - a.priority), acceptedTokens))),
225
+ suggestions: removeDuplicateSuggestion(removeEmptySuggestion(removeAcceptedSuggestions(adjustPathSuggestions(suggestions.sort((a, b) => b.priority - a.priority), partialToken), acceptedTokens))),
208
226
  argumentDescription: activeArg.description ?? activeArg.name,
209
227
  };
210
228
  };
@@ -6,7 +6,7 @@ import fsAsync from "node:fs/promises";
6
6
  import { getPathSeperator } from "../utils/shell.js";
7
7
  import log from "../utils/log.js";
8
8
  export const buildExecuteShellCommand = (timeout) => async ({ command, env, args, cwd }) => {
9
- const child = spawn(command, args, { cwd, env });
9
+ const child = spawn(command, args, { cwd, env: { ...env, ISTERM: "1" } });
10
10
  setTimeout(() => child.kill("SIGKILL"), timeout);
11
11
  let stdout = "";
12
12
  let stderr = "";
@@ -28,7 +28,8 @@ export const buildExecuteShellCommand = (timeout) => async ({ command, env, args
28
28
  export const resolveCwd = async (cmdToken, cwd, shell) => {
29
29
  if (cmdToken == null)
30
30
  return { cwd, pathy: false, complete: false };
31
- const { token } = cmdToken;
31
+ const { token: rawToken, isQuoted } = cmdToken;
32
+ const token = !isQuoted ? rawToken.replaceAll("\\ ", " ") : rawToken;
32
33
  const sep = getPathSeperator(shell);
33
34
  if (!token.includes(sep))
34
35
  return { cwd, pathy: false, complete: false };
@@ -12,7 +12,7 @@ export const renderConfirmation = (live) => {
12
12
  const statusMessage = live ? chalk.green("live") : chalk.red("not found");
13
13
  return `inshellisense session [${statusMessage}]\n`;
14
14
  };
15
- export const render = async (shell, underTest, parentTermExit) => {
15
+ export const render = async (shell, underTest) => {
16
16
  const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest });
17
17
  const suggestionManager = new SuggestionManager(term, shell);
18
18
  let hasActiveSuggestions = false;
@@ -129,9 +129,6 @@ export const render = async (shell, underTest, parentTermExit) => {
129
129
  }
130
130
  });
131
131
  term.onExit(({ exitCode }) => {
132
- if (parentTermExit && process.ppid) {
133
- process.kill(process.ppid);
134
- }
135
132
  process.exit(exitCode);
136
133
  });
137
134
  process.stdout.on("resize", () => {
@@ -30,6 +30,11 @@ const promptPatternsSchema = {
30
30
  required: ["regex", "postfix"],
31
31
  },
32
32
  };
33
+ const specPathsSchema = {
34
+ type: "array",
35
+ items: { type: "string" },
36
+ nullable: true,
37
+ };
33
38
  const configSchema = {
34
39
  type: "object",
35
40
  nullable: true,
@@ -55,6 +60,13 @@ const configSchema = {
55
60
  nu: promptPatternsSchema,
56
61
  },
57
62
  },
63
+ specs: {
64
+ type: "object",
65
+ nullable: true,
66
+ properties: {
67
+ path: specPathsSchema,
68
+ },
69
+ },
58
70
  },
59
71
  additionalProperties: false,
60
72
  };
@@ -98,6 +110,9 @@ export const loadConfig = async (program) => {
98
110
  pwsh: config.prompt?.pwsh,
99
111
  nu: config.prompt?.nu,
100
112
  },
113
+ specs: {
114
+ path: [`${os.homedir()}/.fig/autocomplete/build`, ...(config?.specs?.path ?? [])],
115
+ },
101
116
  };
102
117
  }
103
118
  };
@@ -29,6 +29,7 @@ export const supportedShells = [
29
29
  Shell.Xonsh,
30
30
  Shell.Nushell,
31
31
  ].filter((shell) => shell != null);
32
+ export const initSupportedShells = supportedShells.filter((shell) => shell != Shell.Cmd);
32
33
  export const userZdotdir = process.env?.ZDOTDIR ?? os.homedir() ?? `~`;
33
34
  export const zdotdir = path.join(os.tmpdir(), `is-zsh`);
34
35
  const configFolder = ".inshellisense";
@@ -99,3 +100,33 @@ export const getBackspaceSequence = (press, shell) => shell === Shell.Pwsh || sh
99
100
  export const getPathSeperator = (shell) => (shell == Shell.Bash || shell == Shell.Xonsh || shell == Shell.Nushell ? "/" : path.sep);
100
101
  // nu fully re-writes the prompt every keystroke resulting in duplicate start/end sequences on the same line
101
102
  export const getShellPromptRewrites = (shell) => shell == Shell.Nushell;
103
+ export const getShellConfig = (shell) => {
104
+ switch (shell) {
105
+ case Shell.Zsh:
106
+ case Shell.Bash:
107
+ return `if [[ -z "\${ISTERM}" && $- = *i* && $- != *c* ]]; then
108
+ is -s ${shell} ; exit
109
+ fi`;
110
+ case Shell.Powershell:
111
+ case Shell.Pwsh:
112
+ return `$__IsCommandFlag = ([Environment]::GetCommandLineArgs() | ForEach-Object { $_.contains("-Command") }) -contains $true
113
+ $__IsNoExitFlag = ([Environment]::GetCommandLineArgs() | ForEach-Object { $_.contains("-NoExit") }) -contains $true
114
+ $__IsInteractive = -not $__IsCommandFlag -or ($__IsCommandFlag -and $__IsNoExitFlag)
115
+ if ([string]::IsNullOrEmpty($env:ISTERM) -and [Environment]::UserInteractive -and $__IsInteractive) {
116
+ is -s ${shell}
117
+ Stop-Process -Id $pid
118
+ }`;
119
+ case Shell.Fish:
120
+ return `if test -z "$ISTERM" && status --is-interactive
121
+ is -s fish ; kill %self
122
+ end`;
123
+ case Shell.Xonsh:
124
+ return `if 'ISTERM' not in \${...} and $XONSH_INTERACTIVE:
125
+ is -s xonsh ; exit`;
126
+ case Shell.Nushell:
127
+ return `if "ISTERM" not-in $env and $nu.is-interactive {
128
+ is -s nu ; exit
129
+ }`;
130
+ }
131
+ return "";
132
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/inshellisense",
3
- "version": "0.0.1-rc.11",
3
+ "version": "0.0.1-rc.13",
4
4
  "description": "IDE style command line auto complete",
5
5
  "type": "module",
6
6
  "engines": {