From e2a3e719bfa33e6472359f1a007c4d7092b7053e Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Sun, 7 Jun 2020 19:30:05 -0600 Subject: [PATCH] All languages 17 working now --- backend/src/api.ts | 72 ++++++++++++++++++++------ backend/src/langs.ts | 114 +++++++++++++++++++++++++++++++++++++----- backend/src/server.ts | 5 ++ frontend/src/app.ts | 8 ++- 4 files changed, 171 insertions(+), 28 deletions(-) diff --git a/backend/src/api.ts b/backend/src/api.ts index 9142ce2..4872cdf 100644 --- a/backend/src/api.ts +++ b/backend/src/api.ts @@ -1,3 +1,5 @@ +"use strict"; + import * as fs from "fs"; import * as pty from "node-pty"; import { IPty } from "node-pty"; @@ -18,19 +20,27 @@ export class Session { this.config = langs[lang]; this.term = { pty: null, live: false }; this.code = ""; - this.ws.send( - JSON.stringify({ - event: "setMonacoLanguage", - monacoLanguage: this.config.monacoLang, - }) - ); - if (this.config.template) { + try { this.ws.send( JSON.stringify({ - event: "insertTemplate", - template: this.config.template, + event: "setMonacoLanguage", + 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); ws.on("message", this.handleClientMessage); @@ -67,12 +77,16 @@ export class Session { } }; run = async () => { - const { repl, file, suffix, compile, run } = this.config; + const { repl, main, suffix, compile, run, hacks } = this.config; if (this.term.pty) { this.term.pty.kill(); 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) => tmp.dir({ unsafeCleanup: true }, (err, path) => { if (err) { @@ -84,14 +98,14 @@ export class Session { ); let cmdline: string; 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) { let code = this.code; if (suffix) { code += suffix; } await new Promise((resolve, reject) => - fs.writeFile(path.resolve(tmpdir, file), code, (err) => { + fs.writeFile(path.resolve(tmpdir, main), code, (err) => { if (err) { reject(err); } else { @@ -108,6 +122,30 @@ export class Session { } else { 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 = { pty: pty.spawn("bash", ["-c", cmdline], { name: "xterm-color", @@ -121,7 +159,13 @@ export class Session { // Capture term in closure so that we don't keep sending output // from the old pty even after it's been killed (see ghci). if (term.live) { - this.ws.send(JSON.stringify({ event: "terminalOutput", output: data })); + try { + this.ws.send( + JSON.stringify({ event: "terminalOutput", output: data }) + ); + } catch (err) { + // + } } }); }; diff --git a/backend/src/langs.ts b/backend/src/langs.ts index 1a34cfb..34223a2 100644 --- a/backend/src/langs.ts +++ b/backend/src/langs.ts @@ -1,28 +1,32 @@ +"use strict"; + export interface LangConfig { name: string; monacoLang: string; repl?: string; - file?: string; + main?: string; prefix?: string; suffix?: string; compile?: string; run?: string; template?: string; + hacks?: "ghci-config"[]; } export const langs: { [key: string]: LangConfig } = { bash: { name: "Bash", monacoLang: "shell", - repl: "bash", - file: "main.bash", + repl: "bash --rcfile /dev/null", + main: "main.bash", run: "bash --rcfile main.bash", - template: 'echo "Hello, world!"', + template: `echo "Hello, world!" +`, }, c: { name: "C", monacoLang: "c", - file: "main.c", + main: "main.c", compile: "clang -Wall -Wextra main.c -o main", run: "./main", template: `#include @@ -36,81 +40,167 @@ int main() { "c++": { name: "C++", monacoLang: "cpp", + main: "main.cpp", + compile: "clang++ -Wall -Wextra main.cpp -o main", + run: "./main", + template: `#include + +int main() { + std::cout << "Hello, world!" << std::endl; + return 0; +} +`, }, clojure: { name: "Clojure", monacoLang: "clojure", repl: "clojure", + main: "main.clj", + run: "clojure -i main.clj -r", + template: `(println "Hello, world!") +`, }, emacs: { name: "Emacs Lisp", monacoLang: "plaintext", - repl: "emacs", + repl: "emacs --eval '(ielm)'", + main: "main.el", + run: "emacs --load main.el --eval '(ielm)'", + template: `(message "Hello, world!") +`, }, fish: { name: "Fish", monacoLang: "plaintext", repl: "SHELL=/usr/bin/fish fish", + main: "main.fish", + run: 'fish -C "$(< main.fish)"', + template: `echo "Hello, world!" +`, }, go: { name: "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: { name: "Haskell", monacoLang: "plaintext", repl: "ghci", - file: "Main.hs", - run: "ghci Main.hs", + main: "Main.hs", + run: "ghci", + template: `module Main where + +main :: IO () +main = putStrLn "Hello, world!" +`, + hacks: ["ghci-config"], }, java: { name: "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: { name: "Julia", monacoLang: "plaintext", repl: "julia", + main: "main.jl", + run: "julia -L main.jl", + template: `println("Hello, world!") +`, }, lua: { name: "Lua", monacoLang: "lua", repl: "lua", + main: "main.lua", + run: "lua -i main.lua", + template: `print("Hello, world!") +`, }, nodejs: { name: "Node.js", monacoLang: "javascript", repl: "node", - file: "main.js", - suffix: '\n;require("repl").start();', + main: "main.js", + suffix: ` +require("repl").start(); +`, run: "node main.js", + template: `console.log("Hello, world!") +`, }, python: { name: "Python", monacoLang: "python", repl: "python3 -u", - file: "main.py", + main: "main.py", run: "python3 -u -i main.py", + template: `print("Hello, world!") +`, }, ruby: { name: "Ruby", monacoLang: "ruby", 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: { name: "Rust", monacoLang: "rust", + main: "main.rs", + compile: "rustc main.rs", + run: "./main", + template: `fn main() { + println!("Hello, world!"); +} +`, }, vim: { name: "Vimscript", monacoLang: "plaintext", repl: "vim", + main: "main.vim", + run: `vim -c "$(< main.vim)"`, + template: `:echo "Hello, world!" +`, }, zsh: { name: "Zsh", monacoLang: "shell", repl: "SHELL=/usr/bin/zsh zsh", - file: ".zshrc", + main: ".zshrc", run: "ZDOTDIR=. zsh", + template: `echo "Hello, world!" +`, }, }; diff --git a/backend/src/server.ts b/backend/src/server.ts index dd191db..981276e 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,3 +1,5 @@ +"use strict"; + import * as appRoot from "app-root-path"; import * as express from "express"; import { Request } from "express"; @@ -23,6 +25,9 @@ app.use(sslRedirect()); app.get("/", (_, res) => { res.sendFile(appRoot.path + "/frontend/pages/index.html"); }); +app.get("/cpp", (_, res) => { + res.redirect("/c++"); +}); app.get("/:lang", (req, res) => { if (langs[req.params.lang]) { res.sendFile(appRoot.path + "/frontend/pages/app.html"); diff --git a/frontend/src/app.ts b/frontend/src/app.ts index 930755c..6bf6b89 100644 --- a/frontend/src/app.ts +++ b/frontend/src/app.ts @@ -1,3 +1,5 @@ +"use strict"; + import * as monaco from "monaco-editor"; import { Terminal } from "xterm"; import { FitAddon } from "xterm-addon-fit"; @@ -24,10 +26,9 @@ function tryConnect() { socket = new WebSocket( (document.location.protocol === "http:" ? "ws://" : "wss://") + document.location.host + - `/api/v1/ws?lang=${lang}` + `/api/v1/ws?lang=${encodeURIComponent(lang)}` ); socket.addEventListener("open", () => { - retryDelayMs = initialRetryDelayMs; console.log("Successfully connected to server"); }); socket.addEventListener("message", (event: MessageEvent) => { @@ -38,6 +39,9 @@ function tryConnect() { console.error("Malformed message from server:", event.data); return; } + if (message?.event && message?.event !== "error") { + retryDelayMs = initialRetryDelayMs; + } switch (message?.event) { case "terminalClear": term.reset();