mirror of
https://github.com/denoland/deno.git
synced 2025-07-24 05:35:33 +00:00
refactor(cli/fmt_errors): Color stack traces in Rust (#7628)
This commit is contained in:
parent
a33315aaa7
commit
a43984c9cf
6 changed files with 279 additions and 209 deletions
|
@ -3,30 +3,156 @@
|
|||
use crate::colors;
|
||||
use crate::source_maps::apply_source_map;
|
||||
use crate::source_maps::SourceMapGetter;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::error::JsError as CoreJsError;
|
||||
use deno_core::error::{AnyError, JsError as CoreJsError, JsStackFrame};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
const SOURCE_ABBREV_THRESHOLD: usize = 150;
|
||||
|
||||
pub fn format_location(filename: &str, line: i64, col: i64) -> String {
|
||||
format!(
|
||||
"{}:{}:{}",
|
||||
colors::cyan(filename),
|
||||
colors::yellow(&line.to_string()),
|
||||
colors::yellow(&col.to_string())
|
||||
)
|
||||
fn default_color(s: &str, internal: bool) -> String {
|
||||
if internal {
|
||||
colors::gray(s).to_string()
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_stack(
|
||||
fn cyan(s: &str, internal: bool) -> String {
|
||||
if internal {
|
||||
colors::gray(s).to_string()
|
||||
} else {
|
||||
colors::cyan(s).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn yellow(s: &str, internal: bool) -> String {
|
||||
if internal {
|
||||
colors::gray(s).to_string()
|
||||
} else {
|
||||
colors::yellow(s).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn italic_bold(s: &str, internal: bool) -> String {
|
||||
if internal {
|
||||
colors::italic_bold_gray(s).to_string()
|
||||
} else {
|
||||
colors::italic_bold(s).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// Keep in sync with `cli/rt/40_error_stack.js`.
|
||||
pub fn format_location(frame: &JsStackFrame) -> String {
|
||||
let internal = frame
|
||||
.file_name
|
||||
.as_ref()
|
||||
.map_or(false, |f| f.starts_with("deno:"));
|
||||
if frame.is_native {
|
||||
return cyan("native", internal);
|
||||
}
|
||||
let mut result = String::new();
|
||||
if let Some(file_name) = &frame.file_name {
|
||||
result += &cyan(&file_name, internal);
|
||||
} else {
|
||||
if frame.is_eval {
|
||||
result += &(cyan(&frame.eval_origin.as_ref().unwrap(), internal) + ", ");
|
||||
}
|
||||
result += &cyan("<anonymous>", internal);
|
||||
}
|
||||
if let Some(line_number) = frame.line_number {
|
||||
result += &format!(
|
||||
"{}{}",
|
||||
default_color(":", internal),
|
||||
yellow(&line_number.to_string(), internal)
|
||||
);
|
||||
if let Some(column_number) = frame.column_number {
|
||||
result += &format!(
|
||||
"{}{}",
|
||||
default_color(":", internal),
|
||||
yellow(&column_number.to_string(), internal)
|
||||
);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// Keep in sync with `cli/rt/40_error_stack.js`.
|
||||
fn format_frame(frame: &JsStackFrame) -> String {
|
||||
let internal = frame
|
||||
.file_name
|
||||
.as_ref()
|
||||
.map_or(false, |f| f.starts_with("deno:"));
|
||||
let is_method_call =
|
||||
!(frame.is_top_level.unwrap_or_default() || frame.is_constructor);
|
||||
let mut result = String::new();
|
||||
if frame.is_async {
|
||||
result += &colors::gray("async ").to_string();
|
||||
}
|
||||
if frame.is_promise_all {
|
||||
result += &italic_bold(
|
||||
&format!(
|
||||
"Promise.all (index {})",
|
||||
frame.promise_index.unwrap_or_default().to_string()
|
||||
),
|
||||
internal,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
if is_method_call {
|
||||
let mut formatted_method = String::new();
|
||||
if let Some(function_name) = &frame.function_name {
|
||||
if let Some(type_name) = &frame.type_name {
|
||||
if !function_name.starts_with(type_name) {
|
||||
formatted_method += &format!("{}.", type_name);
|
||||
}
|
||||
}
|
||||
formatted_method += &function_name;
|
||||
if let Some(method_name) = &frame.method_name {
|
||||
if !function_name.ends_with(method_name) {
|
||||
formatted_method += &format!(" [as {}]", method_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(type_name) = &frame.type_name {
|
||||
formatted_method += &format!("{}.", type_name);
|
||||
}
|
||||
if let Some(method_name) = &frame.method_name {
|
||||
formatted_method += &method_name
|
||||
} else {
|
||||
formatted_method += "<anonymous>";
|
||||
}
|
||||
}
|
||||
result += &italic_bold(&formatted_method, internal);
|
||||
} else if frame.is_constructor {
|
||||
result += &colors::gray("new ").to_string();
|
||||
if let Some(function_name) = &frame.function_name {
|
||||
result += &italic_bold(&function_name, internal);
|
||||
} else {
|
||||
result += &cyan("<anonymous>", internal);
|
||||
}
|
||||
} else if let Some(function_name) = &frame.function_name {
|
||||
result += &italic_bold(&function_name, internal);
|
||||
} else {
|
||||
result += &format_location(frame);
|
||||
return result;
|
||||
}
|
||||
result += &format!(
|
||||
" {}{}{}",
|
||||
default_color("(", internal),
|
||||
format_location(frame),
|
||||
default_color(")", internal)
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
fn format_stack(
|
||||
is_error: bool,
|
||||
message_line: &str,
|
||||
source_line: Option<&str>,
|
||||
start_column: Option<i64>,
|
||||
end_column: Option<i64>,
|
||||
formatted_frames: &[String],
|
||||
frames: &[JsStackFrame],
|
||||
level: usize,
|
||||
) -> String {
|
||||
let mut s = String::new();
|
||||
|
@ -38,11 +164,11 @@ pub fn format_stack(
|
|||
is_error,
|
||||
level,
|
||||
));
|
||||
for formatted_frame in formatted_frames {
|
||||
for frame in frames {
|
||||
s.push_str(&format!(
|
||||
"\n{:indent$} at {}",
|
||||
"",
|
||||
formatted_frame,
|
||||
format_frame(frame),
|
||||
indent = level
|
||||
));
|
||||
}
|
||||
|
@ -128,31 +254,23 @@ impl Deref for JsError {
|
|||
|
||||
impl fmt::Display for JsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut formatted_frames = self.0.formatted_frames.clone();
|
||||
|
||||
// The formatted_frames passed from prepareStackTrace() are colored.
|
||||
if !colors::use_color() {
|
||||
formatted_frames = formatted_frames
|
||||
.iter()
|
||||
.map(|s| colors::strip_ansi_codes(s).to_string())
|
||||
.collect();
|
||||
}
|
||||
let mut frames = self.0.frames.clone();
|
||||
|
||||
// When the stack frame array is empty, but the source location given by
|
||||
// (script_resource_name, line_number, start_column + 1) exists, this is
|
||||
// likely a syntax error. For the sake of formatting we treat it like it was
|
||||
// given as a single stack frame.
|
||||
if formatted_frames.is_empty()
|
||||
if frames.is_empty()
|
||||
&& self.0.script_resource_name.is_some()
|
||||
&& self.0.line_number.is_some()
|
||||
&& self.0.start_column.is_some()
|
||||
{
|
||||
formatted_frames = vec![format_location(
|
||||
self.0.script_resource_name.as_ref().unwrap(),
|
||||
self.0.line_number.unwrap(),
|
||||
self.0.start_column.unwrap() + 1,
|
||||
)]
|
||||
};
|
||||
frames = vec![JsStackFrame::from_location(
|
||||
self.0.script_resource_name.clone(),
|
||||
self.0.line_number,
|
||||
self.0.start_column.map(|n| n + 1),
|
||||
)];
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
|
@ -163,7 +281,7 @@ impl fmt::Display for JsError {
|
|||
self.0.source_line.as_deref(),
|
||||
self.0.start_column,
|
||||
self.0.end_column,
|
||||
&formatted_frames,
|
||||
&frames,
|
||||
0
|
||||
)
|
||||
)?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue