writer.rs 7.1 KB

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