Skip to content

Commit 53f55b3

Browse files
committed
wandbox周りなどリファクタ
1 parent f9e0fe7 commit 53f55b3

File tree

13 files changed

+306
-276
lines changed

13 files changed

+306
-276
lines changed

app/[docs_id]/markdown.tsx

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import Markdown, { Components } from "react-markdown";
22
import remarkGfm from "remark-gfm";
33
import SyntaxHighlighter from "react-syntax-highlighter";
4-
import { PythonEmbeddedTerminal } from "../terminal/python/embedded";
54
import { type AceLang, EditorComponent } from "../terminal/editor";
6-
import { ExecFile, ExecLang } from "../terminal/exec";
5+
import { ExecFile } from "../terminal/exec";
76
import { useChangeTheme } from "./themeToggle";
87
import {
98
tomorrow,
109
atomOneDark,
1110
} from "react-syntax-highlighter/dist/esm/styles/hljs";
1211
import { ReactNode } from "react";
12+
import { RuntimeLang } from "@/terminal/runtime";
13+
import { ReplTerminal } from "@/terminal/repl";
1314

1415
export function StyledMarkdown({ content }: { content: string }) {
1516
return (
@@ -94,6 +95,19 @@ function CodeComponent({
9495
className || ""
9596
);
9697
if (match) {
98+
let runtimeLang: RuntimeLang | undefined = undefined;
99+
switch (match[1]) {
100+
case "python":
101+
runtimeLang = "python";
102+
break;
103+
case "cpp":
104+
case "c++":
105+
runtimeLang = "cpp";
106+
break;
107+
default:
108+
console.warn(`Unsupported language for runtime: ${match[1]}`);
109+
break;
110+
}
97111
if (match[2] === "-exec" && match[3]) {
98112
/*
99113
```python-exec:main.py
@@ -105,24 +119,11 @@ function CodeComponent({
105119
hello, world!
106120
---------------------------
107121
*/
108-
let execLang: ExecLang | undefined = undefined;
109-
switch (match[1]) {
110-
case "python":
111-
execLang = "python";
112-
break;
113-
case "cpp":
114-
case "c++":
115-
execLang = "cpp";
116-
break;
117-
default:
118-
console.warn(`Unsupported language for exec: ${match[1]}`);
119-
break;
120-
}
121-
if (execLang) {
122+
if (runtimeLang) {
122123
return (
123124
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
124125
<ExecFile
125-
language={execLang}
126+
language={runtimeLang}
126127
filenames={match[3].split(",")}
127128
content={String(props.children || "").replace(/\n$/, "")}
128129
/>
@@ -131,25 +132,21 @@ function CodeComponent({
131132
}
132133
} else if (match[2] === "-repl") {
133134
// repl付きの言語指定
134-
// 現状はPythonのみ対応
135135
if (!match[3]) {
136136
console.warn(
137137
`${match[1]}-repl without terminal id! content: ${String(props.children).slice(0, 20)}...`
138138
);
139139
}
140-
switch (match[1]) {
141-
case "python":
142-
return (
143-
<div className="bg-base-300 border border-primary border-2 shadow-md m-2 p-4 pr-1 rounded-lg">
144-
<PythonEmbeddedTerminal
145-
terminalId={match[3]}
146-
content={String(props.children || "").replace(/\n$/, "")}
147-
/>
148-
</div>
149-
);
150-
default:
151-
console.warn(`Unsupported language for repl: ${match[1]}`);
152-
break;
140+
if (runtimeLang) {
141+
return (
142+
<div className="bg-base-300 border border-primary border-2 shadow-md m-2 p-4 pr-1 rounded-lg">
143+
<ReplTerminal
144+
terminalId={match[3]}
145+
language={runtimeLang}
146+
initContent={String(props.children || "").replace(/\n$/, "")}
147+
/>
148+
</div>
149+
);
153150
}
154151
} else if (match[3]) {
155152
// ファイル名指定がある場合、ファイルエディター
@@ -180,7 +177,6 @@ function CodeComponent({
180177
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
181178
<EditorComponent
182179
language={aceLang}
183-
tabSize={4}
184180
filename={match[3]}
185181
readonly={match[2] === "-readonly"}
186182
initContent={String(props.children || "").replace(/\n$/, "")}

app/layout.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ import "./globals.css";
55
import { Navbar } from "./navbar";
66
import { Sidebar } from "./sidebar";
77
import { ReactNode } from "react";
8-
import { PyodideProvider } from "./terminal/python/pyodide";
9-
import { WandboxProvider } from "./terminal/wandbox/wandbox";
108
import { EmbedContextProvider } from "./terminal/embedContext";
119
import { AutoAnonymousLogin } from "./accountMenu";
1210
import { SidebarMdProvider } from "./[docs_id]/dynamicMdContext";
11+
import { RuntimeProvider } from "./terminal/runtime";
1312

1413
export const metadata: Metadata = {
1514
title: "Create Next App",
@@ -25,13 +24,15 @@ export default function RootLayout({
2524
<AutoAnonymousLogin />
2625
<SidebarMdProvider>
2726
<div className="drawer lg:drawer-open">
28-
<input id="drawer-toggle" type="checkbox" className="drawer-toggle" />
27+
<input
28+
id="drawer-toggle"
29+
type="checkbox"
30+
className="drawer-toggle"
31+
/>
2932
<div className="drawer-content flex flex-col">
3033
<Navbar />
3134
<EmbedContextProvider>
32-
<PyodideProvider>
33-
<WandboxProvider>{children}</WandboxProvider>
34-
</PyodideProvider>
35+
<RuntimeProvider>{children}</RuntimeProvider>
3536
</EmbedContextProvider>
3637
</div>
3738
<div className="drawer-side shadow-md z-50">

app/terminal/README.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
# my.code(); Runtime API
22

3-
## runtime.ts
3+
## runtime.tsx
44

55
各言語のランタイムはRuntimeContextインターフェースの実装を返すフックを実装する必要があります。
66

7-
runtime.ts`useRuntime(lang)` は各言語のフックを呼び出し、その中で指定された言語のランタイムを返します。
7+
runtime.tsx`useRuntime(lang)` は各言語のフックを呼び出し、その中で指定された言語のランタイムを返します。
88

99
関数はすべてuseCallbackやuseMemoなどを用いレンダリングごとに同じインスタンスを返すように実装してください。
1010

1111
### 共通
1212

1313
* ready: `boolean`
1414
* ランタイムの初期化が完了したか、不要である場合true
15-
* tabSize: `number`
16-
* REPLおよびコードエディターののタブ幅を指定します。
1715
* mutex?: `MutexInterface`
1816
* ランタイムに排他制御が必要な場合、MutexInterfaceのインスタンスを返してください。
1917
* interrupt?: `() => Promise<void>`
@@ -48,10 +46,6 @@ runtime.ts の `useRuntime(lang)` は各言語のフックを呼び出し、そ
4846
]
4947
```
5048
が返されるようにします。
51-
* prompt?: `string`
52-
* REPLの1行目のプロンプト文字列を指定します。
53-
* promptMore?: `string`
54-
* REPLの2行目以降のプロンプト文字列を指定します。
5549
5650
### ファイル実行用
5751
@@ -61,6 +55,17 @@ runtime.ts の `useRuntime(lang)` は各言語のフックを呼び出し、そ
6155
* getCommandlineStr: `(filenames: string[]) => string`
6256
* 指定されたファイルを実行するためのコマンドライン引数文字列を返します。表示用です。
6357
58+
### LangConstant
59+
60+
言語ごとに固定の定数です。
61+
62+
* tabSize: `number`
63+
* REPLおよびコードエディターののタブ幅を指定します。1以上
64+
* prompt?: `string`
65+
* REPLの1行目のプロンプト文字列を指定します。
66+
* promptMore?: `string`
67+
* REPLの2行目以降のプロンプト文字列を指定します。省略時はpromptが使われます
68+
6469
## embedContext.tsx
6570
6671
Replの実行結果(`replOutputs`)、ユーザーが編集したファイル(`files`)、ファイルの実行結果(`execResults`)の情報を保持します。
@@ -113,6 +118,14 @@ EditorComponent コンポーネントを提供します。
113118
114119
### Pyodide (Python)
115120
116-
Pyodide を web worker で動かしています。
117-
ランタイムの初期化に時間がかかるため、バックグラウンドで初期化を行います。
121+
Pyodide を web worker で動かしています。worker側のスクリプトは /public/python.worker.js にあります。
122+
123+
### Wandbox (C++)
124+
125+
wandbox.org のAPIを利用してC++コードを実行しています。C++以外にもいろいろな言語に対応しています。
126+
127+
APIから利用可能なコンパイラとオプションのリストが得られるので、言語ごとにそこからオプションを選択するロジックを実装しています。
128+
129+
C++ではg++の中でheadでない最新のものを選択し、warningスイッチオン、boost有効、std=最新を指定しています。
130+
また、コード実行時にシグナルハンドラーをユーザーのコードに挿入し、エラー時にスタックトレースを表示する処理とそれをjs側でパースする処理を実装しています。
118131

app/terminal/editor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ import { useEffect } from "react";
2424
import clsx from "clsx";
2525
import { useChangeTheme } from "../[docs_id]/themeToggle";
2626
import { useEmbedContext } from "./embedContext";
27+
import { langConstants } from "./runtime";
2728
// snippetを有効化するにはsnippetもimportする必要がある: import "ace-builds/src-min-noconflict/snippets/python";
2829

2930
// mode-xxxx.js のファイル名と、AceEditorの mode プロパティの値が対応する
3031
export type AceLang = "python" | "c_cpp" | "json" | "csv" | "text";
3132

3233
interface EditorProps {
3334
language?: AceLang;
34-
tabSize: number;
3535
filename: string;
3636
initContent: string;
3737
readonly?: boolean;
@@ -88,7 +88,7 @@ export function EditorComponent(props: EditorProps) {
8888
name={`ace-editor-${props.filename}`}
8989
mode={props.language}
9090
theme={theme}
91-
tabSize={props.tabSize}
91+
tabSize={langConstants(props.language || "text").tabSize}
9292
width="100%"
9393
height={
9494
Math.max((props.initContent.split("\n").length + 2) * 14, 128) + "px"

app/terminal/python/page.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export default function PythonPage() {
1414
/>
1515
<EditorComponent
1616
language="python"
17-
tabSize={4}
1817
filename="main.py"
1918
initContent="print('hello, world!')"
2019
/>
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ export function PyodideProvider({ children }: { children: ReactNode }) {
208208
return initCommands;
209209
}, []);
210210

211+
const getCommandlineStr = useCallback(
212+
(filenames: string[]) => `python ${filenames[0]}`,
213+
[]
214+
);
215+
211216
return (
212217
<PyodideContext.Provider
213218
value={{
@@ -218,10 +223,7 @@ export function PyodideProvider({ children }: { children: ReactNode }) {
218223
runFiles,
219224
interrupt,
220225
splitReplExamples,
221-
prompt: ">>> ",
222-
promptMore: "... ",
223-
tabSize: 4,
224-
getCommandlineStr: (filenames: string[]) => `python ${filenames[0]}`,
226+
getCommandlineStr,
225227
}}
226228
>
227229
{children}

app/terminal/repl.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from "./terminal";
1414
import { Terminal } from "@xterm/xterm";
1515
import { useEmbedContext } from "./embedContext";
16-
import { emptyMutex, RuntimeLang, useRuntime } from "./runtime";
16+
import { emptyMutex, langConstants, RuntimeLang, useRuntime } from "./runtime";
1717

1818
export interface ReplOutput {
1919
type: "stdout" | "stderr" | "error" | "return" | "trace" | "system"; // 出力の種類
@@ -71,15 +71,16 @@ export function ReplTerminal({
7171

7272
const {
7373
ready: runtimeReady,
74-
tabSize,
7574
mutex: runtimeMutex = emptyMutex,
7675
interrupt: runtimeInterrupt,
7776
runCommand,
7877
checkSyntax,
7978
splitReplExamples,
80-
prompt = "> ",
81-
promptMore = prompt,
8279
} = useRuntime(language);
80+
const { tabSize, prompt, promptMore } = langConstants(language);
81+
if (!prompt) {
82+
console.warn(`prompt not defined for language: ${language}`);
83+
}
8384

8485
if (!runCommand) {
8586
throw new Error(`runCommand not available for language: ${language}`);
@@ -136,7 +137,7 @@ export function ReplTerminal({
136137
inputBuffer.current = newBuffer();
137138
for (let i = 0; i < inputBuffer.current.length; i++) {
138139
terminalInstanceRef.current.write(
139-
i === 0 ? prompt : promptMore || prompt
140+
(i === 0 ? prompt : (promptMore ?? prompt)) ?? "> "
140141
);
141142
if (language) {
142143
terminalInstanceRef.current.write(

0 commit comments

Comments
 (0)