diff --git a/Cargo.toml b/Cargo.toml index 7db59226c..4cb62ce41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,8 @@ serde_json = "1.0.79" rand = { version = "0.8.4", features = ["small_rng"] } tokio = { version = "1.16.1", features = ["full"] } reqwest = { version = "0.11.9", features = ["json"] } -dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui"] } +# dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui"] } +dioxus = { path = ".", features = ["desktop"] } fern = { version = "0.6.0", features = ["colored"] } criterion = "0.3.5" thiserror = "1.0.30" diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 1f702e825..ddfb11ca8 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -192,11 +192,11 @@ pub fn rsx(s: TokenStream) -> TokenStream { Ok(captured) => { let lazy = quote::quote! { LazyNodes::new(move |__cx|{ + let code_location = get_line_num(); let captured = #captured; - let line_num = get_line_num(); let text = #rsx_text; - resolve_scope(line_num, text, captured, __cx) + resolve_scope(code_location, text, captured, __cx) }) }; if let Some(cx) = captured.custom_context { diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs index 3d8c7cde9..1dacadbf4 100644 --- a/packages/desktop/src/controller.rs +++ b/packages/desktop/src/controller.rs @@ -81,17 +81,11 @@ impl DesktopController { impl ErrorHandler for DesktopErrorHandler { fn handle_error(&self, err: Error) { if let Some(conn) = &mut *self.latest_connection.lock().unwrap() { - match err { - Error::RecompileRequiredError(reason) => { - conn.get_mut() - .write_all( - (serde_json::to_string(&reason).unwrap() + "\n") - .as_bytes(), - ) - .unwrap(); - } - Error::ParseError(err) => log::warn!("{}", err), - } + conn.get_mut() + .write_all( + (serde_json::to_string(&err).unwrap() + "\n").as_bytes(), + ) + .unwrap(); } } } diff --git a/packages/rsx_interpreter/Cargo.toml b/packages/rsx_interpreter/Cargo.toml index 3b13646ae..2734ceebe 100644 --- a/packages/rsx_interpreter/Cargo.toml +++ b/packages/rsx_interpreter/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT/Apache-2.0" [dependencies] syn = { version = "1.0", features = ["extra-traits"] } +proc-macro2 = { version = "1.0.39", features = ["span-locations"] } quote = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/packages/rsx_interpreter/src/captuered_context.rs b/packages/rsx_interpreter/src/captuered_context.rs index 40c471112..57bafd51f 100644 --- a/packages/rsx_interpreter/src/captuered_context.rs +++ b/packages/rsx_interpreter/src/captuered_context.rs @@ -4,6 +4,8 @@ use dioxus_rsx::{ }; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{Expr, Ident, Result}; + +use crate::CodeLocation; #[derive(Default)] pub struct CapturedContextBuilder { pub ifmted: Vec, @@ -137,6 +139,7 @@ impl ToTokens for CapturedContextBuilder { iterators: vec![#((#iterators_str, #iterators)),*], expressions: vec![#((#captured_attr_expressions_text, #captured_expressions.to_string())),*], listeners: vec![#((#listeners_str, #listeners)),*], + location: code_location.clone() } }) } @@ -155,6 +158,8 @@ pub struct CapturedContext<'a> { pub expressions: Vec<(&'static str, String)>, // map listener code to the resulting listener pub listeners: Vec<(&'static str, Listener<'a>)>, + // used to provide better error messages + pub location: CodeLocation, } pub struct IfmtArgs { diff --git a/packages/rsx_interpreter/src/error.rs b/packages/rsx_interpreter/src/error.rs index 24ad86e20..e2d0e4dcd 100644 --- a/packages/rsx_interpreter/src/error.rs +++ b/packages/rsx_interpreter/src/error.rs @@ -1,9 +1,11 @@ use serde::{Deserialize, Serialize}; +use crate::CodeLocation; + /// An error produced when interperting the rsx -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub enum Error { - ParseError(syn::Error), + ParseError(ParseError), RecompileRequiredError(RecompileReason), } @@ -14,3 +16,24 @@ pub enum RecompileReason { CapturedComponent(String), CapturedListener(String), } + +#[derive(Debug, Serialize, Deserialize)] +pub struct ParseError { + pub message: String, + pub location: CodeLocation, +} + +impl ParseError { + pub fn new(error: syn::Error, mut location: CodeLocation) -> Self { + let message = error.to_string(); + let syn_call_site = error.span().start(); + location.line += syn_call_site.line as u32; + if syn_call_site.line == 0{ + location.column += syn_call_site.column as u32; + } + else{ + location.column = syn_call_site.column as u32; + } + ParseError { message, location } + } +} diff --git a/packages/rsx_interpreter/src/interperter.rs b/packages/rsx_interpreter/src/interperter.rs index 40e31912a..14066d3d5 100644 --- a/packages/rsx_interpreter/src/interperter.rs +++ b/packages/rsx_interpreter/src/interperter.rs @@ -7,7 +7,7 @@ use syn::{parse2, parse_str, Expr}; use crate::attributes::attrbute_to_static_str; use crate::captuered_context::{CapturedContext, IfmtArgs}; use crate::elements::element_to_static_str; -use crate::error::{Error, RecompileReason}; +use crate::error::{Error, ParseError, RecompileReason}; fn resolve_ifmt(ifmt: &IfmtInput, captured: &IfmtArgs) -> Result { let mut result = String::new(); @@ -64,7 +64,8 @@ fn build_node<'a>( let bump = factory.bump(); match node { BodyNode::Text(text) => { - let ifmt = IfmtInput::from_str(&text.value()).map_err(|err| Error::ParseError(err))?; + let ifmt = IfmtInput::from_str(&text.value()) + .map_err(|err| Error::ParseError(ParseError::new(err, ctx.location.clone())))?; let text = bump.alloc(resolve_ifmt(&ifmt, &ctx.captured)?); Ok(factory.text(format_args!("{}", text))) } @@ -76,14 +77,16 @@ fn build_node<'a>( let (name, value, span): (String, IfmtInput, Span) = match &attr.attr { ElementAttr::AttrText { name, value } => ( name.to_string(), - IfmtInput::from_str(&value.value()) - .map_err(|err| Error::ParseError(err))?, + IfmtInput::from_str(&value.value()).map_err(|err| { + Error::ParseError(ParseError::new(err, ctx.location.clone())) + })?, name.span(), ), ElementAttr::CustomAttrText { name, value } => ( name.value(), - IfmtInput::from_str(&value.value()) - .map_err(|err| Error::ParseError(err))?, + IfmtInput::from_str(&value.value()).map_err(|err| { + Error::ParseError(ParseError::new(err, ctx.location.clone())) + })?, name.span(), ), _ => unreachable!(), @@ -99,9 +102,9 @@ fn build_node<'a>( namespace, }); } else { - return Err(Error::ParseError(syn::Error::new( - span, - "unknown attribute", + return Err(Error::ParseError(ParseError::new( + syn::Error::new(span, "unknown attribute"), + ctx.location.clone(), ))); } } @@ -152,8 +155,9 @@ fn build_node<'a>( for attr in el.attributes { match attr.attr { ElementAttr::EventTokens { .. } => { - let expr: Expr = - parse2(attr.to_token_stream()).map_err(|err| Error::ParseError(err))?; + let expr: Expr = parse2(attr.to_token_stream()).map_err(|err| { + Error::ParseError(ParseError::new(err, ctx.location.clone())) + })?; if let Some(idx) = ctx.listeners.iter().position(|(code, _)| { if let Ok(parsed) = parse_str::(*code) { parsed == expr @@ -186,8 +190,9 @@ fn build_node<'a>( None, )), Some(lit) => { - let ifmt: IfmtInput = - parse_str(&lit.value()).map_err(|err| Error::ParseError(err))?; + let ifmt: IfmtInput = parse_str(&lit.value()).map_err(|err| { + Error::ParseError(ParseError::new(err, ctx.location.clone())) + })?; let key = bump.alloc(resolve_ifmt(&ifmt, &ctx.captured)?); Ok(factory.raw_element( @@ -201,15 +206,15 @@ fn build_node<'a>( } } } else { - Err(Error::ParseError(syn::Error::new( - el.name.span(), - "unknown element", + Err(Error::ParseError(ParseError::new( + syn::Error::new(el.name.span(), "unknown element"), + ctx.location.clone(), ))) } } BodyNode::Component(comp) => { - let expr: Expr = - parse2(comp.to_token_stream()).map_err(|err| Error::ParseError(err))?; + let expr: Expr = parse2(comp.to_token_stream()) + .map_err(|err| Error::ParseError(ParseError::new(err, ctx.location.clone())))?; if let Some(idx) = ctx.components.iter().position(|(code, _)| { if let Ok(parsed) = parse_str::(*code) { parsed == expr diff --git a/packages/rsx_interpreter/src/lib.rs b/packages/rsx_interpreter/src/lib.rs index 07542b085..db6fb4a3a 100644 --- a/packages/rsx_interpreter/src/lib.rs +++ b/packages/rsx_interpreter/src/lib.rs @@ -1,7 +1,7 @@ use captuered_context::CapturedContext; use dioxus_core::{NodeFactory, SchedulerMsg, VNode}; use dioxus_hooks::UnboundedSender; -use error::Error; +use error::{Error, ParseError}; use interperter::build; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; @@ -71,7 +71,8 @@ fn interpert_rsx<'a, 'b>( context: captuered_context::CapturedContext<'a>, ) -> Result, Error> { build( - parse_str(text).map_err(|err| Error::ParseError(err))?, + parse_str(text) + .map_err(|err| Error::ParseError(ParseError::new(err, context.location.clone())))?, context, &factory, ) diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index b4d1a58f0..2377d9336 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -275,15 +275,8 @@ pub async fn run_with_props(root: Component, root_props: T // forward stream to the websocket dom.base_scope().spawn_forever(async move { while let Some(err) = error_channel_receiver.next().await { - match err { - Error::RecompileRequiredError(err) => { - ws.send_with_str(serde_json::to_string(&err).unwrap().as_str()) - .unwrap(); - } - Error::ParseError(err) => { - web_sys::console::warn_1(&wasm_bindgen::JsValue::from_str(&err.to_string())) - } - } + ws.send_with_str(serde_json::to_string(&err).unwrap().as_str()) + .unwrap(); } }); }