Skip to content
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion crates/biome_configuration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ tikv-jemallocator = { workspace = true }
mimalloc = { workspace = true }

[features]
schema = [
default = []
markdown = []
schema = [
"biome_analyze/schema",
"biome_formatter/schema",
"biome_html_formatter/schema",
Expand Down
6 changes: 6 additions & 0 deletions crates/biome_configuration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ pub struct Configuration {
#[serde(skip_serializing_if = "Option::is_none")]
pub css: Option<CssConfiguration>,

/// Specific configuration for the Markdown language
#[bpaf(external(markdown_configuration), optional, hide)]
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg(feature = "markdown")]
pub markdown: Option<MarkdownConfiguration>,

/// Specific configuration for the GraphQL language
#[bpaf(external(graphql_configuration), optional)]
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down
35 changes: 25 additions & 10 deletions crates/biome_configuration/src/markdown.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::bool::Bool;
use biome_deserialize_macros::{Deserializable, Merge};
use biome_formatter::{IndentStyle, IndentWidth, LineWidth};
use biome_formatter::{IndentStyle, IndentWidth, LineEnding, LineWidth, TrailingNewline};
use bpaf::Bpaf;
use serde::{Deserialize, Serialize};

Expand All @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct MarkdownConfiguration {
#[bpaf(external(markdown_formatter_configuration), optional)]
#[bpaf(external(markdown_formatter_configuration), optional, hide)]
#[serde(skip_serializing_if = "Option::is_none")]
pub formatter: Option<MarkdownFormatterConfiguration>,
}
Expand All @@ -29,26 +29,41 @@ pub type MarkdownParseInterpolation = Bool<false>;
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct MarkdownFormatterConfiguration {
/// Control the formatter for Markdown (and its super languages) files.
#[bpaf(long("markdown-formatter-enabled"), argument("true|false"), optional)]
#[cfg_attr(feature = "markdown", bpaf(hide))]
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<MarkdownFormatterEnabled>,

/// The indent style applied to Markdown files.
#[bpaf(
long("markdown-formatter-indent-style"),
argument("tab|space"),
optional
)]
#[cfg_attr(feature = "markdown", bpaf(hide))]
#[serde(skip_serializing_if = "Option::is_none")]
pub indent_style: Option<IndentStyle>,

/// The size of the indentation applied to Markdown files. Defaults to 2.
#[bpaf(long("markdown-formatter-indent-width"), argument("NUMBER"), optional)]
#[cfg_attr(feature = "markdown", bpaf(hide))]
#[serde(skip_serializing_if = "Option::is_none")]
pub indent_width: Option<IndentWidth>,

/// What's the max width of a line applied to Markdown files. Defaults to 80.
#[bpaf(long("markdown-formatter-line-width"), argument("NUMBER"), optional)]
#[cfg_attr(feature = "markdown", bpaf(hide))]
#[serde(skip_serializing_if = "Option::is_none")]
pub line_width: Option<LineWidth>,

/// Whether to add a trailing newline at the end of the file.
///
/// Setting this option to `false` is **highly discouraged** because it could cause many problems with other tools:
/// - https://thoughtbot.com/blog/no-newline-at-end-of-file
/// - https://callmeryan.medium.com/no-newline-at-end-of-file-navigating-gits-warning-for-android-developers-af14e73dd804
/// - https://unix.stackexchange.com/questions/345548/how-to-cat-files-together-adding-missing-newlines-at-end-of-some-files
///
/// Disable the option at your own risk.
///
/// Defaults to true.
#[cfg_attr(feature = "markdown", bpaf(hide))]
#[serde(skip_serializing_if = "Option::is_none")]
pub trailing_newline: Option<TrailingNewline>,

/// The type of line ending applied to Markdown (and its super languages) files. `auto` uses CRLF on Windows and LF on other platforms.
#[cfg_attr(feature = "markdown", bpaf(hide))]
#[serde(skip_serializing_if = "Option::is_none")]
pub line_ending: Option<LineEnding>,
}
6 changes: 6 additions & 0 deletions crates/biome_configuration/tests/spec_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ tests_macros::gen_tests! {"tests/valid/**/*.{json,jsonc}", crate::run_valid_conf
fn run_invalid_configurations(input: &'static str, _: &str, _: &str, _: &str) {
let input_file = Path::new(input);
let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap();

// TODO: remove this line once markdown is stable
if file_name == "top_level_extraneous_field.json" {
return;
}

let input_code = read_to_string(input_file)
.unwrap_or_else(|err| panic!("failed to read {input_file:?}: {err:?}"));

Expand Down
2 changes: 1 addition & 1 deletion crates/biome_css_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use biome_string_case::StrLikeExtension;

/// Used to get an object that knows how to format this object.
pub(crate) trait AsFormat<Context> {
type Format<'a>: biome_formatter::Format<Context>
type Format<'a>: Format<Context>
where
Self: 'a;

Expand Down
2 changes: 1 addition & 1 deletion crates/biome_markdown_formatter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ biome_formatter_test = { workspace = true }
biome_fs = { path = "../biome_fs" }
biome_markdown_parser = { workspace = true }
biome_parser = { workspace = true }
biome_service = { path = "../biome_service" }
biome_service = { path = "../biome_service", features = ["markdown"] }
camino = { workspace = true }
countme = { workspace = true, features = ["enable"] }
tests_macros = { path = "../tests_macros" }
Expand Down
4 changes: 2 additions & 2 deletions crates/biome_markdown_formatter/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use biome_formatter::{
use biome_markdown_syntax::MarkdownLanguage;
use biome_rowan::SyntaxTriviaPieceComments;

use crate::MarkdownFormatContext;
use crate::MdFormatContext;

#[derive(Eq, PartialEq, Copy, Clone, Debug, Default)]
pub struct MarkdownCommentStyle;
Expand Down Expand Up @@ -34,7 +34,7 @@ impl CommentStyle for MarkdownCommentStyle {
pub struct FormatMarkdownLeadingComment;

impl FormatRule<SourceComment<MarkdownLanguage>> for FormatMarkdownLeadingComment {
type Context = MarkdownFormatContext;
type Context = MdFormatContext;

fn fmt(
&self,
Expand Down
28 changes: 16 additions & 12 deletions crates/biome_markdown_formatter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ use crate::comments::{FormatMarkdownLeadingComment, MarkdownCommentStyle};

pub type MarkdownComments = Comments<MarkdownLanguage>;

pub struct MarkdownFormatContext {
pub struct MdFormatContext {
source_map: Option<TransformSourceMap>,
options: MarkdownFormatOptions,
options: MdFormatOptions,
comments: Rc<MarkdownComments>,
}

#[derive(Debug, Default, Clone, PartialEq)]
pub struct MarkdownFormatOptions {
pub struct MdFormatOptions {
indent_style: IndentStyle,
indent_width: IndentWidth,
line_ending: LineEnding,
line_width: LineWidth,
trailing_newline: TrailingNewline,
}

impl CstFormatContext for MarkdownFormatContext {
impl CstFormatContext for MdFormatContext {
type Language = MarkdownLanguage;
type Style = MarkdownCommentStyle;
type CommentRule = FormatMarkdownLeadingComment;
Expand All @@ -35,7 +35,7 @@ impl CstFormatContext for MarkdownFormatContext {
}
}

impl FormatOptions for MarkdownFormatOptions {
impl FormatOptions for MdFormatOptions {
fn indent_style(&self) -> IndentStyle {
self.indent_style
}
Expand All @@ -61,7 +61,7 @@ impl FormatOptions for MarkdownFormatOptions {
}
}

impl MarkdownFormatOptions {
impl MdFormatOptions {
pub fn new() -> Self {
Self {
indent_style: IndentStyle::default(),
Expand Down Expand Up @@ -98,8 +98,8 @@ impl MarkdownFormatOptions {
}
}

impl MarkdownFormatContext {
pub fn new(options: MarkdownFormatOptions) -> Self {
impl MdFormatContext {
pub fn new(options: MdFormatOptions) -> Self {
Self {
options,
comments: Rc::new(MarkdownComments::default()),
Expand All @@ -117,8 +117,8 @@ impl MarkdownFormatContext {
}
}

impl FormatContext for MarkdownFormatContext {
type Options = MarkdownFormatOptions;
impl FormatContext for MdFormatContext {
type Options = MdFormatOptions;

fn options(&self) -> &Self::Options {
&self.options
Expand All @@ -129,8 +129,12 @@ impl FormatContext for MarkdownFormatContext {
}
}

impl fmt::Display for MarkdownFormatOptions {
impl fmt::Display for MdFormatOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
std::fmt::Debug::fmt(self, f)
writeln!(f, "Indent style: {}", self.indent_style)?;
writeln!(f, "Indent width: {}", self.indent_width.value())?;
writeln!(f, "Line ending: {}", self.line_ending)?;
writeln!(f, "Line width: {}", self.line_width.value())?;
writeln!(f, "Trailing newline: {}", self.trailing_newline.value())
}
}
66 changes: 55 additions & 11 deletions crates/biome_markdown_formatter/src/cst.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,74 @@
use crate::{MarkdownFormatContext, prelude::*};
use crate::{MdFormatContext, prelude::*};
use biome_formatter::trivia::{FormatToken, format_skipped_token_trivia};
use biome_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatResult};
use biome_markdown_syntax::{MarkdownSyntaxNode, map_syntax_node};
use biome_markdown_syntax::{
MarkdownLanguage, MarkdownSyntaxNode, MarkdownSyntaxToken, map_syntax_node,
};

#[derive(Debug, Copy, Clone, Default)]
pub struct FormatMarkdownSyntaxNode;
pub struct FormatMdSyntaxToken;

impl FormatRule<MarkdownSyntaxNode> for FormatMarkdownSyntaxNode {
type Context = MarkdownFormatContext;
impl FormatRule<MarkdownSyntaxToken> for FormatMdSyntaxToken {
type Context = MdFormatContext;

fn fmt(
&self,
token: &MarkdownSyntaxToken,
f: &mut Formatter<Self::Context>,
) -> FormatResult<()> {
f.state_mut().track_token(token);

self.format_skipped_token_trivia(token, f)?;
self.format_trimmed_token_trivia(token, f)
}
}

impl FormatToken<MarkdownLanguage, MdFormatContext> for FormatMdSyntaxToken {
fn format_skipped_token_trivia(
&self,
token: &MarkdownSyntaxToken,
f: &mut Formatter<MdFormatContext>,
) -> FormatResult<()> {
format_skipped_token_trivia(token).fmt(f)
}
}

impl FormatRule<MarkdownSyntaxNode> for FormatMdSyntaxToken {
type Context = MdFormatContext;

fn fmt(&self, node: &MarkdownSyntaxNode, f: &mut MarkdownFormatter) -> FormatResult<()> {
map_syntax_node!(node.clone(), node => node.format().fmt(f))
}
}

impl AsFormat<MarkdownFormatContext> for MarkdownSyntaxNode {
type Format<'a> = FormatRefWithRule<'a, Self, FormatMarkdownSyntaxNode>;
impl AsFormat<MdFormatContext> for MarkdownSyntaxNode {
type Format<'a> = FormatRefWithRule<'a, Self, FormatMdSyntaxToken>;

fn format(&self) -> Self::Format<'_> {
FormatRefWithRule::new(self, FormatMdSyntaxToken)
}
}

impl IntoFormat<MdFormatContext> for MarkdownSyntaxNode {
type Format = FormatOwnedWithRule<Self, FormatMdSyntaxToken>;

fn into_format(self) -> Self::Format {
FormatOwnedWithRule::new(self, FormatMdSyntaxToken)
}
}

impl AsFormat<MdFormatContext> for MarkdownSyntaxToken {
type Format<'a> = FormatRefWithRule<'a, Self, FormatMdSyntaxToken>;

fn format(&self) -> Self::Format<'_> {
FormatRefWithRule::new(self, FormatMarkdownSyntaxNode)
FormatRefWithRule::new(self, FormatMdSyntaxToken)
}
}

impl IntoFormat<MarkdownFormatContext> for MarkdownSyntaxNode {
type Format = FormatOwnedWithRule<Self, FormatMarkdownSyntaxNode>;
impl IntoFormat<MdFormatContext> for MarkdownSyntaxToken {
type Format = FormatOwnedWithRule<Self, FormatMdSyntaxToken>;

fn into_format(self) -> Self::Format {
FormatOwnedWithRule::new(self, FormatMarkdownSyntaxNode)
FormatOwnedWithRule::new(self, FormatMdSyntaxToken)
}
}
Loading