Add code formatting button, Python/Haskell support

This commit is contained in:
Radon Rosborough 2020-07-19 17:40:54 -06:00
parent 3fd5bd32d8
commit 68bb853fa0
7 changed files with 119 additions and 21 deletions

View File

@ -35,6 +35,12 @@ export class Session {
writer: rpc.StreamMessageWriter;
} | null = null;
daemon: { proc: ChildProcess } | null = null;
formatter: {
proc: ChildProcess;
live: boolean;
input: string;
output: string;
} | null = null;
get homedir() {
return `/tmp/riju/${this.uuid}`;
@ -224,6 +230,13 @@ export class Session {
}
await this.runCode(msg.code);
break;
case "formatCode":
if (typeof msg.code !== "string") {
this.logBadMessage(msg);
break;
}
await this.formatCode(msg.code);
break;
case "lspInput":
if (typeof msg.input !== "object" || !msg) {
this.logBadMessage(msg);
@ -257,12 +270,11 @@ export class Session {
compile,
run,
template,
hacks,
} = this.config;
if (this.term) {
const pid = this.term.pty.pid;
const args = this.privilegedSpawn(
bash(`kill -SIGTERM ${pid}; sleep 3; kill -SIGKILL ${pid}`)
bash(`kill -SIGTERM ${pid}; sleep 1; kill -SIGKILL ${pid}`)
);
spawn(args[0], args.slice(1));
// Signal to terminalOutput message generator using closure.
@ -304,18 +316,6 @@ export class Session {
]),
{ input: code }
);
if (hacks && hacks.includes("ghci-config") && run) {
if (code) {
await this.run(
this.privilegedSpawn(["sh", "-c", `cat > ${this.homedir}/.ghci`]),
{ input: ":load Main\nmain\n" }
);
} else {
await this.run(
this.privilegedSpawn(["rm", "-f", `${this.homedir}/.ghci`])
);
}
}
const termArgs = this.privilegedSpawn(bash(cmdline));
const term = {
pty: pty.spawn(termArgs[0], termArgs.slice(1), {
@ -338,6 +338,66 @@ export class Session {
}
};
formatCode = async (code: string) => {
if (!this.config.format) {
this.log("formatCode ignored because format is null");
return;
}
if (this.formatter) {
const pid = this.formatter.proc.pid;
const args = this.privilegedSpawn(
bash(`kill -SIGTERM ${pid}; sleep 1; kill -SIGKILL ${pid}`)
);
spawn(args[0], args.slice(1));
this.formatter.live = false;
this.formatter = null;
}
const args = this.privilegedSpawn(bash(this.config.format));
const formatter = {
proc: spawn(args[0], args.slice(1)),
live: true,
input: code,
output: "",
};
formatter.proc.stdout!.on("data", (data) => {
if (formatter.live) {
formatter.output += data.toString("utf8");
}
});
formatter.proc.stderr!.on("data", (data) => {
if (formatter.live) {
this.send({
event: "serviceLog",
service: "formatter",
output: data.toString("utf8"),
});
}
});
formatter.proc.on("exit", (code, signal) => {
if (code === 0) {
this.send({
event: "formattedCode",
code: formatter.output,
originalCode: formatter.input,
});
} else {
this.send({
event: "serviceFailed",
service: "formatter",
error: `Exited with status ${signal || code}`,
});
}
});
formatter.proc.on("error", (err) =>
this.send({
event: "serviceFailed",
service: "formatter",
error: `${err}`,
})
);
this.formatter = formatter;
};
teardown = async () => {
try {
if (this.tearingDown) {

View File

@ -11,6 +11,7 @@ export interface LangConfig {
createEmpty?: string;
compile?: string;
run: string;
format?: string;
pkg?: {
install: string;
uninstall?: string;
@ -24,7 +25,6 @@ export interface LangConfig {
lspConfig?: any;
lspLang?: string;
template: string;
hacks?: "ghci-config"[];
test?: {
ensure?: string;
};
@ -764,9 +764,10 @@ function main(): void {
haskell: {
aliases: ["ghc", "ghci", "hs"],
name: "Haskell",
repl: "ghci",
repl: "rm -f .ghci && ghci",
main: "Main.hs",
run: "ghci",
run: "(echo ':load Main' && echo 'main') > .ghci && ghci",
format: "brittany Main.hs",
lspSetup: "cp /opt/haskell/hie.yaml hie.yaml",
lsp: "HIE_HOOGLE_DATABASE=/opt/haskell/hoogle.hoo hie --lsp",
lspInit: {
@ -1337,6 +1338,7 @@ main = do
repl: "python3 -u",
main: "main.py",
run: "python3 -u -i main.py",
format: "cat main.py | black -",
pkg: {
install: "pip3 install --user NAME",
uninstall: "pip3 uninstall NAME",
@ -1350,7 +1352,7 @@ main = do
},
},
},
template: `print("Hello, world!")
template: `print('Hello, world!')
`,
},
قلب: {

View File

@ -16,6 +16,7 @@
<div id="editor" class="column"></div>
<div id="terminal" class="column"></div>
<button type="button" class="btn btn-success" id="runButton">Run</button>
<button type="button" class="btn btn-info" id="formatButton">Prettify</button>
<a href="/" class="btn btn-secondary" id="backButton">Switch to a different language</a>
</div>
<script>

View File

@ -25,6 +25,7 @@ interface RijuConfig {
id: string;
monacoLang?: string;
main: string;
format?: string;
lspDisableDynamicRegistration?: boolean;
lspInit?: any;
lspConfig?: any;
@ -133,7 +134,7 @@ async function main() {
function sendMessage(message: any) {
if (DEBUG) {
console.log("SEND", message);
console.log("SEND:", message);
}
if (socket) {
socket.send(JSON.stringify(message));
@ -165,8 +166,7 @@ async function main() {
DEBUG &&
message &&
message.event !== "lspOutput" &&
message.event !== "lspLog" &&
message.event !== "daemonLog"
message.event !== "serviceLog"
) {
console.log("RECEIVE:", message);
}
@ -184,6 +184,18 @@ async function main() {
}
term.write(message.output);
return;
case "formattedCode":
if (
typeof message.code !== "string" ||
typeof message.originalCode !== "string"
) {
console.error("Unexpected message from server:", message);
return;
}
if (editor.getValue() === message.originalCode) {
editor.setValue(message.code);
}
return;
case "lspStarted":
if (typeof message.root !== "string") {
console.error("Unexpected message from server:", message);
@ -308,6 +320,12 @@ async function main() {
document.getElementById("runButton")!.addEventListener("click", () => {
sendMessage({ event: "runCode", code: editor.getValue() });
});
if (config.format) {
document.getElementById("formatButton")!.classList.add("visible");
document.getElementById("formatButton")!.addEventListener("click", () => {
sendMessage({ event: "formatCode", code: editor.getValue() });
});
}
}
main().catch(console.error);

View File

@ -27,6 +27,17 @@ body {
right: calc(50% + 25px);
}
#formatButton {
position: absolute;
bottom: 25px;
right: calc(50% + 25px);
visibility: hidden;
}
#formatButton.visible {
visibility: visible;
}
#backButton {
position: absolute;
left: 25px;

View File

@ -82,6 +82,9 @@ npm install -g pug-cli
# PureScript
npm install -g purescript spago
# Python
pip3 install black
# ReasonML
npm install -g bs-platform

View File

@ -121,6 +121,9 @@ tar -xf linux-x86_64-static.tar.gz
mv stack-*-linux-x86_64-static/stack /usr/bin/stack
rm -rf stack-*-linux-x86_64-static linux-x86_64-static.tar.gz
wget "https://drive.google.com/uc?export=download&id=1MpozlNLmWeUaQuT-5t6gyE3Yv56gUbea" -O /usr/local/bin/brittany
chmod +x /usr/local/bin/brittany
mkdir -p /opt/haskell
gdown "https://drive.google.com/uc?export=download&id=1GPoR_ja4ns16KCamRgwB-JVag4HK0igz" /usr/bin/hie
gdown "https://drive.google.com/uc?export=download&id=1qSxj8JjAeetAmNjUGayX0RBARgr5R4Ij" /opt/haskell/hoogle.hoo