From 70cd9cf46577c944451c8dcb47fb2ee116970203 Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 01:20:57 +0900 Subject: [PATCH 1/8] Introduce console_error_panic_hook crate for debug --- momonga/Cargo.toml | 1 + momonga/src/lib.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/momonga/Cargo.toml b/momonga/Cargo.toml index 72d95fc..6ef02c4 100644 --- a/momonga/Cargo.toml +++ b/momonga/Cargo.toml @@ -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" diff --git a/momonga/src/lib.rs b/momonga/src/lib.rs index 02d2ff6..ab202d9 100644 --- a/momonga/src/lib.rs +++ b/momonga/src/lib.rs @@ -48,6 +48,9 @@ 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(_) => (), From 92258f4add69c801dc2011608c13be101b8bbcc4 Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 01:44:55 +0900 Subject: [PATCH 2/8] Add wasm-pack build step to each running server scripts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 255d2d1..b2b1bc8 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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": { From a090c42e28895820dd55c50971ef33311691a6a2 Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 05:08:07 +0900 Subject: [PATCH 3/8] Add comment about optimizing momonga_run() running --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 3037ed8..a07d61f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -34,7 +34,7 @@ function App() { const handleRunClick = () => { 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) => { From 000b9bb7eb2b6192dbacbd82d1a31ccdfe50b2b7 Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 15:57:12 +0900 Subject: [PATCH 4/8] Separate useEffect() according to its dependency array --- src/App.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a07d61f..ac4f12b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -70,12 +70,14 @@ function App() { window.addEventListener("printstderr", handlePrintstderrEvent); window.addEventListener("printstdout", handlePrintStdoutEvent); - localStorage.setItem("userLayout", userLayout); - return () => { window.removeEventListener("printstdout", handlePrintStdoutEvent); window.removeEventListener("printstderr", handlePrintstderrEvent); }; + }, []); + + useEffect(() => { + localStorage.setItem("userLayout", userLayout); }, [userLayout]); return ( From 5902e1acb39ecae1ef864d3758be19a9675f089d Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 16:08:44 +0900 Subject: [PATCH 5/8] Rename emitted events name from momonga_run() --- momonga/src/data.rs | 4 ++-- momonga/src/lib.rs | 14 +++++++------- src/App.tsx | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/momonga/src/data.rs b/momonga/src/data.rs index 3d90bc0..4db736f 100644 --- a/momonga/src/data.rs +++ b/momonga/src/data.rs @@ -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> { @@ -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))) } diff --git a/momonga/src/lib.rs b/momonga/src/lib.rs index ab202d9..b1fe58e 100644 --- a/momonga/src/lib.rs +++ b/momonga/src/lib.rs @@ -24,17 +24,17 @@ pub fn interpret(src: &str) -> Option { } #[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( @@ -54,8 +54,8 @@ pub fn momonga_run(source: &str) { 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()), } } diff --git a/src/App.tsx b/src/App.tsx index ac4f12b..405d230 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -59,20 +59,20 @@ function App() { useEffect(() => { init(); - 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); + 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); }; }, []); From 47484502391c87710158da0c879da86bd514aa1f Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 16:50:56 +0900 Subject: [PATCH 6/8] Check syntax and display status while typing --- momonga/src/lib.rs | 11 +++++++++++ src/App.tsx | 17 +++++++++++++++-- src/components/Editor.tsx | 13 +++++++++++-- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/momonga/src/lib.rs b/momonga/src/lib.rs index b1fe58e..235ea32 100644 --- a/momonga/src/lib.rs +++ b/momonga/src/lib.rs @@ -59,3 +59,14 @@ pub fn momonga_run(source: &str) { 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, + } +} diff --git a/src/App.tsx b/src/App.tsx index 405d230..e74b4aa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 isWasmIntitializedRef = useRef(false); + const srcRef = useRef(""); + const [isParseError, setIsParseError] = useState(false); const [stdout, setStdout] = useState([]); const [stderr, setStderr] = useState([]); const [snippetKey, setSnippetKey] = useState(snippets[0].key); @@ -39,6 +45,9 @@ function App() { const handleSrcChange = (src: string) => { srcRef.current = src; + + if (!isWasmIntitializedRef.current) return; + setIsParseError(is_momonga_parse_error(src)); }; const handleLayoutClick = () => { @@ -57,7 +66,10 @@ function App() { }; useEffect(() => { - init(); + (async () => { + await init(); + isWasmIntitializedRef.current = true; + })(); const handleStdoutEvent = (ev: Event) => { const event = ev as CustomEvent; @@ -123,6 +135,7 @@ function App() { }} > ; snippetKey: SnippetKey; onSrcChange: (src: string) => void; }; export const Editor = React.memo( - ({ srcRef, snippetKey, onSrcChange }: Props) => { + ({ isParseError, srcRef, snippetKey, onSrcChange }: Props) => { const [editor, setEditor] = useState(null); const monacoEl = useRef(null); @@ -157,6 +158,14 @@ export const Editor = React.memo( } }, [editor, monacoTheme]); - return ; + return ( + + ); }, ); From 99fd4c208806ef7f6c51320ad51bad9ae2a45047 Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 17:49:44 +0900 Subject: [PATCH 7/8] Memoize components and functions --- src/App.tsx | 33 ++++---- src/components/Editor.tsx | 4 +- src/components/Header.tsx | 160 +++++++++++++++++++------------------- src/components/Output.tsx | 6 +- 4 files changed, 105 insertions(+), 98 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e74b4aa..9683a7a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { Box, @@ -37,33 +37,36 @@ function App() { ); const isHorizontalLayout = userLayout === "horizontal" || isMuiMdScreen; - const handleRunClick = () => { + const handleRunClick = useCallback(() => { setStdout([]); setStderr([]); 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; if (!isWasmIntitializedRef.current) return; setIsParseError(is_momonga_parse_error(src)); - }; + }, []); - const handleLayoutClick = () => { + const handleLayoutClick = useCallback(() => { setUserLayout((prev) => prev === "horizontal" ? "vertical" : "horizontal", ); - }; + }, []); - const handleSnippetChange = (event: SelectChangeEvent) => { - const snippet = snippets.find( - (snippet) => snippet.key === event.target.value, - ); - if (snippet) { - setSnippetKey(snippet.key); - } - }; + const handleSnippetChange = useCallback( + (event: SelectChangeEvent) => { + const snippet = snippets.find( + (snippet) => snippet.key === event.target.value, + ); + if (snippet) { + setSnippetKey(snippet.key); + } + }, + [], + ); useEffect(() => { (async () => { diff --git a/src/components/Editor.tsx b/src/components/Editor.tsx index 0d5c68c..0504b7a 100644 --- a/src/components/Editor.tsx +++ b/src/components/Editor.tsx @@ -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"; @@ -103,7 +103,7 @@ type Props = { onSrcChange: (src: string) => void; }; -export const Editor = React.memo( +export const Editor = memo( ({ isParseError, srcRef, snippetKey, onSrcChange }: Props) => { const [editor, setEditor] = useState(null); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 0150ec7..eaf86a9 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,4 @@ -import { useContext } from "react"; +import { memo, useContext } from "react"; import { AppBar, @@ -29,83 +29,85 @@ type Props = { onMainLayoutClick: () => void; }; -export const Header = ({ - isMuiMdScreen, - isHorizontalLayout, - snippetKey, - onMainLayoutClick, - onRunClick, - onSnippetChange, -}: Props) => { - const { mode, toggleMode } = useContext(ThemeContext); - return ( - - - - - - - Code Snippets - - - - - - + + + Code Snippets + + + + + - Grammar - - {!isMuiMdScreen && ( - - {isHorizontalLayout ? ( - - ) : ( - - )} + + {!isMuiMdScreen && ( + + {isHorizontalLayout ? ( + + ) : ( + + )} + + )} + + {mode === "light" ? : } - )} - - {mode === "light" ? : } - - - - - ); -}; + + + + ); + }, +); diff --git a/src/components/Output.tsx b/src/components/Output.tsx index d0b0820..2887cd7 100644 --- a/src/components/Output.tsx +++ b/src/components/Output.tsx @@ -1,3 +1,5 @@ +import { memo } from "react"; + import { Box, Divider } from "@mui/material"; import { Stderr, Stdout } from "@/types/types"; @@ -7,7 +9,7 @@ type Props = { stderr: Stderr; }; -export const Output = ({ stdout, stderr }: Props) => { +export const Output = memo(({ stdout, stderr }: Props) => { return ( { ); -}; +}); From bf64f0158ddc6040662dca66cc56957b03faca88 Mon Sep 17 00:00:00 2001 From: "radish@MBA2020" Date: Sun, 31 Dec 2023 18:40:58 +0900 Subject: [PATCH 8/8] Fix typo --- src/App.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 9683a7a..39c6280 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,7 +20,7 @@ import init, { } from "../momonga/pkg/momonga"; function App() { - const isWasmIntitializedRef = useRef(false); + const isWasmInitializedRef = useRef(false); const srcRef = useRef(""); const [isParseError, setIsParseError] = useState(false); @@ -46,7 +46,7 @@ function App() { const handleSrcChange = useCallback((src: string) => { srcRef.current = src; - if (!isWasmIntitializedRef.current) return; + if (!isWasmInitializedRef.current) return; setIsParseError(is_momonga_parse_error(src)); }, []); @@ -71,7 +71,7 @@ function App() { useEffect(() => { (async () => { await init(); - isWasmIntitializedRef.current = true; + isWasmInitializedRef.current = true; })(); const handleStdoutEvent = (ev: Event) => {