All languages 17 working now
This commit is contained in:
parent
2b8e0fd217
commit
e2a3e719bf
|
@ -1,3 +1,5 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as pty from "node-pty";
|
import * as pty from "node-pty";
|
||||||
import { IPty } from "node-pty";
|
import { IPty } from "node-pty";
|
||||||
|
@ -18,19 +20,27 @@ export class Session {
|
||||||
this.config = langs[lang];
|
this.config = langs[lang];
|
||||||
this.term = { pty: null, live: false };
|
this.term = { pty: null, live: false };
|
||||||
this.code = "";
|
this.code = "";
|
||||||
this.ws.send(
|
try {
|
||||||
JSON.stringify({
|
|
||||||
event: "setMonacoLanguage",
|
|
||||||
monacoLanguage: this.config.monacoLang,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
if (this.config.template) {
|
|
||||||
this.ws.send(
|
this.ws.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
event: "insertTemplate",
|
event: "setMonacoLanguage",
|
||||||
template: this.config.template,
|
monacoLanguage: this.config.monacoLang,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
if (this.config.template) {
|
||||||
|
try {
|
||||||
|
this.ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
event: "insertTemplate",
|
||||||
|
template: this.config.template,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.run().catch(console.error);
|
this.run().catch(console.error);
|
||||||
ws.on("message", this.handleClientMessage);
|
ws.on("message", this.handleClientMessage);
|
||||||
|
@ -67,12 +77,16 @@ export class Session {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
run = async () => {
|
run = async () => {
|
||||||
const { repl, file, suffix, compile, run } = this.config;
|
const { repl, main, suffix, compile, run, hacks } = this.config;
|
||||||
if (this.term.pty) {
|
if (this.term.pty) {
|
||||||
this.term.pty.kill();
|
this.term.pty.kill();
|
||||||
this.term.live = false;
|
this.term.live = false;
|
||||||
}
|
}
|
||||||
this.ws.send(JSON.stringify({ event: "terminalClear" }));
|
try {
|
||||||
|
this.ws.send(JSON.stringify({ event: "terminalClear" }));
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
const tmpdir: string = await new Promise((resolve, reject) =>
|
const tmpdir: string = await new Promise((resolve, reject) =>
|
||||||
tmp.dir({ unsafeCleanup: true }, (err, path) => {
|
tmp.dir({ unsafeCleanup: true }, (err, path) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -84,14 +98,14 @@ export class Session {
|
||||||
);
|
);
|
||||||
let cmdline: string;
|
let cmdline: string;
|
||||||
if (!run) {
|
if (!run) {
|
||||||
cmdline = `echo 'Support for ${this.config.name} is not yet implemented.`;
|
cmdline = `echo 'Support for ${this.config.name} is not yet implemented.'`;
|
||||||
} else if (this.code) {
|
} else if (this.code) {
|
||||||
let code = this.code;
|
let code = this.code;
|
||||||
if (suffix) {
|
if (suffix) {
|
||||||
code += suffix;
|
code += suffix;
|
||||||
}
|
}
|
||||||
await new Promise((resolve, reject) =>
|
await new Promise((resolve, reject) =>
|
||||||
fs.writeFile(path.resolve(tmpdir, file), code, (err) => {
|
fs.writeFile(path.resolve(tmpdir, main), code, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
|
@ -108,6 +122,30 @@ export class Session {
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (hacks && hacks.includes("ghci-config") && run) {
|
||||||
|
if (this.code) {
|
||||||
|
const contents = ":load Main\nmain\n";
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
fs.writeFile(path.resolve(tmpdir, ".ghci"), contents, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await new Promise((resolve, reject) =>
|
||||||
|
fs.unlink(path.resolve(tmpdir, ".ghci"), (err) => {
|
||||||
|
if (err && err.code !== "ENOENT") {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
const term = {
|
const term = {
|
||||||
pty: pty.spawn("bash", ["-c", cmdline], {
|
pty: pty.spawn("bash", ["-c", cmdline], {
|
||||||
name: "xterm-color",
|
name: "xterm-color",
|
||||||
|
@ -121,7 +159,13 @@ export class Session {
|
||||||
// Capture term in closure so that we don't keep sending output
|
// Capture term in closure so that we don't keep sending output
|
||||||
// from the old pty even after it's been killed (see ghci).
|
// from the old pty even after it's been killed (see ghci).
|
||||||
if (term.live) {
|
if (term.live) {
|
||||||
this.ws.send(JSON.stringify({ event: "terminalOutput", output: data }));
|
try {
|
||||||
|
this.ws.send(
|
||||||
|
JSON.stringify({ event: "terminalOutput", output: data })
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,28 +1,32 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
export interface LangConfig {
|
export interface LangConfig {
|
||||||
name: string;
|
name: string;
|
||||||
monacoLang: string;
|
monacoLang: string;
|
||||||
repl?: string;
|
repl?: string;
|
||||||
file?: string;
|
main?: string;
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
compile?: string;
|
compile?: string;
|
||||||
run?: string;
|
run?: string;
|
||||||
template?: string;
|
template?: string;
|
||||||
|
hacks?: "ghci-config"[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const langs: { [key: string]: LangConfig } = {
|
export const langs: { [key: string]: LangConfig } = {
|
||||||
bash: {
|
bash: {
|
||||||
name: "Bash",
|
name: "Bash",
|
||||||
monacoLang: "shell",
|
monacoLang: "shell",
|
||||||
repl: "bash",
|
repl: "bash --rcfile /dev/null",
|
||||||
file: "main.bash",
|
main: "main.bash",
|
||||||
run: "bash --rcfile main.bash",
|
run: "bash --rcfile main.bash",
|
||||||
template: 'echo "Hello, world!"',
|
template: `echo "Hello, world!"
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
c: {
|
c: {
|
||||||
name: "C",
|
name: "C",
|
||||||
monacoLang: "c",
|
monacoLang: "c",
|
||||||
file: "main.c",
|
main: "main.c",
|
||||||
compile: "clang -Wall -Wextra main.c -o main",
|
compile: "clang -Wall -Wextra main.c -o main",
|
||||||
run: "./main",
|
run: "./main",
|
||||||
template: `#include <stdio.h>
|
template: `#include <stdio.h>
|
||||||
|
@ -36,81 +40,167 @@ int main() {
|
||||||
"c++": {
|
"c++": {
|
||||||
name: "C++",
|
name: "C++",
|
||||||
monacoLang: "cpp",
|
monacoLang: "cpp",
|
||||||
|
main: "main.cpp",
|
||||||
|
compile: "clang++ -Wall -Wextra main.cpp -o main",
|
||||||
|
run: "./main",
|
||||||
|
template: `#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Hello, world!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
clojure: {
|
clojure: {
|
||||||
name: "Clojure",
|
name: "Clojure",
|
||||||
monacoLang: "clojure",
|
monacoLang: "clojure",
|
||||||
repl: "clojure",
|
repl: "clojure",
|
||||||
|
main: "main.clj",
|
||||||
|
run: "clojure -i main.clj -r",
|
||||||
|
template: `(println "Hello, world!")
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
emacs: {
|
emacs: {
|
||||||
name: "Emacs Lisp",
|
name: "Emacs Lisp",
|
||||||
monacoLang: "plaintext",
|
monacoLang: "plaintext",
|
||||||
repl: "emacs",
|
repl: "emacs --eval '(ielm)'",
|
||||||
|
main: "main.el",
|
||||||
|
run: "emacs --load main.el --eval '(ielm)'",
|
||||||
|
template: `(message "Hello, world!")
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
fish: {
|
fish: {
|
||||||
name: "Fish",
|
name: "Fish",
|
||||||
monacoLang: "plaintext",
|
monacoLang: "plaintext",
|
||||||
repl: "SHELL=/usr/bin/fish fish",
|
repl: "SHELL=/usr/bin/fish fish",
|
||||||
|
main: "main.fish",
|
||||||
|
run: 'fish -C "$(< main.fish)"',
|
||||||
|
template: `echo "Hello, world!"
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
go: {
|
go: {
|
||||||
name: "Go",
|
name: "Go",
|
||||||
monacoLang: "go",
|
monacoLang: "go",
|
||||||
|
main: "main.go",
|
||||||
|
compile: "go build main.go",
|
||||||
|
run: "./main",
|
||||||
|
template: `package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, world!")
|
||||||
|
}
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
haskell: {
|
haskell: {
|
||||||
name: "Haskell",
|
name: "Haskell",
|
||||||
monacoLang: "plaintext",
|
monacoLang: "plaintext",
|
||||||
repl: "ghci",
|
repl: "ghci",
|
||||||
file: "Main.hs",
|
main: "Main.hs",
|
||||||
run: "ghci Main.hs",
|
run: "ghci",
|
||||||
|
template: `module Main where
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn "Hello, world!"
|
||||||
|
`,
|
||||||
|
hacks: ["ghci-config"],
|
||||||
},
|
},
|
||||||
java: {
|
java: {
|
||||||
name: "Java",
|
name: "Java",
|
||||||
monacoLang: "java",
|
monacoLang: "java",
|
||||||
|
main: "Main.java",
|
||||||
|
compile: "javac Main.java",
|
||||||
|
run: "java Main",
|
||||||
|
template: `public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Hello, world!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
julia: {
|
julia: {
|
||||||
name: "Julia",
|
name: "Julia",
|
||||||
monacoLang: "plaintext",
|
monacoLang: "plaintext",
|
||||||
repl: "julia",
|
repl: "julia",
|
||||||
|
main: "main.jl",
|
||||||
|
run: "julia -L main.jl",
|
||||||
|
template: `println("Hello, world!")
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
lua: {
|
lua: {
|
||||||
name: "Lua",
|
name: "Lua",
|
||||||
monacoLang: "lua",
|
monacoLang: "lua",
|
||||||
repl: "lua",
|
repl: "lua",
|
||||||
|
main: "main.lua",
|
||||||
|
run: "lua -i main.lua",
|
||||||
|
template: `print("Hello, world!")
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
nodejs: {
|
nodejs: {
|
||||||
name: "Node.js",
|
name: "Node.js",
|
||||||
monacoLang: "javascript",
|
monacoLang: "javascript",
|
||||||
repl: "node",
|
repl: "node",
|
||||||
file: "main.js",
|
main: "main.js",
|
||||||
suffix: '\n;require("repl").start();',
|
suffix: `
|
||||||
|
require("repl").start();
|
||||||
|
`,
|
||||||
run: "node main.js",
|
run: "node main.js",
|
||||||
|
template: `console.log("Hello, world!")
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
python: {
|
python: {
|
||||||
name: "Python",
|
name: "Python",
|
||||||
monacoLang: "python",
|
monacoLang: "python",
|
||||||
repl: "python3 -u",
|
repl: "python3 -u",
|
||||||
file: "main.py",
|
main: "main.py",
|
||||||
run: "python3 -u -i main.py",
|
run: "python3 -u -i main.py",
|
||||||
|
template: `print("Hello, world!")
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
ruby: {
|
ruby: {
|
||||||
name: "Ruby",
|
name: "Ruby",
|
||||||
monacoLang: "ruby",
|
monacoLang: "ruby",
|
||||||
repl: "irb",
|
repl: "irb",
|
||||||
|
main: "main.rb",
|
||||||
|
suffix: `
|
||||||
|
require 'irb'
|
||||||
|
IRB.setup(ARGV[0], argv: [])
|
||||||
|
workspace = IRB::WorkSpace.new(binding)
|
||||||
|
binding_irb = IRB::Irb.new(workspace)
|
||||||
|
binding_irb.run(IRB.conf)
|
||||||
|
`,
|
||||||
|
run: "ruby main.rb",
|
||||||
|
template: `puts "Hello, world!"
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
rust: {
|
rust: {
|
||||||
name: "Rust",
|
name: "Rust",
|
||||||
monacoLang: "rust",
|
monacoLang: "rust",
|
||||||
|
main: "main.rs",
|
||||||
|
compile: "rustc main.rs",
|
||||||
|
run: "./main",
|
||||||
|
template: `fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
vim: {
|
vim: {
|
||||||
name: "Vimscript",
|
name: "Vimscript",
|
||||||
monacoLang: "plaintext",
|
monacoLang: "plaintext",
|
||||||
repl: "vim",
|
repl: "vim",
|
||||||
|
main: "main.vim",
|
||||||
|
run: `vim -c "$(< main.vim)"`,
|
||||||
|
template: `:echo "Hello, world!"
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
zsh: {
|
zsh: {
|
||||||
name: "Zsh",
|
name: "Zsh",
|
||||||
monacoLang: "shell",
|
monacoLang: "shell",
|
||||||
repl: "SHELL=/usr/bin/zsh zsh",
|
repl: "SHELL=/usr/bin/zsh zsh",
|
||||||
file: ".zshrc",
|
main: ".zshrc",
|
||||||
run: "ZDOTDIR=. zsh",
|
run: "ZDOTDIR=. zsh",
|
||||||
|
template: `echo "Hello, world!"
|
||||||
|
`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
import * as appRoot from "app-root-path";
|
import * as appRoot from "app-root-path";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
|
@ -23,6 +25,9 @@ app.use(sslRedirect());
|
||||||
app.get("/", (_, res) => {
|
app.get("/", (_, res) => {
|
||||||
res.sendFile(appRoot.path + "/frontend/pages/index.html");
|
res.sendFile(appRoot.path + "/frontend/pages/index.html");
|
||||||
});
|
});
|
||||||
|
app.get("/cpp", (_, res) => {
|
||||||
|
res.redirect("/c++");
|
||||||
|
});
|
||||||
app.get("/:lang", (req, res) => {
|
app.get("/:lang", (req, res) => {
|
||||||
if (langs[req.params.lang]) {
|
if (langs[req.params.lang]) {
|
||||||
res.sendFile(appRoot.path + "/frontend/pages/app.html");
|
res.sendFile(appRoot.path + "/frontend/pages/app.html");
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
import { Terminal } from "xterm";
|
import { Terminal } from "xterm";
|
||||||
import { FitAddon } from "xterm-addon-fit";
|
import { FitAddon } from "xterm-addon-fit";
|
||||||
|
@ -24,10 +26,9 @@ function tryConnect() {
|
||||||
socket = new WebSocket(
|
socket = new WebSocket(
|
||||||
(document.location.protocol === "http:" ? "ws://" : "wss://") +
|
(document.location.protocol === "http:" ? "ws://" : "wss://") +
|
||||||
document.location.host +
|
document.location.host +
|
||||||
`/api/v1/ws?lang=${lang}`
|
`/api/v1/ws?lang=${encodeURIComponent(lang)}`
|
||||||
);
|
);
|
||||||
socket.addEventListener("open", () => {
|
socket.addEventListener("open", () => {
|
||||||
retryDelayMs = initialRetryDelayMs;
|
|
||||||
console.log("Successfully connected to server");
|
console.log("Successfully connected to server");
|
||||||
});
|
});
|
||||||
socket.addEventListener("message", (event: MessageEvent) => {
|
socket.addEventListener("message", (event: MessageEvent) => {
|
||||||
|
@ -38,6 +39,9 @@ function tryConnect() {
|
||||||
console.error("Malformed message from server:", event.data);
|
console.error("Malformed message from server:", event.data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (message?.event && message?.event !== "error") {
|
||||||
|
retryDelayMs = initialRetryDelayMs;
|
||||||
|
}
|
||||||
switch (message?.event) {
|
switch (message?.event) {
|
||||||
case "terminalClear":
|
case "terminalClear":
|
||||||
term.reset();
|
term.reset();
|
||||||
|
|
Loading…
Reference in New Issue