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

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
@@ -8,7 +8,7 @@
8
8
 
9
9
  ### Requirements
10
10
 
11
- - `node >= 16.x`
11
+ - `21 < node >= 16.x`
12
12
  - node-gyp dependencies installed for your platform (see [node-gyp](https://github.com/nodejs/node-gyp) for more details)
13
13
 
14
14
  ### Installation
@@ -28,5 +28,5 @@ export const action = (program) => async (options) => {
28
28
  else if (shell == Shell.Bash) {
29
29
  await setupBashPreExec();
30
30
  }
31
- await render(shell);
31
+ await render(shell, options.test ?? false);
32
32
  };
package/build/index.js CHANGED
@@ -2,12 +2,17 @@
2
2
  // Copyright (c) Microsoft Corporation.
3
3
  // Licensed under the MIT License.
4
4
  /* eslint-disable header/header */
5
- import { Command } from "commander";
5
+ import { Command, Option } from "commander";
6
6
  import complete from "./commands/complete.js";
7
7
  import uninstall from "./commands/uninstall.js";
8
8
  import { action, supportedShells } from "./commands/root.js";
9
9
  import { getVersion } from "./utils/version.js";
10
10
  const program = new Command();
11
+ const hiddenOption = (flags, description) => {
12
+ const option = new Option(flags, description);
13
+ option.hidden = true;
14
+ return option;
15
+ };
11
16
  program
12
17
  .name("inshellisense")
13
18
  .description("IDE style command line auto complete")
@@ -15,6 +20,7 @@ program
15
20
  .action(action(program))
16
21
  .option("-s, --shell <shell>", `shell to use for command execution, supported shells: ${supportedShells}`)
17
22
  .option("-c, --check", `check if shell is in an inshellisense session`)
23
+ .addOption(hiddenOption("-T, --test", "used to make e2e tests reproducible across machines"))
18
24
  .option("-V, --verbose", `enable verbose logging`)
19
25
  .showHelpAfterError("(add --help for additional information)");
20
26
  program.addCommand(complete);
@@ -96,7 +96,7 @@ export class CommandManager {
96
96
  }
97
97
  }
98
98
  if (this.#shell == Shell.Cmd) {
99
- return lineText.match(/^(?<prompt>(\(.+\)\s)?(?:[A-Z]:\\.*>))/)?.groups?.prompt;
99
+ return lineText.match(/^(?<prompt>(\(.+\)\s)?(?:[A-Z]:\\.*>)|(> ))/)?.groups?.prompt;
100
100
  }
101
101
  // Custom prompts like starship end in the common \u276f character
102
102
  const customPrompt = lineText.match(/.*\u276f(?=[^\u276f]*$)/g)?.[0];
@@ -119,12 +119,15 @@ export class CommandManager {
119
119
  }
120
120
  _isSuggestion(cell) {
121
121
  const color = cell?.getFgColor();
122
- const dullColor = color == 8 || (color ?? 0) > 235;
122
+ const dim = (cell?.isDim() ?? 0) > 0;
123
+ const italic = (cell?.isItalic() ?? 0) > 0;
124
+ const dullColor = color == 8 || color == 7 || (color ?? 0) > 235 || (color == 15 && dim);
125
+ const dullItalic = (color ?? 0) > 235 || (dullColor && italic);
123
126
  if (this.#shell == Shell.Powershell) {
124
127
  return false;
125
128
  }
126
129
  else if (this.#shell == Shell.Pwsh) {
127
- return (color ?? 0) > 235;
130
+ return dullItalic;
128
131
  }
129
132
  return dullColor;
130
133
  }
@@ -141,16 +144,15 @@ export class CommandManager {
141
144
  if (this.#activeCommand.promptEndMarker == null || this.#activeCommand.promptStartMarker == null) {
142
145
  return;
143
146
  }
144
- const promptEndMarker = this.#activeCommand.promptEndMarker;
145
- const promptStartMarker = this.#activeCommand.promptStartMarker;
146
147
  const globalCursorPosition = this.#terminal.buffer.active.baseY + this.#terminal.buffer.active.cursorY;
147
148
  const withinPollDistance = globalCursorPosition < this.#activeCommand.promptEndMarker.line + 5;
148
- if (globalCursorPosition < promptStartMarker.line) {
149
+ if (globalCursorPosition < this.#activeCommand.promptStartMarker.line) {
149
150
  this.handleClear();
151
+ this.#activeCommand.promptEndMarker = this.#terminal.registerMarker(0);
150
152
  }
151
153
  // if we haven't fond the prompt yet, poll over the next 5 lines searching for it
152
154
  if (this.#activeCommand.promptText == null && withinPollDistance) {
153
- for (let i = globalCursorPosition; i < promptEndMarker.line + maxPromptPollDistance; i++) {
155
+ for (let i = globalCursorPosition; i < this.#activeCommand.promptEndMarker.line + maxPromptPollDistance; i++) {
154
156
  if (this.#previousCommandLines.has(i))
155
157
  continue;
156
158
  const promptResult = this._getWindowsPrompt(i);
@@ -159,17 +161,18 @@ export class CommandManager {
159
161
  this.#activeCommand.promptEndX = promptResult.length;
160
162
  this.#activeCommand.promptText = promptResult;
161
163
  this.#previousCommandLines.add(i);
164
+ break;
162
165
  }
163
166
  }
164
167
  }
165
168
  // if the prompt is set, now parse out the values from the terminal
166
169
  if (this.#activeCommand.promptText != null) {
167
- let lineY = promptEndMarker.line;
168
- let line = this.#terminal.buffer.active.getLine(promptEndMarker.line);
170
+ let lineY = this.#activeCommand.promptEndMarker.line;
171
+ let line = this.#terminal.buffer.active.getLine(this.#activeCommand.promptEndMarker.line);
169
172
  let command = "";
170
173
  let suggestions = "";
171
174
  for (;;) {
172
- for (let i = lineY == promptEndMarker.line ? this.#activeCommand.promptText.length : 0; i < this.#terminal.cols; i++) {
175
+ for (let i = lineY == this.#activeCommand.promptEndMarker.line ? this.#activeCommand.promptText.length : 0; i < this.#terminal.cols; i++) {
173
176
  const cell = line?.getCell(i);
174
177
  if (cell == null)
175
178
  continue;
@@ -5,7 +5,7 @@ import process from "node:process";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import url from "node:url";
8
- import pty from "node-pty";
8
+ import pty from "@homebridge/node-pty-prebuilt-multiarch";
9
9
  import { Shell, userZdotdir, zdotdir } from "../utils/shell.js";
10
10
  import { IsTermOscPs, IstermOscPt, IstermPromptStart, IstermPromptEnd } from "../utils/ansi.js";
11
11
  import xterm from "xterm-headless";
@@ -30,13 +30,13 @@ export class ISTerm {
30
30
  #term;
31
31
  #commandManager;
32
32
  #shell;
33
- constructor({ shell, cols, rows, env, shellTarget, shellArgs }) {
33
+ constructor({ shell, cols, rows, env, shellTarget, shellArgs, underTest }) {
34
34
  this.#pty = pty.spawn(shellTarget, shellArgs ?? [], {
35
35
  name: "xterm-256color",
36
36
  cols,
37
37
  rows,
38
38
  cwd: process.cwd(),
39
- env: { ...convertToPtyEnv(shell), ...env },
39
+ env: { ...convertToPtyEnv(shell, underTest), ...env },
40
40
  });
41
41
  this.pid = this.#pty.pid;
42
42
  this.cols = this.#pty.cols;
@@ -62,6 +62,10 @@ export class ISTerm {
62
62
  };
63
63
  this.onExit = this.#pty.onExit;
64
64
  }
65
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
+ on(_event, _listener) {
67
+ throw new Error("Method not implemented as deprecated in node-pty.");
68
+ }
65
69
  _deserializeIsMessage(message) {
66
70
  return message.replaceAll(/\\(\\|x([0-9a-f]{2}))/gi, (_match, op, hex) => (hex ? String.fromCharCode(parseInt(hex, 16)) : op));
67
71
  }
@@ -221,7 +225,8 @@ const convertToPtyTarget = async (shell) => {
221
225
  case Shell.Bash:
222
226
  shellArgs = ["--init-file", path.join(shellFolderPath, "shellIntegration.bash")];
223
227
  break;
224
- case (Shell.Powershell, Shell.Pwsh):
228
+ case Shell.Powershell:
229
+ case Shell.Pwsh:
225
230
  shellArgs = ["-noexit", "-command", `try { . "${path.join(shellFolderPath, "shellIntegration.ps1")}" } catch {}`];
226
231
  break;
227
232
  case Shell.Fish:
@@ -230,13 +235,17 @@ const convertToPtyTarget = async (shell) => {
230
235
  }
231
236
  return { shellTarget, shellArgs };
232
237
  };
233
- const convertToPtyEnv = (shell) => {
238
+ const convertToPtyEnv = (shell, underTest) => {
234
239
  const env = {
235
240
  ...process.env,
236
241
  ISTERM: "1",
242
+ ISTERM_TESTING: underTest ? "1" : undefined,
237
243
  };
238
244
  switch (shell) {
239
245
  case Shell.Cmd: {
246
+ if (underTest) {
247
+ return { ...env, PROMPT: `${IstermPromptStart}$G ${IstermPromptEnd}` };
248
+ }
240
249
  const prompt = process.env.PROMPT ? process.env.PROMPT : "$P$G";
241
250
  return { ...env, PROMPT: `${IstermPromptStart}${prompt}${IstermPromptEnd}` };
242
251
  }
@@ -14,6 +14,9 @@ speclist.forEach((s) => {
14
14
  let activeSet = specSet;
15
15
  const specRoutes = s.split("/");
16
16
  specRoutes.forEach((route, idx) => {
17
+ if (typeof activeSet !== "object") {
18
+ return;
19
+ }
17
20
  if (idx === specRoutes.length - 1) {
18
21
  const prefix = versionedSpeclist.includes(s) ? "/index.js" : `.js`;
19
22
  activeSet[route] = `@withfig/autocomplete/build/${s}${prefix}`;
@@ -93,12 +96,12 @@ const getSubcommand = (spec) => {
93
96
  };
94
97
  const executeShellCommand = buildExecuteShellCommand(5000);
95
98
  const genSubcommand = async (command, parentCommand) => {
96
- const subcommandIdx = parentCommand.subcommands?.findIndex((s) => s.name === command);
97
- if (subcommandIdx == null)
99
+ if (!parentCommand.subcommands || parentCommand.subcommands.length === 0)
98
100
  return;
99
- const subcommand = parentCommand.subcommands?.at(subcommandIdx);
100
- if (subcommand == null)
101
+ const subcommandIdx = parentCommand.subcommands.findIndex((s) => (Array.isArray(s.name) ? s.name.includes(command) : s.name === command));
102
+ if (subcommandIdx === -1)
101
103
  return;
104
+ const subcommand = parentCommand.subcommands[subcommandIdx];
102
105
  // this pulls in the spec from the load spec and overwrites the subcommand in the parent with the loaded spec.
103
106
  // then it returns the subcommand and clears the loadSpec field so that it doesn't get called again
104
107
  switch (typeof subcommand.loadSpec) {
@@ -4,7 +4,7 @@ import { getSuggestions } from "../runtime/runtime.js";
4
4
  import { renderBox, truncateText, truncateMultilineText } from "./utils.js";
5
5
  import ansi from "ansi-escapes";
6
6
  import chalk from "chalk";
7
- import { parseKeystroke } from "../utils/ansi.js";
7
+ import log from "../utils/log.js";
8
8
  const maxSuggestions = 5;
9
9
  const suggestionWidth = 40;
10
10
  const descriptionWidth = 30;
@@ -29,6 +29,7 @@ export class SuggestionManager {
29
29
  const commandText = this.#term.getCommandState().commandText;
30
30
  if (!commandText) {
31
31
  this.#suggestBlob = undefined;
32
+ this.#activeSuggestionIdx = 0;
32
33
  return;
33
34
  }
34
35
  if (commandText == this.#command) {
@@ -37,6 +38,7 @@ export class SuggestionManager {
37
38
  this.#command = commandText;
38
39
  const suggestionBlob = await getSuggestions(commandText, this.#term.cwd, this.#shell);
39
40
  this.#suggestBlob = suggestionBlob;
41
+ this.#activeSuggestionIdx = 0;
40
42
  }
41
43
  _renderArgumentDescription(description, x) {
42
44
  if (!description)
@@ -60,10 +62,15 @@ export class SuggestionManager {
60
62
  return idx == activeSuggestionIdx ? chalk.bgHex(activeSuggestionBackgroundColor)(truncatedSuggestion) : truncatedSuggestion;
61
63
  }), suggestionWidth, x);
62
64
  }
65
+ validate(suggestion) {
66
+ const commandText = this.#term.getCommandState().commandText;
67
+ return !commandText ? { data: "", rows: 0 } : suggestion;
68
+ }
63
69
  async render(remainingLines) {
64
70
  await this._loadSuggestions();
65
- if (!this.#suggestBlob)
71
+ if (!this.#suggestBlob) {
66
72
  return { data: "", rows: 0 };
73
+ }
67
74
  const { suggestions, argumentDescription } = this.#suggestBlob;
68
75
  const page = Math.min(Math.floor(this.#activeSuggestionIdx / maxSuggestions) + 1, Math.floor(suggestions.length / maxSuggestions) + 1);
69
76
  const pagedSuggestions = suggestions.filter((_, idx) => idx < page * maxSuggestions && idx >= (page - 1) * maxSuggestions);
@@ -112,20 +119,21 @@ export class SuggestionManager {
112
119
  rows,
113
120
  };
114
121
  }
115
- update(input) {
116
- const keyStroke = parseKeystroke(input);
117
- if (keyStroke == null)
122
+ update(keyPress) {
123
+ const { name } = keyPress;
124
+ if (!this.#suggestBlob) {
118
125
  return false;
119
- if (keyStroke == "esc") {
126
+ }
127
+ if (name == "escape") {
120
128
  this.#suggestBlob = undefined;
121
129
  }
122
- else if (keyStroke == "up") {
130
+ else if (name == "up") {
123
131
  this.#activeSuggestionIdx = Math.max(0, this.#activeSuggestionIdx - 1);
124
132
  }
125
- else if (keyStroke == "down") {
133
+ else if (name == "down") {
126
134
  this.#activeSuggestionIdx = Math.min(this.#activeSuggestionIdx + 1, (this.#suggestBlob?.suggestions.length ?? 1) - 1);
127
135
  }
128
- else if (keyStroke == "tab") {
136
+ else if (name == "tab") {
129
137
  const removals = "\u007F".repeat(this.#suggestBlob?.charactersToDrop ?? 0);
130
138
  const suggestion = this.#suggestBlob?.suggestions.at(this.#activeSuggestionIdx);
131
139
  const chars = suggestion?.insertValue ?? suggestion?.name + " ";
@@ -134,6 +142,10 @@ export class SuggestionManager {
134
142
  }
135
143
  this.#term.write(removals + chars);
136
144
  }
137
- return "handled";
145
+ else {
146
+ return false;
147
+ }
148
+ log.debug({ msg: "handled keypress", ...keyPress });
149
+ return true;
138
150
  }
139
151
  }
@@ -1,9 +1,10 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
+ import readline from "node:readline";
3
4
  import ansi from "ansi-escapes";
4
5
  import chalk from "chalk";
5
- import { inputModifier } from "./input.js";
6
6
  import log from "../utils/log.js";
7
+ import { Shell } from "../utils/shell.js";
7
8
  import isterm from "../isterm/index.js";
8
9
  import { eraseLinesBelow } from "../utils/ansi.js";
9
10
  import { SuggestionManager, MAX_LINES } from "./suggestionManager.js";
@@ -11,12 +12,14 @@ export const renderConfirmation = (live) => {
11
12
  const statusMessage = live ? chalk.green("live") : chalk.red("not found");
12
13
  return `inshellisense session [${statusMessage}]\n`;
13
14
  };
14
- export const render = async (shell) => {
15
- const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns });
15
+ export const render = async (shell, underTest) => {
16
+ const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest });
16
17
  const suggestionManager = new SuggestionManager(term, shell);
17
18
  let hasActiveSuggestions = false;
18
19
  let previousSuggestionsRows = 0;
19
- process.stdin.setRawMode(true);
20
+ if (process.stdin.isTTY)
21
+ process.stdin.setRawMode(true);
22
+ readline.emitKeypressEvents(process.stdin);
20
23
  const writeOutput = (data) => {
21
24
  log.debug({ msg: "writing data", data });
22
25
  process.stdout.write(data);
@@ -27,7 +30,7 @@ export const render = async (shell) => {
27
30
  // Considers when data includes newlines which have shifted the cursor position downwards
28
31
  const newlines = Math.max((data.match(/\r/g) || []).length, (data.match(/\n/g) || []).length);
29
32
  const linesOfInterest = MAX_LINES + newlines;
30
- if (term.getCursorState().remainingLines <= previousSuggestionsRows) {
33
+ if (term.getCursorState().remainingLines <= MAX_LINES) {
31
34
  // handles when suggestions get loaded before shell output so you need to always clear below output as a precaution
32
35
  if (term.getCursorState().remainingLines != 0) {
33
36
  writeOutput(ansi.cursorHide + ansi.cursorSavePosition + eraseLinesBelow(linesOfInterest + 1) + ansi.cursorRestorePosition);
@@ -47,12 +50,13 @@ export const render = async (shell) => {
47
50
  else {
48
51
  writeOutput(data);
49
52
  }
50
- setImmediate(async () => {
51
- const suggestion = await suggestionManager.render(term.getCursorState().remainingLines);
53
+ process.nextTick(async () => {
54
+ // validate result to prevent stale suggestion being provided
55
+ const suggestion = suggestionManager.validate(await suggestionManager.render(term.getCursorState().remainingLines));
52
56
  const commandState = term.getCommandState();
53
57
  if (suggestion.data != "" && commandState.cursorTerminated && !commandState.hasOutput) {
54
58
  if (hasActiveSuggestions) {
55
- if (term.getCursorState().remainingLines < suggestion.rows) {
59
+ if (term.getCursorState().remainingLines < MAX_LINES) {
56
60
  writeOutput(ansi.cursorHide +
57
61
  ansi.cursorSavePosition +
58
62
  ansi.cursorPrevLine.repeat(MAX_LINES) +
@@ -76,7 +80,7 @@ export const render = async (shell) => {
76
80
  }
77
81
  }
78
82
  else {
79
- if (term.getCursorState().remainingLines < suggestion.rows) {
83
+ if (term.getCursorState().remainingLines < MAX_LINES) {
80
84
  writeOutput(ansi.cursorHide + ansi.cursorSavePosition + ansi.cursorUp() + suggestion.data + ansi.cursorRestorePosition + ansi.cursorShow);
81
85
  }
82
86
  else {
@@ -92,7 +96,7 @@ export const render = async (shell) => {
92
96
  }
93
97
  else {
94
98
  if (hasActiveSuggestions) {
95
- if (term.getCursorState().remainingLines <= previousSuggestionsRows) {
99
+ if (term.getCursorState().remainingLines <= MAX_LINES) {
96
100
  writeOutput(ansi.cursorHide +
97
101
  ansi.cursorSavePosition +
98
102
  ansi.cursorPrevLine.repeat(MAX_LINES) +
@@ -109,13 +113,19 @@ export const render = async (shell) => {
109
113
  previousSuggestionsRows = suggestion.rows;
110
114
  });
111
115
  });
112
- process.stdin.on("data", (d) => {
113
- const suggestionResult = suggestionManager.update(d);
114
- if (previousSuggestionsRows > 0 && suggestionResult == "handled") {
116
+ process.stdin.on("keypress", (...keyPress) => {
117
+ const press = keyPress[1];
118
+ const inputHandled = suggestionManager.update(press);
119
+ if (previousSuggestionsRows > 0 && inputHandled) {
115
120
  term.noop();
116
121
  }
117
- else if (suggestionResult != "fully-handled") {
118
- term.write(inputModifier(d));
122
+ else if (!inputHandled) {
123
+ if (press.name == "backspace" && (shell === Shell.Pwsh || shell === Shell.Powershell || shell === Shell.Cmd)) {
124
+ term.write("\u007F");
125
+ }
126
+ else {
127
+ term.write(press.sequence);
128
+ }
119
129
  }
120
130
  });
121
131
  term.onExit(({ exitCode }) => {
@@ -1,10 +1,9 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
  const ESC = "\u001B";
4
- const CSI = "\u001B[";
4
+ const CSI = ESC + "[";
5
5
  const OSC = "\u001B]";
6
6
  const BEL = "\u0007";
7
- const SS3 = "\u001BO";
8
7
  export const IsTermOscPs = 6973;
9
8
  const IS_OSC = OSC + IsTermOscPs + ";";
10
9
  export var IstermOscPt;
@@ -32,25 +31,3 @@ export const scrollDown = (count = 1) => CSI + count + "T";
32
31
  export const eraseLinesBelow = (count = 1) => {
33
32
  return [...Array(count).keys()].map(() => cursorNextLine + eraseLine).join("");
34
33
  };
35
- export const parseKeystroke = (b) => {
36
- let s;
37
- if (b[0] > 127 && b[1] === undefined) {
38
- b[0] -= 128;
39
- s = "\u001B" + String(b);
40
- }
41
- else {
42
- s = String(b);
43
- }
44
- if (s == ESC) {
45
- return "esc";
46
- }
47
- else if (s == CSI + "A" || s == SS3 + "A") {
48
- return "up";
49
- }
50
- else if (s == CSI + "B" || s == SS3 + "B") {
51
- return "down";
52
- }
53
- else if (s == "\t") {
54
- return "tab";
55
- }
56
- };
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@microsoft/inshellisense",
3
- "version": "0.0.1-rc.6",
3
+ "version": "0.0.1-rc.8",
4
4
  "description": "IDE style command line auto complete",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": ">=16.6.0 <21.0.0"
8
+ },
6
9
  "bin": {
7
10
  "inshellisense": "./build/index.js",
8
11
  "is": "./build/index.js"
@@ -15,11 +18,13 @@
15
18
  ],
16
19
  "scripts": {
17
20
  "build": "tsc",
18
- "start": "node ./build/index.js",
21
+ "dev": "node --loader ts-node/esm src/index.ts -V",
19
22
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
23
+ "test:e2e": "tui-test",
20
24
  "lint": "eslint src/ --ext .ts,.tsx && prettier src/ --check",
21
25
  "lint:fix": "eslint src/ --ext .ts,.tsx --fix && prettier src/ --write",
22
- "debug": "node --inspect --loader ts-node/esm src/index.ts -V"
26
+ "debug": "node --inspect --loader ts-node/esm src/index.ts -V",
27
+ "pre-commit": "lint-staged"
23
28
  },
24
29
  "repository": {
25
30
  "type": "git",
@@ -34,14 +39,15 @@
34
39
  },
35
40
  "homepage": "https://github.com/microsoft/inshellisense#readme",
36
41
  "dependencies": {
37
- "@withfig/autocomplete": "^2.633.0",
42
+ "@homebridge/node-pty-prebuilt-multiarch": "^0.11.12",
43
+ "@microsoft/tui-test": "^0.0.1-rc.3",
44
+ "@withfig/autocomplete": "2.651.0",
38
45
  "ajv": "^8.12.0",
39
46
  "ansi-escapes": "^6.2.0",
40
47
  "ansi-styles": "^6.2.1",
41
48
  "chalk": "^5.3.0",
42
49
  "commander": "^11.0.0",
43
50
  "find-process": "^1.4.7",
44
- "node-pty": "^1.0.0",
45
51
  "which": "^4.0.0",
46
52
  "wrap-ansi": "^8.1.0",
47
53
  "xterm-headless": "^5.3.0"
@@ -58,10 +64,18 @@
58
64
  "eslint-config-prettier": "^9.0.0",
59
65
  "eslint-plugin-header": "^3.1.1",
60
66
  "eslint-plugin-react": "^7.33.2",
67
+ "husky": "^9.0.11",
61
68
  "jest": "^29.7.0",
69
+ "lint-staged": "^15.2.2",
62
70
  "prettier": "3.0.3",
63
71
  "ts-jest": "^29.1.1",
64
72
  "ts-node": "^10.9.2",
65
73
  "typescript": "^5.2.2"
74
+ },
75
+ "lint-staged": {
76
+ "{,src/**/}*.{ts,tsx}": [
77
+ "eslint --fix",
78
+ "prettier --write"
79
+ ]
66
80
  }
67
81
  }
@@ -36,15 +36,21 @@ __is_escape_value() {
36
36
  }
37
37
 
38
38
  __is_update_cwd() {
39
- builtin printf '\e]6973;CWD;%s\a' "$(__vsc_escape_value "${PWD}")"
39
+ builtin printf '\e]6973;CWD;%s\a' "$(__is_escape_value "${PWD}")"
40
40
  }
41
41
 
42
42
  __is_update_prompt() {
43
43
  __is_prior_prompt="$PS1"
44
- PS1="%{$(__is_prompt_start)%}$PS1%{$(__is_prompt_end)%}"
44
+ if [[ $ISTERM_TESTING == "1" ]]; then
45
+ __is_prior_prompt="> "
46
+ fi
47
+ PS1="%{$(__is_prompt_start)%}$__is_prior_prompt%{$(__is_prompt_end)%}"
45
48
  }
46
49
 
47
50
  __is_precmd() {
51
+ if [[ $PS1 != *"$(__is_prompt_start)"* ]]; then
52
+ __is_update_prompt
53
+ fi
48
54
  __is_update_cwd
49
55
  }
50
56
 
@@ -57,6 +57,9 @@ fi
57
57
  __is_update_prompt() {
58
58
  if [[ "$__is_custom_PS1" == "" || "$__is_custom_PS1" != "$PS1" ]]; then
59
59
  __is_original_PS1=$PS1
60
+ if [ $ISTERM_TESTING == "1" ]; then
61
+ __is_original_PS1="> "
62
+ fi
60
63
  __is_custom_PS1="\[$(__is_prompt_start)\]$__is_original_PS1\[$(__is_prompt_end)\]"
61
64
  export PS1="$__is_custom_PS1"
62
65
  fi
@@ -11,4 +11,9 @@ end
11
11
  function __is_update_cwd --on-event fish_prompt; set __is_cwd (__is_escape_value "$PWD"); printf "\e]6973;CWD;$__is_cwd\a"; end
12
12
 
13
13
  __is_copy_function fish_prompt is_user_prompt
14
+
15
+ if [ "$ISTERM_TESTING" = "1" ]
16
+ function is_user_prompt; printf '> '; end
17
+ end
18
+
14
19
  function fish_prompt; printf (__is_prompt_start); printf (is_user_prompt); printf (__is_prompt_end); end
@@ -1,5 +1,12 @@
1
1
  $Global:__IsOriginalPrompt = $function:Prompt
2
2
 
3
+ function Global:__IsTestingPrompt() {
4
+ return "PS > "
5
+ }
6
+ if ($env:ISTERM_TESTING -eq "1") {
7
+ $Global:__IsOriginalPrompt = $function:__IsTestingPrompt
8
+ }
9
+
3
10
  function Global:__IS-Escape-Value([string]$value) {
4
11
  [regex]::Replace($value, '[\\\n;]', { param($match)
5
12
  -Join (
package/build/ui/input.js DELETED
@@ -1,9 +0,0 @@
1
- // Copyright (c) Microsoft Corporation.
2
- // Licensed under the MIT License.
3
- export const inputModifier = (input) => {
4
- switch (input.toString()) {
5
- case "\b":
6
- return "\u007F"; // DEL
7
- }
8
- return input.toString();
9
- };