Skip to content

Commit 76bd878

Browse files
committed
言語の定義をリファクタ、1箇所に統一
1 parent f5fa9ac commit 76bd878

File tree

9 files changed

+187
-251
lines changed

9 files changed

+187
-251
lines changed

app/[lang]/[pageId]/markdown.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@ import remarkCjkFriendly from "remark-cjk-friendly";
55
import { EditorComponent } from "@/terminal/editor";
66
import { ExecFile } from "@/terminal/exec";
77
import { JSX, ReactNode } from "react";
8-
import { getAceLang, getRuntimeLang, MarkdownLang } from "@my-code/runtime/languages";
8+
import { langConstants, MarkdownLang } from "@my-code/runtime/languages";
99
import { ReplTerminal } from "@/terminal/repl";
10-
import {
11-
getSyntaxHighlighterLang,
12-
StyledSyntaxHighlighter,
13-
} from "./styledSyntaxHighlighter";
10+
import { StyledSyntaxHighlighter } from "./styledSyntaxHighlighter";
1411

1512
export function StyledMarkdown({ content }: { content: string }) {
1613
return (
@@ -92,7 +89,7 @@ function CodeComponent({
9289
className || ""
9390
);
9491
if (match) {
95-
const runtimeLang = getRuntimeLang(match[1] as MarkdownLang | undefined);
92+
const language = langConstants(match[1] as MarkdownLang | undefined);
9693
if (match[2] === "-exec" && match[3]) {
9794
/*
9895
```python-exec:main.py
@@ -104,10 +101,10 @@ function CodeComponent({
104101
hello, world!
105102
---------------------------
106103
*/
107-
if (runtimeLang) {
104+
if (language.runtime) {
108105
return (
109106
<ExecFile
110-
language={runtimeLang}
107+
language={language}
111108
filenames={match[3].split(",")}
112109
content={String(props.children || "").replace(/\n$/, "")}
113110
/>
@@ -120,39 +117,35 @@ function CodeComponent({
120117
`${match[1]}-repl without terminal id! content: ${String(props.children).slice(0, 20)}...`
121118
);
122119
}
123-
if (runtimeLang) {
120+
if (language.runtime) {
124121
return (
125122
<ReplTerminal
126123
terminalId={match[3]}
127-
language={runtimeLang}
124+
language={language}
128125
initContent={String(props.children || "").replace(/\n$/, "")}
129126
/>
130127
);
131128
}
132129
} else if (match[3]) {
133130
// ファイル名指定がある場合、ファイルエディター
134-
const aceLang = getAceLang(match[1] as MarkdownLang | undefined);
135131
return (
136132
<EditorComponent
137-
language={aceLang}
133+
language={language}
138134
filename={match[3]}
139135
readonly={match[2] === "-readonly"}
140136
initContent={String(props.children || "").replace(/\n$/, "")}
141137
/>
142138
);
143139
}
144-
const syntaxHighlighterLang = getSyntaxHighlighterLang(
145-
match[1] as MarkdownLang | undefined
146-
);
147140
return (
148-
<StyledSyntaxHighlighter language={syntaxHighlighterLang}>
141+
<StyledSyntaxHighlighter language={language}>
149142
{String(props.children || "").replace(/\n$/, "")}
150143
</StyledSyntaxHighlighter>
151144
);
152145
} else if (String(props.children).includes("\n")) {
153146
// 言語指定なしコードブロック
154147
return (
155-
<StyledSyntaxHighlighter language={undefined}>
148+
<StyledSyntaxHighlighter language={langConstants(undefined)}>
156149
{String(props.children || "").replace(/\n$/, "")}
157150
</StyledSyntaxHighlighter>
158151
);
@@ -170,4 +163,4 @@ export function InlineCode({ children }: { children: ReactNode }) {
170163
{children}
171164
</code>
172165
);
173-
}
166+
}

app/[lang]/[pageId]/styledSyntaxHighlighter.tsx

Lines changed: 3 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
tomorrowNight,
77
} from "react-syntax-highlighter/dist/esm/styles/hljs";
88
import { lazy, Suspense, useEffect, useState } from "react";
9-
import { MarkdownLang, SyntaxHighlighterLang } from "@my-code/runtime/languages";
9+
import { LangConstants } from "@my-code/runtime/languages";
1010

1111
// SyntaxHighlighterはファイルサイズがでかいので & HydrationErrorを起こすので、SSRを無効化する
1212
const SyntaxHighlighter = lazy(() => {
@@ -17,59 +17,9 @@ const SyntaxHighlighter = lazy(() => {
1717
}
1818
});
1919

20-
export function getSyntaxHighlighterLang(
21-
lang: MarkdownLang | undefined
22-
): SyntaxHighlighterLang | undefined {
23-
switch (lang) {
24-
case "python":
25-
case "py":
26-
return "python";
27-
case "ruby":
28-
case "rb":
29-
return "ruby";
30-
case "cpp":
31-
case "c++":
32-
return "cpp";
33-
case "rust":
34-
case "rs":
35-
return "rust";
36-
case "javascript":
37-
case "js":
38-
return "javascript";
39-
case "typescript":
40-
case "ts":
41-
return "typescript";
42-
case "bash":
43-
case "sh":
44-
return "bash";
45-
case "powershell":
46-
return "powershell";
47-
case "json":
48-
return "json";
49-
case "toml":
50-
return "ini";
51-
case "html":
52-
return "html";
53-
case "makefile":
54-
return "makefile";
55-
case "cmake":
56-
return "cmake";
57-
case "csv": // not supported
58-
case "text":
59-
case "txt":
60-
case undefined:
61-
return undefined;
62-
default:
63-
lang satisfies never;
64-
console.error(
65-
`getSyntaxHighlighterLang() does not handle language ${lang}`
66-
);
67-
return undefined;
68-
}
69-
}
7020
export function StyledSyntaxHighlighter(props: {
7121
children: string;
72-
language: SyntaxHighlighterLang | undefined;
22+
language: LangConstants;
7323
}) {
7424
const theme = useChangeTheme();
7525
const codetheme = theme === "tomorrow" ? tomorrow : tomorrowNight;
@@ -80,7 +30,7 @@ export function StyledSyntaxHighlighter(props: {
8030
return initHighlighter ? (
8131
<Suspense fallback={<FallbackPre>{props.children}</FallbackPre>}>
8232
<SyntaxHighlighter
83-
language={props.language}
33+
language={props.language.rsh}
8434
PreTag="div"
8535
className="border-2 border-current/20 mx-2 my-2 rounded-box p-4! bg-base-300! text-base-content!"
8636
style={codetheme}

app/terminal/editor.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { lazy, Suspense, useEffect, useState } from "react";
44
import clsx from "clsx";
55
import { useChangeTheme } from "@/themeToggle";
66
import { useEmbedContext } from "./embedContext";
7-
import { AceLang, langConstants } from "@my-code/runtime/languages";
7+
import { LangConstants } from "@my-code/runtime/languages";
88

99
// https://github.com/securingsincity/react-ace/issues/27 により普通のimportができない
1010
const AceEditor = lazy(async () => {
@@ -32,7 +32,7 @@ const AceEditor = lazy(async () => {
3232
});
3333

3434
interface EditorProps {
35-
language?: AceLang;
35+
language: LangConstants;
3636
filename: string;
3737
initContent: string;
3838
readonly?: boolean;
@@ -58,6 +58,15 @@ export function EditorComponent(props: EditorProps) {
5858
// 最小8行 or 初期内容+1行
5959
const editorHeight = Math.max(props.initContent.split("\n").length + 1, 8);
6060

61+
if (
62+
process.env.NODE_ENV === "development" &&
63+
props.language.ace === undefined
64+
) {
65+
throw new Error(
66+
`language ${props.language.originalLang} does not have ace mode defined!`
67+
);
68+
}
69+
6170
return (
6271
<div className="border border-accent border-2 shadow-md m-2 rounded-box overflow-hidden">
6372
<div className="flex flex-row items-center bg-base-200">
@@ -108,9 +117,9 @@ export function EditorComponent(props: EditorProps) {
108117
>
109118
<AceEditor
110119
name={`ace-editor-${props.filename}`}
111-
mode={props.language}
120+
mode={props.language.ace ?? "text"}
112121
theme={theme}
113-
tabSize={langConstants(props.language || "text").tabSize}
122+
tabSize={props.language.tabSize ?? 4}
114123
width="100%"
115124
height={editorHeight * (fontSize + 1) + "px"}
116125
className="font-mono!" // Aceのデフォルトフォントを上書き

app/terminal/exec.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { writeOutput } from "./repl";
1111
import { useEffect, useState } from "react";
1212
import { useEmbedContext } from "./embedContext";
1313
import clsx from "clsx";
14-
import { RuntimeLang } from "@my-code/runtime/languages";
14+
import { LangConstants } from "@my-code/runtime/languages";
1515
import { useRuntime } from "@my-code/runtime/context";
1616

1717
interface ExecProps {
@@ -20,7 +20,7 @@ interface ExecProps {
2020
* C++の場合はソースコード(.cpp)を全部指定する。
2121
*/
2222
filenames: string[];
23-
language: RuntimeLang;
23+
language: LangConstants;
2424
content: string;
2525
}
2626
export function ExecFile(props: ExecProps) {
@@ -33,10 +33,16 @@ export function ExecFile(props: ExecProps) {
3333
}
3434
},
3535
});
36-
const { files, clearExecResult, addExecOutput, writeFile } = useEmbedContext();
36+
const { files, clearExecResult, addExecOutput, writeFile } =
37+
useEmbedContext();
3738

39+
if (props.language.runtime === undefined) {
40+
throw new Error(
41+
`Language ${props.language.originalLang} does not have a runtime environment.`
42+
);
43+
}
3844
const { ready, runFiles, getCommandlineStr, runtimeInfo, interrupt } =
39-
useRuntime(props.language);
45+
useRuntime(props.language.runtime);
4046

4147
// ユーザーがクリックした時(triggered) && ランタイムが準備できた時に、実際にinitCommandを実行する(executing)
4248
const [executionState, setExecutionState] = useState<
@@ -135,7 +141,7 @@ export function ExecFile(props: ExecProps) {
135141
<div className="tooltip-content bg-secondary/60 backdrop-blur-xs">
136142
ブラウザ上で動作する
137143
<span className="mx-0.5">
138-
{runtimeInfo?.prettyLangName || props.language}
144+
{runtimeInfo?.prettyLangName || props.language.runtime}
139145
</span>
140146
{runtimeInfo?.version && (
141147
<span className="mr-0.5">{runtimeInfo?.version}</span>

app/terminal/highlight.ts

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import chalk from "chalk";
22
chalk.level = 3;
3-
import { RuntimeLang } from "@my-code/runtime/languages";
3+
import { LangConstants } from "@my-code/runtime/languages";
44

55
export async function importPrism() {
66
if (typeof window !== "undefined") {
@@ -15,28 +15,6 @@ export async function importPrism() {
1515
}
1616
}
1717

18-
type PrismLang = "python" | "ruby" | "javascript";
19-
20-
function getPrismLanguage(language: RuntimeLang): PrismLang {
21-
switch (language) {
22-
case "python":
23-
return "python";
24-
case "ruby":
25-
return "ruby";
26-
case "javascript":
27-
return "javascript";
28-
case "cpp":
29-
case "typescript":
30-
case "rust":
31-
throw new Error(
32-
`highlight for ${language} is disabled because it should not support REPL`
33-
);
34-
default:
35-
language satisfies never;
36-
throw new Error(`Prism language not implemented for: ${language}`);
37-
}
38-
}
39-
4018
const nothing = (text: string): string => text;
4119

4220
// PrismのトークンクラスとANSIコードをマッピング
@@ -87,13 +65,19 @@ const prismToAnsi: Record<string, (text: string) => string> = {
8765
export function highlightCodeToAnsi(
8866
Prism: typeof import("prismjs"),
8967
code: string,
90-
language: RuntimeLang
68+
language: LangConstants
9169
): string {
70+
if (!language.prism) {
71+
console.error(
72+
`Language ${language.originalLang} does not have a Prism definition.`
73+
);
74+
return code;
75+
}
9276
// Prismでハイライト処理を行い、HTML文字列を取得
9377
const highlightedHtml = Prism.highlight(
9478
code,
95-
Prism.languages[getPrismLanguage(language)],
96-
getPrismLanguage(language)
79+
Prism.languages[language.prism],
80+
language.prism
9781
);
9882

9983
// 一時的なDOM要素を作成してパース
@@ -141,4 +125,3 @@ export function highlightCodeToAnsi(
141125
""
142126
);
143127
}
144-

app/terminal/page.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import "mocha/mocha.css";
55
import { Fragment, useEffect, useRef, useState } from "react";
66
import { useWandbox } from "@my-code/runtime/wandbox/runtime";
77
import { RuntimeContext } from "@my-code/runtime/interface";
8-
import { getAceLang, RuntimeLang } from "@my-code/runtime/languages";
9-
import { useEmbedContext } from "./embedContext";
8+
import { langConstants, RuntimeLang } from "@my-code/runtime/languages";
109
import { defineTests } from "@my-code/runtime/tests";
1110
import { usePyodide } from "@my-code/runtime/worker/pyodide";
1211
import { useRuby } from "@my-code/runtime/worker/ruby";
@@ -130,26 +129,26 @@ function RuntimeSample({
130129
{config.repl && (
131130
<ReplTerminal
132131
terminalId="1"
133-
language={lang}
132+
language={langConstants(lang)}
134133
initContent={config.replInitContent}
135134
/>
136135
)}
137136
{config.editor &&
138137
Object.entries(config.editor).map(([filename, initContent]) => (
139138
<EditorComponent
140139
key={filename}
141-
language={getAceLang(lang)}
140+
language={langConstants(lang)}
142141
filename={filename}
143142
initContent={initContent}
144143
/>
145144
))}
146145
{config.exec && (
147-
<ExecFile filenames={config.exec} language={lang} content="" />
146+
<ExecFile filenames={config.exec} language={langConstants(lang)} content="" />
148147
)}
149148
{config.readonlyFiles?.map((filename) => (
150149
<EditorComponent
151150
key={filename}
152-
language={getAceLang(lang)}
151+
language={langConstants(lang)}
153152
filename={filename}
154153
initContent=""
155154
readonly

0 commit comments

Comments
 (0)