From cf12978037269a0240378ac9411f735837f32b05 Mon Sep 17 00:00:00 2001 From: jdp Date: Tue, 16 Mar 2010 05:17:17 -0400 Subject: [PATCH 1/6] generalized it so that pygment lexer and comment characters can be specified, made shebang line ignore more clear --- bin/rocco | 9 +++++++-- lib/rocco.rb | 29 ++++++++++++++++++++--------- lib/rocco/tasks.rb | 16 ++++++++++++++-- rocco.gemspec | 2 +- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/bin/rocco b/bin/rocco index 5ba062c..d09df4c 100755 --- a/bin/rocco +++ b/bin/rocco @@ -28,9 +28,12 @@ end # Parse command line options, aborting if anything goes wrong. output_dir = '.' sources = [] +options = {} ARGV.options { |o| o.program_name = File.basename($0) 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.parse! } or abort_with_note @@ -76,8 +79,10 @@ Dir.mkdir output_dir if !File.directory?(output_dir) # Run each file through Rocco and write output. sources.each do |filename| - rocco = Rocco.new(filename, sources) - dest = File.join(output_dir, File.basename(filename, '.rb') + '.html') + rocco = Rocco.new(filename, sources, options) + fnparts = filename.split('.') + fnparts.pop + dest = File.join(output_dir, fnparts.join('.') + '.html') puts "rocco: #{filename} -> #{dest}" File.open(dest, 'wb') { |fd| fd.write(rocco.to_html) } end diff --git a/lib/rocco.rb b/lib/rocco.rb index 75c3e31..f73425c 100644 --- a/lib/rocco.rb +++ b/lib/rocco.rb @@ -57,13 +57,18 @@ end #### Public Interface # `Rocco.new` takes a source `filename`, an optional list of source filenames -# for other documentation sources, and an optional `block`. 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. +# for other documentation sources, an `options` hash, and an optional `block`. +# The `options` hash respects two members: `:language`, which specifies which +# 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 VERSION = '0.2' - def initialize(filename, sources=[], &block) + def initialize(filename, sources=[], options={}, &block) @file = filename @data = if block_given? @@ -71,7 +76,10 @@ class Rocco else File.read(filename) end + defaults = { :language => 'ruby', :comment_chars => '#' } + @options = defaults.merge(options) @sources = sources + @comment_pattern = Regexp.new("^\\s*#{@options[:comment_chars]}") @sections = highlight(split(parse(@data))) end @@ -98,13 +106,16 @@ class Rocco # 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 - # 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) sections = [] docs, code = [], [] - data.split("\n").each do |line| + lines = data.split("\n") + lines.shift if lines[0] =~ /^\#\!/ + lines.each do |line| case line - when /^\s*#(?!\!)/ + when @comment_pattern if code.any? sections << [docs, code] docs, code = [], [] @@ -124,7 +135,7 @@ class Rocco def split(sections) docs_blocks, code_blocks = [], [] 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") end [docs_blocks, code_blocks] @@ -147,7 +158,7 @@ class Rocco # Pygments. We `popen` a read/write pygmentize process in the parent and # then fork off a child process to write the input. code_html = nil - open("|pygmentize -l ruby -f html", 'r+') do |fd| + open("|pygmentize -l #{@options[:language]} -f html", 'r+') do |fd| pid = fork { fd.close_read diff --git a/lib/rocco/tasks.rb b/lib/rocco/tasks.rb index 286eb54..03b2832 100644 --- a/lib/rocco/tasks.rb +++ b/lib/rocco/tasks.rb @@ -33,6 +33,15 @@ # # 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 # will have to do for now. @@ -49,10 +58,11 @@ class Rocco # `Rocco::Task.new` takes a task name, the destination directory docs # should be built under, and a source file pattern or file list. class Task - def initialize(task_name, dest='docs/', sources='lib/**/*.rb') + def initialize(task_name, dest='docs/', sources='lib/**/*.rb', options={}) @name = task_name @dest = dest[-1] == ?/ ? dest : "#{dest}/" @sources = FileList[sources] + @options = options # Make sure there's a `directory` task defined for our destination. define_directory_task @dest @@ -67,6 +77,8 @@ class Rocco # That way all Rocco generated are removed when running `rake clean`. CLEAN.include "#{@dest}#{dest_file}" if defined? CLEAN end + + # Pass end # Define the destination directory task and make the `:rocco` task depend @@ -92,7 +104,7 @@ class Rocco prerequisites = [@dest, source_file] + rocco_source_files file dest_file => prerequisites do |f| 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) } end task @name => dest_file diff --git a/rocco.gemspec b/rocco.gemspec index 2c63ca4..35c86b9 100644 --- a/rocco.gemspec +++ b/rocco.gemspec @@ -4,7 +4,7 @@ Gem::Specification.new do |s| s.name = 'rocco' s.version = '0.2' - s.date = '2010-03-11' + s.date = '2010-03-16' s.description = "Docco in Ruby" s.summary = s.description From bf401ef38b1f1f3f6786a0d62a28572e4da5b117 Mon Sep 17 00:00:00 2001 From: jdp Date: Tue, 16 Mar 2010 05:19:53 -0400 Subject: [PATCH 2/6] quick cleanup of docs i never erased --- lib/rocco/tasks.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/rocco/tasks.rb b/lib/rocco/tasks.rb index 03b2832..1d2edbb 100644 --- a/lib/rocco/tasks.rb +++ b/lib/rocco/tasks.rb @@ -77,8 +77,6 @@ class Rocco # That way all Rocco generated are removed when running `rake clean`. CLEAN.include "#{@dest}#{dest_file}" if defined? CLEAN end - - # Pass end # Define the destination directory task and make the `:rocco` task depend From 37aeba2247c675bc2072c0387276b495e64d1603 Mon Sep 17 00:00:00 2001 From: jdp Date: Tue, 16 Mar 2010 05:27:49 -0400 Subject: [PATCH 3/6] updated usage for bin/rocco --- bin/rocco | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/rocco b/bin/rocco index d09df4c..2a1254f 100755 --- a/bin/rocco +++ b/bin/rocco @@ -1,11 +1,14 @@ #!/usr/bin/env ruby -#/ Usage: rocco [-o ] ... +#/ Usage: rocco [-l ] [-c ] [-o ] ... #/ Generate literate-programming-style documentation for Ruby source s. #/ #/ Options: -#/ -o, --output= Directory where generated HTML files are written +#/ -l, --language= The Pygments lexer to use to highlight code +#/ -c, --comment-chars= +#/ The string to recognize as a comment marker +#/ -o, --output= Directory where generated HTML files are written #/ -#/ --help Show this help message +#/ --help Show this help message require 'optparse' From 3b48e38cba14ef955695a10344d91bea5792bdbe Mon Sep 17 00:00:00 2001 From: jdp Date: Tue, 16 Mar 2010 22:58:29 -0400 Subject: [PATCH 4/6] leading tabs in code are now replaced with two spaces! --- lib/rocco.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rocco.rb b/lib/rocco.rb index f73425c..3ae9737 100644 --- a/lib/rocco.rb +++ b/lib/rocco.rb @@ -136,7 +136,10 @@ class Rocco docs_blocks, code_blocks = [], [] sections.each do |docs,code| 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 [docs_blocks, code_blocks] end From 3af16f3afe92a7796109a516bdc9b5f4e6acfce6 Mon Sep 17 00:00:00 2001 From: jdp Date: Wed, 17 Mar 2010 00:41:06 -0400 Subject: [PATCH 5/6] table of contents generation works properly again --- bin/rocco | 3 +-- lib/rocco/layout.rb | 3 ++- rocco.gemspec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/rocco b/bin/rocco index 2a1254f..b20f74e 100755 --- a/bin/rocco +++ b/bin/rocco @@ -84,8 +84,7 @@ Dir.mkdir output_dir if !File.directory?(output_dir) sources.each do |filename| rocco = Rocco.new(filename, sources, options) fnparts = filename.split('.') - fnparts.pop - dest = File.join(output_dir, fnparts.join('.') + '.html') + dest = fnparts.slice(0, fnparts.length - 1).join('.') + '.html' puts "rocco: #{filename} -> #{dest}" File.open(dest, 'wb') { |fd| fd.write(rocco.to_html) } end diff --git a/lib/rocco/layout.rb b/lib/rocco/layout.rb index a2d7a80..e89dde2 100644 --- a/lib/rocco/layout.rb +++ b/lib/rocco/layout.rb @@ -28,10 +28,11 @@ class Rocco::Layout < Mustache def sources @doc.sources.sort.map do |source| + srcparts = File.basename(source).split('.') { :path => source, :basename => File.basename(source), - :url => File.basename(source, '.rb') + '.html' + :url => srcparts.slice(0, srcparts.length - 1).join('.') + '.html' } end end diff --git a/rocco.gemspec b/rocco.gemspec index 35c86b9..61d59b3 100644 --- a/rocco.gemspec +++ b/rocco.gemspec @@ -4,7 +4,7 @@ Gem::Specification.new do |s| s.name = 'rocco' s.version = '0.2' - s.date = '2010-03-16' + s.date = '2010-03-17' s.description = "Docco in Ruby" s.summary = s.description From 1978a5fe9820b84733de4c405f009922c69672b7 Mon Sep 17 00:00:00 2001 From: jdp Date: Wed, 17 Mar 2010 02:35:06 -0400 Subject: [PATCH 6/6] made filename munging more idiomatic, rake tasks work properly again --- bin/rocco | 3 +-- lib/rocco/layout.rb | 3 +-- lib/rocco/tasks.rb | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bin/rocco b/bin/rocco index b20f74e..5f03958 100755 --- a/bin/rocco +++ b/bin/rocco @@ -83,8 +83,7 @@ Dir.mkdir output_dir if !File.directory?(output_dir) # Run each file through Rocco and write output. sources.each do |filename| rocco = Rocco.new(filename, sources, options) - fnparts = filename.split('.') - dest = fnparts.slice(0, fnparts.length - 1).join('.') + '.html' + dest = filename.split('.')[0..-2].join('.') + '.html' puts "rocco: #{filename} -> #{dest}" File.open(dest, 'wb') { |fd| fd.write(rocco.to_html) } end diff --git a/lib/rocco/layout.rb b/lib/rocco/layout.rb index e89dde2..5ad61dd 100644 --- a/lib/rocco/layout.rb +++ b/lib/rocco/layout.rb @@ -28,11 +28,10 @@ class Rocco::Layout < Mustache def sources @doc.sources.sort.map do |source| - srcparts = File.basename(source).split('.') { :path => source, :basename => File.basename(source), - :url => srcparts.slice(0, srcparts.length - 1).join('.') + '.html' + :url => File.basename(source).split('.')[0..-2].join('.') + '.html' } end end diff --git a/lib/rocco/tasks.rb b/lib/rocco/tasks.rb index 1d2edbb..7c0ac6c 100644 --- a/lib/rocco/tasks.rb +++ b/lib/rocco/tasks.rb @@ -51,8 +51,8 @@ require 'rocco' # 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. class Rocco - def self.make(dest='docs/', source_files='lib/**/*.rb') - Task.new(:rocco, dest, source_files) + def self.make(dest='docs/', source_files='lib/**/*.rb', options={}) + Task.new(:rocco, dest, source_files, options) end # `Rocco::Task.new` takes a task name, the destination directory docs @@ -70,7 +70,7 @@ class Rocco # Run over the source file list, constructing destination filenames # and defining file tasks. @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}" # If `rake/clean` was required, add the generated files to the list.