Last active
November 13, 2025 16:11
-
-
Save toonetown/f7690761e2a91d6c43038e778851909d to your computer and use it in GitHub Desktop.
Fixes common markdown formatting issues to comply with GitHub Flavored Markdown standards
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| # encoding: UTF-8 | |
| # Markdown Formatter Preprocessor for Marked 2 | |
| # Fixes common markdown formatting issues to comply with GitHub Flavored Markdown standards | |
| # | |
| # This script reads markdown from STDIN and outputs formatted markdown to STDOUT. | |
| # It ensures proper blank lines before lists, headings, code blocks, and tables. | |
| # | |
| # Usage in Marked 2: | |
| # Settings > Advanced > Custom Preprocessor | |
| # Path: /opt/homebrew/bin/marked2-preprocessor | |
| # Check "Use by default" | |
| Encoding.default_external = Encoding::UTF_8 | |
| Encoding.default_internal = Encoding::UTF_8 | |
| def format_markdown(content) | |
| lines = content.split("\n") | |
| output = [] | |
| in_code_block = false | |
| in_table = false | |
| prev_line_blank = true | |
| code_fence_marker = nil | |
| lines.each_with_index do |line, index| | |
| stripped = line.strip | |
| prev_line = index > 0 ? lines[index - 1].strip : "" | |
| next_line = index < lines.length - 1 ? lines[index + 1].strip : "" | |
| # Track code blocks | |
| if stripped.start_with?('```') || stripped.start_with?('~~~') | |
| if !in_code_block | |
| # Starting a code block | |
| in_code_block = true | |
| code_fence_marker = stripped[0..2] | |
| # Ensure blank line before code block (unless at start of file or after blank line) | |
| if !prev_line_blank && index > 0 && !prev_line.empty? | |
| output << "" | |
| end | |
| output << line | |
| prev_line_blank = false | |
| next | |
| elsif stripped.start_with?(code_fence_marker) | |
| # Ending a code block | |
| in_code_block = false | |
| code_fence_marker = nil | |
| output << line | |
| prev_line_blank = false | |
| next | |
| end | |
| end | |
| # If inside code block, don't modify | |
| if in_code_block | |
| output << line | |
| prev_line_blank = false | |
| next | |
| end | |
| # Detect if line is a table row (starts with | or is a separator line) | |
| is_table_row = stripped.start_with?('|') || (stripped.match?(/^\|?[-:\s|]+\|?$/) && stripped.include?('-')) | |
| # Track table state | |
| if is_table_row && !in_table | |
| # Starting a table - ensure blank line before (unless at start or after blank line) | |
| if !prev_line_blank && index > 0 && !prev_line.empty? | |
| output << "" | |
| end | |
| in_table = true | |
| elsif !is_table_row && in_table | |
| # Exiting table | |
| in_table = false | |
| end | |
| # Detect headings (lines starting with #) | |
| if stripped.match?(/^[#]{1,6}\s+/) | |
| # Ensure blank line before heading (unless at start of file or after blank line) | |
| if !prev_line_blank && index > 0 && !prev_line.empty? | |
| output << "" | |
| end | |
| output << line | |
| prev_line_blank = false | |
| next | |
| end | |
| # Detect list items (lines starting with -, *, +, or number followed by .) | |
| is_list_item = stripped.match?(/^[-*+]\s+/) || stripped.match?(/^\d+\.\s+/) | |
| if is_list_item | |
| # Check if previous line was also a list item or blank | |
| prev_is_list = prev_line.match?(/^[-*+]\s+/) || prev_line.match?(/^\d+\.\s+/) | |
| # If starting a new list (not continuing), ensure blank line before | |
| if !prev_is_list && !prev_line_blank && index > 0 && !prev_line.empty? | |
| output << "" | |
| end | |
| output << line | |
| prev_line_blank = false | |
| next | |
| end | |
| # Handle blank lines | |
| if stripped.empty? | |
| # Only add blank line if the previous line wasn't already blank | |
| # This prevents multiple consecutive blank lines | |
| if !prev_line_blank | |
| output << line | |
| prev_line_blank = true | |
| end | |
| next | |
| end | |
| # Regular content line | |
| output << line | |
| prev_line_blank = false | |
| end | |
| # Join lines and ensure file ends with newline | |
| result = output.join("\n") | |
| result += "\n" unless result.end_with?("\n") | |
| result | |
| end | |
| # Read from STDIN | |
| input = STDIN.read | |
| # Format the markdown | |
| output = format_markdown(input) | |
| # Write to STDOUT | |
| STDOUT.write(output) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment