Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Real-time syntax checking feature, and other improvements #19

Merged
merged 9 commits into from
Dec 31, 2023
1 change: 1 addition & 0 deletions momonga/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ wasm-bindgen = "0.2"
lazy_static = "1.4.0"
pest = "2.6"
pest_derive = "2.6"
console_error_panic_hook = "0.1.7"

[dependencies.web-sys]
version = "0.3"
Expand Down
4 changes: 2 additions & 2 deletions momonga/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};

use crate::env::Store;
use crate::error::EvalError;
use crate::{emit_print_event, PrintEvent};
use crate::{emit_output_event, OutputEvent};

#[derive(Debug, PartialEq, Clone)]
pub enum Value<'a> {
Expand Down Expand Up @@ -100,6 +100,6 @@ pub fn momonga_pop(args: BuiltinArgs) -> BuiltinReturn {
}

pub fn momonga_print(args: BuiltinArgs) -> BuiltinReturn {
emit_print_event(PrintEvent::Stdout, &(*args[0].borrow()).to_string());
emit_output_event(OutputEvent::Stdout, &(*args[0].borrow()).to_string());
Ok(Rc::new(RefCell::new(Value::None)))
}
28 changes: 21 additions & 7 deletions momonga/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ pub fn interpret(src: &str) -> Option<String> {
}

