provisioning/scripts/fix-md013-lines.nu

163 lines
4.8 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env nu
# Fix MD013 line length errors by wrapping long lines at natural break points
def wrap-line [line: string, max_len: int]: string -> string {
let len = $line | str length
# Skip if line is short enough
if $len <= $max_len {
return $line
}
# Skip table rows (can't easily wrap)
if ($line | str trim | str starts-with "|") {
return $line
}
# Skip code blocks (shouldn't wrap)
if ($line | str trim | str starts-with "```") {
return $line
}
# Skip heading lines
if ($line | str trim | str starts-with "#") {
return $line
}
# Skip list items that start with - or *
let trimmed = $line | str trim
if ($trimmed | str starts-with "- ") or ($trimmed | str starts-with "* ") or ($trimmed =~ '^\d+\. ') {
# List items - try to wrap at sentence boundaries
return (wrap-at-punctuation $line $max_len)
}
# Regular prose - wrap at sentence or clause boundaries
wrap-at-punctuation $line $max_len
}
def wrap-at-punctuation [line: string, max_len: int]: string -> string {
let len = $line | str length
if $len <= $max_len {
return $line
}
# Find the last space before max_len that follows punctuation or a good break point
let break_points = [". " ", " "; " ": " "- " "— " " and " " or " " but " " which " " that " " when " " where "]
mut best_pos = -1
for bp in $break_points {
# Find positions of this break point
let positions = find-all-positions $line $bp
for pos in $positions {
# Check if this position is valid (before max_len but not too early)
let break_at = $pos + ($bp | str length)
if $break_at <= $max_len and $break_at > $best_pos and $break_at > 50 {
$best_pos = $break_at
}
}
}
# If no good break point found, fall back to last space before max_len
if $best_pos == -1 {
let spaces = find-all-positions $line " "
for pos in ($spaces | reverse) {
if $pos < $max_len and $pos > 40 {
$best_pos = $pos + 1
break
}
}
}
# If still no break point, return as-is (can't wrap safely)
if $best_pos == -1 {
return $line
}
# Split at best position
let first = $line | str substring 0..$best_pos | str trim-end
let rest = $line | str substring $best_pos..
let rest_trimmed = $rest | str trim-start
# Return wrapped result
$"($first)\n($rest_trimmed)"
}
def find-all-positions [text: string, pattern: string]: list<int> {
mut positions = []
mut start = 0
let text_len = $text | str length
let pattern_len = $pattern | str length
while $start < ($text_len - $pattern_len) {
let sub = $text | str substring $start..($start + $pattern_len)
if $sub == $pattern {
$positions = ($positions | append $start)
}
$start = $start + 1
}
$positions
}
def process-file [file: path, max_len: int] {
let content = open $file --raw
let lines = $content | lines
mut in_code_block = false
mut new_lines = []
for line in $lines {
# Track code blocks
if ($line | str trim | str starts-with "```") {
$in_code_block = not $in_code_block
$new_lines = ($new_lines | append $line)
continue
}
# Don't process lines inside code blocks
if $in_code_block {
$new_lines = ($new_lines | append $line)
continue
}
# Wrap long lines
let wrapped = wrap-line $line $max_len
# Split wrapped result back into lines
let wrapped_lines = $wrapped | lines
$new_lines = ($new_lines | append $wrapped_lines)
}
let new_content = $new_lines | str join "\n"
$new_content | save -f $file
}
def main [
file?: path # Specific file to process (optional)
--max-len: int = 150 # Maximum line length
--dry-run (-d) # Show what would be changed without modifying files
] {
if $file != null {
print $"Processing: ($file)"
process-file $file $max_len
} else {
# Get files with MD013 errors from markdownlint
print "Getting files with MD013 errors..."
let result = (markdownlint-cli2 --config .markdownlint-cli2.jsonc "**/*.md" | complete)
let errors = $result.stderr | lines | where { $in | str contains "MD013" }
let files = $errors | each { $in | split row ":" | first } | uniq
print $"Found ($files | length) files with MD013 errors"
for f in $files {
if $dry_run {
print $"Would fix: ($f)"
} else {
print $"Fixing: ($f)"
process-file $f $max_len
}
}
}
print "Done"
}