Skip to content

Commit

Permalink
Support formulas (#630)
Browse files Browse the repository at this point in the history
* Add support for formulas in the variables pane

* Improve handling of formulas

* Fix multi-line formulae
  • Loading branch information
dfalbel authored Nov 25, 2024
1 parent 586df40 commit f3fffe3
Showing 1 changed file with 62 additions and 1 deletion.
63 changes: 62 additions & 1 deletion crates/ark/src/variables/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use harp::utils::r_is_s4;
use harp::utils::r_is_simple_vector;
use harp::utils::r_is_unbound;
use harp::utils::r_promise_force_with_rollback;
use harp::utils::r_type2char;
use harp::utils::r_typeof;
use harp::utils::r_vec_is_single_dimension_with_single_value;
use harp::utils::r_vec_shape;
Expand All @@ -55,6 +56,7 @@ use stdext::local;
use stdext::unwrap;

use crate::methods::ArkGenerics;
use crate::modules::ARK_ENVS;

// Constants.
const MAX_DISPLAY_VALUE_ENTRIES: usize = 1_000;
Expand Down Expand Up @@ -90,8 +92,13 @@ impl WorkspaceVariableDisplayValue {
},
CLOSXP => Self::from_closure(value),
ENVSXP => Self::from_env(value),
LANGSXP => Self::from_language(value),
_ if r_is_matrix(value) => Self::from_matrix(value),
_ => Self::from_default(value),
RAWSXP | LGLSXP | INTSXP | REALSXP | STRSXP | CPLXSXP => Self::from_default(value),
_ => Self::from_error(Error::Anyhow(anyhow!(
"Unexpected type {}",
r_type2char(r_typeof(value))
))),
}
}

Expand All @@ -106,6 +113,40 @@ impl WorkspaceVariableDisplayValue {
Self::new(String::from(""), false)
}

fn from_language(value: SEXP) -> Self {
if r_inherits(value, "formula") {
return match Self::from_formula(value) {
Ok(display_value) => display_value,
Err(err) => Self::from_error(harp::Error::Anyhow(err)),
};
}

return Self::from_error(Error::Anyhow(anyhow!("Unexpected language object type")));
}

fn from_formula(value: SEXP) -> anyhow::Result<Self> {
// `format` for formula will return a character vector, splitting the expressions within
// the formula, for instance `~{x + y}` will be split into `~` and `["~{", "x", "}"]`.
let formatted: Vec<String> = RFunction::new("base", "format")
.add(value)
.call_in(ARK_ENVS.positron_ns)?
.try_into()?;

if formatted.len() < 1 {
return Err(anyhow!("Failed to format formula"));
}

let (mut truncated, mut display_value) =
truncate_chars(formatted[0].clone(), MAX_DISPLAY_VALUE_LENGTH);

if formatted.len() > 1 {
display_value.push_str(" ...");
truncated = true;
}

Ok(Self::new(display_value, truncated))
}

fn from_data_frame(value: SEXP) -> Self {
let dim = match unsafe { harp::df_dim(value) } {
Ok(dim) => dim,
Expand Down Expand Up @@ -1906,6 +1947,26 @@ mod tests {
})
}

#[test]
fn test_support_formula() {
r_task(|| {
let vars = inspect_from_expr("list(x = x ~ y + z + a)");
assert_eq!(vars[0].display_value, "x ~ y + z + a");

let vars = inspect_from_expr("list(x = x ~ {y + z + a})");
assert_eq!(vars[0].display_value, "x ~ { ...");
assert_eq!(vars[0].is_truncated, true);

let formula: String = (0..100).map(|i| format!("x{i}")).collect_vec().join(" + ");
let vars = inspect_from_expr(format!("list(x = x ~ {formula})").as_str());

assert_eq!(vars[0].is_truncated, true);
// The deparser truncates the formula at 70 characters so we don't expect to get to
// MAX_DISPLAY_VALUE_LENGTH. We do have protections if this behavior changes, though.
assert_eq!(vars[0].display_value.len(), 70);
})
}

#[test]
fn test_truncation_on_matrices() {
r_task(|| {
Expand Down

0 comments on commit f3fffe3

Please sign in to comment.