Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions app/terminal/exec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
systemMessageColor,
useTerminal,
} from "./terminal";
import { writeOutput } from "./repl";
import { writeOutput, ReplOutput } from "./repl";
import { useEffect, useState } from "react";
import { useEmbedContext } from "./embedContext";
import { RuntimeLang, useRuntime } from "./runtime";
Expand Down Expand Up @@ -46,16 +46,24 @@ export function ExecFile(props: ExecProps) {
(async () => {
clearTerminal(terminalInstanceRef.current!);
terminalInstanceRef.current!.write(systemMessageColor("実行中です..."));
const outputs = await runFiles(props.filenames, files);
clearTerminal(terminalInstanceRef.current!);
writeOutput(
terminalInstanceRef.current!,
outputs,
false,
undefined,
null, // ファイル実行で"return"メッセージが返ってくることはないはずなので、Prismを渡す必要はない
props.language
);
const outputs: ReplOutput[] = [];
let isFirstOutput = true;
await runFiles(props.filenames, files, (output) => {
outputs.push(output);
if (isFirstOutput) {
// Clear "実行中です..." message only on first output
clearTerminal(terminalInstanceRef.current!);
isFirstOutput = false;
}
// Append only the new output
writeOutput(
terminalInstanceRef.current!,
output,
undefined,
null, // ファイル実行で"return"メッセージが返ってくることはないはずなので、Prismを渡す必要はない
props.language
);
});
// TODO: 1つのファイル名しか受け付けないところに無理やりコンマ区切りで全部のファイル名を突っ込んでいる
setExecResult(props.filenames.join(","), outputs);
setExecutionState("idle");
Expand Down
112 changes: 60 additions & 52 deletions app/terminal/repl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import { useEmbedContext } from "./embedContext";
import { emptyMutex, langConstants, RuntimeLang, useRuntime } from "./runtime";
import clsx from "clsx";

export type ReplOutputType = "stdout" | "stderr" | "error" | "return" | "trace" | "system";
export type ReplOutputType =
| "stdout"
| "stderr"
| "error"
| "return"
| "trace"
| "system";
export interface ReplOutput {
type: ReplOutputType; // 出力の種類
message: string; // 出力メッセージ
Expand All @@ -30,47 +36,37 @@ export type SyntaxStatus = "complete" | "incomplete" | "invalid"; // 構文チ

export function writeOutput(
term: Terminal,
outputs: ReplOutput[],
endNewLine: boolean,
output: ReplOutput,
returnPrefix: string | undefined,
Prism: typeof import("prismjs") | null,
language: RuntimeLang
) {
for (let i = 0; i < outputs.length; i++) {
const output = outputs[i];
if (i > 0) {
term.writeln("");
}
// 出力内容に応じて色を変える
const message = String(output.message).replace(/\n/g, "\r\n");
switch (output.type) {
case "error":
term.write(chalk.red(message));
break;
case "trace":
term.write(chalk.blue.italic(message));
break;
case "system":
term.write(systemMessageColor(message));
break;
case "return":
if (returnPrefix) {
term.write(returnPrefix);
}
if (Prism) {
term.write(highlightCodeToAnsi(Prism, message, language));
} else {
console.warn("Prism is not loaded, cannot highlight return value");
term.write(message);
}
break;
default:
term.write(message);
break;
}
}
if (endNewLine && outputs.length > 0) {
term.writeln("");
// 出力内容に応じて色を変える
const message = String(output.message).replace(/\n/g, "\r\n");
switch (output.type) {
case "error":
term.writeln(chalk.red(message));
break;
case "trace":
term.writeln(chalk.blue.italic(message));
break;
case "system":
term.writeln(systemMessageColor(message));
break;
case "return":
if (returnPrefix) {
term.write(returnPrefix);
}
if (Prism) {
term.writeln(highlightCodeToAnsi(Prism, message, language));
} else {
console.warn("Prism is not loaded, cannot highlight return value");
term.writeln(message);
}
break;
default:
term.writeln(message);
break;
}
}

Expand Down Expand Up @@ -176,21 +172,18 @@ export function ReplTerminal({

// ランタイムからのoutputを描画し、inputBufferをリセット
const handleOutput = useCallback(
(outputs: ReplOutput[]) => {
(output: ReplOutput) => {
if (terminalInstanceRef.current) {
writeOutput(
terminalInstanceRef.current,
outputs,
true,
output,
returnPrefix,
Prism,
language
);
// 出力が終わったらプロンプトを表示
updateBuffer(() => [""]);
}
},
[Prism, updateBuffer, terminalInstanceRef, returnPrefix, language]
[Prism, terminalInstanceRef, returnPrefix, language]
);

const keyHandler = useCallback(
Expand Down Expand Up @@ -220,11 +213,15 @@ export function ReplTerminal({
terminalInstanceRef.current.writeln("");
const command = inputBuffer.current.join("\n").trim();
inputBuffer.current = [];
const outputs = await runtimeMutex.runExclusive(() =>
runCommand(command)
);
handleOutput(outputs);
addReplOutput?.(terminalId, command, outputs);
const collectedOutputs: ReplOutput[] = [];
await runtimeMutex.runExclusive(async () => {
await runCommand(command, (output) => {
collectedOutputs.push(output);
handleOutput(output);
});
});
updateBuffer(() => [""]);
addReplOutput?.(terminalId, command, collectedOutputs);
}
} else if (code === 127) {
// Backspace
Expand Down Expand Up @@ -301,8 +298,13 @@ export function ReplTerminal({
updateBuffer(() => cmd.command.split("\n"));
terminalInstanceRef.current!.writeln("");
inputBuffer.current = [];
handleOutput(cmd.output);
for (const output of cmd.output) {
handleOutput(output);
}
updateBuffer(() => [""]);
}
} else {
updateBuffer(() => [""]);
}
terminalInstanceRef.current!.scrollToTop();
setInitCommandState("idle");
Expand All @@ -320,7 +322,10 @@ export function ReplTerminal({
const initCommandResult: ReplCommand[] = [];
await runtimeMutex.runExclusive(async () => {
for (const cmd of initCommand!) {
const outputs = await runCommand(cmd.command);
const outputs: ReplOutput[] = [];
await runCommand(cmd.command, (output) => {
outputs.push(output);
});
initCommandResult.push({
command: cmd.command,
output: outputs,
Expand All @@ -333,7 +338,10 @@ export function ReplTerminal({
updateBuffer(() => cmd.command.split("\n"));
terminalInstanceRef.current!.writeln("");
inputBuffer.current = [];
handleOutput(cmd.output);
for (const output of cmd.output) {
handleOutput(output);
}
updateBuffer(() => [""]);
}
}
updateBuffer(() => [""]);
Expand Down
10 changes: 7 additions & 3 deletions app/terminal/runtime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ export interface RuntimeContext {
mutex?: MutexInterface;
interrupt?: () => void;
// repl
runCommand?: (command: string) => Promise<ReplOutput[]>;
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>>
) => Promise<ReplOutput[]>;
files: Readonly<Record<string, string>>,
onOutput: (output: ReplOutput) => void
) => Promise<void>;
getCommandlineStr?: (filenames: string[]) => string;
}
export interface LangConstants {
Expand Down
Loading