-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathruntime.tsx
More file actions
218 lines (212 loc) · 5.54 KB
/
runtime.tsx
File metadata and controls
218 lines (212 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
"use client";
import { MutexInterface } from "async-mutex";
import { ReplOutput, SyntaxStatus, ReplCommand } from "./repl";
import { useWandbox, WandboxProvider } from "./wandbox/runtime";
import { AceLang } from "./editor";
import { ReactNode, useEffect } from "react";
import { PyodideContext, usePyodide } from "./worker/pyodide";
import { RubyContext, useRuby } from "./worker/ruby";
import { JSEvalContext, useJSEval } from "./worker/jsEval";
import { WorkerProvider } from "./worker/runtime";
import { TypeScriptProvider, useTypeScript } from "./typescript/runtime";
import { MarkdownLang } from "@/[lang]/[pageId]/styledSyntaxHighlighter";
/**
* Common runtime context interface for different languages
*
* see README.md for details
*
*/
export interface RuntimeContext {
init?: () => void;
ready: boolean;
mutex?: MutexInterface;
interrupt?: () => void;
// repl
runCommand?: (
command: string,
onOutput: (output: ReplOutput) => void
) => Promise<void>;
checkSyntax?: (code: string) => Promise<SyntaxStatus>;
splitReplExamples?: (content: string) => ReplCommand[];
// file
runFiles: (
filenames: string[],
files: Readonly<Record<string, string>>,
onOutput: (output: ReplOutput) => void
) => Promise<void>;
getCommandlineStr?: (filenames: string[]) => string;
runtimeInfo?: RuntimeInfo;
}
export interface RuntimeInfo {
prettyLangName: string;
version?: string;
}
export interface LangConstants {
tabSize: number;
prompt?: string;
promptMore?: string;
returnPrefix?: string;
}
export type RuntimeLang =
| "python"
| "ruby"
| "cpp"
| "rust"
| "javascript"
| "typescript";
export function getRuntimeLang(
lang: MarkdownLang | undefined
): RuntimeLang | undefined {
// markdownで指定される可能性のある言語名からRuntimeLangを取得
switch (lang) {
case "python":
case "py":
return "python";
case "ruby":
case "rb":
return "ruby";
case "cpp":
case "c++":
return "cpp";
case "rust":
case "rs":
return "rust";
case "javascript":
case "js":
return "javascript";
case "typescript":
case "ts":
return "typescript";
case "bash":
case "sh":
case "powershell":
case "json":
case "toml":
case "csv":
case "text":
case "txt":
case "html":
case "makefile":
case "cmake":
case undefined:
// unsupported languages
return undefined;
default:
lang satisfies never;
console.error(`getRuntimeLang() does not handle language ${lang}`);
return undefined;
}
}
export function useRuntime(language: RuntimeLang): RuntimeContext {
// すべての言語のcontextをインスタンス化
const pyodide = usePyodide();
const ruby = useRuby();
const jsEval = useJSEval();
const typescript = useTypeScript(jsEval);
const wandboxCpp = useWandbox("cpp");
const wandboxRust = useWandbox("rust");
let runtime: RuntimeContext;
switch (language) {
case "python":
runtime = pyodide;
break;
case "ruby":
runtime = ruby;
break;
case "javascript":
runtime = jsEval;
break;
case "typescript":
runtime = typescript;
break;
case "cpp":
runtime = wandboxCpp;
break;
case "rust":
runtime = wandboxRust;
break;
default:
language satisfies never;
throw new Error(`Runtime not implemented for language: ${language}`);
}
const { init } = runtime;
useEffect(() => {
init?.();
}, [init]);
return runtime;
}
export function RuntimeProvider({ children }: { children: ReactNode }) {
return (
<WorkerProvider context={PyodideContext} lang="python">
<WorkerProvider context={RubyContext} lang="ruby">
<WorkerProvider context={JSEvalContext} lang="javascript">
<WandboxProvider>
<TypeScriptProvider>{children}</TypeScriptProvider>
</WandboxProvider>
</WorkerProvider>
</WorkerProvider>
</WorkerProvider>
);
}
export function langConstants(lang: RuntimeLang | AceLang): LangConstants {
switch (lang) {
case "python":
return {
tabSize: 4,
prompt: ">>> ",
promptMore: "... ",
};
case "ruby":
return {
tabSize: 2,
// TODO: 実際のirbのプロンプトは静的でなく、(main)や番号などの動的な表示がある
prompt: "irb> ",
promptMore: "irb* ",
returnPrefix: "=> ",
};
case "javascript":
case "typescript":
return {
tabSize: 2,
prompt: "> ",
promptMore: "... ",
};
case "c_cpp":
case "cpp":
return {
// 2文字派と4文字派があるが、geminiが4文字で出力するので4でいいや
tabSize: 4,
};
case "rust":
return {
tabSize: 4,
};
case "json":
return {
// python-7章で使っている
tabSize: 4,
};
case "csv":
case "text":
return {
// tabは使わないが、0は指定できないようなので適当にデフォルト値
tabSize: 4,
};
default:
lang satisfies never;
throw new Error(`LangConstants not defined for language: ${lang}`);
}
}
export const emptyMutex: MutexInterface = {
async runExclusive<T>(fn: () => Promise<T> | T) {
const result = fn();
return result instanceof Promise ? result : Promise.resolve(result);
},
acquire: async () => {
return () => {}; // Release function (no-op)
},
waitForUnlock: async () => {},
isLocked: () => false,
cancel: () => {},
release: () => {},
};