pub mod bib;
pub mod config;
pub mod directives;
#[cfg(feature = "lsp")]
pub mod external_formatters;
#[cfg(any(feature = "lsp", not(target_arch = "wasm32")))]
mod external_formatters_common;
#[cfg(not(target_arch = "wasm32"))]
pub mod external_formatters_sync;
#[cfg(any(feature = "lsp", not(target_arch = "wasm32")))]
mod external_tools_common;
pub mod formatter;
pub mod includes;
pub mod linter;
#[cfg(feature = "lsp")]
pub mod lsp;
pub mod metadata;
pub mod parser;
pub mod range_utils;
pub mod salsa;
pub mod syntax;
mod utils;
mod yaml_engine;
#[cfg(test)]
mod yaml_regions;
pub use config::BlankLines;
pub use config::Config;
pub use config::ConfigBuilder;
#[cfg(any(feature = "lsp", not(target_arch = "wasm32")))]
pub use external_tools_common::set_warning_color_override;
pub use formatter::format_tree;
#[cfg(feature = "lsp")]
pub use formatter::format_tree_async;
pub use parser::parse;
pub use syntax::SyntaxNode;
pub fn markdown_extensions() -> &'static [&'static str] {
&["md", "markdown", "mdown", "mkd", "mkdn"]
}
pub fn all_document_extensions() -> &'static [&'static str] {
&[
"qmd",
"Rmd",
"rmd",
"Rmarkdown",
"rmarkdown",
"md",
"markdown",
"mdown",
"mkd",
"mkdn",
]
}
#[cfg(debug_assertions)]
fn init_logger() {
let _ = env_logger::builder().is_test(true).try_init();
}
fn detect_line_ending(input: &str) -> &str {
let rn_pos = input.find("\r\n");
let n_pos = input.find('\n');
if let (Some(rn), Some(n)) = (rn_pos, n_pos) {
if rn < n {
return "\r\n";
}
} else if rn_pos.is_some() {
return "\r\n";
}
"\n"
}
fn apply_line_ending(text: &str, target: &str) -> String {
if target == "\r\n" {
text.replace("\r\n", "\n").replace("\n", "\r\n")
} else {
text.replace("\r\n", "\n")
}
}
pub fn format(input: &str, config: Option<Config>, range: Option<(usize, usize)>) -> String {
#[cfg(debug_assertions)]
{
init_logger();
}
let config = config.unwrap_or_default();
let target_line_ending = match config.line_ending {
Some(config::LineEnding::Lf) => "\n",
Some(config::LineEnding::Crlf) => "\r\n",
Some(config::LineEnding::Auto) | None => {
detect_line_ending(input)
}
};
let tree = parser::parse(input, Some(config.clone()));
let expanded_range = range.and_then(|(start_line, end_line)| {
let result = range_utils::expand_line_range_to_blocks(&tree, input, start_line, end_line);
if let Some((start, end)) = result {
log::debug!(
"Range lines {}:{} expanded to byte range {}:{} (text: {:?}...{:?})",
start_line,
end_line,
start,
end,
&input[start..start.min(start + 20)],
&input[end.saturating_sub(20).max(start)..end]
);
}
result
});
let out = formatter::format_tree(&tree, &config, expanded_range);
apply_line_ending(&out, target_line_ending)
}
pub fn format_with_defaults(input: &str) -> String {
format(input, None, None)
}
#[cfg(feature = "lsp")]
pub async fn format_async(
input: &str,
config: Option<Config>,
range: Option<(usize, usize)>,
) -> String {
#[cfg(debug_assertions)]
{
init_logger();
}
let config = config.unwrap_or_default();
let target_line_ending = match config.line_ending {
Some(config::LineEnding::Lf) => "\n",
Some(config::LineEnding::Crlf) => "\r\n",
Some(config::LineEnding::Auto) | None => {
detect_line_ending(input)
}
};
let tree = parser::parse(input, Some(config.clone()));
let expanded_range = range.and_then(|(start_line, end_line)| {
range_utils::expand_line_range_to_blocks(&tree, input, start_line, end_line)
});
let out = formatter::format_tree_async(&tree, &config, expanded_range).await;
apply_line_ending(&out, target_line_ending)
}
#[cfg(feature = "lsp")]
pub async fn format_async_with_defaults(input: &str) -> String {
format_async(input, None, None).await
}