buffer.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. use std::{
  2. collections::{HashMap, VecDeque},
  3. fmt::{Result, Write},
  4. rc::Rc,
  5. };
  6. use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed};
  7. use proc_macro2::{LineColumn, Span};
  8. use syn::{spanned::Spanned, Expr};
  9. #[derive(Default, Debug)]
  10. pub struct Buffer {
  11. pub src: Vec<String>,
  12. pub cached_formats: HashMap<Location, String>,
  13. pub buf: String,
  14. pub indent: usize,
  15. pub comments: VecDeque<usize>,
  16. }
  17. #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
  18. pub struct Location {
  19. pub line: usize,
  20. pub col: usize,
  21. }
  22. impl Location {
  23. pub fn new(start: LineColumn) -> Self {
  24. Self {
  25. line: start.line,
  26. col: start.column,
  27. }
  28. }
  29. }
  30. impl Buffer {
  31. // Create a new line and tab it to the current tab level
  32. pub fn tabbed_line(&mut self) -> Result {
  33. self.new_line()?;
  34. self.tab()
  35. }
  36. // Create a new line and tab it to the current tab level
  37. pub fn indented_tabbed_line(&mut self) -> Result {
  38. self.new_line()?;
  39. self.indented_tab()
  40. }
  41. pub fn tab(&mut self) -> Result {
  42. self.write_tabs(self.indent)
  43. }
  44. pub fn indented_tab(&mut self) -> Result {
  45. self.write_tabs(self.indent + 1)
  46. }
  47. pub fn write_tabs(&mut self, num: usize) -> std::fmt::Result {
  48. for _ in 0..num {
  49. write!(self.buf, " ")?
  50. }
  51. Ok(())
  52. }
  53. pub fn new_line(&mut self) -> Result {
  54. writeln!(self.buf)
  55. }
  56. // Expects to be written directly into place
  57. pub fn write_ident(&mut self, node: &BodyNode) -> Result {
  58. match node {
  59. BodyNode::Element(el) => self.write_element(el),
  60. BodyNode::Component(component) => self.write_component(component),
  61. BodyNode::Text(text) => self.write_text(text),
  62. BodyNode::RawExpr(exp) => self.write_raw_expr(exp),
  63. }
  64. }
  65. pub fn write_text(&mut self, text: &syn::LitStr) -> Result {
  66. write!(self.buf, "\"{}\"", text.value())
  67. }
  68. pub fn consume(self) -> Option<String> {
  69. Some(self.buf)
  70. }
  71. pub fn write_comments(&mut self, child: Span) -> Result {
  72. // collect all comments upwards
  73. let start = child.start();
  74. let line_start = start.line - 1;
  75. for (id, line) in self.src[..line_start].iter().enumerate().rev() {
  76. if line.trim().starts_with("//") || line.is_empty() {
  77. if id != 0 {
  78. self.comments.push_front(id);
  79. }
  80. } else {
  81. break;
  82. }
  83. }
  84. let mut last_was_empty = false;
  85. while let Some(comment_line) = self.comments.pop_front() {
  86. let line = &self.src[comment_line];
  87. if line.is_empty() {
  88. if !last_was_empty {
  89. self.new_line()?;
  90. }
  91. last_was_empty = true;
  92. } else {
  93. last_was_empty = false;
  94. self.tabbed_line()?;
  95. write!(self.buf, "{}", self.src[comment_line].trim())?;
  96. }
  97. }
  98. Ok(())
  99. }
  100. // Push out the indent level and write each component, line by line
  101. pub fn write_body_indented(&mut self, children: &[BodyNode]) -> Result {
  102. self.indent += 1;
  103. for child in children {
  104. // Exprs handle their own indenting/line breaks
  105. if !matches!(child, BodyNode::RawExpr(_)) {
  106. self.write_comments(child.span())?;
  107. self.tabbed_line()?;
  108. }
  109. self.write_ident(child)?;
  110. }
  111. self.indent -= 1;
  112. Ok(())
  113. }
  114. pub(crate) fn is_short_attrs(&mut self, attributes: &[ElementAttrNamed]) -> usize {
  115. let mut total = 0;
  116. for attr in attributes {
  117. if self.current_element_has_comments(attr.span()) {
  118. return 100000;
  119. }
  120. total += match &attr.attr {
  121. ElementAttr::AttrText { value, name } => {
  122. value.value().len() + name.span().line_length() + 3
  123. }
  124. ElementAttr::AttrExpression { name, value } => {
  125. value.span().line_length() + name.span().line_length() + 3
  126. }
  127. ElementAttr::CustomAttrText { value, name } => {
  128. value.value().len() + name.value().len() + 3
  129. }
  130. ElementAttr::CustomAttrExpression { name, value } => {
  131. name.value().len() + value.span().line_length() + 3
  132. }
  133. ElementAttr::EventTokens { tokens, name } => {
  134. let location = Location::new(tokens.span().start());
  135. let len = if let std::collections::hash_map::Entry::Vacant(e) =
  136. self.cached_formats.entry(location)
  137. {
  138. let formatted = prettyplease::unparse_expr(tokens);
  139. let len = if formatted.contains('\n') {
  140. 10000
  141. } else {
  142. formatted.len()
  143. };
  144. e.insert(formatted);
  145. len
  146. } else {
  147. self.cached_formats[&location].len()
  148. };
  149. len + name.span().line_length() + 3
  150. }
  151. };
  152. }
  153. total
  154. }
  155. pub fn retrieve_formatted_expr(&mut self, expr: &Expr) -> &str {
  156. self.cached_formats
  157. .entry(Location::new(expr.span().start()))
  158. .or_insert(prettyplease::unparse_expr(expr))
  159. .as_str()
  160. }
  161. }
  162. trait SpanLength {
  163. fn line_length(&self) -> usize;
  164. }
  165. impl SpanLength for Span {
  166. fn line_length(&self) -> usize {
  167. self.end().line - self.start().line
  168. }
  169. }