Fix stuff, help command

This commit is contained in:
Daniel Kluge 2021-12-17 18:55:00 +01:00
parent a535c5b69a
commit c25a0e34e2
8 changed files with 77 additions and 19 deletions

View File

@ -11,7 +11,7 @@ const REPLHistory: NextPage<REPLHistoryParams> = ({history, inputRef}) => {
const focusInput = () => {if (inputRef.current) inputRef.current.focus();}; const focusInput = () => {if (inputRef.current) inputRef.current.focus();};
return <div className={styles.container} onClick={focusInput}> return <div className={styles.container} onClick={focusInput}>
{ history.map((value, idx) => <div className={styles.line} key={idx}>{value === "" ? "\u00A0" : value}</div>) } { history.map((value, idx) => <pre className={styles.line} key={idx}>{value === "" ? "\u00A0" : value}</pre>) }
</div>; </div>;
}; };

View File

@ -15,19 +15,34 @@ const REPLInput: NextPage<REPLInputParams> = ({historyCallback, inputRef}) => {
const [justTabbed, setJustTabbed] = React.useState<number>(0); const [justTabbed, setJustTabbed] = React.useState<number>(0);
const replinOnChange = (e: React.FormEvent<HTMLInputElement>) => { const replinOnChange = (e: React.FormEvent<HTMLInputElement>) => {
const currentInput = (e.target as HTMLInputElement).value.toLowerCase(); const input = (e.target as HTMLInputElement);
(e.target as HTMLInputElement).value = currentInput; const currentInput = input.value.toLowerCase();
const suggest = commandCompletion(currentInput); // Force lowercase
setCurrentCmd(suggest); input.value = currentInput;
if (suggest.length === 0) suggest.push("");
if (typed.current) typed.current.innerHTML = currentInput; if (currentInput.includes(" ")) {
if (completion.current) completion.current.innerHTML = suggest[0].substring(currentInput.length); // Command already typed
input.maxLength = 524288; // Default value
if (typed.current) typed.current.innerHTML = "";
if (completion.current) completion.current.innerHTML = "";
setCurrentCmd([]);
return;
} else {
input.maxLength = 20;
// Get completion hint
const suggest = commandCompletion(currentInput);
setCurrentCmd(suggest);
if (suggest.length === 0) suggest.push("");
if (typed.current) typed.current.innerHTML = currentInput;
if (completion.current) completion.current.innerHTML = suggest[0].substring(currentInput.length);
}
}; };
const tabComplete = (e: React.KeyboardEvent<HTMLInputElement>) => { const tabComplete = (e: React.KeyboardEvent<HTMLInputElement>) => {
const input = (e.target as HTMLInputElement);
if (e.key === "Tab" && currentCmd.length !== 0) { if (e.key === "Tab" && currentCmd.length !== 0) {
e.preventDefault(); e.preventDefault();
(e.target as HTMLInputElement).value = currentCmd[justTabbed % currentCmd.length]; input.value = currentCmd[justTabbed % currentCmd.length];
if(typed.current) typed.current.innerHTML = currentCmd[justTabbed % currentCmd.length]; if(typed.current) typed.current.innerHTML = currentCmd[justTabbed % currentCmd.length];
if(completion.current) completion.current.innerHTML = ""; if(completion.current) completion.current.innerHTML = "";
setJustTabbed(justTabbed + 1); setJustTabbed(justTabbed + 1);
@ -35,7 +50,7 @@ const REPLInput: NextPage<REPLInputParams> = ({historyCallback, inputRef}) => {
if (e.key === "Enter") { if (e.key === "Enter") {
const result = executeCommand((e.target as HTMLInputElement).value); const result = executeCommand((e.target as HTMLInputElement).value);
(e.target as HTMLInputElement).value = ""; input.value = "";
if(typed.current) typed.current.innerHTML = ""; if(typed.current) typed.current.innerHTML = "";
if(completion.current) completion.current.innerHTML = ""; if(completion.current) completion.current.innerHTML = "";
historyCallback(result); historyCallback(result);
@ -47,7 +62,7 @@ const REPLInput: NextPage<REPLInputParams> = ({historyCallback, inputRef}) => {
return <div className={styles.wrapperwrapper}> return <div className={styles.wrapperwrapper}>
<span className={styles.inputstart}>$&nbsp;</span> <span className={styles.inputstart}>$&nbsp;</span>
<div className={styles.wrapper}> <div className={styles.wrapper}>
<input ref={inputRef as MutableRefObject<HTMLInputElement>} className={styles.in} type={"text"} onChange={replinOnChange} onKeyDown={tabComplete} spellCheck={"false"} autoFocus /> <input ref={inputRef as MutableRefObject<HTMLInputElement>} className={styles.in} type={"text"} onChange={replinOnChange} onKeyDown={tabComplete} spellCheck={"false"} autoFocus maxLength={20} />
<span className={styles.completionWrapper}><span ref={typed} className={styles.typed}></span><span ref={completion} className={styles.completion}></span></span> <span className={styles.completionWrapper}><span ref={typed} className={styles.typed}></span><span ref={completion} className={styles.completion}></span></span>
</div> </div>
</div>; </div>;

View File

@ -1,4 +1,4 @@
import type { Command } from "./types"; import type { Command, Flag } from "./types";
function illegalUse(raw: string, cmd: Command): string[] { function illegalUse(raw: string, cmd: Command): string[] {
return [ return [
@ -31,6 +31,10 @@ function checkSubcmd(subcmds: string[], cmd: Command): boolean {
return true; return true;
} }
function checkFlagInclude(flagsProvided: string[], flag: Flag): boolean {
return flagsProvided.includes(`-${flag.short}`) || flagsProvided.includes(`--${flag.long}`);
}
export function printSyntax(cmd: Command): string[] { export function printSyntax(cmd: Command): string[] {
let flagsOption = ""; let flagsOption = "";
let flagsDesc = []; let flagsDesc = [];
@ -82,4 +86,31 @@ const about: Command = {
} }
}; };
export const commandList = [about]; const help: Command = {
name: "help",
desc: "Shows helptext.",
flags: [{ long: "more", short: "m", desc: "Show more information." }],
execute: (flags, _args, _raw) => {
checkFlags(flags, help);
if (help.flags && checkFlagInclude(flags, help.flags[0])) {
return [
"Hello user!",
"What you see here should resemble an CLI. If you ever used Linux this should be pretty easy for you.",
"Everyone else: Have no fear. It is pretty simple. You just type in commands and the output is shown here or it does something on the webite.",
"To find out, which commands are available, you can type just 'help'.",
"",
"Have fun!"
];
} else {
const available = ["Available commands:"];
commandList.forEach(cmd => available.push(`\t${cmd.name}\t${cmd.desc}`));
available.concat([
"",
"Need more help? Type 'help -m'!"
]);
return available;
}
}
};
export const commandList = [about, help];

View File

@ -1,4 +1,4 @@
interface Flag { export interface Flag {
short: string; short: string;
long: string; long: string;
desc: string; desc: string;
@ -15,5 +15,5 @@ export interface Command {
desc: string; desc: string;
flags?: Flag[]; flags?: Flag[];
subcommands?: SubCommand[]; subcommands?: SubCommand[];
execute: (flags: string[], args: string[], raw: string) => string[]; execute: (flags: string[], args: string[], raw: string, extra?: any) => string[];
} }

2
package-lock.json generated
View File

@ -6,7 +6,7 @@
"": { "": {
"name": "my_website", "name": "my_website",
"dependencies": { "dependencies": {
"next": "^12.0.7", "next": "12.0.7",
"react": "17.0.2", "react": "17.0.2",
"react-dom": "17.0.2" "react-dom": "17.0.2"
}, },

View File

@ -1,13 +1,17 @@
import type { NextPage } from "next"; import type { NextPage } from "next";
import Head from "next/head";
import REPL from "../components/REPL"; import REPL from "../components/REPL";
import styles from "../styles/Home.module.css"; import styles from "../styles/Home.module.css";
const Home: NextPage = () => { const Home: NextPage = () => {
return ( return (<>
<Head>
<title>c0ntroller.de</title>
</Head>
<div className={styles.container}> <div className={styles.container}>
<REPL /> <REPL />
</div> </div>
); </>);
}; };
export default Home; export default Home;

View File

@ -9,4 +9,8 @@
.line { .line {
padding: 3px 0; padding: 3px 0;
white-space: pre-wrap;
font-family: inherit;
display: block;
margin: 0;
} }

View File

@ -53,13 +53,17 @@
.completionWrapper span { .completionWrapper span {
padding: 0; padding: 0;
margin: 0; margin: 0;
display: inline-block; display: flex;
flex-direction: column;
} }
.typed { .typed {
opacity: 0; opacity: 0;
flex-shrink: 1;
overflow-x: hidden;
} }
.completion { .completion {
color: var(--repl-color-hint); color: var(--repl-color-hint);
flex-grow: 1;
} }