1
0

component.rs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 || self.out.indent.split_line_attributes() {
  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().unwrap();
  129. }
  130. let name = &field.name;
  131. match &field.content {
  132. ContentField::ManExpr(exp) => {
  133. let out = prettyplease::unparse_expr(exp);
  134. let mut lines = out.split('\n').peekable();
  135. let first = lines.next().unwrap();
  136. write!(self.out, "{name}: {first}")?;
  137. for line in lines {
  138. self.out.new_line()?;
  139. self.out.indented_tab()?;
  140. write!(self.out, "{line}")?;
  141. }
  142. }
  143. ContentField::Formatted(s) => {
  144. write!(
  145. self.out,
  146. "{}: {}",
  147. name,
  148. s.source.as_ref().unwrap().to_token_stream()
  149. )?;
  150. }
  151. ContentField::OnHandlerRaw(exp) => {
  152. let out = prettyplease::unparse_expr(exp);
  153. let mut lines = out.split('\n').peekable();
  154. let first = lines.next().unwrap();
  155. write!(self.out, "{name}: {first}")?;
  156. for line in lines {
  157. self.out.new_line()?;
  158. self.out.indented_tab()?;
  159. write!(self.out, "{line}")?;
  160. }
  161. }
  162. }
  163. if field_iter.peek().is_some() || manual_props.is_some() {
  164. write!(self.out, ",")?;
  165. if sameline {
  166. write!(self.out, " ")?;
  167. }
  168. }
  169. }
  170. if let Some(exp) = manual_props {
  171. if !sameline {
  172. self.out.indented_tabbed_line().unwrap();
  173. }
  174. self.write_manual_props(exp)?;
  175. }
  176. Ok(())
  177. }
  178. pub fn field_len(
  179. &mut self,
  180. fields: &[ComponentField],
  181. manual_props: &Option<syn::Expr>,
  182. ) -> usize {
  183. let attr_len = fields
  184. .iter()
  185. .map(|field| match &field.content {
  186. ContentField::Formatted(s) => ifmt_to_string(s).len() ,
  187. ContentField::OnHandlerRaw(exp) | ContentField::ManExpr(exp) => {
  188. let formatted = prettyplease::unparse_expr(exp);
  189. let len = if formatted.contains('\n') {
  190. 10000
  191. } else {
  192. formatted.len()
  193. };
  194. self.cached_formats.insert(Location::new(exp.span().start()) , formatted);
  195. len
  196. },
  197. } + 10)
  198. .sum::<usize>();
  199. match manual_props {
  200. Some(p) => {
  201. let content = prettyplease::unparse_expr(p);
  202. if content.len() + attr_len > 80 {
  203. return 100000;
  204. }
  205. let mut lines = content.lines();
  206. lines.next().unwrap();
  207. if lines.next().is_none() {
  208. attr_len + content.len()
  209. } else {
  210. 100000
  211. }
  212. }
  213. None => attr_len,
  214. }
  215. }
  216. fn write_manual_props(&mut self, exp: &syn::Expr) -> Result {
  217. /*
  218. We want to normalize the expr to the appropriate indent level.
  219. */
  220. let formatted = prettyplease::unparse_expr(exp);
  221. let mut lines = formatted.lines();
  222. let first_line = lines.next().unwrap();
  223. write!(self.out, "..{first_line}")?;
  224. for line in lines {
  225. self.out.indented_tabbed_line()?;
  226. write!(self.out, "{line}")?;
  227. }
  228. Ok(())
  229. }
  230. }