Skip to content

Commit 729ae37

Browse files
dfalbellionel-
andauthored
Use correct definition of table for the connections pane (#248)
* Handle custom icons for connections. * lenght(path) == 0 is just fine, as it means that we are at root level and it might actually provide an icon. * Actually at root level we want the global connection icon. * Refactor flattenting object types to match RStudio implementation. * Contains data request. * The number of rows can't be a named argument as different implementations are using different names. * Rewrite object types flattenting making it easier to read. * Change name for clarity * Simplify contains Co-authored-by: Lionel Henry <[email protected]> * Prefer depth-first search. --------- Co-authored-by: Lionel Henry <[email protected]>
1 parent 921180c commit 729ae37

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

crates/ark/src/connections/r_connection.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ pub enum ConnectionResponse {
5252
IconResponse {
5353
icon: Option<String>,
5454
},
55+
ContainsDataResponse {
56+
contains_data: bool,
57+
},
5558
}
5659

5760
#[derive(Debug, Serialize, Deserialize)]
@@ -65,6 +68,8 @@ pub enum ConnectionRequest {
6568
PreviewTable { path: Vec<ConnectionTable> },
6669
// The UI asks for an icon for a given element
6770
IconRequest { path: Vec<ConnectionTable> },
71+
// The UI asks if the object contains data
72+
ContainsDataRequest { path: Vec<ConnectionTable> },
6873
}
6974

7075
#[derive(Deserialize, Serialize)]
@@ -235,6 +240,22 @@ impl RConnection {
235240
})?;
236241
Ok(ConnectionResponse::IconResponse { icon: icon_path })
237242
},
243+
ConnectionRequest::ContainsDataRequest { path } => {
244+
// Calls back into R to check if the object contains data.
245+
let contains_data = r_task(|| -> Result<_, anyhow::Error> {
246+
unsafe {
247+
let mut contains_data_call: RFunction =
248+
RFunction::from(".ps.connection_contains_data");
249+
contains_data_call.add(RObject::from(self.comm.comm_id.clone()));
250+
for obj in path {
251+
contains_data_call.param(obj.kind.as_str(), obj.name);
252+
}
253+
let contains_data = contains_data_call.call()?;
254+
Ok(RObject::to::<bool>(contains_data)?)
255+
}
256+
})?;
257+
Ok(ConnectionResponse::ContainsDataResponse { contains_data })
258+
},
238259
}
239260
}
240261

crates/ark/src/modules/positron/connection.R

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@
3737
listColumns = listColumns,
3838
previewObject = previewObject,
3939
connectionObject = connectionObject,
40-
actions = actions
40+
actions = actions,
41+
# objectTypes are computed only once when creating the connection and are assumed to be static
42+
# until the end of the connection.
43+
objectTypes = connection_flatten_object_types(listObjectTypes())
4144
)
4245
invisible(id)
4346
}
@@ -68,6 +71,25 @@
6871

6972
options("connectionObserver" = .ps.connection_observer())
7073

74+
connection_flatten_object_types <- function(object_tree) {
75+
# RStudio actually flattens the objectTree to make it easier to find metadata for an object type.
76+
# See link below for the original implementation
77+
# https://github.com/rstudio/rstudio/blob/fac89e1c4179fd23f47ff218bb106fd4e5cf2917/src/cpp/session/modules/SessionConnections.R#L165
78+
object_types <- list()
79+
while (length(object_tree) != 0) {
80+
object <- object_tree[[1]]
81+
name <- names(object_tree)[1]
82+
object_types[[name]] <- object
83+
84+
object_tree <- object_tree[-1]
85+
if (!is.null(object$contains) && !identical(object$contains, "data")) {
86+
contains <- object$contains[!names(object$contains) %in% names(object_types)]
87+
object_tree <- c(contains, object_tree)
88+
}
89+
}
90+
object_types
91+
}
92+
7193
#' @export
7294
.ps.connection_list_objects <- function(id, ...) {
7395
con <- get(id, getOption("connectionObserver")$.connections)
@@ -87,12 +109,17 @@ options("connectionObserver" = .ps.connection_observer())
87109
}
88110

89111
#' @export
90-
.ps.connection_preview_object <- function(id, ..., table) {
112+
.ps.connection_preview_object <- function(id, ...) {
113+
path <- list(...)
91114
con <- get(id, getOption("connectionObserver")$.connections)
92115
if (is.null(con)) {
93116
return(NULL)
94117
}
95-
View(con$previewObject(table = table, ..., limit = 100), title = table)
118+
# we assume the first unnamed argument refers to the number of rows that
119+
# will be collected; this is basically what RStudio does here:
120+
# https://github.com/rstudio/rstudio/blob/018ea143118a15d46a5eaef16a43aef28ac03fb9/src/cpp/session/modules/connections/SessionConnections.cpp#L477-L480
121+
table <- con$previewObject(1000, ...)
122+
utils::View(table, title = utils::tail(path, 1)[[1]])
96123
}
97124

98125
.ps.connection_close <- function(id, ...) {
@@ -115,12 +142,20 @@ options("connectionObserver" = .ps.connection_observer())
115142
return(con$icon)
116143
}
117144

118-
object_types <- con$listObjectTypes()
119-
object_types <- object_types[[1]] # root is always element 1
145+
object_types <- con$objectTypes[[utils::tail(path, 1)]]
146+
object_types$icon
147+
}
148+
149+
#' @export
150+
.ps.connection_contains_data <- function(id, ...) {
151+
con <- get(id, getOption("connectionObserver")$.connections)
152+
path <- names(list(...))
120153

121-
for (p in path) {
122-
object_types <- object_types$contains[[p]]
154+
if (length(path) == 0) {
155+
# we are at the root of the connection, so must not contain data.
156+
return(FALSE)
123157
}
124158

125-
object_types$icon
159+
object_types <- con$objectTypes[[utils::tail(path, 1)]]
160+
identical(object_types$contains, "data")
126161
}

0 commit comments

Comments
 (0)