181 lines
4.5 KiB
JavaScript
181 lines
4.5 KiB
JavaScript
import { rustExecute } from "./rustRepl-CARlbZyO.js";
|
|
import { onMounted, ref } from "vue";
|
|
import { http } from "../utils/http.js";
|
|
import { sleep } from "../utils/sleep.js";
|
|
|
|
//#region src/client/composables/codeRepl.ts
|
|
const ignoredNodes = [".diff.remove", ".vp-copy-ignore"];
|
|
const RE_LANGUAGE = /language-(\w+)/;
|
|
const api = {
|
|
go: "https://api.pengzhanbo.cn/repl/golang/run",
|
|
kotlin: "https://api.pengzhanbo.cn/repl/kotlin/run"
|
|
};
|
|
let pyodide = null;
|
|
const langAlias = {
|
|
kt: "kotlin",
|
|
kotlin: "kotlin",
|
|
go: "go",
|
|
rust: "rust",
|
|
rs: "rust",
|
|
py: "python",
|
|
python: "python"
|
|
};
|
|
const supportLang = [
|
|
"kotlin",
|
|
"go",
|
|
"rust",
|
|
"python"
|
|
];
|
|
function resolveLang(lang) {
|
|
return lang ? langAlias[lang] || lang : "";
|
|
}
|
|
function resolveCode(el) {
|
|
const clone = el.cloneNode(true);
|
|
clone.querySelectorAll(ignoredNodes.join(",")).forEach((node) => node.remove());
|
|
return clone.textContent || "";
|
|
}
|
|
function resolveCodeInfo(el) {
|
|
const wrapper = el.querySelector("div[class*=language-]");
|
|
const lang = wrapper?.className.match(RE_LANGUAGE)?.[1];
|
|
const codeEl = wrapper?.querySelector("pre");
|
|
let code = "";
|
|
if (codeEl) code = resolveCode(codeEl);
|
|
return {
|
|
lang: resolveLang(lang),
|
|
code
|
|
};
|
|
}
|
|
function useCodeRepl(el) {
|
|
const lang = ref();
|
|
const loaded = ref(true);
|
|
const firstRun = ref(true);
|
|
const finished = ref(true);
|
|
const stdout = ref([]);
|
|
const stderr = ref([]);
|
|
const error = ref("");
|
|
const backendVersion = ref("");
|
|
onMounted(() => {
|
|
if (el.value) lang.value = resolveCodeInfo(el.value).lang;
|
|
});
|
|
const executeMap = {
|
|
kotlin: executeKotlin,
|
|
go: executeGolang,
|
|
rust: executeRust,
|
|
python: executePython
|
|
};
|
|
function onCleanRun() {
|
|
loaded.value = false;
|
|
finished.value = false;
|
|
stdout.value = [];
|
|
stderr.value = [];
|
|
error.value = "";
|
|
firstRun.value = true;
|
|
backendVersion.value = "";
|
|
}
|
|
async function onRunCode() {
|
|
if (!el.value || !loaded.value) return;
|
|
const info = resolveCodeInfo(el.value);
|
|
lang.value = info.lang;
|
|
if (!lang.value || !info.code || !supportLang.includes(lang.value)) return;
|
|
if (firstRun.value) firstRun.value = false;
|
|
loaded.value = false;
|
|
finished.value = false;
|
|
stdout.value = [];
|
|
stderr.value = [];
|
|
error.value = "";
|
|
await executeMap[lang.value]?.(info.code);
|
|
}
|
|
async function executeGolang(code) {
|
|
const res = await http.post(api.go, { code });
|
|
backendVersion.value = `v${res.version}`;
|
|
loaded.value = true;
|
|
if (res.error) {
|
|
error.value = res.error;
|
|
finished.value = true;
|
|
return;
|
|
}
|
|
const events = res.events || [];
|
|
for (const event of events) if (event.kind === "stdout") {
|
|
if (event.delay) await sleep(event.delay / 1e6);
|
|
stdout.value.push(event.message);
|
|
} else if (event.kind === "stderr") stderr.value.push(event.message);
|
|
finished.value = true;
|
|
}
|
|
async function executeKotlin(code) {
|
|
const filename = "File.kt";
|
|
const res = await http.post(api.kotlin, {
|
|
args: "",
|
|
files: [{
|
|
name: filename,
|
|
publicId: "",
|
|
text: code
|
|
}]
|
|
});
|
|
backendVersion.value = `v${res.version}`;
|
|
loaded.value = true;
|
|
if (res.errors) {
|
|
const errors = Array.isArray(res.errors[filename]) ? res.errors[filename] : [res.errors[filename]];
|
|
if (errors.length) errors.forEach(({ message, severity }) => severity === "ERROR" && stderr.value.push(message));
|
|
}
|
|
stdout.value.push(res.text);
|
|
finished.value = true;
|
|
}
|
|
async function executeRust(code) {
|
|
await rustExecute(code, {
|
|
onBegin: () => {
|
|
loaded.value = true;
|
|
finished.value = false;
|
|
stdout.value = [];
|
|
stderr.value = [];
|
|
error.value = "";
|
|
backendVersion.value = "release";
|
|
},
|
|
onError(message) {
|
|
error.value = message;
|
|
},
|
|
onStdout(message) {
|
|
stdout.value.push(message);
|
|
},
|
|
onStderr(message) {
|
|
stderr.value.push(message);
|
|
},
|
|
onEnd: () => {
|
|
finished.value = true;
|
|
}
|
|
});
|
|
}
|
|
async function executePython(code) {
|
|
loaded.value = false;
|
|
finished.value = false;
|
|
if (pyodide === null) {
|
|
const { loadPyodide, version } = await import(
|
|
/* webpackChunkName: "pyodide" */
|
|
"pyodide"
|
|
);
|
|
pyodide = await loadPyodide({ indexURL: `https://cdn.jsdelivr.net/pyodide/v${version}/full/` });
|
|
}
|
|
pyodide.setStdout({ batched: (msg) => stdout.value.push(msg) });
|
|
try {
|
|
stdout.value.push(pyodide.runPython(code));
|
|
} catch (e) {
|
|
stderr.value.push(String(e));
|
|
}
|
|
loaded.value = true;
|
|
finished.value = true;
|
|
}
|
|
return {
|
|
onRunCode,
|
|
onCleanRun,
|
|
lang,
|
|
backendVersion,
|
|
firstRun,
|
|
stderr,
|
|
stdout,
|
|
loaded,
|
|
finished,
|
|
error
|
|
};
|
|
}
|
|
|
|
//#endregion
|
|
export { resolveCode, resolveCodeInfo, useCodeRepl }; |