diff --git a/crates/rune/src/fmt/comments.rs b/crates/rune/src/fmt/comments.rs index 0b6340beb..d60f5b9ae 100644 --- a/crates/rune/src/fmt/comments.rs +++ b/crates/rune/src/fmt/comments.rs @@ -114,7 +114,7 @@ fn parse_block_comment(chars: &mut CharIndices<'_>) -> Option { while let Some((_, c)) = chars.next() { if c == '*' { if let Some((_, '/')) = chars.clone().next() { - return Some(chars.next()?.0); + return Some(chars.next()?.0 + 1); } } } diff --git a/crates/rune/src/fmt/comments/tests.rs b/crates/rune/src/fmt/comments/tests.rs index dd9d37b52..09b3f9ceb 100644 --- a/crates/rune/src/fmt/comments/tests.rs +++ b/crates/rune/src/fmt/comments/tests.rs @@ -14,7 +14,7 @@ fn test_parse_block_comment() { let input = "/* this is a comment */"; let mut chars = input.char_indices(); let end = parse_block_comment(&mut chars).unwrap(); - assert_eq!(end, input.len() - 1); + assert_eq!(end, input.len()); } #[test] diff --git a/crates/rune/src/fmt/indent_writer.rs b/crates/rune/src/fmt/indent_writer.rs index f67b85f3b..61e1a5092 100644 --- a/crates/rune/src/fmt/indent_writer.rs +++ b/crates/rune/src/fmt/indent_writer.rs @@ -9,7 +9,7 @@ use core::str; use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; use crate::alloc::{self, try_vec, Vec}; -use crate::ast::Span; +use crate::ast::{ByteIndex, Span}; use super::comments::Comment; use super::error::FormattingError; @@ -136,19 +136,7 @@ impl<'a> SpanInjectionWriter<'a> { pub(super) fn into_inner(mut self) -> Result>, FormattingError> { while !self.queued_spans.is_empty() { let span = self.queued_spans.remove(0); - match span { - ResolvedSpan::Empty(_) => { - writeln!(self.writer)?; - } - ResolvedSpan::Comment(comment) => { - if comment.on_new_line { - writeln!(self.writer, "{}", self.resolve(comment.span)?)?; - } else { - self.extend_previous_line(b" ")?; - self.extend_previous_line(self.resolve(comment.span)?.as_bytes())?; - } - } - } + self.write_span(span)?; } Ok(self.writer.into_inner()) @@ -195,37 +183,58 @@ impl<'a> SpanInjectionWriter<'a> { self.write_spanned(Span::new(0, 0), text, false, false) } - pub(super) fn write_spanned( - &mut self, - span: Span, - text: &str, - newline: bool, - space: bool, - ) -> Result<(), FormattingError> { + pub(super) fn write_queued_spans(&mut self, until: ByteIndex) -> Result<(), FormattingError> { // The queued recovered spans are ordered so we can pop them from the front if they're before the current span. // If the current span is before the first queued span, we need to inject the queued span. while let Some(queued_span) = self.queued_spans.first() { - if queued_span.span().start > span.start { + if queued_span.span().start > until { break; } let queued_span = self.queued_spans.remove(0); - match queued_span { - ResolvedSpan::Empty(_) => { - writeln!(self.writer)?; - } - ResolvedSpan::Comment(comment) => { + self.write_span(queued_span)?; + } + + Ok(()) + } + + fn write_span(&mut self, span: ResolvedSpan) -> Result<(), FormattingError> { + match span { + ResolvedSpan::Empty(_) => { + writeln!(self.writer)?; + } + ResolvedSpan::Comment(comment) => { + let mut lines = self.resolve(comment.span)?.lines(); + + if let Some(first_line) = lines.next() { if comment.on_new_line { - writeln!(self.writer, "{}", self.resolve(comment.span)?)?; + writeln!(self.writer, "{}", first_line)?; } else { self.extend_previous_line(b" ")?; - self.extend_previous_line(self.resolve(comment.span)?.as_bytes())?; + self.extend_previous_line(first_line.as_bytes())?; } } + + for line in lines { + self.newline()?; + self.extend_previous_line(line.as_bytes())?; + } } } + Ok(()) + } + + pub(super) fn write_spanned( + &mut self, + span: Span, + text: &str, + newline: bool, + space: bool, + ) -> Result<(), FormattingError> { + self.write_queued_spans(span.start)?; + write!(self.writer, "{}", text)?; if space { diff --git a/crates/rune/src/fmt/printer.rs b/crates/rune/src/fmt/printer.rs index c5ad74c21..fae59dae9 100644 --- a/crates/rune/src/fmt/printer.rs +++ b/crates/rune/src/fmt/printer.rs @@ -152,7 +152,6 @@ impl<'a> Printer<'a> { for attribute in attributes { self.visit_attribute(attribute)?; } - self.writer.newline()?; self.emit_visibility(visibility)?; @@ -1788,6 +1787,7 @@ impl<'a> Printer<'a> { self.visit_statement(statement)?; } + self.writer.write_queued_spans(close.span.start)?; self.writer.dedent(); self.writer.write_spanned_raw(close.span, false, false)?; @@ -1802,7 +1802,7 @@ impl<'a> Printer<'a> { } ast::Stmt::Item(item, semi) => { self.visit_item(item, *semi)?; - if !matches!(item, ast::Item::Fn(_)) { + if !matches!(item, ast::Item::Const(_) | ast::Item::Fn(_)) { self.writer.newline()?; } } diff --git a/crates/rune/src/tests.rs b/crates/rune/src/tests.rs index 85360ec7c..868dc1cf0 100644 --- a/crates/rune/src/tests.rs +++ b/crates/rune/src/tests.rs @@ -438,6 +438,7 @@ mod external_match; mod external_ops; mod float; mod for_loop; +mod format_source; mod generics; mod getter_setter; mod instance; diff --git a/crates/rune/src/tests/format_source.rs b/crates/rune/src/tests/format_source.rs new file mode 100644 index 000000000..c40aa27d0 --- /dev/null +++ b/crates/rune/src/tests/format_source.rs @@ -0,0 +1,195 @@ +prelude!(); + +use crate::fmt::format_source; + +#[track_caller] +fn assert_format_source(source: &str, expected: Option<&str>) -> Result<()> { + let formated = format_source(source)?; + let expected = expected.unwrap_or(source); + assert_eq!(formated, expected); + + Ok(()) +} + +/// https://github.com/rune-rs/rune/issues/684 +#[test] +fn bug_684() -> Result<()> { + let source = r#"pub fn main() { + /* + test + */ +} +"#; + + assert_format_source(source, None) +} + +#[test] +fn fmt_block_comment() -> Result<()> { + let source = r#"//test1 +/*test2*/"#; + let expected = format!("{source}\n"); + + assert_format_source(source, Some(&expected)) +} + +#[test] +fn fmt_block_comment_indent() -> Result<()> { + let source = r#"struct Test { + a, /* test1 + test2 +test 3*/ +} +"#; + + assert_format_source(source, None) +} + +#[test] +fn fmt_block_comment_indent2() -> Result<()> { + let source = r#"fn test() { + /* test1 + test2 */ + + if true { + /* + if false { + // test3 + } + */ + } /* else { + // test 4 + } */ +} +/* test 5.1 + test 5.2 + test 5.3 +*/ +"#; + + assert_format_source(source, None) +} + +/// https://github.com/rune-rs/rune/issues/693 +#[test] +fn bug_693() -> Result<()> { + let source = r#"pub fn main() { + if true { + // test + } +} +"#; + + assert_format_source(source, None) +} + +#[test] +fn fmt_comment_line() -> Result<()> { + let source = r#"pub fn main() { + // test 1 + if true { + // test 2.1 + let a = 1; + // test 2.2 + } + // test 3 +} +"#; + + assert_format_source(source, None) +} + +/// https://github.com/rune-rs/rune/issues/703 +#[test] +fn bug_703() -> Result<()> { + let source = r#"pub fn main() { + const TEST = 1; +} +"#; + + assert_format_source(source, None) +} + +#[test] +fn fmt_global_const() -> Result<()> { + let source = r#"const TEST1=1;const TEST2=2; +const TEST3=1;"#; + let expected = r#"const TEST1 = 1; +const TEST2 = 2; +const TEST3 = 1; +"#; + + assert_format_source(source, Some(expected)) +} + +#[test] +fn fmt_len() -> Result<()> { + let source = r#"pub fn main() { + let var = 1; +} +"#; + + assert_format_source(source, None) +} + +#[test] +#[ignore] +fn fmt_println() -> Result<()> { + let source = r#"pub fn main(){println!("The value is {}",42);}"#; + let expected = r#"pub fn main() { + println!("The value is {}", 42); +} +"#; + + assert_format_source(source, Some(expected)) +} + +#[test] +fn fmt_while_loop() -> Result<()> { + let source = r#"pub fn main(){let value=0;while value<100{if value>=50{break;}value=value+1;}println!("The value is {}",value);// => The value is 50 +}"#; + let expected = r#"pub fn main() { + let value = 0; + while value < 100 { + if value >= 50 { + break; + } + value = value + 1; + } + println!("The value is {}",value); // => The value is 50 +} +"#; + + assert_format_source(source, Some(expected)) +} + +#[test] +fn fmt_async_http_timeout() -> Result<()> { + let source = r#"struct Timeout; + +async fn request(timeout) { + let request = http::get(`http://httpstat.us/200?sleep=${timeout}`); + let timeout = time::sleep(time::Duration::from_secs(2)); + + let result = select { + _ = timeout => Err(Timeout), + res = request => res, + }?; + + println!("{}", result.status()); + Ok(()) +} + +pub async fn main() { + if let Err(Timeout) = request(1000).await { + println("Request timed out!"); + } + + if let Err(Timeout) = request(4000).await { + println("Request timed out!"); + } +} +"#; + + assert_format_source(source, None) +}