use rumdl_lib::code_block_tools::{CodeBlockDiagnostic, WriteBack, write_back_verified};
use rumdl_lib::config as rumdl_config;
use rumdl_lib::lint_context::LintContext;
use rumdl_lib::rule::Rule;
pub use rumdl_lib::embedded_lint::check_embedded_markdown_blocks;
pub(super) use rumdl_lib::embedded_lint::has_fenced_code_blocks;
pub(super) use rumdl_lib::embedded_lint::markdown_code_blocks;
pub(super) use rumdl_lib::embedded_lint::should_format_embedded_markdown;
pub(super) use rumdl_lib::embedded_lint::should_lint_embedded_markdown;
pub(super) const MAX_EMBEDDED_DEPTH: usize = rumdl_lib::embedded_lint::MAX_EMBEDDED_DEPTH;
#[derive(Debug, Default)]
pub struct EmbeddedFormatOutcome {
pub formatted: usize,
pub refused: Vec<rumdl_lib::rule::LintWarning>,
}
pub fn format_embedded_markdown_blocks(
content: &mut String,
rules: &[Box<dyn Rule>],
config: &rumdl_config::Config,
) -> EmbeddedFormatOutcome {
let mut outcome = EmbeddedFormatOutcome::default();
if rumdl_lib::merge_conflict::detect_for_analysis(content, rules, config, None).is_some() {
return outcome;
}
outcome.formatted = format_embedded_markdown_blocks_recursive(content, rules, config, 0, 0, &mut outcome.refused);
outcome
}
fn format_embedded_markdown_blocks_recursive(
content: &mut String,
rules: &[Box<dyn Rule>],
config: &rumdl_config::Config,
depth: usize,
line_offset: usize,
refused: &mut Vec<rumdl_lib::rule::LintWarning>,
) -> usize {
if depth >= MAX_EMBEDDED_DEPTH {
return 0;
}
if !has_fenced_code_blocks(content) {
return 0;
}
let blocks = markdown_code_blocks(content);
if blocks.is_empty() {
return 0;
}
let inline_config = rumdl_lib::inline_config::InlineConfig::from_content(content);
let mut write_backs = Vec::new();
let mut nested_counts = vec![0; blocks.len()];
for (index, block) in blocks.iter().enumerate() {
if block.code.trim().is_empty() {
continue;
}
let block_rules: Vec<Box<dyn Rule>> = rules
.iter()
.filter(|rule| !inline_config.is_rule_disabled(rule.name(), block.start_line + 2))
.map(|r| dyn_clone::clone_box(&**r))
.collect();
let had_trailing_newline = block.code.ends_with('\n');
let mut formatted = block.code.clone();
nested_counts[index] = format_embedded_markdown_blocks_recursive(
&mut formatted,
&block_rules,
config,
depth + 1,
line_offset + block.start_line + 1,
refused,
);
let ctx = LintContext::new(&formatted, config.markdown_flavor(), None);
let mut warnings = Vec::new();
for rule in &block_rules {
if let Ok(rule_warnings) = rule.check(&ctx) {
warnings.extend(rule_warnings);
}
}
if !warnings.is_empty() {
super::processing::apply_fixes_to_fragment(&block_rules, &mut formatted, config);
}
if !had_trailing_newline && formatted.ends_with('\n') {
formatted.pop();
}
if formatted != block.code {
write_backs.push(WriteBack { index, code: formatted });
}
}
let (result, refused_blocks) = write_back_verified(content, &blocks, &write_backs, markdown_code_blocks);
*content = result;
for &index in &refused_blocks {
let line = line_offset + blocks[index].start_line + 1;
refused.push(CodeBlockDiagnostic::refused_write_back(line).to_lint_warning());
}
write_backs
.iter()
.filter(|write_back| !refused_blocks.contains(&write_back.index))
.map(|write_back| 1 + nested_counts[write_back.index])
.sum()
}