@microsoft/inshellisense 0.0.1-rc.12 → 0.0.1-rc.14

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;
@@ -12,7 +12,7 @@ export const action = (program) => async (options) => {
12
12
  const inISTerm = process.env.ISTERM === "1";
13
13
  if (options.check || inISTerm) {
14
14
  process.stdout.write(renderConfirmation(inISTerm));
15
- return;
15
+ process.exit(0);
16
16
  }
17
17
  if (options.verbose)
18
18
  await log.enable();
@@ -32,5 +32,5 @@ export const action = (program) => async (options) => {
32
32
  await setupBashPreExec();
33
33
  }
34
34
  await loadAliases(shell);
35
- await render(shell, options.test ?? false, options.parentTermExit ?? false);
35
+ await render(shell, options.test ?? false, options.login ?? false);
36
36
  };
@@ -0,0 +1,26 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { Command } from "commander";
4
+ import { loadConfig } from "../../utils/config.js";
5
+ import { getSpecNames, loadLocalSpecsSet } from "../../runtime/runtime.js";
6
+ import { getAliasNames, loadAliases } from "../../runtime/alias.js";
7
+ import { aliasSupportedShells } from "../../utils/shell.js";
8
+ const supportedShells = aliasSupportedShells.join(", ");
9
+ const action = (program) => async (options) => {
10
+ await loadConfig(program);
11
+ await loadLocalSpecsSet();
12
+ const { shell } = options;
13
+ if (shell && !aliasSupportedShells.map((s) => s.valueOf()).includes(shell)) {
14
+ program.error(`Unsupported shell: '${shell}', supported shells: ${supportedShells}`, { exitCode: 1 });
15
+ }
16
+ if (shell) {
17
+ await loadAliases(shell);
18
+ }
19
+ process.stdout.write(JSON.stringify([...getAliasNames(), ...getSpecNames()]));
20
+ process.exit(0);
21
+ };
22
+ const cmd = new Command("list");
23
+ cmd.description(`list the names of all available specs`);
24
+ cmd.option("-s, --shell <shell>", `shell to use alias specs, supported shells: ${supportedShells}`);
25
+ cmd.action(action(cmd));
26
+ export default cmd;
@@ -0,0 +1,8 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { Command } from "commander";
4
+ import list from "./list.js";
5
+ const cmd = new Command("specs");
6
+ cmd.description(`manage specs`);
7
+ cmd.addCommand(list);
8
+ export default cmd;
package/build/index.js CHANGED
@@ -5,6 +5,8 @@
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";
9
+ import specs from "./commands/specs/root.js";
8
10
  import { action, supportedShells } from "./commands/root.js";
9
11
  import { getVersion } from "./utils/version.js";
10
12
  const program = new Command();
@@ -18,12 +20,15 @@ program
18
20
  .description("IDE style command line auto complete")
19
21
  .version(await getVersion(), "-v, --version", "output the current version")
20
22
  .action(action(program))
23
+ .option("-l, --login", `start shell as a login shell`)
21
24
  .option("-s, --shell <shell>", `shell to use for command execution, supported shells: ${supportedShells}`)
22
25
  .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
26
  .addOption(hiddenOption("-T, --test", "used to make e2e tests reproducible across machines"))
25
27
  .option("-V, --verbose", `enable verbose logging`)
26
- .showHelpAfterError("(add --help for additional information)");
28
+ .showHelpAfterError("(add --help for additional information)")
29
+ .passThroughOptions();
27
30
  program.addCommand(complete);
28
31
  program.addCommand(uninstall);
32
+ program.addCommand(init);
33
+ program.addCommand(specs);
29
34
  program.parse();
@@ -188,6 +188,9 @@ export class CommandManager {
188
188
  cursorTerminated: this.#activeCommand.cursorTerminated,
189
189
  };
190
190
  }
