Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: improve rust-analyzer auto-completion #1782

Merged
merged 3 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 64 additions & 2 deletions leptos_macro/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
use syn::{
parse::Parse, parse_quote, spanned::Spanned,
AngleBracketedGenericArguments, Attribute, FnArg, GenericArgument, Item,
ItemFn, LitStr, Meta, Pat, PatIdent, Path, PathArguments, ReturnType, Stmt,
Type, TypePath, Visibility,
ItemFn, LitStr, Meta, Pat, PatIdent, Path, PathArguments, ReturnType,
Signature, Stmt, Type, TypePath, Visibility,
};

pub struct Model {
is_transparent: bool,
is_island: bool,
Expand Down Expand Up @@ -540,6 +541,67 @@ impl Model {
}
}

/// A model that is more lenient in case of a syntax error in the function body,
/// but does not actually implement the behavior of the real model. This is
/// used to improve IDEs and rust-analyzer's auto-completion behavior in case
/// of a syntax error.
pub struct DummyModel {
attrs: Vec<Attribute>,
vis: Visibility,
sig: Signature,
body: TokenStream,
}

impl Parse for DummyModel {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;

// The body is left untouched, so it will not cause an error
// even if the syntax is invalid.
let body: TokenStream = input.parse()?;

Ok(Self {
attrs,
vis,
sig,
body,
})
}
}

impl ToTokens for DummyModel {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
vis,
sig,
body,
} = self;

// Strip attributes like documentation comments and #[prop]
// from the signature, so as to not confuse the user with incorrect
// error messages.
let sig = {
let mut sig = sig.clone();
sig.inputs.iter_mut().for_each(|arg| {
if let FnArg::Typed(ty) = arg {
ty.attrs.clear();
}
});
sig
};

let output = quote! {
#(#attrs)*
#vis #sig #body
};

tokens.append_all(output)
}
}

struct Prop {
docs: Docs,
prop_opts: PropOpt,
Expand Down
19 changes: 15 additions & 4 deletions leptos_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,10 +597,21 @@ pub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
false
};

parse_macro_input!(s as component::Model)
.is_transparent(is_transparent)
.into_token_stream()
.into()
let parse_result = syn::parse::<component::Model>(s.clone());

if let Ok(model) = parse_result {
model
.is_transparent(is_transparent)
.into_token_stream()
.into()
} else {
// When the input syntax is invalid, e.g. while typing, we let
// the dummy model output tokens similar to the input, which improves
// IDEs and rust-analyzer's auto-complete capabilities.
parse_macro_input!(s as component::DummyModel)
.into_token_stream()
.into()
}
}

/// Defines a component as an interactive island when you are using the
Expand Down
11 changes: 6 additions & 5 deletions leptos_macro/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ pub fn server_impl(
args: proc_macro::TokenStream,
s: TokenStream,
) -> TokenStream {
let function: syn::ItemFn =
match syn::parse(s).map_err(|e| e.to_compile_error()) {
Ok(f) => f,
Err(e) => return e.into(),
};
let function: syn::ItemFn = match syn::parse(s.clone()) {
Ok(f) => f,
// Returning the original input stream in the case of a parsing
// error helps IDEs and rust-analyzer with auto-completion.
Err(_) => return s,
};
let ItemFn {
attrs,
vis,
Expand Down