writer.rs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. use dioxus_rsx::{AttributeType, BodyNode, ElementAttrValue, ForLoop, IfChain};
  2. use proc_macro2::{LineColumn, Span};
  3. use quote::ToTokens;
  4. use std::{
  5. collections::{HashMap, VecDeque},
  6. fmt::{Result, Write},
  7. };
  8. use syn::{spanned::Spanned, Expr};
  9. use crate::buffer::Buffer;
  10. use crate::ifmt_to_string;
  11. #[derive(Debug)]
  12. pub struct Writer<'a> {
  13. pub raw_src: &'a str,
  14. pub src: Vec<&'a str>,
  15. pub cached_formats: HashMap<Location, String>,
  16. pub comments: VecDeque<usize>,
  17. pub out: Buffer,
  18. }
  19. #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
  20. pub struct Location {
  21. pub line: usize,
  22. pub col: usize,
  23. }
  24. impl Location {
  25. pub fn new(start: LineColumn) -> Self {
  26. Self {
  27. line: start.line,
  28. col: start.column,
  29. }
  30. }
  31. }
  32. impl<'a> Writer<'a> {
  33. pub fn new(raw_src: &'a str) -> Self {
  34. let src = raw_src.lines().collect();
  35. Self {
  36. raw_src,
  37. src,
  38. cached_formats: HashMap::new(),
  39. comments: VecDeque::new(),
  40. out: Buffer::default(),
  41. }
  42. }
  43. // Expects to be written directly into place
  44. pub fn write_ident(&mut self, node: &BodyNode) -> Result {
  45. match node {
  46. BodyNode::Element(el) => self.write_element(el),
  47. BodyNode::Component(component) => self.write_component(component),
  48. BodyNode::Text(text) => self.out.write_text(text),
  49. BodyNode::RawExpr(exp) => self.write_raw_expr(exp.span()),
  50. BodyNode::ForLoop(forloop) => self.write_for_loop(forloop),
  51. BodyNode::IfChain(ifchain) => self.write_if_chain(ifchain),
  52. }
  53. }
  54. pub fn consume(self) -> Option<String> {
  55. Some(self.out.buf)
  56. }
  57. pub fn write_comments(&mut self, child: Span) -> Result {
  58. // collect all comments upwards
  59. let start = child.start();
  60. let line_start = start.line - 1;
  61. for (id, line) in self.src[..line_start].iter().enumerate().rev() {
  62. if line.trim().starts_with("//") || line.is_empty() {
  63. if id != 0 {
  64. self.comments.push_front(id);
  65. }
  66. } else {
  67. break;
  68. }
  69. }
  70. let mut last_was_empty = false;
  71. while let Some(comment_line) = self.comments.pop_front() {
  72. let line = &self.src[comment_line];
  73. if line.is_empty() {
  74. if !last_was_empty {
  75. self.out.new_line()?;
  76. }
  77. last_was_empty = true;
  78. } else {
  79. last_was_empty = false;
  80. self.out.tabbed_line()?;
  81. write!(self.out, "{}", self.src[comment_line].trim())?;
  82. }
  83. }
  84. Ok(())
  85. }
  86. // Push out the indent level and write each component, line by line
  87. pub fn write_body_indented(&mut self, children: &[BodyNode]) -> Result {
  88. self.out.indent_level += 1;
  89. self.write_body_no_indent(children)?;
  90. self.out.indent_level -= 1;
  91. Ok(())
  92. }
  93. pub fn write_body_no_indent(&mut self, children: &[BodyNode]) -> Result {
  94. let last_child = children.len();
  95. let iter = children.iter().peekable().enumerate();
  96. for (idx, child) in iter {
  97. if self.current_span_is_primary(child.span()) {
  98. self.write_comments(child.span())?;
  99. }
  100. match child {
  101. // check if the expr is a short
  102. BodyNode::RawExpr { .. } => {
  103. self.out.tabbed_line()?;
  104. self.write_ident(child)?;
  105. if idx != last_child - 1 {
  106. write!(self.out, ",")?;
  107. }
  108. }
  109. _ => {
  110. self.out.tabbed_line()?;
  111. self.write_ident(child)?;
  112. }
  113. }
  114. }
  115. Ok(())
  116. }
  117. pub(crate) fn attr_value_len(&mut self, value: &ElementAttrValue) -> usize {
  118. match value {
  119. ElementAttrValue::AttrOptionalExpr { condition, value } => {
  120. let condition_len = self.retrieve_formatted_expr(condition).len();
  121. let value_len = self.attr_value_len(value);
  122. condition_len + value_len + 6
  123. }
  124. ElementAttrValue::AttrLiteral(lit) => ifmt_to_string(lit).len(),
  125. ElementAttrValue::AttrExpr(expr) => expr.span().line_length(),
  126. ElementAttrValue::Shorthand(expr) => expr.span().line_length(),
  127. ElementAttrValue::EventTokens(tokens) => {
  128. let location = Location::new(tokens.span().start());
  129. let len = if let std::collections::hash_map::Entry::Vacant(e) =
  130. self.cached_formats.entry(location)
  131. {
  132. let formatted = prettyplease::unparse_expr(tokens);
  133. let len = if formatted.contains('\n') {
  134. 10000
  135. } else {
  136. formatted.len()
  137. };
  138. e.insert(formatted);
  139. len
  140. } else {
  141. self.cached_formats[&location].len()
  142. };
  143. len
  144. }
  145. }
  146. }
  147. pub(crate) fn is_short_attrs(&mut self, attributes: &[AttributeType]) -> usize {
  148. let mut total = 0;
  149. for attr in attributes {
  150. if self.current_span_is_primary(attr.start()) {
  151. 'line: for line in self.src[..attr.start().start().line - 1].iter().rev() {
  152. match (line.trim().starts_with("//"), line.is_empty()) {
  153. (true, _) => return 100000,
  154. (_, true) => continue 'line,
  155. _ => break 'line,
  156. }
  157. }
  158. }
  159. match attr {
  160. AttributeType::Named(attr) => {
  161. let name_len = match &attr.attr.name {
  162. dioxus_rsx::ElementAttrName::BuiltIn(name) => {
  163. let name = name.to_string();
  164. name.len()
  165. }
  166. dioxus_rsx::ElementAttrName::Custom(name) => name.value().len() + 2,
  167. };
  168. total += name_len;
  169. total += self.attr_value_len(&attr.attr.value);
  170. }
  171. AttributeType::Spread(expr) => {
  172. let expr_len = self.retrieve_formatted_expr(expr).len();
  173. total += expr_len + 3;
  174. }
  175. };
  176. total += 6;
  177. }
  178. total
  179. }
  180. pub fn retrieve_formatted_expr(&mut self, expr: &Expr) -> &str {
  181. self.cached_formats
  182. .entry(Location::new(expr.span().start()))
  183. .or_insert_with(|| prettyplease::unparse_expr(expr))
  184. .as_str()
  185. }
  186. fn write_for_loop(&mut self, forloop: &ForLoop) -> std::fmt::Result {
  187. write!(
  188. self.out,
  189. "for {} in {} {{",
  190. forloop.pat.clone().into_token_stream(),
  191. prettyplease::unparse_expr(&forloop.expr)
  192. )?;
  193. if forloop.body.is_empty() {
  194. write!(self.out, "}}")?;
  195. return Ok(());
  196. }
  197. self.write_body_indented(&forloop.body)?;
  198. self.out.tabbed_line()?;
  199. write!(self.out, "}}")?;
  200. Ok(())
  201. }
  202. fn write_if_chain(&mut self, ifchain: &IfChain) -> std::fmt::Result {
  203. // Recurse in place by setting the next chain
  204. let mut branch = Some(ifchain);
  205. while let Some(chain) = branch {
  206. let IfChain {
  207. if_token,
  208. cond,
  209. then_branch,
  210. else_if_branch,
  211. else_branch,
  212. } = chain;
  213. write!(
  214. self.out,
  215. "{} {} {{",
  216. if_token.to_token_stream(),
  217. prettyplease::unparse_expr(cond)
  218. )?;
  219. self.write_body_indented(then_branch)?;
  220. if let Some(else_if_branch) = else_if_branch {
  221. // write the closing bracket and else
  222. self.out.tabbed_line()?;
  223. write!(self.out, "}} else ")?;
  224. branch = Some(else_if_branch);
  225. } else if let Some(else_branch) = else_branch {
  226. self.out.tabbed_line()?;
  227. write!(self.out, "}} else {{")?;
  228. self.write_body_indented(else_branch)?;
  229. branch = None;
  230. } else {
  231. branch = None;
  232. }
  233. }
  234. self.out.tabbed_line()?;
  235. write!(self.out, "}}")?;
  236. Ok(())
  237. }
  238. }
  239. pub(crate) trait SpanLength {
  240. fn line_length(&self) -> usize;
  241. }
  242. impl SpanLength for Span {
  243. fn line_length(&self) -> usize {
  244. self.end().line - self.start().line
  245. }
  246. }