aboutsummaryrefslogtreecommitdiff
path: root/dev_tools
diff options
context:
space:
mode:
Diffstat (limited to 'dev_tools')
-rw-r--r--dev_tools/src/bin/docs-code-box-fix.rs165
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
+}