mirror of
https://github.com/wavetermdev/inshellisense.git
synced 2026-08-05 13:43:30 -07:00
fix: reduce generator failures, add cwd tracking, and improve failure logging (#193)
* fix: reduce generator failures, add cwd tracking, and improve failure logging Signed-off-by: Chapman Pendery <cpendery@vt.edu> * fix: update snapshot Signed-off-by: Chapman Pendery <cpendery@vt.edu> * ci: drop suggestion detection test for now Signed-off-by: Chapman Pendery <cpendery@vt.edu> * fix: add suggestion item Signed-off-by: Chapman Pendery <cpendery@vt.edu> * fix: truncation on generator suggestion Signed-off-by: Chapman Pendery <cpendery@vt.edu> --------- Signed-off-by: Chapman Pendery <cpendery@vt.edu>
This commit is contained in:
Generated
+62
@@ -18,6 +18,7 @@
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^11.0.0",
|
||||
"find-process": "^1.4.7",
|
||||
"wcwidth": "^1.0.1",
|
||||
"which": "^4.0.0",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"xterm-headless": "^5.3.0"
|
||||
@@ -30,6 +31,7 @@
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/react": "^18.2.24",
|
||||
"@types/wcwidth": "^1.0.2",
|
||||
"@types/which": "^3.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"@typescript-eslint/parser": "^6.7.4",
|
||||
@@ -2164,6 +2166,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
|
||||
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
|
||||
},
|
||||
"node_modules/@types/wcwidth": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/wcwidth/-/wcwidth-1.0.2.tgz",
|
||||
"integrity": "sha512-flVlIrTSL6Z0RS2/IQYP0sUDajwuZ21FQAdOjzitcraVAarBFBwBpixtvxMno56vzFvLAbaGZEhkmMVYmWVUQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/which": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.3.tgz",
|
||||
@@ -3216,6 +3224,14 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@@ -3452,6 +3468,17 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/defaults": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
|
||||
"integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
|
||||
"dependencies": {
|
||||
"clone": "^1.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz",
|
||||
@@ -8966,6 +8993,14 @@
|
||||
"makeerror": "1.0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/wcwidth": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
|
||||
"dependencies": {
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
|
||||
@@ -10901,6 +10936,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
|
||||
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
|
||||
},
|
||||
"@types/wcwidth": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/wcwidth/-/wcwidth-1.0.2.tgz",
|
||||
"integrity": "sha512-flVlIrTSL6Z0RS2/IQYP0sUDajwuZ21FQAdOjzitcraVAarBFBwBpixtvxMno56vzFvLAbaGZEhkmMVYmWVUQA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/which": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.3.tgz",
|
||||
@@ -11609,6 +11650,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@@ -11785,6 +11831,14 @@
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"dev": true
|
||||
},
|
||||
"defaults": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
|
||||
"integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
|
||||
"requires": {
|
||||
"clone": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"define-data-property": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz",
|
||||
@@ -15695,6 +15749,14 @@
|
||||
"makeerror": "1.0.12"
|
||||
}
|
||||
},
|
||||
"wcwidth": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
|
||||
"requires": {
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^11.0.0",
|
||||
"find-process": "^1.4.7",
|
||||
"wcwidth": "^1.0.1",
|
||||
"which": "^4.0.0",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"xterm-headless": "^5.3.0"
|
||||
@@ -56,6 +57,7 @@
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/react": "^18.2.24",
|
||||
"@types/wcwidth": "^1.0.2",
|
||||
"@types/which": "^3.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||
"@typescript-eslint/parser": "^6.7.4",
|
||||
|
||||
@@ -27,13 +27,14 @@ export const runGenerator = async (generator: Fig.Generator, tokens: string[], c
|
||||
if (script) {
|
||||
const shellInput = typeof script === "function" ? script(tokens) : script;
|
||||
const scriptOutput = Array.isArray(shellInput)
|
||||
? await executeShellCommand({ command: shellInput.at(0) ?? "", args: shellInput.slice(1) })
|
||||
: await executeShellCommand(shellInput);
|
||||
? await executeShellCommand({ command: shellInput.at(0) ?? "", args: shellInput.slice(1), cwd })
|
||||
: await executeShellCommand({ ...shellInput, cwd });
|
||||
|
||||
const scriptStdout = scriptOutput.stdout.trim();
|
||||
if (postProcess) {
|
||||
suggestions.push(...postProcess(scriptOutput.stdout, tokens));
|
||||
suggestions.push(...postProcess(scriptStdout, tokens));
|
||||
} else if (splitOn) {
|
||||
suggestions.push(...scriptOutput.stdout.split(splitOn).map((s) => ({ name: s })));
|
||||
suggestions.push(...scriptStdout.split(splitOn).map((s) => ({ name: s })));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +52,8 @@ export const runGenerator = async (generator: Fig.Generator, tokens: string[], c
|
||||
}
|
||||
return suggestions;
|
||||
} catch (e) {
|
||||
log.debug({ msg: "generator failed", e, script, splitOn, template });
|
||||
const err = typeof e === "string" ? e : e instanceof Error ? e.message : e;
|
||||
log.debug({ msg: "generator failed", err, script, splitOn, template });
|
||||
}
|
||||
return suggestions;
|
||||
};
|
||||
|
||||
@@ -20,7 +20,11 @@ enum SuggestionIcons {
|
||||
Default = "📀",
|
||||
}
|
||||
|
||||
const getIcon = (suggestionType?: Fig.SuggestionType | undefined): string => {
|
||||
const getIcon = (icon: string | undefined, suggestionType: Fig.SuggestionType | undefined): string => {
|
||||
// TODO: enable fig icons once spacing is better
|
||||
// if (icon && /[^\u0000-\u00ff]/.test(icon)) {
|
||||
// return icon;
|
||||
// }
|
||||
switch (suggestionType) {
|
||||
case "arg":
|
||||
return SuggestionIcons.Argument;
|
||||
@@ -51,7 +55,7 @@ const toSuggestion = (suggestion: Fig.Suggestion, name?: string, type?: Fig.Sugg
|
||||
return {
|
||||
name: name ?? getLong(suggestion.name),
|
||||
description: suggestion.description,
|
||||
icon: getIcon(type ?? suggestion.type),
|
||||
icon: getIcon(suggestion.icon, type ?? suggestion.type),
|
||||
allNames: suggestion.name instanceof Array ? suggestion.name : [suggestion.name],
|
||||
priority: suggestion.priority ?? 50,
|
||||
insertValue: suggestion.insertValue,
|
||||
@@ -77,7 +81,7 @@ function filter<T extends Fig.BaseSuggestion & { name?: Fig.SingleOrArray<string
|
||||
? {
|
||||
name: matchedName,
|
||||
description: s.description,
|
||||
icon: getIcon(s.type ?? suggestionType),
|
||||
icon: getIcon(s.icon, s.type ?? suggestionType),
|
||||
allNames: s.name,
|
||||
priority: s.priority ?? 50,
|
||||
insertValue: s.insertValue,
|
||||
@@ -88,7 +92,7 @@ function filter<T extends Fig.BaseSuggestion & { name?: Fig.SingleOrArray<string
|
||||
? {
|
||||
name: s.name,
|
||||
description: s.description,
|
||||
icon: getIcon(s.type ?? suggestionType),
|
||||
icon: getIcon(s.icon, s.type ?? suggestionType),
|
||||
allNames: [s.name],
|
||||
priority: s.priority ?? 50,
|
||||
insertValue: s.insertValue,
|
||||
@@ -106,7 +110,7 @@ function filter<T extends Fig.BaseSuggestion & { name?: Fig.SingleOrArray<string
|
||||
? {
|
||||
name: matchedName,
|
||||
description: s.description,
|
||||
icon: getIcon(s.type ?? suggestionType),
|
||||
icon: getIcon(s.icon, s.type ?? suggestionType),
|
||||
allNames: s.name,
|
||||
insertValue: s.insertValue,
|
||||
priority: s.priority ?? 50,
|
||||
@@ -117,7 +121,7 @@ function filter<T extends Fig.BaseSuggestion & { name?: Fig.SingleOrArray<string
|
||||
? {
|
||||
name: s.name,
|
||||
description: s.description,
|
||||
icon: getIcon(s.type ?? suggestionType),
|
||||
icon: getIcon(s.icon, s.type ?? suggestionType),
|
||||
allNames: [s.name],
|
||||
insertValue: s.insertValue,
|
||||
priority: s.priority ?? 50,
|
||||
@@ -139,8 +143,14 @@ const generatorSuggestions = async (
|
||||
): Promise<Suggestion[]> => {
|
||||
const generators = generator instanceof Array ? generator : generator ? [generator] : [];
|
||||
const tokens = acceptedTokens.map((t) => t.token);
|
||||
if (partialCmd) tokens.push(partialCmd);
|
||||
const suggestions = (await Promise.all(generators.map((gen) => runGenerator(gen, tokens, cwd)))).flat();
|
||||
return filter<Fig.Suggestion>(suggestions, filterStrategy, partialCmd, undefined);
|
||||
return filter<Fig.Suggestion>(
|
||||
suggestions.map((suggestion) => ({ ...suggestion, priority: suggestion.priority ?? 60 })),
|
||||
filterStrategy,
|
||||
partialCmd,
|
||||
undefined,
|
||||
);
|
||||
};
|
||||
|
||||
const templateSuggestions = async (
|
||||
|
||||
@@ -6,12 +6,12 @@ import log from "../utils/log.js";
|
||||
|
||||
const filepathsTemplate = async (cwd: string): Promise<Fig.TemplateSuggestion[]> => {
|
||||
const files = await fsAsync.readdir(cwd, { withFileTypes: true });
|
||||
return files.filter((f) => f.isFile() || f.isDirectory()).map((f) => ({ name: f.name, priority: 90, context: { templateType: "filepaths" } }));
|
||||
return files.filter((f) => f.isFile() || f.isDirectory()).map((f) => ({ name: f.name, priority: 55, context: { templateType: "filepaths" } }));
|
||||
};
|
||||
|
||||
const foldersTemplate = async (cwd: string): Promise<Fig.TemplateSuggestion[]> => {
|
||||
const files = await fsAsync.readdir(cwd, { withFileTypes: true });
|
||||
return files.filter((f) => f.isDirectory()).map((f) => ({ name: f.name, priority: 90, context: { templateType: "folders" } }));
|
||||
return files.filter((f) => f.isDirectory()).map((f) => ({ name: f.name, priority: 55, context: { templateType: "folders" } }));
|
||||
};
|
||||
|
||||
// TODO: implement history template
|
||||
|
||||
@@ -414,6 +414,24 @@ exports[`parseCommand fullyTypedSuggestion 1`] = `
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand generatorUsingPartialInput 1`] = `
|
||||
{
|
||||
"charactersToDrop": 21,
|
||||
"suggestions": [
|
||||
{
|
||||
"allNames": [
|
||||
"Microsoft.Azure.WebJobs",
|
||||
],
|
||||
"description": "This package contains the runtime assemblies for Microsoft.Azure.WebJobs.Host. It also adds rich diagnostics capabilities which makes it easier to monitor the WebJobs in the dashboard. For more information, please visit http://go.microsoft.com/fwlink/?LinkID=320971",
|
||||
"icon": "📀",
|
||||
"insertValue": "Microsoft.Azure.WebJobs",
|
||||
"name": "Microsoft.Azure.WebJobs",
|
||||
"priority": 60,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand loadSpec 1`] = `
|
||||
{
|
||||
"charactersToDrop": 3,
|
||||
@@ -490,7 +508,7 @@ exports[`parseCommand optionsSuggestedAfterVariadicArg 1`] = `
|
||||
"icon": "📀",
|
||||
"insertValue": undefined,
|
||||
"name": "package-lock.json",
|
||||
"priority": 90,
|
||||
"priority": 55,
|
||||
},
|
||||
{
|
||||
"allNames": [
|
||||
|
||||
@@ -21,6 +21,7 @@ const testData = [
|
||||
{ name: "nestedNonCommands", command: "az az ", skip: true }, // TODO: fix skipped test
|
||||
{ name: "loadSpec", command: "aws acm add" },
|
||||
{ name: "noArgsArgumentGiven", command: "gcc lab ", maxSuggestions: 3 },
|
||||
{ name: "generatorUsingPartialInput", command: "dotnet add package Microsoft.Azure.WebJo", maxSuggestions: 1 },
|
||||
];
|
||||
|
||||
describe(`parseCommand`, () => {
|
||||
|
||||
@@ -162,7 +162,14 @@ shells.map((activeShell) => {
|
||||
await expect(terminal.getByText("clear")).toBeVisible();
|
||||
});
|
||||
|
||||
test.when([Shell.Zsh, Shell.Powershell].includes(activeShell), "command detection with suggestions", async ({ terminal }) => {
|
||||
test("proper overflow truncation in command", async ({ terminal }) => {
|
||||
await expect(terminal.getByText("> ")).toBeVisible();
|
||||
|
||||
terminal.write("dotnet add package Holoon.Newtonsoft");
|
||||
await expect(terminal.getByText("CanBeUndefi…│")).toBeVisible();
|
||||
});
|
||||
|
||||
test.skip("command detection with suggestions", async ({ terminal }) => {
|
||||
await expect(terminal.getByText("> ")).toBeVisible();
|
||||
|
||||
terminal.write(`dotnet add item\r`);
|
||||
|
||||
@@ -78,7 +78,7 @@ export class SuggestionManager {
|
||||
private _renderSuggestions(suggestions: Suggestion[], activeSuggestionIdx: number, x: number) {
|
||||
return renderBox(
|
||||
suggestions.map((suggestion, idx) => {
|
||||
const suggestionText = `${suggestion.icon} ${suggestion.name}`.padEnd(suggestionWidth - borderWidth, " ");
|
||||
const suggestionText = `${suggestion.icon} ${suggestion.name}`;
|
||||
const truncatedSuggestion = truncateText(suggestionText, suggestionWidth - 2);
|
||||
return idx == activeSuggestionIdx ? chalk.bgHex(activeSuggestionBackgroundColor)(truncatedSuggestion) : truncatedSuggestion;
|
||||
}),
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@
|
||||
import ansi from "ansi-escapes";
|
||||
import wrapAnsi from "wrap-ansi";
|
||||
import chalk from "chalk";
|
||||
import wcwdith from "wcwidth";
|
||||
|
||||
/**
|
||||
* Renders a box around the given rows
|
||||
@@ -40,6 +41,7 @@ export const truncateMultilineText = (description: string, width: number, maxHei
|
||||
*/
|
||||
export const truncateText = (text: string, width: number) => {
|
||||
const textPoints = [...text];
|
||||
const slicedText = textPoints.slice(0, width - 1);
|
||||
const wcOffset = Math.max(wcwdith(text) - textPoints.length, 0);
|
||||
const slicedText = textPoints.slice(0, width - 1 - wcOffset);
|
||||
return slicedText.length == textPoints.length ? text.padEnd(width) : (slicedText.join("") + "…").padEnd(width);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user