Merge custom langs and comment chars from jdp/master
This commit is contained in:
commit
2f8337f49c
16
bin/rocco
16
bin/rocco
@ -1,11 +1,14 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
#/ Usage: rocco [-o <dir>] <file>...
|
#/ Usage: rocco [-l <lang>] [-c <chars>] [-o <dir>] <file>...
|
||||||
#/ Generate literate-programming-style documentation for Ruby source <file>s.
|
#/ Generate literate-programming-style documentation for Ruby source <file>s.
|
||||||
#/
|
#/
|
||||||
#/ Options:
|
#/ Options:
|
||||||
#/ -o, --output=<dir> Directory where generated HTML files are written
|
#/ -l, --language=<lang> The Pygments lexer to use to highlight code
|
||||||
|
#/ -c, --comment-chars=<chars>
|
||||||
|
#/ The string to recognize as a comment marker
|
||||||
|
#/ -o, --output=<dir> Directory where generated HTML files are written
|
||||||
#/
|
#/
|
||||||
#/ --help Show this help message
|
#/ --help Show this help message
|
||||||
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
|
|
||||||
@ -28,9 +31,12 @@ end
|
|||||||
# Parse command line options, aborting if anything goes wrong.
|
# Parse command line options, aborting if anything goes wrong.
|
||||||
output_dir = '.'
|
output_dir = '.'
|
||||||
sources = []
|
sources = []
|
||||||
|
options = {}
|
||||||
ARGV.options { |o|
|
ARGV.options { |o|
|
||||||
o.program_name = File.basename($0)
|
o.program_name = File.basename($0)
|
||||||
o.on("-o", "--output=DIR") { |dir| output_dir = dir }
|
o.on("-o", "--output=DIR") { |dir| output_dir = dir }
|
||||||
|
o.on("-l", "--language=LANG") { |lang| options[:language] = lang }
|
||||||
|
o.on("-c", "--comment-chars=CHARS") { |chars| options[:comment_chars] = Regexp.escape(chars) }
|
||||||
o.on_tail("-h", "--help") { usage($stdout, 0) }
|
o.on_tail("-h", "--help") { usage($stdout, 0) }
|
||||||
o.parse!
|
o.parse!
|
||||||
} or abort_with_note
|
} or abort_with_note
|
||||||
@ -76,8 +82,8 @@ Dir.mkdir output_dir if !File.directory?(output_dir)
|
|||||||
|
|
||||||
# Run each file through Rocco and write output.
|
# Run each file through Rocco and write output.
|
||||||
sources.each do |filename|
|
sources.each do |filename|
|
||||||
rocco = Rocco.new(filename, sources)
|
rocco = Rocco.new(filename, sources, options)
|
||||||
dest = File.join(output_dir, File.basename(filename, '.rb') + '.html')
|
dest = filename.split('.')[0..-2].join('.') + '.html'
|
||||||
puts "rocco: #{filename} -> #{dest}"
|
puts "rocco: #{filename} -> #{dest}"
|
||||||
File.open(dest, 'wb') { |fd| fd.write(rocco.to_html) }
|
File.open(dest, 'wb') { |fd| fd.write(rocco.to_html) }
|
||||||
end
|
end
|
||||||
|
34
lib/rocco.rb
34
lib/rocco.rb
@ -57,13 +57,18 @@ end
|
|||||||
#### Public Interface
|
#### Public Interface
|
||||||
|
|
||||||
# `Rocco.new` takes a source `filename`, an optional list of source filenames
|
# `Rocco.new` takes a source `filename`, an optional list of source filenames
|
||||||
# for other documentation sources, and an optional `block`. When `block` is
|
# for other documentation sources, an `options` hash, and an optional `block`.
|
||||||
# given, it must read the contents of the file using whatever means necessary
|
# The `options` hash respects two members: `:language`, which specifies which
|
||||||
# and return it as a string. With no `block`, the file is read to retrieve data.
|
# Pygments lexer to use; and `:comment_chars`, which specifies the comment
|
||||||
|
# characters of the target language. The options default to `'ruby'` and `'#'`,
|
||||||
|
# respectively.
|
||||||
|
# When `block` is given, it must read the contents of the file using whatever
|
||||||
|
# means necessary and return it as a string. With no `block`, the file is read
|
||||||
|
# to retrieve data.
|
||||||
class Rocco
|
class Rocco
|
||||||
VERSION = '0.3'
|
VERSION = '0.3'
|
||||||
|
|
||||||
def initialize(filename, sources=[], &block)
|
def initialize(filename, sources=[], options={}, &block)
|
||||||
@file = filename
|
@file = filename
|
||||||
@data =
|
@data =
|
||||||
if block_given?
|
if block_given?
|
||||||
@ -71,7 +76,10 @@ class Rocco
|
|||||||
else
|
else
|
||||||
File.read(filename)
|
File.read(filename)
|
||||||
end
|
end
|
||||||
|
defaults = { :language => 'ruby', :comment_chars => '#' }
|
||||||
|
@options = defaults.merge(options)
|
||||||
@sources = sources
|
@sources = sources
|
||||||
|
@comment_pattern = Regexp.new("^\\s*#{@options[:comment_chars]}")
|
||||||
@sections = highlight(split(parse(@data)))
|
@sections = highlight(split(parse(@data)))
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -98,13 +106,16 @@ class Rocco
|
|||||||
|
|
||||||
# Parse the raw file data into a list of two-tuples. Each tuple has the
|
# Parse the raw file data into a list of two-tuples. Each tuple has the
|
||||||
# form `[docs, code]` where both elements are arrays containing the
|
# form `[docs, code]` where both elements are arrays containing the
|
||||||
# raw lines parsed from the input file.
|
# raw lines parsed from the input file. The first line is ignored if it
|
||||||
|
# is a shebang line.
|
||||||
def parse(data)
|
def parse(data)
|
||||||
sections = []
|
sections = []
|
||||||
docs, code = [], []
|
docs, code = [], []
|
||||||
data.split("\n").each do |line|
|
lines = data.split("\n")
|
||||||
|
lines.shift if lines[0] =~ /^\#\!/
|
||||||
|
lines.each do |line|
|
||||||
case line
|
case line
|
||||||
when /^\s*#(?:\s+|$)/
|
when @comment_pattern
|
||||||
if code.any?
|
if code.any?
|
||||||
sections << [docs, code]
|
sections << [docs, code]
|
||||||
docs, code = [], []
|
docs, code = [], []
|
||||||
@ -124,8 +135,11 @@ class Rocco
|
|||||||
def split(sections)
|
def split(sections)
|
||||||
docs_blocks, code_blocks = [], []
|
docs_blocks, code_blocks = [], []
|
||||||
sections.each do |docs,code|
|
sections.each do |docs,code|
|
||||||
docs_blocks << docs.map { |line| line.sub(/^\s*#\s?/, '') }.join("\n")
|
docs_blocks << docs.map { |line| line.sub(@comment_pattern, '') }.join("\n")
|
||||||
code_blocks << code.join("\n")
|
code_blocks << code.map do |line|
|
||||||
|
tabs = line.match(/^(\t+)/)
|
||||||
|
tabs ? line.sub(/^\t+/, ' ' * tabs.captures[0].length) : line
|
||||||
|
end.join("\n")
|
||||||
end
|
end
|
||||||
[docs_blocks, code_blocks]
|
[docs_blocks, code_blocks]
|
||||||
end
|
end
|
||||||
@ -147,7 +161,7 @@ class Rocco
|
|||||||
# Pygments. We `popen` a read/write pygmentize process in the parent and
|
# Pygments. We `popen` a read/write pygmentize process in the parent and
|
||||||
# then fork off a child process to write the input.
|
# then fork off a child process to write the input.
|
||||||
code_html = nil
|
code_html = nil
|
||||||
open("|pygmentize -l ruby -f html", 'r+') do |fd|
|
open("|pygmentize -l #{@options[:language]} -f html", 'r+') do |fd|
|
||||||
pid =
|
pid =
|
||||||
fork {
|
fork {
|
||||||
fd.close_read
|
fd.close_read
|
||||||
|
@ -31,7 +31,7 @@ class Rocco::Layout < Mustache
|
|||||||
{
|
{
|
||||||
:path => source,
|
:path => source,
|
||||||
:basename => File.basename(source),
|
:basename => File.basename(source),
|
||||||
:url => File.basename(source, '.rb') + '.html'
|
:url => File.basename(source).split('.')[0..-2].join('.') + '.html'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -33,6 +33,15 @@
|
|||||||
#
|
#
|
||||||
# Rocco::make 'html/', ['lib/thing.rb', 'lib/thing/*.rb']
|
# Rocco::make 'html/', ['lib/thing.rb', 'lib/thing/*.rb']
|
||||||
#
|
#
|
||||||
|
# Finally, it is also possible to specify which Pygments language you would
|
||||||
|
# like to use to highlight the code, as well as the comment characters for the
|
||||||
|
# language in the `options` hash:
|
||||||
|
#
|
||||||
|
# Rocco::make 'html/', 'lib/thing/**/*.rb', {
|
||||||
|
# :language => 'io',
|
||||||
|
# :comment_chars => '#'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
|
||||||
# Might be nice to defer this until we actually need to build docs but this
|
# Might be nice to defer this until we actually need to build docs but this
|
||||||
# will have to do for now.
|
# will have to do for now.
|
||||||
@ -42,17 +51,18 @@ require 'rocco'
|
|||||||
# of sugar over `Rocco::Task.new`. If you want your Rake task to be named
|
# of sugar over `Rocco::Task.new`. If you want your Rake task to be named
|
||||||
# something other than `:rocco`, you can use `Rocco::Task` directly.
|
# something other than `:rocco`, you can use `Rocco::Task` directly.
|
||||||
class Rocco
|
class Rocco
|
||||||
def self.make(dest='docs/', source_files='lib/**/*.rb')
|
def self.make(dest='docs/', source_files='lib/**/*.rb', options={})
|
||||||
Task.new(:rocco, dest, source_files)
|
Task.new(:rocco, dest, source_files, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# `Rocco::Task.new` takes a task name, the destination directory docs
|
# `Rocco::Task.new` takes a task name, the destination directory docs
|
||||||
# should be built under, and a source file pattern or file list.
|
# should be built under, and a source file pattern or file list.
|
||||||
class Task
|
class Task
|
||||||
def initialize(task_name, dest='docs/', sources='lib/**/*.rb')
|
def initialize(task_name, dest='docs/', sources='lib/**/*.rb', options={})
|
||||||
@name = task_name
|
@name = task_name
|
||||||
@dest = dest[-1] == ?/ ? dest : "#{dest}/"
|
@dest = dest[-1] == ?/ ? dest : "#{dest}/"
|
||||||
@sources = FileList[sources]
|
@sources = FileList[sources]
|
||||||
|
@options = options
|
||||||
|
|
||||||
# Make sure there's a `directory` task defined for our destination.
|
# Make sure there's a `directory` task defined for our destination.
|
||||||
define_directory_task @dest
|
define_directory_task @dest
|
||||||
@ -60,7 +70,7 @@ class Rocco
|
|||||||
# Run over the source file list, constructing destination filenames
|
# Run over the source file list, constructing destination filenames
|
||||||
# and defining file tasks.
|
# and defining file tasks.
|
||||||
@sources.each do |source_file|
|
@sources.each do |source_file|
|
||||||
dest_file = File.basename(source_file, '.rb') + '.html'
|
dest_file = File.basename(source_file).split('.')[0..-2].join('.') + '.html'
|
||||||
define_file_task source_file, "#{@dest}#{dest_file}"
|
define_file_task source_file, "#{@dest}#{dest_file}"
|
||||||
|
|
||||||
# If `rake/clean` was required, add the generated files to the list.
|
# If `rake/clean` was required, add the generated files to the list.
|
||||||
@ -92,7 +102,7 @@ class Rocco
|
|||||||
prerequisites = [@dest, source_file] + rocco_source_files
|
prerequisites = [@dest, source_file] + rocco_source_files
|
||||||
file dest_file => prerequisites do |f|
|
file dest_file => prerequisites do |f|
|
||||||
verbose { puts "rocco: #{source_file} -> #{dest_file}" }
|
verbose { puts "rocco: #{source_file} -> #{dest_file}" }
|
||||||
rocco = Rocco.new(source_file, @sources.to_a)
|
rocco = Rocco.new(source_file, @sources.to_a, @options)
|
||||||
File.open(dest_file, 'wb') { |fd| fd.write(rocco.to_html) }
|
File.open(dest_file, 'wb') { |fd| fd.write(rocco.to_html) }
|
||||||
end
|
end
|
||||||
task @name => dest_file
|
task @name => dest_file
|
||||||
|
Loading…
Reference in New Issue
Block a user