You've already forked inshellisense
mirror of
https://github.com/wavetermdev/inshellisense.git
synced 2026-04-22 15:25:33 -07:00
feat: write & test command parser + tweak tsconfig for @/ imports
Signed-off-by: Chapman Pendery <cpendery@vt.edu>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
};
|
||||
Generated
+8381
File diff suppressed because it is too large
Load Diff
+6
-2
@@ -6,7 +6,8 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node ./build/index.js"
|
||||
"start": "node ./build/index.js",
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -30,8 +31,11 @@
|
||||
"devDependencies": {
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@types/ink": "^2.0.3",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/react": "^18.2.24",
|
||||
"@withfig/autocomplete-types": "^1.28.0",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import {render} from "../ui/ui.js"
|
||||
|
||||
import { render } from "@/ui/ui.js";
|
||||
|
||||
export const action = () => {
|
||||
render()
|
||||
}
|
||||
render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
type CommandToken = {
|
||||
token: string;
|
||||
complete: boolean;
|
||||
isOption: boolean;
|
||||
};
|
||||
|
||||
const cmdDelim = /(\|\|)|(&&)|(;)/;
|
||||
const spaceRegex = /\s/;
|
||||
|
||||
export const parseCommand = (command: string): CommandToken[] => {
|
||||
const lastCommand = command.split(cmdDelim).at(-1)?.trimStart();
|
||||
return lastCommand ? parse(lastCommand) : [];
|
||||
};
|
||||
|
||||
const parse = (command: string): CommandToken[] => {
|
||||
const tokens: CommandToken[] = [];
|
||||
let [readingQuotedString, readingFlag, readingCmd] = [false, false, false];
|
||||
let readingIdx = 0;
|
||||
let readingQuoteChar = "";
|
||||
|
||||
[...command].forEach((char, idx) => {
|
||||
const reading = readingQuotedString || readingFlag || readingCmd;
|
||||
if (!reading && (char === `'` || char === `"`)) {
|
||||
[readingQuotedString, readingIdx, readingQuoteChar] = [true, idx, char];
|
||||
return;
|
||||
} else if (!reading && char === `-`) {
|
||||
[readingFlag, readingIdx] = [true, idx];
|
||||
return;
|
||||
} else if (!reading && !spaceRegex.test(char)) {
|
||||
[readingCmd, readingIdx] = [true, idx];
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
readingQuotedString &&
|
||||
char === readingQuoteChar &&
|
||||
command.at(idx - 1) !== "\\"
|
||||
) {
|
||||
readingQuotedString = false;
|
||||
const complete =
|
||||
idx + 1 < command.length && spaceRegex.test(command[idx + 1]);
|
||||
tokens.push({
|
||||
token: command.slice(readingIdx, idx + 1),
|
||||
complete,
|
||||
isOption: false,
|
||||
});
|
||||
} else if ((readingFlag && spaceRegex.test(char)) || char === "=") {
|
||||
readingFlag = false;
|
||||
tokens.push({
|
||||
token: command.slice(readingIdx, idx),
|
||||
complete: true,
|
||||
isOption: true,
|
||||
});
|
||||
} else if (readingCmd && spaceRegex.test(char)) {
|
||||
readingCmd = false;
|
||||
tokens.push({
|
||||
token: command.slice(readingIdx, idx),
|
||||
complete: true,
|
||||
isOption: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const reading = readingQuotedString || readingFlag || readingCmd;
|
||||
if (reading) {
|
||||
tokens.push({
|
||||
token: command.slice(readingIdx),
|
||||
complete: false,
|
||||
isOption: false,
|
||||
});
|
||||
}
|
||||
|
||||
return tokens;
|
||||
};
|
||||
@@ -0,0 +1,321 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`parseCommand cmd 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd "value' 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": false,
|
||||
"isOption": false,
|
||||
"token": ""value' ",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd "value'\\"\\"" 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": ""value'\\"\\""",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd 'value' 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "'value'",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd --flag value 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "--flag",
|
||||
},
|
||||
{
|
||||
"complete": false,
|
||||
"isOption": false,
|
||||
"token": "value",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd --flag="value" 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "--flag",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": ""value"",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd --flag='value' 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "--flag",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "'value'",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd --flag=value 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "--flag",
|
||||
},
|
||||
{
|
||||
"complete": false,
|
||||
"isOption": false,
|
||||
"token": "value",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "-f",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f 'value' 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "-f",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "'value'",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": false,
|
||||
"isOption": false,
|
||||
"token": "-f",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f 2`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": false,
|
||||
"isOption": false,
|
||||
"token": "-f",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f value 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "-f",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "value",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f= 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "-f",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f="value" 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "-f",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": ""value"",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f='val 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "-f",
|
||||
},
|
||||
{
|
||||
"complete": false,
|
||||
"isOption": false,
|
||||
"token": "'val",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd -f=value 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": true,
|
||||
"token": "-f",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "value",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": false,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parseCommand cmd value 1`] = `
|
||||
[
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "cmd",
|
||||
},
|
||||
{
|
||||
"complete": true,
|
||||
"isOption": false,
|
||||
"token": "value",
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { parseCommand } from "../../runtime/parser";
|
||||
|
||||
const testData = [
|
||||
{ command: `cmd --flag value` },
|
||||
{ command: `cmd --flag=value` },
|
||||
{ command: `cmd --flag='value' ` },
|
||||
{ command: `cmd --flag="value" ` },
|
||||
{ command: `cmd 'value' ` },
|
||||
{ command: `cmd value ` },
|
||||
{ command: `cmd -f` },
|
||||
{ command: `cmd -f=value ` },
|
||||
{ command: `cmd -f value ` },
|
||||
{ command: `cmd -f 'value' ` },
|
||||
{ command: `cmd -f="value" ` },
|
||||
{ command: `cmd -f='val` },
|
||||
{ command: `cmd -f` },
|
||||
{ command: `cmd -f=` },
|
||||
{ command: `cmd -f ` },
|
||||
{ command: `cmd` },
|
||||
{ command: `cmd ` },
|
||||
{ command: `cmd "value' ` },
|
||||
{ command: `cmd "value'\\"\\"" ` },
|
||||
];
|
||||
|
||||
describe(`parseCommand`, () => {
|
||||
testData.forEach(({ command }) => {
|
||||
test(command, () => {
|
||||
expect(parseCommand(command)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
Text,
|
||||
Box,
|
||||
@@ -7,10 +7,10 @@ import {
|
||||
measureElement,
|
||||
DOMElement,
|
||||
} from "ink";
|
||||
import { getSuggestions } from "../runtime/runtime.js";
|
||||
import wrapAnsi from "wrap-ansi";
|
||||
import { getSuggestions } from "@/runtime/runtime.js";
|
||||
import Cursor from "./cursor.js";
|
||||
import Suggestions from "./suggestions.js";
|
||||
import wrapAnsi from "wrap-ansi";
|
||||
|
||||
const Prompt = "> ";
|
||||
|
||||
|
||||
+11
-7
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"include": ["src/**/*"],
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"outDir": "./build",
|
||||
"types": ["@withfig/autocomplete-types"]
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "build", "src/**/*.test.ts", "src/**/*.spec.ts"],
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"outDir": "./build",
|
||||
"types": ["@withfig/autocomplete-types", "jest"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user