element.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. use crate::{ifmt_to_string, prettier_please::unparse_expr, Writer};
  2. use dioxus_rsx::*;
  3. use proc_macro2::Span;
  4. use quote::ToTokens;
  5. use std::{
  6. fmt::Result,
  7. fmt::{self, Write},
  8. };
  9. use syn::{spanned::Spanned, token::Brace, Expr};
  10. #[derive(Debug)]
  11. enum ShortOptimization {
  12. /// Special because we want to print the closing bracket immediately
  13. ///
  14. /// IE
  15. /// `div {}` instead of `div { }`
  16. Empty,
  17. /// Special optimization to put everything on the same line and add some buffer spaces
  18. ///
  19. /// IE
  20. ///
  21. /// `div { "asdasd" }` instead of a multiline variant
  22. Oneliner,
  23. /// Optimization where children flow but props remain fixed on top
  24. PropsOnTop,
  25. /// The noisiest optimization where everything flows
  26. NoOpt,
  27. }
  28. /*
  29. // whitespace
  30. div {
  31. // some whitespace
  32. class: "asdasd"
  33. // whjiot
  34. asdasd // whitespace
  35. }
  36. */
  37. impl Writer<'_> {
  38. pub fn write_element(&mut self, el: &Element) -> Result {
  39. let Element {
  40. name,
  41. key,
  42. attributes,
  43. children,
  44. brace,
  45. ..
  46. } = el;
  47. /*
  48. 1. Write the tag
  49. 2. Write the key
  50. 3. Write the attributes
  51. 4. Write the children
  52. */
  53. write!(self.out, "{name} {{")?;
  54. // decide if we have any special optimizations
  55. // Default with none, opt the cases in one-by-one
  56. let mut opt_level = ShortOptimization::NoOpt;
  57. // check if we have a lot of attributes
  58. let attr_len = self.is_short_attrs(attributes);
  59. let is_short_attr_list = (attr_len + self.out.indent_level * 4) < 80;
  60. let children_len = self.is_short_children(children);
  61. let is_small_children = children_len.is_some();
  62. // if we have few attributes and a lot of children, place the attrs on top
  63. if is_short_attr_list && !is_small_children {
  64. opt_level = ShortOptimization::PropsOnTop;
  65. }
  66. // even if the attr is long, it should be put on one line
  67. if !is_short_attr_list && attributes.len() <= 1 {
  68. if children.is_empty() {
  69. opt_level = ShortOptimization::Oneliner;
  70. } else {
  71. opt_level = ShortOptimization::PropsOnTop;
  72. }
  73. }
  74. // if we have few children and few attributes, make it a one-liner
  75. if is_short_attr_list && is_small_children {
  76. if children_len.unwrap() + attr_len + self.out.indent_level * 4 < 100 {
  77. opt_level = ShortOptimization::Oneliner;
  78. } else {
  79. opt_level = ShortOptimization::PropsOnTop;
  80. }
  81. }
  82. // If there's nothing at all, empty optimization
  83. if attributes.is_empty() && children.is_empty() && key.is_none() {
  84. opt_level = ShortOptimization::Empty;
  85. // Write comments if they exist
  86. self.write_todo_body(brace)?;
  87. }
  88. // multiline handlers bump everything down
  89. if attr_len > 1000 || self.out.indent.split_line_attributes() {
  90. opt_level = ShortOptimization::NoOpt;
  91. }
  92. match opt_level {
  93. ShortOptimization::Empty => {}
  94. ShortOptimization::Oneliner => {
  95. write!(self.out, " ")?;
  96. self.write_attributes(brace, attributes, key, true)?;
  97. if !children.is_empty() && (!attributes.is_empty() || key.is_some()) {
  98. write!(self.out, ", ")?;
  99. }
  100. for (id, child) in children.iter().enumerate() {
  101. self.write_ident(child)?;
  102. if id != children.len() - 1 && children.len() > 1 {
  103. write!(self.out, ", ")?;
  104. }
  105. }
  106. write!(self.out, " ")?;
  107. }
  108. ShortOptimization::PropsOnTop => {
  109. if !attributes.is_empty() || key.is_some() {
  110. write!(self.out, " ")?;
  111. }
  112. self.write_attributes(brace, attributes, key, true)?;
  113. if !children.is_empty() && (!attributes.is_empty() || key.is_some()) {
  114. write!(self.out, ",")?;
  115. }
  116. if !children.is_empty() {
  117. self.write_body_indented(children)?;
  118. }
  119. self.out.tabbed_line()?;
  120. }
  121. ShortOptimization::NoOpt => {
  122. self.write_attributes(brace, attributes, key, false)?;
  123. if !children.is_empty() && (!attributes.is_empty() || key.is_some()) {
  124. write!(self.out, ",")?;
  125. }
  126. if !children.is_empty() {
  127. self.write_body_indented(children)?;
  128. }
  129. self.out.tabbed_line()?;
  130. }
  131. }
  132. write!(self.out, "}}")?;
  133. Ok(())
  134. }
  135. fn write_attributes(
  136. &mut self,
  137. brace: &Brace,
  138. attributes: &[AttributeType],
  139. key: &Option<IfmtInput>,
  140. sameline: bool,
  141. ) -> Result {
  142. let mut attr_iter = attributes.iter().peekable();
  143. if let Some(key) = key {
  144. if !sameline {
  145. self.out.indented_tabbed_line()?;
  146. }
  147. write!(self.out, "key: {}", ifmt_to_string(key))?;
  148. if !attributes.is_empty() {
  149. write!(self.out, ",")?;
  150. if sameline {
  151. write!(self.out, " ")?;
  152. }
  153. }
  154. }
  155. while let Some(attr) = attr_iter.next() {
  156. self.out.indent_level += 1;
  157. if !sameline {
  158. self.write_attr_comments(brace, attr.start())?;
  159. }
  160. self.out.indent_level -= 1;
  161. if !sameline {
  162. self.out.indented_tabbed_line()?;
  163. }
  164. self.write_attribute(attr)?;
  165. if attr_iter.peek().is_some() {
  166. write!(self.out, ",")?;
  167. if sameline {
  168. write!(self.out, " ")?;
  169. }
  170. }
  171. }
  172. Ok(())
  173. }
  174. fn write_attribute_name(&mut self, attr: &ElementAttrName) -> Result {
  175. match attr {
  176. ElementAttrName::BuiltIn(name) => {
  177. write!(self.out, "{}", name)?;
  178. }
  179. ElementAttrName::Custom(name) => {
  180. write!(self.out, "{}", name.to_token_stream())?;
  181. }
  182. }
  183. Ok(())
  184. }
  185. fn write_attribute_value(&mut self, value: &ElementAttrValue) -> Result {
  186. match value {
  187. ElementAttrValue::AttrOptionalExpr { condition, value } => {
  188. write!(
  189. self.out,
  190. "if {condition} {{ ",
  191. condition = unparse_expr(condition),
  192. )?;
  193. self.write_attribute_value(value)?;
  194. write!(self.out, " }}")?;
  195. }
  196. ElementAttrValue::AttrLiteral(value) => {
  197. write!(self.out, "{value}", value = ifmt_to_string(value))?;
  198. }
  199. ElementAttrValue::Shorthand(value) => {
  200. write!(self.out, "{value}",)?;
  201. }
  202. ElementAttrValue::AttrExpr(value) => {
  203. let out = unparse_expr(value);
  204. let mut lines = out.split('\n').peekable();
  205. let first = lines.next().unwrap();
  206. // a one-liner for whatever reason
  207. // Does not need a new line
  208. if lines.peek().is_none() {
  209. write!(self.out, "{first}")?;
  210. } else {
  211. writeln!(self.out, "{first}")?;
  212. while let Some(line) = lines.next() {
  213. self.out.indented_tab()?;
  214. write!(self.out, "{line}")?;
  215. if lines.peek().is_none() {
  216. write!(self.out, "")?;
  217. } else {
  218. writeln!(self.out)?;
  219. }
  220. }
  221. }
  222. }
  223. ElementAttrValue::EventTokens(tokens) => {
  224. let out = self.retrieve_formatted_expr(tokens).to_string();
  225. let mut lines = out.split('\n').peekable();
  226. let first = lines.next().unwrap();
  227. // a one-liner for whatever reason
  228. // Does not need a new line
  229. if lines.peek().is_none() {
  230. write!(self.out, "{first}")?;
  231. } else {
  232. writeln!(self.out, "{first}")?;
  233. while let Some(line) = lines.next() {
  234. self.out.indented_tab()?;
  235. write!(self.out, "{line}")?;
  236. if lines.peek().is_none() {
  237. write!(self.out, "")?;
  238. } else {
  239. writeln!(self.out)?;
  240. }
  241. }
  242. }
  243. }
  244. }
  245. Ok(())
  246. }
  247. fn write_attribute(&mut self, attr: &AttributeType) -> Result {
  248. match attr {
  249. AttributeType::Named(attr) => self.write_named_attribute(attr),
  250. AttributeType::Spread(attr) => self.write_spread_attribute(attr),
  251. }
  252. }
  253. fn write_named_attribute(&mut self, attr: &ElementAttrNamed) -> Result {
  254. self.write_attribute_name(&attr.attr.name)?;
  255. write!(self.out, ": ")?;
  256. self.write_attribute_value(&attr.attr.value)?;
  257. Ok(())
  258. }
  259. fn write_spread_attribute(&mut self, attr: &Expr) -> Result {
  260. write!(self.out, "..")?;
  261. write!(self.out, "{}", unparse_expr(attr))?;
  262. Ok(())
  263. }
  264. // make sure the comments are actually relevant to this element.
  265. // test by making sure this element is the primary element on this line
  266. pub fn current_span_is_primary(&self, location: Span) -> bool {
  267. let start = location.start();
  268. let line_start = start.line - 1;
  269. let beginning = self
  270. .src
  271. .get(line_start)
  272. .filter(|this_line| this_line.len() > start.column)
  273. .map(|this_line| this_line[..start.column].trim())
  274. .unwrap_or_default();
  275. beginning.is_empty()
  276. }
  277. pub fn is_empty_children(&self, children: &[BodyNode]) -> bool {
  278. children.is_empty()
  279. }
  280. // check if the children are short enough to be on the same line
  281. // We don't have the notion of current line depth - each line tries to be < 80 total
  282. // returns the total line length if it's short
  283. // returns none if the length exceeds the limit
  284. // I think this eventually becomes quadratic :(
  285. pub fn is_short_children(&mut self, children: &[BodyNode]) -> Option<usize> {
  286. if children.is_empty() {
  287. // todo: allow elements with comments but no children
  288. // like div { /* comment */ }
  289. // or
  290. // div {
  291. // // some helpful
  292. // }
  293. return Some(0);
  294. }
  295. for child in children {
  296. if self.current_span_is_primary(child.span()) {
  297. 'line: for line in self.src[..child.span().start().line - 1].iter().rev() {
  298. match (line.trim().starts_with("//"), line.is_empty()) {
  299. (true, _) => return None,
  300. (_, true) => continue 'line,
  301. _ => break 'line,
  302. }
  303. }
  304. }
  305. }
  306. match children {
  307. [BodyNode::Text(ref text)] => Some(ifmt_to_string(text).len()),
  308. [BodyNode::Component(ref comp)] => {
  309. let attr_len = self.field_len(&comp.fields, &comp.manual_props);
  310. if attr_len > 80 {
  311. None
  312. } else if comp.children.is_empty() {
  313. Some(attr_len)
  314. } else {
  315. None
  316. }
  317. }
  318. // TODO: let rawexprs to be inlined
  319. [BodyNode::RawExpr(ref expr)] => get_expr_length(expr),
  320. [BodyNode::Element(ref el)] => {
  321. let attr_len = self.is_short_attrs(&el.attributes);
  322. if el.children.is_empty() && attr_len < 80 {
  323. return Some(el.name.to_string().len());
  324. }
  325. if el.children.len() == 1 {
  326. if let BodyNode::Text(ref text) = el.children[0] {
  327. let value = ifmt_to_string(text);
  328. if value.len() + el.name.to_string().len() + attr_len < 80 {
  329. return Some(value.len() + el.name.to_string().len() + attr_len);
  330. }
  331. }
  332. }
  333. None
  334. }
  335. // todo, allow non-elements to be on the same line
  336. items => {
  337. let mut total_count = 0;
  338. for item in items {
  339. match item {
  340. BodyNode::Component(_) | BodyNode::Element(_) => return None,
  341. BodyNode::Text(text) => {
  342. total_count += ifmt_to_string(text).len();
  343. }
  344. BodyNode::RawExpr(expr) => match get_expr_length(expr) {
  345. Some(len) => total_count += len,
  346. None => return None,
  347. },
  348. BodyNode::ForLoop(_forloop) => return None,
  349. BodyNode::IfChain(_chain) => return None,
  350. }
  351. }
  352. Some(total_count)
  353. }
  354. }
  355. }
  356. /// empty everything except for some comments
  357. fn write_todo_body(&mut self, brace: &Brace) -> fmt::Result {
  358. let span = brace.span.span();
  359. let start = span.start();
  360. let end = span.end();
  361. if start.line == end.line {
  362. return Ok(());
  363. }
  364. writeln!(self.out)?;
  365. for idx in start.line..end.line {
  366. let line = &self.src[idx];
  367. if line.trim().starts_with("//") {
  368. for _ in 0..self.out.indent_level + 1 {
  369. write!(self.out, " ")?
  370. }
  371. writeln!(self.out, "{}", line.trim()).unwrap();
  372. }
  373. }
  374. for _ in 0..self.out.indent_level {
  375. write!(self.out, " ")?
  376. }
  377. Ok(())
  378. }
  379. }
  380. fn get_expr_length(expr: &Expr) -> Option<usize> {
  381. let span = expr.span();
  382. let (start, end) = (span.start(), span.end());
  383. if start.line == end.line {
  384. Some(end.column - start.column)
  385. } else {
  386. None
  387. }
  388. }