#!/usr/bin/env ruby fix = ARGV.shift if ARGV.first == "--fix" files = Array(ARGV.shift || Dir.glob("./**/*.md")) files.each do |file| content = File.read(file) # First, scan for all references ([#]: at beginning of line) # and look for duplicates refs = content.scan(/^\[\d+\]:/) dups = refs.combination(2).filter_map { |i, j| i if i == j } # If duplicate refs detected, this won't work, so bail out if dups.any? warn "Duplicate refs detected in #{file} (#{dups * ", "})" exit 1 end idx = 0 code_block = false cache = {} # Find all numbered links (in text + references) + code delimeters # (for fenced code blocks, "`" suffices because "```" is an odd number) updated = content.gsub(/\[\d+\]|`/) do |token| case token when "`" # We don't want to do substitution inside code blocks # (where `[#]` sometimes occurs) but we do want to toggle # whether or not we're in a code block code_block = !code_block token else # If we're in a code block, just return the token # Otherwise, check if the reference has already been assigned # - If so, return the cached value # - If not, bump the index, cache the value, and return it code_block ? token : cache[token] ||= "[#{idx += 1}]" end end if content != updated warn "Links in #{file} are not in order" if fix File.write(file, updated) else exit 1 end end end