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
72 changes: 62 additions & 10 deletions app/terminal/embedContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@ interface IEmbedContext {
) => Promise<Readonly<Record<Filename, string>>>;

replOutputs: Readonly<Record<TerminalId, ReplCommand[]>>;
addReplCommand: (terminalId: TerminalId, command: string) => string;
addReplOutput: (
terminalId: TerminalId,
command: string,
output: ReplOutput[]
commandId: string,
output: ReplOutput
) => void;

execResults: Readonly<Record<Filename, ReplOutput[]>>;
setExecResult: (filename: Filename, output: ReplOutput[]) => void;
clearExecResult: (filename: Filename) => void;
addExecOutput: (filename: Filename, output: ReplOutput) => void;
}
const EmbedContext = createContext<IEmbedContext>(null!);

Expand All @@ -63,6 +65,9 @@ export function EmbedContextProvider({ children }: { children: ReactNode }) {
const [replOutputs, setReplOutputs] = useState<
Record<TerminalId, ReplCommand[]>
>({});
const [commandIdCounters, setCommandIdCounters] = useState<
Record<TerminalId, number>
>({});
const [execResults, setExecResults] = useState<
Record<Filename, ReplOutput[]>
>({});
Expand All @@ -71,6 +76,7 @@ export function EmbedContextProvider({ children }: { children: ReactNode }) {
if (pathname && pathname !== currentPathname) {
setCurrentPathname(pathname);
setReplOutputs({});
setCommandIdCounters({});
setExecResults({});
}
}, [pathname, currentPathname]);
Expand Down Expand Up @@ -100,26 +106,70 @@ export function EmbedContextProvider({ children }: { children: ReactNode }) {
},
[pathname]
);
const addReplOutput = useCallback(
(terminalId: TerminalId, command: string, output: ReplOutput[]) =>
const addReplCommand = useCallback(
(terminalId: TerminalId, command: string): string => {
let commandId = "";
setCommandIdCounters((counters) => {
const newCounters = { ...counters };
const currentCount = newCounters[terminalId] ?? 0;
commandId = String(currentCount);
newCounters[terminalId] = currentCount + 1;
return newCounters;
});
setReplOutputs((outs) => {
outs = { ...outs };
if (!(terminalId in outs)) {
outs[terminalId] = [];
}
outs[terminalId] = [
...outs[terminalId],
{ command: command, output: output },
{ command: command, output: [], commandId },
];
return outs;
});
return commandId;
},
[]
);
const addReplOutput = useCallback(
(terminalId: TerminalId, commandId: string, output: ReplOutput) =>
setReplOutputs((outs) => {
outs = { ...outs };
if (terminalId in outs) {
outs[terminalId] = [...outs[terminalId]];
// Find the command by commandId
const commandIndex = outs[terminalId].findIndex(
(cmd) => cmd.commandId === commandId
);
if (commandIndex >= 0) {
const command = outs[terminalId][commandIndex];
outs[terminalId][commandIndex] = {
...command,
output: [...command.output, output],
};
}
}
return outs;
}),
[]
);
const clearExecResult = useCallback(
(filename: Filename) =>
setExecResults((results) => {
results = { ...results };
results[filename] = [];
return results;
}),
[]
);
const setExecResult = useCallback(
(filename: Filename, output: ReplOutput[]) =>
const addExecOutput = useCallback(
(filename: Filename, output: ReplOutput) =>
setExecResults((results) => {
results = { ...results };
results[filename] = output;
if (!(filename in results)) {
results[filename] = [];
}
results[filename] = [...results[filename], output];
return results;
}),
[]
Expand All @@ -131,9 +181,11 @@ export function EmbedContextProvider({ children }: { children: ReactNode }) {
files: files[pathname] || {},
writeFile,
replOutputs,
addReplCommand,
addReplOutput,
execResults,
setExecResult,
clearExecResult,
addExecOutput,
}}
>
{children}
Expand Down
14 changes: 7 additions & 7 deletions app/terminal/exec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function ExecFile(props: ExecProps) {
}
},
});
const { files, setExecResult } = useEmbedContext();
const { files, clearExecResult, addExecOutput } = useEmbedContext();

