Skip to content

Commit dad2c39

Browse files
authored
Use new UI event to send a putative path to the system for opening (#877)
* WIP poking around * Rough in over here in ark * Regenerate UI comm; actually implement the event * Normalize on this side In particular, I'm thinking of expanding `~`, i.e. the home directory * Align function name with event name
1 parent 69f49c4 commit dad2c39

File tree

3 files changed

+60
-28
lines changed

3 files changed

+60
-28
lines changed

crates/amalthea/src/comm/ui_comm.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,13 @@ pub struct ShowHtmlFileParams {
292292
pub height: i64,
293293
}
294294

295+
/// Parameters for the OpenWithSystem method.
296+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
297+
pub struct OpenWithSystemParams {
298+
/// The file path to open with the system default application
299+
pub path: String,
300+
}
301+
295302
/**
296303
* Backend RPC request types for the ui comm
297304
*/
@@ -502,6 +509,10 @@ pub enum UiFrontendEvent {
502509
#[serde(rename = "show_html_file")]
503510
ShowHtmlFile(ShowHtmlFileParams),
504511

512+
/// Open a file or folder with the system default application
513+
#[serde(rename = "open_with_system")]
514+
OpenWithSystem(OpenWithSystemParams),
515+
505516
/// This event is used to signal that the stored messages the front-end
506517
/// replays when constructing multi-output plots should be reset. This
507518
/// happens for things like a holoviews extension being changed.

crates/ark/src/browser.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55
//
66
//
77

8-
use amalthea::comm::ui_comm::ShowUrlParams;
9-
use amalthea::comm::ui_comm::UiFrontendEvent;
108
use harp::object::RObject;
9+
use harp::utils::r_normalize_path;
1110
use libr::Rf_ScalarLogical;
1211
use libr::SEXP;
1312

1413
use crate::help::message::HelpEvent;
1514
use crate::help::message::ShowHelpUrlParams;
1615
use crate::interface::RMain;
16+
use crate::ui::events::send_open_with_system_event;
17+
use crate::ui::events::send_show_url_event;
1718

1819
#[harp::register]
1920
pub unsafe extern "C-unwind" fn ps_browse_url(url: SEXP) -> anyhow::Result<SEXP> {
@@ -35,33 +36,32 @@ fn handle_help_url(url: String) -> anyhow::Result<()> {
3536
}
3637

3738
unsafe fn ps_browse_url_impl(url: SEXP) -> anyhow::Result<SEXP> {
38-
// Extract URL.
39-
let url = RObject::view(url).to::<String>()?;
40-
let _span = tracing::trace_span!("browseURL", url = %url).entered();
39+
// Extract URL string for analysis
40+
let url_string = RObject::view(url).to::<String>()?;
41+
let _span = tracing::trace_span!("browseURL", url = %url_string).entered();
4142

4243
// Handle help server requests.
43-
if is_help_url(&url) {
44+
if is_help_url(&url_string) {
4445
log::trace!("Help is handling URL");
45-
handle_help_url(url)?;
46+
handle_help_url(url_string)?;
4647
return Ok(Rf_ScalarLogical(1));
47-
} else {
48-
log::trace!("Help is not handling URL");
4948
}
5049

51-
// TODO: What is the right thing to do outside of Positron when
52-
// `options(browser =)` is called? Right now we error.
53-
54-
// For all other URLs, create a ShowUrl event and send it to the main
55-
// thread; Positron will handle it.
56-
let params = ShowUrlParams { url };
57-
let event = UiFrontendEvent::ShowUrl(params);
58-
59-
let main = RMain::get();
60-
let ui_comm_tx = main
61-
.get_ui_comm_tx()
62-
.ok_or_else(|| anyhow::anyhow!("UI comm not connected."))?;
63-
64-
ui_comm_tx.send_event(event);
50+
// Handle web URLs
51+
if is_web_url(&url_string) {
52+
log::trace!("Handling web URL");
53+
send_show_url_event(&url_string)?;
54+
return Ok(Rf_ScalarLogical(1));
55+
}
6556

57+
// This is probably a file path? Send to the front end and ask for system
58+
// default opener.
59+
log::trace!("Treating as file path and asking system to open");
60+
let path = r_normalize_path(url.into())?;
61+
send_open_with_system_event(&path)?;
6662
Ok(Rf_ScalarLogical(1))
6763
}
64+
65+
fn is_web_url(url: &str) -> bool {
66+
url.starts_with("http://") || url.starts_with("https://")
67+
}

crates/ark/src/ui/events.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
use amalthea::comm::ui_comm::OpenEditorParams;
9+
use amalthea::comm::ui_comm::OpenWithSystemParams;
910
use amalthea::comm::ui_comm::OpenWorkspaceParams;
1011
use amalthea::comm::ui_comm::Position;
1112
use amalthea::comm::ui_comm::Range;
@@ -96,23 +97,43 @@ pub unsafe extern "C-unwind" fn ps_ui_set_selection_ranges(ranges: SEXP) -> anyh
9697
Ok(R_NilValue)
9798
}
9899

99-
#[harp::register]
100-
pub unsafe extern "C-unwind" fn ps_ui_show_url(url: SEXP) -> anyhow::Result<SEXP> {
100+
pub fn send_show_url_event(url: &str) -> anyhow::Result<()> {
101101
let params = ShowUrlParams {
102-
url: RObject::view(url).try_into()?,
102+
url: url.to_string(),
103103
};
104-
105104
let event = UiFrontendEvent::ShowUrl(params);
106105

107106
let main = RMain::get();
108107
let ui_comm_tx = main
109108
.get_ui_comm_tx()
110-
.ok_or_else(|| ui_comm_not_connected("ui_show_url"))?;
109+
.ok_or_else(|| ui_comm_not_connected("show_url"))?;
111110
ui_comm_tx.send_event(event);
112111

112+
Ok(())
113+
}
114+
115+
#[harp::register]
116+
pub unsafe extern "C-unwind" fn ps_ui_show_url(url: SEXP) -> anyhow::Result<SEXP> {
117+
let url_string = RObject::view(url).to::<String>()?;
118+
send_show_url_event(&url_string)?;
113119
Ok(R_NilValue)
114120
}
115121

122+
pub fn send_open_with_system_event(path: &str) -> anyhow::Result<()> {
123+
let params = OpenWithSystemParams {
124+
path: path.to_string(),
125+
};
126+
let event = UiFrontendEvent::OpenWithSystem(params);
127+
128+
let main = RMain::get();
129+
let ui_comm_tx = main
130+
.get_ui_comm_tx()
131+
.ok_or_else(|| ui_comm_not_connected("open_with_system"))?;
132+
ui_comm_tx.send_event(event);
133+
134+
Ok(())
135+
}
136+
116137
pub fn ps_ui_robj_as_ranges(ranges: SEXP) -> anyhow::Result<Vec<Range>> {
117138
let ranges_as_r_objects: Vec<RObject> = RObject::view(ranges).try_into()?;
118139
let ranges_as_result: Result<Vec<Vec<i32>>, _> = ranges_as_r_objects

0 commit comments

Comments
 (0)