191
+ clearActiveCommand() {
192
+ this.#activeCommand = {};
193
+ }
191
194
  termSync() {
192
195
  if (this.#activeCommand.promptEndMarker == null || this.#activeCommand.promptStartMarker == null) {
193
196
  return;
@@ -197,6 +200,7 @@ export class CommandManager {
197
200
  if (globalCursorPosition < this.#activeCommand.promptStartMarker.line) {
198
201
  this.handleClear();
199
202
  this.#activeCommand.promptEndMarker = this.#terminal.registerMarker(0);
203
+ return;
200
204
  }
201
205
  if (this.#activeCommand.promptEndMarker == null)
202
206
  return;
@@ -223,7 +227,7 @@ export class CommandManager {
223
227
  let wrappedCommand = "";
224
228
  let suggestions = "";
225
229
  let isWrapped = false;
226
- for (;;) {
230
+ for (; lineY < this.#terminal.buffer.active.baseY + this.#terminal.rows;) {
227
231
  for (let i = lineY == this.#activeCommand.promptEndMarker.line ? this.#activeCommand.promptText.length : 0; i < this.#terminal.cols; i++) {
228
232
  if (command.endsWith(" "))
229
233
  break; // assume that a command that ends with 4 spaces is terminated, avoids capturing right prompts
@@ -31,13 +31,13 @@ export class ISTerm {
31
31
  #term;
32
32
  #commandManager;
33
33
  #shell;
34
- constructor({ shell, cols, rows, env, shellTarget, shellArgs, underTest }) {
34
+ constructor({ shell, cols, rows, env, shellTarget, shellArgs, underTest, login }) {
35
35
  this.#pty = pty.spawn(shellTarget, shellArgs ?? [], {
36
36
  name: "xterm-256color",
37
37
  cols,
38
38
  rows,
39
39
  cwd: process.cwd(),
40
- env: { ...convertToPtyEnv(shell, underTest), ...env },
40
+ env: { ...convertToPtyEnv(shell, underTest, login), ...env },
41
41
  });
42
42
  this.pid = this.#pty.pid;
43
43
  this.cols = this.#pty.cols;
@@ -144,24 +144,37 @@ export class ISTerm {
144
144
  };
145
145
  }
146
146
  _sameAccent(baseCell, targetCell) {
147
- return baseCell?.isBold() == targetCell?.isBold() && baseCell?.isItalic() == targetCell?.isItalic() && baseCell?.isUnderline() == targetCell?.isUnderline();
147
+ return (baseCell?.isBold() == targetCell?.isBold() &&
148
+ baseCell?.isItalic() == targetCell?.isItalic() &&
149
+ baseCell?.isUnderline() == targetCell?.isUnderline() &&
150
+ baseCell?.extended.underlineStyle == targetCell?.extended.underlineStyle &&
151
+ baseCell?.hasExtendedAttrs() == targetCell?.hasExtendedAttrs() &&
152
+ baseCell?.isInverse() == targetCell?.isInverse() &&
153
+ baseCell?.isBlink() == targetCell?.isBlink() &&
154
+ baseCell?.isInvisible() == targetCell?.isInvisible() &&
155
+ baseCell?.isDim() == targetCell?.isDim() &&
156
+ baseCell?.isStrikethrough() == targetCell?.isStrikethrough());
148
157
  }
149
158
  _getAnsiAccents(cell) {
150
159
  if (cell == null)
151
160
  return "";
152
- let boldAnsi = "";
153
- if (cell.isBold()) {
154
- boldAnsi = "\x1b[1m";
155
- }
156
- let italicAnsi = "";
157
- if (cell.isItalic()) {
158
- italicAnsi = "\x1b[3m";
159
- }
160
161
  let underlineAnsi = "";
161
162
  if (cell.isUnderline()) {
162
- underlineAnsi = "\x1b[4m";
163
+ if (cell.hasExtendedAttrs() && cell.extended.underlineStyle) {
164
+ underlineAnsi = `\x1b[4:${cell.extended.underlineStyle}m`;
165
+ }
166
+ else {
167
+ underlineAnsi = "\x1b[4m";
168
+ }
163
169
  }
164
- return boldAnsi + italicAnsi + underlineAnsi;
170
+ const boldAnsi = cell.isBold() ? "\x1b[1m" : "";
171
+ const dimAnsi = cell.isDim() ? "\x1b[2m" : "";
172
+ const italicAnsi = cell.isItalic() ? "\x1b[3m" : "";
173
+ const blinkAnsi = cell.isBlink() ? "\x1b[5m" : "";
174
+ const inverseAnsi = cell.isInverse() ? "\x1b[7m" : "";
175
+ const invisibleAnsi = cell.isInvisible() ? "\x1b[8m" : "";
176
+ const strikethroughAnsi = cell.isStrikethrough() ? "\x1b[9m" : "";
177
+ return boldAnsi + italicAnsi + underlineAnsi + inverseAnsi + dimAnsi + blinkAnsi + invisibleAnsi + strikethroughAnsi;
165
178
  }
166
179
  _sameColor(baseCell, targetCell) {
167
180
  return (baseCell?.getBgColorMode() == targetCell?.getBgColorMode() &&
@@ -196,6 +209,9 @@ export class ISTerm {
196
209
  }
197
210
  return bgAnsi + fgAnsi;
198
211
  }
212
+ clearCommand() {
213
+ this.#commandManager.clearActiveCommand();
214
+ }
199
215
  getCells(height, direction) {
200
216
  const currentCursorPosition = this.#term.buffer.active.cursorY + this.#term.buffer.active.baseY;
201
217
  const writeLine = (y) => {
@@ -207,10 +223,15 @@ export class ISTerm {
207
223
  for (let x = 0; x < line.length; x++) {
208
224
  const cell = line.getCell(x);
209
225
  const chars = cell?.getChars() ?? "";
210
- if (!this._sameColor(prevCell, cell)) {
226
+ const sameColor = this._sameColor(prevCell, cell);
227
+ const sameAccents = this._sameAccent(prevCell, cell);
228
+ if (!sameColor || !sameAccents) {
229
+ ansiLine.push("\x1b[0m");
230
+ }
231
+ if (!sameColor) {
211
232
  ansiLine.push(this._getAnsiColors(cell));
212
233
  }
213
- if (!this._sameAccent(prevCell, cell)) {
234
+ if (!sameAccents) {
214
235
  ansiLine.push(this._getAnsiAccents(cell));
215
236
  }
216
237
  ansiLine.push(chars == "" ? " " : chars);
@@ -237,10 +258,10 @@ export class ISTerm {
237
258
  }
238
259
  }
239
260
  export const spawn = async (options) => {
240
- const { shellTarget, shellArgs } = await convertToPtyTarget(options.shell, options.underTest);
261
+ const { shellTarget, shellArgs } = await convertToPtyTarget(options.shell, options.underTest, options.login);
241
262
  return new ISTerm({ ...options, shellTarget, shellArgs });
242
263
  };
243
- const convertToPtyTarget = async (shell, underTest) => {
264
+ const convertToPtyTarget = async (shell, underTest, login) => {
244
265
  const platform = os.platform();
245
266
  const shellTarget = shell == Shell.Bash && platform == "win32" ? await gitBashPath() : platform == "win32" ? `${shell}.exe` : shell;
246
267
  const shellFolderPath = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "..", "..", "shell");
@@ -273,15 +294,31 @@ const convertToPtyTarget = async (shell, underTest) => {
273
294
  shellArgs.push("-n");
274
295
  break;
275
296
  }
297
+ if (login) {
298
+ switch (shell) {
299
+ case Shell.Powershell:
300
+ case Shell.Pwsh:
301
+ shellArgs.unshift("-login");
302
+ break;
303
+ case Shell.Zsh:
304
+ case Shell.Fish:
305
+ case Shell.Xonsh:
306
+ case Shell.Nushell:
307
+ shellArgs.unshift("--login");
308
+ break;
309
+ }
310
+ }
276
311
  return { shellTarget, shellArgs };
277
312
  };
278
- const convertToPtyEnv = (shell, underTest) => {
313
+ const convertToPtyEnv = (shell, underTest, login) => {
279
314
  const env = {
280
315
  ...process.env,
281
316
  ISTERM: "1",
282
317
  };
283
318
  if (underTest)
284
319
  env.ISTERM_TESTING = "1";
320
+ if (login)
321
+ env.ISTERM_LOGIN = "1";
285
322
  switch (shell) {
286
323
  case Shell.Cmd: {
287
324
  if (underTest) {
@@ -48,6 +48,7 @@ export const loadAliases = async (shell) => {
48
48
  }
49
49
  return [];
50
50
  };
51
+ export const getAliasNames = () => Object.keys(loadedAliases);
51
52
  export const aliasExpand = (command) => {
52
53
  if (!command.at(0)?.complete)
53
54
  return command;
@@ -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
  };
@@ -103,6 +103,9 @@ export const getSuggestions = async (cmd, cwd, shell) => {
103
103
  }
104
104
  return { ...result, charactersToDrop };
105
105
  };
106
+ export const getSpecNames = () => {
107
+ return Object.keys(specSet).filter((spec) => !spec.startsWith("@") && spec != "-");
108
+ };
106
109
  const getPersistentOptions = (persistentOptions, options) => {
107
110
  const persistentOptionNames = new Set(persistentOptions.map((o) => (typeof o.name === "string" ? [o.name] : o.name)).flat());
108
111
  return persistentOptions.concat((options ?? []).filter((o) => (typeof o.name == "string" ? !persistentOptionNames.has(o.name) : o.name.some((n) => !persistentOptionNames.has(n))) && o.isPersistent === true));
@@ -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 };
@@ -122,6 +122,9 @@ export class SuggestionManager {
122
122
  }
123
123
  update(keyPress) {
124
124
  const { name, shift, ctrl } = keyPress;
125
+ if (name == "return") {
126
+ this.#term.clearCommand(); // clear the current command on enter
127
+ }
125
128
  if (!this.#suggestBlob) {
126
129
  return false;
127
130
  }
@@ -12,8 +12,8 @@ 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) => {
16
- const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest });
15
+ export const render = async (shell, underTest, login) => {
16
+ const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest, login });
17
17
  const suggestionManager = new SuggestionManager(term, shell);
18
18
  let hasActiveSuggestions = false;
19
19
  let previousSuggestionsRows = 0;
@@ -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", () => {
@@ -29,6 +29,8 @@ 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);
33
+ export const aliasSupportedShells = [Shell.Bash, Shell.Zsh];
32
34
  export const userZdotdir = process.env?.ZDOTDIR ?? os.homedir() ?? `~`;
33
35
  export const zdotdir = path.join(os.tmpdir(), `is-zsh`);
34
36
  const configFolder = ".inshellisense";
@@ -99,3 +101,53 @@ export const getBackspaceSequence = (press, shell) => shell === Shell.Pwsh || sh
99
101
  export const getPathSeperator = (shell) => (shell == Shell.Bash || shell == Shell.Xonsh || shell == Shell.Nushell ? "/" : path.sep);
100
102
  // nu fully re-writes the prompt every keystroke resulting in duplicate start/end sequences on the same line
101
103
  export const getShellPromptRewrites = (shell) => shell == Shell.Nushell;
104
+ export const getShellConfig = (shell) => {
105
+ switch (shell) {
106
+ case Shell.Zsh:
107
+ return `if [[ -z "\${ISTERM}" && $- = *i* && $- != *c* ]]; then
108
+ if [[ -o login ]]; then
109
+ is -s zsh --login ; exit
110
+ else
111
+ is -s zsh ; exit
112
+ fi
113
+ fi`;
114
+ case Shell.Bash:
115
+ return `if [[ -z "\${ISTERM}" && $- = *i* && $- != *c* ]]; then
116
+ shopt -q login_shell
117
+ login_shell=$?
118
+ if [ $login_shell -eq 0 ]; then
119
+ is -s bash --login ; exit
120
+ else
121
+ is -s bash ; exit
122
+ fi
123
+ fi`;
124
+ case Shell.Powershell:
125
+ case Shell.Pwsh:
126
+ return `$__IsCommandFlag = ([Environment]::GetCommandLineArgs() | ForEach-Object { $_.contains("-Command") }) -contains $true
127
+ $__IsNoExitFlag = ([Environment]::GetCommandLineArgs() | ForEach-Object { $_.contains("-NoExit") }) -contains $true
128
+ $__IsInteractive = -not $__IsCommandFlag -or ($__IsCommandFlag -and $__IsNoExitFlag)
129
+ if ([string]::IsNullOrEmpty($env:ISTERM) -and [Environment]::UserInteractive -and $__IsInteractive) {
130
+ is -s ${shell}
131
+ Stop-Process -Id $pid
132
+ }`;
133
+ case Shell.Fish:
134
+ return `if test -z "$ISTERM" && status --is-interactive
135
+ if status --is-login
136
+ is -s fish --login ; kill %self
137
+ else
138
+ is -s fish ; kill %self
139
+ end
140
+ end`;
141
+ case Shell.Xonsh:
142
+ return `if 'ISTERM' not in \${...} and $XONSH_INTERACTIVE:
143
+ if $XONSH_LOGIN:
144
+ is -s xonsh --login ; exit
145
+ else:
146
+ is -s xonsh ; exit`;
147
+ case Shell.Nushell:
148
+ return `if "ISTERM" not-in $env and $nu.is-interactive {
149
+ if $nu.is-login { is -s nu --login ; exit } else { is -s nu ; exit }
150
+ }`;
151
+ }
152
+ return "";
153
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/inshellisense",
3
- "version": "0.0.1-rc.12",
3
+ "version": "0.0.1-rc.14",
4
4
  "description": "IDE style command line auto complete",
5
5
  "type": "module",
6
6
  "engines": {
@@ -41,6 +41,7 @@
41
41
  "dependencies": {
42
42
  "@homebridge/node-pty-prebuilt-multiarch": "^0.11.12",
43
43
  "@withfig/autocomplete": "2.651.0",
44
+ "@xterm/headless": "^5.5.0",
44
45
  "ajv": "^8.12.0",
45
46
  "ansi-escapes": "^6.2.0",
46
47
  "ansi-styles": "^6.2.1",
@@ -51,8 +52,7 @@
51
52
  "toml": "^3.0.0",
52
53
  "wcwidth": "^1.0.1",
53
54
  "which": "^4.0.0",
54
- "wrap-ansi": "^8.1.0",
55
- "@xterm/headless": "^5.3.0"
55
+ "wrap-ansi": "^8.1.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@microsoft/tui-test": "^0.0.1-rc.3",
@@ -65,6 +65,7 @@
65
65
  "@typescript-eslint/eslint-plugin": "^6.7.4",
66
66
  "@typescript-eslint/parser": "^6.7.4",
67
67
  "@withfig/autocomplete-types": "^1.28.0",
68
+ "@xterm/xterm": "^5.5.0",
68
69
  "eslint": "^8.51.0",
69
70
  "eslint-config-prettier": "^9.0.0",
70
71
  "eslint-plugin-header": "^3.1.1",
@@ -1,5 +1,19 @@
1
- if [ -r ~/.bashrc ]; then
2
- . ~/.bashrc
1
+ if [ -z "$ISTERM_LOGIN" ]; then
2
+ if [ -r ~/.bashrc ]; then
3
+ . ~/.bashrc
4
+ fi
5
+ else
6
+ if [ -r /etc/profile ]; then
7
+ . /etc/profile
8
+ fi
9
+ # execute the first that exists
10
+ if [ -r ~/.bash_profile ]; then
11
+ . ~/.bash_profile
12
+ elif [ -r ~/.bash_login ]; then
13
+ . ~/.bash_login
14
+ elif [ -r ~/.profile ]; then
15
+ . ~/.profile
16
+ fi
3
17
  fi
4
18
 
5
19
  if [ -r ~/.inshellisense/bash-preexec.sh ]; then