const { ready, runFiles, getCommandlineStr } = useRuntime(props.language);

Expand All @@ -46,10 +46,12 @@ export function ExecFile(props: ExecProps) {
(async () => {
clearTerminal(terminalInstanceRef.current!);
terminalInstanceRef.current!.write(systemMessageColor("実行中です..."));
const outputs: ReplOutput[] = [];
// TODO: 1つのファイル名しか受け付けないところに無理やりコンマ区切りで全部のファイル名を突っ込んでいる
const filenameKey = props.filenames.join(",");
clearExecResult(filenameKey);
let isFirstOutput = true;
await runFiles(props.filenames, files, (output) => {
outputs.push(output);
addExecOutput(filenameKey, output);
if (isFirstOutput) {
// Clear "実行中です..." message only on first output
clearTerminal(terminalInstanceRef.current!);
Expand All @@ -63,10 +65,7 @@ export function ExecFile(props: ExecProps) {
null, // ファイル実行で"return"メッセージが返ってくることはないはずなので、Prismを渡す必要はない
props.language
);
// TODO: 実行が完了したあとに出力された場合、embedContextのsetExecResultにも出力を追加する必要があるが、それに対応したAPIになっていない
});
// TODO: 1つのファイル名しか受け付けないところに無理やりコンマ区切りで全部のファイル名を突っ込んでいる
setExecResult(props.filenames.join(","), outputs);
setExecutionState("idle");
})();
}
Expand All @@ -75,7 +74,8 @@ export function ExecFile(props: ExecProps) {
ready,
props.filenames,
runFiles,
setExecResult,
clearExecResult,
addExecOutput,
terminalInstanceRef,
props.language,
files,
Expand Down
10 changes: 5 additions & 5 deletions app/terminal/repl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface ReplOutput {
export interface ReplCommand {
command: string;
output: ReplOutput[];
commandId?: string; // Optional for backward compatibility
}
export type SyntaxStatus = "complete" | "incomplete" | "invalid"; // 構文チェックの結果

Expand Down Expand Up @@ -80,7 +81,7 @@ export function ReplTerminal({
language,
initContent,
}: ReplComponentProps) {
const { addReplOutput } = useEmbedContext();
const { addReplCommand, addReplOutput } = useEmbedContext();

const [Prism, setPrism] = useState<typeof import("prismjs") | null>(null);
useEffect(() => {
Expand Down Expand Up @@ -217,7 +218,7 @@ export function ReplTerminal({
terminalInstanceRef.current.writeln("");
const command = inputBuffer.current.join("\n").trim();
inputBuffer.current = [];
const collectedOutputs: ReplOutput[] = [];
const commandId = addReplCommand(terminalId, command);
let executionDone = false;
await runtimeMutex.runExclusive(async () => {
await runCommand(command, (output) => {
Expand All @@ -226,16 +227,14 @@ export function ReplTerminal({
updateBuffer(null, () => {
handleOutput(output);
});
// TODO: embedContextのaddReplOutputにも出力を追加する必要があるが、それに対応したAPIになっていない
} else {
collectedOutputs.push(output);
handleOutput(output);
}
addReplOutput(terminalId, commandId, output);
});
});
executionDone = true;
updateBuffer(() => [""]);
addReplOutput?.(terminalId, command, collectedOutputs);
}
} else if (code === 127) {
// Backspace
Expand Down Expand Up @@ -279,6 +278,7 @@ export function ReplTerminal({
runCommand,
handleOutput,
tabSize,
addReplCommand,
addReplOutput,
terminalId,
terminalInstanceRef,
Expand Down