Skip to content

Commit

Permalink
Update 30_generate_mermaid_nav.sh
Browse files Browse the repository at this point in the history
  • Loading branch information
gllmAR authored Dec 9, 2024
1 parent 8d8f522 commit ac56ec0
Showing 1 changed file with 78 additions and 159 deletions.
237 changes: 78 additions & 159 deletions .run/30_generate_mermaid_nav.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,229 +2,148 @@

EXCLUDED_DIRS=(".git" "node_modules" "__pycache__" ".vscode")

# Get the current working directory (root of the project) and ensure it ends with a slash
ROOT_DIR=$(pwd)
ROOT_DIR="${ROOT_DIR%/}/"

# Function to extract the title from README.md
# Extracts title from the first line starting with '# ' in README.md
get_title_from_readme() {
local readme_path="$1"
local title
title=$(grep -m 1 "^# " "$readme_path" | sed 's/^# //')
echo "$title"
}

# Function to check if README.md contains the generateMermaidNav comment
contains_generate_mermaid_nav_comment() {
local readme_path="$1"
grep -q '<!-- generateMermaidNav -->' "$readme_path"
grep -m 1 "^# " "$readme_path" | sed 's/^# //'
}

# Function to generate the Mermaid diagram content recursively
generate_mermaid_content() {
# Recursively generates a list of subdirectories that contain README.md, formatted as markdown links
generate_subnav_content() {
local parent_dir="$1"
local parent_id="$2"
local indent_level="$2"
local content_lines=()
local subdirs=()

# Remove trailing slash from parent_dir if any
parent_dir="${parent_dir%/}"

# Iterate over subdirectories
for subdir in "$parent_dir"/*/; do
if [[ -d "$subdir" ]]; then
# Remove trailing slash from subdir
subdir="${subdir%/}"

local base_dir
base_dir=$(basename "$subdir")

# Skip excluded directories
if [[ " ${EXCLUDED_DIRS[*]} " =~ " ${base_dir} " ]]; then
continue
fi

local subdir_readme="$subdir/README.md"
local subdir_title="$base_dir"

# Use title from README.md if available
if [[ -f "$subdir_readme" ]]; then
local title_from_readme
title_from_readme=$(get_title_from_readme "$subdir_readme")
if [[ -n "$title_from_readme" ]]; then
subdir_title="$title_from_readme"
local subdir_title
subdir_title=$(get_title_from_readme "$subdir_readme")
[[ -z "$subdir_title" ]] && subdir_title="$base_dir"

local relative_path="${subdir#$ROOT_DIR/}"
relative_path="${relative_path#/}"
relative_path="${relative_path%/}"

local link
if [[ -n "$relative_path" ]]; then
link="/${relative_path}/"
else
link="/"
fi
fi

# Get relative path from ROOT_DIR
local relative_subdir="${subdir#$ROOT_DIR}"
relative_subdir="${relative_subdir#/}" # Remove leading slash

# Generate a unique node ID based on relative path
local node_id
if [[ -z "$relative_subdir" ]]; then
node_id="root"
else
node_id=$(echo "$relative_subdir" | sed 's/[^a-zA-Z0-9]/_/g')
fi

# Construct the link path with /#/ prefix
local link_path="/#/$relative_subdir/"
# Ensure there are no double slashes
link_path=$(echo "$link_path" | sed 's#//*#/#g')

# Escape special characters in titles
subdir_title=$(echo "$subdir_title" | sed 's/"/\\"/g')

# Add the node definition with clickable link
content_lines+=(" $node_id[\"$subdir_title\"]")
content_lines+=(" click $node_id \"$link_path\"")
local indent=""
for ((i=0; i<indent_level; i++)); do
indent+=" " # 4 spaces
done
content_lines+=("${indent}* [${subdir_title}](${link})")

# Add the edge from parent to child if parent exists
if [[ -n "$parent_id" ]]; then
content_lines+=(" $parent_id --> $node_id")
local sub_content
sub_content=$(generate_subnav_content "$subdir" $((indent_level + 1)))
if [[ -n "$sub_content" ]]; then
content_lines+=("$sub_content")
fi
fi

# Collect subdirectories for further processing
subdirs+=("$subdir:$node_id")
fi
done

# Process subdirectories recursively
for entry in "${subdirs[@]}"; do
IFS=':' read -r subdir child_id <<< "$entry"
sub_content=$(generate_mermaid_content "$subdir" "$child_id")
content_lines+=("$sub_content")
done

# Return the content lines
printf "%s\n" "${content_lines[@]}"
}

# Function to update README.md with the new Mermaid diagram content
update_readme_with_mermaid() {
# Replaces content between <!-- start-replace-subnav --> and <!-- end-replace-subnav --> with new content
replace_content_between_tags() {
local readme_path="$1"
local mermaid_content="$2"
local tmpfile=$(mktemp)
local in_section=0
local start_tag_found=0
local end_tag_found=0

while IFS= read -r line || [ -n "$line" ]; do
if [[ "$line" == *'<!-- generateMermaidNav -->'* ]]; then
echo "$line" >> "$tmpfile"
in_section=1
start_tag_found=1
# Write the mermaid content immediately after the start tag
echo '```mermaid' >> "$tmpfile"
echo "$mermaid_content" >> "$tmpfile"
echo '```' >> "$tmpfile"
elif [[ "$line" == *'<!-- generateMermaidNavEnd -->'* ]]; then
echo "$line" >> "$tmpfile"
in_section=0
end_tag_found=1
elif [[ $in_section -eq 0 ]]; then
echo "$line" >> "$tmpfile"
fi
# If in_section is 1 (between tags), skip the line
done < "$readme_path"
local subnav_content="$2"

if [[ $start_tag_found -eq 1 && $end_tag_found -eq 0 ]]; then
# Add the end tag if it was missing
echo "<!-- generateMermaidNavEnd -->" >> "$tmpfile"
fi
if grep -q "<!-- start-replace-subnav -->" "$readme_path" && grep -q "<!-- end-replace-subnav -->" "$readme_path"; then
echo "Processing $readme_path..."

if [[ $start_tag_found -eq 0 ]]; then
# Start tag not found; append the content at the end of tmpfile
{
echo -e "\n<!-- generateMermaidNav -->"
echo '```mermaid'
echo "$mermaid_content"
echo '```'
echo "<!-- generateMermaidNavEnd -->"
} >> "$tmpfile"
fi
# Find line numbers of the tags
local start_line
start_line=$(grep -n "<!-- start-replace-subnav -->" "$readme_path" | cut -d: -f1 | head -n 1)
local end_line
end_line=$(grep -n "<!-- end-replace-subnav -->" "$readme_path" | cut -d: -f1 | head -n 1)

# Replace the original file with the updated content
mv "$tmpfile" "$readme_path"
}
# If for some reason start > end, skip
if [[ -z "$start_line" || -z "$end_line" || $start_line -gt $end_line ]]; then
echo "Skipping $readme_path, invalid tag order."
return
fi

# Function to generate Mermaid diagram in README.md
generate_mermaid_in_readme() {
local dir_path="$1"
local readme_path="$dir_path/README.md"
# Construct the updated file:
# 1. Lines before start tag
# 2. Start tag line
# 3. New subnav content
# 4. End tag line
# 5. Lines after end tag
sed -n "1,$((start_line-1))p" "$readme_path" > "$readme_path.tmp"
sed -n "${start_line}p" "$readme_path" >> "$readme_path.tmp"
printf "%s\n" "$subnav_content" >> "$readme_path.tmp"
sed -n "${end_line}p" "$readme_path" >> "$readme_path.tmp"
sed -n "$((end_line+1)),\$p" "$readme_path" >> "$readme_path.tmp"

mv "$readme_path.tmp" "$readme_path"

echo "Updated $readme_path with new subnav content."
else
echo "Skipping $readme_path, <!-- start-replace-subnav --> or <!-- end-replace-subnav --> not found."
fi
}

# Get relative path from ROOT_DIR
local relative_dir="${dir_path#$ROOT_DIR}"
relative_dir="${relative_dir#/}" # Remove leading slash
# Updates the given directory's README.md with subnavigation if README.md exists
generate_readme_in_subfolders() {
local parent_dir="$1"
local readme_path="$parent_dir/README.md"

# Generate the root node ID
local root_id
if [[ -z "$relative_dir" ]]; then
root_id="root"
else
root_id=$(echo "$relative_dir" | sed 's/[^a-zA-Z0-9]/_/g')
if [[ ! -f "$readme_path" ]]; then
return
fi

local title
title=$(get_title_from_readme "$readme_path")
if [[ -z "$title" ]]; then
title=$(basename "$dir_path")
fi
title=$(echo "$title" | sed 's/"/\\"/g')

# Start the Mermaid content as an array
local mermaid_lines=()
mermaid_lines+=("flowchart LR")
mermaid_lines+=(" $root_id[\"$title\"]")

# Generate the rest of the content
local sub_content
sub_content=$(generate_mermaid_content "$dir_path" "$root_id")
if [[ -n "$sub_content" ]]; then
mermaid_lines+=("$sub_content")
fi
[[ -z "$title" ]] && title=$(basename "$parent_dir")

# Combine the lines into a single string
local mermaid_content
mermaid_content=$(printf "%s\n" "${mermaid_lines[@]}")
local subnav_content
subnav_content=$(generate_subnav_content "$parent_dir" 0)

# Update the README.md with the new mermaid content
update_readme_with_mermaid "$readme_path" "$mermaid_content"
replace_content_between_tags "$readme_path" "$subnav_content"
}

# Function to walk through directories and generate the mermaid content
generate_mermaid_nav() {
# Recursively walk through directories to generate and insert subnav content
generate_subnav() {
local dir_path="$1"

# Remove trailing slash from dir_path if any
dir_path="${dir_path%/}"

# Check for README.md in the current directory
if [[ -f "$dir_path/README.md" ]]; then
# Check if README.md contains the generateMermaidNav comment
if contains_generate_mermaid_nav_comment "$dir_path/README.md"; then
generate_mermaid_in_readme "$dir_path"
fi
generate_readme_in_subfolders "$dir_path"
else
echo "Skipped directory (no README.md): $dir_path"
fi

# Recurse into subdirectories, avoiding excluded directories
for subdir in "$dir_path"/*/; do
# Ensure subdir is a directory
if [[ -d "$subdir" ]]; then
# Remove trailing slash from subdir
subdir="${subdir%/}"

local base_dir
base_dir=$(basename "$subdir")
if [[ ! " ${EXCLUDED_DIRS[*]} " =~ " ${base_dir} " ]]; then
generate_mermaid_nav "$subdir"
generate_subnav "$subdir"
fi
fi
done
}

# Start from the root directory
generate_mermaid_nav "$ROOT_DIR"
# Start from the current directory
generate_subnav "$ROOT_DIR"

0 comments on commit ac56ec0

Please sign in to comment.