component.rs 7.5 KB

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