@microsoft/inshellisense 0.0.1-rc.13 → 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/build/commands/root.js +2 -2
- package/build/commands/specs/list.js +26 -0
- package/build/commands/specs/root.js +8 -0
- package/build/index.js +5 -1
- package/build/isterm/commandManager.js +4 -0
- package/build/isterm/pty.js +55 -18
- package/build/runtime/alias.js +1 -0
- package/build/runtime/runtime.js +3 -0
- package/build/ui/suggestionManager.js +3 -0
- package/build/ui/ui-root.js +2 -2
- package/build/utils/shell.js +25 -4
- package/package.json +4 -3
- package/shell/shellIntegration.bash +16 -2
package/build/commands/root.js
CHANGED
|
@@ -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
|
-
|
|
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);
|
|
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;
|
package/build/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Command, Option } from "commander";
|
|
|
6
6
|
import complete from "./commands/complete.js";
|
|
7
7
|
import uninstall from "./commands/uninstall.js";
|
|
8
8
|
import init from "./commands/init.js";
|
|
9
|
+
import specs from "./commands/specs/root.js";
|
|
9
10
|
import { action, supportedShells } from "./commands/root.js";
|
|
10
11
|
import { getVersion } from "./utils/version.js";
|
|
11
12
|
const program = new Command();
|
|
@@ -19,12 +20,15 @@ program
|
|
|
19
20
|
.description("IDE style command line auto complete")
|
|
20
21
|
.version(await getVersion(), "-v, --version", "output the current version")
|
|
21
22
|
.action(action(program))
|
|
23
|
+
.option("-l, --login", `start shell as a login shell`)
|
|
22
24
|
.option("-s, --shell <shell>", `shell to use for command execution, supported shells: ${supportedShells}`)
|
|
23
25
|
.option("-c, --check", `check if shell is in an inshellisense session`)
|
|
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);
|
|
29
32
|
program.addCommand(init);
|
|
33
|
+
program.addCommand(specs);
|
|
30
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;
|
package/build/isterm/pty.js
CHANGED
|
@@ -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() &&
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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) {
|
package/build/runtime/alias.js
CHANGED
package/build/runtime/runtime.js
CHANGED
|
@@ -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));
|
|
@@ -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
|
}
|
package/build/ui/ui-root.js
CHANGED
|
@@ -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) => {
|
|
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;
|
package/build/utils/shell.js
CHANGED
|
@@ -30,6 +30,7 @@ export const supportedShells = [
|
|
|
30
30
|
Shell.Nushell,
|
|
31
31
|
].filter((shell) => shell != null);
|
|
32
32
|
export const initSupportedShells = supportedShells.filter((shell) => shell != Shell.Cmd);
|
|
33
|
+
export const aliasSupportedShells = [Shell.Bash, Shell.Zsh];
|
|
33
34
|
export const userZdotdir = process.env?.ZDOTDIR ?? os.homedir() ?? `~`;
|
|
34
35
|
export const zdotdir = path.join(os.tmpdir(), `is-zsh`);
|
|
35
36
|
const configFolder = ".inshellisense";
|
|
@@ -103,9 +104,22 @@ export const getShellPromptRewrites = (shell) => shell == Shell.Nushell;
|
|
|
103
104
|
export const getShellConfig = (shell) => {
|
|
104
105
|
switch (shell) {
|
|
105
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`;
|
|
106
114
|
case Shell.Bash:
|
|
107
115
|
return `if [[ -z "\${ISTERM}" && $- = *i* && $- != *c* ]]; then
|
|
108
|
-
|
|
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
|
|
109
123
|
fi`;
|
|
110
124
|
case Shell.Powershell:
|
|
111
125
|
case Shell.Pwsh:
|
|
@@ -118,14 +132,21 @@ if ([string]::IsNullOrEmpty($env:ISTERM) -and [Environment]::UserInteractive -an
|
|
|
118
132
|
}`;
|
|
119
133
|
case Shell.Fish:
|
|
120
134
|
return `if test -z "$ISTERM" && status --is-interactive
|
|
121
|
-
is
|
|
135
|
+
if status --is-login
|
|
136
|
+
is -s fish --login ; kill %self
|
|
137
|
+
else
|
|
138
|
+
is -s fish ; kill %self
|
|
139
|
+
end
|
|
122
140
|
end`;
|
|
123
141
|
case Shell.Xonsh:
|
|
124
142
|
return `if 'ISTERM' not in \${...} and $XONSH_INTERACTIVE:
|
|
125
|
-
|
|
143
|
+
if $XONSH_LOGIN:
|
|
144
|
+
is -s xonsh --login ; exit
|
|
145
|
+
else:
|
|
146
|
+
is -s xonsh ; exit`;
|
|
126
147
|
case Shell.Nushell:
|
|
127
148
|
return `if "ISTERM" not-in $env and $nu.is-interactive {
|
|
128
|
-
is -s nu ; exit
|
|
149
|
+
if $nu.is-login { is -s nu --login ; exit } else { is -s nu ; exit }
|
|
129
150
|
}`;
|
|
130
151
|
}
|
|
131
152
|
return "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/inshellisense",
|
|
3
|
-
"version": "0.0.1-rc.
|
|
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 [ -
|
|
2
|
-
|
|
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
|