judge/src/app/playground/page.tsx

175 lines
5.2 KiB
TypeScript
Raw Normal View History

"use client";
import * as monaco from "monaco-editor";
import { connectToLanguageServer } from "@/lib/lsp";
import { useEffect, useRef, useState } from "react";
import { Editor, loader } from "@monaco-editor/react";
import { MonacoLanguageClient } from "monaco-languageclient";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { COriginal, CplusplusOriginal, JavaOriginal } from "devicons-react";
const files: {
[key: string]: { name: string; language: string; value: string };
} = {
c: {
name: "main.c",
language: "c",
value: `#include <stdio.h>
int main() {
printf("Hello, World!");
return 0;
}`,
},
cpp: {
name: "main.cpp",
language: "cpp",
value: `#include <iostream>
int main() {
std::cout << "Hello, World!";
return 0;
}`,
},
java: {
name: "Main.java",
language: "java",
value: `public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}`,
},
};
loader.config({ monaco });
export default function PlaygroundPage() {
const [language, setLanguage] = useState<string>("c");
const file = files[language];
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
const webSocketRef = useRef<WebSocket | null>(null);
const languageClientRef = useRef<MonacoLanguageClient | null>(null);
useEffect(() => {
const handleLanguageChange = async () => {
if (editorRef.current) {
const model = editorRef.current.getModel();
if (model) {
const lineCount = model.getLineCount();
const lastLineLength = model.getLineLength(lineCount);
editorRef.current.setPosition({
lineNumber: lineCount,
column: lastLineLength + 1,
});
editorRef.current.focus();
if (languageClientRef.current) {
await languageClientRef.current.stop();
languageClientRef.current = null;
}
if (
webSocketRef.current &&
webSocketRef.current.readyState !== WebSocket.CLOSED &&
webSocketRef.current.readyState !== WebSocket.CLOSING
) {
webSocketRef.current.close();
webSocketRef.current = null;
}
try {
const languageClient = await connectToLanguageServer(
language,
webSocketRef
);
languageClientRef.current = languageClient;
} catch (error) {
console.error("Failed to connect to language server:", error);
}
}
}
};
handleLanguageChange();
}, [language]);
return (
<div className="h-full flex flex-col">
<Tabs defaultValue={language as string}>
<ScrollArea>
<TabsList className="m-3">
<TabsTrigger
value="c"
onClick={() => setLanguage("c")}
disabled={language === "c"}
>
<COriginal
className="-ms-0.5 me-1.5"
size={16}
strokeWidth={2}
aria-hidden="true"
/>
C
</TabsTrigger>
<TabsTrigger
value="cpp"
className="group"
onClick={() => setLanguage("cpp")}
disabled={language === "cpp"}
>
<CplusplusOriginal
className="-ms-0.5 me-1.5"
size={16}
strokeWidth={2}
aria-hidden="true"
/>
C++
</TabsTrigger>
<TabsTrigger
value="java"
className="group"
onClick={() => setLanguage("java")}
disabled={language === "java"}
>
<JavaOriginal
className="-ms-0.5 me-1.5"
size={16}
strokeWidth={2}
aria-hidden="true"
/>
Java
</TabsTrigger>
</TabsList>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</Tabs>
<Editor
theme="vs-dark"
path={file.name}
defaultLanguage={file.language}
defaultValue={file.value}
options={{ automaticLayout: true }}
onMount={(editor) => {
editorRef.current = editor;
if (editorRef.current) {
const model = editorRef.current.getModel();
if (model) {
const lineCount = model.getLineCount();
const lastLineLength = model.getLineLength(lineCount);
editorRef.current.setPosition({
lineNumber: lineCount,
column: lastLineLength + 1,
});
editorRef.current.focus();
connectToLanguageServer(language, webSocketRef)
.then((languageClient) => {
languageClientRef.current = languageClient;
})
.catch((error) => {
console.error("Failed to connect to language server:", error);
});
}
}
}}
/>
</div>
);
}