#[wasm_bindgen]
pub enum PrintEvent {
pub enum OutputEvent {
Stdout,
Stderr,
}

#[wasm_bindgen]
pub fn emit_print_event(print_event: PrintEvent, data: &str) {
pub fn emit_output_event(output_event: OutputEvent, data: &str) {
let window = web_sys::window().unwrap();
let type_ = match print_event {
PrintEvent::Stdout => "printstdout",
PrintEvent::Stderr => "printstderr",
let type_ = match output_event {
OutputEvent::Stdout => "stdout",
OutputEvent::Stderr => "stderr",
};
let event = CustomEvent::new(type_).unwrap();
event.init_custom_event_with_can_bubble_and_cancelable_and_detail(
Expand All @@ -48,11 +48,25 @@ pub fn emit_print_event(print_event: PrintEvent, data: &str) {

#[wasm_bindgen]
pub fn momonga_run(source: &str) {
#[cfg(debug_assertions)]
console_error_panic_hook::set_once();

match parse(source) {
Ok(ast) => match eval(&ast, Rc::new(RefCell::new(Env::new_with_builtins()))) {
Ok(_) => (),
Err(eval_err) => emit_print_event(PrintEvent::Stderr, &eval_err.to_string()),
Err(eval_err) => emit_output_event(OutputEvent::Stderr, &eval_err.to_string()),
},
Err(parse_err) => emit_print_event(PrintEvent::Stderr, &parse_err.to_string()),
Err(parse_err) => emit_output_event(OutputEvent::Stderr, &parse_err.to_string()),
}
}

#[wasm_bindgen]
pub fn is_momonga_parse_error(source: &str) -> bool {
#[cfg(debug_assertions)]
console_error_panic_hook::set_once();

match parse(source) {
Ok(_ast) => false,
Err(_parse_err) => true,
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"node": "18.x"
},
"scripts": {
"dev": "vite",
"dev": "cd ./momonga && wasm-pack build --dev --target web && cd ../ && vite",
"build": "npm run build:core && npm run build:web",
"build:core": "cd ./momonga && wasm-pack build --release --target web",
"build:web": "tsc && vite build",
Expand All @@ -16,7 +16,7 @@
"lint": "npm run lint:web && npm run lint:core",
"lint:core": "cd momonga && cargo clippy -- -D warnings",
"lint:web": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"preview": "npm run build && vite preview",
"prepare": "simple-git-hooks"
},
"simple-git-hooks": {
Expand Down
70 changes: 44 additions & 26 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";

import {
Box,
Expand All @@ -14,10 +14,16 @@ import { Header } from "@/components/Header";
import { Output } from "@/components/Output";
import { snippets } from "@/constants";
import { Layout, SnippetKey, Stderr, Stdout } from "@/types/types";
import init, { momonga_run } from "../momonga/pkg/momonga";
import init, {
is_momonga_parse_error,
momonga_run,
} from "../momonga/pkg/momonga";

function App() {
const isWasmInitializedRef = useRef<boolean>(false);

const srcRef = useRef<string>("");
const [isParseError, setIsParseError] = useState<boolean>(false);
const [stdout, setStdout] = useState<Stdout>([]);
const [stderr, setStderr] = useState<Stderr>([]);
const [snippetKey, setSnippetKey] = useState<SnippetKey>(snippets[0].key);
Expand All @@ -31,51 +37,62 @@ function App() {
);
const isHorizontalLayout = userLayout === "horizontal" || isMuiMdScreen;

const handleRunClick = () => {
const handleRunClick = useCallback(() => {
setStdout([]);
setStderr([]);
momonga_run(srcRef.current);
};
momonga_run(srcRef.current); // NOTE: In order to run on Worker, it is necessary to change the way of passing its output data to main thread.
}, []);

const handleSrcChange = (src: string) => {
const handleSrcChange = useCallback((src: string) => {
srcRef.current = src;
};

const handleLayoutClick = () => {
if (!isWasmInitializedRef.current) return;
setIsParseError(is_momonga_parse_error(src));
}, []);

const handleLayoutClick = useCallback(() => {
setUserLayout((prev) =>
prev === "horizontal" ? "vertical" : "horizontal",
);
};
}, []);

const handleSnippetChange = (event: SelectChangeEvent<string>) => {
const snippet = snippets.find(
(snippet) => snippet.key === event.target.value,
);
if (snippet) {
setSnippetKey(snippet.key);
}
};
const handleSnippetChange = useCallback(
(event: SelectChangeEvent<string>) => {
const snippet = snippets.find(
(snippet) => snippet.key === event.target.value,
);
if (snippet) {
setSnippetKey(snippet.key);
}
},
[],
);

useEffect(() => {
init();
(async () => {
await init();
isWasmInitializedRef.current = true;
})();

const handlePrintStdoutEvent = (ev: Event) => {
const handleStdoutEvent = (ev: Event) => {
const event = ev as CustomEvent;
setStdout((prev) => [...prev, event.detail]);
};
const handlePrintstderrEvent = (ev: Event) => {
const handleStderrEvent = (ev: Event) => {
const event = ev as CustomEvent;
setStderr((prev) => [...prev, event.detail]);
};
window.addEventListener("printstderr", handlePrintstderrEvent);
window.addEventListener("printstdout", handlePrintStdoutEvent);

localStorage.setItem("userLayout", userLayout);
window.addEventListener("stderr", handleStderrEvent);
window.addEventListener("stdout", handleStdoutEvent);

return () => {
window.removeEventListener("printstdout", handlePrintStdoutEvent);
window.removeEventListener("printstderr", handlePrintstderrEvent);
window.removeEventListener("stdout", handleStdoutEvent);
window.removeEventListener("stderr", handleStderrEvent);
};
}, []);

useEffect(() => {
localStorage.setItem("userLayout", userLayout);
}, [userLayout]);

return (
Expand Down Expand Up @@ -121,6 +138,7 @@ function App() {
}}
>
<Editor
isParseError={isParseError}
srcRef={srcRef}
snippetKey={snippetKey}
onSrcChange={handleSrcChange}
Expand Down
17 changes: 13 additions & 4 deletions src/components/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
import { MutableRefObject, memo, useEffect, useRef, useState } from "react";

import { Box } from "@mui/material";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
Expand Down Expand Up @@ -97,13 +97,14 @@ monaco.editor.defineTheme("monaco-theme-dark", {
});

type Props = {
isParseError: boolean;
srcRef: MutableRefObject<string>;
snippetKey: SnippetKey;
onSrcChange: (src: string) => void;
};

export const Editor = React.memo(
({ srcRef, snippetKey, onSrcChange }: Props) => {
export const Editor = memo(
({ isParseError, srcRef, snippetKey, onSrcChange }: Props) => {
const [editor, setEditor] =
useState<monaco.editor.IStandaloneCodeEditor | null>(null);
const monacoEl = useRef<HTMLElement>(null);
Expand Down Expand Up @@ -157,6 +158,14 @@ export const Editor = React.memo(
}
}, [editor, monacoTheme]);

return <Box ref={monacoEl} sx={{ height: "100%" }}></Box>;
return (
<Box
ref={monacoEl}
sx={{
height: "100%",
border: isParseError ? "2px solid #FF0000" : "2px solid transparent",
}}
></Box>
);
},
);
Loading
Loading