Skip to content

Commit 0219d45

Browse files
committed
Move format_traceback() to harp
1 parent 5d353a3 commit 0219d45

File tree

4 files changed

+16
-95
lines changed

4 files changed

+16
-95
lines changed

crates/ark/src/errors.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use harp::object::RObject;
99
use harp::r_symbol;
10+
use harp::session::r_format_traceback;
1011
use libr::R_GlobalEnv;
1112
use libr::R_NilValue;
1213
use libr::Rf_eval;
@@ -43,6 +44,11 @@ unsafe extern "C" fn ps_record_error(evalue: SEXP, traceback: SEXP) -> anyhow::R
4344
Ok(R_NilValue)
4445
}
4546

47+
#[harp::register]
48+
unsafe extern "C" fn ps_format_traceback(calls: SEXP) -> anyhow::Result<SEXP> {
49+
Ok(r_format_traceback(calls.into())?.sexp)
50+
}
51+
4652
pub unsafe fn initialize() {
4753
// Must be called after the public Positron function environment is set up
4854
info!("Initializing global error handler");

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

Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -137,99 +137,8 @@ handle_error_base <- function(cnd) {
137137
}
138138

139139
#' @param traceback A list of calls.
140-
format_traceback <- function(traceback = list()) {
141-
n <- length(traceback)
142-
143-
# TODO: This implementation prints the traceback in the same ordering
144-
# as rlang, i.e. with call 1 on the stack being the first thing you
145-
# see. `traceback()` prints in the reverse order, so users may want a
146-
# way to reverse both our display ordering and rlang's (requiring a
147-
# new `rlang::format()` argument).
148-
149-
# Collect source location, if there is any
150-
srcrefs <- lapply(traceback, function(call) attr(call, "srcref"))
151-
srcrefs <- vapply(srcrefs, src_loc, FUN.VALUE = character(1))
152-
has_srcref <- nchar(srcrefs) != 0L
153-
srcrefs[has_srcref] <- vec_paste0(" at ", srcrefs[has_srcref])
154-
155-
# Converts to a list of quoted calls to a list of deparsd calls.
156-
# Respects global options `"traceback.max.lines"` and `"deparse.max.lines"`!
157-
traceback <- .traceback(traceback)
158-
159-
# Prepend the stack number to each deparsed call, padding multiline calls as needed,
160-
# and then collapse multiline calls into one line
161-
prefixes <- vec_paste0(seq_len(n), ". ")
162-
prefixes <- format(prefixes, justify = "right")
163-
164-
traceback <- mapply(prepend_prefix, traceback, prefixes, SIMPLIFY = FALSE)
165-
traceback <- lapply(traceback, function(lines) paste0(lines, collapse = "\n"))
166-
traceback <- as.character(traceback)
167-
168-
paste0(traceback, srcrefs)
169-
}
170-
171-
prepend_prefix <- function(lines, prefix) {
172-
n_lines <- length(lines)
173-
174-
if (n_lines == 0L) {
175-
return(lines)
176-
}
177-
178-
# First line gets the prefix
179-
line <- lines[[1L]]
180-
line <- vec_paste0(prefix, line)
181-
182-
# Other lines are padded with whitespace as needed
183-
padding <- strrep(" ", times = nchar(prefix))
184-
185-
lines <- lines[-1L]
186-
lines <- vec_paste0(padding, lines)
187-
188-
lines <- c(line, lines)
189-
190-
lines
191-
}
192-
193-
src_loc <- function(srcref) {
194-
# Adapted from `rlang:::src_loc()`
195-
if (is.null(srcref)) {
196-
return("")
197-
}
198-
199-
srcfile <- attr(srcref, "srcfile")
200-
if (is.null(srcfile)) {
201-
return("")
202-
}
203-
204-
# May be:
205-
# - An actual file path
206-
# - `""` for user defined functions in the console
207-
# - `"<text>"` for `parse()`d functions
208-
# We only try and display the source location for file paths
209-
file <- srcfile$filename
210-
if (identical(file, "") || identical(file, "<text>")) {
211-
return("")
212-
}
213-
214-
file_trimmed <- path_trim_prefix(file, 3L)
215-
216-
first_line <- srcref[[1L]]
217-
first_column <- srcref[[5L]]
218-
219-
# TODO: We could generate file hyperlinks here like `rlang:::src_loc()`
220-
paste0(file_trimmed, ":", first_line, ":", first_column)
221-
}
222-
223-
path_trim_prefix <- function(path, n) {
224-
# `rlang:::path_trim_prefix()`
225-
split <- strsplit(path, "/")[[1]]
226-
n_split <- length(split)
227-
228-
if (n_split <= n) {
229-
path
230-
} else {
231-
paste(split[seq(n_split - n + 1, n_split)], collapse = "/")
232-
}
140+
format_traceback <- function(calls = list()) {
141+
.ps.Call("ps_format_traceback", calls)
233142
}
234143

235144
handle_error_rlang <- function(cnd) {

crates/harp/src/modules/init.R

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ safe_evalq <- function(expr, env) {
4545
)
4646
}
4747

48-
# ?FIXME: Can we reuse the following in Ark?
49-
5048
#' @param traceback A list of calls.
5149
format_traceback <- function(traceback = list()) {
5250
n <- length(traceback)

crates/harp/src/session.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use stdext::unwrap;
1212

1313
use crate::exec::r_parse;
1414
use crate::exec::RFunction;
15+
use crate::exec::RFunctionExt;
16+
use crate::modules::HARP_ENV;
1517
use crate::object::RObject;
1618
use crate::protect::RProtect;
1719
use crate::r_lang;
@@ -218,6 +220,12 @@ fn stack_shift(stack: &mut Vec<FrameInfo>, pointer: FrameInfo) {
218220
}
219221
}
220222

223+
pub fn r_format_traceback(calls: RObject) -> crate::Result<RObject> {
224+
RFunction::new("", "format_traceback")
225+
.add(calls)
226+
.call_in(unsafe { HARP_ENV.unwrap() })
227+
}
228+
221229
fn init_interface() {
222230
unsafe {
223231
let nframe_call = r_lang!(r_symbol!("sys.nframe"));

0 commit comments

Comments
 (0)