fix: even better comment handling

This commit is contained in:
Jonathan Kelley committed 2022-07-05 01:53:08 -04:00
1 parent 47f3cc2529
commit 4471ccba49
8 files changed
+115 -73

No files matched your search

+1 -1
View File
@@ -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.
+55 -42
View File
@@ -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<Location, String>,
pub buf: String,
pub indent: usize,
pub comments: VecDeque<usize>,
}
#[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<String> {
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()
}
}
+10 -4
View File
@@ -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,
+10
View File
@@ -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);
}
-25
View File
@@ -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::<Vec<_>>()
.join("\n")
}
#[test]
fn way_and_back() {
let blocks = dioxus_autofmt::fmt_file(SRC).into_iter().next().unwrap();
println!("{}", blocks.formatted);
}
+21
View File
@@ -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"
}
}
@@ -15,6 +15,12 @@ rsx! {
// body
div { "text" }
div {
// Attr
class: "asdads",
"health"
}
div {
+12 -1
View File
@@ -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,