@damurka/jovian 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +175 -0
- package/docs/api/README.md +55 -0
- package/docs/api/session.md +143 -0
- package/docs/api/types.md +120 -0
- package/docs/architecture/overview.md +218 -0
- package/docs/cpp-usage.md +60 -0
- package/docs/development.md +105 -0
- package/docs/getting-started.md +127 -0
- package/docs/guides/comms.md +70 -0
- package/docs/guides/environments.md +80 -0
- package/docs/guides/history.md +44 -0
- package/docs/guides/interactive-input.md +48 -0
- package/docs/guides/interrupting.md +45 -0
- package/docs/guides/playground.md +33 -0
- package/docs/guides/sessions-lifecycle.md +70 -0
- package/docs/kernels.md +117 -0
- package/docs/protocol.md +135 -0
- package/docs/releasing.md +73 -0
- package/docs/troubleshooting.md +110 -0
- package/lib/execution/execution-queue.d.ts +20 -0
- package/lib/execution/execution-queue.js +256 -0
- package/lib/handlers/display-handler.d.ts +7 -0
- package/lib/handlers/display-handler.js +10 -0
- package/lib/handlers/error-handler.d.ts +7 -0
- package/lib/handlers/error-handler.js +8 -0
- package/lib/handlers/result-handler.d.ts +7 -0
- package/lib/handlers/result-handler.js +10 -0
- package/lib/handlers/stream-handler.d.ts +7 -0
- package/lib/handlers/stream-handler.js +8 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +5 -0
- package/lib/messaging/message-parser.d.ts +6 -0
- package/lib/messaging/message-parser.js +33 -0
- package/lib/messaging/message-router.d.ts +14 -0
- package/lib/messaging/message-router.js +41 -0
- package/lib/middleware/index.d.ts +5 -0
- package/lib/middleware/index.js +5 -0
- package/lib/middleware/middleware-chain.d.ts +7 -0
- package/lib/middleware/middleware-chain.js +14 -0
- package/lib/middleware/middleware.d.ts +5 -0
- package/lib/middleware/middleware.js +2 -0
- package/lib/middleware/plugins/logging-plugin.d.ts +6 -0
- package/lib/middleware/plugins/logging-plugin.js +9 -0
- package/lib/middleware/plugins/metrics-plugin.d.ts +8 -0
- package/lib/middleware/plugins/metrics-plugin.js +13 -0
- package/lib/session/comm.d.ts +39 -0
- package/lib/session/comm.js +58 -0
- package/lib/session/native-paths.d.ts +48 -0
- package/lib/session/native-paths.js +108 -0
- package/lib/session/session-manager.d.ts +229 -0
- package/lib/session/session-manager.js +842 -0
- package/lib/session/supervisor-client.d.ts +36 -0
- package/lib/session/supervisor-client.js +147 -0
- package/lib/types/engine.d.ts +269 -0
- package/lib/types/engine.js +2 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.js +3 -0
- package/lib/types/messages.d.ts +68 -0
- package/lib/types/messages.js +2 -0
- package/lib/utils/logger.d.ts +12 -0
- package/lib/utils/logger.js +58 -0
- package/lib/utils/network.d.ts +11 -0
- package/lib/utils/network.js +50 -0
- package/package.json +57 -0
- package/packages/hera/DESCRIPTION +29 -0
- package/packages/hera/LICENSE +2 -0
- package/packages/hera/LICENSE.md +21 -0
- package/packages/hera/NAMESPACE +32 -0
- package/packages/hera/NEWS.md +7 -0
- package/packages/hera/R/cell_options.R +13 -0
- package/packages/hera/R/comm.R +228 -0
- package/packages/hera/R/completion.R +54 -0
- package/packages/hera/R/execute.R +199 -0
- package/packages/hera/R/inspect.R +73 -0
- package/packages/hera/R/log.R +14 -0
- package/packages/hera/R/mime_bundle.R +65 -0
- package/packages/hera/R/routines.R +86 -0
- package/packages/hera/R/utils.R +32 -0
- package/packages/hera/R/zzz.R +128 -0
- package/packages/hera/man/Comm.Rd +179 -0
- package/packages/hera/man/CommManager.Rd +215 -0
- package/packages/hera/man/View.Rd +22 -0
- package/packages/hera/man/cell_options.Rd +20 -0
- package/packages/hera/man/clear_output.Rd +23 -0
- package/packages/hera/man/complete.Rd +23 -0
- package/packages/hera/man/display_data.Rd +22 -0
- package/packages/hera/man/is_elara.Rd +18 -0
- package/packages/hera/man/mime_bundle.Rd +25 -0
- package/packages/hera/man/mime_types.Rd +22 -0
- package/packages/hera/man/reexports.Rd +16 -0
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@damurka/jovian",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "David Kariuki",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/damurka/jovian.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/damurka/jovian#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/damurka/jovian/issues"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public",
|
|
16
|
+
"provenance": true
|
|
17
|
+
},
|
|
18
|
+
"description": "Supervised R and Python Jupyter kernels for Node.js and Electron, backed by native C++ processes",
|
|
19
|
+
"keywords": [
|
|
20
|
+
"r",
|
|
21
|
+
"python",
|
|
22
|
+
"jupyter",
|
|
23
|
+
"kernel",
|
|
24
|
+
"repl",
|
|
25
|
+
"data-science",
|
|
26
|
+
"electron"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"main": "./lib/index.js",
|
|
30
|
+
"types": "./lib/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./lib/index.d.ts",
|
|
34
|
+
"import": "./lib/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./types": {
|
|
37
|
+
"types": "./lib/types/index.d.ts",
|
|
38
|
+
"import": "./lib/types/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22.4.0"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"lib",
|
|
47
|
+
"packages/hera",
|
|
48
|
+
"docs",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"optionalDependencies": {
|
|
53
|
+
"@damurka/jovian-win32-x64": "0.1.0",
|
|
54
|
+
"@damurka/jovian-linux-x64": "0.1.0",
|
|
55
|
+
"@damurka/jovian-darwin-arm64": "0.1.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Package: hera
|
|
2
|
+
Title: Companion to the 'Elara' 'jupyter' Kernel
|
|
3
|
+
Version: 0.6.0.9001
|
|
4
|
+
Authors@R: c(
|
|
5
|
+
person("Romain", "François", email = "romain@tada.science", role = c("aut", "cre")),
|
|
6
|
+
person("QuantStack", role = c("cph"))
|
|
7
|
+
)
|
|
8
|
+
Description: Set of R functions to be coupled with the 'Elara' 'jupyter' kernel
|
|
9
|
+
in order to drive execution of code in notebook input cells, how R objects
|
|
10
|
+
are to be displayed in output cells, and handle two way communication
|
|
11
|
+
with the front end through comms.
|
|
12
|
+
License: MIT + file LICENSE
|
|
13
|
+
Encoding: UTF-8
|
|
14
|
+
Roxygen: list(markdown = TRUE)
|
|
15
|
+
Imports:
|
|
16
|
+
cli,
|
|
17
|
+
evaluate,
|
|
18
|
+
glue,
|
|
19
|
+
IRdisplay,
|
|
20
|
+
jsonlite,
|
|
21
|
+
R6,
|
|
22
|
+
repr,
|
|
23
|
+
rlang,
|
|
24
|
+
tools,
|
|
25
|
+
utils
|
|
26
|
+
URL: https://github.com/damurka/jovian
|
|
27
|
+
BugReports: https://github.com/damurka/jovian/issues
|
|
28
|
+
Config/roxygen2/version: 8.0.0
|
|
29
|
+
Config/jovian/release: 0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Quantstack
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Generated by roxygen2: do not edit by hand
|
|
2
|
+
|
|
3
|
+
S3method(mime_bundle,default)
|
|
4
|
+
S3method(mime_types,default)
|
|
5
|
+
S3method(mime_types,help_files_with_topic)
|
|
6
|
+
S3method(mime_types,htmlwidget)
|
|
7
|
+
S3method(mime_types,shiny.tag)
|
|
8
|
+
S3method(mime_types,shiny.tag.list)
|
|
9
|
+
export(Comm)
|
|
10
|
+
export(CommManager)
|
|
11
|
+
export(View)
|
|
12
|
+
export(cell_options)
|
|
13
|
+
export(clear_output)
|
|
14
|
+
export(complete)
|
|
15
|
+
export(display)
|
|
16
|
+
export(display_data)
|
|
17
|
+
export(is_elara)
|
|
18
|
+
export(mime_bundle)
|
|
19
|
+
export(mime_types)
|
|
20
|
+
import(glue)
|
|
21
|
+
importFrom(IRdisplay,display)
|
|
22
|
+
importFrom(IRdisplay,prepare_mimebundle)
|
|
23
|
+
importFrom(R6,R6Class)
|
|
24
|
+
importFrom(grDevices,pdf)
|
|
25
|
+
importFrom(grDevices,png)
|
|
26
|
+
importFrom(jsonlite,fromJSON)
|
|
27
|
+
importFrom(jsonlite,toJSON)
|
|
28
|
+
importFrom(jsonlite,unbox)
|
|
29
|
+
importFrom(rlang,caller_env)
|
|
30
|
+
importFrom(utils,capture.output)
|
|
31
|
+
importFrom(utils,head)
|
|
32
|
+
importFrom(utils,tail)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#' Options for current jupyter cell
|
|
2
|
+
#'
|
|
3
|
+
#' @param ... options to set locally to the notebook cell. Forwarded to [rlang::local_options()].
|
|
4
|
+
#'
|
|
5
|
+
#' @examples
|
|
6
|
+
#' \dontrun{
|
|
7
|
+
#' cell_options(repr.plot.bg = "gray")
|
|
8
|
+
#' }
|
|
9
|
+
#'
|
|
10
|
+
#' @export
|
|
11
|
+
cell_options <- function(...) {
|
|
12
|
+
rlang::local_options(..., .frame = the$frame_cell_execute)
|
|
13
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
.CommManager__register_target_callback <- function(comm, request) {
|
|
2
|
+
callback <- CommManager$target_callback(request$content$target_name)
|
|
3
|
+
callback(comm, request)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
#' Comm Manager class
|
|
7
|
+
#'
|
|
8
|
+
#' @rdname CommManager
|
|
9
|
+
CommManagerClass <- R6::R6Class("CommManagerClass",
|
|
10
|
+
public = list(
|
|
11
|
+
|
|
12
|
+
#' @param ... currently unused
|
|
13
|
+
#' @param error_call see [rlang::args_error_context()]
|
|
14
|
+
initialize = function(..., error_call = caller_env()) {
|
|
15
|
+
rlang::check_dots_empty(call = error_call)
|
|
16
|
+
|
|
17
|
+
private$env_targets <- new.env()
|
|
18
|
+
private$env_comms <- new.env()
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
#' @param target_name name of the comm target, e.g. "jupyter.widgets"
|
|
22
|
+
#' @param callback callback function taking two arguments 'comm' and 'message'.
|
|
23
|
+
register_comm_target = function(target_name, callback = function(comm, message){}) {
|
|
24
|
+
private$env_targets[[target_name]] <- callback
|
|
25
|
+
invisible(hera_dot_call("CommManager__register_target", target_name))
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
#' @param target_name name of the comm target
|
|
29
|
+
unregister_comm_target = function(target_name) {
|
|
30
|
+
rm(list = target_name, private$env_targets)
|
|
31
|
+
invisible(hera_dot_call("CommManager__unregister_target", target_name))
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
#' @param target_name name of the target
|
|
35
|
+
#' @param description description of the comm
|
|
36
|
+
new_comm = function(target_name, description = "") {
|
|
37
|
+
hera_dot_call("CommManager__new_comm", target_name, description)
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
#' @return the list of currently open comms
|
|
41
|
+
comms = function() {
|
|
42
|
+
as.list(private$env_comms)
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
#' @param target_name name of the comm target
|
|
46
|
+
#'
|
|
47
|
+
#' @return the callback for that target name
|
|
48
|
+
target_callback = function(target_name) {
|
|
49
|
+
private$env_targets[[target_name]]
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
#' @param comm [Comm] instance to preserve
|
|
53
|
+
preserve = function(comm) {
|
|
54
|
+
assign(comm$id, comm, envir = private$env_comms)
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
#' @param comm [Comm] instance to release
|
|
58
|
+
release = function(comm) {
|
|
59
|
+
rm(list = comm$id, envir = private$env_comms)
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
#' @param target_name name of the target to get info about all comm opens. If NULL, info for comms for all targets are returned.
|
|
63
|
+
get_comm_info = function(target_name = NULL) {
|
|
64
|
+
hera_dot_call("CommManager__get_comm_info", target_name)
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
|
|
68
|
+
private = list(
|
|
69
|
+
env_targets = NULL,
|
|
70
|
+
env_comms = NULL
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# set later in zzz.R
|
|
75
|
+
|
|
76
|
+
#' Comm manager
|
|
77
|
+
#'
|
|
78
|
+
#' Instance of the [CommManagerClass] class.
|
|
79
|
+
#'
|
|
80
|
+
#' @export
|
|
81
|
+
CommManager <- NULL
|
|
82
|
+
|
|
83
|
+
#' Comm class
|
|
84
|
+
#'
|
|
85
|
+
#' @export
|
|
86
|
+
Comm <- R6::R6Class("Comm",
|
|
87
|
+
public = list(
|
|
88
|
+
|
|
89
|
+
#' @param xp external pointer to an instance of the C++ class 'adrastea::Comm'
|
|
90
|
+
#' @param description description of this comm
|
|
91
|
+
initialize = function(xp, description = "") {
|
|
92
|
+
private$xp <- xp
|
|
93
|
+
private$description <- description
|
|
94
|
+
CommManager$preserve(self)
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
#' @param data data
|
|
98
|
+
#' @param metadata metadata
|
|
99
|
+
#' @param buffers list of raw vectors to send as binary buffers
|
|
100
|
+
open = function(data = NULL, metadata = NULL, buffers = list()) {
|
|
101
|
+
stopifnot(is.list(buffers), all(vapply(buffers, is.raw, logical(1))))
|
|
102
|
+
js_metadata <- enc2utf8(jsonlite::toJSON(metadata, auto_unbox = TRUE, null = if (is.null(metadata)) "list" else "null"))
|
|
103
|
+
js_data <- enc2utf8(jsonlite::toJSON(data, auto_unbox = TRUE, null = "null"))
|
|
104
|
+
|
|
105
|
+
invisible(hera_dot_call("Comm__open", private$xp, js_metadata, js_data, buffers))
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
#' @param data data
|
|
109
|
+
#' @param metadata metadata
|
|
110
|
+
#' @param buffers list of raw vectors to send as binary buffers
|
|
111
|
+
close = function(data = NULL, metadata = NULL, buffers = list()) {
|
|
112
|
+
stopifnot(is.list(buffers), all(vapply(buffers, is.raw, logical(1))))
|
|
113
|
+
js_metadata <- enc2utf8(jsonlite::toJSON(metadata, auto_unbox = TRUE, null = if (is.null(metadata)) "list" else "null"))
|
|
114
|
+
js_data <- enc2utf8(jsonlite::toJSON(data, auto_unbox = TRUE, null = "null"))
|
|
115
|
+
|
|
116
|
+
invisible(hera_dot_call("Comm__close", private$xp, js_metadata, js_data, buffers))
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
#' @param data data
|
|
120
|
+
#' @param metadata metadata
|
|
121
|
+
#' @param buffers list of raw vectors to send as binary buffers
|
|
122
|
+
send = function(data = NULL, metadata = NULL, buffers = list()) {
|
|
123
|
+
stopifnot(is.list(buffers), all(vapply(buffers, is.raw, logical(1))))
|
|
124
|
+
js_metadata <- enc2utf8(jsonlite::toJSON(metadata, auto_unbox = TRUE, null = if (is.null(metadata)) "list" else "null"))
|
|
125
|
+
js_data <- enc2utf8(jsonlite::toJSON(data, auto_unbox = TRUE, null = "null"))
|
|
126
|
+
|
|
127
|
+
invisible(hera_dot_call("Comm__send", private$xp, js_metadata, js_data, buffers))
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
#' @param handler function to call when the comm is closed
|
|
131
|
+
on_close = function(handler) {
|
|
132
|
+
private$close_handler <- function(request) {
|
|
133
|
+
handler(request)
|
|
134
|
+
self$finalize()
|
|
135
|
+
}
|
|
136
|
+
invisible(hera_dot_call("Comm__on_close", private$xp, private$close_handler))
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
#' @param handler function to call when receiving a message
|
|
140
|
+
on_message = function(handler) {
|
|
141
|
+
private$message_handler <- handler
|
|
142
|
+
invisible(hera_dot_call("Comm__on_message", private$xp, private$message_handler))
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
#' @return information about the comm
|
|
146
|
+
print = function() {
|
|
147
|
+
if (identical(private$description, "")) {
|
|
148
|
+
writeLines(glue("<Comm id={self$id} target_name='{self$target_name}'>"))
|
|
149
|
+
} else {
|
|
150
|
+
writeLines(glue("<Comm id={self$id} target_name='{self$target_name}' description='{private$description}' >"))
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
|
|
155
|
+
active = list(
|
|
156
|
+
|
|
157
|
+
#' @field id
|
|
158
|
+
#' id of the comm. read only.
|
|
159
|
+
id = function() {
|
|
160
|
+
hera_dot_call("Comm__id", private$xp)
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
#' @field target_name
|
|
164
|
+
#' name of the target for this comm
|
|
165
|
+
target_name = function() {
|
|
166
|
+
hera_dot_call("Comm__target_name", private$xp)
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
|
|
170
|
+
private = list(
|
|
171
|
+
xp = NULL,
|
|
172
|
+
description = "",
|
|
173
|
+
close_handler = NULL,
|
|
174
|
+
message_handler = NULL,
|
|
175
|
+
|
|
176
|
+
finalize = function() {
|
|
177
|
+
CommManager$release(self)
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
Message <- R6::R6Class("Message",
|
|
183
|
+
public = list(
|
|
184
|
+
initialize = function(xp) {
|
|
185
|
+
private$xp <- xp
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
print = function() {
|
|
189
|
+
print(cli::rule("$content"))
|
|
190
|
+
str(self$content)
|
|
191
|
+
|
|
192
|
+
print(cli::rule("$header"))
|
|
193
|
+
str(self$header)
|
|
194
|
+
|
|
195
|
+
print(cli::rule("$parent_header"))
|
|
196
|
+
str(self$parent_header)
|
|
197
|
+
|
|
198
|
+
print(cli::rule("$metadata"))
|
|
199
|
+
str(self$metadata)
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
|
|
203
|
+
active = list(
|
|
204
|
+
content = function() {
|
|
205
|
+
jsonlite::fromJSON(hera_dot_call("Message__get_content", private$xp))
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
header = function() {
|
|
209
|
+
jsonlite::fromJSON(hera_dot_call("Message__get_header", private$xp))
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
parent_header = function() {
|
|
213
|
+
jsonlite::fromJSON(hera_dot_call("Message__get_parent_header", private$xp))
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
metadata = function() {
|
|
217
|
+
jsonlite::fromJSON(hera_dot_call("Message__get_metadata", private$xp))
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
buffers = function() {
|
|
221
|
+
hera_dot_call("Message__get_buffers", private$xp)
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
|
|
225
|
+
private = list(
|
|
226
|
+
xp = NULL
|
|
227
|
+
)
|
|
228
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
triple_colon <- function(pkg, fun) {
|
|
2
|
+
eval(rlang::call2(":::", as.symbol(pkg), as.symbol(fun)))
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
utils___assignLineBuffer <- triple_colon("utils", ".assignLinebuffer")
|
|
6
|
+
utils___assignEnd <- triple_colon("utils", ".assignEnd")
|
|
7
|
+
utils___guessTokenFromLine <- triple_colon("utils", ".guessTokenFromLine")
|
|
8
|
+
utils___completeToken <- triple_colon("utils", ".completeToken")
|
|
9
|
+
utils___retrieveCompletions <- triple_colon("utils", ".retrieveCompletions")
|
|
10
|
+
|
|
11
|
+
#' Code completion
|
|
12
|
+
#'
|
|
13
|
+
#' @param code R code to complete
|
|
14
|
+
#' @param cursor_pos position of the cursor
|
|
15
|
+
#'
|
|
16
|
+
#' @examples
|
|
17
|
+
#' complete("rnorm(")
|
|
18
|
+
#'
|
|
19
|
+
#' @return a list that contains potential completions as the first item
|
|
20
|
+
#'
|
|
21
|
+
#' @export
|
|
22
|
+
complete <- function(code, cursor_pos = nchar(code)) {
|
|
23
|
+
# Find which line we're on and position within that line
|
|
24
|
+
lines <- strsplit(code, '\n', fixed = TRUE)[[1]]
|
|
25
|
+
chars_before_line <- 0L
|
|
26
|
+
for (line in lines) {
|
|
27
|
+
new_cursor_pos <- cursor_pos - nchar(line) - 1L # -1 for the newline
|
|
28
|
+
if (new_cursor_pos < 0L) {
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
cursor_pos <- new_cursor_pos
|
|
32
|
+
chars_before_line <- chars_before_line + nchar(line) + 1L
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# guard from errors when completion is invoked in empty cells
|
|
36
|
+
if (is.null(line)) {
|
|
37
|
+
line <- ''
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
utils___assignLineBuffer(line)
|
|
41
|
+
utils___assignEnd(cursor_pos)
|
|
42
|
+
|
|
43
|
+
info <- utils___guessTokenFromLine(update = FALSE)
|
|
44
|
+
utils___guessTokenFromLine()
|
|
45
|
+
utils___completeToken()
|
|
46
|
+
|
|
47
|
+
start_position <- chars_before_line + info$start
|
|
48
|
+
comps <- utils___retrieveCompletions()
|
|
49
|
+
|
|
50
|
+
list(
|
|
51
|
+
comps,
|
|
52
|
+
c(start_position, start_position + nchar(info$token))
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# evaluate captures stdout into a temporary file and only hands it over when a
|
|
2
|
+
# top-level expression finishes (or a message/warning/plot happens), so one
|
|
3
|
+
# long expression -- for (i in 1:1e6) print(i), purrr::walk(x, print) -- shows
|
|
4
|
+
# nothing until it is over. Making evaluate's sink a *split* sink
|
|
5
|
+
# (sink(con, split = TRUE)) tees the same output to R's console too, and the
|
|
6
|
+
# console (Elara's WriteConsoleEx hook) publishes it to the client at once.
|
|
7
|
+
# evaluate only does that through its debug flag, which also echoes every
|
|
8
|
+
# expression's source, so the one internal function that creates the sink is
|
|
9
|
+
# wrapped instead. try()'s output is redirected to the same place (evaluate
|
|
10
|
+
# points it at the sink's file, which the tee does not cover).
|
|
11
|
+
#
|
|
12
|
+
# Returns whether the wrapper is in place; when it is not (a different
|
|
13
|
+
# evaluate), output simply keeps arriving per expression, as before.
|
|
14
|
+
patch_evaluate_sink <- function() {
|
|
15
|
+
ns <- asNamespace("evaluate")
|
|
16
|
+
name <- "local_persistent_sink_connection"
|
|
17
|
+
original <- get0(name, envir = ns, inherits = FALSE)
|
|
18
|
+
if (!is.function(original) || !identical(names(formals(original)), c("debug", "frame"))) {
|
|
19
|
+
return(FALSE)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
patched <- function(debug = FALSE, frame = parent.frame()) {
|
|
23
|
+
tee <- isTRUE(the$tee_stdout)
|
|
24
|
+
reader <- original(debug = debug || tee, frame = frame)
|
|
25
|
+
if (tee) {
|
|
26
|
+
# evaluate saved the previous value and restores it when it finishes.
|
|
27
|
+
options(try.outFile = stdout())
|
|
28
|
+
}
|
|
29
|
+
reader
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
tryCatch({
|
|
33
|
+
unlockBinding(name, ns)
|
|
34
|
+
assign(name, patched, envir = ns)
|
|
35
|
+
lockBinding(name, ns)
|
|
36
|
+
TRUE
|
|
37
|
+
}, error = function(e) FALSE)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
handle_message <- function(msg) {
|
|
41
|
+
publish_stream("stderr", conditionMessage(msg))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
handle_warning <- function(w) {
|
|
45
|
+
call <- conditionCall(w)
|
|
46
|
+
call <- if (is.null(call)) '' else sprintf(' in %s', deparse(call)[[1]])
|
|
47
|
+
msg <- sprintf('Warning message%s:\n%s\n', call, dQuote(conditionMessage(w)))
|
|
48
|
+
|
|
49
|
+
publish_stream("stderr", msg)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
handle_error <- function(e) {
|
|
53
|
+
if (inherits(e, "rlang_error") && isNamespaceLoaded("rlang")) {
|
|
54
|
+
assign("last_error", e, triple_colon("rlang", "the"))
|
|
55
|
+
|
|
56
|
+
the$last_error <- structure(
|
|
57
|
+
list(ename = "ERROR", evalue = "", format(e, backtrace = TRUE)),
|
|
58
|
+
class = "error_reply"
|
|
59
|
+
)
|
|
60
|
+
} else {
|
|
61
|
+
sys_calls <- sys.calls()
|
|
62
|
+
sys_calls <- head(tail(sys_calls, -16), -3)
|
|
63
|
+
stack <- capture.output(traceback(sys_calls, max.lines = 1L))
|
|
64
|
+
|
|
65
|
+
evalue <- paste(conditionMessage(e), collapse = "\n")
|
|
66
|
+
trace_back <- c(
|
|
67
|
+
cli::col_red("--- Error"),
|
|
68
|
+
evalue,
|
|
69
|
+
"",
|
|
70
|
+
cli::col_red("--- Traceback (most recent call last)"),
|
|
71
|
+
stack
|
|
72
|
+
)
|
|
73
|
+
the$last_error <- structure(list(ename = "ERROR", evalue = evalue, trace_back), class = "error_reply")
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
handle_value <- function(obj, visible) {
|
|
78
|
+
set_last_value(obj, visible)
|
|
79
|
+
|
|
80
|
+
if (visible && inherits(obj, "ggplot")) {
|
|
81
|
+
print(obj)
|
|
82
|
+
the$last_visible <- FALSE
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
handle_graphics <- function(plot) {
|
|
88
|
+
attr(plot, ".irkernel_width") <- getOption('repr.plot.width' , repr::repr_option_defaults$repr.plot.width)
|
|
89
|
+
attr(plot, ".irkernel_height") <- getOption('repr.plot.height', repr::repr_option_defaults$repr.plot.height)
|
|
90
|
+
attr(plot, ".irkernel_res") <- getOption('repr.plot.res', repr::repr_option_defaults$repr.plot.res)
|
|
91
|
+
attr(plot, ".irkernel_ppi") <- attr(plot, ".irkernel_res") / getOption('jupyter.plot_scale', 2)
|
|
92
|
+
|
|
93
|
+
if (!plot_builds_upon(the$last_plot, plot)) {
|
|
94
|
+
send_plot(the$last_plot)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
the$last_plot <- plot
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
send_plot <- function(plot) {
|
|
101
|
+
w <- attr(plot, '.irkernel_width')
|
|
102
|
+
h <- attr(plot, '.irkernel_height')
|
|
103
|
+
res <- attr(plot, ".irkernel_res")
|
|
104
|
+
ppi <- attr(plot, ".irkernel_ppi")
|
|
105
|
+
|
|
106
|
+
data <- namedlist()
|
|
107
|
+
metadata <- namedlist()
|
|
108
|
+
|
|
109
|
+
for (mime in getOption('jupyter.plot_mimetypes')) {
|
|
110
|
+
data[[mime]] <- repr::mime2repr[[mime]](plot, width = w, height = h, res = res)
|
|
111
|
+
|
|
112
|
+
if (!identical(mime, 'text/plain')) {
|
|
113
|
+
metadata[[mime]] <- list(
|
|
114
|
+
width = w * ppi,
|
|
115
|
+
height = h * ppi
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Isolating SVGs (putting them in an iframe) avoids strange
|
|
120
|
+
# interactions with CSS on the page.
|
|
121
|
+
if (identical(mime, 'image/svg+xml')) {
|
|
122
|
+
metadata[[mime]]$isolated <- TRUE
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
display_data(data, metadata)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# currently not exported, because it is only meant to be called
|
|
130
|
+
# from xeus-r / interpreter::execute_request_impl
|
|
131
|
+
execute <- function(code, execution_counter, silent = FALSE, eval_env = rlang::global_env()) {
|
|
132
|
+
the$last_error <- NULL
|
|
133
|
+
|
|
134
|
+
parsed <- tryCatch(
|
|
135
|
+
parse(text = code),
|
|
136
|
+
error = function(e) {
|
|
137
|
+
msg <- paste(conditionMessage(e), collapse = "\n")
|
|
138
|
+
the$last_error <- structure(list(ename = "PARSE ERROR", evalue = msg), class = "error_reply")
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
if (!is.null(the$last_error)) return(the$last_error)
|
|
142
|
+
|
|
143
|
+
if (is.null(the$evaluate_sink_patched)) {
|
|
144
|
+
the$evaluate_sink_patched <- patch_evaluate_sink()
|
|
145
|
+
}
|
|
146
|
+
# A silent execution must not show anything, so it is not tee'd.
|
|
147
|
+
the$tee_stdout <- !silent && isTRUE(the$evaluate_sink_patched)
|
|
148
|
+
|
|
149
|
+
output_handler <- if (silent) {
|
|
150
|
+
evaluate::new_output_handler()
|
|
151
|
+
} else {
|
|
152
|
+
evaluate::new_output_handler(
|
|
153
|
+
# With the tee on, stdout was already published live from the console.
|
|
154
|
+
text = if (the$tee_stdout) function(txt) NULL else function(txt) publish_stream("stdout", txt),
|
|
155
|
+
graphics = handle_graphics,
|
|
156
|
+
message = handle_message,
|
|
157
|
+
warning = handle_warning,
|
|
158
|
+
error = handle_error,
|
|
159
|
+
value = handle_value
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
the$last_plot <- NULL
|
|
164
|
+
the$last_visible <- FALSE
|
|
165
|
+
|
|
166
|
+
filename <- glue("[{execution_counter}]")
|
|
167
|
+
|
|
168
|
+
the$frame_cell_execute <- environment()
|
|
169
|
+
evaluate::evaluate(
|
|
170
|
+
code,
|
|
171
|
+
envir = eval_env,
|
|
172
|
+
output_handler = output_handler,
|
|
173
|
+
stop_on_error = 1L,
|
|
174
|
+
filename = filename
|
|
175
|
+
)
|
|
176
|
+
if (!is.null(the$last_error)) return(the$last_error)
|
|
177
|
+
|
|
178
|
+
if (!silent && !is.null(the$last_plot)) {
|
|
179
|
+
tryCatch(send_plot(the$last_plot), error = handle_error)
|
|
180
|
+
}
|
|
181
|
+
if (!is.null(the$last_error)) return(the$last_error)
|
|
182
|
+
|
|
183
|
+
if (isTRUE(the$last_visible)) {
|
|
184
|
+
obj <- .Last.value
|
|
185
|
+
|
|
186
|
+
bundle <- mime_bundle(obj)
|
|
187
|
+
|
|
188
|
+
structure(class = "execution_result",
|
|
189
|
+
list(
|
|
190
|
+
# data = toJSON(bundle$data, auto_unbox = TRUE),
|
|
191
|
+
# metadata = toJSON(bundle$metadata, auto_unbox = TRUE)
|
|
192
|
+
# Before toJSON, ensure data is UTF-8
|
|
193
|
+
data = enc2utf8(toJSON(sanitize_raw_for_json(bundle$data), auto_unbox = TRUE)),
|
|
194
|
+
metadata = enc2utf8(toJSON(bundle$metadata, auto_unbox = TRUE))
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
}
|