All languages 17 working now

This commit is contained in:
Radon Rosborough 2020-06-07 19:30:05 -06:00
parent 2b8e0fd217
commit e2a3e719bf
4 changed files with 171 additions and 28 deletions

View File

@ -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) {
//
}
} }
}); });
}; };

View File

@ -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!"
`,
}, },
}; };

View File

@ -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");

View File

@ -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();