Skip to content

Evaluate code in playground #415

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/Playground.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,29 @@ function Playground$ControlPanel(Props) {
[Symbol.for("name")]: "Format"
});
};
var onRunClick = function (evt) {
evt.preventDefault();
var getSuccessCompilationResult = function (result) {
if (result.TAG === /* Success */1) {
return result._0;
}

};
var x = ready.result;
if (typeof x === "number") {
console.log("nothing");
return ;
}
if (x.TAG === /* Conv */0) {
console.log("conv");
return ;
}
Belt_Option.map(getSuccessCompilationResult(x._0), (function (r) {
eval(r.js_code);

}));

};
var createShareLink = function (param) {
var lang = ready.targetLang;
var params = lang >= 2 ? [] : [[
Expand All @@ -1551,6 +1574,11 @@ function Playground$ControlPanel(Props) {
}, React.createElement(Playground$ControlPanel$Button, {
children: "Format",
onClick: onFormatClick
})), React.createElement("div", {
className: "mr-2"
}, React.createElement(Playground$ControlPanel$Button, {
children: "Run",
onClick: onRunClick
})), React.createElement(Playground$ControlPanel$ShareButton, {
createShareLink: createShareLink,
actionIndicatorKey: actionIndicatorKey
Expand Down
19 changes: 19 additions & 0 deletions src/Playground.res
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,7 @@ module ControlPanel = {
}

@val @scope(("window", "location")) external origin: string = "origin"
@val external eval: string => unit = "eval"
@react.component
let make = (
~actionIndicatorKey: string,
Expand All @@ -1194,6 +1195,23 @@ module ControlPanel = {
dispatch(Format(editorCode.current))
}

let onRunClick = evt => {
ReactEvent.Mouse.preventDefault(evt)

let getSuccessCompilationResult = result =>
switch result {
| RescriptCompilerApi.CompilationResult.Success(r) => Some(r)
| _ => None
}

switch ready.result {
| FinalResult.Nothing => Js.log("nothing")
| FinalResult.Comp(x) =>
getSuccessCompilationResult(x)->Belt.Option.map(r => eval(r.js_code))->ignore
| FinalResult.Conv(_) => Js.log("conv")
}
}

let createShareLink = () => {
let params = switch ready.targetLang {
| Res => []
Expand All @@ -1216,6 +1234,7 @@ module ControlPanel = {
<div className="mr-2">
<Button onClick=onFormatClick> {React.string("Format")} </Button>
</div>
<div className="mr-2"> <Button onClick=onRunClick> {React.string("Run")} </Button> </div>
<ShareButton actionIndicatorKey createShareLink />
</>
| _ => React.null
Expand Down
8 changes: 4 additions & 4 deletions src/bindings/Worker.resi
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ module Make: (Config: Config) =>
include Config

module App: {
let postMessage: (worker, fromApp) => unit
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without these changes I was getting type errors. I posted about it on the forum: https://forum.rescript-lang.org/t/regarding-src-bindings-worker-res-in-the-rescript-lang-org-repo/2004/2

let onMessage: (worker, {"data": fromWorker} => unit) => unit
let postMessage: (worker, Config.fromApp) => unit
let onMessage: (worker, {"data": Config.fromWorker} => unit) => unit
let onError: (worker, 'a => unit) => unit
let terminate: worker => unit
}

module Worker: {
type self
let postMessage: fromWorker => unit
let onMessage: (self, {"data": fromApp} => unit) => unit
let postMessage: Config.fromWorker => unit
let onMessage: (self, {"data": Config.fromApp} => unit) => unit
let self: self
let importScripts: string => unit
}
Expand Down
182 changes: 182 additions & 0 deletions src/common/Eval.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Curry from "rescript/lib/es6/curry.js";
import * as React from "react";
import * as $$Worker from "../bindings/Worker.mjs";

function make(param) {
return $$Worker.make("./EvalWorker.mjs");
}

var Config = {
make: make
};

var EvalWorker = $$Worker.Make(Config);

var worker = Curry._1(EvalWorker.make, undefined);

var eventListeners = {
contents: []
};

Curry._2(EvalWorker.App.onMessage, worker, (function (msg) {
eventListeners.contents.forEach(function (listener) {
return Curry._1(listener, msg);
});

}));

function addEventListener(listener) {
eventListeners.contents.push(listener);

}

function removeEventListener(listener) {
eventListeners.contents = eventListeners.contents.filter(function (l) {
return l !== listener;
});

}

function workerMessageToAction(message) {
var message$1 = message.result;
var forCode = message.forCode;
if (message$1.TAG === /* Ok */0) {
return {
TAG: 1,
forCode: forCode,
message: message$1._0,
[Symbol.for("name")]: "Success"
};
} else {
return {
TAG: 2,
forCode: forCode,
message: message$1._0,
[Symbol.for("name")]: "Fail"
};
}
}

function reducer(state, action) {
if (typeof state === "number") {
switch (action.TAG | 0) {
case /* Evaluate */0 :
return {
TAG: 0,
_0: action._0,
[Symbol.for("name")]: "Evaluating"
};
case /* Success */1 :
case /* Fail */2 :
return state;

}
} else {
switch (state.TAG | 0) {
case /* Evaluating */0 :
var code = state._0;
switch (action.TAG | 0) {
case /* Evaluate */0 :
return state;
case /* Success */1 :
if (action.forCode === code) {
return {
TAG: 1,
_0: action.message,
[Symbol.for("name")]: "Evaluated"
};
} else {
return state;
}
case /* Fail */2 :
if (action.forCode === code) {
return {
TAG: 2,
_0: action.message,
[Symbol.for("name")]: "Error"
};
} else {
return state;
}

}
case /* Evaluated */1 :
switch (action.TAG | 0) {
case /* Evaluate */0 :
return {
TAG: 0,
_0: action._0,
[Symbol.for("name")]: "Evaluating"
};
case /* Success */1 :
case /* Fail */2 :
return state;

}
case /* Error */2 :
switch (action.TAG | 0) {
case /* Evaluate */0 :
return {
TAG: 0,
_0: action._0,
[Symbol.for("name")]: "Evaluating"
};
case /* Success */1 :
case /* Fail */2 :
return state;

}

}
}
}

function useEval(param) {
var match = React.useReducer(reducer, /* Idle */0);
var dispatch = match[1];
var state = match[0];
React.useEffect((function () {
var listener = function (message) {
return Curry._1(dispatch, workerMessageToAction(message.data));
};
addEventListener(listener);
return (function (param) {
return removeEventListener(listener);
});
}), []);
React.useEffect((function () {
if (typeof state !== "number" && state.TAG === /* Evaluating */0) {
Curry._2(EvalWorker.App.postMessage, worker, {
_0: state._0,
[Symbol.for("name")]: "EvalMessage"
});
}

}), [state]);
return [
state,
(function (code) {
return Curry._1(dispatch, {
TAG: 0,
_0: code,
[Symbol.for("name")]: "Evaluate"
});
})
];
}

export {
Config ,
EvalWorker ,
worker ,
eventListeners ,
addEventListener ,
removeEventListener ,
workerMessageToAction ,
reducer ,
useEval ,

}
/* EvalWorker Not a pure module */
70 changes: 70 additions & 0 deletions src/common/Eval.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
type evalResult = result<string, string>

module Config = {
type fromWorker = ResultMessage({forCode: string, result: evalResult})
type fromApp = EvalMessage(string)
let make = () => Worker.make("./EvalWorker.mjs")
}

module EvalWorker = Worker.Make(Config)

let worker = EvalWorker.make()

let eventListeners = ref([])

worker->EvalWorker.App.onMessage(msg =>
eventListeners.contents->Js.Array2.forEach(listener => listener(msg))
)

let addEventListener = listener => {
eventListeners.contents->Js.Array2.push(listener)->ignore
}
let removeEventListener = listener => {
eventListeners := eventListeners.contents->Js.Array2.filter(l => l !== listener)
}

type state = Idle | Evaluating(string) | Evaluated(string) | Error(string)
type action =
| Evaluate(string)
| Success({forCode: string, message: string})
| Fail({forCode: string, message: string})

let workerMessageToAction = message =>
switch message {
| Config.ResultMessage({forCode, result: Ok(message)}) =>
Success({forCode: forCode, message: message})
| Config.ResultMessage({forCode, result: Error(message)}) =>
Fail({forCode: forCode, message: message})
}

let reducer = (state, action) =>
switch (state, action) {
| (Idle, Evaluate(code)) => Evaluating(code)
| (Evaluating(code), Success({forCode, message})) if forCode === code => Evaluated(message)
| (Evaluating(code), Fail({forCode, message})) if forCode === code => Error(message)
| (Error(_), Evaluate(code)) => Evaluating(code)
| (Evaluated(_), Evaluate(code)) => Evaluating(code)
| _ => state
}

let useEval = () => {
let (state, dispatch) = React.useReducer(reducer, Idle)

React.useEffect1(() => {
let listener = message => message["data"]->workerMessageToAction->dispatch
addEventListener(listener)

Some(() => removeEventListener(listener))
}, [])

React.useEffect1(() => {
switch state {
| Evaluating(code) => worker->EvalWorker.App.postMessage(Config.EvalMessage(code))
| _ => ()
}

None
}, [state])

(state, code => Evaluate(code)->dispatch)
}
2 changes: 2 additions & 0 deletions src/common/EvalWorker.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Generated by ReScript, PLEASE EDIT WITH CARE
/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
Empty file added src/common/EvalWorker.res
Empty file.