Autocomplete rsx (#2421)
* autocomplete rsx * hide the completions module * fix autocomplete for identifiers that start with a raw identifier * add field completion hints for props * remove extra import * Revert "add field completion hints for props" This reverts commit 194899c73783e0f9b5d601d53365746d838f2048. * fix autocomplete inside raw expressions * fix autofmt * feat: just use regular ifmt if it's compatible reuses the tokens for ifmt so RA provides its HIR lowering on simple formatted strings * fix merging ifmt strings * add a better error message for components called like functions --------- Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
1 parent
245003a5d4
commit
2523581cf4
21 files changed
+788
-217
No files matched your search
+123
-19
@@ -11,18 +11,18 @@
|
||||
//! - [ ] Keys
|
||||
//! - [ ] Properties spreading with with `..` syntax
|
||||
|
||||
use self::location::CallerLocation;
|
||||
use self::{location::CallerLocation, util::try_parse_braces};
|
||||
|
||||
use super::*;
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::{
|
||||
ext::IdentExt, parse::ParseBuffer, spanned::Spanned, token::Brace,
|
||||
AngleBracketedGenericArguments, Error, Expr, Ident, LitStr, PathArguments, Token,
|
||||
ext::IdentExt, spanned::Spanned, token::Brace, AngleBracketedGenericArguments, Error, Expr,
|
||||
Ident, LitStr, PathArguments, Token,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Component {
|
||||
pub name: syn::Path,
|
||||
pub prop_gen_args: Option<AngleBracketedGenericArguments>,
|
||||
@@ -30,8 +30,35 @@ pub struct Component {
|
||||
pub fields: Vec<ComponentField>,
|
||||
pub children: Vec<BodyNode>,
|
||||
pub manual_props: Option<Expr>,
|
||||
pub brace: syn::token::Brace,
|
||||
pub brace: Option<syn::token::Brace>,
|
||||
pub location: CallerLocation,
|
||||
errors: Vec<syn::Error>,
|
||||
}
|
||||
|
||||
impl PartialEq for Component {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
&& self.prop_gen_args == other.prop_gen_args
|
||||
&& self.key == other.key
|
||||
&& self.fields == other.fields
|
||||
&& self.children == other.children
|
||||
&& self.manual_props == other.manual_props
|
||||
&& self.brace == other.brace
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Component {}
|
||||
|
||||
impl Hash for Component {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
self.prop_gen_args.hash(state);
|
||||
self.key.hash(state);
|
||||
self.fields.hash(state);
|
||||
self.children.hash(state);
|
||||
self.manual_props.hash(state);
|
||||
self.brace.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Component {
|
||||
@@ -42,8 +69,10 @@ impl Parse for Component {
|
||||
// extract the path arguments from the path into prop_gen_args
|
||||
let prop_gen_args = normalize_path(&mut name);
|
||||
|
||||
let content: ParseBuffer;
|
||||
let brace = syn::braced!(content in stream);
|
||||
let Ok((brace, content)) = try_parse_braces(stream) else {
|
||||
// If there are no braces, this is an incomplete component. We still parse it so that we can autocomplete it, but we don't need to parse the children
|
||||
return Ok(Self::incomplete(name));
|
||||
};
|
||||
|
||||
let mut fields = Vec::new();
|
||||
let mut children = Vec::new();
|
||||
@@ -65,7 +94,7 @@ impl Parse for Component {
|
||||
&& !content.peek2(Token![:])
|
||||
&& !content.peek2(Token![-]))
|
||||
{
|
||||
// If it
|
||||
// If it is a key, make sure it isn't static and then add it to the component
|
||||
if content.fork().parse::<Ident>()? == "key" {
|
||||
_ = content.parse::<Ident>()?;
|
||||
_ = content.parse::<Token![:]>()?;
|
||||
@@ -94,8 +123,9 @@ impl Parse for Component {
|
||||
fields,
|
||||
children,
|
||||
manual_props,
|
||||
brace,
|
||||
brace: Some(brace),
|
||||
key,
|
||||
errors: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -116,19 +146,78 @@ impl ToTokens for Component {
|
||||
|
||||
let fn_name = self.fn_name();
|
||||
|
||||
tokens.append_all(quote! {
|
||||
let errors = self.errors();
|
||||
|
||||
let component_node = quote_spanned! { name.span() =>
|
||||
dioxus_core::DynamicNode::Component({
|
||||
#[allow(unused_imports)]
|
||||
use dioxus_core::prelude::Properties;
|
||||
(#builder).into_vcomponent(
|
||||
#name #prop_gen_args,
|
||||
#fn_name
|
||||
)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
let component = if errors.is_empty() {
|
||||
component_node
|
||||
} else {
|
||||
quote_spanned! {
|
||||
name.span() => {
|
||||
#errors
|
||||
#component_node
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.append_all(component);
|
||||
}
|
||||
}
|
||||
|
||||
impl Component {
|
||||
/// Create a new Component
|
||||
pub fn new(
|
||||
name: syn::Path,
|
||||
prop_gen_args: Option<AngleBracketedGenericArguments>,
|
||||
fields: Vec<ComponentField>,
|
||||
children: Vec<BodyNode>,
|
||||
manual_props: Option<Expr>,
|
||||
key: Option<IfmtInput>,
|
||||
brace: syn::token::Brace,
|
||||
) -> Self {
|
||||
Self {
|
||||
errors: vec![],
|
||||
name,
|
||||
prop_gen_args,
|
||||
fields,
|
||||
children,
|
||||
manual_props,
|
||||
brace: Some(brace),
|
||||
key,
|
||||
location: CallerLocation::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn incomplete(name: syn::Path) -> Self {
|
||||
Self {
|
||||
errors: vec![syn::Error::new(
|
||||
name.span(),
|
||||
format!(
|
||||
"Missing braces after component name `{}`",
|
||||
name.segments.last().unwrap().ident
|
||||
),
|
||||
)],
|
||||
name,
|
||||
prop_gen_args: None,
|
||||
fields: Vec::new(),
|
||||
children: Vec::new(),
|
||||
manual_props: None,
|
||||
brace: None,
|
||||
key: None,
|
||||
location: CallerLocation::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_component_path(path: &syn::Path) -> Result<()> {
|
||||
// ensure path segments doesn't have PathArguments, only the last
|
||||
// segment is allowed to have one.
|
||||
@@ -157,15 +246,18 @@ impl Component {
|
||||
}
|
||||
|
||||
fn collect_manual_props(&self, manual_props: &Expr) -> TokenStream2 {
|
||||
let mut toks = quote! { let mut __manual_props = #manual_props; };
|
||||
let mut toks =
|
||||
quote_spanned! { manual_props.span() => let mut __manual_props = #manual_props; };
|
||||
for field in &self.fields {
|
||||
if field.name == "key" {
|
||||
continue;
|
||||
}
|
||||
let ComponentField { name, content } = field;
|
||||
toks.append_all(quote! { __manual_props.#name = #content; });
|
||||
toks.append_all(
|
||||
quote_spanned! { manual_props.span() => __manual_props.#name = #content; },
|
||||
);
|
||||
}
|
||||
toks.append_all(quote! { __manual_props });
|
||||
toks.append_all(quote_spanned! { manual_props.span() => __manual_props });
|
||||
quote! {{ #toks }}
|
||||
}
|
||||
|
||||
@@ -173,23 +265,35 @@ impl Component {
|
||||
let name = &self.name;
|
||||
|
||||
let mut toks = match &self.prop_gen_args {
|
||||
Some(gen_args) => quote! { fc_to_builder(#name #gen_args) },
|
||||
None => quote! { fc_to_builder(#name) },
|
||||
Some(gen_args) => quote_spanned! { name.span() => fc_to_builder(#name #gen_args) },
|
||||
None => quote_spanned! { name.span() => fc_to_builder(#name) },
|
||||
};
|
||||
for field in &self.fields {
|
||||
toks.append_all(quote! {#field})
|
||||
}
|
||||
if !self.children.is_empty() {
|
||||
let renderer = TemplateRenderer::as_tokens(&self.children, None);
|
||||
toks.append_all(quote! { .children( { #renderer } ) });
|
||||
toks.append_all(quote_spanned! { name.span() => .children( #renderer ) });
|
||||
}
|
||||
toks.append_all(quote! { .build() });
|
||||
toks.append_all(quote_spanned! { name.span() => .build() });
|
||||
toks
|
||||
}
|
||||
|
||||
fn fn_name(&self) -> String {
|
||||
self.name.segments.last().unwrap().ident.to_string()
|
||||
}
|
||||
|
||||
/// If this element is only partially complete, return the errors that occurred during parsing
|
||||
pub(crate) fn errors(&self) -> TokenStream2 {
|
||||
let Self { errors, .. } = self;
|
||||
|
||||
let mut tokens = quote! {};
|
||||
for error in errors {
|
||||
tokens.append_all(error.to_compile_error());
|
||||
}
|
||||
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
// the struct's fields info
|
||||
|
||||
Reference in new issue
Block a user