@microsoft/inshellisense 0.0.1-rc.19 → 0.0.1-rc.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,7 +93,7 @@ You can customize the keybindings for inshellisense by adding a `bindings` secti
93
93
  ```toml
94
94
  [bindings.acceptSuggestion]
95
95
  key = "tab"
96
- # shift and tab are optional and default to false
96
+ # shift and ctrl are optional and default to false
97
97
  shift = false
98
98
  ctrl = false
99
99
 
@@ -33,5 +33,5 @@ export const action = (program) => async (options) => {
33
33
  await setupBashPreExec();
34
34
  }
35
35
  await loadAliases(shell);
36
- await render(shell, options.test ?? false, options.login ?? false);
36
+ await render(program, shell, options.test ?? false, options.login ?? false);
37
37
  };
package/build/index.js CHANGED
@@ -26,7 +26,6 @@ program
26
26
  .option("-c, --check", `check if shell is in an inshellisense session`)
27
27
  .addOption(hiddenOption("-T, --test", "used to make e2e tests reproducible across machines"))
28
28
  .option("-V, --verbose", `enable verbose logging`)
29
- .showHelpAfterError("(add --help for additional information)")
30
29
  .passThroughOptions();
31
30
  program.addCommand(complete);
32
31
  program.addCommand(uninstall);
@@ -17,6 +17,7 @@ import log from "../utils/log.js";
17
17
  import { gitBashPath } from "../utils/shell.js";
18
18
  import styles from "ansi-styles";
19
19
  import * as ansi from "../utils/ansi.js";
20
+ import which from "which";
20
21
  const ISTermOnDataEvent = "data";
21
22
  export class ISTerm {
22
23
  pid;
@@ -282,10 +283,18 @@ export class ISTerm {
282
283
  return lines.reverse().join(ansi.cursorNextLine);
283
284
  }
284
285
  }
285
- export const spawn = async (options) => {
286
+ export const spawn = async (program, options) => {
286
287
  const { shellTarget, shellArgs } = await convertToPtyTarget(options.shell, options.underTest, options.login);
288
+ if (!(await shellExists(shellTarget))) {
289
+ program.error(`shell not found on PATH: ${shellTarget}`, { exitCode: 1 });
290
+ }
287
291
  return new ISTerm({ ...options, shellTarget, shellArgs });
288
292
  };
293
+ const shellExists = async (shellTarget) => {
294
+ const fileExists = fs.existsSync(shellTarget);
295
+ const fileOnPath = await which(shellTarget, { nothrow: true });
296
+ return fileExists || fileOnPath != null;
297
+ };
289
298
  const convertToPtyTarget = async (shell, underTest, login) => {
290
299
  const platform = os.platform();
291
300
  const shellTarget = shell == Shell.Bash && platform == "win32" ? await gitBashPath() : platform == "win32" ? `${shell}.exe` : shell;
@@ -10,7 +10,7 @@ const platform = os.platform();
10
10
  const executeShellCommand = await buildExecuteShellCommand(5000);
11
11
  const loadBashAliases = async () => {
12
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() });
13
+ const { stdout, stderr, status } = await executeShellCommand({ command: shellTarget, args: ["-i", "-c", "alias"], cwd: process.cwd(), env: { ISTERM: "1" } });
14
14
  if (status !== 0) {
15
15
  log.debug({ msg: "failed to load bash aliases", stderr, status });
16
16
  return;
@@ -24,7 +24,7 @@ const loadBashAliases = async () => {
24
24
  });
25
25
  };
26
26
  const loadZshAliases = async () => {
27
- const { stdout, stderr, status } = await executeShellCommand({ command: Shell.Zsh, args: ["-i", "-c", "alias"], cwd: process.cwd() });
27
+ const { stdout, stderr, status } = await executeShellCommand({ command: Shell.Zsh, args: ["-i", "-c", "alias"], cwd: process.cwd(), env: { ISTERM: "1" } });
28
28
  if (status !== 0) {
29
29
  log.debug({ msg: "failed to load zsh aliases", stderr, status });
30
30
  return;
@@ -1,10 +1,11 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
  import chalk from "chalk";
4
- import { checkLegacyConfigs, checkShellConfigs } from "../utils/shell.js";
4
+ import { checkLegacyConfigs, checkShellConfigPlugin, checkShellConfigs } from "../utils/shell.js";
5
5
  export const render = async () => {
6
6
  let errors = 0;
7
7
  errors += await renderLegacyConfigIssues();
8
+ errors += await renderShellPluginIssues();
8
9
  errors += renderShellConfigIssues();
9
10
  process.exit(errors);
10
11
  };
@@ -39,3 +40,30 @@ const renderShellConfigIssues = () => {
39
40
  }
40
41
  return 0;
41
42
  };
43
+ const renderShellPluginIssues = async () => {
44
+ const { shellsWithoutPlugin, shellsWithBadPlugin } = await checkShellConfigPlugin();
45
+ if (shellsWithoutPlugin.length == 0) {
46
+ process.stdout.write(chalk.green("✓") + " all shells have plugins\n");
47
+ }
48
+ else {
49
+ process.stderr.write(chalk.red("•") + " the following shells do not have the plugin installed:\n");
50
+ shellsWithoutPlugin.forEach((shell) => {
51
+ process.stderr.write(chalk.red(" - ") + shell + "\n");
52
+ });
53
+ process.stderr.write(chalk.yellow(" review the README to generate the missing shell plugins, this warning can be ignored if you prefer manual startup\n"));
54
+ }
55
+ if (shellsWithBadPlugin.length == 0) {
56
+ process.stdout.write(chalk.green("✓") + " all shells have correct plugins\n");
57
+ }
58
+ else {
59
+ process.stderr.write(chalk.red("•") + " the following shells have plugins incorrectly installed:\n");
60
+ shellsWithBadPlugin.forEach((shell) => {
61
+ process.stderr.write(chalk.red(" - ") + shell + "\n");
62
+ });
63
+ process.stderr.write(chalk.yellow(" remove and regenerate the plugins according to the README, only whitespace can be after the shell plugins\n"));
64
+ }
65
+ if (shellsWithoutPlugin.length > 0 || shellsWithBadPlugin.length > 0) {
66
+ return 1;
67
+ }
68
+ return 0;
69
+ };
@@ -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, login) => {
16
- const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest, login });
15
+ export const render = async (program, shell, underTest, login) => {
16
+ const term = await isterm.spawn(program, { 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;
@@ -12,6 +12,17 @@ import util from "node:util";
12
12
  import childProcess from "node:child_process";
13
13
  import log from "./log.js";
14
14
  const exec = util.promisify(childProcess.exec);
15
+ const safeExec = async (command, options) => {
16
+ const defaultOptions = { timeout: 500, env: { ISTERM: "1" } };
17
+ try {
18
+ const { stdout, stderr } = await exec(command, { ...defaultOptions, ...options });
19
+ return { stdout, stderr };
20
+ }
21
+ catch (e) {
22
+ log.debug({ msg: `error executing exec command: ${e}` });
23
+ return { stdout: undefined, stderr: undefined };
24
+ }
25
+ };
15
26
  export var Shell;
16
27
  (function (Shell) {
17
28
  Shell["Bash"] = "bash";
@@ -64,14 +75,33 @@ export const checkLegacyConfigs = async () => {
64
75
  }
65
76
  return shellsWithLegacyConfig;
66
77
  };
78
+ export const checkShellConfigPlugin = async () => {
79
+ const shellsWithoutPlugin = [];
80
+ const shellsWithBadPlugin = [];
81
+ for (const shell of supportedShells) {
82
+ const profilePath = await getProfilePath(shell);
83
+ if (profilePath != null && fs.existsSync(profilePath)) {
84
+ const profile = await fsAsync.readFile(profilePath, "utf8");
85
+ const profileContainsSource = profile.includes(getShellSourceCommand(shell));
86
+ const profileEndsWithSource = profile.trimEnd().endsWith(getShellSourceCommand(shell));
87
+ if (!profileContainsSource) {
88
+ shellsWithoutPlugin.push(shell);
89
+ }
90
+ else if (!profileEndsWithSource) {
91
+ shellsWithBadPlugin.push(shell);
92
+ }
93
+ }
94
+ }
95
+ return { shellsWithoutPlugin, shellsWithBadPlugin };
96
+ };
67
97
  const getProfilePath = async (shell) => {
68
98
  switch (shell) {
69
99
  case Shell.Bash:
70
100
  return path.join(os.homedir(), ".bashrc");
71
101
  case Shell.Powershell:
72
- return (await exec(`echo $profile`, { shell })).stdout.trim();
102
+ return (await safeExec(`echo $profile`, { shell })).stdout?.trim();
73
103
  case Shell.Pwsh:
74
- return (await exec(`echo $profile`, { shell })).stdout.trim();
104
+ return (await safeExec(`echo $profile`, { shell })).stdout?.trim();
75
105
  case Shell.Zsh:
76
106
  return path.join(os.homedir(), ".zshrc");
77
107
  case Shell.Fish:
@@ -79,7 +109,7 @@ const getProfilePath = async (shell) => {
79
109
  case Shell.Xonsh:
80
110
  return path.join(os.homedir(), ".xonshrc");
81
111
  case Shell.Nushell:
82
- return (await exec(`echo $nu.env-path`, { shell })).stdout.trim();
112
+ return (await safeExec(`echo $nu.env-path`, { shell })).stdout?.trim();
83
113
  }
84
114
  };
85
115
  export const createShellConfigs = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microsoft/inshellisense",
3
- "version": "0.0.1-rc.19",
3
+ "version": "0.0.1-rc.20",
4
4
  "description": "IDE style command line auto complete",
5
5
  "type": "module",
6
6
  "engines": {