Show KCL backtraces (#7033)
* Add backtrace to errors * Add display of backtraces with hints * Change pane badge to only show count of errors * Fix property name to not collide with Error superclass * Increase min stack again * Add e2e test that checks that the diagnostics are created in CodeMirror * Remove unneeded code * Change to the new hotness
This commit is contained in:
@ -439,12 +439,7 @@ impl EngineManager for EngineConnection {
|
||||
request_sent: tx,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to send debug: {}", e),
|
||||
source_ranges: vec![],
|
||||
})
|
||||
})?;
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(format!("Failed to send debug: {}", e), vec![])))?;
|
||||
|
||||
let _ = rx.await;
|
||||
Ok(())
|
||||
@ -479,25 +474,25 @@ impl EngineManager for EngineConnection {
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to send modeling command: {}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to send modeling command: {}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
// Wait for the request to be sent.
|
||||
rx.await
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("could not send request to the engine actor: {e}"),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("could not send request to the engine actor: {e}"),
|
||||
vec![source_range],
|
||||
))
|
||||
})?
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("could not send request to the engine: {e}"),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("could not send request to the engine: {e}"),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
@ -521,15 +516,15 @@ impl EngineManager for EngineConnection {
|
||||
// Check if we have any pending errors.
|
||||
let pe = self.pending_errors.read().await;
|
||||
if !pe.is_empty() {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: pe.join(", ").to_string(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
pe.join(", ").to_string(),
|
||||
vec![source_range],
|
||||
)));
|
||||
} else {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: "Modeling command failed: websocket closed early".to_string(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
"Modeling command failed: websocket closed early".to_string(),
|
||||
vec![source_range],
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,10 +543,10 @@ impl EngineManager for EngineConnection {
|
||||
}
|
||||
}
|
||||
|
||||
Err(KclError::Engine(KclErrorDetails {
|
||||
message: format!("Modeling command timed out `{}`", id),
|
||||
source_ranges: vec![source_range],
|
||||
}))
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
format!("Modeling command timed out `{}`", id),
|
||||
vec![source_range],
|
||||
)))
|
||||
}
|
||||
|
||||
async fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||
|
@ -147,32 +147,27 @@ impl EngineConnection {
|
||||
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||
) -> Result<(), KclError> {
|
||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize modeling command: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize modeling command: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize id to source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize id to source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
self.manager
|
||||
.fire_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: e.to_string().into(),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -185,33 +180,28 @@ impl EngineConnection {
|
||||
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||
) -> Result<WebSocketResponse, KclError> {
|
||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize modeling command: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize modeling command: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize id to source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize id to source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
let promise = self
|
||||
.manager
|
||||
.send_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: e.to_string().into(),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
||||
// Try to parse the error as an engine error.
|
||||
@ -219,53 +209,52 @@ impl EngineConnection {
|
||||
if let Ok(kittycad_modeling_cmds::websocket::FailureWebSocketResponse { errors, .. }) =
|
||||
serde_json::from_str(&err_str)
|
||||
{
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
|
||||
vec![source_range],
|
||||
))
|
||||
} else if let Ok(data) =
|
||||
serde_json::from_str::<Vec<kittycad_modeling_cmds::websocket::FailureWebSocketResponse>>(&err_str)
|
||||
{
|
||||
if let Some(data) = data.first() {
|
||||
// It could also be an array of responses.
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: data
|
||||
.errors
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
data.errors
|
||||
.iter()
|
||||
.map(|e| e.message.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
vec![source_range],
|
||||
))
|
||||
} else {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: "Received empty response from engine".into(),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
"Received empty response from engine".into(),
|
||||
vec![source_range],
|
||||
))
|
||||
}
|
||||
} else {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to wait for promise from send modeling command: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to wait for promise from send modeling command: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
}
|
||||
})?;
|
||||
|
||||
if value.is_null() || value.is_undefined() {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: "Received null or undefined response from engine".into(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
"Received null or undefined response from engine".into(),
|
||||
vec![source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
// Convert JsValue to a Uint8Array
|
||||
let data = js_sys::Uint8Array::from(value);
|
||||
|
||||
let ws_result: WebSocketResponse = bson::from_slice(&data.to_vec()).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to deserialize bson response from engine: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to deserialize bson response from engine: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(ws_result)
|
||||
@ -316,18 +305,16 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
*self.default_planes.write().await = Some(new_planes);
|
||||
|
||||
// Start a new session.
|
||||
let promise = self.manager.start_new_session().map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: e.to_string().into(),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
let promise = self
|
||||
.manager
|
||||
.start_new_session()
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to wait for promise from start new session: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to wait for promise from start new session: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
|
@ -276,10 +276,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
{
|
||||
let duration = instant::Duration::from_millis(1);
|
||||
wasm_timer::Delay::new(duration).await.map_err(|err| {
|
||||
KclError::Internal(KclErrorDetails {
|
||||
message: format!("Failed to sleep: {:?}", err),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
format!("Failed to sleep: {:?}", err),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -293,10 +293,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
Err(KclError::Engine(KclErrorDetails {
|
||||
message: "async command timed out".to_string(),
|
||||
source_ranges: vec![source_range],
|
||||
}))
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
"async command timed out".to_string(),
|
||||
vec![source_range],
|
||||
)))
|
||||
}
|
||||
|
||||
/// Ensure ALL async commands have been completed.
|
||||
@ -547,10 +547,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
id_to_source_range.insert(Uuid::from(*cmd_id), *range);
|
||||
}
|
||||
_ => {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: format!("The request is not a modeling command: {:?}", req),
|
||||
source_ranges: vec![*range],
|
||||
}));
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
format!("The request is not a modeling command: {:?}", req),
|
||||
vec![*range],
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -595,10 +595,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
self.parse_batch_responses(last_id.into(), id_to_source_range, responses)
|
||||
} else {
|
||||
// We should never get here.
|
||||
Err(KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to get batch response: {:?}", response),
|
||||
source_ranges: vec![source_range],
|
||||
}))
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to get batch response: {:?}", response),
|
||||
vec![source_range],
|
||||
)))
|
||||
}
|
||||
}
|
||||
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd: _, cmd_id }) => {
|
||||
@ -610,20 +610,20 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
// request so we need the original request source range in case the engine returns
|
||||
// an error.
|
||||
let source_range = id_to_source_range.get(cmd_id.as_ref()).cloned().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to get source range for command ID: {:?}", cmd_id),
|
||||
source_ranges: vec![],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to get source range for command ID: {:?}", cmd_id),
|
||||
vec![],
|
||||
))
|
||||
})?;
|
||||
let ws_resp = self
|
||||
.inner_send_modeling_cmd(cmd_id.into(), source_range, final_req, id_to_source_range)
|
||||
.await?;
|
||||
self.parse_websocket_response(ws_resp, source_range)
|
||||
}
|
||||
_ => Err(KclError::Engine(KclErrorDetails {
|
||||
message: format!("The final request is not a modeling command: {:?}", final_req),
|
||||
source_ranges: vec![source_range],
|
||||
})),
|
||||
_ => Err(KclError::Engine(KclErrorDetails::new(
|
||||
format!("The final request is not a modeling command: {:?}", final_req),
|
||||
vec![source_range],
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -729,10 +729,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
for (name, plane_id, color) in plane_settings {
|
||||
let info = DEFAULT_PLANE_INFO.get(&name).ok_or_else(|| {
|
||||
// We should never get here.
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to get default plane info for: {:?}", name),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to get default plane info for: {:?}", name),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
planes.insert(
|
||||
name,
|
||||
@ -763,15 +763,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
WebSocketResponse::Success(success) => Ok(success.resp),
|
||||
WebSocketResponse::Failure(fail) => {
|
||||
let _request_id = fail.request_id;
|
||||
Err(KclError::Engine(KclErrorDetails {
|
||||
message: fail
|
||||
.errors
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
fail.errors
|
||||
.iter()
|
||||
.map(|e| e.message.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
source_ranges: vec![source_range],
|
||||
}))
|
||||
vec![source_range],
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -806,25 +805,25 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
BatchResponse::Failure { errors } => {
|
||||
// Get the source range for the command.
|
||||
let source_range = id_to_source_range.get(cmd_id).cloned().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to get source range for command ID: {:?}", cmd_id),
|
||||
source_ranges: vec![],
|
||||
})
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to get source range for command ID: {:?}", cmd_id),
|
||||
vec![],
|
||||
))
|
||||
})?;
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
|
||||
vec![source_range],
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error that we did not get an error or the response we wanted.
|
||||
// This should never happen but who knows.
|
||||
Err(KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to find response for command ID: {:?}", id),
|
||||
source_ranges: vec![],
|
||||
}))
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
format!("Failed to find response for command ID: {:?}", id),
|
||||
vec![],
|
||||
)))
|
||||
}
|
||||
|
||||
async fn modify_grid(
|
||||
|
Reference in New Issue
Block a user