diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-05-03 00:57:45 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-05-03 00:57:45 +0800 |
| commit | 8f44a3e779fb8ee66c8f51b49d6c77e5a80e3821 (patch) | |
| tree | 5b281084e889eabc941b8125cc3a66240ab9f287 /dev_tools | |
| parent | 332e52af1883bec57e2a6d1f1291e434f18cd0c9 (diff) | |
Add tool to fix blank lines around code blocks in Markdown
Diffstat (limited to 'dev_tools')
| -rw-r--r-- | dev_tools/src/bin/docs-code-box-fix.rs | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/dev_tools/src/bin/docs-code-box-fix.rs b/dev_tools/src/bin/docs-code-box-fix.rs new file mode 100644 index 0000000..0212259 --- /dev/null +++ b/dev_tools/src/bin/docs-code-box-fix.rs @@ -0,0 +1,165 @@ +use std::fs; +use std::path::Path; + +/// Docsify code blocks require that blank lines before and after code blocks are not completely empty, +/// but must contain at least one space, otherwise code block rendering will have issues. +/// +/// This tool scans all `.md` files in the docs directory, +/// and replaces completely empty lines before and after code blocks with blank lines containing a single space. + +const DOCS_DIR: &str = "./docs"; + +fn main() { + println!("Fixing code box empty lines in docs/**/*.md ..."); + let repo_root = find_git_repo().expect("Cannot find git repo root"); + let docs_dir = repo_root.join(DOCS_DIR); + + let mut fixed_count = 0; + let mut file_count = 0; + + collect_md_files(&docs_dir, &mut |path| { + if let Some(name) = path.file_name() { + let name = name.to_string_lossy(); + if name.to_lowercase() == "_sidebar.md" { + return; + } + } + + let content = fs::read_to_string(path).unwrap_or_default(); + if content.is_empty() { + return; + } + + let new_content = fix_code_box_empty_lines(&content); + if new_content != content { + fs::write(path, &new_content).unwrap(); + println!(" Fixed: {}", path.display()); + fixed_count += 1; + } + file_count += 1; + }); + + println!( + "Done. Scanned {} files, fixed {} files.", + file_count, fixed_count + ); +} + +fn fix_code_box_empty_lines(content: &str) -> String { + let mut result = String::new(); + let lines: Vec<&str> = content.lines().collect(); + let len = lines.len(); + + let mut i = 0; + while i < len { + let line = lines[i]; + + // detect beginning of code block: beginning with ``` + if line.trim_start().starts_with("```") { + // record the beginning line of the code block + result.push_str(line); + result.push('\n'); + i += 1; + + // find the end of the code block + let mut found_end = false; + let code_start = i; // record starting position of code content + let mut code_end = len; // index of code block end line + + while i < len { + let cline = lines[i]; + if cline.trim_start().starts_with("```") && cline.trim() != "" { + // this is the closing marker + code_end = i; + found_end = true; + break; + } + i += 1; + } + + // check the blank line before the code block + // if result ends with \n\n, add a space to turn it into \n \n + ensure_space_before_code_block(&mut result); + + // output code content + for j in code_start..code_end { + let code_line = lines[j]; + if code_line.is_empty() { + result.push(' '); + } else { + result.push_str(code_line); + } + result.push('\n'); + } + + if found_end { + result.push_str(lines[code_end]); + result.push('\n'); + i += 1; + + // check the blank line after the code block + // if the next line is blank, change it to one with a space + if i < len && lines[i].trim().is_empty() && lines[i].is_empty() { + // skip the original blank line, write " \n" + result.push(' '); + result.push('\n'); + i += 1; + } + } + } else { + result.push_str(line); + result.push('\n'); + i += 1; + } + } + + // remove trailing newlines + while result.ends_with('\n') { + result.pop(); + } + result.push('\n'); + + result +} + +/// ensure there is a blank line with a space before the code block +fn ensure_space_before_code_block(result: &mut String) { + // if result ends with \n\n, + // turn it into \n \n + let len = result.len(); + if len >= 2 && result[len - 2..] == *"\n\n" { + // insert a space before the last \n + result.insert(len - 1, ' '); + } +} + +/// recursively collect all .md files in the docs directory +fn collect_md_files(dir: &Path, callback: &mut dyn FnMut(&Path)) { + if let Ok(entries) = fs::read_dir(dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.is_dir() { + collect_md_files(&path, callback); + } else if path.extension().is_some_and(|ext| ext == "md") { + callback(&path); + } + } + } +} + +fn find_git_repo() -> Option<std::path::PathBuf> { + let mut current_dir = std::env::current_dir().ok()?; + + loop { + let git_dir = current_dir.join(".git"); + if git_dir.exists() && git_dir.is_dir() { + return Some(current_dir); + } + + if !current_dir.pop() { + break; + } + } + + None +} |
