From 4471ccba499f51e9c54f390607cd542a32a015af Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 5 Jul 2022 01:53:08 -0400 Subject: [PATCH] fix: even better comment handling --- packages/autofmt/README.md | 2 +- packages/autofmt/src/buffer.rs | 97 ++++++++++++--------- packages/autofmt/src/element.rs | 14 ++- packages/autofmt/tests/fil.rs | 10 +++ packages/autofmt/tests/sample.rs | 25 ------ packages/autofmt/tests/samples/attrs.rsx | 21 +++++ packages/autofmt/tests/samples/comments.rsx | 6 ++ packages/rsx/src/element.rs | 13 ++- 8 files changed, 115 insertions(+), 73 deletions(-) delete mode 100644 packages/autofmt/tests/sample.rs create mode 100644 packages/autofmt/tests/samples/attrs.rsx diff --git a/packages/autofmt/README.md b/packages/autofmt/README.md index 9fded3260..07036d46d 100644 --- a/packages/autofmt/README.md +++ b/packages/autofmt/README.md @@ -2,4 +2,4 @@ This crate formats rsx! by parsing call bodies and pretty-printing them back out. -It also incorporates a fork of prettyplease to allow formatting arbitrary rust code too. Prettyplease rejected a suggestion to allow arbitrary expression formatting - something our fork lets us do. + diff --git a/packages/autofmt/src/buffer.rs b/packages/autofmt/src/buffer.rs index fc45e622e..2554c8ff4 100644 --- a/packages/autofmt/src/buffer.rs +++ b/packages/autofmt/src/buffer.rs @@ -1,11 +1,12 @@ use std::{ - collections::HashMap, + collections::{HashMap, VecDeque}, fmt::{Result, Write}, + rc::Rc, }; use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed}; use proc_macro2::{LineColumn, Span}; -use syn::spanned::Spanned; +use syn::{spanned::Spanned, Expr}; #[derive(Default, Debug)] pub struct Buffer { @@ -13,6 +14,7 @@ pub struct Buffer { pub cached_formats: HashMap, pub buf: String, pub indent: usize, + pub comments: VecDeque, } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] @@ -79,46 +81,47 @@ impl Buffer { Some(self.buf) } + pub fn write_comments(&mut self, child: Span) -> Result { + // collect all comments upwards + let start = child.start(); + let line_start = start.line - 1; + + for (id, line) in self.src[..line_start].iter().enumerate().rev() { + if line.trim().starts_with("//") || line.is_empty() { + if id != 0 { + self.comments.push_front(id); + } + } else { + break; + } + } + + let mut last_was_empty = false; + while let Some(comment_line) = self.comments.pop_front() { + let line = &self.src[comment_line]; + if line.is_empty() { + if !last_was_empty { + self.new_line()?; + } + last_was_empty = true; + } else { + last_was_empty = false; + self.tabbed_line()?; + write!(self.buf, "{}", self.src[comment_line].trim())?; + } + } + + Ok(()) + } + // Push out the indent level and write each component, line by line pub fn write_body_indented(&mut self, children: &[BodyNode]) -> Result { self.indent += 1; - let mut comments = Vec::new(); - for child in children { // Exprs handle their own indenting/line breaks if !matches!(child, BodyNode::RawExpr(_)) { - // collect all comments upwards - let start = child.span().start(); - let line_start = start.line; - - if self.current_element_has_comments(child) { - for (id, line) in self.src[..line_start - 1].iter().enumerate().rev() { - if line.trim().starts_with("//") || line.is_empty() { - if id != 0 { - comments.push(id); - } - } else { - break; - } - } - } - - let mut last_was_empty = false; - for comment_line in comments.drain(..).rev() { - let line = &self.src[comment_line]; - if line.is_empty() { - if !last_was_empty { - self.new_line()?; - } - last_was_empty = true; - } else { - last_was_empty = false; - self.tabbed_line()?; - write!(self.buf, "{}", self.src[comment_line].trim())?; - } - } - + self.write_comments(child.span())?; self.tabbed_line()?; } @@ -130,9 +133,14 @@ impl Buffer { } pub(crate) fn is_short_attrs(&mut self, attributes: &[ElementAttrNamed]) -> usize { - attributes - .iter() - .map(|attr| match &attr.attr { + let mut total = 0; + + for attr in attributes { + if self.current_element_has_comments(attr.span()) { + return 100000; + } + + total += match &attr.attr { ElementAttr::AttrText { value, name } => { value.value().len() + name.span().line_length() + 3 } @@ -165,12 +173,17 @@ impl Buffer { len + name.span().line_length() + 3 } - }) - .sum() + }; + } + + total } - pub fn retrieve_formatted_expr(&mut self, location: LineColumn) -> Option { - self.cached_formats.remove(&Location::new(location)) + pub fn retrieve_formatted_expr(&mut self, expr: &Expr) -> &str { + self.cached_formats + .entry(Location::new(expr.span().start())) + .or_insert(prettyplease::unparse_expr(expr)) + .as_str() } } diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index 70a70b0d2..a778eb259 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -1,5 +1,6 @@ use crate::Buffer; use dioxus_rsx::*; +use proc_macro2::Span; use std::{fmt::Result, fmt::Write}; use syn::spanned::Spanned; @@ -146,9 +147,14 @@ impl Buffer { } while let Some(attr) = attr_iter.next() { + self.indent += 1; + self.write_comments(attr.attr.start())?; + self.indent -= 1; + if !sameline { self.indented_tabbed_line()?; } + self.write_attribute(attr)?; if attr_iter.peek().is_some() { @@ -188,7 +194,7 @@ impl Buffer { } ElementAttr::EventTokens { name, tokens } => { - let out = self.retrieve_formatted_expr(tokens.span().start()).unwrap(); + let out = self.retrieve_formatted_expr(tokens).to_string(); let mut lines = out.split('\n').peekable(); let first = lines.next().unwrap(); @@ -216,8 +222,8 @@ impl Buffer { Ok(()) } - pub fn current_element_has_comments(&self, child: &BodyNode) -> bool { - let start = child.span().start(); + pub fn current_element_has_comments(&self, location: Span) -> bool { + let start = location.start(); let line_start = start.line; // make sure the comments are actually relevant to this element. @@ -245,7 +251,7 @@ impl Buffer { } for child in children { - if self.current_element_has_comments(child) { + if self.current_element_has_comments(child.span()) { 'line: for line in self.src[..child.span().start().line - 1].iter().rev() { match (line.trim().starts_with("//"), line.is_empty()) { (true, _) => return None, diff --git a/packages/autofmt/tests/fil.rs b/packages/autofmt/tests/fil.rs index f53c29448..e3f660e56 100644 --- a/packages/autofmt/tests/fil.rs +++ b/packages/autofmt/tests/fil.rs @@ -30,3 +30,13 @@ fn comment_case_work() { println!("{}", out); } + +#[test] +fn comment_attr_case_work() { + let src = include_str!("./samples/attrs.rsx"); + + let formatted = dioxus_autofmt::fmt_file(src); + let out = dioxus_autofmt::apply_formats(src, formatted); + + println!("{}", out); +} diff --git a/packages/autofmt/tests/sample.rs b/packages/autofmt/tests/sample.rs deleted file mode 100644 index 349894f69..000000000 --- a/packages/autofmt/tests/sample.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![allow(unused)] - -const SRC: &str = include_str!("./samples/all.rsx"); - -fn body() -> &'static str { - &SRC[6..SRC.len() - 3] -} - -fn unindented_body() -> String { - body() - .lines() - .map(|line| match line.strip_prefix(" ") { - Some(line) => line, - None => line, - }) - .collect::>() - .join("\n") -} - -#[test] -fn way_and_back() { - let blocks = dioxus_autofmt::fmt_file(SRC).into_iter().next().unwrap(); - - println!("{}", blocks.formatted); -} diff --git a/packages/autofmt/tests/samples/attrs.rsx b/packages/autofmt/tests/samples/attrs.rsx new file mode 100644 index 000000000..9363d802b --- /dev/null +++ b/packages/autofmt/tests/samples/attrs.rsx @@ -0,0 +1,21 @@ +rsx! { + div { + // My comment here 1 + // My comment here 2 + // My comment here 3 + // My comment here 4 + class: "asdasd", + +// Comment here + onclick: move |_| { + let a = 10; + let b = 40; + let c = 50; + }, + + // my comment + + // This here + "hi" + } +} diff --git a/packages/autofmt/tests/samples/comments.rsx b/packages/autofmt/tests/samples/comments.rsx index 5cf18edc0..2c2051efe 100644 --- a/packages/autofmt/tests/samples/comments.rsx +++ b/packages/autofmt/tests/samples/comments.rsx @@ -15,6 +15,12 @@ rsx! { // body div { "text" } + div { + // Attr + class: "asdads", + "health" + } + div { diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index 456f45b54..b2b832f04 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -1,6 +1,6 @@ use super::*; -use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseBuffer, ParseStream}, @@ -209,7 +209,18 @@ pub enum ElementAttr { /// onclick: {} EventTokens { name: Ident, tokens: Expr }, } + impl ElementAttr { + pub fn start(&self) -> Span { + match self { + ElementAttr::AttrText { name, .. } => name.span(), + ElementAttr::AttrExpression { name, .. } => name.span(), + ElementAttr::CustomAttrText { name, .. } => name.span(), + ElementAttr::CustomAttrExpression { name, .. } => name.span(), + ElementAttr::EventTokens { name, .. } => name.span(), + } + } + pub fn is_expr(&self) -> bool { matches!( self,