diff --git a/crates/biome_console/src/write/html.rs b/crates/biome_console/src/write/html.rs
index 42a96a368869..2b9c586d3b88 100644
--- a/crates/biome_console/src/write/html.rs
+++ b/crates/biome_console/src/write/html.rs
@@ -9,7 +9,18 @@ use super::Write;
/// Adapter struct implementing [Write] over types implementing [io::Write],
/// renders markup as UTF-8 strings of HTML code
-pub struct HTML(pub W);
+pub struct HTML(pub W, bool);
+
+impl HTML {
+ pub fn new(writer: W) -> Self {
+ Self(writer, false)
+ }
+
+ pub fn with_mdx(mut self) -> Self {
+ self.1 = true;
+ self
+ }
+}
impl Write for HTML
where
@@ -17,13 +28,13 @@ where
{
fn write_str(&mut self, elements: &MarkupElements, content: &str) -> io::Result<()> {
push_styles(&mut self.0, elements)?;
- HtmlAdapter(&mut self.0).write_all(content.as_bytes())?;
+ HtmlAdapter(&mut self.0, self.1).write_all(content.as_bytes())?;
pop_styles(&mut self.0, elements)
}
fn write_fmt(&mut self, elements: &MarkupElements, content: fmt::Arguments) -> io::Result<()> {
push_styles(&mut self.0, elements)?;
- HtmlAdapter(&mut self.0).write_fmt(content)?;
+ HtmlAdapter(&mut self.0, self.1).write_fmt(content)?;
pop_styles(&mut self.0, elements)
}
}
@@ -79,13 +90,13 @@ fn pop_styles(fmt: &mut W, elements: &MarkupElements) -> io::Resul
/// Adapter wrapping a type implementing [io::Write]. It's responsible for:
/// - and adding HTML special characters escaping to the written byte sequence
/// - and adding HTML line breaks for newline characters
-struct HtmlAdapter(W);
+struct HtmlAdapter(W, bool);
impl io::Write for HtmlAdapter {
fn write(&mut self, mut buf: &[u8]) -> io::Result {
let mut bytes = 0;
- const CHARS_TO_CHECK: [u8; 6] = [b'"', b'&', b'<', b'>', b'\n', b'\r'];
+ const CHARS_TO_CHECK: [u8; 8] = [b'"', b'&', b'<', b'>', b'\n', b'\r', b'{', b'}'];
while let Some(idx) = buf.iter().position(|byte| CHARS_TO_CHECK.contains(byte)) {
let (before, after) = buf.split_at(idx);
@@ -94,20 +105,31 @@ impl io::Write for HtmlAdapter {
// SAFETY: Because of the above `position` match we know the buffer
// contains at least the matching byte
- let (byte, after) = after.split_first().unwrap();
- match *byte {
- b'"' => self.0.write_all(b""")?,
- b'&' => self.0.write_all(b"&")?,
- b'<' => self.0.write_all(b"<")?,
- b'>' => self.0.write_all(b">")?,
- b'\n' => self.0.write_all(b"
")?,
- b'\r' => self.0.write_all(b"
")?,
- _ => unreachable!(),
+ let result = after.split_first();
+ if let Some((byte, after)) = result {
+ match *byte {
+ b'"' => self.0.write_all(b""")?,
+ b'&' => self.0.write_all(b"&")?,
+ b'<' => self.0.write_all(b"<")?,
+ b'>' => self.0.write_all(b">")?,
+ b'\n' => self.0.write_all(b"
")?,
+ b'\r' => self.0.write_all(b"
")?,
+ _ => {
+ if self.1 {
+ match *byte {
+ b'{' => self.0.write_all(b"{")?,
+ b'}' => self.0.write_all(b"}")?,
+ _ => self.0.write_all(&[*byte])?,
+ }
+ } else {
+ unreachable!()
+ }
+ }
+ }
+ // Only 1 byte of the input was written
+ bytes += 1;
+ buf = after;
}
-
- // Only 1 byte of the input was written
- bytes += 1;
- buf = after;
}
self.0.write_all(buf)?;
@@ -129,7 +151,7 @@ mod test {
#[test]
fn test_new_lines() {
let mut buf = Vec::new();
- let mut writer = super::HTML(&mut buf);
+ let mut writer = super::HTML(&mut buf, false);
let mut formatter = Formatter::new(&mut writer);
formatter
@@ -156,7 +178,7 @@ mod test {
#[test]
fn test_escapes() {
let mut buf = Vec::new();
- let mut writer = super::HTML(&mut buf);
+ let mut writer = super::HTML(&mut buf, false);
let mut formatter = Formatter::new(&mut writer);
formatter
@@ -176,7 +198,7 @@ mod test {
#[test]
fn test_escapes_and_new_lines() {
let mut buf = Vec::new();
- let mut writer = super::HTML(&mut buf);
+ let mut writer = super::HTML(&mut buf, false);
let mut formatter = Formatter::new(&mut writer);
formatter
@@ -190,4 +212,40 @@ mod test {
"New rules that are still under development.
."
);
}
+
+ #[test]
+ fn does_not_escape_curly_braces() {
+ let mut buf = Vec::new();
+ let mut writer = super::HTML(&mut buf, false);
+ let mut formatter = Formatter::new(&mut writer);
+
+ formatter
+ .write_markup(markup! {
+ "New rules that are still under development.\n\n."
+ })
+ .unwrap();
+
+ assert_eq!(
+ String::from_utf8(buf).unwrap(),
+ "New rules that are still under development.
."
+ );
+ }
+
+ #[test]
+ fn escape_curly_braces() {
+ let mut buf = Vec::new();
+ let mut writer = super::HTML(&mut buf, false).with_mdx();
+ let mut formatter = Formatter::new(&mut writer);
+
+ formatter
+ .write_markup(markup! {
+ "New rules that are {still} under development.\n\n."
+ })
+ .unwrap();
+
+ assert_eq!(
+ String::from_utf8(buf).unwrap(),
+ "New rules that are {still} under development.
."
+ );
+ }
}
diff --git a/crates/biome_wasm/src/utils.rs b/crates/biome_wasm/src/utils.rs
index 4bd2ceff9d02..b8ca493cc36e 100644
--- a/crates/biome_wasm/src/utils.rs
+++ b/crates/biome_wasm/src/utils.rs
@@ -62,7 +62,7 @@ impl DiagnosticPrinter {
.with_file_path(&self.file_name)
.with_file_source_code(&self.file_source);
- let mut html = HTML(&mut self.buffer);
+ let mut html = HTML::new(&mut self.buffer);
Formatter::new(&mut html)
.write_markup(markup!({ printer(&err) }))
.map_err(into_error)?;