| import { useCallback, useState, useRef } from 'react'; |
|
|
| export const LANGUAGE_OPTIONS = [ |
| { value: 'python', label: 'Python', monacoLanguage: 'python', extension: 'py', pistonLang: 'python', pistonVersion: '3.10.0' }, |
| { value: 'python3', label: 'Python 3', monacoLanguage: 'python', extension: 'py', pistonLang: 'python3', pistonVersion: '3.10.0' }, |
| { value: 'javascript', label: 'JavaScript', monacoLanguage: 'javascript', extension: 'js', pistonLang: 'javascript', pistonVersion: '18.15.0' }, |
| { value: 'java', label: 'Java', monacoLanguage: 'java', extension: 'java', pistonLang: 'java', pistonVersion: '15.0.2' }, |
| { value: 'c', label: 'C', monacoLanguage: 'c', extension: 'c', pistonLang: 'c', pistonVersion: '10.2.0' }, |
| { value: 'cpp', label: 'C++', monacoLanguage: 'cpp', extension: 'cpp', pistonLang: 'c++', pistonVersion: '10.2.0' }, |
| ]; |
|
|
| export const DEFAULT_CODE_BY_LANGUAGE = { |
| python: `# Type your input in the terminal box below after clicking RUN |
| name = input("Enter your name: ") |
| print(f"Hello, {name}!") |
| `, |
| python3: `# Type your input in the terminal box below after clicking RUN |
| name = input("Enter your name: ") |
| print(f"Hello, {name}!") |
| `, |
| javascript: `const fs = require("fs"); |
| const input = fs.readFileSync(0, "utf8").trim(); |
| |
| console.log(\`Hello, \${input}!\`); |
| `, |
| java: `import java.util.Scanner; |
| |
| public class Main { |
| public static void main(String[] args) { |
| Scanner scanner = new Scanner(System.in); |
| String name = scanner.next(); |
| System.out.println("Hello, " + name + "!"); |
| } |
| } |
| `, |
| c: `#include <stdio.h> |
| |
| int main() { |
| char name[100]; |
| scanf("%99s", name); |
| printf("Hello, %s!\\n", name); |
| return 0; |
| } |
| `, |
| cpp: `#include <iostream> |
| using namespace std; |
| |
| int main() { |
| string name; |
| cin >> name; |
| cout << "Hello, " << name << "!" << endl; |
| return 0; |
| } |
| `, |
| }; |
|
|
| function compilerApiUrl() { |
| const base = (import.meta.env.VITE_COMPILER_API_BASE ?? '').trim().replace(/\/$/, ''); |
| return `${base}/api/compile`; |
| } |
|
|
| async function parseResponse(response) { |
| const text = await response.text(); |
| if (!text) return {}; |
| try { |
| return JSON.parse(text); |
| } catch { |
| return { error: text }; |
| } |
| } |
|
|
| export default function useCompiler(initialLanguage = 'python') { |
| const [language, setLanguage] = useState(initialLanguage); |
| const [code, setCode] = useState(DEFAULT_CODE_BY_LANGUAGE[initialLanguage]); |
| const [input, setInput] = useState(''); |
| const [output, setOutput] = useState(''); |
| const [error, setError] = useState(''); |
| const [loading, setLoading] = useState(false); |
| const [executionTime, setExecutionTime] = useState(null); |
| const [status, setStatus] = useState('idle'); |
| const [compileOutput, setCompileOutput] = useState(''); |
| const [analysis, setAnalysis] = useState(null); |
|
|
| const [executionId, setExecutionId] = useState(null); |
| const eventSourceRef = useRef(null); |
|
|
| const handleRunInteractive = useCallback( |
| async (overrides = {}) => { |
| const runCode = overrides.code ?? code; |
| const runLanguage = overrides.language ?? language; |
| const onStdout = overrides.onStdout ?? (() => {}); |
| const onStderr = overrides.onStderr ?? (() => {}); |
| const onDone = overrides.onDone ?? (() => {}); |
|
|
| if (!runCode.trim()) { |
| onStderr('Code is required.'); |
| onDone('error', 0); |
| return; |
| } |
|
|
| setLoading(true); |
| setOutput(''); |
| setError(''); |
| setExecutionTime(null); |
| setStatus('running'); |
| setCompileOutput(''); |
| setAnalysis(null); |
| setExecutionId(null); |
|
|
| if (eventSourceRef.current) { |
| eventSourceRef.current.close(); |
| eventSourceRef.current = null; |
| } |
|
|
| try { |
| const base = (import.meta.env.VITE_COMPILER_API_BASE ?? '').trim().replace(/\/$/, ''); |
| const startRes = await fetch(`${base}/api/compile/start`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ code: runCode, language: runLanguage }), |
| }); |
|
|
| const startData = await parseResponse(startRes); |
| if (!startRes.ok || !startData.executionId) { |
| throw new Error(startData.error || 'Failed to start execution'); |
| } |
|
|
| const id = startData.executionId; |
| setExecutionId(id); |
|
|
| const es = new EventSource(`${base}/api/compile/stream/${id}`); |
| eventSourceRef.current = es; |
|
|
| es.addEventListener('stdout', (e) => { |
| try { |
| const text = JSON.parse(e.data); |
| setOutput(prev => prev + text); |
| onStdout(text); |
| } catch(err) {} |
| }); |
|
|
| es.addEventListener('stderr', (e) => { |
| try { |
| const text = JSON.parse(e.data); |
| setError(prev => prev + text); |
| onStderr(text); |
| } catch(err) {} |
| }); |
|
|
| es.addEventListener('done', (e) => { |
| try { |
| const data = JSON.parse(e.data); |
| setStatus(data.status); |
| setExecutionTime(data.executionTime); |
| onDone(data.status, data.executionTime); |
| } catch(err) {} |
| setLoading(false); |
| es.close(); |
| eventSourceRef.current = null; |
| }); |
|
|
| es.onerror = () => { |
| if (es.readyState === EventSource.CLOSED) return; |
| const msg = '\n[Connection lost — is the compiler server running?]'; |
| setError(prev => prev + msg); |
| onStderr(msg); |
| onDone('error', 0); |
| setStatus('error'); |
| setLoading(false); |
| es.close(); |
| eventSourceRef.current = null; |
| }; |
|
|
| } catch (err) { |
| const msg = err.message; |
| setError(msg); |
| onStderr(msg); |
| onDone('error', 0); |
| setStatus('error'); |
| setLoading(false); |
| } |
| }, |
| [code, language], |
| ); |
|
|
|
|
| const sendInput = useCallback(async (text) => { |
| if (!executionId || status !== 'running') return; |
| try { |
| const base = (import.meta.env.VITE_COMPILER_API_BASE ?? '').trim().replace(/\/$/, ''); |
| await fetch(`${base}/api/compile/input/${executionId}`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ input: text + '\n' }), |
| }); |
| } catch (err) { |
| console.error('Failed to send input', err); |
| } |
| }, [executionId, status]); |
|
|
| const resetResult = useCallback(() => { |
| if (eventSourceRef.current) { |
| eventSourceRef.current.close(); |
| eventSourceRef.current = null; |
| } |
| setOutput(''); |
| setError(''); |
| setExecutionTime(null); |
| setStatus('idle'); |
| setCompileOutput(''); |
| setAnalysis(null); |
| setExecutionId(null); |
| }, []); |
|
|
| return { |
| code, |
| setCode, |
| language, |
| setLanguage, |
| input, |
| setInput, |
| output, |
| setOutput, |
| error, |
| setError, |
| loading, |
| executionTime, |
| status, |
| compileOutput, |
| analysis, |
| handleRunInteractive, |
| sendInput, |
| resetResult, |
| }; |
| } |
|
|