From 5d007b7a595c70d6b2bfd2c018fca873f27a04d1 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Wed, 24 Jul 2024 19:42:10 +0000 Subject: [PATCH 1/4] Improve data.R integration --- .../HomePage/DataGenerationWindow/DataPyFileEditor.tsx | 1 + .../HomePage/DataGenerationWindow/DataRFileEditor.tsx | 7 +++++++ .../DataGenerationWindow/getDataGenerationToolbarItems.tsx | 5 +++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/gui/src/app/pages/HomePage/DataGenerationWindow/DataPyFileEditor.tsx b/gui/src/app/pages/HomePage/DataGenerationWindow/DataPyFileEditor.tsx index afbf4806..a6e6c274 100644 --- a/gui/src/app/pages/HomePage/DataGenerationWindow/DataPyFileEditor.tsx +++ b/gui/src/app/pages/HomePage/DataGenerationWindow/DataPyFileEditor.tsx @@ -78,6 +78,7 @@ const DataPyFileEditor: FunctionComponent = ({ const toolbarItems: ToolbarItem[] = useMemo( () => getDataGenerationToolbarItems({ + name: "pyodide", status, runnable: fileContent === editedFileContent, onRun: handleRun, diff --git a/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx b/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx index b3296b05..b6972665 100644 --- a/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx +++ b/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx @@ -63,6 +63,9 @@ const DataRFileEditor: FunctionComponent = ({ setStatus("running"); const rCode = ` +# redirect install.packages to webr's version +webr::shim_install() + # Create a list to store printed statements print_log <- list() @@ -87,6 +90,9 @@ print <- function(..., sep = " ", collapse = NULL) { fileContent + "\n\n" + ` +if (typeof(data) != "list") { + stop("[stan-playground] data must be a list") +} result <- list(data = data, print_log = print_log) json_result <- jsonlite::toJSON(result, pretty = TRUE, auto_unbox = TRUE) json_result @@ -131,6 +137,7 @@ json_result const toolbarItems: ToolbarItem[] = useMemo( () => getDataGenerationToolbarItems({ + name: "WebR", status, runnable: fileContent === editedFileContent, onRun: handleRun, diff --git a/gui/src/app/pages/HomePage/DataGenerationWindow/getDataGenerationToolbarItems.tsx b/gui/src/app/pages/HomePage/DataGenerationWindow/getDataGenerationToolbarItems.tsx index 5985710e..2e0e6c6e 100644 --- a/gui/src/app/pages/HomePage/DataGenerationWindow/getDataGenerationToolbarItems.tsx +++ b/gui/src/app/pages/HomePage/DataGenerationWindow/getDataGenerationToolbarItems.tsx @@ -4,11 +4,12 @@ import { Help, PlayArrow } from "@mui/icons-material"; const getDataGenerationToolbarItems = (o: { status: PyodideWorkerStatus; + name: string; runnable: boolean; onRun: () => void; onHelp: () => void; }): ToolbarItem[] => { - const { status, onRun, runnable, onHelp } = o; + const { status, onRun, runnable, onHelp, name } = o; const ret: ToolbarItem[] = []; ret.push({ type: "button", @@ -29,7 +30,7 @@ const getDataGenerationToolbarItems = (o: { let label: string; let color: string; if (status === "loading") { - label = "Loading pyodide..."; + label = `Loading ${name}...`; color = "blue"; } else if (status === "running") { label = "Running..."; From 8de418f617e51ddd6df01aadf24fc49c26731848 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Wed, 24 Jul 2024 20:05:58 +0000 Subject: [PATCH 2/4] Show different message if same data is produced again --- .../DataGenerationWindow.tsx | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/gui/src/app/pages/HomePage/DataGenerationWindow/DataGenerationWindow.tsx b/gui/src/app/pages/HomePage/DataGenerationWindow/DataGenerationWindow.tsx index 0c24ec97..b4f9dc06 100644 --- a/gui/src/app/pages/HomePage/DataGenerationWindow/DataGenerationWindow.tsx +++ b/gui/src/app/pages/HomePage/DataGenerationWindow/DataGenerationWindow.tsx @@ -38,22 +38,33 @@ const DataGenerationChildWindow: FunctionComponent< const consoleRef = useRef(null); const handleSetData = useCallback( - (data: any) => { - update({ - type: "editFile", - content: JSON.stringify(data, null, 2), - filename: ProjectKnownFiles.DATAFILE, - }); - update({ type: "commitFile", filename: ProjectKnownFiles.DATAFILE }); - // Use "stan-playground" prefix to distinguish from console output of the running code - writeConsoleOutToDiv( - consoleRef, - "[stan-playground] Data updated", - "stdout", - ); + (newData: unknown) => { + const dataJson = JSON.stringify(newData, null, 2); + + if (dataJson !== data.dataFileContent) { + update({ + type: "editFile", + content: dataJson, + filename: ProjectKnownFiles.DATAFILE, + }); + update({ type: "commitFile", filename: ProjectKnownFiles.DATAFILE }); + // Use "stan-playground" prefix to distinguish from console output of the running code + writeConsoleOutToDiv( + consoleRef, + "[stan-playground] Data updated", + "stdout", + ); + } else { + writeConsoleOutToDiv( + consoleRef, + "[stan-playground] Data unchanged", + "stdout", + ); + } }, - [update, consoleRef], + [update, consoleRef, data.dataFileContent], ); + const EditorComponent = language === "python" ? DataPyFileEditor : DataRFileEditor; return ( From badb9d8bf8f4c0a35075bb600f45f3e0786a27e3 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Wed, 24 Jul 2024 20:34:31 +0000 Subject: [PATCH 3/4] Fix R printing --- .../DataGenerationWindow/DataRFileEditor.tsx | 67 ++++++------------- 1 file changed, 21 insertions(+), 46 deletions(-) diff --git a/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx b/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx index b6972665..b5c0f386 100644 --- a/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx +++ b/gui/src/app/pages/HomePage/DataGenerationWindow/DataRFileEditor.tsx @@ -5,9 +5,10 @@ import { useMemo, useState, } from "react"; -import { WebR } from "webr"; +import { RString, WebR } from "webr"; import getDataGenerationToolbarItems from "./getDataGenerationToolbarItems"; import TextEditor, { ToolbarItem } from "@SpComponents/TextEditor"; +import { writeConsoleOutToDiv } from "@SpPyodide/AnalysisPyFileEditor"; type Props = { fileName: string; @@ -60,32 +61,14 @@ const DataRFileEditor: FunctionComponent = ({ try { const webR = await loadWebRInstance(); + const shelter = await new webR.Shelter(); + setStatus("running"); const rCode = ` # redirect install.packages to webr's version webr::shim_install() - -# Create a list to store printed statements -print_log <- list() - -# Check if original print function is already saved -if (!exists("sp_original_print")) { - # Save the original print function - sp_original_print <- print -} - -# Override the print function -print <- function(..., sep = " ", collapse = NULL) { - # Capture the printed output - printed_output <- paste(..., sep = sep, collapse = collapse) - - # Append to the print log - print_log <<- c(print_log, printed_output) - - # Call the original print function - sp_original_print(printed_output) -} +\n\n ` + fileContent + "\n\n" + @@ -93,37 +76,29 @@ print <- function(..., sep = " ", collapse = NULL) { if (typeof(data) != "list") { stop("[stan-playground] data must be a list") } -result <- list(data = data, print_log = print_log) -json_result <- jsonlite::toJSON(result, pretty = TRUE, auto_unbox = TRUE) -json_result +.SP_DATA <- jsonlite::toJSON(data, pretty = TRUE, auto_unbox = TRUE) +.SP_DATA `; - const resultJson = await webR.evalRString(rCode); - const result = JSON.parse(resultJson); - if (setData && result.data) { - setData(result.data); - } - if (outputDiv.current && result.print_log) { - result.print_log.forEach((x: string) => { - const divElement = document.createElement("div"); - divElement.style.color = "blue"; - const preElement = document.createElement("pre"); - divElement.appendChild(preElement); - preElement.textContent = x; - outputDiv.current?.appendChild(divElement); + try { + const ret = await shelter.captureR(rCode); + ret.output.forEach(({ type, data }) => { + if (type === "stdout" || type === "stderr") { + writeConsoleOutToDiv(outputDiv, data, type); + } }); + + const result = JSON.parse(await (ret.result as RString).toString()); + if (setData) { + setData(result); + } + } finally { + shelter.purge(); } setStatus("completed"); } catch (e: any) { console.error(e); - if (outputDiv.current) { - const divElement = document.createElement("div"); - divElement.style.color = "red"; - const preElement = document.createElement("pre"); - divElement.appendChild(preElement); - preElement.textContent = e.toString(); - outputDiv.current.appendChild(divElement); - } + writeConsoleOutToDiv(outputDiv, e.toString(), "stderr"); setStatus("failed"); } }, [editedFileContent, fileContent, status, setData, outputDiv]); From 40812be689b8f3b5dcf7950f4dc4f2fefbb9729e Mon Sep 17 00:00:00 2001 From: Jeremy Magland Date: Thu, 25 Jul 2024 09:46:59 -0400 Subject: [PATCH 4/4] Add EditorHintText css class --- gui/src/app/FileEditor/TextEditor.tsx | 2 +- gui/src/localStyles.css | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gui/src/app/FileEditor/TextEditor.tsx b/gui/src/app/FileEditor/TextEditor.tsx index a77b4b2f..5398d188 100644 --- a/gui/src/app/FileEditor/TextEditor.tsx +++ b/gui/src/app/FileEditor/TextEditor.tsx @@ -248,8 +248,8 @@ const createHintTextContentWidget = (hintText: string) => { const node = document.createElement("div"); node.style.width = "max-content"; node.style.pointerEvents = "none"; + node.className = "EditorHintText"; node.textContent = hintText; - node.style.fontStyle = "italic"; return node; }, getId: () => "hintText", diff --git a/gui/src/localStyles.css b/gui/src/localStyles.css index 51a69168..bd47c831 100644 --- a/gui/src/localStyles.css +++ b/gui/src/localStyles.css @@ -192,3 +192,8 @@ span.EditorTitle { height: 100%; overflow: auto; } + +.EditorHintText { + font-style: italic; + color: #aaa; +} \ No newline at end of file