@microsoft/inshellisense 0.0.1-rc.4 → 0.0.1-rc.5
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 +4 -8
- package/build/commands/root.js +9 -29
- package/build/commands/uninstall.js +1 -1
- package/build/index.js +1 -6
- package/build/isterm/commandManager.js +212 -0
- package/build/isterm/index.js +4 -0
- package/build/isterm/pty.js +217 -0
- package/build/runtime/template.js +1 -1
- package/build/ui/input.js +7 -53
- package/build/ui/suggestionManager.js +119 -0
- package/build/ui/ui-root.js +109 -64
- package/build/ui/ui-uninstall.js +1 -3
- package/build/ui/utils.js +41 -0
- package/build/utils/ansi.js +55 -0
- package/build/utils/config.js +68 -0
- package/build/utils/log.js +22 -0
- package/build/utils/shell.js +68 -1
- package/package.json +9 -4
- package/shell/shellIntegration-env.zsh +9 -0
- package/shell/shellIntegration-login.zsh +4 -0
- package/shell/shellIntegration-profile.zsh +4 -0
- package/shell/shellIntegration-rc.zsh +19 -0
- package/shell/shellIntegration.bash +31 -0
- package/shell/shellIntegration.fish +6 -0
- package/shell/shellIntegration.ps1 +8 -0
- package/build/commands/bind.js +0 -12
- package/build/ui/suggestions.js +0 -84
- package/build/ui/ui-bind.js +0 -69
- package/build/ui/ui-init.js +0 -44
- package/build/utils/bindings.js +0 -236
- package/build/utils/cache.js +0 -21
- package/shell/key-bindings-powershell.ps1 +0 -27
- package/shell/key-bindings-pwsh.ps1 +0 -27
- package/shell/key-bindings.bash +0 -7
- package/shell/key-bindings.fish +0 -8
- package/shell/key-bindings.zsh +0 -10
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import { getSuggestions } from "../runtime/runtime.js";
|
|
4
|
+
import { renderBox, truncateText, truncateMultilineText } from "./utils.js";
|
|
5
|
+
import ansi from "ansi-escapes";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { parseKeystroke } from "../utils/ansi.js";
|
|
8
|
+
const maxSuggestions = 5;
|
|
9
|
+
const suggestionWidth = 40;
|
|
10
|
+
const descriptionWidth = 30;
|
|
11
|
+
const descriptionHeight = 5;
|
|
12
|
+
const borderWidth = 2;
|
|
13
|
+
const activeSuggestionBackgroundColor = "#7D56F4";
|
|
14
|
+
export const MAX_LINES = borderWidth + Math.max(maxSuggestions, descriptionHeight);
|
|
15
|
+
export class SuggestionManager {
|
|
16
|
+
#term;
|
|
17
|
+
#command;
|
|
18
|
+
#activeSuggestionIdx;
|
|
19
|
+
#suggestBlob;
|
|
20
|
+
constructor(terminal) {
|
|
21
|
+
this.#term = terminal;
|
|
22
|
+
this.#suggestBlob = { suggestions: [] };
|
|
23
|
+
this.#command = "";
|
|
24
|
+
this.#activeSuggestionIdx = 0;
|
|
25
|
+
}
|
|
26
|
+
async _loadSuggestions() {
|
|
27
|
+
const commandText = this.#term.getCommandState().commandText;
|
|
28
|
+
if (!commandText) {
|
|
29
|
+
this.#suggestBlob = undefined;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (commandText == this.#command) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.#command = commandText;
|
|
36
|
+
const suggestionBlob = await getSuggestions(commandText);
|
|
37
|
+
this.#suggestBlob = suggestionBlob;
|
|
38
|
+
}
|
|
39
|
+
_renderArgumentDescription(description, x) {
|
|
40
|
+
if (!description)
|
|
41
|
+
return "";
|
|
42
|
+
return renderBox([truncateText(description, descriptionWidth - borderWidth)], descriptionWidth, x);
|
|
43
|
+
}
|
|
44
|
+
_renderDescription(description, x) {
|
|
45
|
+
if (!description)
|
|
46
|
+
return "";
|
|
47
|
+
return renderBox(truncateMultilineText(description, descriptionWidth - borderWidth, descriptionHeight), descriptionWidth, x);
|
|
48
|
+
}
|
|
49
|
+
_renderSuggestions(suggestions, activeSuggestionIdx, x) {
|
|
50
|
+
return renderBox(suggestions.map((suggestion, idx) => {
|
|
51
|
+
const suggestionText = `${suggestion.icon} ${suggestion.name}`.padEnd(suggestionWidth - borderWidth, " ");
|
|
52
|
+
const truncatedSuggestion = truncateText(suggestionText, suggestionWidth - 2);
|
|
53
|
+
return idx == activeSuggestionIdx ? chalk.bgHex(activeSuggestionBackgroundColor)(truncatedSuggestion) : truncatedSuggestion;
|
|
54
|
+
}), suggestionWidth, x);
|
|
55
|
+
}
|
|
56
|
+
async render() {
|
|
57
|
+
await this._loadSuggestions();
|
|
58
|
+
if (!this.#suggestBlob)
|
|
59
|
+
return { data: "", columns: 0 };
|
|
60
|
+
const { suggestions, argumentDescription } = this.#suggestBlob;
|
|
61
|
+
const page = Math.min(Math.floor(this.#activeSuggestionIdx / maxSuggestions) + 1, Math.floor(suggestions.length / maxSuggestions) + 1);
|
|
62
|
+
const pagedSuggestions = suggestions.filter((_, idx) => idx < page * maxSuggestions && idx >= (page - 1) * maxSuggestions);
|
|
63
|
+
const activePagedSuggestionIndex = this.#activeSuggestionIdx % maxSuggestions;
|
|
64
|
+
const activeDescription = pagedSuggestions.at(activePagedSuggestionIndex)?.description || argumentDescription || "";
|
|
65
|
+
const wrappedPadding = this.#term.getCursorState().cursorX % this.#term.cols;
|
|
66
|
+
const maxPadding = activeDescription.length !== 0 ? this.#term.cols - suggestionWidth - descriptionWidth : this.#term.cols - suggestionWidth;
|
|
67
|
+
const swapDescription = wrappedPadding > maxPadding && activeDescription.length !== 0;
|
|
68
|
+
const swappedPadding = swapDescription ? Math.max(wrappedPadding - descriptionWidth, 0) : wrappedPadding;
|
|
69
|
+
const clampedLeftPadding = Math.min(Math.min(wrappedPadding, swappedPadding), maxPadding);
|
|
70
|
+
if (suggestions.length <= this.#activeSuggestionIdx) {
|
|
71
|
+
this.#activeSuggestionIdx = Math.max(suggestions.length - 1, 0);
|
|
72
|
+
}
|
|
73
|
+
if (pagedSuggestions.length == 0) {
|
|
74
|
+
if (argumentDescription != null) {
|
|
75
|
+
return {
|
|
76
|
+
data: ansi.cursorHide +
|
|
77
|
+
ansi.cursorUp(2) +
|
|
78
|
+
ansi.cursorForward(clampedLeftPadding) +
|
|
79
|
+
this._renderArgumentDescription(argumentDescription, clampedLeftPadding),
|
|
80
|
+
columns: 3,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return { data: "", columns: 0 };
|
|
84
|
+
}
|
|
85
|
+
const columnsUsed = pagedSuggestions.length + borderWidth;
|
|
86
|
+
const ui = swapDescription
|
|
87
|
+
? this._renderDescription(activeDescription, clampedLeftPadding) +
|
|
88
|
+
this._renderSuggestions(pagedSuggestions, activePagedSuggestionIndex, clampedLeftPadding + descriptionWidth)
|
|
89
|
+
: this._renderSuggestions(pagedSuggestions, activePagedSuggestionIndex, clampedLeftPadding) +
|
|
90
|
+
this._renderDescription(activeDescription, clampedLeftPadding + suggestionWidth);
|
|
91
|
+
return {
|
|
92
|
+
data: ansi.cursorHide + ansi.cursorUp(columnsUsed - 1) + ansi.cursorForward(clampedLeftPadding) + ui + ansi.cursorShow,
|
|
93
|
+
columns: columnsUsed,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
update(input) {
|
|
97
|
+
const keyStroke = parseKeystroke(input);
|
|
98
|
+
if (keyStroke == null)
|
|
99
|
+
return false;
|
|
100
|
+
if (keyStroke == "esc") {
|
|
101
|
+
this.#suggestBlob = undefined;
|
|
102
|
+
}
|
|
103
|
+
else if (keyStroke == "up") {
|
|
104
|
+
this.#activeSuggestionIdx = Math.max(0, this.#activeSuggestionIdx - 1);
|
|
105
|
+
}
|
|
106
|
+
else if (keyStroke == "down") {
|
|
107
|
+
this.#activeSuggestionIdx = Math.min(this.#activeSuggestionIdx + 1, (this.#suggestBlob?.suggestions.length ?? 1) - 1);
|
|
108
|
+
}
|
|
109
|
+
else if (keyStroke == "tab") {
|
|
110
|
+
const removals = "\u007F".repeat(this.#suggestBlob?.charactersToDrop ?? 0);
|
|
111
|
+
const chars = this.#suggestBlob?.suggestions.at(this.#activeSuggestionIdx)?.name + " ";
|
|
112
|
+
if (this.#suggestBlob == null || !chars.trim() || this.#suggestBlob?.suggestions.length == 0) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
this.#term.write(removals + chars);
|
|
116
|
+
}
|
|
117
|
+
return "handled";
|
|
118
|
+
}
|
|
119
|
+
}
|
package/build/ui/ui-root.js
CHANGED
|
@@ -1,71 +1,116 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
3
|
+
import { inputModifier } from "./input.js";
|
|
4
|
+
import log from "../utils/log.js";
|
|
5
|
+
import isterm from "../isterm/index.js";
|
|
6
|
+
import { eraseLinesBelow } from "../utils/ansi.js";
|
|
7
|
+
import ansi from "ansi-escapes";
|
|
8
|
+
import { SuggestionManager, MAX_LINES } from "./suggestionManager.js";
|
|
9
|
+
export const render = async (shell) => {
|
|
10
|
+
const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns });
|
|
11
|
+
const suggestionManager = new SuggestionManager(term);
|
|
12
|
+
let hasActiveSuggestions = false;
|
|
13
|
+
let previousSuggestionsColumns = 0;
|
|
14
|
+
process.stdin.setRawMode(true);
|
|
15
|
+
const writeOutput = (data) => {
|
|
16
|
+
log.debug({ msg: "writing data", data });
|
|
17
|
+
process.stdout.write(data);
|
|
18
|
+
};
|
|
19
|
+
writeOutput(ansi.clearTerminal);
|
|
20
|
+
term.onData((data) => {
|
|
21
|
+
const commandState = term.getCommandState();
|
|
22
|
+
if ((commandState.hasOutput || hasActiveSuggestions) && !commandState.persistentOutput) {
|
|
23
|
+
if (term.getCursorState().remainingLines < previousSuggestionsColumns) {
|
|
24
|
+
writeOutput(ansi.cursorHide +
|
|
25
|
+
ansi.cursorSavePosition +
|
|
26
|
+
ansi.cursorPrevLine.repeat(MAX_LINES) +
|
|
27
|
+
term.getCells(MAX_LINES, "above") +
|
|
28
|
+
ansi.cursorRestorePosition +
|
|
29
|
+
ansi.cursorShow +
|
|
30
|
+
data);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
writeOutput(ansi.cursorHide + ansi.cursorSavePosition + eraseLinesBelow(MAX_LINES + 1) + ansi.cursorRestorePosition + ansi.cursorShow + data);
|
|
34
|
+
}
|
|
24
35
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (key.ctrl && input.toLowerCase() == "d") {
|
|
28
|
-
uiResult = undefined;
|
|
29
|
-
exit();
|
|
30
|
-
}
|
|
31
|
-
if (key.return) {
|
|
32
|
-
setIsExiting(true);
|
|
36
|
+
else {
|
|
37
|
+
writeOutput(data);
|
|
33
38
|
}
|
|
39
|
+
setImmediate(async () => {
|
|
40
|
+
const suggestion = await suggestionManager.render();
|
|
41
|
+
const commandState = term.getCommandState();
|
|
42
|
+
if (suggestion.data != "" && commandState.cursorTerminated && !commandState.hasOutput) {
|
|
43
|
+
if (hasActiveSuggestions) {
|
|
44
|
+
if (term.getCursorState().remainingLines < suggestion.columns) {
|
|
45
|
+
writeOutput(ansi.cursorHide +
|
|
46
|
+
ansi.cursorSavePosition +
|
|
47
|
+
ansi.cursorPrevLine.repeat(MAX_LINES) +
|
|
48
|
+
term.getCells(MAX_LINES, "above") +
|
|
49
|
+
ansi.cursorRestorePosition +
|
|
50
|
+
ansi.cursorSavePosition +
|
|
51
|
+
ansi.cursorUp() +
|
|
52
|
+
suggestion.data +
|
|
53
|
+
ansi.cursorRestorePosition +
|
|
54
|
+
ansi.cursorShow);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const offset = MAX_LINES - suggestion.columns;
|
|
58
|
+
writeOutput(ansi.cursorHide +
|
|
59
|
+
ansi.cursorSavePosition +
|
|
60
|
+
eraseLinesBelow(MAX_LINES) +
|
|
61
|
+
(offset > 0 ? ansi.cursorUp(offset) : "") +
|
|
62
|
+
suggestion.data +
|
|
63
|
+
ansi.cursorRestorePosition +
|
|
64
|
+
ansi.cursorShow);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
if (term.getCursorState().remainingLines < suggestion.columns) {
|
|
69
|
+
writeOutput(ansi.cursorHide + ansi.cursorSavePosition + ansi.cursorUp() + suggestion.data + ansi.cursorRestorePosition + ansi.cursorShow);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
writeOutput(ansi.cursorHide +
|
|
73
|
+
ansi.cursorSavePosition +
|
|
74
|
+
ansi.cursorNextLine.repeat(suggestion.columns) +
|
|
75
|
+
suggestion.data +
|
|
76
|
+
ansi.cursorRestorePosition +
|
|
77
|
+
ansi.cursorShow);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
hasActiveSuggestions = true;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
if (hasActiveSuggestions) {
|
|
84
|
+
if (term.getCursorState().remainingLines < previousSuggestionsColumns) {
|
|
85
|
+
writeOutput(ansi.cursorHide +
|
|
86
|
+
ansi.cursorSavePosition +
|
|
87
|
+
ansi.cursorPrevLine.repeat(MAX_LINES) +
|
|
88
|
+
term.getCells(MAX_LINES, "above") +
|
|
89
|
+
ansi.cursorRestorePosition +
|
|
90
|
+
ansi.cursorShow);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
writeOutput(ansi.cursorHide + ansi.cursorSavePosition + eraseLinesBelow(MAX_LINES) + ansi.cursorRestorePosition + ansi.cursorShow);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
hasActiveSuggestions = false;
|
|
97
|
+
}
|
|
98
|
+
previousSuggestionsColumns = suggestion.columns;
|
|
99
|
+
});
|
|
34
100
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
101
|
+
process.stdin.on("data", (d) => {
|
|
102
|
+
const suggestionResult = suggestionManager.update(d);
|
|
103
|
+
if (previousSuggestionsColumns > 0 && suggestionResult == "handled") {
|
|
104
|
+
term.noop();
|
|
39
105
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}, [command]);
|
|
47
|
-
if (isExiting) {
|
|
48
|
-
return (React.createElement(Text, null,
|
|
49
|
-
Prompt,
|
|
50
|
-
command));
|
|
51
|
-
}
|
|
52
|
-
return (React.createElement(Box, { flexDirection: "column", ref: measureRef },
|
|
53
|
-
React.createElement(Box, null,
|
|
54
|
-
React.createElement(Text, null,
|
|
55
|
-
React.createElement(Input, { value: command, setValue: setCommand, prompt: Prompt, activeSuggestion: activeSuggestion, tabCompletionDropSize: tabCompletionDropSize }))),
|
|
56
|
-
React.createElement(Suggestions, { leftPadding: leftPadding, setActiveSuggestion: setActiveSuggestion, suggestions: suggestions })));
|
|
57
|
-
}
|
|
58
|
-
export const render = async (command) => {
|
|
59
|
-
uiResult = undefined;
|
|
60
|
-
const { waitUntilExit } = inkRender(React.createElement(UI, { startingCommand: command ?? "" }));
|
|
61
|
-
await waitUntilExit();
|
|
62
|
-
return uiResult;
|
|
63
|
-
};
|
|
64
|
-
function getLeftPadding(windowWidth, command) {
|
|
65
|
-
const wrappedText = wrapAnsi(command + "", windowWidth, {
|
|
66
|
-
trim: false,
|
|
67
|
-
hard: true,
|
|
106
|
+
else if (suggestionResult != "fully-handled") {
|
|
107
|
+
term.write(inputModifier(d));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
term.onExit(({ exitCode }) => {
|
|
111
|
+
process.exit(exitCode);
|
|
68
112
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
113
|
+
process.stdout.on("resize", () => {
|
|
114
|
+
term.resize(process.stdout.columns, process.stdout.rows);
|
|
115
|
+
});
|
|
116
|
+
};
|
package/build/ui/ui-uninstall.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import {
|
|
4
|
+
import { deleteConfigFolder } from "../utils/config.js";
|
|
5
5
|
export const render = async () => {
|
|
6
|
-
await unbindAll();
|
|
7
|
-
process.stdout.write(chalk.green("✓") + " successfully uninstalled all existing bindings \n");
|
|
8
6
|
deleteConfigFolder();
|
|
9
7
|
process.stdout.write(chalk.green("✓") + " successfully deleted the .inshellisense config folder \n");
|
|
10
8
|
process.stdout.write(chalk.magenta("•") + " to complete the uninstall, run the the command: " + chalk.underline(chalk.cyan("npm uninstall -g @microsoft/inshellisense")) + "\n");
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import ansi from "ansi-escapes";
|
|
4
|
+
import wrapAnsi from "wrap-ansi";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
/**
|
|
7
|
+
* Renders a box around the given rows
|
|
8
|
+
* @param rows the text content to be included in the box, must be <= width - 2
|
|
9
|
+
* @param width the max width of a row
|
|
10
|
+
* @param x the column to start the box at
|
|
11
|
+
*/
|
|
12
|
+
export const renderBox = (rows, width, x, borderColor) => {
|
|
13
|
+
const result = [];
|
|
14
|
+
const setColor = (text) => (borderColor ? chalk.hex(borderColor).apply(text) : text);
|
|
15
|
+
result.push(ansi.cursorTo(x) + setColor("┌" + "─".repeat(width - 2) + "┐") + ansi.cursorTo(x));
|
|
16
|
+
rows.forEach((row) => {
|
|
17
|
+
result.push(ansi.cursorDown() + setColor("│") + row + setColor("│") + ansi.cursorTo(x));
|
|
18
|
+
});
|
|
19
|
+
result.push(ansi.cursorDown() + setColor("└" + "─".repeat(width - 2) + "┘") + ansi.cursorTo(x));
|
|
20
|
+
return result.join("") + ansi.cursorUp(rows.length + 1);
|
|
21
|
+
};
|
|
22
|
+
export const truncateMultilineText = (description, width, maxHeight) => {
|
|
23
|
+
const wrappedText = wrapAnsi(description, width, {
|
|
24
|
+
trim: false,
|
|
25
|
+
hard: true,
|
|
26
|
+
});
|
|
27
|
+
const lines = wrappedText.split("\n");
|
|
28
|
+
const truncatedLines = lines.slice(0, maxHeight);
|
|
29
|
+
if (lines.length > maxHeight) {
|
|
30
|
+
truncatedLines[maxHeight - 1] = [...truncatedLines[maxHeight - 1]].slice(0, -1).join("") + "…";
|
|
31
|
+
}
|
|
32
|
+
return truncatedLines.map((line) => line.padEnd(width));
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Truncates the text to the given width
|
|
36
|
+
*/
|
|
37
|
+
export const truncateText = (text, width) => {
|
|
38
|
+
const textPoints = [...text];
|
|
39
|
+
const slicedText = textPoints.slice(0, width - 1);
|
|
40
|
+
return slicedText.length == textPoints.length ? text.padEnd(width) : (slicedText.join("") + "…").padEnd(width);
|
|
41
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
const ESC = "\u001B";
|
|
4
|
+
const CSI = "\u001B[";
|
|
5
|
+
const OSC = "\u001B]";
|
|
6
|
+
const BEL = "\u0007";
|
|
7
|
+
const SS3 = "\u001BO";
|
|
8
|
+
export const IsTermOscPs = 6973;
|
|
9
|
+
const IS_OSC = OSC + IsTermOscPs + ";";
|
|
10
|
+
export var IstermOscPt;
|
|
11
|
+
(function (IstermOscPt) {
|
|
12
|
+
IstermOscPt["PromptStarted"] = "PS";
|
|
13
|
+
IstermOscPt["PromptEnded"] = "PE";
|
|
14
|
+
})(IstermOscPt || (IstermOscPt = {}));
|
|
15
|
+
export const IstermPromptStart = IS_OSC + IstermOscPt.PromptStarted + BEL;
|
|
16
|
+
export const IstermPromptEnd = IS_OSC + IstermOscPt.PromptEnded + BEL;
|
|
17
|
+
export const cursorHide = CSI + "?25l";
|
|
18
|
+
export const cursorShow = CSI + "?25h";
|
|
19
|
+
export const cursorNextLine = CSI + "E";
|
|
20
|
+
export const eraseLine = CSI + "2K";
|
|
21
|
+
export const cursorBackward = (count = 1) => CSI + count + "D";
|
|
22
|
+
export const cursorTo = ({ x, y }) => {
|
|
23
|
+
return CSI + (y ?? "") + ";" + (x ?? "") + "H";
|
|
24
|
+
};
|
|
25
|
+
export const deleteLinesBelow = (count = 1) => {
|
|
26
|
+
return [...Array(count).keys()].map(() => CSI + "B" + CSI + "M").join("");
|
|
27
|
+
};
|
|
28
|
+
export const deleteLine = (count = 1) => CSI + count + "M";
|
|
29
|
+
export const scrollUp = (count = 1) => CSI + count + "S";
|
|
30
|
+
export const scrollDown = (count = 1) => CSI + count + "T";
|
|
31
|
+
export const eraseLinesBelow = (count = 1) => {
|
|
32
|
+
return [...Array(count).keys()].map(() => cursorNextLine + eraseLine).join("");
|
|
33
|
+
};
|
|
34
|
+
export const parseKeystroke = (b) => {
|
|
35
|
+
let s;
|
|
36
|
+
if (b[0] > 127 && b[1] === undefined) {
|
|
37
|
+
b[0] -= 128;
|
|
38
|
+
s = "\u001B" + String(b);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
s = String(b);
|
|
42
|
+
}
|
|
43
|
+
if (s == ESC) {
|
|
44
|
+
return "esc";
|
|
45
|
+
}
|
|
46
|
+
else if (s == CSI + "A" || s == SS3 + "A") {
|
|
47
|
+
return "up";
|
|
48
|
+
}
|
|
49
|
+
else if (s == CSI + "B" || s == SS3 + "B") {
|
|
50
|
+
return "down";
|
|
51
|
+
}
|
|
52
|
+
else if (s == "\t") {
|
|
53
|
+
return "tab";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import fsAsync from "node:fs/promises";
|
|
7
|
+
import _Ajv from "ajv";
|
|
8
|
+
const Ajv = _Ajv;
|
|
9
|
+
const ajv = new Ajv();
|
|
10
|
+
const configSchema = {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
promptRegex: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
bash: {
|
|
17
|
+
type: "object",
|
|
18
|
+
nullable: true,
|
|
19
|
+
properties: {
|
|
20
|
+
regex: { type: "string" },
|
|
21
|
+
postfix: { type: "string" },
|
|
22
|
+
},
|
|
23
|
+
required: ["regex", "postfix"],
|
|
24
|
+
},
|
|
25
|
+
pwsh: {
|
|
26
|
+
type: "object",
|
|
27
|
+
nullable: true,
|
|
28
|
+
properties: {
|
|
29
|
+
regex: { type: "string" },
|
|
30
|
+
postfix: { type: "string" },
|
|
31
|
+
},
|
|
32
|
+
required: ["regex", "postfix"],
|
|
33
|
+
},
|
|
34
|
+
powershell: {
|
|
35
|
+
type: "object",
|
|
36
|
+
nullable: true,
|
|
37
|
+
properties: {
|
|
38
|
+
regex: { type: "string" },
|
|
39
|
+
postfix: { type: "string" },
|
|
40
|
+
},
|
|
41
|
+
required: ["regex", "postfix"],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
nullable: true,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
};
|
|
49
|
+
const configFolder = ".inshellisense";
|
|
50
|
+
const cachePath = path.join(os.homedir(), configFolder, "config.json");
|
|
51
|
+
let globalConfig = {};
|
|
52
|
+
export const getConfig = () => globalConfig;
|
|
53
|
+
export const loadConfig = async (program) => {
|
|
54
|
+
if (fs.existsSync(cachePath)) {
|
|
55
|
+
const config = JSON.parse((await fsAsync.readFile(cachePath)).toString());
|
|
56
|
+
const isValid = ajv.validate(configSchema, config);
|
|
57
|
+
if (!isValid) {
|
|
58
|
+
program.error("inshellisense config is invalid: " + ajv.errorsText());
|
|
59
|
+
}
|
|
60
|
+
globalConfig = config;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
export const deleteConfigFolder = async () => {
|
|
64
|
+
const cliConfigPath = path.join(os.homedir(), configFolder);
|
|
65
|
+
if (fs.existsSync(cliConfigPath)) {
|
|
66
|
+
fs.rmSync(cliConfigPath, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import fsAsync from "node:fs/promises";
|
|
7
|
+
const logTarget = path.join(os.homedir(), ".inshellisense", "inshellisense.log");
|
|
8
|
+
const logEnabled = false;
|
|
9
|
+
const reset = async () => {
|
|
10
|
+
await fsAsync.writeFile(logTarget, "");
|
|
11
|
+
};
|
|
12
|
+
const debug = (content) => {
|
|
13
|
+
if (!logEnabled) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
fs.appendFile(logTarget, JSON.stringify(content) + "\n", (err) => {
|
|
17
|
+
if (err != null) {
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
export default { reset, debug };
|
package/build/utils/shell.js
CHANGED
|
@@ -2,9 +2,76 @@
|
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import find from "find-process";
|
|
5
|
-
import
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import which from "which";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import url from "node:url";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import fsAsync from "node:fs/promises";
|
|
11
|
+
export var Shell;
|
|
12
|
+
(function (Shell) {
|
|
13
|
+
Shell["Bash"] = "bash";
|
|
14
|
+
Shell["Powershell"] = "powershell";
|
|
15
|
+
Shell["Pwsh"] = "pwsh";
|
|
16
|
+
Shell["Zsh"] = "zsh";
|
|
17
|
+
Shell["Fish"] = "fish";
|
|
18
|
+
Shell["Cmd"] = "cmd";
|
|
19
|
+
})(Shell || (Shell = {}));
|
|
20
|
+
export const supportedShells = [Shell.Bash, process.platform == "win32" ? Shell.Powershell : null, Shell.Pwsh, Shell.Zsh, Shell.Fish].filter((shell) => shell != null);
|
|
21
|
+
export const userZdotdir = process.env?.ZDOTDIR ?? os.homedir() ?? `~`;
|
|
22
|
+
export const zdotdir = path.join(os.tmpdir(), `is-zsh`);
|
|
23
|
+
export const setupZshDotfiles = async () => {
|
|
24
|
+
const shellFolderPath = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "..", "..", "shell");
|
|
25
|
+
await fsAsync.cp(path.join(shellFolderPath, "shellIntegration-rc.zsh"), path.join(zdotdir, ".zshrc"));
|
|
26
|
+
await fsAsync.cp(path.join(shellFolderPath, "shellIntegration-profile.zsh"), path.join(zdotdir, ".zprofile"));
|
|
27
|
+
await fsAsync.cp(path.join(shellFolderPath, "shellIntegration-env.zsh"), path.join(zdotdir, ".zshenv"));
|
|
28
|
+
await fsAsync.cp(path.join(shellFolderPath, "shellIntegration-login.zsh"), path.join(zdotdir, ".zlogin"));
|
|
29
|
+
};
|
|
6
30
|
export const inferShell = async () => {
|
|
31
|
+
try {
|
|
32
|
+
const name = path.parse(process.env.SHELL ?? "").name;
|
|
33
|
+
const shellName = supportedShells.find((shell) => name.includes(shell));
|
|
34
|
+
if (shellName)
|
|
35
|
+
return shellName;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* empty */
|
|
39
|
+
}
|
|
7
40
|
const processResult = (await find("pid", process.ppid)).at(0);
|
|
8
41
|
const name = processResult?.name;
|
|
9
42
|
return name != null ? supportedShells.find((shell) => name.includes(shell)) : undefined;
|
|
10
43
|
};
|
|
44
|
+
export const gitBashPath = async () => {
|
|
45
|
+
const gitBashPaths = await getGitBashPaths();
|
|
46
|
+
for (const gitBashPath of gitBashPaths) {
|
|
47
|
+
if (fs.existsSync(gitBashPath)) {
|
|
48
|
+
return gitBashPath;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw new Error("unable to find a git bash executable installed");
|
|
52
|
+
};
|
|
53
|
+
const getGitBashPaths = async () => {
|
|
54
|
+
const gitDirs = new Set();
|
|
55
|
+
const gitExePath = await which("git.exe", { nothrow: true });
|
|
56
|
+
if (gitExePath) {
|
|
57
|
+
const gitExeDir = path.dirname(gitExePath);
|
|
58
|
+
gitDirs.add(path.resolve(gitExeDir, "../.."));
|
|
59
|
+
}
|
|
60
|
+
const addValid = (set, value) => {
|
|
61
|
+
if (value)
|
|
62
|
+
set.add(value);
|
|
63
|
+
};
|
|
64
|
+
// Add common git install locations
|
|
65
|
+
addValid(gitDirs, process.env["ProgramW6432"]);
|
|
66
|
+
addValid(gitDirs, process.env["ProgramFiles"]);
|
|
67
|
+
addValid(gitDirs, process.env["ProgramFiles(X86)"]);
|
|
68
|
+
addValid(gitDirs, `${process.env["LocalAppData"]}\\Program`);
|
|
69
|
+
const gitBashPaths = [];
|
|
70
|
+
for (const gitDir of gitDirs) {
|
|
71
|
+
gitBashPaths.push(`${gitDir}\\Git\\bin\\bash.exe`, `${gitDir}\\Git\\usr\\bin\\bash.exe`, `${gitDir}\\usr\\bin\\bash.exe`);
|
|
72
|
+
}
|
|
73
|
+
// Add special installs that don't follow the standard directory structure
|
|
74
|
+
gitBashPaths.push(`${process.env["UserProfile"]}\\scoop\\apps\\git\\current\\bin\\bash.exe`);
|
|
75
|
+
gitBashPaths.push(`${process.env["UserProfile"]}\\scoop\\apps\\git-with-openssh\\current\\bin\\bash.exe`);
|
|
76
|
+
return gitBashPaths;
|
|
77
|
+
};
|
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.5",
|
|
4
4
|
"description": "IDE style command line auto complete",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,17 +34,22 @@
|
|
|
34
34
|
"homepage": "https://github.com/microsoft/inshellisense#readme",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@withfig/autocomplete": "^2.633.0",
|
|
37
|
+
"ajv": "^8.12.0",
|
|
38
|
+
"ansi-escapes": "^6.2.0",
|
|
39
|
+
"ansi-styles": "^6.2.1",
|
|
37
40
|
"chalk": "^5.3.0",
|
|
38
41
|
"commander": "^11.0.0",
|
|
39
42
|
"find-process": "^1.4.7",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"wrap-ansi": "^8.1.0"
|
|
43
|
+
"node-pty": "^1.0.0",
|
|
44
|
+
"which": "^4.0.0",
|
|
45
|
+
"wrap-ansi": "^8.1.0",
|
|
46
|
+
"xterm-headless": "^5.3.0"
|
|
43
47
|
},
|
|
44
48
|
"devDependencies": {
|
|
45
49
|
"@tsconfig/node18": "^18.2.2",
|
|
46
50
|
"@types/jest": "^29.5.5",
|
|
47
51
|
"@types/react": "^18.2.24",
|
|
52
|
+
"@types/which": "^3.0.3",
|
|
48
53
|
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
|
49
54
|
"@typescript-eslint/parser": "^6.7.4",
|
|
50
55
|
"@withfig/autocomplete-types": "^1.28.0",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
if [[ -f $USER_ZDOTDIR/.zshrc ]]; then
|
|
2
|
+
ZDOTDIR=$USER_ZDOTDIR
|
|
3
|
+
. $USER_ZDOTDIR/.zshrc
|
|
4
|
+
fi
|
|
5
|
+
|
|
6
|
+
__is_prompt_start() {
|
|
7
|
+
builtin printf '\e]6973;PS\a'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
__is_prompt_end() {
|
|
11
|
+
builtin printf '\e]6973;PE\a'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
__is_update_prompt() {
|
|
15
|
+
__is_prior_prompt="$PS1"
|
|
16
|
+
PS1="%{$(__is_prompt_start)%}$PS1%{$(__is_prompt_end)%}"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
__is_update_prompt
|