MISC: Support JSX, TS, TSX script files (#1216)

This commit is contained in:
catloversg
2024-07-15 04:47:10 +07:00
committed by GitHub
parent 783120c886
commit 864613c616
38 changed files with 895 additions and 201 deletions
+43 -23
View File
@@ -1,20 +1,21 @@
import React, { useContext, useState } from "react";
import { Settings } from "../../Settings/Settings";
import { calculateRamUsage } from "../../Script/RamCalculations";
import { RamCalculationErrorCode } from "../../Script/RamCalculationErrorCodes";
import { formatRam } from "../../ui/formatNumber";
import { useBoolean } from "../../ui/React/hooks";
import { calculateRamUsage, type RamCalculationFailure } from "../../Script/RamCalculations";
import { BaseServer } from "../../Server/BaseServer";
import { Settings } from "../../Settings/Settings";
import { useBoolean } from "../../ui/React/hooks";
import { formatRam } from "../../ui/formatNumber";
import { Options } from "./Options";
import { FilePath } from "../../Paths/FilePath";
import { hasScriptExtension } from "../../Paths/ScriptFilePath";
import type { AST } from "../../utils/ScriptTransformer";
import type { Options } from "./Options";
import { type ScriptFilePath } from "../../Paths/ScriptFilePath";
export interface ScriptEditorContextShape {
ram: string;
ramEntries: string[][];
updateRAM: (newCode: string | null, filename: FilePath | null, server: BaseServer | null) => void;
showRAMError: (error?: RamCalculationFailure) => void;
updateRAM: (ast: AST, path: ScriptFilePath, server: BaseServer) => void;
isUpdatingRAM: boolean;
startUpdatingRAM: () => void;
@@ -30,13 +31,30 @@ export function ScriptEditorContextProvider({ children }: { children: React.Reac
const [ram, setRAM] = useState("RAM: ???");
const [ramEntries, setRamEntries] = useState<string[][]>([["???", ""]]);
const updateRAM: ScriptEditorContextShape["updateRAM"] = (newCode, filename, server) => {
if (newCode == null || filename == null || server == null || !hasScriptExtension(filename)) {
const showRAMError: ScriptEditorContextShape["showRAMError"] = (error) => {
if (!error) {
setRAM("N/A");
setRamEntries([["N/A", ""]]);
return;
}
const ramUsage = calculateRamUsage(newCode, filename, server.scripts, server.hostname);
let errorType;
switch (error.errorCode) {
case RamCalculationErrorCode.SyntaxError:
errorType = "Syntax Error";
break;
case RamCalculationErrorCode.ImportError:
errorType = "Import Error";
break;
default:
errorType = "Unknown Error";
break;
}
setRAM(`RAM: ${errorType}`);
setRamEntries([[errorType, error.errorMessage ?? ""]]);
};
const updateRAM: ScriptEditorContextShape["updateRAM"] = (ast, path, server) => {
const ramUsage = calculateRamUsage(ast, path, server.hostname, server.scripts);
if (ramUsage.cost && ramUsage.cost > 0) {
const entries = ramUsage.entries?.sort((a, b) => b.cost - a.cost) ?? [];
const entriesDisp = [];
@@ -50,18 +68,10 @@ export function ScriptEditorContextProvider({ children }: { children: React.Reac
}
if (ramUsage.errorCode !== undefined) {
setRamEntries([["Syntax Error", ramUsage.errorMessage ?? ""]]);
switch (ramUsage.errorCode) {
case RamCalculationErrorCode.ImportError:
setRAM("RAM: Import Error");
break;
case RamCalculationErrorCode.SyntaxError:
setRAM("RAM: Syntax Error");
break;
}
showRAMError(ramUsage);
} else {
setRAM("RAM: Syntax Error");
setRamEntries([["Syntax Error", ""]]);
setRAM("RAM: Unknown Error");
setRamEntries([["Unknown Error", ""]]);
}
};
@@ -96,7 +106,17 @@ export function ScriptEditorContextProvider({ children }: { children: React.Reac
return (
<ScriptEditorContext.Provider
value={{ ram, ramEntries, updateRAM, isUpdatingRAM, startUpdatingRAM, finishUpdatingRAM, options, saveOptions }}
value={{
ram,
ramEntries,
showRAMError,
updateRAM,
isUpdatingRAM,
startUpdatingRAM,
finishUpdatingRAM,
options,
saveOptions,
}}
>
{children}
</ScriptEditorContext.Provider>
+33 -14
View File
@@ -30,6 +30,9 @@ import { NoOpenScripts } from "./NoOpenScripts";
import { ScriptEditorContextProvider, useScriptEditorContext } from "./ScriptEditorContext";
import { useVimEditor } from "./useVimEditor";
import { useCallback } from "react";
import { type AST, getFileType, parseAST } from "../../utils/ScriptTransformer";
import { RamCalculationErrorCode } from "../../Script/RamCalculationErrorCodes";
import { hasScriptExtension, isLegacyScript } from "../../Paths/ScriptFilePath";
interface IProps {
// Map of filename -> code
@@ -44,7 +47,7 @@ function Root(props: IProps): React.ReactElement {
const rerender = useRerender();
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
const { updateRAM, startUpdatingRAM, finishUpdatingRAM } = useScriptEditorContext();
const { showRAMError, updateRAM, startUpdatingRAM, finishUpdatingRAM } = useScriptEditorContext();
let decorations: monaco.editor.IEditorDecorationsCollection | undefined;
@@ -112,11 +115,14 @@ function Root(props: IProps): React.ReactElement {
return () => document.removeEventListener("keydown", keydown);
}, [save]);
function infLoop(newCode: string): void {
if (editorRef.current === null || currentScript === null) return;
if (!decorations) decorations = editorRef.current.createDecorationsCollection();
if (!currentScript.path.endsWith(".js")) return;
const possibleLines = checkInfiniteLoop(newCode);
function infLoop(ast: AST, code: string): void {
if (editorRef.current === null || currentScript === null || isLegacyScript(currentScript.path)) {
return;
}
if (!decorations) {
decorations = editorRef.current.createDecorationsCollection();
}
const possibleLines = checkInfiniteLoop(ast, code);
if (possibleLines.length !== 0) {
decorations.set(
possibleLines.map((awaitWarning) => ({
@@ -131,21 +137,34 @@ function Root(props: IProps): React.ReactElement {
glyphMarginClassName: "myGlyphMarginClass",
glyphMarginHoverMessage: {
value:
"Possible infinite loop, await something. If this is a false-positive, use `// @ignore-infinite` to suppress.",
"Possible infinite loop, await something. If this is a false positive, use `// @ignore-infinite` to suppress.",
},
},
})),
);
} else decorations.clear();
} else {
decorations.clear();
}
}
const debouncedCodeParsing = debounce((newCode: string) => {
infLoop(newCode);
updateRAM(
!currentScript || currentScript.isTxt ? null : newCode,
currentScript && currentScript.path,
currentScript && GetServer(currentScript.hostname),
);
let server;
if (!currentScript || !hasScriptExtension(currentScript.path) || !(server = GetServer(currentScript.hostname))) {
showRAMError();
return;
}
let ast;
try {
ast = parseAST(newCode, getFileType(currentScript.path));
} catch (error) {
showRAMError({
errorCode: RamCalculationErrorCode.SyntaxError,
errorMessage: error instanceof Error ? error.message : String(error),
});
return;
}
infLoop(ast, newCode);
updateRAM(ast, currentScript.path, server);
finishUpdatingRAM();
}, 300);
+24 -1
View File
@@ -1,6 +1,7 @@
import { GetServer } from "../../Server/AllServers";
import { editor, Uri } from "monaco-editor";
import { OpenScript } from "./OpenScript";
import { getFileType, FileType } from "../../utils/ScriptTransformer";
function getServerCode(scripts: OpenScript[], index: number): string | null {
const openScript = scripts[index];
@@ -26,7 +27,29 @@ function makeModel(hostname: string, filename: string, code: string) {
scheme: "file",
path: `${hostname}/${filename}`,
});
const language = filename.endsWith(".txt") ? "plaintext" : filename.endsWith(".json") ? "json" : "javascript";
let language;
const fileType = getFileType(filename);
switch (fileType) {
case FileType.PLAINTEXT:
language = "plaintext";
break;
case FileType.JSON:
language = "json";
break;
case FileType.JS:
case FileType.JSX:
language = "javascript";
break;
case FileType.TS:
case FileType.TSX:
language = "typescript";
break;
case FileType.NS1:
language = "javascript";
break;
default:
throw new Error(`Invalid file type: ${fileType}. Filename: ${filename}.`);
}
//if somehow a model already exist return it
return editor.getModel(uri) ?? editor.createModel(code, language, uri);
}