EDITOR: Add the ability to Beautify on Save (optionally) (#2287)

This commit is contained in:
Adam Weeden
2025-08-26 19:35:41 -04:00
committed by GitHub
parent 95e38164f7
commit bd3c6c10ef
7 changed files with 72 additions and 28 deletions

View File

@@ -16,4 +16,5 @@ export interface Options {
wordWrap: WordWrapOptions;
cursorStyle: CursorStyle;
cursorBlinking: CursorBlinking;
beautifyOnSave: boolean;
}

View File

@@ -142,6 +142,14 @@ export function OptionsModal(props: OptionsModalProps): ReactElement {
))}
</Select>
</div>
<div style={{ display: "flex", alignItems: "center" }}>
<Typography marginRight={"auto"}>Run Beautify on Save: </Typography>
<Switch
onChange={(e) => props.onOptionChange("beautifyOnSave", e.target.checked)}
checked={props.options.beautifyOnSave}
/>
</div>
</Modal>
);
}

View File

@@ -91,6 +91,7 @@ export function ScriptEditorContextProvider({ children }: { children: React.Reac
wordWrap: Settings.MonacoWordWrap,
cursorStyle: Settings.MonacoCursorStyle,
cursorBlinking: Settings.MonacoCursorBlinking,
beautifyOnSave: Settings.MonacoBeautifyOnSave,
});
function saveOptions(options: Options) {
@@ -105,6 +106,7 @@ export function ScriptEditorContextProvider({ children }: { children: React.Reac
Settings.MonacoCursorStyle = options.cursorStyle;
Settings.MonacoCursorBlinking = options.cursorBlinking;
Settings.MonacoWordWrap = options.wordWrap;
Settings.MonacoBeautifyOnSave = options.beautifyOnSave;
}
return (

View File

@@ -176,15 +176,26 @@ function Root(props: IProps): React.ReactElement {
reloadModelOfCurrentScript();
}
const { showRAMError, updateRAM, startUpdatingRAM, finishUpdatingRAM } = useScriptEditorContext();
const { options, showRAMError, updateRAM, startUpdatingRAM, finishUpdatingRAM } = useScriptEditorContext();
let decorations: monaco.editor.IEditorDecorationsCollection | undefined;
const save = useCallback(() => {
const beautify = useCallback(async (): Promise<void> => {
const action = editorRef.current?.getAction("editor.action.formatDocument");
if (action == null) {
return;
}
return action.run().catch((error) => console.error(error));
}, []);
const save = useCallback(async () => {
if (currentScript === null) {
console.error("currentScript is null when it shouldn't be. Unable to save script");
return;
}
const preSave = options.beautifyOnSave ? beautify : () => Promise.resolve();
// this is duplicate code with saving later.
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalEditScript) {
//Make sure filename + code properly follow tutorial
@@ -200,6 +211,7 @@ function Root(props: IProps): React.ReactElement {
}
//Save the script
await preSave();
saveScript(currentScript);
Router.toPage(Page.Terminal);
@@ -207,11 +219,12 @@ function Root(props: IProps): React.ReactElement {
return;
}
await preSave();
saveScript(currentScript);
rerender();
}, [rerender]);
}, [rerender, options.beautifyOnSave, beautify]);
const run = useCallback(() => {
const run = useCallback(async () => {
if (currentScript === null) {
return;
}
@@ -227,7 +240,7 @@ function Root(props: IProps): React.ReactElement {
}
// Always save before doing anything else.
save();
await save();
const result = createRunningScriptInstance(server, currentScript.path, null, 1, []);
if (!result.success) {
@@ -238,7 +251,7 @@ function Root(props: IProps): React.ReactElement {
}, [save]);
useEffect(() => {
function keydown(event: KeyboardEvent): void {
async function keydown(event: KeyboardEvent) {
if (Settings.DisableHotkeys) {
return;
}
@@ -246,7 +259,7 @@ function Root(props: IProps): React.ReactElement {
if (keyBindingTypes.has(ScriptEditorAction.Save)) {
event.preventDefault();
event.stopPropagation();
save();
await save();
}
if (keyBindingTypes.has(ScriptEditorAction.GoToTerminal)) {
event.preventDefault();
@@ -254,11 +267,14 @@ function Root(props: IProps): React.ReactElement {
}
if (keyBindingTypes.has(ScriptEditorAction.Run)) {
event.preventDefault();
run();
await run();
}
}
document.addEventListener("keydown", keydown);
return () => document.removeEventListener("keydown", keydown);
const listener = (event: KeyboardEvent) => {
keydown(event).catch((error) => console.error(error));
};
document.addEventListener("keydown", listener);
return () => document.removeEventListener("keydown", listener);
}, [save, run]);
function infLoop(ast: AST, code: string): void {
@@ -598,7 +614,7 @@ function Root(props: IProps): React.ReactElement {
{statusBarRef.current}
<Toolbar onSave={save} onRun={run} editor={editorRef.current} />
<Toolbar onSave={save} onRun={run} editor={editorRef.current} onBeautify={beautify} />
</div>
{!currentScript && <NoOpenScripts />}
</>

View File

@@ -31,21 +31,15 @@ type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
interface IProps {
editor: IStandaloneCodeEditor | null;
onSave: () => void;
onRun: () => void;
onSave: () => Promise<void>;
onRun: () => Promise<void>;
onBeautify: () => Promise<void>;
}
export function Toolbar({ editor, onSave, onRun }: IProps) {
export function Toolbar({ editor, onSave, onRun, onBeautify }: IProps) {
const [ramInfoOpen, { on: openRAMInfo, off: closeRAMInfo }] = useBoolean(false);
const [optionsOpen, { on: openOptions, off: closeOptions }] = useBoolean(false);
function beautify(): void {
editor
?.getAction("editor.action.formatDocument")
?.run()
.catch((error) => console.error(error));
}
const { ram, ramEntries, isUpdatingRAM, options, saveOptions } = useScriptEditorContext();
const onOptionChange: OptionsModalProps["onOptionChange"] = (option, value) => {
@@ -68,12 +62,24 @@ export function Toolbar({ editor, onSave, onRun }: IProps) {
<Button startIcon={<SettingsIcon />} onClick={openOptions} sx={{ mr: 1 }}>
Options
</Button>
<Button onClick={beautify}>Beautify</Button>
<Button
onClick={() => {
onBeautify().catch((error) => console.error(error));
}}
>
Beautify
</Button>
<Button color={isUpdatingRAM ? "secondary" : "primary"} sx={{ mx: 1 }} onClick={openRAMInfo}>
{ram}
</Button>
<Tooltip title={parseKeyCombinationsToString(CurrentKeyBindings[ScriptEditorAction.Save])}>
<Button onClick={onSave}>Save</Button>
<Button
onClick={() => {
onSave().catch((error) => console.error(error));
}}
>
Save
</Button>
</Tooltip>
<Tooltip title={parseKeyCombinationsToString(CurrentKeyBindings[ScriptEditorAction.GoToTerminal])}>
<Button sx={{ mx: 1 }} onClick={() => Router.toPage(Page.Terminal)}>
@@ -81,7 +87,12 @@ export function Toolbar({ editor, onSave, onRun }: IProps) {
</Button>
</Tooltip>
<Tooltip title={parseKeyCombinationsToString(CurrentKeyBindings[ScriptEditorAction.Run])}>
<Button sx={{ mr: 1 }} onClick={onRun}>
<Button
sx={{ mr: 1 }}
onClick={() => {
onRun().catch((error) => console.error(error));
}}
>
Run
</Button>
</Tooltip>

View File

@@ -13,7 +13,7 @@ interface IProps {
editor: IStandaloneCodeEditor | null;
onOpenNextTab: (step: number) => void;
onOpenPreviousTab: (step: number) => void;
onSave: () => void;
onSave: () => Promise<void>;
}
export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, onSave }: IProps) {
@@ -32,7 +32,7 @@ export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, on
setVimEditor(MonacoVim.initVimMode(editor, statusBarRef, StatusBar, rerender));
MonacoVim.VimMode.Vim.defineEx("write", "w", function () {
// your own implementation on what you want to do when :w is pressed
actionsRef.current.save();
actionsRef.current.save().catch((error) => console.error(error));
});
MonacoVim.VimMode.Vim.defineEx("quit", "q", function () {
Router.toPage(Page.Terminal);
@@ -43,8 +43,12 @@ export function useVimEditor({ editor, vim, onOpenNextTab, onOpenPreviousTab, on
MonacoVim.VimMode.Vim.mapCommand("@", "", "", null, { context: "normal" });
const saveNQuit = (): void => {
actionsRef.current.save();
Router.toPage(Page.Terminal);
actionsRef.current
.save()
.then(() => {
Router.toPage(Page.Terminal);
})
.catch((error) => console.error(error));
};
// "wqriteandquit" & "xriteandquit" are not typos, prefix must be found in full string
MonacoVim.VimMode.Vim.defineEx("wqriteandquit", "wq", saveNQuit);

View File

@@ -176,6 +176,8 @@ export const Settings = {
MonacoDefaultToVim: false,
/** Word wrap setting for Script Editor. */
MonacoWordWrap: "off" as WordWrapOptions,
/** Whether to run Beautify code formatter on save */
MonacoBeautifyOnSave: false,
/** Control the cursor style*/
MonacoCursorStyle: "line" as CursorStyle,
/** Control the cursor animation style */