Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/autofmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub fn try_fmt_file(

// TESTME
// Writing *should* not fail but it's possible that it does
if writer.write_rsx_call(&body.body).is_err() {
if writer.write_rsx_call(&body).is_err() {
let span = writer.invalid_exprs.pop().unwrap_or_else(Span::call_site);
return Err(syn::Error::new(span, "Failed emit valid rsx - likely due to partially complete expressions in the rsx! macro"));
}
Expand Down Expand Up @@ -149,7 +149,7 @@ pub fn try_fmt_file(
/// that passed partial expansion but failed to parse.
pub fn write_block_out(body: &CallBody) -> Option<String> {
let mut buf = Writer::new("", IndentOptions::default());
buf.write_rsx_call(&body.body).ok()?;
buf.write_rsx_call(body).ok()?;
buf.consume()
}

Expand All @@ -158,7 +158,7 @@ pub fn fmt_block(block: &str, indent_level: usize, indent: IndentOptions) -> Opt

let mut buf = Writer::new(block, indent);
buf.out.indent_level = indent_level;
buf.write_rsx_call(&body.body).ok()?;
buf.write_rsx_call(&body).ok()?;

// writing idents leaves the final line ended at the end of the last ident
if buf.out.buf.contains('\n') {
Expand Down
87 changes: 75 additions & 12 deletions packages/autofmt/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,38 @@ impl<'a> Writer<'a> {
Some(self.out.buf)
}

pub fn write_rsx_call(&mut self, body: &TemplateBody) -> Result {
if body.roots.is_empty() {
pub fn write_rsx_call(&mut self, body: &CallBody) -> Result {
if body.body.roots.is_empty() {
return Ok(());
}

if Self::is_short_rsx_call(&body.roots) {
if Self::is_short_rsx_call(&body.body.roots) {
write!(self.out, " ")?;
self.write_ident(&body.roots[0])?;
self.write_ident(&body.body.roots[0])?;
write!(self.out, " ")?;
} else {
self.out.new_line()?;
self.write_body_indented(&body.roots)?
self.write_body_indented(&body.body.roots)?;
self.write_trailing_body_comments(body)?;
}

Ok(())
}

fn write_trailing_body_comments(&mut self, body: &CallBody) -> Result {
if let Some(span) = body.span {
self.out.indent_level += 1;
let comments = self.accumulate_comments(span.span().end());
if !comments.is_empty() {
self.out.new_line()?;
self.apply_comments(comments)?;
self.out.buf.pop(); // remove the trailing newline, forcing us to end at the end of the comment
}
self.out.indent_level -= 1;
}
Ok(())
}

// Expects to be written directly into place
pub fn write_ident(&mut self, node: &BodyNode) -> Result {
match node {
Expand Down Expand Up @@ -301,7 +316,8 @@ impl<'a> Writer<'a> {
let children_len = self
.is_short_children(children)
.map_err(|_| std::fmt::Error)?;
let is_small_children = children_len.is_some();
let has_trailing_comments = self.has_trailing_comments(children, brace);
let is_small_children = children_len.is_some() && !has_trailing_comments;

// if we have one long attribute and a lot of children, place the attrs on top
if is_short_attr_list && !is_small_children {
Expand All @@ -310,7 +326,11 @@ impl<'a> Writer<'a> {

// even if the attr is long, it should be put on one line
// However if we have childrne we need to just spread them out for readability
if !is_short_attr_list && attributes.len() <= 1 && spreads.is_empty() {
if !is_short_attr_list
&& attributes.len() <= 1
&& spreads.is_empty()
&& !has_trailing_comments
{
if children.is_empty() {
opt_level = ShortOptimization::Oneliner;
} else {
Expand All @@ -328,7 +348,11 @@ impl<'a> Writer<'a> {
}

// If there's nothing at all, empty optimization
if attributes.is_empty() && children.is_empty() && spreads.is_empty() {
if attributes.is_empty()
&& children.is_empty()
&& spreads.is_empty()
&& !has_trailing_comments
{
opt_level = ShortOptimization::Empty;

// Write comments if they exist
Expand Down Expand Up @@ -932,9 +956,8 @@ impl<'a> Writer<'a> {
}

fn final_span_of_node(node: &BodyNode) -> Span {
// Write the trailing comments if there are any
// Get the ending span of the node
let span = match node {
match node {
BodyNode::Element(el) => el
.brace
.as_ref()
Expand All @@ -952,8 +975,7 @@ impl<'a> Writer<'a> {
Some(b) => b.span.span(),
None => i.then_brace.span.span(),
},
};
span
}
}

fn final_span_of_attr(&self, attr: &Attribute) -> Span {
Expand All @@ -969,4 +991,45 @@ impl<'a> Writer<'a> {
.unwrap_or_else(|| ex.then_value.span()),
}
}

fn has_trailing_comments(&self, children: &[BodyNode], brace: &Brace) -> bool {
let brace_span = brace.span.span();

let Some(last_node) = children.last() else {
return false;
};

// Check for any comments after the last node between the last brace
let final_span = Self::final_span_of_node(last_node);
let final_span = final_span.end();
let mut line = final_span.line;
let mut column = final_span.column;
loop {
let Some(src_line) = self.src.get(line - 1) else {
return false;
};

// the line might contain emoji or other unicode characters - this will cause issues
let Some(mut whitespace) = src_line.get(column..).map(|s| s.trim()) else {
return false;
};

let offset = 0;
whitespace = whitespace[offset..].trim();

if whitespace.starts_with("//") {
return true;
}

if line == brace_span.end().line {
// If we reached the end of the brace span, stop
break;
}

line += 1;
column = 0; // reset column to the start of the next line
}

false
}
}
65 changes: 65 additions & 0 deletions packages/autofmt/tests/samples/comments.rsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,69 @@ rsx! {
// please dont eat me 3
abc: 123,
}

div {
// I am just a comment
}

div {
"text"
// I am just a comment
}

div {
div {}
// I am just a comment
}

div {
{some_expr()}
// I am just a comment
}

div {
"text" // I am just a comment
}

div {
div {} // I am just a comment
}

div {
{some_expr()} // I am just a comment
}

div {
// Please dont eat me 1
div {
// Please dont eat me 2
}
// Please dont eat me 3
}

div {
"hi"
// Please dont eat me 1
}
div {
"hi" // Please dont eat me 1
// Please dont eat me 2
}

// Please dont eat me 2
Component {}

// Please dont eat me 1
Component {
// Please dont eat me 2
}

// Please dont eat me 1
Component {
// Please dont eat me 2
}

// Please dont eat me 1
//
// Please dont eat me 2
}
8 changes: 6 additions & 2 deletions packages/rsx/src/rsx_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! This mostly just defers to the root TemplateBody with some additional tooling to provide better errors.
//! Currently the additional tooling doesn't do much.

use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::ToTokens;
use std::{cell::Cell, fmt::Debug};
use syn::{
Expand All @@ -26,12 +26,15 @@ use crate::{BodyNode, TemplateBody};
pub struct CallBody {
pub body: TemplateBody,
pub template_idx: Cell<usize>,
pub span: Option<Span>,
}

impl Parse for CallBody {
fn parse(input: ParseStream) -> Result<Self> {
// Defer to the `new` method such that we can wire up hotreload information
Ok(CallBody::new(input.parse()?))
let mut body = CallBody::new(input.parse()?);
body.span = Some(input.span());
Ok(body)
}
}

Expand All @@ -49,6 +52,7 @@ impl CallBody {
let body = CallBody {
body,
template_idx: Cell::new(0),
span: None,
};

body.body.template_idx.set(body.next_template_idx());
Expand Down
Loading