component.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. use crate::{ifmt_to_string, writer::Location, Writer};
  2. use dioxus_rsx::*;
  3. use quote::ToTokens;
  4. use std::fmt::{Result, Write};
  5. use syn::{spanned::Spanned, AngleBracketedGenericArguments};
  6. #[derive(Debug)]
  7. enum ShortOptimization {
  8. // Special because we want to print the closing bracket immediately
  9. Empty,
  10. // Special optimization to put everything on the same line
  11. Oneliner,
  12. // Optimization where children flow but props remain fixed on top
  13. PropsOnTop,
  14. // The noisiest optimization where everything flows
  15. NoOpt,
  16. }
  17. impl Writer<'_> {
  18. pub fn write_component(
  19. &mut self,
  20. Component {
  21. name,
  22. fields,
  23. children,
  24. manual_props,
  25. prop_gen_args,
  26. ..
  27. }: &Component,
  28. ) -> Result {
  29. self.write_component_name(name, prop_gen_args)?;
  30. // decide if we have any special optimizations
  31. // Default with none, opt the cases in one-by-one
  32. let mut opt_level = ShortOptimization::NoOpt;
  33. // check if we have a lot of attributes
  34. let attr_len = self.field_len(fields, manual_props);
  35. let is_short_attr_list = attr_len < 80;
  36. let is_small_children = self.is_short_children(children).is_some();
  37. // if we have few attributes and a lot of children, place the attrs on top
  38. if is_short_attr_list && !is_small_children {
  39. opt_level = ShortOptimization::PropsOnTop;
  40. }
  41. // even if the attr is long, it should be put on one line
  42. if !is_short_attr_list && (fields.len() <= 1 && manual_props.is_none()) {
  43. if children.is_empty() {
  44. opt_level = ShortOptimization::Oneliner;
  45. } else {
  46. opt_level = ShortOptimization::PropsOnTop;
  47. }
  48. }
  49. // if we have few children and few attributes, make it a one-liner
  50. if is_short_attr_list && is_small_children {
  51. opt_level = ShortOptimization::Oneliner;
  52. }
  53. // If there's nothing at all, empty optimization
  54. if fields.is_empty() && children.is_empty() && manual_props.is_none() {
  55. opt_level = ShortOptimization::Empty;
  56. }
  57. // multiline handlers bump everything down
  58. if attr_len > 1000 {
  59. opt_level = ShortOptimization::NoOpt;
  60. }
  61. // Useful for debugging
  62. // dbg!(
  63. // name.to_token_stream().to_string(),
  64. // &opt_level,
  65. // attr_len,
  66. // is_short_attr_list,
  67. // is_small_children
  68. // );
  69. match opt_level {
  70. ShortOptimization::Empty => {}
  71. ShortOptimization::Oneliner => {
  72. write!(self.out, " ")?;
  73. self.write_component_fields(fields, manual_props, true)?;
  74. if !children.is_empty() && !fields.is_empty() {
  75. write!(self.out, ", ")?;
  76. }
  77. for child in children {
  78. self.write_ident(child)?;
  79. }
  80. write!(self.out, " ")?;
  81. }
  82. ShortOptimization::PropsOnTop => {
  83. write!(self.out, " ")?;
  84. self.write_component_fields(fields, manual_props, true)?;
  85. if !children.is_empty() && !fields.is_empty() {
  86. write!(self.out, ",")?;
  87. }
  88. self.write_body_indented(children)?;
  89. self.out.tabbed_line()?;
  90. }
  91. ShortOptimization::NoOpt => {
  92. self.write_component_fields(fields, manual_props, false)?;
  93. if !children.is_empty() && !fields.is_empty() {
  94. write!(self.out, ",")?;
  95. }
  96. self.write_body_indented(children)?;
  97. self.out.tabbed_line()?;
  98. }
  99. }
  100. write!(self.out, "}}")?;
  101. Ok(())
  102. }
  103. fn write_component_name(
  104. &mut self,
  105. name: &syn::Path,
  106. generics: &Option<AngleBracketedGenericArguments>,
  107. ) -> Result {
  108. let mut name = name.to_token_stream().to_string();
  109. name.retain(|c| !c.is_whitespace());
  110. write!(self.out, "{name}")?;
  111. if let Some(generics) = generics {
  112. let mut written = generics.to_token_stream().to_string();
  113. written.retain(|c| !c.is_whitespace());
  114. write!(self.out, "{written}")?;
  115. }
  116. write!(self.out, " {{")?;
  117. Ok(())
  118. }
  119. fn write_component_fields(
  120. &mut self,
  121. fields: &[ComponentField],
  122. manual_props: &Option<syn::Expr>,
  123. sameline: bool,
  124. ) -> Result {
  125. let mut field_iter = fields.iter().peekable();
  126. while let Some(field) = field_iter.next() {
  127. if !sameline {
  128. self.out.indented_tabbed_line()?;
  129. }
  130. let name = &field.name;
  131. match &field.content {
  132. ContentField::ManExpr(exp) => {
  133. let out = prettyplease::unparse_expr(exp);
  134. write!(self.out, "{name}: {out}")?;
  135. }
  136. ContentField::Formatted(s) => {
  137. write!(
  138. self.out,
  139. "{}: {}",
  140. name,
  141. s.source.as_ref().unwrap().to_token_stream()
  142. )?;
  143. }
  144. ContentField::OnHandlerRaw(exp) => {
  145. let out = prettyplease::unparse_expr(exp);
  146. let mut lines = out.split('\n').peekable();
  147. let first = lines.next().unwrap();
  148. write!(self.out, "{name}: {first}")?;
  149. for line in lines {
  150. self.out.new_line()?;
  151. self.out.indented_tab()?;
  152. write!(self.out, "{line}")?;
  153. }
  154. }
  155. }
  156. if field_iter.peek().is_some() || manual_props.is_some() {
  157. write!(self.out, ",")?;
  158. if sameline {
  159. write!(self.out, " ")?;
  160. }
  161. }
  162. }
  163. if let Some(exp) = manual_props {
  164. if !sameline {
  165. self.out.indented_tabbed_line()?;
  166. }
  167. self.write_manual_props(exp)?;
  168. }
  169. Ok(())
  170. }
  171. pub fn field_len(
  172. &mut self,
  173. fields: &[ComponentField],
  174. manual_props: &Option<syn::Expr>,
  175. ) -> usize {
  176. let attr_len = fields
  177. .iter()
  178. .map(|field| match &field.content {
  179. ContentField::Formatted(s) => ifmt_to_string(s).len() ,
  180. ContentField::OnHandlerRaw(exp) | ContentField::ManExpr(exp) => {
  181. let formatted = prettyplease::unparse_expr(exp);
  182. let len = if formatted.contains('\n') {
  183. 10000
  184. } else {
  185. formatted.len()
  186. };
  187. self.cached_formats.insert(Location::new(exp.span().start()) , formatted);
  188. len
  189. },
  190. } + 10)
  191. .sum::<usize>();
  192. match manual_props {
  193. Some(p) => {
  194. let content = prettyplease::unparse_expr(p);
  195. if content.len() + attr_len > 80 {
  196. return 100000;
  197. }
  198. let mut lines = content.lines();
  199. lines.next().unwrap();
  200. if lines.next().is_none() {
  201. attr_len + content.len()
  202. } else {
  203. 100000
  204. }
  205. }
  206. None => attr_len,
  207. }
  208. }
  209. fn write_manual_props(&mut self, exp: &syn::Expr) -> Result {
  210. /*
  211. We want to normalize the expr to the appropriate indent level.
  212. */
  213. let formatted = prettyplease::unparse_expr(exp);
  214. let mut lines = formatted.lines();
  215. let first_line = lines.next().unwrap();
  216. write!(self.out, "..{first_line}")?;
  217. for line in lines {
  218. self.out.indented_tabbed_line()?;
  219. write!(self.out, "{line}")?;
  220. }
  221. Ok(())
  222. }
  223. }