writer.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed, ForLoop};
  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, ExprIf};
  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 += 1;
  89. self.write_body_no_indent(children)?;
  90. self.out.indent -= 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 is_short_attrs(&mut self, attributes: &[ElementAttrNamed]) -> usize {
  118. let mut total = 0;
  119. for attr in attributes {
  120. if self.current_span_is_primary(attr.attr.start()) {
  121. 'line: for line in self.src[..attr.attr.start().start().line - 1].iter().rev() {
  122. match (line.trim().starts_with("//"), line.is_empty()) {
  123. (true, _) => return 100000,
  124. (_, true) => continue 'line,
  125. _ => break 'line,
  126. }
  127. }
  128. }
  129. total += match &attr.attr {
  130. ElementAttr::AttrText { value, name } => {
  131. ifmt_to_string(value).len() + name.span().line_length() + 6
  132. }
  133. ElementAttr::AttrExpression { name, value } => {
  134. value.span().line_length() + name.span().line_length() + 6
  135. }
  136. ElementAttr::CustomAttrText { value, name } => {
  137. ifmt_to_string(value).len() + name.to_token_stream().to_string().len() + 6
  138. }
  139. ElementAttr::CustomAttrExpression { name, value } => {
  140. name.to_token_stream().to_string().len() + value.span().line_length() + 6
  141. }
  142. ElementAttr::EventTokens { tokens, name } => {
  143. let location = Location::new(tokens.span().start());
  144. let len = if let std::collections::hash_map::Entry::Vacant(e) =
  145. self.cached_formats.entry(location)
  146. {
  147. let formatted = prettyplease::unparse_expr(tokens);
  148. let len = if formatted.contains('\n') {
  149. 10000
  150. } else {
  151. formatted.len()
  152. };
  153. e.insert(formatted);
  154. len
  155. } else {
  156. self.cached_formats[&location].len()
  157. };
  158. len + name.span().line_length() + 6
  159. }
  160. };
  161. }
  162. total
  163. }
  164. pub fn retrieve_formatted_expr(&mut self, expr: &Expr) -> &str {
  165. self.cached_formats
  166. .entry(Location::new(expr.span().start()))
  167. .or_insert_with(|| prettyplease::unparse_expr(expr))
  168. .as_str()
  169. }
  170. fn write_for_loop(&mut self, forloop: &ForLoop) -> std::fmt::Result {
  171. write!(
  172. self.out,
  173. "for {} in {} {{",
  174. forloop.pat.clone().into_token_stream(),
  175. prettyplease::unparse_expr(&forloop.expr)
  176. )?;
  177. if forloop.body.is_empty() {
  178. write!(self.out, "}}")?;
  179. return Ok(());
  180. }
  181. self.write_body_indented(&forloop.body)?;
  182. self.out.tabbed_line()?;
  183. write!(self.out, "}}")?;
  184. Ok(())
  185. }
  186. fn write_if_chain(&mut self, ifchain: &ExprIf) -> std::fmt::Result {
  187. self.write_raw_expr(ifchain.span())
  188. }
  189. }
  190. trait SpanLength {
  191. fn line_length(&self) -> usize;
  192. }
  193. impl SpanLength for Span {
  194. fn line_length(&self) -> usize {
  195. self.end().line - self.start().line
  196. }
  197. }