123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- use crate::{ifmt_to_string, writer::Location, Writer};
- use dioxus_rsx::*;
- use quote::ToTokens;
- use std::fmt::{Result, Write};
- use syn::{spanned::Spanned, AngleBracketedGenericArguments};
- #[derive(Debug)]
- enum ShortOptimization {
- // Special because we want to print the closing bracket immediately
- Empty,
- // Special optimization to put everything on the same line
- Oneliner,
- // Optimization where children flow but props remain fixed on top
- PropsOnTop,
- // The noisiest optimization where everything flows
- NoOpt,
- }
- impl Writer<'_> {
- pub fn write_component(
- &mut self,
- Component {
- name,
- fields,
- children,
- manual_props,
- prop_gen_args,
- ..
- }: &Component,
- ) -> Result {
- self.write_component_name(name, prop_gen_args)?;
- // decide if we have any special optimizations
- // Default with none, opt the cases in one-by-one
- let mut opt_level = ShortOptimization::NoOpt;
- // check if we have a lot of attributes
- let attr_len = self.field_len(fields, manual_props);
- let is_short_attr_list = attr_len < 80;
- let is_small_children = self.is_short_children(children).is_some();
- // if we have few attributes and a lot of children, place the attrs on top
- if is_short_attr_list && !is_small_children {
- opt_level = ShortOptimization::PropsOnTop;
- }
- // even if the attr is long, it should be put on one line
- if !is_short_attr_list && (fields.len() <= 1 && manual_props.is_none()) {
- if children.is_empty() {
- opt_level = ShortOptimization::Oneliner;
- } else {
- opt_level = ShortOptimization::PropsOnTop;
- }
- }
- // if we have few children and few attributes, make it a one-liner
- if is_short_attr_list && is_small_children {
- opt_level = ShortOptimization::Oneliner;
- }
- // If there's nothing at all, empty optimization
- if fields.is_empty() && children.is_empty() && manual_props.is_none() {
- opt_level = ShortOptimization::Empty;
- }
- // multiline handlers bump everything down
- if attr_len > 1000 || self.out.indent.split_line_attributes() {
- opt_level = ShortOptimization::NoOpt;
- }
- // Useful for debugging
- // dbg!(
- // name.to_token_stream().to_string(),
- // &opt_level,
- // attr_len,
- // is_short_attr_list,
- // is_small_children
- // );
- match opt_level {
- ShortOptimization::Empty => {}
- ShortOptimization::Oneliner => {
- write!(self.out, " ")?;
- self.write_component_fields(fields, manual_props, true)?;
- if !children.is_empty() && !fields.is_empty() {
- write!(self.out, ", ")?;
- }
- for child in children {
- self.write_ident(child)?;
- }
- write!(self.out, " ")?;
- }
- ShortOptimization::PropsOnTop => {
- write!(self.out, " ")?;
- self.write_component_fields(fields, manual_props, true)?;
- if !children.is_empty() && !fields.is_empty() {
- write!(self.out, ",")?;
- }
- self.write_body_indented(children)?;
- self.out.tabbed_line()?;
- }
- ShortOptimization::NoOpt => {
- self.write_component_fields(fields, manual_props, false)?;
- if !children.is_empty() && !fields.is_empty() {
- write!(self.out, ",")?;
- }
- self.write_body_indented(children)?;
- self.out.tabbed_line()?;
- }
- }
- write!(self.out, "}}")?;
- Ok(())
- }
- fn write_component_name(
- &mut self,
- name: &syn::Path,
- generics: &Option<AngleBracketedGenericArguments>,
- ) -> Result {
- let mut name = name.to_token_stream().to_string();
- name.retain(|c| !c.is_whitespace());
- write!(self.out, "{name}")?;
- if let Some(generics) = generics {
- let mut written = generics.to_token_stream().to_string();
- written.retain(|c| !c.is_whitespace());
- write!(self.out, "{written}")?;
- }
- write!(self.out, " {{")?;
- Ok(())
- }
- fn write_component_fields(
- &mut self,
- fields: &[ComponentField],
- manual_props: &Option<syn::Expr>,
- sameline: bool,
- ) -> Result {
- let mut field_iter = fields.iter().peekable();
- while let Some(field) = field_iter.next() {
- if !sameline {
- self.out.indented_tabbed_line().unwrap();
- }
- let name = &field.name;
- match &field.content {
- ContentField::ManExpr(exp) => {
- let out = prettyplease::unparse_expr(exp);
- let mut lines = out.split('\n').peekable();
- let first = lines.next().unwrap();
- write!(self.out, "{name}: {first}")?;
- for line in lines {
- self.out.new_line()?;
- self.out.indented_tab()?;
- write!(self.out, "{line}")?;
- }
- }
- ContentField::Formatted(s) => {
- write!(
- self.out,
- "{}: {}",
- name,
- s.source.as_ref().unwrap().to_token_stream()
- )?;
- }
- ContentField::OnHandlerRaw(exp) => {
- let out = prettyplease::unparse_expr(exp);
- let mut lines = out.split('\n').peekable();
- let first = lines.next().unwrap();
- write!(self.out, "{name}: {first}")?;
- for line in lines {
- self.out.new_line()?;
- self.out.indented_tab()?;
- write!(self.out, "{line}")?;
- }
- }
- }
- if field_iter.peek().is_some() || manual_props.is_some() {
- write!(self.out, ",")?;
- if sameline {
- write!(self.out, " ")?;
- }
- }
- }
- if let Some(exp) = manual_props {
- if !sameline {
- self.out.indented_tabbed_line().unwrap();
- }
- self.write_manual_props(exp)?;
- }
- Ok(())
- }
- pub fn field_len(
- &mut self,
- fields: &[ComponentField],
- manual_props: &Option<syn::Expr>,
- ) -> usize {
- let attr_len = fields
- .iter()
- .map(|field| match &field.content {
- ContentField::Formatted(s) => ifmt_to_string(s).len() ,
- ContentField::OnHandlerRaw(exp) | ContentField::ManExpr(exp) => {
- let formatted = prettyplease::unparse_expr(exp);
- let len = if formatted.contains('\n') {
- 10000
- } else {
- formatted.len()
- };
- self.cached_formats.insert(Location::new(exp.span().start()) , formatted);
- len
- },
- } + 10)
- .sum::<usize>();
- match manual_props {
- Some(p) => {
- let content = prettyplease::unparse_expr(p);
- if content.len() + attr_len > 80 {
- return 100000;
- }
- let mut lines = content.lines();
- lines.next().unwrap();
- if lines.next().is_none() {
- attr_len + content.len()
- } else {
- 100000
- }
- }
- None => attr_len,
- }
- }
- fn write_manual_props(&mut self, exp: &syn::Expr) -> Result {
- /*
- We want to normalize the expr to the appropriate indent level.
- */
- let formatted = prettyplease::unparse_expr(exp);
- let mut lines = formatted.lines();
- let first_line = lines.next().unwrap();
- write!(self.out, "..{first_line}")?;
- for line in lines {
- self.out.indented_tabbed_line()?;
- write!(self.out, "{line}")?;
- }
- Ok(())
- }
- }
|