#!/usr/bin/env ruby
# coding: utf-8
# gendoc.rb -- Converts the top-comments inside module.c to modules API
# reference documentation in markdown format.
# Convert the C comment to markdown
def markdown(s)
s = s.gsub(/\*\/$/,"")
s = s.gsub(/^ ?\* ?/,"")
s = s.gsub(/^\/\*\*? ?/,"")
s.chop! while s[-1] == "\n" || s[-1] == " "
lines = s.split("\n")
newlines = []
# Fix some markdown
lines.each{|l|
# Rewrite VM_Xyz() to ValkeyModule_Xyz().
l = l.gsub(/(?\n\n"
puts "### `#{name}`\n\n"
puts " #{proto}\n"
puts "**Available since:** #{$since[shortname] or "unreleased"}\n\n"
comment = ""
while true
i = i-1
comment = src[i]+comment
break if src[i] =~ /\/\*/
end
comment = markdown(comment)
puts comment+"\n\n"
end
# Print a comment from line until */ is found, as markdown.
def section_doc(src, i)
name = get_section_heading(src, i)
comment = "\n\n"
while true
# append line, except if it's a horizontal divider
comment = comment + src[i] if src[i] !~ /^[\/ ]?\*{1,2} ?-{50,}/
break if src[i] =~ /\*\//
i = i+1
end
comment = markdown(comment)
puts comment+"\n\n"
end
# generates an id suitable for links within the page
def section_name_to_id(name)
return "section-" +
name.strip.downcase.gsub(/[^a-z0-9]+/, '-').gsub(/^-+|-+$/, '')
end
# Returns the name of the first section heading in the comment block for which
# is_section_doc(src, i) is true
def get_section_heading(src, i)
if src[i] =~ /^\/\*\*? \#+ *(.*)/
heading = $1
elsif src[i+1] =~ /^ ?\* \#+ *(.*)/
heading = $1
end
return heading.gsub(' -- ', ' – ')
end
# Returns true if the line is the start of a generic documentation section. Such
# section must start with the # symbol, i.e. a markdown heading, on the first or
# the second line.
def is_section_doc(src, i)
return src[i] =~ /^\/\*\*? \#/ ||
(src[i] =~ /^\/\*/ && src[i+1] =~ /^ ?\* \#/)
end
def is_func_line(src, i)
line = src[i]
return line =~ /VM_/ &&
line[0] != ' ' && line[0] != '#' && line[0] != '/' &&
src[i-1] =~ /\*\//
end
puts "---\n"
puts "title: \"Modules API reference\"\n"
puts "linkTitle: \"API reference\"\n"
puts "description: >\n"
puts " Reference for the Valkey Modules API\n"
puts "---\n"
puts "\n"
puts "\n\n"
src = File.open(File.dirname(__FILE__) ++ "/../src/module.c").to_a
# Build function index
$index = {}
src.each_with_index do |line,i|
if is_func_line(src, i)
line =~ /VM_([A-z0-9]+)/
name = "ValkeyModule_#{$1}"
$index[name] = true
end
end
# Populate the 'since' map (name => version) if we're in a git repo.
require "./" ++ File.dirname(__FILE__) ++ '/module-api-since.rb'
git_dir = File.dirname(__FILE__) ++ "/../.git"
if File.directory?(git_dir) && `which git` != ""
`git --git-dir="#{git_dir}" tag --sort=v:refname`.each_line do |version|
next if version !~ /^(\d+)\.\d+\.\d+?$/ || $1.to_i < 4
version.chomp!
`git --git-dir="#{git_dir}" cat-file blob "#{version}:src/module.c"`.each_line do |line|
if line =~ /^\w.*[ \*]VM_([A-z0-9]+)/
name = $1
if ! $since[name]
$since[name] = version
end
end
end
end
end
# Print TOC
puts "## Sections\n\n"
src.each_with_index do |_line,i|
if is_section_doc(src, i)
name = get_section_heading(src, i)
puts "* [#{name}](\##{section_name_to_id(name)})\n"
end
end
puts "* [Function index](#section-function-index)\n\n"
# Docufy: Print function prototype and markdown docs
src.each_with_index do |_line,i|
if is_func_line(src, i)
docufy(src, i)
elsif is_section_doc(src, i)
section_doc(src, i)
end
end
# Print function index
puts "\n\n"
puts "## Function index\n\n"
$index.keys.sort.each{|x| puts "* [`#{x}`](\##{x})\n"}
puts "\n"