for FormatSvelteTextExpression {
- fn fmt_fields(&self, node: &SvelteTextExpression, f: &mut HtmlFormatter) -> FormatResult<()> {
- format_html_verbatim_node(node.syntax()).fmt(f)
- }
-}
diff --git a/crates/biome_html_formatter/src/svelte/mod.rs b/crates/biome_html_formatter/src/svelte/mod.rs
deleted file mode 100644
index 89503df70ca9..000000000000
--- a/crates/biome_html_formatter/src/svelte/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
-
-pub(crate) mod auxiliary;
diff --git a/crates/biome_html_formatter/tests/language.rs b/crates/biome_html_formatter/tests/language.rs
index f716e9984a0e..49c37a161c5e 100644
--- a/crates/biome_html_formatter/tests/language.rs
+++ b/crates/biome_html_formatter/tests/language.rs
@@ -2,7 +2,7 @@ use biome_formatter_test::TestFormatLanguage;
use biome_fs::BiomePath;
use biome_html_formatter::HtmlFormatLanguage;
use biome_html_formatter::context::HtmlFormatContext;
-use biome_html_parser::parse_html;
+use biome_html_parser::{HtmlParseOptions, parse_html};
use biome_html_syntax::{HtmlFileSource, HtmlLanguage};
use biome_parser::AnyParse;
use biome_service::{
@@ -26,7 +26,7 @@ impl TestFormatLanguage for HtmlTestFormatLanguage {
type FormatLanguage = HtmlFormatLanguage;
fn parse(&self, text: &str) -> AnyParse {
- parse_html(text, self.source_type).into()
+ parse_html(text, HtmlParseOptions::from(&self.source_type)).into()
}
fn to_format_language(
diff --git a/crates/biome_html_formatter/tests/quick_test.rs b/crates/biome_html_formatter/tests/quick_test.rs
index 938d92af271e..fae0e5bd1a7e 100644
--- a/crates/biome_html_formatter/tests/quick_test.rs
+++ b/crates/biome_html_formatter/tests/quick_test.rs
@@ -2,7 +2,7 @@ use biome_formatter::{AttributePosition, IndentStyle, LineWidth};
use biome_formatter_test::check_reformat::CheckReformat;
use biome_html_formatter::context::HtmlFormatOptions;
use biome_html_formatter::{HtmlFormatLanguage, format_node};
-use biome_html_parser::parse_html;
+use biome_html_parser::{HtmlParseOptions, parse_html};
use biome_html_syntax::HtmlFileSource;
mod language {
@@ -25,7 +25,7 @@ foo bar baz boof
quick brown fox
"#;
let source_type = HtmlFileSource::html();
- let tree = parse_html(src, source_type);
+ let tree = parse_html(src, HtmlParseOptions::from(&source_type));
let options = HtmlFormatOptions::new(HtmlFileSource::html())
.with_indent_style(IndentStyle::Space)
.with_line_width(LineWidth::try_from(80).unwrap())
diff --git a/crates/biome_html_formatter/tests/spec_tests.rs b/crates/biome_html_formatter/tests/spec_tests.rs
index 1e2e624a4036..3746062df367 100644
--- a/crates/biome_html_formatter/tests/spec_tests.rs
+++ b/crates/biome_html_formatter/tests/spec_tests.rs
@@ -4,6 +4,6 @@ mod spec_test;
mod formatter {
mod html {
- tests_macros::gen_tests! {"tests/specs/html/**/*.html", crate::spec_test::run, ""}
+ tests_macros::gen_tests! {"tests/specs/html/**/*.{html,vue}", crate::spec_test::run, ""}
}
}
diff --git a/crates/biome_html_formatter/tests/specs/html/text_expressions/expressions.vue b/crates/biome_html_formatter/tests/specs/html/text_expressions/expressions.vue
new file mode 100644
index 000000000000..890d97a28128
--- /dev/null
+++ b/crates/biome_html_formatter/tests/specs/html/text_expressions/expressions.vue
@@ -0,0 +1,6 @@
+
+ {{ x => {
+ return "hello"
+} }}
+
+
diff --git a/crates/biome_html_formatter/tests/specs/html/text_expressions/expressions.vue.snap b/crates/biome_html_formatter/tests/specs/html/text_expressions/expressions.vue.snap
new file mode 100644
index 000000000000..e26b7e824664
--- /dev/null
+++ b/crates/biome_html_formatter/tests/specs/html/text_expressions/expressions.vue.snap
@@ -0,0 +1,48 @@
+---
+source: crates/biome_formatter_test/src/snapshot_builder.rs
+info: text_expressions/expressions.vue
+---
+# Input
+
+```vue
+
+ {{ x => {
+ return "hello"
+} }}
+
+
+
+```
+
+
+=============================
+
+# Outputs
+
+## Output 1
+
+-----
+Indent style: Tab
+Indent width: 2
+Line ending: LF
+Line width: 80
+Attribute Position: Auto
+Bracket same line: false
+Whitespace sensitivity: css
+Indent script and style: false
+Self close void elements: never
+-----
+
+```vue
+
+ {{ x => {
+ return "hello"
+} }}
+
+```
+
+
+
+## Unimplemented nodes/tokens
+
+" x => {\n\treturn \"hello\"\n} " => 14..40
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html.snap
index b4c6dd805f1b..09ab544dce10 100644
--- a/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html.snap
+++ b/crates/biome_html_formatter/tests/specs/prettier/html/interpolation/example.html.snap
@@ -40,24 +40,143 @@ x => {
```diff
--- Prettier
+++ Biome
-@@ -4,5 +4,5 @@
- return hello; } }} Magni consectetur in et molestias neque esse voluptatibus
- voluptas. {{ some_variable }} Eum quia nihil nulla esse. Dolorem asperiores
- vero est error {{ preserve invalid interpolation }} reprehenderit voluptates
+@@ -1,8 +1,26 @@
+-
+-
+- Fuga magnam facilis. Voluptatem quaerat porro.{{ x => { const hello = 'world'
+- return hello; } }} Magni consectetur in et molestias neque esse voluptatibus
+- voluptas. {{ some_variable }} Eum quia nihil nulla esse. Dolorem asperiores
+- vero est error {{ preserve invalid interpolation }} reprehenderit voluptates
- minus {{console.log( short_interpolation )}} nemo.
-+ minus {{console.log( short_interpolation ) }} nemo.
-
+-
++Fuga magnam facilis. Voluptatem quaerat porro.{{
++
++
++x => {
++ const hello = 'world'
++ return hello;
++}
++
++
++
++}} Magni consectetur in et molestias neque esse voluptatibus voluptas. {{
++
++
++ some_variable
++
++
++
++}} Eum quia nihil nulla esse. Dolorem asperiores vero est error {{
++
++ preserve
++
++ invalid
++
++ interpolation
++
++}} reprehenderit voluptates minus {{console.log( short_interpolation )}} nemo.
```
# Output
```html
-
-
- Fuga magnam facilis. Voluptatem quaerat porro.{{ x => { const hello = 'world'
- return hello; } }} Magni consectetur in et molestias neque esse voluptatibus
- voluptas. {{ some_variable }} Eum quia nihil nulla esse. Dolorem asperiores
- vero est error {{ preserve invalid interpolation }} reprehenderit voluptates
- minus {{console.log( short_interpolation ) }} nemo.
-
+Fuga magnam facilis. Voluptatem quaerat porro.{{
+
+
+x => {
+ const hello = 'world'
+ return hello;
+}
+
+
+
+}} Magni consectetur in et molestias neque esse voluptatibus voluptas. {{
+
+
+ some_variable
+
+
+
+}} Eum quia nihil nulla esse. Dolorem asperiores vero est error {{
+
+ preserve
+
+ invalid
+
+ interpolation
+
+}} reprehenderit voluptates minus {{console.log( short_interpolation )}} nemo.
+```
+
+# Errors
+```
+example.html:1:114 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Text expressions aren't supported.
+
+ > 1 │ Fuga magnam facilis. Voluptatem quaerat porro.{{
+ │ ^^
+ > 2 │
+ > 3 │
+ > 4 │ x => {
+ ...
+ > 9 │
+ > 10 │
+ > 11 │ }} Magni consectetur in et molestias neque esse voluptatibus voluptas. {{
+ │ ^^
+ 12 │
+
+ i Remove it or enable the parsing using the html.parser.textExpression option.
+
+example.html:11:72 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Text expressions aren't supported.
+
+ > 11 │ }} Magni consectetur in et molestias neque esse voluptatibus voluptas. {{
+ │ ^^
+ > 12 │
+ ...
+ > 17 │
+ > 18 │ }} Eum quia nihil nulla esse. Dolorem asperiores vero est error {{
+ │ ^^
+ 19 │
+ 20 │ preserve
+
+ i Remove it or enable the parsing using the html.parser.textExpression option.
+
+example.html:18:65 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Text expressions aren't supported.
+
+ > 18 │ }} Eum quia nihil nulla esse. Dolorem asperiores vero est error {{
+ │ ^^
+ > 19 │
+ ...
+ > 24 │ interpolation
+ > 25 │
+ > 26 │ }} reprehenderit voluptates minus {{console.log( short_interpolation )}} nemo.
+ │ ^^
+ 27 │
+
+ i Remove it or enable the parsing using the html.parser.textExpression option.
+
+example.html:26:35 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Text expressions aren't supported.
+
+ 24 │ interpolation
+ 25 │
+ > 26 │ }} reprehenderit voluptates minus {{console.log( short_interpolation )}} nemo.
+ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ 27 │
+
+ i Remove it or enable the parsing using the html.parser.textExpression option.
+
+
+```
+
+# Lines exceeding max width of 80 characters
+```
+ 1: Fuga magnam facilis. Voluptatem quaerat porro.{{
+ 26: }} reprehenderit voluptates minus {{console.log( short_interpolation )}} nemo.
```
diff --git a/crates/biome_html_parser/benches/html_parser.rs b/crates/biome_html_parser/benches/html_parser.rs
index a00e7bfbbaf7..bbfebbe68661 100644
--- a/crates/biome_html_parser/benches/html_parser.rs
+++ b/crates/biome_html_parser/benches/html_parser.rs
@@ -1,5 +1,5 @@
use biome_diagnostics::{DiagnosticExt, print_diagnostic_to_string};
-use biome_html_parser::{parse_html, parse_html_with_cache};
+use biome_html_parser::{HtmlParseOptions, parse_html, parse_html_with_cache};
use biome_html_syntax::HtmlFileSource;
use biome_rowan::NodeCache;
use biome_test_utils::BenchCase;
@@ -43,7 +43,8 @@ fn bench_parser(criterion: &mut Criterion) {
&code,
|b, _| {
b.iter(|| {
- let result = black_box(parse_html(code, file_source));
+ let result =
+ black_box(parse_html(code, HtmlParseOptions::from(&file_source)));
diagnostics.extend(result.into_diagnostics());
})
},
@@ -61,11 +62,19 @@ fn bench_parser(criterion: &mut Criterion) {
b.iter_batched(
|| {
let mut cache = NodeCache::default();
- parse_html_with_cache(code, file_source, &mut cache);
+ parse_html_with_cache(
+ code,
+ &mut cache,
+ HtmlParseOptions::from(&file_source),
+ );
cache
},
|mut cache| {
- parse_html_with_cache(code, file_source, &mut cache);
+ parse_html_with_cache(
+ code,
+ &mut cache,
+ HtmlParseOptions::from(&file_source),
+ );
},
BatchSize::SmallInput,
)
diff --git a/crates/biome_html_parser/src/lexer/mod.rs b/crates/biome_html_parser/src/lexer/mod.rs
index 47189c36a844..a3173f764d9a 100644
--- a/crates/biome_html_parser/src/lexer/mod.rs
+++ b/crates/biome_html_parser/src/lexer/mod.rs
@@ -1,6 +1,6 @@
mod tests;
-use crate::token_source::{HtmlEmbeddedLanguage, HtmlLexContext};
+use crate::token_source::{HtmlEmbeddedLanguage, HtmlLexContext, TextExpressionKind};
use biome_html_syntax::HtmlSyntaxKind::{
COMMENT, DOCTYPE_KW, EOF, ERROR_TOKEN, HTML_KW, HTML_LITERAL, HTML_STRING_LITERAL, NEWLINE,
TOMBSTONE, UNICODE_BOM, WHITESPACE,
@@ -59,10 +59,20 @@ impl<'src> HtmlLexer<'src> {
b'/' => self.consume_byte(T![/]),
b'=' => self.consume_byte(T![=]),
b'!' => self.consume_byte(T![!]),
- b'{' if self.is_at_l_text_expression() => self.consume_l_text_expression(),
- b'{' => self.consume_byte(T!['{']),
- b'}' if self.is_at_r_text_expression() => self.consume_r_text_expression(),
- b'}' => self.consume_byte(T!['}']),
+ b'{' => {
+ if self.at_opening_double_text_expression() {
+ self.consume_l_double_text_expression()
+ } else {
+ self.consume_byte(T!['{'])
+ }
+ }
+ b'}' => {
+ if self.at_closing_double_text_expression() {
+ self.consume_r_double_text_expression()
+ } else {
+ self.consume_byte(T!['}'])
+ }
+ }
b'\'' | b'"' => self.consume_string_literal(current),
_ if self.current_kind == T![<] && is_tag_name_byte(current) => {
// tag names must immediately follow a `<`
@@ -88,13 +98,23 @@ impl<'src> HtmlLexer<'src> {
fn consume_token(&mut self, current: u8) -> HtmlSyntaxKind {
match current {
b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(),
- b'{' if self.is_at_l_text_expression() => self.consume_l_text_expression(),
- b'{' => self.consume_byte(T!['{']),
- b'}' if self.is_at_r_text_expression() => self.consume_r_text_expression(),
- b'}' => self.consume_byte(T!['}']),
b'!' if self.current() == T![<] => self.consume_byte(T![!]),
b'/' if self.current() == T![<] => self.consume_byte(T![/]),
- b'-' if self.is_at_frontmatter_edge() => self.consume_frontmatter_edge(),
+ b'-' if self.at_frontmatter_edge() => self.consume_frontmatter_edge(),
+ b'{' => {
+ if self.at_opening_double_text_expression() {
+ self.consume_l_double_text_expression()
+ } else {
+ self.consume_byte(T!['{'])
+ }
+ }
+ b'}' => {
+ if self.at_closing_double_text_expression() {
+ self.consume_r_double_text_expression()
+ } else {
+ self.consume_byte(T!['}'])
+ }
+ }
b'<' => {
// if this truly is the start of a tag, it *must* be immediately followed by a tag name. Whitespace is not allowed.
// https://html.spec.whatwg.org/multipage/syntax.html#start-tags
@@ -114,7 +134,7 @@ impl<'src> HtmlLexer<'src> {
self.consume_byte(HTML_LITERAL)
}
}
- _ => self.consume_html_text(),
+ _ => self.consume_html_text(current),
}
}
@@ -124,6 +144,8 @@ impl<'src> HtmlLexer<'src> {
b'\n' | b'\r' | b'\t' | b' ' => self.consume_newline_or_whitespaces(),
b'<' => self.consume_byte(T![<]),
b'>' => self.consume_byte(T![>]),
+ b'{' => self.consume_byte(T!['{']),
+ b'}' => self.consume_byte(T!['}']),
b'\'' | b'"' => self.consume_string_literal(current),
_ => self.consume_unquoted_string_literal(),
}
@@ -165,7 +187,7 @@ impl<'src> HtmlLexer<'src> {
}
self.advance_byte_or_char(byte);
- if context == HtmlLexContext::AstroFencedCodeBlock && self.is_at_frontmatter_edge() {
+ if context == HtmlLexContext::AstroFencedCodeBlock && self.at_frontmatter_edge() {
return HTML_LITERAL;
}
}
@@ -179,6 +201,44 @@ impl<'src> HtmlLexer<'src> {
}
}
+ fn consume_double_text_expression(&mut self, current: u8) -> HtmlSyntaxKind {
+ match current {
+ b'}' if self.at_closing_double_text_expression() => {
+ self.consume_r_double_text_expression()
+ }
+ b'<' => self.consume_byte(T![<]),
+ _ => {
+ while let Some(current) = self.current_byte() {
+ match current {
+ b'}' if self.at_closing_double_text_expression() => break,
+ _ => {
+ self.advance(1);
+ }
+ }
+ }
+ HTML_LITERAL
+ }
+ }
+ }
+
+ fn consume_single_text_expression(&mut self, current: u8) -> HtmlSyntaxKind {
+ match current {
+ b'}' if !self.at_closing_double_text_expression() => self.consume_byte(T!['}']),
+ b'<' => self.consume_byte(T![<]),
+ _ => {
+ while let Some(current) = self.current_byte() {
+ match current {
+ b'}' if !self.at_closing_double_text_expression() => break,
+ _ => {
+ self.advance(1);
+ }
+ }
+ }
+ HTML_LITERAL
+ }
+ }
+ }
+
fn consume_comment(&mut self) -> HtmlSyntaxKind {
// eat