22
33import chalk from "chalk" ;
44import { usePyodide } from "./python/pyodide" ;
5- import { clearTerminal , getRows , hideCursor , useTerminal } from "./terminal" ;
5+ import {
6+ clearTerminal ,
7+ getRows ,
8+ hideCursor ,
9+ systemMessageColor ,
10+ useTerminal ,
11+ } from "./terminal" ;
612import { useSectionCode } from "../[docs_id]/section" ;
713import { useWandbox } from "./wandbox/wandbox" ;
14+ import { ReplOutput , writeOutput } from "./repl" ;
15+ import { useState } from "react" ;
816
917export type ExecLang = "python" | "cpp" ;
1018
@@ -32,44 +40,25 @@ export function ExecFile(props: ExecProps) {
3240 const pyodide = usePyodide ( ) ;
3341 const wandbox = useWandbox ( ) ;
3442
43+ // 表示するコマンドライン文字列
3544 let commandline : string ;
36- let exec : ( ) => Promise < void > | void ;
45+ // trueの間 (初期化しています...) と表示される
3746 let runtimeInitializing : boolean ;
47+ // 初期化処理が必要な場合の関数
48+ let beforeExec : ( ( ) => Promise < void > ) | null = null ;
49+ // 実行中です... と表示される
50+ const [ isExecuting , setIsExecuting ] = useState < boolean > ( false ) ;
51+ // 実際に実行する関数
52+ let exec : ( ( ) => Promise < ReplOutput [ ] > ) | null = null ;
3853 switch ( props . language ) {
3954 case "python" :
4055 if ( props . filenames . length !== 1 ) {
4156 throw new Error ( "Pythonの実行にはファイル名が1つ必要です" ) ;
4257 }
4358 commandline = `python ${ props . filenames [ 0 ] } ` ;
4459 runtimeInitializing = pyodide . initializing ;
45- exec = async ( ) => {
46- if ( ! pyodide . ready ) {
47- clearTerminal ( terminalInstanceRef . current ! ) ;
48- terminalInstanceRef . current ! . write (
49- chalk . dim . bold . italic ( "(初期化しています...しばらくお待ちください)" )
50- ) ;
51- await pyodide . init ( ) ;
52- }
53- clearTerminal ( terminalInstanceRef . current ! ) ;
54- const outputs = await pyodide . runFile ( props . filenames [ 0 ] ) ;
55- for ( let i = 0 ; i < outputs . length ; i ++ ) {
56- const output = outputs [ i ] ;
57- if ( i > 0 ) {
58- terminalInstanceRef . current ! . writeln ( "" ) ;
59- }
60- // 出力内容に応じて色を変える
61- const message = String ( output . message ) . replace ( / \n / g, "\r\n" ) ;
62- switch ( output . type ) {
63- case "error" :
64- terminalInstanceRef . current ! . writeln ( chalk . red ( message ) ) ;
65- break ;
66- default :
67- terminalInstanceRef . current ! . writeln ( message ) ;
68- break ;
69- }
70- }
71- sectionContext ?. setExecResult ( props . filenames [ 0 ] , outputs ) ;
72- } ;
60+ beforeExec = pyodide . ready ? null : pyodide . init ;
61+ exec = ( ) => pyodide . runFile ( props . filenames [ 0 ] ) ;
7362 break ;
7463 case "cpp" :
7564 if ( ! props . filenames || props . filenames . length === 0 ) {
@@ -79,52 +68,47 @@ export function ExecFile(props: ExecProps) {
7968 ? `${ wandbox . cppOptions . commandline } ${ props . filenames . join ( " " ) } && ./a.out`
8069 : "" ;
8170 runtimeInitializing = false ;
82- exec = async ( ) => {
83- clearTerminal ( terminalInstanceRef . current ! ) ;
84- const namesSource = props . filenames ! . filter ( ( name ) =>
85- name . endsWith ( ".cpp" )
86- ) ;
87- const namesAdditional = props . filenames ! . filter (
88- ( name ) => ! name . endsWith ( ".cpp" )
89- ) ;
90- const outputs = await wandbox . runFiles (
91- "C++" ,
92- namesSource ,
93- namesAdditional
94- ) ;
95- for ( let i = 0 ; i < outputs . length ; i ++ ) {
96- const output = outputs [ i ] ;
97- if ( i > 0 ) {
98- terminalInstanceRef . current ! . writeln ( "" ) ;
99- }
100- // 出力内容に応じて色を変える
101- const message = String ( output . message ) . replace ( / \n / g, "\r\n" ) ;
102- switch ( output . type ) {
103- case "error" :
104- terminalInstanceRef . current ! . write ( chalk . red ( message ) ) ;
105- break ;
106- default :
107- terminalInstanceRef . current ! . write ( message ) ;
108- break ;
109- }
110- }
111- // TODO: 1つのファイル名しか受け付けないところに無理やりコンマ区切りで全部のファイル名を突っ込んでいる
112- sectionContext ?. setExecResult ( props . filenames . join ( "," ) , outputs ) ;
113- } ;
71+ const namesSource = props . filenames ! . filter ( ( name ) =>
72+ name . endsWith ( ".cpp" )
73+ ) ;
74+ const namesAdditional = props . filenames ! . filter (
75+ ( name ) => ! name . endsWith ( ".cpp" )
76+ ) ;
77+ exec = ( ) => wandbox . runFiles ( "C++" , namesSource , namesAdditional ) ;
11478 break ;
11579 default :
11680 props . language satisfies never ;
11781 commandline = `エラー: 非対応の言語 ${ props . language } ` ;
11882 runtimeInitializing = false ;
119- exec = ( ) => undefined ;
12083 break ;
12184 }
85+
86+ const onClick = async ( ) => {
87+ if ( exec ) {
88+ if ( beforeExec ) {
89+ clearTerminal ( terminalInstanceRef . current ! ) ;
90+ terminalInstanceRef . current ! . write (
91+ systemMessageColor ( "(初期化しています...しばらくお待ちください)" )
92+ ) ;
93+ await beforeExec ( ) ;
94+ }
95+ clearTerminal ( terminalInstanceRef . current ! ) ;
96+ terminalInstanceRef . current ! . write ( systemMessageColor ( "実行中です..." ) ) ;
97+ setIsExecuting ( true ) ;
98+ const outputs = await exec ( ) ;
99+ setIsExecuting ( false ) ;
100+ clearTerminal ( terminalInstanceRef . current ! ) ;
101+ writeOutput ( terminalInstanceRef . current ! , outputs , false ) ;
102+ // TODO: 1つのファイル名しか受け付けないところに無理やりコンマ区切りで全部のファイル名を突っ込んでいる
103+ sectionContext ?. setExecResult ( props . filenames . join ( "," ) , outputs ) ;
104+ }
105+ } ;
122106 return (
123107 < div className = "relative" >
124108 < div >
125109 < button
126110 className = "btn btn-soft btn-primary rounded-tl-lg rounded-none"
127- onClick = { exec }
111+ onClick = { onClick }
128112 disabled = { ! termReady || runtimeInitializing }
129113 >
130114 ▶ 実行
@@ -134,7 +118,7 @@ export function ExecFile(props: ExecProps) {
134118 < div className = "bg-base-300 p-4 pt-2 rounded-b-lg" >
135119 < div ref = { terminalRef } />
136120 </ div >
137- { runtimeInitializing && (
121+ { ( runtimeInitializing || isExecuting ) && (
138122 < div className = "absolute z-10 inset-0 cursor-wait" />
139123 ) }
140124 </ div >
0 commit comments