Skip to content

Commit 87fb437

Browse files
pearminiCopilot
andauthored
Fix crash caused by editor (#173)
* Handle for and if statement correctly * Fix test * Handle unexpected error more fridendly * Update app/Nav.jsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 14be08b commit 87fb437

File tree

9 files changed

+80
-10
lines changed

9 files changed

+80
-10
lines changed

app/Editor.jsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ const styles = {
99
iconButton: "w-4 h-4 hover:scale-110 transition-transform duration-100",
1010
};
1111

12+
function debounce(fn, delay = 0) {
13+
let timeout;
14+
return (...args) => {
15+
clearTimeout(timeout);
16+
timeout = setTimeout(() => fn(...args), delay);
17+
};
18+
}
19+
20+
const onDefaultError = debounce(() => {
21+
setTimeout(() => {
22+
alert("Something unexpected happened. Please check the console for details.");
23+
}, 100);
24+
}, 0);
25+
1226
export function Editor({
1327
initialCode,
1428
onUserInput = () => {},
@@ -17,6 +31,7 @@ export function Editor({
1731
toolBarStart = null,
1832
pinToolbar = true,
1933
onDuplicate = null,
34+
onError = onDefaultError,
2035
}) {
2136
const containerRef = useRef(null);
2237
const editorRef = useRef(null);
@@ -25,7 +40,7 @@ export function Editor({
2540
useEffect(() => {
2641
if (containerRef.current) {
2742
containerRef.current.innerHTML = "";
28-
editorRef.current = createEditor(containerRef.current, {code: initialCode});
43+
editorRef.current = createEditor(containerRef.current, {code: initialCode, onError});
2944
if (autoRun) onRun();
3045
}
3146
return () => {

app/Nav.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ export function Nav() {
5757
return (
5858
<header className={cn("flex justify-between items-center p-4 border-b border-gray-200")}>
5959
<div className={cn("flex md:gap-2 items-center ")}>
60-
<SafeLink href="https://recho.dev">
60+
<a href="https://recho.dev" target="_blank" rel="noopener noreferrer">
6161
<h1 className={cn("text-xl font-extrabold")}>Recho</h1>
62-
</SafeLink>
62+
</a>
6363
<SafeLink href="/">
6464
<h1 className={cn("text-xl hidden md:inline font-medium")}>Notebook</h1>
6565
</SafeLink>

app/SafeLink.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export function SafeLink({href, children, className, onClick, ...props}) {
1414
const handleClick = (e) => {
1515
e.preventDefault();
1616
if (isDirty) {
17-
e.preventDefault();
1817
const confirmLeave = window.confirm("Your changes will be lost.");
1918
if (!confirmLeave) return;
2019
}

editor/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const eslintConfig = {
3333
};
3434

3535
export function createEditor(container, options) {
36-
const {code} = options;
36+
const {code, onError} = options;
3737
const dispatcher = d3Dispatch("userInput");
3838
const runtimeRef = {current: null};
3939

@@ -132,8 +132,13 @@ export function createEditor(container, options) {
132132

133133
return {
134134
run: () => {
135-
if (!runtimeRef.current) initRuntime();
136-
runtimeRef.current.run();
135+
try {
136+
if (!runtimeRef.current) initRuntime();
137+
runtimeRef.current.run();
138+
} catch (error) {
139+
console.error(error);
140+
onError?.(error);
141+
}
137142
},
138143
stop: () => {
139144
runtimeRef.current?.destroy();

runtime/index.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,20 @@ function isError(value) {
2424
}
2525

2626
function safeEval(code, inputs) {
27-
const body = `const foo = ${code}; return foo(${inputs.join(",")})`;
28-
const fn = new Function(...inputs, body);
29-
return fn;
27+
const create = (code) => {
28+
const body = `const foo = ${code}; return foo(${inputs.join(",")})`;
29+
const fn = new Function(...inputs, body);
30+
return fn;
31+
};
32+
try {
33+
return create(code);
34+
} catch (error) {
35+
// For non-function statements, such as for statement, we need to wrap the code in a function.
36+
// For example, `for (let i = 0; i < 10; i++) { echo(i); }` will be wrapped in
37+
// `() => { for (let i = 0; i < 10; i++) { echo(i); } }` then return the function.
38+
const wrapped = `() => {${code}}`;
39+
return create(wrapped);
40+
}
3041
}
3142

3243
function debounce(fn, delay = 0) {

test/js/index-tests.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export {syntaxError3} from "./syntax-error3.js";
99
export {syntaxError4} from "./syntax-error4.js";
1010
export {nonCallEcho} from "./non-call-echo.js";
1111
export {echoMultipleValues} from "./echo-multiple-values.js";
12+
export {specialStatement} from "./special-statement.js";

test/js/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ export {syntaxError3} from "./syntax-error3.js";
1818
export {syntaxError4} from "./syntax-error4.js";
1919
export {nonCallEcho} from "./non-call-echo.js";
2020
export {echoMultipleValues} from "./echo-multiple-values.js";
21+
export {specialStatement} from "./special-statement.js";

test/js/special-statement.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const specialStatement = `let height = 5;
2+
let triangle = '';
3+
4+
for (let i = 1; i <= height; i++) {
5+
triangle += ' '.repeat(height - i);
6+
triangle += '*'.repeat(i * 2 - 1);
7+
triangle += '\\n';
8+
}
9+
10+
echo(triangle);
11+
12+
13+
if (true) {
14+
echo("true");
15+
} else {
16+
echo("false");
17+
}
18+
`;

test/output/specialStatement.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
let height = 5;
2+
let triangle = '';
3+
4+
//✗ [SyntaxError: Assignment to external variable 'triangle' (2:2)]
5+
for (let i = 1; i <= height; i++) {
6+
triangle += ' '.repeat(height - i);
7+
triangle += '*'.repeat(i * 2 - 1);
8+
triangle += '\n';
9+
}
10+
11+
//➜ ""
12+
echo(triangle);
13+
14+
15+
//➜ "true"
16+
if (true) {
17+
echo("true");
18+
} else {
19+
echo("false");
20+
}

0 commit comments

Comments
 (0)