From e24ca0160ce9d19f64446ace6ea1163967ad9a75 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 11:47:18 +0100 Subject: [PATCH 01/65] ignore R project --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7d99c7f..2f06c64 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ bld Untitled*.ipynb *.log /*.ipynb +.Rproj.user From 187dd8d489c27ad6d1ad01f57b14ae941528234b Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 11:47:37 +0100 Subject: [PATCH 02/65] :baby: :package: --- hera/.Rbuildignore | 1 + hera/DESCRIPTION | 11 +++++++++++ hera/LICENSE | 2 ++ hera/LICENSE.md | 21 +++++++++++++++++++++ hera/hera.Rproj | 16 ++++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 hera/.Rbuildignore create mode 100644 hera/DESCRIPTION create mode 100644 hera/LICENSE create mode 100644 hera/LICENSE.md create mode 100644 hera/hera.Rproj diff --git a/hera/.Rbuildignore b/hera/.Rbuildignore new file mode 100644 index 0000000..5163d0b --- /dev/null +++ b/hera/.Rbuildignore @@ -0,0 +1 @@ +^LICENSE\.md$ diff --git a/hera/DESCRIPTION b/hera/DESCRIPTION new file mode 100644 index 0000000..8799d07 --- /dev/null +++ b/hera/DESCRIPTION @@ -0,0 +1,11 @@ +Package: hera +Title: Companion to the 'xeus-r' 'Jupyter' kernel for R +Version: 0.0.0.9000 +Authors@R: c( + person("Romain", "François", email = "romain@tada.science", role = c("aut", "cre") + ) +Description: Set of R functions to be coupled with the xeus-r jupyter kernel for R. +License: MIT + file LICENSE +Encoding: UTF-8 +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.3.2 diff --git a/hera/LICENSE b/hera/LICENSE new file mode 100644 index 0000000..46de013 --- /dev/null +++ b/hera/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2025 +COPYRIGHT HOLDER: Quantstack diff --git a/hera/LICENSE.md b/hera/LICENSE.md new file mode 100644 index 0000000..75f3b16 --- /dev/null +++ b/hera/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2025 Quantstack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/hera/hera.Rproj b/hera/hera.Rproj new file mode 100644 index 0000000..e83436a --- /dev/null +++ b/hera/hera.Rproj @@ -0,0 +1,16 @@ +Version: 1.0 + +RestoreWorkspace: Default +SaveWorkspace: Default +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes From 176225b2d18add5a37ae898c9094406275c53054 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 11:51:05 +0100 Subject: [PATCH 03/65] packages imports --- hera/DESCRIPTION | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hera/DESCRIPTION b/hera/DESCRIPTION index 8799d07..479341a 100644 --- a/hera/DESCRIPTION +++ b/hera/DESCRIPTION @@ -9,3 +9,12 @@ License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 +Imports: + cli, + glue, + IRdisplay, + jsonlite, + R6, + rlang, + tools, + utils From f94f5cbca79d642fb3dc16d56b8f44785f496aba Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 12:02:49 +0100 Subject: [PATCH 04/65] move utils to hera --- {share/jupyter/kernels/xr/resources => hera/R}/utils.R | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {share/jupyter/kernels/xr/resources => hera/R}/utils.R (100%) diff --git a/share/jupyter/kernels/xr/resources/utils.R b/hera/R/utils.R similarity index 100% rename from share/jupyter/kernels/xr/resources/utils.R rename to hera/R/utils.R From 22e9e1fd36e508a5364537ff6f18376c7673f9b8 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 12:03:10 +0100 Subject: [PATCH 05/65] placeholder for when loading hera --- hera/R/zzz.R | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 hera/R/zzz.R diff --git a/hera/R/zzz.R b/hera/R/zzz.R new file mode 100644 index 0000000..0ec1de5 --- /dev/null +++ b/hera/R/zzz.R @@ -0,0 +1,6 @@ +hera_namespace <- environment() + +.onLoad <- function(libname, pkgname) { + # - verify this is running within xeus-r + # - handshake +} From b568c2e44a02193263a601199d078e06b3db29a7 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 12:04:24 +0100 Subject: [PATCH 06/65] interpreter::configure_impl() should just be loading the hera package and calling the configure function. The package will deal with loading the R code --- share/jupyter/kernels/xr/resources/setup.R | 25 ------------------- src/xinterpreter.cpp | 29 ++++------------------ 2 files changed, 5 insertions(+), 49 deletions(-) delete mode 100644 share/jupyter/kernels/xr/resources/setup.R diff --git a/share/jupyter/kernels/xr/resources/setup.R b/share/jupyter/kernels/xr/resources/setup.R deleted file mode 100644 index ddbd1b2..0000000 --- a/share/jupyter/kernels/xr/resources/setup.R +++ /dev/null @@ -1,25 +0,0 @@ -local({ - - attach(new.env(), "tools:xeusr", pos = 2L) - .xeus_env <- as.environment("tools:xeusr") - assign(".xeus_env", .xeus_env, pos = .xeus_env) - - # Sys.which is not available in WebAssembly - if (grepl("emscripten", R.version$os)) { - here <- file.path( - "share", "jupyter", "kernels", "xr", "resources" - ) - } else { - here <- file.path( - dirname(Sys.which("xr")), - "..", "share", "jupyter", "kernels", "xr", "resources" - ) - } - - files <- setdiff(list.files(here), "setup.R") - - for (f in files) { - sys.source(file.path(here, f), envir = .xeus_env) - } - -}) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 2a5a62a..b9a0758 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -142,33 +142,14 @@ void interpreter::configure_impl() { std::stringstream ss; -#ifndef __EMSCRIPTEN__ - // Sys.which is not available in WebAssembly - SEXP sym_Sys_which = Rf_install("Sys.which"); - SEXP sym_dirname = Rf_install("dirname"); - SEXP str_xr = Rf_mkString("xr"); - SEXP call_Sys_which = PROTECT(Rf_lang2(sym_Sys_which, str_xr)); - SEXP call = PROTECT(Rf_lang2(sym_dirname, call_Sys_which)); - SEXP dir_xr = Rf_eval(call, R_GlobalEnv); - ss << CHAR(STRING_ELT(dir_xr, 0)) << "/../share/jupyter/kernels/xr/resources/setup.R"; -#else - ss << "/share/jupyter/kernels/xr/resources/setup.R"; -#endif - - SEXP setup_R_code_path = PROTECT(Rf_mkString(ss.str().c_str())); - - SEXP sym_source = Rf_install("source"); - SEXP call_source = PROTECT(Rf_lang2(sym_source, setup_R_code_path)); - - Rf_eval(call_source, R_GlobalEnv); + SEXP sym_library = Rf_install("library"); + SEXP str_hera = PROTECT(Rf_mkString("hera")); + SEXP call_library_hera = PROTECT(Rf_lang2(sym_library, str_hera)); + SEXP out = PROTECT(Rf_eval(call_library_hera, R_GlobalEnv)); r::invoke_xeusr_fn("configure"); -#ifndef __EMSCRIPTEN__ - UNPROTECT(4); -#else - UNPROTECT(2); -#endif + UNPROTECT(3); } nl::json interpreter::is_complete_request_impl(const std::string& code_) From e91284bcb5ea41370115710dd40db6ae28317179 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:05:52 +0100 Subject: [PATCH 07/65] .xeus_call -> hera:::hera_call --- hera/R/zzz.R | 10 +++++++++- share/jupyter/kernels/xr/resources/call.R | 7 ------- src/routines.cpp | 8 ++++---- src/rtools.hpp | 24 +++++++++++++---------- src/xinterpreter.cpp | 9 ++++----- 5 files changed, 31 insertions(+), 27 deletions(-) delete mode 100644 share/jupyter/kernels/xr/resources/call.R diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 0ec1de5..62f8f4a 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -1,6 +1,14 @@ -hera_namespace <- environment() +NAMESPACE <- environment() .onLoad <- function(libname, pkgname) { # - verify this is running within xeus-r # - handshake } + +hera_call <- function(fn, ...) { + get(fn, envir = NAMESPACE)(...) +} + +hera_new <- function(class, xp) { + get(class, envir = NAMESPACE)$new(xp) +} diff --git a/share/jupyter/kernels/xr/resources/call.R b/share/jupyter/kernels/xr/resources/call.R deleted file mode 100644 index 5600131..0000000 --- a/share/jupyter/kernels/xr/resources/call.R +++ /dev/null @@ -1,7 +0,0 @@ -.xeus_call <- function(fn, ...) { - get(fn, envir = .xeus_env)(...) -} - -.xeus_new <- function(class, xp) { - get(class, envir = .xeus_env)$new(xp) -} diff --git a/src/routines.cpp b/src/routines.cpp index 212c2bf..a51e1e2 100644 --- a/src/routines.cpp +++ b/src/routines.cpp @@ -99,7 +99,7 @@ SEXP CommManager__register_target(SEXP name_) { SEXP xptr_comm = PROTECT(R_MakeExternalPtr( reinterpret_cast(ptr_comm), R_NilValue, R_NilValue )); - SEXP r6_comm = PROTECT(r::new_r6("Comm", xptr_comm)); + SEXP r6_comm = PROTECT(r::new_hera_r6("Comm", xptr_comm)); // request auto ptr_request = new xeus::xmessage(std::move(request)); @@ -110,10 +110,10 @@ SEXP CommManager__register_target(SEXP name_) { delete reinterpret_cast(R_ExternalPtrAddr(xp)); }, FALSE); - SEXP r6_request = PROTECT(r::new_r6("Message", xptr_request)); + SEXP r6_request = PROTECT(r::new_hera_r6("Message", xptr_request)); // callback - r::invoke_xeusr_fn(".CommManager__register_target_callback", r6_comm, r6_request); + r::invoke_hera_fn(".CommManager__register_target_callback", r6_comm, r6_request); UNPROTECT(4); }; @@ -204,7 +204,7 @@ class Comm_Message_handler { SEXP call = PROTECT(r::r_call( m_handler, - r::new_r6("Message", xptr_message)) + r::new_hera_r6("Message", xptr_message)) ); Rf_eval(call, R_GlobalEnv); diff --git a/src/rtools.hpp b/src/rtools.hpp index a423134..f6d5450 100644 --- a/src/rtools.hpp +++ b/src/rtools.hpp @@ -34,27 +34,31 @@ SEXP r_call(SEXP head, Types... tail) { } template -SEXP invoke_xeusr_fn(const char* f, Types... args) { - SEXP sym_xeus_call = Rf_install(".xeus_call"); - - SEXP call = PROTECT(r_call(sym_xeus_call, Rf_mkString(f), args...)); +SEXP invoke_hera_fn(const char* f, Types... args) { + SEXP sym_hera = Rf_install("hera"); + SEXP sym_hera_call = Rf_install("hera_call"); + SEXP sym_triple_colon = Rf_install(":::"); + + SEXP call_triple_colon = PROTECT(r_call(sym_triple_colon, sym_hera, sym_hera_call)); + SEXP call = PROTECT(r_call(call_triple_colon, Rf_mkString(f), args...)); SEXP result = Rf_eval(call, R_GlobalEnv); - UNPROTECT(1); + UNPROTECT(2); return result; } -inline SEXP new_r6(const char* klass, SEXP xp) { - SEXP sym_new_r6 = Rf_install(".xeus_new"); +inline SEXP new_hera_r6(const char* klass, SEXP xp) { + SEXP sym_hera = Rf_install("hera"); + SEXP sym_hera_new = Rf_install("hera_new"); + SEXP sym_triple_colon = Rf_install(":::"); - SEXP call = PROTECT(r_call(sym_new_r6, Rf_mkString(klass), xp)); + SEXP call = PROTECT(r_call(sym_triple_colon, Rf_mkString(klass), xp)); SEXP result = Rf_eval(call, R_GlobalEnv); - UNPROTECT(1); + UNPROTECT(2); return result; } - } } diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index b9a0758..7b9b348 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -105,7 +105,7 @@ void interpreter::execute_request_impl( SEXP execution_counter_ = PROTECT(Rf_ScalarInteger(execution_count)); SEXP silent_ = PROTECT(Rf_ScalarLogical(config.silent)); - SEXP result = r::invoke_xeusr_fn("execute", code_, execution_counter_, silent_); + SEXP result = r::invoke_hera_fn("execute", code_, execution_counter_, silent_); if (Rf_inherits(result, "error_reply")) { std::string evalue = CHAR(STRING_ELT(VECTOR_ELT(result, 0), 0)); @@ -147,7 +147,7 @@ void interpreter::configure_impl() SEXP call_library_hera = PROTECT(Rf_lang2(sym_library, str_hera)); SEXP out = PROTECT(Rf_eval(call_library_hera, R_GlobalEnv)); - r::invoke_xeusr_fn("configure"); + r::invoke_hera_fn("configure"); UNPROTECT(3); } @@ -219,7 +219,7 @@ nl::json interpreter::complete_request_impl(const std::string& code, int cursor_ SEXP code_ = PROTECT(Rf_mkString(code.c_str())); SEXP cursor_pos_ = PROTECT(Rf_ScalarInteger(cursor_pos)); - SEXP result = PROTECT(r::invoke_xeusr_fn("complete", code_, cursor_pos_)); + SEXP result = PROTECT(r::invoke_hera_fn("complete", code_, cursor_pos_)); auto matches = json_from_character_vector(VECTOR_ELT(result, 0)); int cursor_start = INTEGER_ELT(VECTOR_ELT(result, 1), 0); @@ -235,11 +235,10 @@ nl::json interpreter::complete_request_impl(const std::string& code, int cursor_ nl::json interpreter::inspect_request_impl(const std::string& code, int cursor_pos, int /*detail_level*/) { - SEXP code_ = PROTECT(Rf_mkString(code.c_str())); SEXP cursor_pos_ = PROTECT(Rf_ScalarInteger(cursor_pos)); - SEXP result = PROTECT(r::invoke_xeusr_fn("inspect", code_, cursor_pos_)); + SEXP result = PROTECT(r::invoke_hera_fn("inspect", code_, cursor_pos_)); bool found = LOGICAL_ELT(VECTOR_ELT(result, 0), 0); if (!found) { UNPROTECT(3); From f57ce3272ab57b1e49905b079ae865943b959a7c Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:12:30 +0100 Subject: [PATCH 08/65] ignore .Rhistory --- .gitignore | 2 ++ src/routines.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2f06c64..a7a1169 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,5 @@ Untitled*.ipynb *.log /*.ipynb .Rproj.user +.Rhistory + diff --git a/src/routines.cpp b/src/routines.cpp index a51e1e2..e55aaae 100644 --- a/src/routines.cpp +++ b/src/routines.cpp @@ -210,7 +210,6 @@ class Comm_Message_handler { Rf_eval(call, R_GlobalEnv); UNPROTECT(2); - } private: From 993ea51e87a4ba592ee2d77a55ffffbfe86a71c4 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:38:14 +0100 Subject: [PATCH 09/65] + hera_dot_call() --- hera/R/zzz.R | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 62f8f4a..681f04d 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -12,3 +12,23 @@ hera_call <- function(fn, ...) { hera_new <- function(class, xp) { get(class, envir = NAMESPACE)$new(xp) } + +is_xeus <- function() { + dlls <- getLoadedDLLs() + embeding <- dlls[["(embedding)"]] + !is.null(embedding) && "xeusr_kernel_info_request" %in% getDLLRegisteredRoutines()$.Call +} + +hera_dot_call <- function(fn, ...) { + # TODO: check that we are indeed withing xeus-r + # and have some sort of plan B + + # if (!is_xeusr()) { + # ... + # } + + call <- rlang::call2(".Call", fn, ..., PACKAGE = "(embedding)") + eval.parent(call) +} + + From f2bd5db23c8f2713dbc6e03e2af7c53ed53ae2ca Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:38:34 +0100 Subject: [PATCH 10/65] move comm code to hera using hera_dot_call --- .../kernels/xr/resources => hera/R}/comm.R | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) rename {share/jupyter/kernels/xr/resources => hera/R}/comm.R (58%) diff --git a/share/jupyter/kernels/xr/resources/comm.R b/hera/R/comm.R similarity index 58% rename from share/jupyter/kernels/xr/resources/comm.R rename to hera/R/comm.R index 619bdc4..7e845ec 100644 --- a/share/jupyter/kernels/xr/resources/comm.R +++ b/hera/R/comm.R @@ -3,7 +3,7 @@ target_callback(comm, request) } -CommManagerClass <- R6::R6Class("CommManagerClass", +CommManagerClass <- R6::R6Class("CommManagerClass", public = list( initialize = function() { private$targets <- new.env() @@ -12,90 +12,90 @@ CommManagerClass <- R6::R6Class("CommManagerClass", register_comm_target = function(target_name, callback) { private$targets[[target_name]] <- callback - invisible(.Call("CommManager__register_target", target_name, PACKAGE = "(embedding)")) - }, + invisible(hera_dot_call("CommManager__register_target", target_name, PACKAGE = "(embedding)")) + }, unregister_comm_target = function(target_name) { rm(list = target_name, private$targets) - invisible(.Call("CommManager__unregister_target", target_name, PACKAGE = "(embedding)")) - }, + invisible(hera_dot_call("CommManager__unregister_target", target_name)) + }, new_comm = function(target_name) { - xp <- .Call("CommManager__new_comm", target_name, PACKAGE = "(embedding)") + xp <- hera_dot_call("CommManager__new_comm", target_name) if (is.null(xp)) { stop(glue::glue("No target '{target_name}' registered")) } Comm$new(xp = xp) } - ), + ), private = list( - targets = NULL, + targets = NULL, comms = NULL ) ) CommManager <- CommManagerClass$new() -Comm <- R6::R6Class("Comm", +Comm <- R6::R6Class("Comm", public = list( initialize = function(xp) { private$xp <- xp - }, + }, open = function(metadata = NULL, data = NULL) { js_metadata <- jsonlite::toJSON(metadata) js_data <- jsonlite::toJSON(data) - invisible(.Call("Comm__open", private$xp, js_metadata, js_data, PACKAGE = "(embedding)")) - }, + invisible(hera_dot_call("Comm__open", private$xp, js_metadata, js_data)) + }, close = function(metadata = NULL, data = NULL) { js_metadata <- jsonlite::toJSON(metadata) js_data <- jsonlite::toJSON(data) - invisible(.Call("Comm__close", private$xp, js_metadata, js_data, PACKAGE = "(embedding)")) - }, + invisible(hera_dot_call("Comm__close", private$xp, js_metadata, js_data)) + }, send = function(metadata = NULL, data = NULL) { js_metadata <- jsonlite::toJSON(metadata) js_data <- jsonlite::toJSON(data) - invisible(.Call("Comm__send", private$xp, js_metadata, js_data, PACKAGE = "(embedding)")) - }, + invisible(hera_dot_call("Comm__send", private$xp, js_metadata, js_data)) + }, on_close = function(handler) { private$close_handler <- handler - invisible(.Call("Comm__on_close", private$xp, handler, PACKAGE = "(embedding)")) - }, + invisible(hera_dot_call("Comm__on_close", private$xp, handler) + }, on_message = function(handler) { private$message_handler <- handler - invisible(.Call("Comm__on_message", private$xp, handler, PACKAGE = "(embedding)")) + invisible(hera_dot_call("Comm__on_message", private$xp, handler)) } - ), + ), active = list( id = function() { - .Call("Comm__id", private$xp, PACKAGE = "(embedding)") - }, + hera_dot_call("Comm__id", private$xp) + }, target_name = function() { - .Call("Comm__target_name", private$xp, PACKAGE = "(embedding)") + hera_dot_call("Comm__target_name", private$xp) } ), - + private = list( - xp = NULL, - close_handler = NULL, + xp = NULL, + close_handler = NULL, message_handler = NULL ) ) -Message <- R6::R6Class("Message", +Message <- R6::R6Class("Message", public = list( initialize = function(xp) { private$xp <- xp - }, + }, print = function() { print(cli::rule("$content")) @@ -110,23 +110,23 @@ Message <- R6::R6Class("Message", print(cli::rule("$metadata")) str(self$metadata) } - ), + ), active = list( content = function() { - jsonlite::fromJSON(.Call("Message__get_content", private$xp, PACKAGE = "(embedding)")) - }, + jsonlite::fromJSON(hera_dot_call("Message__get_content", private$xp)) + }, header = function() { - jsonlite::fromJSON(.Call("Message__get_header", private$xp, PACKAGE = "(embedding)")) - }, + jsonlite::fromJSON(hera_dot_call("Message__get_header", private$xp)) + }, parent_header = function() { - jsonlite::fromJSON(.Call("Message__get_parent_header", private$xp, PACKAGE = "(embedding)")) - }, + jsonlite::fromJSON(hera_dot_call("Message__get_parent_header", private$xp)) + }, metadata = function() { - jsonlite::fromJSON(.Call("Message__get_metadata", private$xp, PACKAGE = "(embedding)")) + jsonlite::fromJSON(hera_dot_call("Message__get_metadata", private$xp)) } ), From e3512484094e8e59ab248d54bb85060035f5d8d2 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:38:46 +0100 Subject: [PATCH 11/65] move completion code to hera --- .../xr/resources => hera/R}/completion.R | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) rename {share/jupyter/kernels/xr/resources => hera/R}/completion.R (53%) diff --git a/share/jupyter/kernels/xr/resources/completion.R b/hera/R/completion.R similarity index 53% rename from share/jupyter/kernels/xr/resources/completion.R rename to hera/R/completion.R index 9a5c7fa..1249aa0 100644 --- a/share/jupyter/kernels/xr/resources/completion.R +++ b/hera/R/completion.R @@ -1,7 +1,15 @@ +triple_colon <- function(pkg, fun) { + eval(rlang::call2(":::", as.symbol(pkg), as.symbol(fun))) +} + +utils___assignLineBuffer <- triple_colon("utils", ".assignLinebuffer") +utils___assignEnd <- triple_colon("utils", ".assignLinebuffer") +utils___guessTokenFromLine <- triple_colon("utils", ".guessTokenFromLine") +utils___completeToken <- triple_colon("utils", ".completeToken") +utils___retrieveCompletions <- triple_colon("utils", ".retrieveCompletions") + # This is mostly inspired from IRkernel::completions() complete <- function(code, cursor_pos = nchar(code)) { - # ---- TODO: this should be done on the C++ side - # Find which line we're on and position within that line lines <- strsplit(code, '\n', fixed = TRUE)[[1]] chars_before_line <- 0L @@ -14,22 +22,21 @@ complete <- function(code, cursor_pos = nchar(code)) { chars_before_line <- chars_before_line + nchar(line) + 1L } - # guard from errors when completion is invoked in empty cells + # guard from errors when completion is invoked in empty cells if (is.null(line)) { line <- '' } - utils:::.assignLinebuffer(line) - utils:::.assignEnd(cursor_pos) + utils___assignLineBuffer(line) + utils___assignEnd(cursor_pos) - info <- utils:::.guessTokenFromLine(update = FALSE) - utils:::.guessTokenFromLine() - utils:::.completeToken() + info <- utils___guessTokenFromLine(update = FALSE) + utils___guessTokenFromLine() + utils___completeToken() start_position <- chars_before_line + info$start - comps <- utils:::.retrieveCompletions() + comps <- utils___retrieveCompletions() # TODO: use jsonlite::toJSON() here list(comps, c(start_position, start_position + nchar(info$token))) - -} \ No newline at end of file +} From d76fe9fa866b0de6ec69fae9792b18e01f35c349 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:42:49 +0100 Subject: [PATCH 12/65] move inspect code to hera --- {share/jupyter/kernels/xr/resources => hera/R}/inspect.R | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {share/jupyter/kernels/xr/resources => hera/R}/inspect.R (100%) diff --git a/share/jupyter/kernels/xr/resources/inspect.R b/hera/R/inspect.R similarity index 100% rename from share/jupyter/kernels/xr/resources/inspect.R rename to hera/R/inspect.R From 5737a26523e7a1ba4ac8b308ce307b21e73b4da9 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:43:26 +0100 Subject: [PATCH 13/65] init_options() happens as part of loading the package --- hera/R/zzz.R | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 681f04d..5d5568e 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -3,6 +3,27 @@ NAMESPACE <- environment() .onLoad <- function(libname, pkgname) { # - verify this is running within xeus-r # - handshake + + init_options() +} + +init_options <- function() { + options( + device = get_null_device(), + cli.num_colors = 256L, + jupyter.plot_mimetypes = c('text/plain', 'image/png'), + jupyter.plot_scale = 2, + + jupyter.rich_display = TRUE, + jupyter.base_display_func = display_data, + jupyter.clear_output_func = clear_output + ) + + repos <- getOption('repos') + if (identical(repos, c(CRAN = '@CRAN@'))) { + repos[['CRAN']] <- 'https://cran.r-project.org' + options(repos = repos) + } } hera_call <- function(fn, ...) { @@ -31,4 +52,13 @@ hera_dot_call <- function(fn, ...) { eval.parent(call) } +get_null_device <- function() { + os <- get_os() + + ok_device <- switch(os, win = png, osx = pdf, unix = png, wasm = png) + null_filename <- switch(os, win = 'NUL', osx = NULL, unix = '/dev/null', wasm = '/tmp/null') + + null_device <- function(filename = null_filename, ...) ok_device(filename, ...) + null_device +} From 6251a802148c5d67387d967ac5b68f42f3b8b637 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:43:43 +0100 Subject: [PATCH 14/65] njo longer need these --- .../jupyter/kernels/xr/resources/configure.R | 42 ------------------- share/jupyter/kernels/xr/resources/jsonlite.R | 0 2 files changed, 42 deletions(-) delete mode 100644 share/jupyter/kernels/xr/resources/configure.R delete mode 100644 share/jupyter/kernels/xr/resources/jsonlite.R diff --git a/share/jupyter/kernels/xr/resources/configure.R b/share/jupyter/kernels/xr/resources/configure.R deleted file mode 100644 index 39819d2..0000000 --- a/share/jupyter/kernels/xr/resources/configure.R +++ /dev/null @@ -1,42 +0,0 @@ -get_null_device <- function() { - os <- get_os() - - ok_device <- switch(os, win = png, osx = pdf, unix = png, wasm = png) - null_filename <- switch(os, win = 'NUL', osx = NULL, unix = '/dev/null', wasm = '/tmp/null') - - null_device <- function(filename = null_filename, ...) ok_device(filename, ...) - null_device -} - -init_options <- function() { - options( - device = get_null_device(), - cli.num_colors = 256L, - jupyter.plot_mimetypes = c('text/plain', 'image/png'), - jupyter.plot_scale = 2, - - jupyter.rich_display = TRUE, - jupyter.base_display_func = display_data, - jupyter.clear_output_func = clear_output - ) - - repos <- getOption('repos') - if (identical(repos, c(CRAN = '@CRAN@'))) { - repos[['CRAN']] <- 'https://cran.r-project.org' - options(repos = repos) - } -} - -configure <- function() { - pos <- which(search() == "tools:xeusr") - - attachNamespace("IRdisplay", pos = pos + 1) - attachNamespace("glue", pos = pos + 1) - attachNamespace("jsonlite", pos = pos + 1) - - # setMethod(jsonlite:::asJSON, "shiny.tag", function(x, ...) { - # jsonlite:::asJSON(as.character(x), ...) - # }) - - init_options() -} diff --git a/share/jupyter/kernels/xr/resources/jsonlite.R b/share/jupyter/kernels/xr/resources/jsonlite.R deleted file mode 100644 index e69de29..0000000 From 4554ed4b531007b05ed9e479e8f1d0c63af0f261 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:44:31 +0100 Subject: [PATCH 15/65] move log code to hera --- {share/jupyter/kernels/xr/resources => hera/R}/log.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {share/jupyter/kernels/xr/resources => hera/R}/log.R (81%) diff --git a/share/jupyter/kernels/xr/resources/log.R b/hera/R/log.R similarity index 81% rename from share/jupyter/kernels/xr/resources/log.R rename to hera/R/log.R index 25e0644..1ccecce 100644 --- a/share/jupyter/kernels/xr/resources/log.R +++ b/hera/R/log.R @@ -3,7 +3,7 @@ logger <- function(level, name) { function(...) { if (isTRUE(getOption('jupyter.log_level') >= level)) { msg <- glue::glue(...) - .Call("xeusr_log", name, msg, PACKAGE = "(embedding)") + hera_dot_call("xeusr_log", name, msg) } invisible(NULL) } From 911a0013a1e26c0a9147d481b558beab58cc1acd Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:49:36 +0100 Subject: [PATCH 16/65] no longer need to be here --- share/jupyter/kernels/xr/resources/routines.R | 4 ---- 1 file changed, 4 deletions(-) diff --git a/share/jupyter/kernels/xr/resources/routines.R b/share/jupyter/kernels/xr/resources/routines.R index 9f11fc4..acdd9b7 100644 --- a/share/jupyter/kernels/xr/resources/routines.R +++ b/share/jupyter/kernels/xr/resources/routines.R @@ -22,10 +22,6 @@ is_complete_request <- function(code) { .Call("xeusr_is_complete_request", code, PACKAGE = "(embedding)") } -cell_options <- function(...) { - rlang::local_options(..., .frame = .xeusr_private_env$frame_cell_execute) -} - View <- function(x, title) { if (!missing(title)) IRdisplay::display_text(title) IRdisplay::display(x) From fff3ae42cbe10b460b5529d661f1e6a9a16c9bad Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:49:44 +0100 Subject: [PATCH 17/65] comment --- share/jupyter/kernels/xr/resources/execute.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/jupyter/kernels/xr/resources/execute.R b/share/jupyter/kernels/xr/resources/execute.R index b744cad..ada9dd7 100644 --- a/share/jupyter/kernels/xr/resources/execute.R +++ b/share/jupyter/kernels/xr/resources/execute.R @@ -1,3 +1,4 @@ +# these might need to be become the$ last_plot <- NULL last_visible <- TRUE last_error <- NULL @@ -156,7 +157,7 @@ execute <- function(code, execution_counter, silent = FALSE) { filename <- glue::glue("[{execution_counter}]") - frame_cell_execute <<- environment() + the$frame_cell_execute <- environment() evaluate::evaluate( code, envir = globalenv(), From e74ebc5585f9a6335c670c1cfda324ce16584220 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:50:13 +0100 Subject: [PATCH 18/65] moving cell_options() to here --- .../kernels/xr/resources/options.R => hera/R/cell_options.R | 2 -- 1 file changed, 2 deletions(-) rename share/jupyter/kernels/xr/resources/options.R => hera/R/cell_options.R (76%) diff --git a/share/jupyter/kernels/xr/resources/options.R b/hera/R/cell_options.R similarity index 76% rename from share/jupyter/kernels/xr/resources/options.R rename to hera/R/cell_options.R index 05ec327..f3ec543 100644 --- a/share/jupyter/kernels/xr/resources/options.R +++ b/hera/R/cell_options.R @@ -1,5 +1,3 @@ -frame_cell_execute <- NULL - cell_options <- function(...) { rlang::local_options(..., .frame = frame_cell_execute) } From 4ed6e86eb46dada18ee78b0b360ff05e82f83ce1 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 13:50:30 +0100 Subject: [PATCH 19/65] the$frame_cell_execute --- hera/R/zzz.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 5d5568e..329c7d9 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -1,4 +1,6 @@ NAMESPACE <- environment() +the <- new.env() +the$frame_cell_execute <- NULL .onLoad <- function(libname, pkgname) { # - verify this is running within xeus-r From 26b37bf906d44e7d4b2da122e2f29efe1cffab97 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 15:05:13 +0100 Subject: [PATCH 20/65] more moving things around --- .../kernels/xr/resources => hera/R}/execute.R | 40 +++++++++---------- hera/R/inspect.R | 2 +- .../xr/resources => hera/R}/routines.R | 0 hera/R/utils.R | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) rename {share/jupyter/kernels/xr/resources => hera/R}/execute.R (81%) rename {share/jupyter/kernels/xr/resources => hera/R}/routines.R (100%) diff --git a/share/jupyter/kernels/xr/resources/execute.R b/hera/R/execute.R similarity index 81% rename from share/jupyter/kernels/xr/resources/execute.R rename to hera/R/execute.R index ada9dd7..b9ebe32 100644 --- a/share/jupyter/kernels/xr/resources/execute.R +++ b/hera/R/execute.R @@ -1,7 +1,7 @@ # these might need to be become the$ -last_plot <- NULL -last_visible <- TRUE -last_error <- NULL +the$last_plot <- NULL +the$last_visible <- TRUE +the$last_error <- NULL handle_message <- function(msg) { publish_stream("stderr", conditionMessage(msg)) @@ -48,7 +48,7 @@ trim_rlang_error <- function(e) { handle_error <- function(e) { if (inherits(e, "rlang_error") && isNamespaceLoaded("rlang")) { e <- trim_rlang_error(e) - assign("last_error", e, rlang:::the) + assign("last_error", e, triple_colon("rlang", "the")) trace_back <- c( cli::col_red("--- Error"), @@ -57,7 +57,7 @@ handle_error <- function(e) { cli::col_red("--- Traceback"), format(e$trace) ) - last_error <<- structure(list(ename = "ERROR", evalue = "", trace_back), class = "error_reply") + the$last_error <- structure(list(ename = "ERROR", evalue = "", trace_back), class = "error_reply") } else { sys_calls <- sys.calls() sys_calls <- head(tail(sys_calls, -16), -3) @@ -71,7 +71,7 @@ handle_error <- function(e) { cli::col_red("--- Traceback (most recent call last)"), stack ) - last_error <<- structure(list(ename = "ERROR", evalue = evalue, trace_back), class = "error_reply") + the$last_error <- structure(list(ename = "ERROR", evalue = evalue, trace_back), class = "error_reply") } } @@ -80,7 +80,7 @@ handle_value <- function(obj, visible) { if (visible && inherits(obj, "ggplot")) { print(obj) - last_visible <<- FALSE + the$last_visible <- FALSE } } @@ -91,11 +91,11 @@ handle_graphics <- function(plot) { attr(plot, ".irkernel_res") <- getOption('repr.plot.res', repr::repr_option_defaults$repr.plot.res) attr(plot, ".irkernel_ppi") <- attr(plot, ".irkernel_res") / getOption('jupyter.plot_scale', 2) - if (!plot_builds_upon(last_plot, plot)) { - send_plot(last_plot) + if (!plot_builds_upon(the$last_plot, plot)) { + send_plot(the$last_plot) } - last_plot <<- plot + the$last_plot <- plot } send_plot <- function(plot) { @@ -128,16 +128,16 @@ send_plot <- function(plot) { } execute <- function(code, execution_counter, silent = FALSE) { - last_error <<- NULL + the$last_error <- NULL parsed <- tryCatch( parse(text = code), error = function(e) { msg <- paste(conditionMessage(e), collapse = "\n") - last_error <<- structure(list(ename = "PARSE ERROR", evalue = msg), class = "error_reply") + the$last_error <- structure(list(ename = "PARSE ERROR", evalue = msg), class = "error_reply") } ) - if (!is.null(last_error)) return(last_error) + if (!is.null(the$last_error)) return(the$last_error) output_handler <- if (silent) { evaluate::new_output_handler() @@ -152,8 +152,8 @@ execute <- function(code, execution_counter, silent = FALSE) { ) } - last_plot <<- NULL - last_visible <<- FALSE + the$last_plot <- NULL + the$last_visible <- FALSE filename <- glue::glue("[{execution_counter}]") @@ -165,14 +165,14 @@ execute <- function(code, execution_counter, silent = FALSE) { stop_on_error = 1L, filename = filename ) - if (!is.null(last_error)) return(last_error) + if (!is.null(the$last_error)) return(the$last_error) - if (!silent && !is.null(last_plot)) { - tryCatch(send_plot(last_plot), error = handle_error) + if (!silent && !is.null(the$last_plot)) { + tryCatch(send_plot(the$last_plot), error = handle_error) } - if (!is.null(last_error)) return(last_error) + if (!is.null(the$last_error)) return(the$last_error) - if (isTRUE(last_visible)) { + if (isTRUE(the$last_visible)) { obj <- .Last.value # TODO: This probably needs to be generalized diff --git a/hera/R/inspect.R b/hera/R/inspect.R index ac790c2..f3ba2bc 100644 --- a/hera/R/inspect.R +++ b/hera/R/inspect.R @@ -10,7 +10,7 @@ inspect <- function(code, cursor_pos) { # check them by a loop. Use get since R CMD check does not like ::: token <- '' for (i in seq(cursor_pos, nchar(code))) { - token_candidate <- utils:::.guessTokenFromLine(code, i) + token_candidate <- utils___guessTokenFromLine(code, i) if (nchar(token_candidate) == 0) break token <- token_candidate } diff --git a/share/jupyter/kernels/xr/resources/routines.R b/hera/R/routines.R similarity index 100% rename from share/jupyter/kernels/xr/resources/routines.R rename to hera/R/routines.R diff --git a/hera/R/utils.R b/hera/R/utils.R index 278e49c..a8c7218 100644 --- a/hera/R/utils.R +++ b/hera/R/utils.R @@ -13,7 +13,7 @@ namedlist <- function() { } set_last_value <- function(obj, visible) { - last_visible <<- visible + the$last_visible <- visible unlockBinding(".Last.value", .BaseNamespaceEnv) assign(".Last.value", obj, .BaseNamespaceEnv) From bd1c5486479c7ee7e10074d3e7e42ab8b4010ac7 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 15:15:52 +0100 Subject: [PATCH 21/65] oopsy --- hera/DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hera/DESCRIPTION b/hera/DESCRIPTION index 479341a..e57cdbb 100644 --- a/hera/DESCRIPTION +++ b/hera/DESCRIPTION @@ -2,7 +2,7 @@ Package: hera Title: Companion to the 'xeus-r' 'Jupyter' kernel for R Version: 0.0.0.9000 Authors@R: c( - person("Romain", "François", email = "romain@tada.science", role = c("aut", "cre") + person("Romain", "François", email = "romain@tada.science", role = c("aut", "cre")) ) Description: Set of R functions to be coupled with the xeus-r jupyter kernel for R. License: MIT + file LICENSE From 55b05073bdb94402d7ea13997fd51dceae3b1a5c Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 15:16:06 +0100 Subject: [PATCH 22/65] pkg stuff --- hera/.Rbuildignore | 2 ++ hera/hera.Rproj | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/hera/.Rbuildignore b/hera/.Rbuildignore index 5163d0b..2eb964e 100644 --- a/hera/.Rbuildignore +++ b/hera/.Rbuildignore @@ -1 +1,3 @@ ^LICENSE\.md$ +^.*\.Rproj$ +^\.Rproj\.user$ diff --git a/hera/hera.Rproj b/hera/hera.Rproj index e83436a..497f8bf 100644 --- a/hera/hera.Rproj +++ b/hera/hera.Rproj @@ -14,3 +14,7 @@ LaTeX: pdfLaTeX AutoAppendNewline: Yes StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source From 08684ca92d7a1a9c10710d7c733e45df84618f25 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 15:20:29 +0100 Subject: [PATCH 23/65] empty NAMESPACE for now --- hera/NAMESPACE | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 hera/NAMESPACE diff --git a/hera/NAMESPACE b/hera/NAMESPACE new file mode 100644 index 0000000..e69de29 From 7661b4ba258c08adabaa297a220f3ee643145f74 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 6 Feb 2025 15:20:41 +0100 Subject: [PATCH 24/65] move the before other files --- hera/R/00_init.R | 2 ++ hera/R/comm.R | 2 +- hera/R/zzz.R | 5 ++--- 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 hera/R/00_init.R diff --git a/hera/R/00_init.R b/hera/R/00_init.R new file mode 100644 index 0000000..b240720 --- /dev/null +++ b/hera/R/00_init.R @@ -0,0 +1,2 @@ +NAMESPACE <- environment() +the <- NULL diff --git a/hera/R/comm.R b/hera/R/comm.R index 7e845ec..2c21135 100644 --- a/hera/R/comm.R +++ b/hera/R/comm.R @@ -65,7 +65,7 @@ Comm <- R6::R6Class("Comm", on_close = function(handler) { private$close_handler <- handler - invisible(hera_dot_call("Comm__on_close", private$xp, handler) + invisible(hera_dot_call("Comm__on_close", private$xp, handler)) }, on_message = function(handler) { diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 329c7d9..0139ee7 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -1,10 +1,9 @@ -NAMESPACE <- environment() -the <- new.env() -the$frame_cell_execute <- NULL .onLoad <- function(libname, pkgname) { # - verify this is running within xeus-r # - handshake + the <<- new.env() + the$frame_cell_execute <- NULL init_options() } From 6abcb834d76ab8f16a5b4e05338ab33d5867561b Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Fri, 7 Feb 2025 09:06:23 +0100 Subject: [PATCH 25/65] ignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a7a1169..6e1a6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,6 @@ Untitled*.ipynb /*.ipynb .Rproj.user .Rhistory +hera.Rcheck +hera_*.tar.gz From 12cf499f34ae6f1b440a1d1cb4312a3f45ba17ab Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Fri, 7 Feb 2025 09:08:09 +0100 Subject: [PATCH 26/65] using roxygen --- hera/hera.Rproj | 1 + 1 file changed, 1 insertion(+) diff --git a/hera/hera.Rproj b/hera/hera.Rproj index 497f8bf..270314b 100644 --- a/hera/hera.Rproj +++ b/hera/hera.Rproj @@ -18,3 +18,4 @@ StripTrailingWhitespace: Yes BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd,collate,namespace From a2624fd243b4172df065507f3a32d4696de3e0e2 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Fri, 7 Feb 2025 09:08:30 +0100 Subject: [PATCH 27/65] rename to avoid check errors --- hera/R/routines.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hera/R/routines.R b/hera/R/routines.R index acdd9b7..1cffaf5 100644 --- a/hera/R/routines.R +++ b/hera/R/routines.R @@ -30,7 +30,7 @@ View <- function(x, title) { ns_utils <- asNamespace("utils") unlockBinding("print.vignette", ns_utils) -print.vignette <- function(x, ...) { +print_vignette <- function(x, ...) { file <- x$PDF if (nzchar(file) == 0) { warning(gettextf("vignette %s has no PDF/HTML", sQuote(x$Topic)), call. = FALSE, domain = NA) @@ -49,7 +49,7 @@ print.vignette <- function(x, ...) { display_data( data = list( "text/html" = paste(html, collapse = "\n") - ), + ), metadata = list( "text/html" = list(isolated = TRUE) ) @@ -58,5 +58,5 @@ print.vignette <- function(x, ...) { invisible(x) } -assign("print.vignette", print.vignette, ns_utils) +assign("print.vignette", print_vignette, ns_utils) lockBinding("print.vignette", ns_utils) From 081e9a1f8f4dafdffe4c6830c8e30d7060f616f2 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Fri, 7 Feb 2025 09:08:44 +0100 Subject: [PATCH 28/65] import pdf and png --- hera/NAMESPACE | 4 ++++ hera/R/zzz.R | 1 + 2 files changed, 5 insertions(+) diff --git a/hera/NAMESPACE b/hera/NAMESPACE index e69de29..7474867 100644 --- a/hera/NAMESPACE +++ b/hera/NAMESPACE @@ -0,0 +1,4 @@ +# Generated by roxygen2: do not edit by hand + +importFrom(grDevices,pdf) +importFrom(grDevices,png) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 0139ee7..10aaac9 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -53,6 +53,7 @@ hera_dot_call <- function(fn, ...) { eval.parent(call) } +#' @importFrom grDevices pdf png get_null_device <- function() { os <- get_os() From ecb8c9b7f4820adebd162438af1646b6901bde2a Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Fri, 7 Feb 2025 09:10:58 +0100 Subject: [PATCH 29/65] more hera_dot_call --- hera/R/routines.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hera/R/routines.R b/hera/R/routines.R index 1cffaf5..1613fb0 100644 --- a/hera/R/routines.R +++ b/hera/R/routines.R @@ -1,25 +1,25 @@ publish_stream <- function(name, text) { - .Call("xeusr_publish_stream", name, text, PACKAGE = "(embedding)") + hera_dot_call("xeusr_publish_stream", name, text) } display_data <- function(data = NULL, metadata = NULL) { - invisible(.Call("xeusr_display_data", jsonlite::toJSON(data), jsonlite::toJSON(metadata), PACKAGE = "(embedding)")) + invisible(hera_dot_call("xeusr_display_data", jsonlite::toJSON(data), jsonlite::toJSON(metadata))) } update_display_data <- function(data = NULL, metadata = NULL) { - invisible(.Call("xeusr_update_display_data", jsonlite::toJSON(data), jsonlite::toJSON(metadata), PACKAGE = "(embedding)")) + invisible(hera_dot_call("xeusr_update_display_data", jsonlite::toJSON(data), jsonlite::toJSON(metadata))) } kernel_info_request <- function() { - .Call("xeusr_kernel_info_request", PACKAGE = "(embedding)") + hera_dot_call("xeusr_kernel_info_request") } clear_output <- function(wait = FALSE) { - invisible(.Call("xeusr_clear_output", isTRUE(wait), PACKAGE = "(embedding)")) + invisible(hera_dot_call("xeusr_clear_output", isTRUE(wait))) } is_complete_request <- function(code) { - .Call("xeusr_is_complete_request", code, PACKAGE = "(embedding)") + hera_dot_call("xeusr_is_complete_request", code) } View <- function(x, title) { From e7007385bb3622ae9eb97a15a2e5b3c7365c86e4 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Fri, 7 Feb 2025 09:33:27 +0100 Subject: [PATCH 30/65] passing check --- hera/DESCRIPTION | 2 ++ hera/NAMESPACE | 6 ++++++ hera/R/cell_options.R | 2 +- hera/R/comm.R | 8 +++++--- hera/R/routines.R | 33 -------------------------------- hera/R/utils.R | 8 ++++---- hera/R/zzz.R | 44 ++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 59 insertions(+), 44 deletions(-) diff --git a/hera/DESCRIPTION b/hera/DESCRIPTION index e57cdbb..98d1ede 100644 --- a/hera/DESCRIPTION +++ b/hera/DESCRIPTION @@ -11,10 +11,12 @@ Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 Imports: cli, + evaluate, glue, IRdisplay, jsonlite, R6, + repr, rlang, tools, utils diff --git a/hera/NAMESPACE b/hera/NAMESPACE index 7474867..be83355 100644 --- a/hera/NAMESPACE +++ b/hera/NAMESPACE @@ -1,4 +1,10 @@ # Generated by roxygen2: do not edit by hand +importFrom(R6,R6Class) importFrom(grDevices,pdf) importFrom(grDevices,png) +importFrom(jsonlite,toJSON) +importFrom(jsonlite,unbox) +importFrom(utils,capture.output) +importFrom(utils,head) +importFrom(utils,tail) diff --git a/hera/R/cell_options.R b/hera/R/cell_options.R index f3ec543..7977f1f 100644 --- a/hera/R/cell_options.R +++ b/hera/R/cell_options.R @@ -1,3 +1,3 @@ cell_options <- function(...) { - rlang::local_options(..., .frame = frame_cell_execute) + rlang::local_options(..., .frame = the$frame_cell_execute) } diff --git a/hera/R/comm.R b/hera/R/comm.R index 2c21135..21cdb66 100644 --- a/hera/R/comm.R +++ b/hera/R/comm.R @@ -1,9 +1,11 @@ +comm_target_env <- new.env() + .CommManager__register_target_callback <- function(comm, request) { target_callback <- comm_target_env[[request$content$target_name]] target_callback(comm, request) } -CommManagerClass <- R6::R6Class("CommManagerClass", +CommManagerClass <- R6Class("CommManagerClass", public = list( initialize = function() { private$targets <- new.env() @@ -36,7 +38,7 @@ CommManagerClass <- R6::R6Class("CommManagerClass", ) CommManager <- CommManagerClass$new() -Comm <- R6::R6Class("Comm", +Comm <- R6Class("Comm", public = list( initialize = function(xp) { private$xp <- xp @@ -91,7 +93,7 @@ Comm <- R6::R6Class("Comm", ) ) -Message <- R6::R6Class("Message", +Message <- R6Class("Message", public = list( initialize = function(xp) { private$xp <- xp diff --git a/hera/R/routines.R b/hera/R/routines.R index 1613fb0..dc336c1 100644 --- a/hera/R/routines.R +++ b/hera/R/routines.R @@ -27,36 +27,3 @@ View <- function(x, title) { IRdisplay::display(x) invisible(x) } - -ns_utils <- asNamespace("utils") -unlockBinding("print.vignette", ns_utils) -print_vignette <- function(x, ...) { - file <- x$PDF - if (nzchar(file) == 0) { - warning(gettextf("vignette %s has no PDF/HTML", sQuote(x$Topic)), call. = FALSE, domain = NA) - return(invisible(x)) - } - - ext <- tolower(tools::file_ext(file)) - if (ext == "pdf") { - warning("can't display pdf vignette yet") - return(invisible(x)) - } - - if (ext == "html") { - html <- readLines(file.path(x$Dir, "doc", file)) - - display_data( - data = list( - "text/html" = paste(html, collapse = "\n") - ), - metadata = list( - "text/html" = list(isolated = TRUE) - ) - ) - } - - invisible(x) -} -assign("print.vignette", print_vignette, ns_utils) -lockBinding("print.vignette", ns_utils) diff --git a/hera/R/utils.R b/hera/R/utils.R index a8c7218..3556850 100644 --- a/hera/R/utils.R +++ b/hera/R/utils.R @@ -15,9 +15,9 @@ namedlist <- function() { set_last_value <- function(obj, visible) { the$last_visible <- visible - unlockBinding(".Last.value", .BaseNamespaceEnv) + get("unlockBinding", envir = baseenv())(".Last.value", .BaseNamespaceEnv) assign(".Last.value", obj, .BaseNamespaceEnv) - lockBinding(".Last.value", .BaseNamespaceEnv) + get("lockBinding", envir = baseenv())(".Last.value", .BaseNamespaceEnv) } # borrowed from IRkernel @@ -25,9 +25,9 @@ plot_builds_upon <- function(prev, current) { if (is.null(prev)) { return(TRUE) } - + lprev <- length(prev[[1]]) lcurrent <- length(current[[1]]) - + lcurrent >= lprev && identical(current[[1]][1:lprev], prev[[1]][1:lprev]) } diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 10aaac9..456beed 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -1,3 +1,37 @@ +#' @importFrom grDevices pdf png +#' @importFrom jsonlite toJSON unbox +#' @importFrom utils head tail capture.output +#' @importFrom R6 R6Class +NULL + +print_vignette <- function(x, ...) { + file <- x$PDF + if (nzchar(file) == 0) { + warning(gettextf("vignette %s has no PDF/HTML", sQuote(x$Topic)), call. = FALSE, domain = NA) + return(invisible(x)) + } + + ext <- tolower(tools::file_ext(file)) + if (ext == "pdf") { + warning("can't display pdf vignette yet") + return(invisible(x)) + } + + if (ext == "html") { + html <- readLines(file.path(x$Dir, "doc", file)) + + display_data( + data = list( + "text/html" = paste(html, collapse = "\n") + ), + metadata = list( + "text/html" = list(isolated = TRUE) + ) + ) + } + + invisible(x) +} .onLoad <- function(libname, pkgname) { # - verify this is running within xeus-r @@ -5,6 +39,12 @@ the <<- new.env() the$frame_cell_execute <- NULL + ns_utils <- asNamespace("utils") + get("unlockBinding", envir = baseenv())("print.vignette", ns_utils) + + assign("print.vignette", print_vignette, ns_utils) + get("lockBinding", envir = baseenv())("print.vignette", ns_utils) + init_options() } @@ -36,8 +76,7 @@ hera_new <- function(class, xp) { } is_xeus <- function() { - dlls <- getLoadedDLLs() - embeding <- dlls[["(embedding)"]] + embedding <- getLoadedDLLs()[["(embedding)"]] !is.null(embedding) && "xeusr_kernel_info_request" %in% getDLLRegisteredRoutines()$.Call } @@ -53,7 +92,6 @@ hera_dot_call <- function(fn, ...) { eval.parent(call) } -#' @importFrom grDevices pdf png get_null_device <- function() { os <- get_os() From 13dac5b3960002d10ff55eed012e4ecb1ceff422 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Fri, 7 Feb 2025 09:41:31 +0100 Subject: [PATCH 31/65] no configure in hera --- src/xinterpreter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 7b9b348..96312dc 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -145,11 +145,9 @@ void interpreter::configure_impl() SEXP sym_library = Rf_install("library"); SEXP str_hera = PROTECT(Rf_mkString("hera")); SEXP call_library_hera = PROTECT(Rf_lang2(sym_library, str_hera)); - SEXP out = PROTECT(Rf_eval(call_library_hera, R_GlobalEnv)); + Rf_eval(call_library_hera, R_GlobalEnv); - r::invoke_hera_fn("configure"); - - UNPROTECT(3); + UNPROTECT(2); } nl::json interpreter::is_complete_request_impl(const std::string& code_) From 9ca0655cd3afa16c9a51f2b77de41de0f7341ec6 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Mon, 10 Feb 2025 14:11:18 +0100 Subject: [PATCH 32/65] workaround for now --- cmake/FindR.cmake | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake index 16feef5..534bdaf 100644 --- a/cmake/FindR.cmake +++ b/cmake/FindR.cmake @@ -50,7 +50,7 @@ if(R_COMMAND) message(STATUS "Configuring for Emscripten...") # Find the pkg-config executable -find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) + find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) if (PKG_CONFIG_EXECUTABLE) # Get the R version using pkg-config @@ -109,15 +109,13 @@ find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config) OUTPUT_STRIP_TRAILING_WHITESPACE) set(R_LDFLAGS ${R_LDFLAGS} CACHE PATH "R CMD config --ldflags") - find_path(R_INCLUDE_DIR R.h - HINTS ${R_ROOT_DIR} ${R_ROOT_DIR}/bin/${R_LIB_ARCH} - PATHS /usr/local/lib /usr/local/lib64 /usr/share - PATH_SUFFIXES include R/include - DOC "Path to file R.h") + set(R_INCLUDE_DIR "${R_HOME}/include" CACHE PATH "Path to R include directory") + + # find_library(R_LIBRARY_BASE NAMES R + # HINTS ${R_ROOT_DIR}/lib/R/lib ${R_ROOT_DIR}/bin/${R_LIB_ARCH} + # DOC "R library (example libR.a, libR.dylib, etc.).") - find_library(R_LIBRARY_BASE R - HINTS ${R_ROOT_DIR}/lib ${R_ROOT_DIR}/bin/${R_LIB_ARCH} - DOC "R library (example libR.a, libR.dylib, etc.).") + set(R_LIBRARY_BASE "${R_HOME}/lib/libR.dylib" CACHE PATH "R library (example libR.a, libR.dylib, etc.).") find_library(R_LIBRARY_BLAS NAMES Rblas blas HINTS ${R_ROOT_DIR}/lib ${R_ROOT_DIR}/bin/${R_LIB_ARCH} From 9826c5f42f5f40e6861f28b5fa4f2dd8d1cefd23 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 09:09:48 +0100 Subject: [PATCH 33/65] correctly set R_INCLUDE_DIR to R_HOME/include --- cmake/FindR.cmake | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake index 534bdaf..d8387fa 100644 --- a/cmake/FindR.cmake +++ b/cmake/FindR.cmake @@ -110,12 +110,9 @@ if(R_COMMAND) set(R_LDFLAGS ${R_LDFLAGS} CACHE PATH "R CMD config --ldflags") set(R_INCLUDE_DIR "${R_HOME}/include" CACHE PATH "Path to R include directory") - - # find_library(R_LIBRARY_BASE NAMES R - # HINTS ${R_ROOT_DIR}/lib/R/lib ${R_ROOT_DIR}/bin/${R_LIB_ARCH} - # DOC "R library (example libR.a, libR.dylib, etc.).") - - set(R_LIBRARY_BASE "${R_HOME}/lib/libR.dylib" CACHE PATH "R library (example libR.a, libR.dylib, etc.).") + find_library(R_LIBRARY_BASE R + HINTS ${R_ROOT_DIR}/lib ${R_ROOT_DIR}/bin/${R_LIB_ARCH} + DOC "R library (example libR.a, libR.dylib, etc.).") find_library(R_LIBRARY_BLAS NAMES Rblas blas HINTS ${R_ROOT_DIR}/lib ${R_ROOT_DIR}/bin/${R_LIB_ARCH} From e91248f01644e151a503db938a7ce7b646a5ca9e Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 09:15:10 +0100 Subject: [PATCH 34/65] ignore default paths --- cmake/FindR.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/FindR.cmake b/cmake/FindR.cmake index d8387fa..35c4e56 100644 --- a/cmake/FindR.cmake +++ b/cmake/FindR.cmake @@ -112,6 +112,7 @@ if(R_COMMAND) set(R_INCLUDE_DIR "${R_HOME}/include" CACHE PATH "Path to R include directory") find_library(R_LIBRARY_BASE R HINTS ${R_ROOT_DIR}/lib ${R_ROOT_DIR}/bin/${R_LIB_ARCH} + NO_DEFAULT_PATH DOC "R library (example libR.a, libR.dylib, etc.).") find_library(R_LIBRARY_BLAS NAMES Rblas blas From 52df11e102a85850c3dced633a54e0fe1064827c Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 10:07:11 +0100 Subject: [PATCH 35/65] addding R package dependencies of hera --- environment-wasm-host.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/environment-wasm-host.yml b/environment-wasm-host.yml index 825b8e7..4d2000f 100644 --- a/environment-wasm-host.yml +++ b/environment-wasm-host.yml @@ -18,6 +18,8 @@ dependencies: # Run dependencies - r-rlang - r-evaluate + - r-glue + - r-r6 - r-jsonlite - r-glue - r-cli >=3.6.3 From b41efe97aa53c68c63532da1324079f9d8ed1fed Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 10:07:29 +0100 Subject: [PATCH 36/65] + dependencies of hera --- environment-dev.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/environment-dev.yml b/environment-dev.yml index 7bb10b1..5603c39 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -13,13 +13,14 @@ dependencies: - nlohmann_json=3.11.3 - r-base >=4 # Run dependencies - - r-rlang + - r-cli - r-evaluate - - r-jsonlite - r-glue - - r-cli - - r-repr - r-IRdisplay + - r-jsonlite + - r-r6 + - r-repr + - r-rlang # Test dependencies - pytest - jupyter_kernel_test>=0.5,<0.6 From c3b86714c7ef536b52d8c6b37b19c06885ba8a0a Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 10:08:14 +0100 Subject: [PATCH 37/65] locally install R package hera --- CMakeLists.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bfd8e9..06c1e92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,8 +66,17 @@ message(STATUS "R_LDFLAGS = ${R_LDFLAGS}") message(STATUS "R_LIBRARY_BASE = ${R_LIBRARY_BASE}") message(STATUS "R_LIBRARY_BLAS = ${R_LIBRARY_BLAS}") message(STATUS "R_LIBRARY_LAPACK = ${R_LIBRARY_LAPACK}") -message(STATUS "R_VERSION_MAJOR = ${R_VERSION_MAJOR}") -message(STATUS "R_VERSION_MINOR = ${R_VERSION_MINOR}") +message(STATUS "R_VERSION_MAJOR = ${R_VERSION_MAJOR}") +message(STATUS "R_VERSION_MINOR = ${R_VERSION_MINOR}") + +# Install R package hera +# ====================== +if(EMSCRIPTEN) +# +else() + execute_process(COMMAND ${R_COMMAND} CMD INSTALL ../hera) +endif() + # Configuration # ============= From a5448f94eee0035bd7811f1e6a50389d070600ca Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 13:20:55 +0100 Subject: [PATCH 38/65] skip some tests for now --- test/test_xr_kernel.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test_xr_kernel.py b/test/test_xr_kernel.py index fc0a191..87446e2 100644 --- a/test/test_xr_kernel.py +++ b/test/test_xr_kernel.py @@ -18,18 +18,18 @@ class KernelTests(jupyter_kernel_test.KernelTests): code_hello_world = "cat('hello, world')" code_stderr = "message('error')" - completion_samples = [ - {"text": "rnorm(", "matches": {"n=", "mean=", "sd="}} - ] + #completion_samples = [ + # {"text": "rnorm(", "matches": {"n=", "mean=", "sd="}} + #] code_execute_result = [{"code": "6*7", "result": ["[1] 42"]}] - code_display_data = [ - {"code": "plot(0)", "mime": "image/png"}, - {"code": "ggplot2::ggplot(iris, ggplot2::aes(Sepal.Length, Sepal.Width)) + ggplot2::geom_point()", "mime": "image/png"}, - {"code": "View(head(iris))", "mime": "text/html"} - ] + #code_display_data = [ + # {"code": "plot(0)", "mime": "image/png"}, + # {"code": "ggplot2::ggplot(iris, ggplot2::aes(Sepal.Length, Sepal.Width)) + ggplot2::geom_point()", "mime": "image/png"}, + # {"code": "View(head(iris))", "mime": "text/html"} + #] # code_page_something = "?cat" - code_clear_output = "clear_output()" + # code_clear_output = "clear_output()" code_generate_error = "stop('ouch')" code_inspect_sample = "print" From bdb41fb42380da69c3fb73ec7eac43d77b95a819 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 13:32:59 +0100 Subject: [PATCH 39/65] =?UTF-8?q?=F0=9F=AB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hera/R/completion.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hera/R/completion.R b/hera/R/completion.R index 1249aa0..f4a752a 100644 --- a/hera/R/completion.R +++ b/hera/R/completion.R @@ -3,7 +3,7 @@ triple_colon <- function(pkg, fun) { } utils___assignLineBuffer <- triple_colon("utils", ".assignLinebuffer") -utils___assignEnd <- triple_colon("utils", ".assignLinebuffer") +utils___assignEnd <- triple_colon("utils", ".assignEnd") utils___guessTokenFromLine <- triple_colon("utils", ".guessTokenFromLine") utils___completeToken <- triple_colon("utils", ".completeToken") utils___retrieveCompletions <- triple_colon("utils", ".retrieveCompletions") From efbcca7e772f99114e1c038d10e96236c52b1cdb Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 13:37:24 +0100 Subject: [PATCH 40/65] adding some documentatio to complete() --- hera/NAMESPACE | 1 + hera/R/completion.R | 15 ++++++++++++--- hera/man/complete.Rd | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 hera/man/complete.Rd diff --git a/hera/NAMESPACE b/hera/NAMESPACE index be83355..9f3753a 100644 --- a/hera/NAMESPACE +++ b/hera/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(complete) importFrom(R6,R6Class) importFrom(grDevices,pdf) importFrom(grDevices,png) diff --git a/hera/R/completion.R b/hera/R/completion.R index f4a752a..a318ec2 100644 --- a/hera/R/completion.R +++ b/hera/R/completion.R @@ -8,7 +8,14 @@ utils___guessTokenFromLine <- triple_colon("utils", ".guessTokenFromLine") utils___completeToken <- triple_colon("utils", ".completeToken") utils___retrieveCompletions <- triple_colon("utils", ".retrieveCompletions") -# This is mostly inspired from IRkernel::completions() +#' Code completion +#' +#' @param code R code to complete +#' @param cursor_pos position of the cursor +#' +#' @return a list that contains potential completions as the first item +#' +#' @export complete <- function(code, cursor_pos = nchar(code)) { # Find which line we're on and position within that line lines <- strsplit(code, '\n', fixed = TRUE)[[1]] @@ -37,6 +44,8 @@ complete <- function(code, cursor_pos = nchar(code)) { start_position <- chars_before_line + info$start comps <- utils___retrieveCompletions() - # TODO: use jsonlite::toJSON() here - list(comps, c(start_position, start_position + nchar(info$token))) + list( + comps, + c(start_position, start_position + nchar(info$token)) + ) } diff --git a/hera/man/complete.Rd b/hera/man/complete.Rd new file mode 100644 index 0000000..2054f06 --- /dev/null +++ b/hera/man/complete.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/completion.R +\name{complete} +\alias{complete} +\title{Code completion} +\usage{ +complete(code, cursor_pos = nchar(code)) +} +\arguments{ +\item{code}{R code to complete} + +\item{cursor_pos}{position of the cursor} +} +\value{ +a list that contains potential completions as the first item +} +\description{ +Code completion +} From e98be917f94c68a367e1aeac64f68b48409e3e94 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 13:37:54 +0100 Subject: [PATCH 41/65] bring back completion tests --- test/test_xr_kernel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_xr_kernel.py b/test/test_xr_kernel.py index 87446e2..9d3731d 100644 --- a/test/test_xr_kernel.py +++ b/test/test_xr_kernel.py @@ -18,9 +18,9 @@ class KernelTests(jupyter_kernel_test.KernelTests): code_hello_world = "cat('hello, world')" code_stderr = "message('error')" - #completion_samples = [ - # {"text": "rnorm(", "matches": {"n=", "mean=", "sd="}} - #] + completion_samples = [ + {"text": "rnorm(", "matches": {"n=", "mean=", "sd="}} + ] code_execute_result = [{"code": "6*7", "result": ["[1] 42"]}] #code_display_data = [ # {"code": "plot(0)", "mime": "image/png"}, From f7ea1ae49b67998716f25aee31c161b6a031febb Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 13:41:40 +0100 Subject: [PATCH 42/65] export and document clear_output() --- hera/NAMESPACE | 1 + hera/R/routines.R | 7 +++++++ hera/man/clear_output.Rd | 17 +++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 hera/man/clear_output.Rd diff --git a/hera/NAMESPACE b/hera/NAMESPACE index 9f3753a..812c292 100644 --- a/hera/NAMESPACE +++ b/hera/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(clear_output) export(complete) importFrom(R6,R6Class) importFrom(grDevices,pdf) diff --git a/hera/R/routines.R b/hera/R/routines.R index dc336c1..8426c54 100644 --- a/hera/R/routines.R +++ b/hera/R/routines.R @@ -14,6 +14,13 @@ kernel_info_request <- function() { hera_dot_call("xeusr_kernel_info_request") } + +#' Clear output +#' +#' @param wait Should this wait +#' +#' @return NULL invisibly +#' @export clear_output <- function(wait = FALSE) { invisible(hera_dot_call("xeusr_clear_output", isTRUE(wait))) } diff --git a/hera/man/clear_output.Rd b/hera/man/clear_output.Rd new file mode 100644 index 0000000..5df0b34 --- /dev/null +++ b/hera/man/clear_output.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/routines.R +\name{clear_output} +\alias{clear_output} +\title{Clear output} +\usage{ +clear_output(wait = FALSE) +} +\arguments{ +\item{wait}{Should this wait} +} +\value{ +NULL invisibly +} +\description{ +Clear output +} From 9500f75d08f79dcd34c1bb2aef45980b291cefe1 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 13:52:17 +0100 Subject: [PATCH 43/65] Fix and export is_xeusr() --- hera/NAMESPACE | 2 ++ hera/R/zzz.R | 24 +++++++++++++++--------- hera/man/is_xeusr.Rd | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 hera/man/is_xeusr.Rd diff --git a/hera/NAMESPACE b/hera/NAMESPACE index 812c292..49c79ea 100644 --- a/hera/NAMESPACE +++ b/hera/NAMESPACE @@ -2,11 +2,13 @@ export(clear_output) export(complete) +export(is_xeusr) importFrom(R6,R6Class) importFrom(grDevices,pdf) importFrom(grDevices,png) importFrom(jsonlite,toJSON) importFrom(jsonlite,unbox) +importFrom(rlang,caller_env) importFrom(utils,capture.output) importFrom(utils,head) importFrom(utils,tail) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 456beed..db815cc 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -2,6 +2,7 @@ #' @importFrom jsonlite toJSON unbox #' @importFrom utils head tail capture.output #' @importFrom R6 R6Class +#' @importFrom rlang caller_env NULL print_vignette <- function(x, ...) { @@ -75,20 +76,25 @@ hera_new <- function(class, xp) { get(class, envir = NAMESPACE)$new(xp) } -is_xeus <- function() { +#' Is this a running xeusr jupyter kernel +#' +#' @return TRUE if the current session is running in a xeusr kernel +#' @export +is_xeusr <- function() { embedding <- getLoadedDLLs()[["(embedding)"]] - !is.null(embedding) && "xeusr_kernel_info_request" %in% getDLLRegisteredRoutines()$.Call + !is.null(embedding) && "xeusr_kernel_info_request" %in% getDLLRegisteredRoutines(embedding)$.Call } -hera_dot_call <- function(fn, ...) { - # TODO: check that we are indeed withing xeus-r - # and have some sort of plan B +hera_dot_call <- function(fn, ..., error_call = caller_env()) { + call <- rlang::call2(".Call", fn, ..., PACKAGE = "(embedding)") - # if (!is_xeusr()) { - # ... - # } + if (!is_xeusr()) { + cli::cli_abort(c( + "{.fn {fn}} must be called inside a xeusr kernel.", + i = "Full call: {.code {call}}" + ), call = error_call) + } - call <- rlang::call2(".Call", fn, ..., PACKAGE = "(embedding)") eval.parent(call) } diff --git a/hera/man/is_xeusr.Rd b/hera/man/is_xeusr.Rd new file mode 100644 index 0000000..64e7cd1 --- /dev/null +++ b/hera/man/is_xeusr.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/zzz.R +\name{is_xeusr} +\alias{is_xeusr} +\title{Is this a running xeusr jupyter kernel} +\usage{ +is_xeusr() +} +\value{ +TRUE if the current session is running in a xeusr kernel +} +\description{ +Is this a running xeusr jupyter kernel +} From 9129136f8d1a7f9e2a00b6d4f51f427fc7c635aa Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 14:01:04 +0100 Subject: [PATCH 44/65] Fix and test is_xeusr() --- hera/R/zzz.R | 15 +++++++-------- test/test_xr_kernel.py | 5 ++++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index db815cc..2f40f1c 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -82,19 +82,18 @@ hera_new <- function(class, xp) { #' @export is_xeusr <- function() { embedding <- getLoadedDLLs()[["(embedding)"]] - !is.null(embedding) && "xeusr_kernel_info_request" %in% getDLLRegisteredRoutines(embedding)$.Call + !is.null(embedding) && "xeusr_kernel_info_request" %in% names(getDLLRegisteredRoutines(embedding)$.Call) } hera_dot_call <- function(fn, ..., error_call = caller_env()) { call <- rlang::call2(".Call", fn, ..., PACKAGE = "(embedding)") - if (!is_xeusr()) { - cli::cli_abort(c( - "{.fn {fn}} must be called inside a xeusr kernel.", - i = "Full call: {.code {call}}" - ), call = error_call) - } - + # if (!is_xeusr()) { + # cli::cli_abort(c( + # "{.fn {fn}} must be called inside a xeusr kernel.", + # i = "Full call: {.code {call}}" + # ), call = error_call) + # } eval.parent(call) } diff --git a/test/test_xr_kernel.py b/test/test_xr_kernel.py index 9d3731d..df3eb2a 100644 --- a/test/test_xr_kernel.py +++ b/test/test_xr_kernel.py @@ -21,7 +21,10 @@ class KernelTests(jupyter_kernel_test.KernelTests): completion_samples = [ {"text": "rnorm(", "matches": {"n=", "mean=", "sd="}} ] - code_execute_result = [{"code": "6*7", "result": ["[1] 42"]}] + code_execute_result = [ + {"code": "6*7" , "result": ["[1] 42"]}, + {"code": "is_xeusr()", "result": ["[1] TRUE"]} + ] #code_display_data = [ # {"code": "plot(0)", "mime": "image/png"}, # {"code": "ggplot2::ggplot(iris, ggplot2::aes(Sepal.Length, Sepal.Width)) + ggplot2::geom_point()", "mime": "image/png"}, From 26cf448a53a873ae702d353533053d3841b202cc Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 14:01:47 +0100 Subject: [PATCH 45/65] bring back code_clear_output test --- test/test_xr_kernel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_xr_kernel.py b/test/test_xr_kernel.py index df3eb2a..45d71ab 100644 --- a/test/test_xr_kernel.py +++ b/test/test_xr_kernel.py @@ -32,7 +32,7 @@ class KernelTests(jupyter_kernel_test.KernelTests): #] # code_page_something = "?cat" - # code_clear_output = "clear_output()" + code_clear_output = "clear_output()" code_generate_error = "stop('ouch')" code_inspect_sample = "print" From 7c746f3a22f2bf52fb7448ad18caef2f183e106a Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 14:06:31 +0100 Subject: [PATCH 46/65] abort hera_dot_call() when !xeusr --- hera/R/zzz.R | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 2f40f1c..bbd1eab 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -88,12 +88,13 @@ is_xeusr <- function() { hera_dot_call <- function(fn, ..., error_call = caller_env()) { call <- rlang::call2(".Call", fn, ..., PACKAGE = "(embedding)") - # if (!is_xeusr()) { - # cli::cli_abort(c( - # "{.fn {fn}} must be called inside a xeusr kernel.", - # i = "Full call: {.code {call}}" - # ), call = error_call) - # } + if (!is_xeusr()) { + cli::cli_abort(c( + "The {.val {fn}} routine must be called inside a xeusr kernel.", + i = "Full internal call to the xeusr routine:", + " " = "{deparse(call)}" + ), call = error_call) + } eval.parent(call) } From 0f47dbc3b841573b66522d0e8a75c277e420bf3b Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Tue, 11 Feb 2025 14:17:07 +0100 Subject: [PATCH 47/65] bring back code_display_data --- hera/NAMESPACE | 3 +++ hera/R/comm.R | 20 ++++++++++---------- hera/R/routines.R | 16 ++++++++++++++-- hera/R/zzz.R | 2 +- hera/man/View.Rd | 16 ++++++++++++++++ hera/man/display_data.Rd | 16 ++++++++++++++++ test/test_xr_kernel.py | 10 +++++----- 7 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 hera/man/View.Rd create mode 100644 hera/man/display_data.Rd diff --git a/hera/NAMESPACE b/hera/NAMESPACE index 49c79ea..3b8d943 100644 --- a/hera/NAMESPACE +++ b/hera/NAMESPACE @@ -1,11 +1,14 @@ # Generated by roxygen2: do not edit by hand +export(View) export(clear_output) export(complete) +export(display_data) export(is_xeusr) importFrom(R6,R6Class) importFrom(grDevices,pdf) importFrom(grDevices,png) +importFrom(jsonlite,fromJSON) importFrom(jsonlite,toJSON) importFrom(jsonlite,unbox) importFrom(rlang,caller_env) diff --git a/hera/R/comm.R b/hera/R/comm.R index 21cdb66..b85a558 100644 --- a/hera/R/comm.R +++ b/hera/R/comm.R @@ -45,22 +45,22 @@ Comm <- R6Class("Comm", }, open = function(metadata = NULL, data = NULL) { - js_metadata <- jsonlite::toJSON(metadata) - js_data <- jsonlite::toJSON(data) + js_metadata <- toJSON(metadata) + js_data <- toJSON(data) invisible(hera_dot_call("Comm__open", private$xp, js_metadata, js_data)) }, close = function(metadata = NULL, data = NULL) { - js_metadata <- jsonlite::toJSON(metadata) - js_data <- jsonlite::toJSON(data) + js_metadata <- toJSON(metadata) + js_data <- toJSON(data) invisible(hera_dot_call("Comm__close", private$xp, js_metadata, js_data)) }, send = function(metadata = NULL, data = NULL) { - js_metadata <- jsonlite::toJSON(metadata) - js_data <- jsonlite::toJSON(data) + js_metadata <- toJSON(metadata) + js_data <- toJSON(data) invisible(hera_dot_call("Comm__send", private$xp, js_metadata, js_data)) }, @@ -116,19 +116,19 @@ Message <- R6Class("Message", active = list( content = function() { - jsonlite::fromJSON(hera_dot_call("Message__get_content", private$xp)) + fromJSON(hera_dot_call("Message__get_content", private$xp)) }, header = function() { - jsonlite::fromJSON(hera_dot_call("Message__get_header", private$xp)) + fromJSON(hera_dot_call("Message__get_header", private$xp)) }, parent_header = function() { - jsonlite::fromJSON(hera_dot_call("Message__get_parent_header", private$xp)) + fromJSON(hera_dot_call("Message__get_parent_header", private$xp)) }, metadata = function() { - jsonlite::fromJSON(hera_dot_call("Message__get_metadata", private$xp)) + fromJSON(hera_dot_call("Message__get_metadata", private$xp)) } ), diff --git a/hera/R/routines.R b/hera/R/routines.R index 8426c54..8b6b695 100644 --- a/hera/R/routines.R +++ b/hera/R/routines.R @@ -2,12 +2,18 @@ publish_stream <- function(name, text) { hera_dot_call("xeusr_publish_stream", name, text) } +#' Display data +#' +#' @param data data to display +#' @param metadata potential metadata +#' +#' @export display_data <- function(data = NULL, metadata = NULL) { - invisible(hera_dot_call("xeusr_display_data", jsonlite::toJSON(data), jsonlite::toJSON(metadata))) + invisible(hera_dot_call("xeusr_display_data", toJSON(data), toJSON(metadata))) } update_display_data <- function(data = NULL, metadata = NULL) { - invisible(hera_dot_call("xeusr_update_display_data", jsonlite::toJSON(data), jsonlite::toJSON(metadata))) + invisible(hera_dot_call("xeusr_update_display_data", toJSON(data), toJSON(metadata))) } kernel_info_request <- function() { @@ -29,6 +35,12 @@ is_complete_request <- function(code) { hera_dot_call("xeusr_is_complete_request", code) } +#' View +#' +#' @param x something to display +#' @param title title of the display +#' +#' @export View <- function(x, title) { if (!missing(title)) IRdisplay::display_text(title) IRdisplay::display(x) diff --git a/hera/R/zzz.R b/hera/R/zzz.R index bbd1eab..3885922 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -1,5 +1,5 @@ #' @importFrom grDevices pdf png -#' @importFrom jsonlite toJSON unbox +#' @importFrom jsonlite toJSON unbox fromJSON #' @importFrom utils head tail capture.output #' @importFrom R6 R6Class #' @importFrom rlang caller_env diff --git a/hera/man/View.Rd b/hera/man/View.Rd new file mode 100644 index 0000000..7875136 --- /dev/null +++ b/hera/man/View.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/routines.R +\name{View} +\alias{View} +\title{View} +\usage{ +View(x, title) +} +\arguments{ +\item{x}{something to display} + +\item{title}{title of the display} +} +\description{ +View +} diff --git a/hera/man/display_data.Rd b/hera/man/display_data.Rd new file mode 100644 index 0000000..e37b9a7 --- /dev/null +++ b/hera/man/display_data.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/routines.R +\name{display_data} +\alias{display_data} +\title{Display data} +\usage{ +display_data(data = NULL, metadata = NULL) +} +\arguments{ +\item{data}{data to display} + +\item{metadata}{potential metadata} +} +\description{ +Display data +} diff --git a/test/test_xr_kernel.py b/test/test_xr_kernel.py index 45d71ab..5a8ea9d 100644 --- a/test/test_xr_kernel.py +++ b/test/test_xr_kernel.py @@ -25,11 +25,11 @@ class KernelTests(jupyter_kernel_test.KernelTests): {"code": "6*7" , "result": ["[1] 42"]}, {"code": "is_xeusr()", "result": ["[1] TRUE"]} ] - #code_display_data = [ - # {"code": "plot(0)", "mime": "image/png"}, - # {"code": "ggplot2::ggplot(iris, ggplot2::aes(Sepal.Length, Sepal.Width)) + ggplot2::geom_point()", "mime": "image/png"}, - # {"code": "View(head(iris))", "mime": "text/html"} - #] + code_display_data = [ + {"code": "plot(0)", "mime": "image/png"}, + {"code": "ggplot2::ggplot(iris, ggplot2::aes(Sepal.Length, Sepal.Width)) + ggplot2::geom_point()", "mime": "image/png"}, + {"code": "View(head(iris))", "mime": "text/html"} + ] # code_page_something = "?cat" code_clear_output = "clear_output()" From 378d54c7042aa9ddcbb28aa05af7db25cc06fe8a Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:33:12 +0100 Subject: [PATCH 48/65] mkdir temp_r_lib --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7aea4c..086372c 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Then you can compile the sources (replace `$CONDA_PREFIX` with a custom installa prefix if need be) ```bash -mkdir build && cd build +mkdir build && cd build && mkdir temp_r_lib cmake .. -D CMAKE_PREFIX_PATH=$CONDA_PREFIX -D CMAKE_INSTALL_PREFIX=$CONDA_PREFIX -D CMAKE_INSTALL_LIBDIR=lib make && make install ``` From b273bdcc5a7db33cd54dc95a8b407a0a317b5617 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:33:54 +0100 Subject: [PATCH 49/65] ignore tgz --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6e1a6aa..134f237 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,4 @@ Untitled*.ipynb .Rhistory hera.Rcheck hera_*.tar.gz - +hera_*.tgz From 022c8c9515c551bb77d19aa00c4283043544cce9 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:34:18 +0100 Subject: [PATCH 50/65] install hera --- CMakeLists.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06c1e92..5b0c1ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,12 +71,11 @@ message(STATUS "R_VERSION_MINOR = ${R_VERSION_MINOR}") # Install R package hera # ====================== -if(EMSCRIPTEN) -# -else() - execute_process(COMMAND ${R_COMMAND} CMD INSTALL ../hera) -endif() +message(STATUS "Installing R 📦 hera to temporary library") +execute_process(COMMAND ${R_COMMAND} CMD INSTALL --preclean --no-staged-install --library=temp_r_lib --build ../hera) +install(DIRECTORY temp_r_lib/ DESTINATION ${R_HOME}/library) +execute_process(COMMAND ${R_SCRIPT_COMMAND} -e "writeLines(paste('Installed 📦 hera version', as.character(packageVersion('hera'))))") # Configuration # ============= From 1e42249a6bd90838515884b6ee38b1b95282b107 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:44:49 +0100 Subject: [PATCH 51/65] making temp_r_lib on actions --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 115854a..e257d3c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -90,6 +90,7 @@ jobs: call C:\Users\runneradmin\micromamba-root\condabin\micromamba.bat activate xeus-r mkdir -p build cd build + mkdir temp_r_lib cmake .. ^ -G "NMake Makefiles" ^ -DCMAKE_BUILD_TYPE=Release ^ From d9999a93749e448eecee1b198f0241ee6ebdc19b Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:47:39 +0100 Subject: [PATCH 52/65] here too --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e257d3c..78d3faa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,6 +33,7 @@ jobs: run: | mkdir -p build cd build + mkdir temp_r_lib cmake .. \ -DCMAKE_PREFIX_PATH=$CONDA_PREFIX \ -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \ From 96188a80c33695f00333c8d6a79002594519ee80 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:49:55 +0100 Subject: [PATCH 53/65] trying a different way --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b0c1ec..301dcec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,8 +73,7 @@ message(STATUS "R_VERSION_MINOR = ${R_VERSION_MINOR}") # ====================== message(STATUS "Installing R 📦 hera to temporary library") -execute_process(COMMAND ${R_COMMAND} CMD INSTALL --preclean --no-staged-install --library=temp_r_lib --build ../hera) -install(DIRECTORY temp_r_lib/ DESTINATION ${R_HOME}/library) +execute_process(COMMAND ${R_COMMAND} CMD INSTALL --preclean --no-staged-install --build ../hera) execute_process(COMMAND ${R_SCRIPT_COMMAND} -e "writeLines(paste('Installed 📦 hera version', as.character(packageVersion('hera'))))") # Configuration From fb715cdc6e2aeb8df73a5a321d3d4f2be0ae9f00 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:50:43 +0100 Subject: [PATCH 54/65] not needed --- .github/workflows/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 78d3faa..115854a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,7 +33,6 @@ jobs: run: | mkdir -p build cd build - mkdir temp_r_lib cmake .. \ -DCMAKE_PREFIX_PATH=$CONDA_PREFIX \ -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \ @@ -91,7 +90,6 @@ jobs: call C:\Users\runneradmin\micromamba-root\condabin\micromamba.bat activate xeus-r mkdir -p build cd build - mkdir temp_r_lib cmake .. ^ -G "NMake Makefiles" ^ -DCMAKE_BUILD_TYPE=Release ^ From f534fd9c1191fc94fae7985a8092e7e74317388d Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 10:52:46 +0100 Subject: [PATCH 55/65] improve msg --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 301dcec..f9068f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ message(STATUS "R_VERSION_MINOR = ${R_VERSION_MINOR}") # Install R package hera # ====================== -message(STATUS "Installing R 📦 hera to temporary library") +message(STATUS "Installing R 📦 hera to ${R_HOME}/library") execute_process(COMMAND ${R_COMMAND} CMD INSTALL --preclean --no-staged-install --build ../hera) execute_process(COMMAND ${R_SCRIPT_COMMAND} -e "writeLines(paste('Installed 📦 hera version', as.character(packageVersion('hera'))))") From 1877bc10b6f8e5051ed5476dd2a85f6cb27d7511 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 13:51:37 +0100 Subject: [PATCH 56/65] rm resources mounting --- .github/workflows/deploy-github-page.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-github-page.yml b/.github/workflows/deploy-github-page.yml index 18f06d8..37beac4 100644 --- a/.github/workflows/deploy-github-page.yml +++ b/.github/workflows/deploy-github-page.yml @@ -70,7 +70,6 @@ jobs: python -m pip install jupyterlite-xeus jupyter lite build \ --XeusAddon.prefix=${{ env.PREFIX }} \ - --XeusAddon.mounts=${{ env.PREFIX }}/share/jupyter/kernels/xr/resources:/share/jupyter/kernels/xr/resources \ --contents README.md \ --contents notebooks/xeus-r.ipynb \ --output-dir dist From 827568b0f0ccd687b8acfd2dcff2eaec46074dc2 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 13:59:16 +0100 Subject: [PATCH 57/65] ignore .Rbuildignore --- hera/.Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/hera/.Rbuildignore b/hera/.Rbuildignore index 2eb964e..898a00a 100644 --- a/hera/.Rbuildignore +++ b/hera/.Rbuildignore @@ -1,3 +1,4 @@ ^LICENSE\.md$ ^.*\.Rproj$ ^\.Rproj\.user$ +^\.github$ From 8b15332dc177618bbf7a81db1d6c3c394c2155b9 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:03:44 +0100 Subject: [PATCH 58/65] usethis::use_github_action() + tweaks --- .github/workflows/R-CMD-check.yaml | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/R-CMD-check.yaml diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..0987aa1 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,55 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + +name: R-CMD-check.yaml + +permissions: read-all + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macos-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v4 + with: + path: hera + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + working-directory: hera + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' + working-directory: hera From 5dccff961effd43102fda399a27c04eb1b6bdb7d Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:08:36 +0100 Subject: [PATCH 59/65] trying only path on actions/checkout step --- .github/workflows/R-CMD-check.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 0987aa1..478a19b 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -46,10 +46,8 @@ jobs: with: extra-packages: any::rcmdcheck needs: check - working-directory: hera - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' - working-directory: hera From f67c5daac4d6e5135a6f36acf8867f013ff600e9 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:11:16 +0100 Subject: [PATCH 60/65] r-lib/actions/setup-r-dependencies + working-directory instead --- .github/workflows/R-CMD-check.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 478a19b..350720a 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -31,9 +31,7 @@ jobs: steps: - uses: actions/checkout@v4 - with: - path: hera - + - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 @@ -44,6 +42,7 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: + working-directory: hera extra-packages: any::rcmdcheck needs: check From 0e80488e4af795beff5e8a8db01052d1f543ad04 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:13:25 +0100 Subject: [PATCH 61/65] also r-lib/actions/check-r-package + working-directory --- .github/workflows/R-CMD-check.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 350720a..b3e1143 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -48,5 +48,6 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: + working-directory: hera upload-snapshots: true build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' From 4564f01585ff547b1ab112db3529624ef3c7a8fb Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:35:27 +0100 Subject: [PATCH 62/65] adding R CMD check badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 086372c..974be35 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Documentation Status](http://readthedocs.org/projects/xeus-r/badge/?version=latest)](https://xeus-r.readthedocs.io/en/latest/?badge=latest) [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyter-xeus/xeus-r/main?urlpath=/lab/tree/notebooks/xeus-r.ipynb) [![lite-badge](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://jupyter-xeus.github.io/xeus-r/) +[![R-CMD-check](https://github.com/jupyter-xeus/xeus-r/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/jupyter-xeus/xeus-r/actions/workflows/R-CMD-check.yaml) `xeus-r` is a Jupyter kernel for the R programming language. From d9d15fff6b798df850ecef7e878233b2297ad748 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:50:17 +0100 Subject: [PATCH 63/65] more careafully load hera --- hera/R/execute.R | 38 ++++++++++++++++++++------------------ src/xinterpreter.cpp | 17 ++++++++++------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/hera/R/execute.R b/hera/R/execute.R index b9ebe32..cc72a5a 100644 --- a/hera/R/execute.R +++ b/hera/R/execute.R @@ -52,9 +52,9 @@ handle_error <- function(e) { trace_back <- c( cli::col_red("--- Error"), - format(e, backtrace = FALSE), + format(e, backtrace = FALSE), "", - cli::col_red("--- Traceback"), + cli::col_red("--- Traceback"), format(e$trace) ) the$last_error <- structure(list(ename = "ERROR", evalue = "", trace_back), class = "error_reply") @@ -66,9 +66,9 @@ handle_error <- function(e) { evalue <- paste(conditionMessage(e), collapse = "\n") trace_back <- c( cli::col_red("--- Error"), - evalue, + evalue, "", - cli::col_red("--- Traceback (most recent call last)"), + cli::col_red("--- Traceback (most recent call last)"), stack ) the$last_error <- structure(list(ename = "ERROR", evalue = evalue, trace_back), class = "error_reply") @@ -90,7 +90,7 @@ handle_graphics <- function(plot) { attr(plot, ".irkernel_height") <- getOption('repr.plot.height', repr::repr_option_defaults$repr.plot.height) attr(plot, ".irkernel_res") <- getOption('repr.plot.res', repr::repr_option_defaults$repr.plot.res) attr(plot, ".irkernel_ppi") <- attr(plot, ".irkernel_res") / getOption('jupyter.plot_scale', 2) - + if (!plot_builds_upon(the$last_plot, plot)) { send_plot(the$last_plot) } @@ -127,31 +127,33 @@ send_plot <- function(plot) { display_data(data, metadata) } +# currently not exported, because it is only meant to be called +# from xeus-r / interpreter::execute_request_impl execute <- function(code, execution_counter, silent = FALSE) { the$last_error <- NULL - + parsed <- tryCatch( - parse(text = code), + parse(text = code), error = function(e) { msg <- paste(conditionMessage(e), collapse = "\n") the$last_error <- structure(list(ename = "PARSE ERROR", evalue = msg), class = "error_reply") } ) if (!is.null(the$last_error)) return(the$last_error) - + output_handler <- if (silent) { evaluate::new_output_handler() } else { evaluate::new_output_handler( - text = function(txt) publish_stream("stdout", txt), + text = function(txt) publish_stream("stdout", txt), graphics = handle_graphics, - message = handle_message, - warning = handle_warning, - error = handle_error, + message = handle_message, + warning = handle_warning, + error = handle_error, value = handle_value - ) + ) } - + the$last_plot <- NULL the$last_visible <- FALSE @@ -162,7 +164,7 @@ execute <- function(code, execution_counter, silent = FALSE) { code, envir = globalenv(), output_handler = output_handler, - stop_on_error = 1L, + stop_on_error = 1L, filename = filename ) if (!is.null(the$last_error)) return(the$last_error) @@ -181,10 +183,10 @@ execute <- function(code, execution_counter, silent = FALSE) { } else { "text/plain" } - + bundle <- IRdisplay::prepare_mimebundle(obj, mimetypes = mimetypes) - - structure(class = "execution_result", + + structure(class = "execution_result", list(toJSON(bundle$data), toJSON(bundle$metadata)) ) } diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 96312dc..ca53cc2 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -140,14 +140,17 @@ void interpreter::execute_request_impl( void interpreter::configure_impl() { - std::stringstream ss; - - SEXP sym_library = Rf_install("library"); - SEXP str_hera = PROTECT(Rf_mkString("hera")); - SEXP call_library_hera = PROTECT(Rf_lang2(sym_library, str_hera)); - Rf_eval(call_library_hera, R_GlobalEnv); + SEXP sym_library = Rf_install("require"); + SEXP str_hera = PROTECT(Rf_mkString("hera")); + SEXP sym_quietly = Rf_install("quietly"); + SEXP call_library_hera = PROTECT(xeus_r::r::r_call(sym_library, str_hera, /* quietly = */ Rf_ScalarLogical(TRUE))); + SET_TAG(CDDR(call_library_hera), sym_quietly); + SEXP out = PROTECT(Rf_eval(call_library_hera, R_GlobalEnv)); + if (LOGICAL_ELT(out, 0) == FALSE) { + // TODO: suicide the kernel because hera is not installed + } - UNPROTECT(2); + UNPROTECT(3); } nl::json interpreter::is_complete_request_impl(const std::string& code_) From 4bad1c3fd47c9c9eed0b00d7df5bd9cc3d94c297 Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:54:16 +0100 Subject: [PATCH 64/65] export cell_options() --- hera/NAMESPACE | 1 + hera/R/cell_options.R | 5 +++++ hera/man/cell_options.Rd | 16 ++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 hera/man/cell_options.Rd diff --git a/hera/NAMESPACE b/hera/NAMESPACE index 3b8d943..bb0c760 100644 --- a/hera/NAMESPACE +++ b/hera/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export(View) +export(cell_options) export(clear_output) export(complete) export(display_data) diff --git a/hera/R/cell_options.R b/hera/R/cell_options.R index 7977f1f..f745a82 100644 --- a/hera/R/cell_options.R +++ b/hera/R/cell_options.R @@ -1,3 +1,8 @@ +#' Options for current jupyter cell +#' +#' @inheritParams rlang::local_options +#' +#' @export cell_options <- function(...) { rlang::local_options(..., .frame = the$frame_cell_execute) } diff --git a/hera/man/cell_options.Rd b/hera/man/cell_options.Rd new file mode 100644 index 0000000..889e50e --- /dev/null +++ b/hera/man/cell_options.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cell_options.R +\name{cell_options} +\alias{cell_options} +\title{Options for current cell} +\usage{ +cell_options(...) +} +\arguments{ +\item{...}{For \code{local_options()} and \code{push_options()}, named +values defining new option values. For \code{peek_options()}, strings +or character vectors of option names.} +} +\description{ +Options for current cell +} From 038bb2f8ff6be925bfb034060b89e990e687e78f Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Wed, 12 Feb 2025 14:57:01 +0100 Subject: [PATCH 65/65] move the$ things around --- hera/R/00_init.R | 2 -- hera/R/execute.R | 5 ----- hera/R/zzz.R | 6 ++++++ hera/man/cell_options.Rd | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) delete mode 100644 hera/R/00_init.R diff --git a/hera/R/00_init.R b/hera/R/00_init.R deleted file mode 100644 index b240720..0000000 --- a/hera/R/00_init.R +++ /dev/null @@ -1,2 +0,0 @@ -NAMESPACE <- environment() -the <- NULL diff --git a/hera/R/execute.R b/hera/R/execute.R index cc72a5a..1861d36 100644 --- a/hera/R/execute.R +++ b/hera/R/execute.R @@ -1,8 +1,3 @@ -# these might need to be become the$ -the$last_plot <- NULL -the$last_visible <- TRUE -the$last_error <- NULL - handle_message <- function(msg) { publish_stream("stderr", conditionMessage(msg)) } diff --git a/hera/R/zzz.R b/hera/R/zzz.R index 3885922..b940939 100644 --- a/hera/R/zzz.R +++ b/hera/R/zzz.R @@ -34,11 +34,16 @@ print_vignette <- function(x, ...) { invisible(x) } +the <- NULL + .onLoad <- function(libname, pkgname) { # - verify this is running within xeus-r # - handshake the <<- new.env() the$frame_cell_execute <- NULL + the$last_plot <- NULL + the$last_visible <- TRUE + the$last_error <- NULL ns_utils <- asNamespace("utils") get("unlockBinding", envir = baseenv())("print.vignette", ns_utils) @@ -68,6 +73,7 @@ init_options <- function() { } } +NAMESPACE <- environment() hera_call <- function(fn, ...) { get(fn, envir = NAMESPACE)(...) } diff --git a/hera/man/cell_options.Rd b/hera/man/cell_options.Rd index 889e50e..4dda0cc 100644 --- a/hera/man/cell_options.Rd +++ b/hera/man/cell_options.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/cell_options.R \name{cell_options} \alias{cell_options} -\title{Options for current cell} +\title{Options for current jupyter cell} \usage{ cell_options(...) } @@ -12,5 +12,5 @@ values defining new option values. For \code{peek_options()}, strings or character vectors of option names.} } \description{ -Options for current cell +Options for current jupyter cell }