Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2b5319377b | ||
|
7673d8104a | ||
|
7f4d68422f | ||
|
bb32ab6d27 |
63
CHANGES.md
63
CHANGES.md
@ -1,63 +0,0 @@
|
|||||||
CHANGES
|
|
||||||
=======
|
|
||||||
|
|
||||||
0.6 (2011-03-05)
|
|
||||||
----------------
|
|
||||||
|
|
||||||
This release brought to you almost entirely
|
|
||||||
by [mikewest](http://github.com/mikewest).
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Added `-t`/`--template` CLI option that allows you to specify a Mustache
|
|
||||||
template that ought be used when rendering the final documentation.
|
|
||||||
(Issue #16)
|
|
||||||
|
|
||||||
* More variables in templates:
|
|
||||||
* `docs?`: True if `docs` contains text of any sort, False if it's empty.
|
|
||||||
* `code?`: True if `code` contains text of any sort, False if it's empty.
|
|
||||||
* `empty?`: True if both `code` and `docs` are empty. False otherwise.
|
|
||||||
* `header?`: True if `docs` contains _only_ a HTML header. False otherwise.
|
|
||||||
|
|
||||||
* Test suite! (Run `rake test`)
|
|
||||||
|
|
||||||
* Autodetect file's language if Pygments is installed locally (Issue #19)
|
|
||||||
|
|
||||||
* Autopopulate comment characters for known languages (Issue #20)
|
|
||||||
|
|
||||||
* Correctly parse block comments (Issue #22)
|
|
||||||
|
|
||||||
* Stripping encoding definitions from Ruby and Python files in the same
|
|
||||||
way we strip shebang lines (Issue #21)
|
|
||||||
|
|
||||||
* Adjusting section IDs to contain descriptive test from headers. A header
|
|
||||||
section's ID might be `section-Header_text_goes_here` for friendlier URLs.
|
|
||||||
Other section IDs will remain the same (`section-2` will stay
|
|
||||||
`section-2`). (Issue #28)
|
|
||||||
|
|
||||||
### Bugs Fixed
|
|
||||||
|
|
||||||
* Docco's CSS changed: we updated Rocco's HTML accordingly, and pinned
|
|
||||||
the CSS file to Docco's 0.3.0 tag. (Issues #12 and #23)
|
|
||||||
|
|
||||||
* Fixed code highlighting for shell scripts (among others) (Issue #13)
|
|
||||||
|
|
||||||
* Fixed buggy regex for comment char stripping (Issue #15)
|
|
||||||
|
|
||||||
* Specifying UTF-8 encoding for Pygments (Issue #10)
|
|
||||||
|
|
||||||
* Extensionless file support (thanks to [Vasily Polovnyov][vast] for the
|
|
||||||
fix!) (Issue #24)
|
|
||||||
|
|
||||||
* Fixing language support for Pygments webservice (Issue #11)
|
|
||||||
|
|
||||||
* The source jumplist now generates correctly relative URLs (Issue #26)
|
|
||||||
|
|
||||||
* Fixed an issue with using mustache's `template_path=` incorrectly.
|
|
||||||
|
|
||||||
[vast]: https://github.com/vast
|
|
||||||
|
|
||||||
0.5
|
|
||||||
---
|
|
||||||
|
|
||||||
Rocco 0.5 emerged from the hazy mists, complete and unfettered by history.
|
|
18
COPYING
18
COPYING
@ -1,18 +0,0 @@
|
|||||||
Copyright (c) 2010 Ryan Tomayko <http://tomayko.com/about>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to
|
|
||||||
deal in the Software without restriction, including without limitation the
|
|
||||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
sell copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
README
23
README
@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
___ ___ ___ ___ ___
|
|
||||||
/\ \ /\ \ /\ \ /\ \ /\ \
|
|
||||||
/::\ \ /::\ \ /::\ \ /::\ \ /::\ \
|
|
||||||
/::\:\__\ /:/\:\__\ /:/\:\__\ /:/\:\__\ /:/\:\__\
|
|
||||||
\;:::/ / \:\/:/ / \:\ \/__/ \:\ \/__/ \:\/:/ /
|
|
||||||
|:\/__/ \::/ / \:\__\ \:\__\ \::/ /
|
|
||||||
\|__| \/__/ \/__/ \/__/ \/__/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Rocco is a quick-and-dirty, literate-programming-style documentation
|
|
||||||
generator for Ruby. See the Rocco generated docs for more information:
|
|
||||||
|
|
||||||
<http://rtomayko.github.com/rocco/>
|
|
||||||
|
|
||||||
|
|
||||||
Rocco is a port of, and borrows heavily from, Docco -- the original
|
|
||||||
quick-and-dirty, hundred-line-long, literate-programming-style
|
|
||||||
documentation generator in CoffeeScript:
|
|
||||||
|
|
||||||
<http://jashkenas.github.com/docco/>
|
|
115
Rakefile
115
Rakefile
@ -1,115 +0,0 @@
|
|||||||
$LOAD_PATH.unshift 'lib'
|
|
||||||
|
|
||||||
require 'rake/testtask'
|
|
||||||
require 'rake/clean'
|
|
||||||
|
|
||||||
task :default => [:sup, :docs, :test]
|
|
||||||
|
|
||||||
desc 'Holla'
|
|
||||||
task :sup do
|
|
||||||
verbose do
|
|
||||||
lines = File.read('README').split("\n")[0,12]
|
|
||||||
lines.map! { |line| line[15..-1] }
|
|
||||||
puts lines.join("\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Run tests (default)'
|
|
||||||
Rake::TestTask.new(:test) do |t|
|
|
||||||
t.test_files = FileList['test/suite.rb']
|
|
||||||
t.ruby_opts = ['-rubygems'] if defined? Gem
|
|
||||||
end
|
|
||||||
|
|
||||||
# Bring in Rocco tasks
|
|
||||||
require 'rocco/tasks'
|
|
||||||
Rocco::make 'docs/'
|
|
||||||
|
|
||||||
desc 'Build rocco docs'
|
|
||||||
task :docs => :rocco
|
|
||||||
directory 'docs/'
|
|
||||||
|
|
||||||
desc 'Build docs and open in browser for the reading'
|
|
||||||
task :read => :docs do
|
|
||||||
sh 'open docs/lib/rocco.html'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Make index.html a copy of rocco.html
|
|
||||||
file 'docs/index.html' => 'docs/lib/rocco.html' do |f|
|
|
||||||
cp 'docs/lib/rocco.html', 'docs/index.html', :preserve => true
|
|
||||||
end
|
|
||||||
task :docs => 'docs/index.html'
|
|
||||||
CLEAN.include 'docs/index.html'
|
|
||||||
|
|
||||||
# Alias for docs task
|
|
||||||
task :doc => :docs
|
|
||||||
|
|
||||||
# GITHUB PAGES ===============================================================
|
|
||||||
|
|
||||||
desc 'Update gh-pages branch'
|
|
||||||
task :pages => ['docs/.git', :docs] do
|
|
||||||
rev = `git rev-parse --short HEAD`.strip
|
|
||||||
Dir.chdir 'docs' do
|
|
||||||
sh "git add *.html"
|
|
||||||
sh "git commit -m 'rebuild pages from #{rev}'" do |ok,res|
|
|
||||||
if ok
|
|
||||||
verbose { puts "gh-pages updated" }
|
|
||||||
sh "git push -q o HEAD:gh-pages"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the pages/ directory clone
|
|
||||||
file 'docs/.git' => ['docs/', '.git/refs/heads/gh-pages'] do |f|
|
|
||||||
sh "cd docs && git init -q && git remote add o ../.git" if !File.exist?(f.name)
|
|
||||||
sh "cd docs && git fetch -q o && git reset -q --hard o/gh-pages && touch ."
|
|
||||||
end
|
|
||||||
CLOBBER.include 'docs/.git'
|
|
||||||
|
|
||||||
# PACKAGING =================================================================
|
|
||||||
|
|
||||||
if defined?(Gem)
|
|
||||||
SPEC = eval(File.read('rocco.gemspec'))
|
|
||||||
|
|
||||||
def package(ext='')
|
|
||||||
"pkg/rocco-#{SPEC.version}" + ext
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Build packages'
|
|
||||||
task :package => %w[.gem .tar.gz].map {|e| package(e)}
|
|
||||||
|
|
||||||
desc 'Build and install as local gem'
|
|
||||||
task :install => package('.gem') do
|
|
||||||
sh "gem install #{package('.gem')}"
|
|
||||||
end
|
|
||||||
|
|
||||||
directory 'pkg/'
|
|
||||||
|
|
||||||
file package('.gem') => %w[pkg/ rocco.gemspec] + SPEC.files do |f|
|
|
||||||
sh "gem build rocco.gemspec"
|
|
||||||
mv File.basename(f.name), f.name
|
|
||||||
end
|
|
||||||
|
|
||||||
file package('.tar.gz') => %w[pkg/] + SPEC.files do |f|
|
|
||||||
sh "git archive --format=tar HEAD | gzip > #{f.name}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# GEMSPEC ===================================================================
|
|
||||||
|
|
||||||
file 'rocco.gemspec' => FileList['{lib,test,bin}/**','Rakefile'] do |f|
|
|
||||||
version = File.read('lib/rocco.rb')[/VERSION = '(.*)'/] && $1
|
|
||||||
date = Time.now.strftime("%Y-%m-%d")
|
|
||||||
spec = File.
|
|
||||||
read(f.name).
|
|
||||||
sub(/s\.version\s*=\s*'.*'/, "s.version = '#{version}'")
|
|
||||||
parts = spec.split(" # = MANIFEST =\n")
|
|
||||||
files = `git ls-files`.
|
|
||||||
split("\n").sort.reject{ |file| file =~ /^\./ }.
|
|
||||||
map{ |file| " #{file}" }.join("\n")
|
|
||||||
parts[1] = " s.files = %w[\n#{files}\n ]\n"
|
|
||||||
spec = parts.join(" # = MANIFEST =\n")
|
|
||||||
spec.sub!(/s.date = '.*'/, "s.date = '#{date}'")
|
|
||||||
File.open(f.name, 'w') { |io| io.write(spec) }
|
|
||||||
puts "#{f.name} #{version} (#{date})"
|
|
||||||
end
|
|
100
bin/rocco
100
bin/rocco
@ -1,100 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
#/ Usage: rocco [-l <lang>] [-c <chars>] [-o <dir>] <file>...
|
|
||||||
#/ Generate literate-programming-style documentation for Ruby source <file>s.
|
|
||||||
#/
|
|
||||||
#/ Options:
|
|
||||||
#/ -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
|
|
||||||
#/ -t, --template=<path> The file to use as template when rendering HTML
|
|
||||||
#/ -d, --docblocks Parse Docblock @annotations in comments
|
|
||||||
#/ --help Show this help message
|
|
||||||
|
|
||||||
require 'optparse'
|
|
||||||
require 'fileutils'
|
|
||||||
|
|
||||||
# Write usage message to stdout and exit.
|
|
||||||
def usage(stream=$stderr, status=1)
|
|
||||||
stream.puts File.readlines(__FILE__).
|
|
||||||
grep(/^#\//).
|
|
||||||
map { |line| line.sub(/^#. ?/, '') }.
|
|
||||||
join
|
|
||||||
exit status
|
|
||||||
end
|
|
||||||
|
|
||||||
# Like `Kernel#abort` but writes a note encouraging the user to consult
|
|
||||||
# `rocco --help` for more information.
|
|
||||||
def abort_with_note(message=nil)
|
|
||||||
$stderr.puts message if message
|
|
||||||
abort "See `rocco --help' for usage information."
|
|
||||||
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("-t", "--template=TEMPLATE") { |template| options[:template_file] = template }
|
|
||||||
o.on("-d", "--docblocks") { options[:docblocks] = true }
|
|
||||||
o.on_tail("-h", "--help") { usage($stdout, 0) }
|
|
||||||
o.parse!
|
|
||||||
} or abort_with_note
|
|
||||||
|
|
||||||
# Use http://pygments.appspot.com in case `pygmentize(1)` isn't available.
|
|
||||||
if ! ENV['PATH'].split(':').any? { |dir| File.exist?("#{dir}/pygmentize") }
|
|
||||||
unless options[:webservice]
|
|
||||||
$stderr.puts "pygmentize not in PATH; using pygments.appspot.com instead"
|
|
||||||
options[:webservice] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Eat sources from ARGV.
|
|
||||||
sources << ARGV.shift while ARGV.any?
|
|
||||||
|
|
||||||
# Make sure we have some files to work with.
|
|
||||||
if sources.empty?
|
|
||||||
abort_with_note "#{File.basename($0)}: no input <file>s given"
|
|
||||||
end
|
|
||||||
|
|
||||||
# What a fucking mess. Most of this is duplicated in rocco.rb too.
|
|
||||||
libdir = File.expand_path('../../lib', __FILE__).sub(/^#{Dir.pwd}\//, '')
|
|
||||||
begin
|
|
||||||
require 'rdiscount'
|
|
||||||
require 'rocco'
|
|
||||||
rescue LoadError
|
|
||||||
case $!.to_s
|
|
||||||
when /rdiscount/
|
|
||||||
if !defined?(Gem)
|
|
||||||
warn "warn: #$!. trying again with rubygems"
|
|
||||||
require 'rubygems'
|
|
||||||
retry
|
|
||||||
else
|
|
||||||
require 'bluecloth'
|
|
||||||
Markdown = BlueCloth
|
|
||||||
$LOADED_FEATURES << 'rdiscount.rb'
|
|
||||||
retry
|
|
||||||
end
|
|
||||||
when /rocco/
|
|
||||||
if !$:.include?(libdir)
|
|
||||||
warn "warn: #$!. trying again with #{libdir} on load path"
|
|
||||||
$:.unshift(libdir)
|
|
||||||
retry
|
|
||||||
end
|
|
||||||
end
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run each file through Rocco and write output.
|
|
||||||
sources.each do |filename|
|
|
||||||
rocco = Rocco.new(filename, sources, options)
|
|
||||||
dest = filename.sub(Regexp.new("#{File.extname(filename)}$"),".html")
|
|
||||||
dest = File.join(output_dir, dest) if output_dir != '.'
|
|
||||||
puts "rocco: #{filename} -> #{dest}"
|
|
||||||
FileUtils.mkdir_p File.dirname(dest)
|
|
||||||
File.open(dest, 'wb') { |fd| fd.write(rocco.to_html) }
|
|
||||||
end
|
|
822
index.html
Normal file
822
index.html
Normal file
@ -0,0 +1,822 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||||
|
<title>rocco.rb</title>
|
||||||
|
<link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='container'>
|
||||||
|
<div id="background"></div>
|
||||||
|
<div id="jump_to">
|
||||||
|
Jump To …
|
||||||
|
<div id="jump_wrapper">
|
||||||
|
<div id="jump_page">
|
||||||
|
<a class="source" href="rocco.html">rocco.rb</a>
|
||||||
|
<a class="source" href="rocco/layout.html">layout.rb</a>
|
||||||
|
<a class="source" href="rocco/tasks.html">tasks.rb</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table cellspacing=0 cellpadding=0>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class=docs><h1>rocco.rb</h1></th>
|
||||||
|
<th class=code></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr id='section-1'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-1">¶</a>
|
||||||
|
</div>
|
||||||
|
<p><strong>Rocco</strong> is a Ruby port of <a href="http://jashkenas.github.com/docco/">Docco</a>, the quick-and-dirty,
|
||||||
|
hundred-line-long, literate-programming-style documentation generator.</p>
|
||||||
|
|
||||||
|
<p>Rocco reads Ruby source files and produces annotated source documentation
|
||||||
|
in HTML format. Comments are formatted with <a href="http://daringfireball.net/projects/markdown/">Markdown</a> and presented
|
||||||
|
alongside syntax highlighted code so as to give an annotation effect.
|
||||||
|
This page is the result of running Rocco against <a href="http://github.com/rtomayko/rocco/blob/master/lib/rocco.rb#commit">its own source file</a>.</p>
|
||||||
|
|
||||||
|
<p>Most of this was written while waiting for <a href="http://nodejs.org/">node.js</a> to build (so I
|
||||||
|
could use Docco!). Docco’s gorgeous HTML and CSS are taken verbatim.
|
||||||
|
The main difference is that Rocco is written in Ruby instead of
|
||||||
|
<a href="http://coffeescript.org/">CoffeeScript</a> and may be a bit easier to obtain and install in
|
||||||
|
existing Ruby environments or where node doesn’t run yet.</p>
|
||||||
|
|
||||||
|
<p>Install Rocco with Rubygems:</p>
|
||||||
|
|
||||||
|
<pre><code>gem install rocco
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>Once installed, the <code>rocco</code> command can be used to generate documentation
|
||||||
|
for a set of Ruby source files:</p>
|
||||||
|
|
||||||
|
<pre><code>rocco lib/*.rb
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>The HTML files are written to the current working directory.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-Prerequisites'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-Prerequisites">¶</a>
|
||||||
|
</div>
|
||||||
|
<h3>Prerequisites</h3>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-3'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-3">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We’ll need a Markdown library. <a href="http://github.com/rtomayko/rdiscount">RDiscount</a>, if we’re lucky. Otherwise,
|
||||||
|
issue a warning and fall back on using BlueCloth.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="k">begin</span>
|
||||||
|
<span class="nb">require</span> <span class="s1">'rdiscount'</span>
|
||||||
|
<span class="k">rescue</span> <span class="no">LoadError</span> <span class="o">=></span> <span class="n">boom</span>
|
||||||
|
<span class="nb">warn</span> <span class="s2">"WARNING: </span><span class="si">#{</span><span class="n">boom</span><span class="si">}</span><span class="s2">. Trying bluecloth."</span>
|
||||||
|
<span class="nb">require</span> <span class="s1">'bluecloth'</span>
|
||||||
|
<span class="no">Markdown</span> <span class="o">=</span> <span class="no">BlueCloth</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-4'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-4">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We use <a href="http://defunkt.github.com/mustache/">{{ mustache }}</a> for
|
||||||
|
HTML templating.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'mustache'</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-5'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-5">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We use <code>Net::HTTP</code> to highlight code via <a href="http://pygments.appspot.com">http://pygments.appspot.com</a></p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'net/http'</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-6'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-6">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Code is run through <a href="http://pygments.org/">Pygments</a> for syntax
|
||||||
|
highlighting. If it’s not installed, locally, use a webservice.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="kp">include</span> <span class="no">FileTest</span>
|
||||||
|
<span class="k">if</span> <span class="o">!</span><span class="no">ENV</span><span class="o">[</span><span class="s1">'PATH'</span><span class="o">].</span><span class="n">split</span><span class="p">(</span><span class="s1">':'</span><span class="p">)</span><span class="o">.</span><span class="n">any?</span> <span class="p">{</span> <span class="o">|</span><span class="n">dir</span><span class="o">|</span> <span class="n">executable?</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/pygmentize"</span><span class="p">)</span> <span class="p">}</span>
|
||||||
|
<span class="nb">warn</span> <span class="s2">"WARNING: Pygments not found. Using webservice."</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-Public_Interface'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-Public_Interface">¶</a>
|
||||||
|
</div>
|
||||||
|
<h3>Public Interface</h3>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-8'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-8">¶</a>
|
||||||
|
</div>
|
||||||
|
<p><code>Rocco.new</code> takes a source <code>filename</code>, an optional list of source filenames
|
||||||
|
for other documentation sources, an <code>options</code> hash, and an optional <code>block</code>.
|
||||||
|
The <code>options</code> hash respects three members:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><p><code>:language</code>: specifies which Pygments lexer to use if one can’t be
|
||||||
|
auto-detected from the filename. <em>Defaults to <code>ruby</code></em>.</p></li>
|
||||||
|
<li><p><code>:comment_chars</code>, which specifies the comment characters of the
|
||||||
|
target language. <em>Defaults to <code>#</code></em>.</p></li>
|
||||||
|
<li><p><code>:template_file</code>, which specifies a external template file to use
|
||||||
|
when rendering the final, highlighted file via Mustache. <em>Defaults
|
||||||
|
to <code>nil</code> (that is, Mustache will use <code>./lib/rocco/layout.mustache</code>)</em>.</p></li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="k">class</span> <span class="nc">Rocco</span>
|
||||||
|
<span class="no">VERSION</span> <span class="o">=</span> <span class="s1">'0.6'</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">sources</span><span class="o">=[]</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="p">{},</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
|
||||||
|
<span class="vi">@file</span> <span class="o">=</span> <span class="n">filename</span>
|
||||||
|
<span class="vi">@sources</span> <span class="o">=</span> <span class="n">sources</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-9'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-9">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>When <code>block</code> is given, it must read the contents of the file using
|
||||||
|
whatever means necessary and return it as a string. With no <code>block</code>,
|
||||||
|
the file is read to retrieve data.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="vi">@data</span> <span class="o">=</span>
|
||||||
|
<span class="k">if</span> <span class="nb">block_given?</span>
|
||||||
|
<span class="k">yield</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="n">defaults</span> <span class="o">=</span> <span class="p">{</span>
|
||||||
|
<span class="ss">:language</span> <span class="o">=></span> <span class="s1">'ruby'</span><span class="p">,</span>
|
||||||
|
<span class="ss">:comment_chars</span> <span class="o">=></span> <span class="s1">'#'</span><span class="p">,</span>
|
||||||
|
<span class="ss">:template_file</span> <span class="o">=></span> <span class="kp">nil</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="vi">@options</span> <span class="o">=</span> <span class="n">defaults</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span><span class="n">options</span><span class="p">)</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-10'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-10">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>If we detect a language</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">if</span> <span class="n">detect_language</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">"text"</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-11'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-11">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>then assign the detected language to <code>:language</code>, and look for
|
||||||
|
comment characters based on that language</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]</span> <span class="o">=</span> <span class="n">detect_language</span><span class="p">()</span>
|
||||||
|
<span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span> <span class="o">=</span> <span class="n">generate_comment_chars</span><span class="p">()</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-12'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-12">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>If we didn’t detect a language, but the user provided one, use it
|
||||||
|
to look around for comment characters to override the default.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">elsif</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]</span> <span class="o">!=</span> <span class="n">defaults</span><span class="o">[</span><span class="ss">:language</span><span class="o">]</span>
|
||||||
|
<span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span> <span class="o">=</span> <span class="n">generate_comment_chars</span><span class="p">()</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-13'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-13">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>If neither is true, then convert the default comment character string
|
||||||
|
into the comment_char syntax (we’ll discuss that syntax in detail when
|
||||||
|
we get to <code>generate_comment_chars()</code> in a moment.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">else</span>
|
||||||
|
<span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span> <span class="o">=</span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="kp">nil</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-14'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-14">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Turn <code>:comment_chars</code> into a regex matching a series of spaces, the
|
||||||
|
<code>:comment_chars</code> string, and the an optional space. We’ll use that
|
||||||
|
to detect single-line comments.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="vi">@comment_pattern</span> <span class="o">=</span>
|
||||||
|
<span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"^</span><span class="se">\\</span><span class="s2">s*</span><span class="si">#{</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:single</span><span class="o">]</span><span class="si">}</span><span class="se">\s</span><span class="s2">?"</span><span class="p">)</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-15'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-15">¶</a>
|
||||||
|
</div>
|
||||||
|
<p><code>parse()</code> the file contents stored in <code>@data</code>. Run the result through
|
||||||
|
<code>split()</code> and that result through <code>highlight()</code> to generate the final
|
||||||
|
section list.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="vi">@sections</span> <span class="o">=</span> <span class="n">highlight</span><span class="p">(</span><span class="nb">split</span><span class="p">(</span><span class="n">parse</span><span class="p">(</span><span class="vi">@data</span><span class="p">)))</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-16'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-16">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>The filename as given to <code>Rocco.new</code>.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:file</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-17'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-17">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>The merged options array</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:options</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-18'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-18">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>A list of two-tuples representing each <em>section</em> of the source file. Each
|
||||||
|
item in the list has the form: <code>[docs_html, code_html]</code>, where both
|
||||||
|
elements are strings containing the documentation and source code HTML,
|
||||||
|
respectively.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:sections</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-19'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-19">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>A list of all source filenames included in the documentation set. Useful
|
||||||
|
for building an index of other files.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:sources</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-20'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-20">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Generate HTML output for the entire document.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="nb">require</span> <span class="s1">'rocco/layout'</span>
|
||||||
|
<span class="k">def</span> <span class="nf">to_html</span>
|
||||||
|
<span class="no">Rocco</span><span class="o">::</span><span class="no">Layout</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:template_file</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">render</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-Helper_Functions'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-Helper_Functions">¶</a>
|
||||||
|
</div>
|
||||||
|
<h2>Helper Functions</h2>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-22'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-22">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Returns <code>true</code> if <code>pygmentize</code> is available locally, <code>false</code> otherwise.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">pygmentize?</span>
|
||||||
|
<span class="vi">@_pygmentize</span> <span class="o">||=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">'PATH'</span><span class="o">].</span><span class="n">split</span><span class="p">(</span><span class="s1">':'</span><span class="p">)</span><span class="o">.</span>
|
||||||
|
<span class="n">any?</span> <span class="p">{</span> <span class="o">|</span><span class="n">dir</span><span class="o">|</span> <span class="n">executable?</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/pygmentize"</span><span class="p">)</span> <span class="p">}</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-23'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-23">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>If <code>pygmentize</code> is available, we can use it to autodetect a file’s
|
||||||
|
language based on its filename. Filenames without extensions, or with
|
||||||
|
extensions that <code>pygmentize</code> doesn’t understand will return <code>text</code>.
|
||||||
|
We’ll also return <code>text</code> if <code>pygmentize</code> isn’t available.</p>
|
||||||
|
|
||||||
|
<p>We’ll memoize the result, as we’ll call this a few times.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">detect_language</span>
|
||||||
|
<span class="vi">@_language</span> <span class="o">||=</span>
|
||||||
|
<span class="k">if</span> <span class="n">pygmentize?</span>
|
||||||
|
<span class="sx">%x[pygmentize -N </span><span class="si">#{</span><span class="vi">@file</span><span class="si">}</span><span class="sx">]</span><span class="o">.</span><span class="n">strip!</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="s2">"text"</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-24'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-24">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Given a file’s language, we should be able to autopopulate the
|
||||||
|
<code>comment_chars</code> variables for single-line comments. If we don’t
|
||||||
|
have comment characters on record for a given language, we’ll
|
||||||
|
use the user-provided <code>:comment_char</code> option (which defaults to
|
||||||
|
<code>#</code>).</p>
|
||||||
|
|
||||||
|
<p>Comment characters are listed as:</p>
|
||||||
|
|
||||||
|
<pre><code>{ :single => "//",
|
||||||
|
:multi_start => "/**",
|
||||||
|
:multi_middle => "*",
|
||||||
|
:multi_end => "*/" }
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p><code>:single</code> denotes the leading character of a single-line comment.
|
||||||
|
<code>:multi_start</code> denotes the string that should appear alone on a
|
||||||
|
line of code to begin a block of documentation. <code>:multi_middle</code>
|
||||||
|
denotes the leading character of block comment content, and
|
||||||
|
<code>:multi_end</code> is the string that ought appear alone on a line to
|
||||||
|
close a block of documentation. That is:</p>
|
||||||
|
|
||||||
|
<pre><code>/** [:multi][:start]
|
||||||
|
* [:multi][:middle]
|
||||||
|
...
|
||||||
|
* [:multi][:middle]
|
||||||
|
*/ [:multi][:end]
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>If a language only has one type of comment, the missing type
|
||||||
|
should be assigned <code>nil</code>.</p>
|
||||||
|
|
||||||
|
<p>At the moment, we’re only returning <code>:single</code>. Consider this
|
||||||
|
groundwork for block comment parsing.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="no">COMMENT_STYLES</span> <span class="o">=</span> <span class="p">{</span>
|
||||||
|
<span class="s2">"bash"</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:single</span> <span class="o">=></span> <span class="s2">"#"</span><span class="p">,</span> <span class="ss">:multi</span> <span class="o">=></span> <span class="kp">nil</span> <span class="p">},</span>
|
||||||
|
<span class="s2">"c"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"//"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s2">"/**"</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="s2">"*"</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s2">"*/"</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"coffee-script"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"#"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s2">"###"</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="kp">nil</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s2">"###"</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"cpp"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"//"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s2">"/**"</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="s2">"*"</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s2">"*/"</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"css"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="kp">nil</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s2">"/**"</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="s2">"*"</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s2">"*/"</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"java"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"//"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s2">"/**"</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="s2">"*"</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s2">"*/"</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"js"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"//"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s2">"/**"</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="s2">"*"</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s2">"*/"</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"lua"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"--"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="kp">nil</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"python"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"#"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s1">'"""'</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="kp">nil</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s1">'"""'</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"rb"</span> <span class="o">=></span> <span class="p">{</span>
|
||||||
|
<span class="ss">:single</span> <span class="o">=></span> <span class="s2">"#"</span><span class="p">,</span>
|
||||||
|
<span class="ss">:multi</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:start</span> <span class="o">=></span> <span class="s1">'=begin'</span><span class="p">,</span> <span class="ss">:middle</span> <span class="o">=></span> <span class="kp">nil</span><span class="p">,</span> <span class="ss">:end</span> <span class="o">=></span> <span class="s1">'=end'</span> <span class="p">}</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="s2">"scheme"</span> <span class="o">=></span> <span class="p">{</span> <span class="ss">:single</span> <span class="o">=></span> <span class="s2">";;"</span><span class="p">,</span> <span class="ss">:multi</span> <span class="o">=></span> <span class="kp">nil</span> <span class="p">},</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">generate_comment_chars</span>
|
||||||
|
<span class="vi">@_commentchar</span> <span class="o">||=</span>
|
||||||
|
<span class="k">if</span> <span class="no">COMMENT_STYLES</span><span class="o">[</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]]</span>
|
||||||
|
<span class="no">COMMENT_STYLES</span><span class="o">[</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]]</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="p">{</span> <span class="ss">:single</span> <span class="o">=></span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span><span class="p">,</span> <span class="ss">:multi</span> <span class="o">=></span> <span class="kp">nil</span> <span class="p">}</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-Internal_Parsing_and_Highlighting'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-Internal_Parsing_and_Highlighting">¶</a>
|
||||||
|
</div>
|
||||||
|
<h2>Internal Parsing and Highlighting</h2>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-26'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-26">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Parse the raw file data into a list of two-tuples. Each tuple has the
|
||||||
|
form <code>[docs, code]</code> where both elements are arrays containing the
|
||||||
|
raw lines parsed from the input file, comment characters stripped.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
||||||
|
<span class="n">sections</span> <span class="o">=</span> <span class="o">[]</span>
|
||||||
|
<span class="n">docs</span><span class="p">,</span> <span class="n">code</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span>
|
||||||
|
<span class="n">lines</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-27'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-27">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>The first line is ignored if it is a shebang line. We also ignore the
|
||||||
|
PEP 263 encoding information in python sourcefiles, and the similar ruby
|
||||||
|
1.9 syntax.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">lines</span><span class="o">.</span><span class="n">shift</span> <span class="k">if</span> <span class="n">lines</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=~</span> <span class="sr">/^\#\!/</span>
|
||||||
|
<span class="n">lines</span><span class="o">.</span><span class="n">shift</span> <span class="k">if</span> <span class="n">lines</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=~</span> <span class="sr">/coding[:=]\s*[-\w.]+/</span> <span class="o">&&</span>
|
||||||
|
<span class="o">[</span> <span class="s2">"python"</span><span class="p">,</span> <span class="s2">"rb"</span> <span class="o">].</span><span class="n">include?</span><span class="p">(</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]</span><span class="p">)</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-28'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-28">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>To detect both block comments and single-line comments, we’ll set
|
||||||
|
up a tiny state machine, and loop through each line of the file.
|
||||||
|
This requires an <code>in_comment_block</code> boolean, and a few regular
|
||||||
|
expressions for line tests.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">in_comment_block</span> <span class="o">=</span> <span class="kp">false</span>
|
||||||
|
<span class="n">single_line_comment</span><span class="p">,</span> <span class="n">block_comment_start</span><span class="p">,</span> <span class="n">block_comment_mid</span><span class="p">,</span> <span class="n">block_comment_end</span> <span class="o">=</span>
|
||||||
|
<span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span>
|
||||||
|
<span class="k">if</span> <span class="ow">not</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:single</span><span class="o">].</span><span class="n">nil?</span>
|
||||||
|
<span class="n">single_line_comment</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"^</span><span class="se">\\</span><span class="s2">s*</span><span class="si">#{</span><span class="no">Regexp</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:single</span><span class="o">]</span><span class="p">)</span><span class="si">}</span><span class="se">\\</span><span class="s2">s?"</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">if</span> <span class="ow">not</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:multi</span><span class="o">].</span><span class="n">nil?</span>
|
||||||
|
<span class="n">block_comment_start</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"^</span><span class="se">\\</span><span class="s2">s*</span><span class="si">#{</span><span class="no">Regexp</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:multi</span><span class="o">][</span><span class="ss">:start</span><span class="o">]</span><span class="p">)</span><span class="si">}</span><span class="se">\\</span><span class="s2">s*$"</span><span class="p">)</span>
|
||||||
|
<span class="n">block_comment_end</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"^</span><span class="se">\\</span><span class="s2">s*</span><span class="si">#{</span><span class="no">Regexp</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:multi</span><span class="o">][</span><span class="ss">:end</span><span class="o">]</span><span class="p">)</span><span class="si">}</span><span class="se">\\</span><span class="s2">s*$"</span><span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:multi</span><span class="o">][</span><span class="ss">:middle</span><span class="o">]</span>
|
||||||
|
<span class="n">block_comment_mid</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"^</span><span class="se">\\</span><span class="s2">s*</span><span class="si">#{</span><span class="no">Regexp</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:multi</span><span class="o">][</span><span class="ss">:middle</span><span class="o">]</span><span class="p">)</span><span class="si">}</span><span class="se">\\</span><span class="s2">s?"</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">lines</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-29'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-29">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>If we’re currently in a comment block, check whether the line matches
|
||||||
|
the <em>end</em> of a comment block.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">if</span> <span class="n">in_comment_block</span>
|
||||||
|
<span class="k">if</span> <span class="n">block_comment_end</span> <span class="o">&&</span> <span class="n">line</span><span class="o">.</span><span class="n">match</span><span class="p">(</span> <span class="n">block_comment_end</span> <span class="p">)</span>
|
||||||
|
<span class="n">in_comment_block</span> <span class="o">=</span> <span class="kp">false</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="n">docs</span> <span class="o"><<</span> <span class="n">line</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span> <span class="n">block_comment_mid</span> <span class="o">||</span> <span class="s1">''</span><span class="p">,</span> <span class="s1">''</span> <span class="p">)</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-30'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-30">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Otherwise, check whether the line matches the beginning of a block, or
|
||||||
|
a single-line comment all on it’s lonesome. In either case, if there’s
|
||||||
|
code, start a new section</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">else</span>
|
||||||
|
<span class="k">if</span> <span class="n">block_comment_start</span> <span class="o">&&</span> <span class="n">line</span><span class="o">.</span><span class="n">match</span><span class="p">(</span> <span class="n">block_comment_start</span> <span class="p">)</span>
|
||||||
|
<span class="n">in_comment_block</span> <span class="o">=</span> <span class="kp">true</span>
|
||||||
|
<span class="k">if</span> <span class="n">code</span><span class="o">.</span><span class="n">any?</span>
|
||||||
|
<span class="n">sections</span> <span class="o"><<</span> <span class="o">[</span><span class="n">docs</span><span class="p">,</span> <span class="n">code</span><span class="o">]</span>
|
||||||
|
<span class="n">docs</span><span class="p">,</span> <span class="n">code</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">elsif</span> <span class="n">single_line_comment</span> <span class="o">&&</span> <span class="n">line</span><span class="o">.</span><span class="n">match</span><span class="p">(</span> <span class="n">single_line_comment</span> <span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="n">code</span><span class="o">.</span><span class="n">any?</span>
|
||||||
|
<span class="n">sections</span> <span class="o"><<</span> <span class="o">[</span><span class="n">docs</span><span class="p">,</span> <span class="n">code</span><span class="o">]</span>
|
||||||
|
<span class="n">docs</span><span class="p">,</span> <span class="n">code</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">docs</span> <span class="o"><<</span> <span class="n">line</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span> <span class="n">single_line_comment</span> <span class="o">||</span> <span class="s1">''</span><span class="p">,</span> <span class="s1">''</span> <span class="p">)</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="n">code</span> <span class="o"><<</span> <span class="n">line</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">sections</span> <span class="o"><<</span> <span class="o">[</span><span class="n">docs</span><span class="p">,</span> <span class="n">code</span><span class="o">]</span> <span class="k">if</span> <span class="n">docs</span><span class="o">.</span><span class="n">any?</span> <span class="o">||</span> <span class="n">code</span><span class="o">.</span><span class="n">any?</span>
|
||||||
|
<span class="n">normalize_leading_spaces</span><span class="p">(</span> <span class="n">sections</span> <span class="p">)</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-31'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-31">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Normalizes documentation whitespace by checking for leading whitespace,
|
||||||
|
removing it, and then removing the same amount of whitespace from each
|
||||||
|
succeeding line. That is:</p>
|
||||||
|
|
||||||
|
<pre><code>def func():
|
||||||
|
"""
|
||||||
|
Comment 1
|
||||||
|
Comment 2
|
||||||
|
"""
|
||||||
|
print "omg!"
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>should yield a comment block of <code>Comment 1\nComment 2</code> and code of
|
||||||
|
<code>def func():\n print "omg!"</code></p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">normalize_leading_spaces</span><span class="p">(</span> <span class="n">sections</span> <span class="p">)</span>
|
||||||
|
<span class="n">sections</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">section</span><span class="o">|</span>
|
||||||
|
<span class="k">if</span> <span class="n">section</span><span class="o">.</span><span class="n">any?</span> <span class="o">&&</span> <span class="n">section</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">any?</span>
|
||||||
|
<span class="n">leading_space</span> <span class="o">=</span> <span class="n">section</span><span class="o">[</span><span class="mi">0</span><span class="o">][</span><span class="mi">0</span><span class="o">].</span><span class="n">match</span><span class="p">(</span> <span class="s2">"^</span><span class="se">\s</span><span class="s2">+"</span> <span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="n">leading_space</span>
|
||||||
|
<span class="n">section</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=</span>
|
||||||
|
<span class="n">section</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">map</span><span class="p">{</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span> <span class="n">line</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr"> /^</span><span class="si">#{</span><span class="n">leading_space</span><span class="o">.</span><span class="n">to_s</span><span class="si">}</span><span class="sr">/</span><span class="p">,</span> <span class="s1">''</span> <span class="p">)</span> <span class="p">}</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">section</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-32'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-32">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Take the list of paired <em>sections</em> two-tuples and split into two
|
||||||
|
separate lists: one holding the comments with leaders removed and
|
||||||
|
one with the code blocks.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">split</span><span class="p">(</span><span class="n">sections</span><span class="p">)</span>
|
||||||
|
<span class="n">docs_blocks</span><span class="p">,</span> <span class="n">code_blocks</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span>
|
||||||
|
<span class="n">sections</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">docs</span><span class="p">,</span><span class="n">code</span><span class="o">|</span>
|
||||||
|
<span class="n">docs_blocks</span> <span class="o"><<</span> <span class="n">docs</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="n">code_blocks</span> <span class="o"><<</span> <span class="n">code</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
|
||||||
|
<span class="n">tabs</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^(\t+)/</span><span class="p">)</span>
|
||||||
|
<span class="n">tabs</span> <span class="p">?</span> <span class="n">line</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr">/^\t+/</span><span class="p">,</span> <span class="s1">' '</span> <span class="o">*</span> <span class="n">tabs</span><span class="o">.</span><span class="n">captures</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">length</span><span class="p">)</span> <span class="p">:</span> <span class="n">line</span>
|
||||||
|
<span class="k">end</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="o">[</span><span class="n">docs_blocks</span><span class="p">,</span> <span class="n">code_blocks</span><span class="o">]</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-33'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-33">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Take the result of <code>split</code> and apply Markdown formatting to comments and
|
||||||
|
syntax highlighting to source code.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">highlight</span><span class="p">(</span><span class="n">blocks</span><span class="p">)</span>
|
||||||
|
<span class="n">docs_blocks</span><span class="p">,</span> <span class="n">code_blocks</span> <span class="o">=</span> <span class="n">blocks</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-34'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-34">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Combine all docs blocks into a single big markdown document with section
|
||||||
|
dividers and run through the Markdown processor. Then split it back out
|
||||||
|
into separate sections.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">markdown</span> <span class="o">=</span> <span class="n">docs_blocks</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\n</span><span class="s2">##### DIVIDER</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="n">docs_html</span> <span class="o">=</span> <span class="no">Markdown</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">markdown</span><span class="p">,</span> <span class="ss">:smart</span><span class="p">)</span><span class="o">.</span>
|
||||||
|
<span class="n">to_html</span><span class="o">.</span>
|
||||||
|
<span class="nb">split</span><span class="p">(</span><span class="sr">/\n*<h5>DIVIDER<\/h5>\n*/m</span><span class="p">)</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-35'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-35">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Combine all code blocks into a single big stream with section dividers and
|
||||||
|
run through either <code>pygmentize(1)</code> or <a href="http://pygments.appspot.com">http://pygments.appspot.com</a></p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">span</span><span class="p">,</span> <span class="n">espan</span> <span class="o">=</span> <span class="s1">'<span class="c.?">'</span><span class="p">,</span> <span class="s1">'</span>'</span>
|
||||||
|
<span class="k">if</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:single</span><span class="o">]</span>
|
||||||
|
<span class="n">front</span> <span class="o">=</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:single</span><span class="o">]</span>
|
||||||
|
<span class="n">divider_input</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\n\n</span><span class="si">#{</span><span class="n">front</span><span class="si">}</span><span class="s2"> DIVIDER</span><span class="se">\n\n</span><span class="s2">"</span>
|
||||||
|
<span class="n">divider_output</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
|
||||||
|
<span class="o">[</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">n*"</span><span class="p">,</span>
|
||||||
|
<span class="n">span</span><span class="p">,</span>
|
||||||
|
<span class="no">Regexp</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">front</span><span class="p">),</span>
|
||||||
|
<span class="s1">' DIVIDER'</span><span class="p">,</span>
|
||||||
|
<span class="n">espan</span><span class="p">,</span>
|
||||||
|
<span class="s2">"</span><span class="se">\\</span><span class="s2">n*"</span>
|
||||||
|
<span class="o">].</span><span class="n">join</span><span class="p">,</span> <span class="no">Regexp</span><span class="o">::</span><span class="no">MULTILINE</span>
|
||||||
|
<span class="p">)</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="n">front</span> <span class="o">=</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:multi</span><span class="o">][</span><span class="ss">:start</span><span class="o">]</span>
|
||||||
|
<span class="n">back</span> <span class="o">=</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">][</span><span class="ss">:multi</span><span class="o">][</span><span class="ss">:end</span><span class="o">]</span>
|
||||||
|
<span class="n">divider_input</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\n\n</span><span class="si">#{</span><span class="n">front</span><span class="si">}</span><span class="se">\n</span><span class="s2">DIVIDER</span><span class="se">\n</span><span class="si">#{</span><span class="n">back</span><span class="si">}</span><span class="se">\n\n</span><span class="s2">"</span>
|
||||||
|
<span class="n">divider_output</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
|
||||||
|
<span class="o">[</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">n*"</span><span class="p">,</span>
|
||||||
|
<span class="n">span</span><span class="p">,</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">front</span><span class="p">),</span> <span class="n">espan</span><span class="p">,</span>
|
||||||
|
<span class="s2">"</span><span class="se">\\</span><span class="s2">n"</span><span class="p">,</span>
|
||||||
|
<span class="n">span</span><span class="p">,</span> <span class="s2">"DIVIDER"</span><span class="p">,</span> <span class="n">espan</span><span class="p">,</span>
|
||||||
|
<span class="s2">"</span><span class="se">\\</span><span class="s2">n"</span><span class="p">,</span>
|
||||||
|
<span class="n">span</span><span class="p">,</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">back</span><span class="p">),</span> <span class="n">espan</span><span class="p">,</span>
|
||||||
|
<span class="s2">"</span><span class="se">\\</span><span class="s2">n*"</span>
|
||||||
|
<span class="o">].</span><span class="n">join</span><span class="p">,</span> <span class="no">Regexp</span><span class="o">::</span><span class="no">MULTILINE</span>
|
||||||
|
<span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="n">code_stream</span> <span class="o">=</span> <span class="n">code_blocks</span><span class="o">.</span><span class="n">join</span><span class="p">(</span> <span class="n">divider_input</span> <span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="n">code_html</span> <span class="o">=</span>
|
||||||
|
<span class="k">if</span> <span class="n">pygmentize?</span>
|
||||||
|
<span class="n">highlight_pygmentize</span><span class="p">(</span><span class="n">code_stream</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="n">highlight_webservice</span><span class="p">(</span><span class="n">code_stream</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-36'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-36">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Do some post-processing on the pygments output to split things back
|
||||||
|
into sections and remove partial <code><pre></code> blocks.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">code_html</span> <span class="o">=</span> <span class="n">code_html</span><span class="o">.</span>
|
||||||
|
<span class="nb">split</span><span class="p">(</span><span class="n">divider_output</span><span class="p">)</span><span class="o">.</span>
|
||||||
|
<span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">code</span><span class="o">|</span> <span class="n">code</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr">/\n?<div class="highlight"><pre>/m</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="p">}</span><span class="o">.</span>
|
||||||
|
<span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">code</span><span class="o">|</span> <span class="n">code</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr">/\n?<\/pre><\/div>\n/m</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="p">}</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-37'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-37">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Lastly, combine the docs and code lists back into a list of two-tuples.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">docs_html</span><span class="o">.</span><span class="n">zip</span><span class="p">(</span><span class="n">code_html</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-38'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-38">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We <code>popen</code> a read/write pygmentize process in the parent and
|
||||||
|
then fork off a child process to write the input.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">highlight_pygmentize</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
|
||||||
|
<span class="n">code_html</span> <span class="o">=</span> <span class="kp">nil</span>
|
||||||
|
<span class="nb">open</span><span class="p">(</span><span class="s2">"|pygmentize -l </span><span class="si">#{</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]</span><span class="si">}</span><span class="s2"> -O encoding=utf-8 -f html"</span><span class="p">,</span> <span class="s1">'r+'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">fd</span><span class="o">|</span>
|
||||||
|
<span class="n">pid</span> <span class="o">=</span>
|
||||||
|
<span class="nb">fork</span> <span class="p">{</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_read</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">write</span> <span class="n">code</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_write</span>
|
||||||
|
<span class="nb">exit!</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_write</span>
|
||||||
|
<span class="n">code_html</span> <span class="o">=</span> <span class="n">fd</span><span class="o">.</span><span class="n">read</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_read</span>
|
||||||
|
<span class="no">Process</span><span class="o">.</span><span class="n">wait</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="n">code_html</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-39'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-39">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Pygments is not one of those things that’s trivial for a ruby user to install,
|
||||||
|
so we’ll fall back on a webservice to highlight the code if it isn’t available.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">highlight_webservice</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
|
||||||
|
<span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span>
|
||||||
|
<span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s1">'http://pygments.appspot.com/'</span><span class="p">),</span>
|
||||||
|
<span class="p">{</span><span class="s1">'lang'</span> <span class="o">=></span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]</span><span class="p">,</span> <span class="s1">'code'</span> <span class="o">=></span> <span class="n">code</span><span class="p">}</span>
|
||||||
|
<span class="p">)</span><span class="o">.</span><span class="n">body</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-40'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-40">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>And that’s it.</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
89
layout.html
Normal file
89
layout.html
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||||
|
<title>layout.rb</title>
|
||||||
|
<link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='container'>
|
||||||
|
<div id="background"></div>
|
||||||
|
<div id="jump_to">
|
||||||
|
Jump To …
|
||||||
|
<div id="jump_wrapper">
|
||||||
|
<div id="jump_page">
|
||||||
|
<a class="source" href="rocco.html">rocco.rb</a>
|
||||||
|
<a class="source" href="layout.html">layout.rb</a>
|
||||||
|
<a class="source" href="tasks.html">tasks.rb</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table cellspacing=0 cellpadding=0>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class=docs><h1>layout.rb</h1></th>
|
||||||
|
<th class=code></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr id='section-1'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-1">¶</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'mustache'</span>
|
||||||
|
|
||||||
|
<span class="k">class</span> <span class="nc">Rocco</span><span class="o">::</span><span class="no">Layout</span> <span class="o"><</span> <span class="no">Mustache</span>
|
||||||
|
<span class="nb">self</span><span class="o">.</span><span class="n">template_path</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">doc</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span>
|
||||||
|
<span class="vi">@doc</span> <span class="o">=</span> <span class="n">doc</span>
|
||||||
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">file</span><span class="o">.</span><span class="n">nil?</span>
|
||||||
|
<span class="no">Rocco</span><span class="o">::</span><span class="no">Layout</span><span class="o">.</span><span class="n">template_file</span> <span class="o">=</span> <span class="n">file</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">title</span>
|
||||||
|
<span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="vi">@doc</span><span class="o">.</span><span class="n">file</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">sections</span>
|
||||||
|
<span class="n">num</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
|
<span class="vi">@doc</span><span class="o">.</span><span class="n">sections</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">docs</span><span class="p">,</span><span class="n">code</span><span class="o">|</span>
|
||||||
|
<span class="p">{</span>
|
||||||
|
<span class="ss">:docs</span> <span class="o">=></span> <span class="n">docs</span><span class="p">,</span>
|
||||||
|
<span class="ss">:docs?</span> <span class="o">=></span> <span class="o">!</span><span class="n">docs</span><span class="o">.</span><span class="n">empty?</span><span class="p">,</span>
|
||||||
|
<span class="ss">:header?</span> <span class="o">=></span> <span class="sr">/^<h.>.+<\/h.>$/</span><span class="o">.</span><span class="n">match</span><span class="p">(</span> <span class="n">docs</span> <span class="p">),</span>
|
||||||
|
|
||||||
|
<span class="ss">:code</span> <span class="o">=></span> <span class="n">code</span><span class="p">,</span>
|
||||||
|
<span class="ss">:code?</span> <span class="o">=></span> <span class="o">!</span><span class="n">code</span><span class="o">.</span><span class="n">empty?</span><span class="p">,</span>
|
||||||
|
|
||||||
|
<span class="ss">:empty?</span> <span class="o">=></span> <span class="p">(</span> <span class="n">code</span><span class="o">.</span><span class="n">empty?</span> <span class="o">&&</span> <span class="n">docs</span><span class="o">.</span><span class="n">empty?</span> <span class="p">),</span>
|
||||||
|
<span class="ss">:num</span> <span class="o">=></span> <span class="p">(</span><span class="n">num</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">sources?</span>
|
||||||
|
<span class="vi">@doc</span><span class="o">.</span><span class="n">sources</span><span class="o">.</span><span class="n">length</span> <span class="o">></span> <span class="mi">1</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">sources</span>
|
||||||
|
<span class="vi">@doc</span><span class="o">.</span><span class="n">sources</span><span class="o">.</span><span class="n">sort</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">source</span><span class="o">|</span>
|
||||||
|
<span class="p">{</span>
|
||||||
|
<span class="ss">:path</span> <span class="o">=></span> <span class="n">source</span><span class="p">,</span>
|
||||||
|
<span class="ss">:basename</span> <span class="o">=></span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">source</span><span class="p">),</span>
|
||||||
|
<span class="ss">:url</span> <span class="o">=></span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">source</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="o">-</span><span class="mi">2</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'.html'</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
531
lib/rocco.rb
531
lib/rocco.rb
@ -1,531 +0,0 @@
|
|||||||
# **Rocco** is a Ruby port of [Docco][do], the quick-and-dirty,
|
|
||||||
# hundred-line-long, literate-programming-style documentation generator.
|
|
||||||
#
|
|
||||||
# Rocco reads Ruby source files and produces annotated source documentation
|
|
||||||
# in HTML format. Comments are formatted with [Markdown][md] and presented
|
|
||||||
# alongside syntax highlighted code so as to give an annotation effect.
|
|
||||||
# This page is the result of running Rocco against [its own source file][so].
|
|
||||||
#
|
|
||||||
# Most of this was written while waiting for [node.js][no] to build (so I
|
|
||||||
# could use Docco!). Docco's gorgeous HTML and CSS are taken verbatim.
|
|
||||||
# The main difference is that Rocco is written in Ruby instead of
|
|
||||||
# [CoffeeScript][co] and may be a bit easier to obtain and install in
|
|
||||||
# existing Ruby environments or where node doesn't run yet.
|
|
||||||
#
|
|
||||||
# Install Rocco with Rubygems:
|
|
||||||
#
|
|
||||||
# gem install rocco
|
|
||||||
#
|
|
||||||
# Once installed, the `rocco` command can be used to generate documentation
|
|
||||||
# for a set of Ruby source files:
|
|
||||||
#
|
|
||||||
# rocco lib/*.rb
|
|
||||||
#
|
|
||||||
# The HTML files are written to the current working directory.
|
|
||||||
#
|
|
||||||
# [no]: http://nodejs.org/
|
|
||||||
# [do]: http://jashkenas.github.com/docco/
|
|
||||||
# [co]: http://coffeescript.org/
|
|
||||||
# [md]: http://daringfireball.net/projects/markdown/
|
|
||||||
# [so]: http://github.com/rtomayko/rocco/blob/master/lib/rocco.rb#commit
|
|
||||||
|
|
||||||
#### Prerequisites
|
|
||||||
|
|
||||||
# We'll need a Markdown library. [RDiscount][rd], if we're lucky. Otherwise,
|
|
||||||
# issue a warning and fall back on using BlueCloth.
|
|
||||||
#
|
|
||||||
# [rd]: http://github.com/rtomayko/rdiscount
|
|
||||||
begin
|
|
||||||
require 'rdiscount'
|
|
||||||
rescue LoadError => boom
|
|
||||||
warn "WARNING: #{boom}. Trying bluecloth."
|
|
||||||
require 'bluecloth'
|
|
||||||
Markdown = BlueCloth
|
|
||||||
end
|
|
||||||
|
|
||||||
# We use [{{ mustache }}](http://defunkt.github.com/mustache/) for
|
|
||||||
# HTML templating.
|
|
||||||
require 'mustache'
|
|
||||||
|
|
||||||
# We use `Net::HTTP` to highlight code via <http://pygments.appspot.com>
|
|
||||||
require 'net/http'
|
|
||||||
|
|
||||||
# Code is run through [Pygments](http://pygments.org/) for syntax
|
|
||||||
# highlighting. If it's not installed, locally, use a webservice.
|
|
||||||
if !ENV['PATH'].split(':').any? { |dir| File.executable?("#{dir}/pygmentize") }
|
|
||||||
warn "WARNING: Pygments not found. Using webservice."
|
|
||||||
end
|
|
||||||
|
|
||||||
#### Public Interface
|
|
||||||
|
|
||||||
# `Rocco.new` takes a source `filename`, an optional list of source filenames
|
|
||||||
# for other documentation sources, an `options` hash, and an optional `block`.
|
|
||||||
# The `options` hash respects three members:
|
|
||||||
#
|
|
||||||
# * `:language`: specifies which Pygments lexer to use if one can't be
|
|
||||||
# auto-detected from the filename. _Defaults to `ruby`_.
|
|
||||||
#
|
|
||||||
# * `:comment_chars`, which specifies the comment characters of the
|
|
||||||
# target language. _Defaults to `#`_.
|
|
||||||
#
|
|
||||||
# * `:template_file`, which specifies a external template file to use
|
|
||||||
# when rendering the final, highlighted file via Mustache. _Defaults
|
|
||||||
# to `nil` (that is, Mustache will use `./lib/rocco/layout.mustache`)_.
|
|
||||||
#
|
|
||||||
class Rocco
|
|
||||||
VERSION = '0.7'
|
|
||||||
|
|
||||||
def initialize(filename, sources=[], options={}, &block)
|
|
||||||
@file = filename
|
|
||||||
@sources = sources
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
@data =
|
|
||||||
if block_given?
|
|
||||||
yield
|
|
||||||
else
|
|
||||||
File.read(filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
defaults = {
|
|
||||||
:language => 'ruby',
|
|
||||||
:comment_chars => '#',
|
|
||||||
:template_file => nil
|
|
||||||
}
|
|
||||||
@options = defaults.merge(options)
|
|
||||||
|
|
||||||
# If we detect a language
|
|
||||||
if detect_language() != "text"
|
|
||||||
# then assign the detected language to `:language`, and look for
|
|
||||||
# comment characters based on that language
|
|
||||||
@options[:language] = detect_language()
|
|
||||||
@options[:comment_chars] = generate_comment_chars()
|
|
||||||
|
|
||||||
# If we didn't detect a language, but the user provided one, use it
|
|
||||||
# to look around for comment characters to override the default.
|
|
||||||
elsif @options[:language] != defaults[:language]
|
|
||||||
@options[:comment_chars] = generate_comment_chars()
|
|
||||||
|
|
||||||
# If neither is true, then convert the default comment character string
|
|
||||||
# into the comment_char syntax (we'll discuss that syntax in detail when
|
|
||||||
# we get to `generate_comment_chars()` in a moment.
|
|
||||||
else
|
|
||||||
@options[:comment_chars] = {
|
|
||||||
:single => @options[:comment_chars],
|
|
||||||
:multi => nil
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Turn `:comment_chars` into a regex matching a series of spaces, the
|
|
||||||
# `:comment_chars` string, and the an optional space. We'll use that
|
|
||||||
# to detect single-line comments.
|
|
||||||
@comment_pattern =
|
|
||||||
Regexp.new("^\\s*#{@options[:comment_chars][:single]}\s?")
|
|
||||||
|
|
||||||
# `parse()` the file contents stored in `@data`. Run the result through
|
|
||||||
# `split()` and that result through `highlight()` to generate the final
|
|
||||||
# section list.
|
|
||||||
@sections = highlight(split(parse(@data)))
|
|
||||||
end
|
|
||||||
|
|
||||||
# The filename as given to `Rocco.new`.
|
|
||||||
attr_reader :file
|
|
||||||
|
|
||||||
# The merged options array
|
|
||||||
attr_reader :options
|
|
||||||
|
|
||||||
# A list of two-tuples representing each *section* of the source file. Each
|
|
||||||
# item in the list has the form: `[docs_html, code_html]`, where both
|
|
||||||
# elements are strings containing the documentation and source code HTML,
|
|
||||||
# respectively.
|
|
||||||
attr_reader :sections
|
|
||||||
|
|
||||||
# A list of all source filenames included in the documentation set. Useful
|
|
||||||
# for building an index of other files.
|
|
||||||
attr_reader :sources
|
|
||||||
|
|
||||||
# Generate HTML output for the entire document.
|
|
||||||
require 'rocco/layout'
|
|
||||||
def to_html
|
|
||||||
Rocco::Layout.new(self, @options[:template_file]).render
|
|
||||||
end
|
|
||||||
|
|
||||||
# Helper Functions
|
|
||||||
# ----------------
|
|
||||||
|
|
||||||
# Returns `true` if `pygmentize` is available locally, `false` otherwise.
|
|
||||||
def pygmentize?
|
|
||||||
@_pygmentize ||= ENV['PATH'].split(':').
|
|
||||||
any? { |dir| File.executable?("#{dir}/pygmentize") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# If `pygmentize` is available, we can use it to autodetect a file's
|
|
||||||
# language based on its filename. Filenames without extensions, or with
|
|
||||||
# extensions that `pygmentize` doesn't understand will return `text`.
|
|
||||||
# We'll also return `text` if `pygmentize` isn't available.
|
|
||||||
#
|
|
||||||
# We'll memoize the result, as we'll call this a few times.
|
|
||||||
def detect_language
|
|
||||||
@_language ||=
|
|
||||||
if pygmentize?
|
|
||||||
%x[pygmentize -N #{@file}].strip.split('+').first
|
|
||||||
else
|
|
||||||
"text"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Given a file's language, we should be able to autopopulate the
|
|
||||||
# `comment_chars` variables for single-line comments. If we don't
|
|
||||||
# have comment characters on record for a given language, we'll
|
|
||||||
# use the user-provided `:comment_char` option (which defaults to
|
|
||||||
# `#`).
|
|
||||||
#
|
|
||||||
# Comment characters are listed as:
|
|
||||||
#
|
|
||||||
# { :single => "//",
|
|
||||||
# :multi_start => "/**",
|
|
||||||
# :multi_middle => "*",
|
|
||||||
# :multi_end => "*/" }
|
|
||||||
#
|
|
||||||
# `:single` denotes the leading character of a single-line comment.
|
|
||||||
# `:multi_start` denotes the string that should appear alone on a
|
|
||||||
# line of code to begin a block of documentation. `:multi_middle`
|
|
||||||
# denotes the leading character of block comment content, and
|
|
||||||
# `:multi_end` is the string that ought appear alone on a line to
|
|
||||||
# close a block of documentation. That is:
|
|
||||||
#
|
|
||||||
# /** [:multi][:start]
|
|
||||||
# * [:multi][:middle]
|
|
||||||
# ...
|
|
||||||
# * [:multi][:middle]
|
|
||||||
# */ [:multi][:end]
|
|
||||||
#
|
|
||||||
# If a language only has one type of comment, the missing type
|
|
||||||
# should be assigned `nil`.
|
|
||||||
#
|
|
||||||
# At the moment, we're only returning `:single`. Consider this
|
|
||||||
# groundwork for block comment parsing.
|
|
||||||
C_STYLE_COMMENTS = {
|
|
||||||
:single => "//",
|
|
||||||
:multi => { :start => "/**", :middle => "*", :end => "*/" },
|
|
||||||
:heredoc => nil
|
|
||||||
}
|
|
||||||
COMMENT_STYLES = {
|
|
||||||
"bash" => { :single => "#", :multi => nil },
|
|
||||||
"c" => C_STYLE_COMMENTS,
|
|
||||||
"coffee-script" => {
|
|
||||||
:single => "#",
|
|
||||||
:multi => { :start => "###", :middle => nil, :end => "###" },
|
|
||||||
:heredoc => nil
|
|
||||||
},
|
|
||||||
"cpp" => C_STYLE_COMMENTS,
|
|
||||||
"csharp" => C_STYLE_COMMENTS,
|
|
||||||
"css" => {
|
|
||||||
:single => nil,
|
|
||||||
:multi => { :start => "/**", :middle => "*", :end => "*/" },
|
|
||||||
:heredoc => nil
|
|
||||||
},
|
|
||||||
"html" => {
|
|
||||||
:single => nil,
|
|
||||||
:multi => { :start => '<!--', :middle => nil, :end => '-->' },
|
|
||||||
:heredoc => nil
|
|
||||||
},
|
|
||||||
"java" => C_STYLE_COMMENTS,
|
|
||||||
"js" => C_STYLE_COMMENTS,
|
|
||||||
"lua" => {
|
|
||||||
:single => "--",
|
|
||||||
:multi => nil,
|
|
||||||
:heredoc => nil
|
|
||||||
},
|
|
||||||
"php" => C_STYLE_COMMENTS,
|
|
||||||
"python" => {
|
|
||||||
:single => "#",
|
|
||||||
:multi => { :start => '"""', :middle => nil, :end => '"""' },
|
|
||||||
:heredoc => nil
|
|
||||||
},
|
|
||||||
"rb" => {
|
|
||||||
:single => "#",
|
|
||||||
:multi => { :start => '=begin', :middle => nil, :end => '=end' },
|
|
||||||
:heredoc => "<<-"
|
|
||||||
},
|
|
||||||
"scala" => C_STYLE_COMMENTS,
|
|
||||||
"scheme" => { :single => ";;", :multi => nil, :heredoc => nil },
|
|
||||||
"xml" => {
|
|
||||||
:single => nil,
|
|
||||||
:multi => { :start => '<!--', :middle => nil, :end => '-->' },
|
|
||||||
:heredoc => nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def generate_comment_chars
|
|
||||||
@_commentchar ||=
|
|
||||||
if COMMENT_STYLES[@options[:language]]
|
|
||||||
COMMENT_STYLES[@options[:language]]
|
|
||||||
else
|
|
||||||
{ :single => @options[:comment_chars], :multi => nil, :heredoc => nil }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Internal Parsing and Highlighting
|
|
||||||
# ---------------------------------
|
|
||||||
|
|
||||||
# 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, comment characters stripped.
|
|
||||||
def parse(data)
|
|
||||||
sections = []
|
|
||||||
docs, code = [], []
|
|
||||||
lines = data.split("\n")
|
|
||||||
|
|
||||||
# The first line is ignored if it is a shebang line. We also ignore the
|
|
||||||
# PEP 263 encoding information in python sourcefiles, and the similar ruby
|
|
||||||
# 1.9 syntax.
|
|
||||||
lines.shift if lines[0] =~ /^\#\!/
|
|
||||||
lines.shift if lines[0] =~ /coding[:=]\s*[-\w.]+/ &&
|
|
||||||
[ "python", "rb" ].include?(@options[:language])
|
|
||||||
|
|
||||||
# To detect both block comments and single-line comments, we'll set
|
|
||||||
# up a tiny state machine, and loop through each line of the file.
|
|
||||||
# This requires an `in_comment_block` boolean, and a few regular
|
|
||||||
# expressions for line tests. We'll do the same for fake heredoc parsing.
|
|
||||||
in_comment_block = false
|
|
||||||
in_heredoc = false
|
|
||||||
single_line_comment, block_comment_start, block_comment_mid, block_comment_end =
|
|
||||||
nil, nil, nil, nil
|
|
||||||
if not @options[:comment_chars][:single].nil?
|
|
||||||
single_line_comment = Regexp.new("^\\s*#{Regexp.escape(@options[:comment_chars][:single])}\\s?")
|
|
||||||
end
|
|
||||||
if not @options[:comment_chars][:multi].nil?
|
|
||||||
block_comment_start = Regexp.new("^\\s*#{Regexp.escape(@options[:comment_chars][:multi][:start])}\\s*$")
|
|
||||||
block_comment_end = Regexp.new("^\\s*#{Regexp.escape(@options[:comment_chars][:multi][:end])}\\s*$")
|
|
||||||
block_comment_one_liner = Regexp.new("^\\s*#{Regexp.escape(@options[:comment_chars][:multi][:start])}\\s*(.*?)\\s*#{Regexp.escape(@options[:comment_chars][:multi][:end])}\\s*$")
|
|
||||||
block_comment_start_with = Regexp.new("^\\s*#{Regexp.escape(@options[:comment_chars][:multi][:start])}\\s*(.*?)$")
|
|
||||||
block_comment_end_with = Regexp.new("\\s*(.*?)\\s*#{Regexp.escape(@options[:comment_chars][:multi][:end])}\\s*$")
|
|
||||||
if @options[:comment_chars][:multi][:middle]
|
|
||||||
block_comment_mid = Regexp.new("^\\s*#{Regexp.escape(@options[:comment_chars][:multi][:middle])}\\s?")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not @options[:comment_chars][:heredoc].nil?
|
|
||||||
heredoc_start = Regexp.new("#{Regexp.escape(@options[:comment_chars][:heredoc])}(\\S+)$")
|
|
||||||
end
|
|
||||||
lines.each do |line|
|
|
||||||
# If we're currently in a comment block, check whether the line matches
|
|
||||||
# the _end_ of a comment block or the _end_ of a comment block with a
|
|
||||||
# comment.
|
|
||||||
if in_comment_block
|
|
||||||
if block_comment_end && line.match(block_comment_end)
|
|
||||||
in_comment_block = false
|
|
||||||
elsif block_comment_end_with && line.match(block_comment_end_with)
|
|
||||||
in_comment_block = false
|
|
||||||
docs << line.match(block_comment_end_with).captures.first.
|
|
||||||
sub(block_comment_mid || '', '')
|
|
||||||
else
|
|
||||||
docs << line.sub(block_comment_mid || '', '')
|
|
||||||
end
|
|
||||||
# If we're currently in a heredoc, we're looking for the end of the
|
|
||||||
# heredoc, and everything it contains is code.
|
|
||||||
elsif in_heredoc
|
|
||||||
if line.match(Regexp.new("^#{Regexp.escape(in_heredoc)}$"))
|
|
||||||
in_heredoc = false
|
|
||||||
end
|
|
||||||
code << line
|
|
||||||
# Otherwise, check whether the line starts a heredoc. If so, note the end
|
|
||||||
# pattern, and the line is code. Otherwise check whether the line matches
|
|
||||||
# the beginning of a block, or a single-line comment all on it's lonesome.
|
|
||||||
# In either case, if there's code, start a new section.
|
|
||||||
else
|
|
||||||
if heredoc_start && line.match(heredoc_start)
|
|
||||||
in_heredoc = $1
|
|
||||||
code << line
|
|
||||||
elsif block_comment_one_liner && line.match(block_comment_one_liner)
|
|
||||||
if code.any?
|
|
||||||
sections << [docs, code]
|
|
||||||
docs, code = [], []
|
|
||||||
end
|
|
||||||
docs << line.match(block_comment_one_liner).captures.first
|
|
||||||
elsif block_comment_start && line.match(block_comment_start)
|
|
||||||
in_comment_block = true
|
|
||||||
if code.any?
|
|
||||||
sections << [docs, code]
|
|
||||||
docs, code = [], []
|
|
||||||
end
|
|
||||||
elsif block_comment_start_with && line.match(block_comment_start_with)
|
|
||||||
in_comment_block = true
|
|
||||||
if code.any?
|
|
||||||
sections << [docs, code]
|
|
||||||
docs, code = [], []
|
|
||||||
end
|
|
||||||
docs << line.match(block_comment_start_with).captures.first
|
|
||||||
elsif single_line_comment && line.match(single_line_comment)
|
|
||||||
if code.any?
|
|
||||||
sections << [docs, code]
|
|
||||||
docs, code = [], []
|
|
||||||
end
|
|
||||||
docs << line.sub(single_line_comment || '', '')
|
|
||||||
else
|
|
||||||
code << line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
sections << [docs, code] if docs.any? || code.any?
|
|
||||||
normalize_leading_spaces(sections)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Normalizes documentation whitespace by checking for leading whitespace,
|
|
||||||
# removing it, and then removing the same amount of whitespace from each
|
|
||||||
# succeeding line. That is:
|
|
||||||
#
|
|
||||||
# def func():
|
|
||||||
# """
|
|
||||||
# Comment 1
|
|
||||||
# Comment 2
|
|
||||||
# """
|
|
||||||
# print "omg!"
|
|
||||||
#
|
|
||||||
# should yield a comment block of `Comment 1\nComment 2` and code of
|
|
||||||
# `def func():\n print "omg!"`
|
|
||||||
def normalize_leading_spaces(sections)
|
|
||||||
sections.map do |section|
|
|
||||||
if section.any? && section[0].any?
|
|
||||||
leading_space = section[0][0].match("^\s+")
|
|
||||||
if leading_space
|
|
||||||
section[0] =
|
|
||||||
section[0].map{ |line| line.sub(/^#{leading_space.to_s}/, '') }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
section
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Take the list of paired *sections* two-tuples and split into two
|
|
||||||
# separate lists: one holding the comments with leaders removed and
|
|
||||||
# one with the code blocks.
|
|
||||||
def split(sections)
|
|
||||||
docs_blocks, code_blocks = [], []
|
|
||||||
sections.each do |docs,code|
|
|
||||||
docs_blocks << docs.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
|
|
||||||
|
|
||||||
# Take a list of block comments and convert Docblock @annotations to
|
|
||||||
# Markdown syntax.
|
|
||||||
def docblock(docs)
|
|
||||||
docs.map do |doc|
|
|
||||||
doc.split("\n").map do |line|
|
|
||||||
line.match(/^@\w+/) ? line.sub(/^@(\w+)\s+/, '> **\1** ')+" " : line
|
|
||||||
end.join("\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Take the result of `split` and apply Markdown formatting to comments and
|
|
||||||
# syntax highlighting to source code.
|
|
||||||
def highlight(blocks)
|
|
||||||
docs_blocks, code_blocks = blocks
|
|
||||||
|
|
||||||
# Pre-process Docblock @annotations.
|
|
||||||
if @options[:docblocks]
|
|
||||||
docs_blocks = docblock(docs_blocks)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Combine all docs blocks into a single big markdown document with section
|
|
||||||
# dividers and run through the Markdown processor. Then split it back out
|
|
||||||
# into separate sections.
|
|
||||||
markdown = docs_blocks.join("\n\n##### DIVIDER\n\n")
|
|
||||||
docs_html = process_markdown(markdown).
|
|
||||||
split(/\n*<h5>DIVIDER<\/h5>\n*/m)
|
|
||||||
|
|
||||||
# Combine all code blocks into a single big stream with section dividers and
|
|
||||||
# run through either `pygmentize(1)` or <http://pygments.appspot.com>
|
|
||||||
span, espan = '<span class="c.?">', '</span>'
|
|
||||||
if @options[:comment_chars][:single]
|
|
||||||
front = @options[:comment_chars][:single]
|
|
||||||
divider_input = "\n\n#{front} DIVIDER\n\n"
|
|
||||||
divider_output = Regexp.new(
|
|
||||||
[ "\\n*",
|
|
||||||
span,
|
|
||||||
Regexp.escape(CGI.escapeHTML(front)),
|
|
||||||
' DIVIDER',
|
|
||||||
espan,
|
|
||||||
"\\n*"
|
|
||||||
].join, Regexp::MULTILINE
|
|
||||||
)
|
|
||||||
else
|
|
||||||
front = @options[:comment_chars][:multi][:start]
|
|
||||||
back = @options[:comment_chars][:multi][:end]
|
|
||||||
divider_input = "\n\n#{front}\nDIVIDER\n#{back}\n\n"
|
|
||||||
divider_output = Regexp.new(
|
|
||||||
[ "\\n*",
|
|
||||||
span, Regexp.escape(CGI.escapeHTML(front)), espan,
|
|
||||||
"\\n",
|
|
||||||
span, "DIVIDER", espan,
|
|
||||||
"\\n",
|
|
||||||
span, Regexp.escape(CGI.escapeHTML(back)), espan,
|
|
||||||
"\\n*"
|
|
||||||
].join, Regexp::MULTILINE
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
code_stream = code_blocks.join(divider_input)
|
|
||||||
|
|
||||||
code_html =
|
|
||||||
if pygmentize?
|
|
||||||
highlight_pygmentize(code_stream)
|
|
||||||
else
|
|
||||||
highlight_webservice(code_stream)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Do some post-processing on the pygments output to split things back
|
|
||||||
# into sections and remove partial `<pre>` blocks.
|
|
||||||
code_html = code_html.
|
|
||||||
split(divider_output).
|
|
||||||
map { |code| code.sub(/\n?<div class="highlight"><pre>/m, '') }.
|
|
||||||
map { |code| code.sub(/\n?<\/pre><\/div>\n/m, '') }
|
|
||||||
|
|
||||||
# Lastly, combine the docs and code lists back into a list of two-tuples.
|
|
||||||
docs_html.zip(code_html)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Convert Markdown to classy HTML.
|
|
||||||
def process_markdown(text)
|
|
||||||
Markdown.new(text, :smart).to_html
|
|
||||||
end
|
|
||||||
|
|
||||||
# We `popen` a read/write pygmentize process in the parent and
|
|
||||||
# then fork off a child process to write the input.
|
|
||||||
def highlight_pygmentize(code)
|
|
||||||
code_html = nil
|
|
||||||
open("|pygmentize -l #{@options[:language]} -O encoding=utf-8 -f html", 'r+') do |fd|
|
|
||||||
pid =
|
|
||||||
fork {
|
|
||||||
fd.close_read
|
|
||||||
fd.write code
|
|
||||||
fd.close_write
|
|
||||||
exit!
|
|
||||||
}
|
|
||||||
fd.close_write
|
|
||||||
code_html = fd.read
|
|
||||||
fd.close_read
|
|
||||||
Process.wait(pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
code_html
|
|
||||||
end
|
|
||||||
|
|
||||||
# Pygments is not one of those things that's trivial for a ruby user to install,
|
|
||||||
# so we'll fall back on a webservice to highlight the code if it isn't available.
|
|
||||||
def highlight_webservice(code)
|
|
||||||
Net::HTTP.post_form(
|
|
||||||
URI.parse('http://pygments.appspot.com/'),
|
|
||||||
{'lang' => @options[:language], 'code' => code}
|
|
||||||
).body
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# And that's it.
|
|
@ -1,190 +0,0 @@
|
|||||||
/*--------------------- Layout and Typography ----------------------------*/
|
|
||||||
body {
|
|
||||||
font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 22px;
|
|
||||||
color: #252519;
|
|
||||||
margin: 0; padding: 0;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #261a3b;
|
|
||||||
}
|
|
||||||
a:visited {
|
|
||||||
color: #261a3b;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0 0 15px 0;
|
|
||||||
}
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
margin: 0px 0 15px 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
#container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
#background {
|
|
||||||
position: fixed;
|
|
||||||
top: 0; left: 525px; right: 0; bottom: 0;
|
|
||||||
background: #f5f5ff;
|
|
||||||
border-left: 1px solid #e5e5ee;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
#jump_to, #jump_page {
|
|
||||||
background: white;
|
|
||||||
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
|
|
||||||
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
|
|
||||||
font: 10px Arial;
|
|
||||||
text-transform: uppercase;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
#jump_to, #jump_wrapper {
|
|
||||||
position: fixed;
|
|
||||||
top: 0; right: 0;
|
|
||||||
padding: 5px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#jump_wrapper {
|
|
||||||
padding: 0;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
#jump_to:hover #jump_wrapper {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
#jump_page {
|
|
||||||
padding: 5px 0 3px;
|
|
||||||
margin: 0 0 25px 25px;
|
|
||||||
overflow: hidden;
|
|
||||||
zoom: 1;
|
|
||||||
}
|
|
||||||
#jump_page .source {
|
|
||||||
float: left;
|
|
||||||
display: inline;
|
|
||||||
padding: 5px 10px;
|
|
||||||
text-decoration: none;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
#jump_page .source:hover {
|
|
||||||
background: #f5f5ff;
|
|
||||||
}
|
|
||||||
#jump_page .source:first-child {
|
|
||||||
}
|
|
||||||
table td {
|
|
||||||
border: 0;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
td.docs, th.docs {
|
|
||||||
max-width: 450px;
|
|
||||||
min-width: 450px;
|
|
||||||
min-height: 5px;
|
|
||||||
padding: 10px 25px 1px 50px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
vertical-align: top;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.docs pre {
|
|
||||||
margin: 15px 0 15px;
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
.docs p tt, .docs p code {
|
|
||||||
background: #f8f8ff;
|
|
||||||
border: 1px solid #dedede;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 0 0.2em;
|
|
||||||
}
|
|
||||||
.pilwrap {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.pilcrow {
|
|
||||||
font: 12px Arial;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #454545;
|
|
||||||
position: absolute;
|
|
||||||
top: 3px; left: -20px;
|
|
||||||
padding: 1px 2px;
|
|
||||||
opacity: 0;
|
|
||||||
-webkit-transition: opacity 0.2s linear;
|
|
||||||
}
|
|
||||||
td.docs:hover .pilcrow {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
td.code, th.code {
|
|
||||||
padding: 14px 15px 16px 25px;
|
|
||||||
width: 100%;
|
|
||||||
vertical-align: top;
|
|
||||||
background: #f5f5ff;
|
|
||||||
border-left: 1px solid #e5e5ee;
|
|
||||||
}
|
|
||||||
pre, tt, code {
|
|
||||||
font-size: 12px; line-height: 18px;
|
|
||||||
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
|
|
||||||
margin: 0; padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*---------------------- Syntax Highlighting -----------------------------*/
|
|
||||||
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
|
|
||||||
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
|
|
||||||
body .hll { background-color: #ffffcc }
|
|
||||||
body .c { color: #408080; font-style: italic } /* Comment */
|
|
||||||
body .err { border: 1px solid #FF0000 } /* Error */
|
|
||||||
body .k { color: #954121 } /* Keyword */
|
|
||||||
body .o { color: #666666 } /* Operator */
|
|
||||||
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
|
||||||
body .cp { color: #BC7A00 } /* Comment.Preproc */
|
|
||||||
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
|
|
||||||
body .cs { color: #408080; font-style: italic } /* Comment.Special */
|
|
||||||
body .gd { color: #A00000 } /* Generic.Deleted */
|
|
||||||
body .ge { font-style: italic } /* Generic.Emph */
|
|
||||||
body .gr { color: #FF0000 } /* Generic.Error */
|
|
||||||
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
|
||||||
body .gi { color: #00A000 } /* Generic.Inserted */
|
|
||||||
body .go { color: #808080 } /* Generic.Output */
|
|
||||||
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
|
||||||
body .gs { font-weight: bold } /* Generic.Strong */
|
|
||||||
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
|
||||||
body .gt { color: #0040D0 } /* Generic.Traceback */
|
|
||||||
body .kc { color: #954121 } /* Keyword.Constant */
|
|
||||||
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
|
|
||||||
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
|
|
||||||
body .kp { color: #954121 } /* Keyword.Pseudo */
|
|
||||||
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
|
|
||||||
body .kt { color: #B00040 } /* Keyword.Type */
|
|
||||||
body .m { color: #666666 } /* Literal.Number */
|
|
||||||
body .s { color: #219161 } /* Literal.String */
|
|
||||||
body .na { color: #7D9029 } /* Name.Attribute */
|
|
||||||
body .nb { color: #954121 } /* Name.Builtin */
|
|
||||||
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
|
||||||
body .no { color: #880000 } /* Name.Constant */
|
|
||||||
body .nd { color: #AA22FF } /* Name.Decorator */
|
|
||||||
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
|
||||||
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
|
||||||
body .nf { color: #0000FF } /* Name.Function */
|
|
||||||
body .nl { color: #A0A000 } /* Name.Label */
|
|
||||||
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
|
||||||
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
|
|
||||||
body .nv { color: #19469D } /* Name.Variable */
|
|
||||||
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
|
||||||
body .w { color: #bbbbbb } /* Text.Whitespace */
|
|
||||||
body .mf { color: #666666 } /* Literal.Number.Float */
|
|
||||||
body .mh { color: #666666 } /* Literal.Number.Hex */
|
|
||||||
body .mi { color: #666666 } /* Literal.Number.Integer */
|
|
||||||
body .mo { color: #666666 } /* Literal.Number.Oct */
|
|
||||||
body .sb { color: #219161 } /* Literal.String.Backtick */
|
|
||||||
body .sc { color: #219161 } /* Literal.String.Char */
|
|
||||||
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
|
|
||||||
body .s2 { color: #219161 } /* Literal.String.Double */
|
|
||||||
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
|
||||||
body .sh { color: #219161 } /* Literal.String.Heredoc */
|
|
||||||
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
|
||||||
body .sx { color: #954121 } /* Literal.String.Other */
|
|
||||||
body .sr { color: #BB6688 } /* Literal.String.Regex */
|
|
||||||
body .s1 { color: #219161 } /* Literal.String.Single */
|
|
||||||
body .ss { color: #19469D } /* Literal.String.Symbol */
|
|
||||||
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
|
|
||||||
body .vc { color: #19469D } /* Name.Variable.Class */
|
|
||||||
body .vg { color: #19469D } /* Name.Variable.Global */
|
|
||||||
body .vi { color: #19469D } /* Name.Variable.Instance */
|
|
||||||
body .il { color: #666666 } /* Literal.Number.Integer.Long */
|
|
@ -1,46 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<style type="text/css">{{{ styles }}}</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='container'>
|
|
||||||
<div id="background"></div>
|
|
||||||
{{#sources?}}
|
|
||||||
<div id="jump_to">
|
|
||||||
Jump To …
|
|
||||||
<div id="jump_wrapper">
|
|
||||||
<div id="jump_page">
|
|
||||||
{{#sources}}
|
|
||||||
<a class="source" href="{{ url }}">{{ basename }}</a>
|
|
||||||
{{/sources}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/sources?}}
|
|
||||||
<table cellspacing=0 cellpadding=0>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class=docs><h1>{{ title }}</h1></th>
|
|
||||||
<th class=code></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#sections}}
|
|
||||||
<tr id='section-{{ section_id }}'>
|
|
||||||
<td class=docs>
|
|
||||||
<div class="pilwrap">
|
|
||||||
<a class="pilcrow" href="#section-{{ section_id }}">¶</a>
|
|
||||||
</div>
|
|
||||||
{{{ docs }}}
|
|
||||||
</td>
|
|
||||||
<td class=code>
|
|
||||||
<div class='highlight'><pre>{{{ code }}}</pre></div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/sections}}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
@ -1,63 +0,0 @@
|
|||||||
require 'mustache'
|
|
||||||
require 'pathname'
|
|
||||||
|
|
||||||
class Rocco::Layout < Mustache
|
|
||||||
self.template_path = "#{File.dirname(__FILE__)}/.."
|
|
||||||
|
|
||||||
def initialize(doc, file=nil)
|
|
||||||
@doc = doc
|
|
||||||
if not file.nil?
|
|
||||||
Rocco::Layout.template_file = file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def title
|
|
||||||
File.basename(@doc.file)
|
|
||||||
end
|
|
||||||
|
|
||||||
def file
|
|
||||||
@doc.file
|
|
||||||
end
|
|
||||||
|
|
||||||
def styles
|
|
||||||
File.read(File.expand_path('../docco.css', __FILE__))
|
|
||||||
end
|
|
||||||
|
|
||||||
def sections
|
|
||||||
num = 0
|
|
||||||
@doc.sections.map do |docs,code|
|
|
||||||
code ||= ''
|
|
||||||
is_header = /^<h.>(.+)<\/h.>$/.match( docs )
|
|
||||||
header_text = is_header && is_header[1].split.join("_")
|
|
||||||
num += 1
|
|
||||||
{
|
|
||||||
:docs => docs,
|
|
||||||
:docs? => !docs.empty?,
|
|
||||||
:header? => is_header,
|
|
||||||
|
|
||||||
:code => code,
|
|
||||||
:code? => !code.empty?,
|
|
||||||
|
|
||||||
:empty? => ( code.empty? && docs.empty? ),
|
|
||||||
:section_id => is_header ? header_text : num
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sources?
|
|
||||||
@doc.sources.length > 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def sources
|
|
||||||
currentpath = Pathname.new( File.dirname( @doc.file ) )
|
|
||||||
@doc.sources.sort { |left, right| File.split(left).last <=> File.split(right).last }.map do |source|
|
|
||||||
htmlpath = Pathname.new( source.sub( Regexp.new( "#{File.extname(source)}$"), ".html" ) )
|
|
||||||
|
|
||||||
{
|
|
||||||
:path => source,
|
|
||||||
:basename => File.basename(source),
|
|
||||||
:url => htmlpath.relative_path_from( currentpath )
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,123 +0,0 @@
|
|||||||
#### Rocco Rake Tasks
|
|
||||||
#
|
|
||||||
# To use the Rocco Rake tasks, require `rocco/tasks` in your `Rakefile`
|
|
||||||
# and define a Rake task with `rocco_task`. In its simplest form, `rocco_task`
|
|
||||||
# takes the path to a destination directory where HTML docs should be built:
|
|
||||||
#
|
|
||||||
# require 'rocco/tasks'
|
|
||||||
#
|
|
||||||
# desc "Build Rocco Docs"
|
|
||||||
# Rocco::make 'docs/'
|
|
||||||
#
|
|
||||||
# This creates a `:rocco` rake task, which can then be run with:
|
|
||||||
#
|
|
||||||
# rake rocco
|
|
||||||
#
|
|
||||||
# It's a good idea to guard against Rocco not being available, since your
|
|
||||||
# Rakefile will fail to load otherwise. Consider doing something like this,
|
|
||||||
# so that your Rakefile will still work
|
|
||||||
#
|
|
||||||
# begin
|
|
||||||
# require 'rocco/tasks'
|
|
||||||
# Rocco::make 'docs/'
|
|
||||||
# rescue LoadError
|
|
||||||
# warn "#$! -- rocco tasks not loaded."
|
|
||||||
# task :rocco
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# It's also possible to pass a glob pattern:
|
|
||||||
#
|
|
||||||
# Rocco::make 'html/', 'lib/thing/**/*.rb'
|
|
||||||
#
|
|
||||||
# Or a list of glob patterns:
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
require 'rocco'
|
|
||||||
|
|
||||||
# Reopen the Rocco class and add a `make` class method. This is a simple bit
|
|
||||||
# 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', options={})
|
|
||||||
Task.new(:rocco, dest, source_files, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# `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
|
|
||||||
include Rake::DSL if defined?(Rake::DSL)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Run over the source file list, constructing destination filenames
|
|
||||||
# and defining file tasks.
|
|
||||||
@sources.each do |source_file|
|
|
||||||
dest_file = source_file.sub(Regexp.new("#{File.extname(source_file)}$"), ".html")
|
|
||||||
define_file_task source_file, "#{@dest}#{dest_file}"
|
|
||||||
|
|
||||||
# If `rake/clean` was required, add the generated files to the list.
|
|
||||||
# That way all Rocco generated are removed when running `rake clean`.
|
|
||||||
CLEAN.include "#{@dest}#{dest_file}" if defined? CLEAN
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define the destination directory task and make the `:rocco` task depend
|
|
||||||
# on it. This causes the destination directory to be created if it doesn't
|
|
||||||
# already exist.
|
|
||||||
def define_directory_task(path)
|
|
||||||
directory path
|
|
||||||
task @name => path
|
|
||||||
end
|
|
||||||
|
|
||||||
# Setup a `file` task for a single Rocco output file (`dest_file`). It
|
|
||||||
# depends on the source file, the destination directory, and all of Rocco's
|
|
||||||
# internal source code, so that the destination file is rebuilt when any of
|
|
||||||
# those changes.
|
|
||||||
#
|
|
||||||
# You can run these tasks directly with Rake:
|
|
||||||
#
|
|
||||||
# rake docs/foo.html docs/bar.html
|
|
||||||
#
|
|
||||||
# ... would generate the `foo.html` and `bar.html` files but only if they
|
|
||||||
# don't already exist or one of their dependencies was changed.
|
|
||||||
def define_file_task(source_file, dest_file)
|
|
||||||
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, @options)
|
|
||||||
FileUtils.mkdir_p(File.dirname(dest_file))
|
|
||||||
File.open(dest_file, 'wb') { |fd| fd.write(rocco.to_html) }
|
|
||||||
end
|
|
||||||
task @name => dest_file
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a `FileList` that includes all of Roccos source files. This causes
|
|
||||||
# output files to be regenerated properly when someone upgrades the Rocco
|
|
||||||
# library.
|
|
||||||
def rocco_source_files
|
|
||||||
libdir = File.expand_path('../..', __FILE__)
|
|
||||||
FileList["#{libdir}/rocco.rb", "#{libdir}/rocco/**"]
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,56 +0,0 @@
|
|||||||
$LOAD_PATH.unshift 'lib'
|
|
||||||
require "rocco"
|
|
||||||
|
|
||||||
Gem::Specification.new do |s|
|
|
||||||
s.specification_version = 2 if s.respond_to? :specification_version=
|
|
||||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
||||||
|
|
||||||
s.name = 'rocco'
|
|
||||||
s.version = Rocco::VERSION
|
|
||||||
s.date = Time.now.strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
s.description = "Docco in Ruby"
|
|
||||||
s.summary = s.description
|
|
||||||
|
|
||||||
s.authors = ["Ryan Tomayko", "Mike West"]
|
|
||||||
s.email = ["r@tomayko.com", "<mike@mikewest.org>"]
|
|
||||||
|
|
||||||
# = MANIFEST =
|
|
||||||
s.files = %w[
|
|
||||||
CHANGES.md
|
|
||||||
COPYING
|
|
||||||
README
|
|
||||||
Rakefile
|
|
||||||
bin/rocco
|
|
||||||
lib/rocco.rb
|
|
||||||
lib/rocco/layout.mustache
|
|
||||||
lib/rocco/layout.rb
|
|
||||||
lib/rocco/tasks.rb
|
|
||||||
rocco.gemspec
|
|
||||||
test/fixtures/issue10.iso-8859-1.rb
|
|
||||||
test/fixtures/issue10.utf-8.rb
|
|
||||||
test/helper.rb
|
|
||||||
test/suite.rb
|
|
||||||
test/test_basics.rb
|
|
||||||
test/test_block_comments.rb
|
|
||||||
test/test_comment_normalization.rb
|
|
||||||
test/test_commentchar_detection.rb
|
|
||||||
test/test_descriptive_section_names.rb
|
|
||||||
test/test_language_detection.rb
|
|
||||||
test/test_reported_issues.rb
|
|
||||||
test/test_skippable_lines.rb
|
|
||||||
test/test_source_list.rb
|
|
||||||
]
|
|
||||||
# = MANIFEST =
|
|
||||||
|
|
||||||
s.executables = ["rocco"]
|
|
||||||
|
|
||||||
s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/}
|
|
||||||
s.add_dependency 'rdiscount'
|
|
||||||
s.add_dependency 'mustache'
|
|
||||||
|
|
||||||
s.has_rdoc = false
|
|
||||||
s.homepage = "http://rtomayko.github.com/rocco/"
|
|
||||||
s.require_paths = %w[lib]
|
|
||||||
s.rubygems_version = '1.1.1'
|
|
||||||
end
|
|
449
rocco.html
Normal file
449
rocco.html
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||||
|
<title>rocco.rb</title>
|
||||||
|
<link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='container'>
|
||||||
|
<div id="background"></div>
|
||||||
|
<div id="jump_to">
|
||||||
|
Jump To …
|
||||||
|
<div id="jump_wrapper">
|
||||||
|
<div id="jump_page">
|
||||||
|
<a class="source" href="rocco.html">rocco.rb</a>
|
||||||
|
<a class="source" href="layout.html">layout.rb</a>
|
||||||
|
<a class="source" href="tasks.html">tasks.rb</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table cellspacing=0 cellpadding=0>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class=docs><h1>rocco.rb</h1></th>
|
||||||
|
<th class=code></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr id='section-1'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-1">¶</a>
|
||||||
|
</div>
|
||||||
|
<p><strong>Rocco</strong> is a Ruby port of <a href="http://jashkenas.github.com/docco/">Docco</a>, the quick-and-dirty,
|
||||||
|
hundred-line-long, literate-programming-style documentation generator.</p>
|
||||||
|
|
||||||
|
<p>Rocco reads Ruby source files and produces annotated source documentation
|
||||||
|
in HTML format. Comments are formatted with <a href="http://daringfireball.net/projects/markdown/">Markdown</a> and presented
|
||||||
|
alongside syntax highlighted code so as to give an annotation effect.
|
||||||
|
This page is the result of running Rocco against <a href="http://github.com/rtomayko/rocco/blob/master/lib/rocco.rb#commit">its own source file</a>.</p>
|
||||||
|
|
||||||
|
<p>Most of this was written while waiting for <a href="http://nodejs.org/">node.js</a> to build (so I
|
||||||
|
could use Docco!). Docco’s gorgeous HTML and CSS are taken verbatim.
|
||||||
|
The main difference is that Rocco is written in Ruby instead of
|
||||||
|
<a href="http://coffeescript.org/">CoffeeScript</a> and may be a bit easier to obtain and install in
|
||||||
|
existing Ruby environments or where node doesn’t run yet.</p>
|
||||||
|
|
||||||
|
<p>Install Rocco with Rubygems:</p>
|
||||||
|
|
||||||
|
<pre><code>gem install rocco
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>Once installed, the <code>rocco</code> command can be used to generate documentation
|
||||||
|
for a set of Ruby source files:</p>
|
||||||
|
|
||||||
|
<pre><code>rocco lib/*.rb
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>The HTML files are written to the current working directory.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-2'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-2">¶</a>
|
||||||
|
</div>
|
||||||
|
<h3>Prerequisites</h3>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-3'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-3">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We’ll need a Markdown library. <a href="http://github.com/rtomayko/rdiscount">RDiscount</a>, if we’re lucky. Otherwise,
|
||||||
|
issue a warning and fall back on using BlueCloth.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="k">begin</span>
|
||||||
|
<span class="nb">require</span> <span class="s1">'rdiscount'</span>
|
||||||
|
<span class="k">rescue</span> <span class="no">LoadError</span> <span class="o">=></span> <span class="n">boom</span>
|
||||||
|
<span class="nb">warn</span> <span class="s2">"WARNING: </span><span class="si">#{</span><span class="n">boom</span><span class="si">}</span><span class="s2">. Trying bluecloth."</span>
|
||||||
|
<span class="nb">require</span> <span class="s1">'bluecloth'</span>
|
||||||
|
<span class="no">Markdown</span> <span class="o">=</span> <span class="no">BlueCloth</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-4'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-4">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We use <a href="http://defunkt.github.com/mustache/">{{ mustache }}</a> for
|
||||||
|
HTML templating.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'mustache'</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-5'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-5">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We use <code>Net::HTTP</code> to highlight code via <a href="http://pygments.appspot.com">http://pygments.appspot.com</a></p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'net/http'</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-6'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-6">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Code is run through <a href="http://pygments.org/">Pygments</a> for syntax
|
||||||
|
highlighting. If it’s not installed, locally, use a webservice.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="kp">include</span> <span class="no">FileTest</span>
|
||||||
|
<span class="k">if</span> <span class="o">!</span><span class="no">ENV</span><span class="o">[</span><span class="s1">'PATH'</span><span class="o">].</span><span class="n">split</span><span class="p">(</span><span class="s1">':'</span><span class="p">)</span><span class="o">.</span><span class="n">any?</span> <span class="p">{</span> <span class="o">|</span><span class="n">dir</span><span class="o">|</span> <span class="n">executable?</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/pygmentize"</span><span class="p">)</span> <span class="p">}</span>
|
||||||
|
<span class="nb">warn</span> <span class="s2">"WARNING: Pygments not found. Using webservice."</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-7'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-7">¶</a>
|
||||||
|
</div>
|
||||||
|
<h3>Public Interface</h3>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-8'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-8">¶</a>
|
||||||
|
</div>
|
||||||
|
<p><code>Rocco.new</code> takes a source <code>filename</code>, an optional list of source filenames
|
||||||
|
for other documentation sources, an <code>options</code> hash, and an optional <code>block</code>.
|
||||||
|
The <code>options</code> hash respects two members: <code>:language</code>, which specifies which
|
||||||
|
Pygments lexer to use; and <code>:comment_chars</code>, which specifies the comment
|
||||||
|
characters of the target language. The options default to <code>'ruby'</code> and <code>'#'</code>,
|
||||||
|
respectively.
|
||||||
|
When <code>block</code> is given, it must read the contents of the file using whatever
|
||||||
|
means necessary and return it as a string. With no <code>block</code>, the file is read
|
||||||
|
to retrieve data.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="k">class</span> <span class="nc">Rocco</span>
|
||||||
|
<span class="no">VERSION</span> <span class="o">=</span> <span class="s1">'0.5'</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">sources</span><span class="o">=[]</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="p">{},</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span>
|
||||||
|
<span class="vi">@file</span> <span class="o">=</span> <span class="n">filename</span>
|
||||||
|
<span class="vi">@data</span> <span class="o">=</span>
|
||||||
|
<span class="k">if</span> <span class="nb">block_given?</span>
|
||||||
|
<span class="k">yield</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">defaults</span> <span class="o">=</span> <span class="p">{</span>
|
||||||
|
<span class="ss">:language</span> <span class="o">=></span> <span class="s1">'ruby'</span><span class="p">,</span>
|
||||||
|
<span class="ss">:comment_chars</span> <span class="o">=></span> <span class="s1">'#'</span><span class="p">,</span>
|
||||||
|
<span class="ss">:template_file</span> <span class="o">=></span> <span class="kp">nil</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="vi">@options</span> <span class="o">=</span> <span class="n">defaults</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
|
||||||
|
<span class="vi">@sources</span> <span class="o">=</span> <span class="n">sources</span>
|
||||||
|
<span class="vi">@comment_pattern</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">"^</span><span class="se">\\</span><span class="s2">s*</span><span class="si">#{</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span><span class="si">}</span><span class="se">\s</span><span class="s2">?"</span><span class="p">)</span>
|
||||||
|
<span class="vi">@template_file</span> <span class="o">=</span> <span class="vi">@options</span><span class="o">[</span><span class="ss">:template_file</span><span class="o">]</span>
|
||||||
|
<span class="vi">@sections</span> <span class="o">=</span> <span class="n">highlight</span><span class="p">(</span><span class="nb">split</span><span class="p">(</span><span class="n">parse</span><span class="p">(</span><span class="vi">@data</span><span class="p">)))</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-9'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-9">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>The filename as given to <code>Rocco.new</code>.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:file</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-10'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-10">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>A list of two-tuples representing each <em>section</em> of the source file. Each
|
||||||
|
item in the list has the form: <code>[docs_html, code_html]</code>, where both
|
||||||
|
elements are strings containing the documentation and source code HTML,
|
||||||
|
respectively.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:sections</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-11'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-11">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>A list of all source filenames included in the documentation set. Useful
|
||||||
|
for building an index of other files.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:sources</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-12'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-12">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>An absolute path to a file that ought be used as a template for the
|
||||||
|
HTML-rendered documentation.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="kp">attr_reader</span> <span class="ss">:template_file</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-13'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-13">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Generate HTML output for the entire document.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="nb">require</span> <span class="s1">'rocco/layout'</span>
|
||||||
|
<span class="k">def</span> <span class="nf">to_html</span>
|
||||||
|
<span class="no">Rocco</span><span class="o">::</span><span class="no">Layout</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="vi">@template_file</span><span class="p">)</span><span class="o">.</span><span class="n">render</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-14'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-14">¶</a>
|
||||||
|
</div>
|
||||||
|
<h3>Internal Parsing and Highlighting</h3>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-15'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-15">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Parse the raw file data into a list of two-tuples. Each tuple has the
|
||||||
|
form <code>[docs, code]</code> where both elements are arrays containing the
|
||||||
|
raw lines parsed from the input file. The first line is ignored if it
|
||||||
|
is a shebang line.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">parse</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
|
||||||
|
<span class="n">sections</span> <span class="o">=</span> <span class="o">[]</span>
|
||||||
|
<span class="n">docs</span><span class="p">,</span> <span class="n">code</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span>
|
||||||
|
<span class="n">lines</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="n">lines</span><span class="o">.</span><span class="n">shift</span> <span class="k">if</span> <span class="n">lines</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">=~</span> <span class="sr">/^\#\!/</span>
|
||||||
|
<span class="n">lines</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
|
||||||
|
<span class="k">case</span> <span class="n">line</span>
|
||||||
|
<span class="k">when</span> <span class="vi">@comment_pattern</span>
|
||||||
|
<span class="k">if</span> <span class="n">code</span><span class="o">.</span><span class="n">any?</span>
|
||||||
|
<span class="n">sections</span> <span class="o"><<</span> <span class="o">[</span><span class="n">docs</span><span class="p">,</span> <span class="n">code</span><span class="o">]</span>
|
||||||
|
<span class="n">docs</span><span class="p">,</span> <span class="n">code</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">docs</span> <span class="o"><<</span> <span class="n">line</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="n">code</span> <span class="o"><<</span> <span class="n">line</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">sections</span> <span class="o"><<</span> <span class="o">[</span><span class="n">docs</span><span class="p">,</span> <span class="n">code</span><span class="o">]</span> <span class="k">if</span> <span class="n">docs</span><span class="o">.</span><span class="n">any?</span> <span class="o">||</span> <span class="n">code</span><span class="o">.</span><span class="n">any?</span>
|
||||||
|
<span class="n">sections</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-16'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-16">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Take the list of paired <em>sections</em> two-tuples and split into two
|
||||||
|
separate lists: one holding the comments with leaders removed and
|
||||||
|
one with the code blocks.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">split</span><span class="p">(</span><span class="n">sections</span><span class="p">)</span>
|
||||||
|
<span class="n">docs_blocks</span><span class="p">,</span> <span class="n">code_blocks</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span>
|
||||||
|
<span class="n">sections</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">docs</span><span class="p">,</span><span class="n">code</span><span class="o">|</span>
|
||||||
|
<span class="n">docs_blocks</span> <span class="o"><<</span> <span class="n">docs</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span> <span class="n">line</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="vi">@comment_pattern</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="p">}</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="n">code_blocks</span> <span class="o"><<</span> <span class="n">code</span><span class="o">.</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
|
||||||
|
<span class="n">tabs</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^(\t+)/</span><span class="p">)</span>
|
||||||
|
<span class="n">tabs</span> <span class="p">?</span> <span class="n">line</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr">/^\t+/</span><span class="p">,</span> <span class="s1">' '</span> <span class="o">*</span> <span class="n">tabs</span><span class="o">.</span><span class="n">captures</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">length</span><span class="p">)</span> <span class="p">:</span> <span class="n">line</span>
|
||||||
|
<span class="k">end</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="o">[</span><span class="n">docs_blocks</span><span class="p">,</span> <span class="n">code_blocks</span><span class="o">]</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-17'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-17">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Take the result of <code>split</code> and apply Markdown formatting to comments and
|
||||||
|
syntax highlighting to source code.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">highlight</span><span class="p">(</span><span class="n">blocks</span><span class="p">)</span>
|
||||||
|
<span class="n">docs_blocks</span><span class="p">,</span> <span class="n">code_blocks</span> <span class="o">=</span> <span class="n">blocks</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-18'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-18">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Combine all docs blocks into a single big markdown document with section
|
||||||
|
dividers and run through the Markdown processor. Then split it back out
|
||||||
|
into separate sections.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">markdown</span> <span class="o">=</span> <span class="n">docs_blocks</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\n</span><span class="s2">##### DIVIDER</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="n">docs_html</span> <span class="o">=</span> <span class="no">Markdown</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">markdown</span><span class="p">,</span> <span class="ss">:smart</span><span class="p">)</span><span class="o">.</span>
|
||||||
|
<span class="n">to_html</span><span class="o">.</span>
|
||||||
|
<span class="nb">split</span><span class="p">(</span><span class="sr">/\n*<h5>DIVIDER<\/h5>\n*/m</span><span class="p">)</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-19'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-19">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Combine all code blocks into a single big stream and run through either
|
||||||
|
<code>pygmentize(1)</code> or <a href="http://pygments.appspot.com">http://pygments.appspot.com</a></p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">code_stream</span> <span class="o">=</span> <span class="n">code_blocks</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\n</span><span class="si">#{</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span><span class="si">}</span><span class="s2"> DIVIDER</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="k">if</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">'PATH'</span><span class="o">].</span><span class="n">split</span><span class="p">(</span><span class="s1">':'</span><span class="p">)</span><span class="o">.</span><span class="n">any?</span> <span class="p">{</span> <span class="o">|</span><span class="n">dir</span><span class="o">|</span> <span class="n">executable?</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/pygmentize"</span><span class="p">)</span> <span class="p">}</span>
|
||||||
|
<span class="n">code_html</span> <span class="o">=</span> <span class="n">highlight_pygmentize</span><span class="p">(</span><span class="n">code_stream</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span>
|
||||||
|
<span class="n">code_html</span> <span class="o">=</span> <span class="n">highlight_webservice</span><span class="p">(</span><span class="n">code_stream</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-20'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-20">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Do some post-processing on the pygments output to split things back
|
||||||
|
into sections and remove partial <code><pre></code> blocks.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">code_html</span> <span class="o">=</span> <span class="n">code_html</span><span class="o">.</span>
|
||||||
|
<span class="nb">split</span><span class="p">(</span><span class="sr">/\n*<span class="c.?"></span><span class="si">#{</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:comment_chars</span><span class="o">]</span><span class="si">}</span><span class="sr"> DIVIDER<\/span>\n*/m</span><span class="p">)</span><span class="o">.</span>
|
||||||
|
<span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">code</span><span class="o">|</span> <span class="n">code</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr">/\n?<div class="highlight"><pre>/m</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="p">}</span><span class="o">.</span>
|
||||||
|
<span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">code</span><span class="o">|</span> <span class="n">code</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr">/\n?<\/pre><\/div>\n/m</span><span class="p">,</span> <span class="s1">''</span><span class="p">)</span> <span class="p">}</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-21'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-21">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Lastly, combine the docs and code lists back into a list of two-tuples.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">docs_html</span><span class="o">.</span><span class="n">zip</span><span class="p">(</span><span class="n">code_html</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-22'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-22">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>We <code>popen</code> a read/write pygmentize process in the parent and
|
||||||
|
then fork off a child process to write the input.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">highlight_pygmentize</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
|
||||||
|
<span class="n">code_html</span> <span class="o">=</span> <span class="kp">nil</span>
|
||||||
|
<span class="nb">open</span><span class="p">(</span><span class="s2">"|pygmentize -l </span><span class="si">#{</span><span class="vi">@options</span><span class="o">[</span><span class="ss">:language</span><span class="o">]</span><span class="si">}</span><span class="s2"> -O encoding=utf-8 -f html"</span><span class="p">,</span> <span class="s1">'r+'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">fd</span><span class="o">|</span>
|
||||||
|
<span class="n">pid</span> <span class="o">=</span>
|
||||||
|
<span class="nb">fork</span> <span class="p">{</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_read</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">write</span> <span class="n">code</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_write</span>
|
||||||
|
<span class="nb">exit!</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_write</span>
|
||||||
|
<span class="n">code_html</span> <span class="o">=</span> <span class="n">fd</span><span class="o">.</span><span class="n">read</span>
|
||||||
|
<span class="n">fd</span><span class="o">.</span><span class="n">close_read</span>
|
||||||
|
<span class="no">Process</span><span class="o">.</span><span class="n">wait</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="n">code_html</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
</pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-23'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-23">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Pygments is not one of those things that’s trivial for a ruby user to install,
|
||||||
|
so we’ll fall back on a webservice to highlight the code if it isn’t available.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">highlight_webservice</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
|
||||||
|
<span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span>
|
||||||
|
<span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s1">'http://pygments.appspot.com/'</span><span class="p">),</span>
|
||||||
|
<span class="p">{</span><span class="s1">'lang'</span> <span class="o">=></span> <span class="vi">@options</span><span class="o">[</span><span class="s1">'language'</span><span class="o">]</span><span class="p">,</span> <span class="s1">'code'</span> <span class="o">=></span> <span class="n">code</span><span class="p">}</span>
|
||||||
|
<span class="p">)</span><span class="o">.</span><span class="n">body</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-24'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-24">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>And that’s it.</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
242
tasks.html
Normal file
242
tasks.html
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html;charset=utf-8">
|
||||||
|
<title>tasks.rb</title>
|
||||||
|
<link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='container'>
|
||||||
|
<div id="background"></div>
|
||||||
|
<div id="jump_to">
|
||||||
|
Jump To …
|
||||||
|
<div id="jump_wrapper">
|
||||||
|
<div id="jump_page">
|
||||||
|
<a class="source" href="rocco.html">rocco.rb</a>
|
||||||
|
<a class="source" href="layout.html">layout.rb</a>
|
||||||
|
<a class="source" href="tasks.html">tasks.rb</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table cellspacing=0 cellpadding=0>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class=docs><h1>tasks.rb</h1></th>
|
||||||
|
<th class=code></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr id='section-1'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-1">¶</a>
|
||||||
|
</div>
|
||||||
|
<h3>Rocco Rake Tasks</h3>
|
||||||
|
|
||||||
|
<p>To use the Rocco Rake tasks, require <code>rocco/tasks</code> in your <code>Rakefile</code>
|
||||||
|
and define a Rake task with <code>rocco_task</code>. In its simplest form, <code>rocco_task</code>
|
||||||
|
takes the path to a destination directory where HTML docs should be built:</p>
|
||||||
|
|
||||||
|
<pre><code>require 'rocco/tasks'
|
||||||
|
|
||||||
|
desc "Build Rocco Docs"
|
||||||
|
Rocco::make 'docs/'
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>This creates a <code>:rocco</code> rake task, which can then be run with:</p>
|
||||||
|
|
||||||
|
<pre><code>rake rocco
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>It’s a good idea to guard against Rocco not being available, since your
|
||||||
|
Rakefile will fail to load otherwise. Consider doing something like this,
|
||||||
|
so that your Rakefile will still work</p>
|
||||||
|
|
||||||
|
<pre><code>begin
|
||||||
|
require 'rocco/tasks'
|
||||||
|
Rocco::make 'docs/'
|
||||||
|
rescue LoadError
|
||||||
|
warn "#$! -- rocco tasks not loaded."
|
||||||
|
task :rocco
|
||||||
|
end
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>It’s also possible to pass a glob pattern:</p>
|
||||||
|
|
||||||
|
<pre><code>Rocco::make 'html/', 'lib/thing/**/*.rb'
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>Or a list of glob patterns:</p>
|
||||||
|
|
||||||
|
<pre><code>Rocco::make 'html/', ['lib/thing.rb', 'lib/thing/*.rb']
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>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 <code>options</code> hash:</p>
|
||||||
|
|
||||||
|
<p> Rocco::make ‘html/’, ‘lib/thing/<em>*/</em>.rb’, {</p>
|
||||||
|
|
||||||
|
<pre><code> :language => 'io',
|
||||||
|
:comment_chars => '#'
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p> }</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-2'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-2">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Might be nice to defer this until we actually need to build docs but this
|
||||||
|
will have to do for now.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="nb">require</span> <span class="s1">'rocco'</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-3'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-3">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Reopen the Rocco class and add a <code>make</code> class method. This is a simple bit
|
||||||
|
of sugar over <code>Rocco::Task.new</code>. If you want your Rake task to be named
|
||||||
|
something other than <code>:rocco</code>, you can use <code>Rocco::Task</code> directly.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre><span class="k">class</span> <span class="nc">Rocco</span>
|
||||||
|
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">make</span><span class="p">(</span><span class="n">dest</span><span class="o">=</span><span class="s1">'docs/'</span><span class="p">,</span> <span class="n">source_files</span><span class="o">=</span><span class="s1">'lib/**/*.rb'</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="p">{})</span>
|
||||||
|
<span class="no">Task</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:rocco</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">source_files</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-4'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-4">¶</a>
|
||||||
|
</div>
|
||||||
|
<p><code>Rocco::Task.new</code> takes a task name, the destination directory docs
|
||||||
|
should be built under, and a source file pattern or file list.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">class</span> <span class="nc">Task</span>
|
||||||
|
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">task_name</span><span class="p">,</span> <span class="n">dest</span><span class="o">=</span><span class="s1">'docs/'</span><span class="p">,</span> <span class="n">sources</span><span class="o">=</span><span class="s1">'lib/**/*.rb'</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="p">{})</span>
|
||||||
|
<span class="vi">@name</span> <span class="o">=</span> <span class="n">task_name</span>
|
||||||
|
<span class="vi">@dest</span> <span class="o">=</span> <span class="n">dest</span><span class="o">[-</span><span class="mi">1</span><span class="o">]</span> <span class="o">==</span> <span class="sc">?/</span> <span class="p">?</span> <span class="n">dest</span> <span class="p">:</span> <span class="s2">"</span><span class="si">#{</span><span class="n">dest</span><span class="si">}</span><span class="s2">/"</span>
|
||||||
|
<span class="vi">@sources</span> <span class="o">=</span> <span class="no">FileList</span><span class="o">[</span><span class="n">sources</span><span class="o">]</span>
|
||||||
|
<span class="vi">@options</span> <span class="o">=</span> <span class="n">options</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-5'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-5">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Make sure there’s a <code>directory</code> task defined for our destination.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="n">define_directory_task</span> <span class="vi">@dest</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-6'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-6">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Run over the source file list, constructing destination filenames
|
||||||
|
and defining file tasks.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="vi">@sources</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">source_file</span><span class="o">|</span>
|
||||||
|
<span class="n">dest_file</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">source_file</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="o">-</span><span class="mi">2</span><span class="o">].</span><span class="n">join</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'.html'</span>
|
||||||
|
<span class="n">define_file_task</span> <span class="n">source_file</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="vi">@dest</span><span class="si">}#{</span><span class="n">dest_file</span><span class="si">}</span><span class="s2">"</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-7'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-7">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>If <code>rake/clean</code> was required, add the generated files to the list.
|
||||||
|
That way all Rocco generated are removed when running <code>rake clean</code>.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="no">CLEAN</span><span class="o">.</span><span class="n">include</span> <span class="s2">"</span><span class="si">#{</span><span class="vi">@dest</span><span class="si">}#{</span><span class="n">dest_file</span><span class="si">}</span><span class="s2">"</span> <span class="k">if</span> <span class="n">defined?</span> <span class="no">CLEAN</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-8'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-8">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Define the destination directory task and make the <code>:rocco</code> task depend
|
||||||
|
on it. This causes the destination directory to be created if it doesn’t
|
||||||
|
already exist.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">define_directory_task</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
|
||||||
|
<span class="n">directory</span> <span class="n">path</span>
|
||||||
|
<span class="n">task</span> <span class="vi">@name</span> <span class="o">=></span> <span class="n">path</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-9'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-9">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Setup a <code>file</code> task for a single Rocco output file (<code>dest_file</code>). It
|
||||||
|
depends on the source file, the destination directory, and all of Rocco’s
|
||||||
|
internal source code, so that the destination file is rebuilt when any of
|
||||||
|
those changes.</p>
|
||||||
|
|
||||||
|
<p>You can run these tasks directly with Rake:</p>
|
||||||
|
|
||||||
|
<pre><code>rake docs/foo.html docs/bar.html
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>… would generate the <code>foo.html</code> and <code>bar.html</code> files but only if they
|
||||||
|
don’t already exist or one of their dependencies was changed.</p>
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">define_file_task</span><span class="p">(</span><span class="n">source_file</span><span class="p">,</span> <span class="n">dest_file</span><span class="p">)</span>
|
||||||
|
<span class="n">prerequisites</span> <span class="o">=</span> <span class="o">[</span><span class="vi">@dest</span><span class="p">,</span> <span class="n">source_file</span><span class="o">]</span> <span class="o">+</span> <span class="n">rocco_source_files</span>
|
||||||
|
<span class="n">file</span> <span class="n">dest_file</span> <span class="o">=></span> <span class="n">prerequisites</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
|
||||||
|
<span class="n">verbose</span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"rocco: </span><span class="si">#{</span><span class="n">source_file</span><span class="si">}</span><span class="s2"> -> </span><span class="si">#{</span><span class="n">dest_file</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
|
||||||
|
<span class="n">rocco</span> <span class="o">=</span> <span class="no">Rocco</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">source_file</span><span class="p">,</span> <span class="vi">@sources</span><span class="o">.</span><span class="n">to_a</span><span class="p">,</span> <span class="vi">@options</span><span class="p">)</span>
|
||||||
|
<span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">dest_file</span><span class="p">,</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">fd</span><span class="o">|</span> <span class="n">fd</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">rocco</span><span class="o">.</span><span class="n">to_html</span><span class="p">)</span> <span class="p">}</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="n">task</span> <span class="vi">@name</span> <span class="o">=></span> <span class="n">dest_file</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id='section-10'>
|
||||||
|
<td class=docs>
|
||||||
|
<div class="pilwrap">
|
||||||
|
<a class="pilcrow" href="#section-10">¶</a>
|
||||||
|
</div>
|
||||||
|
<p>Return a <code>FileList</code> that includes all of Roccos source files. This causes
|
||||||
|
output files to be regenerated properly when someone upgrades the Rocco
|
||||||
|
library.</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class=code>
|
||||||
|
<div class='highlight'><pre> <span class="k">def</span> <span class="nf">rocco_source_files</span>
|
||||||
|
<span class="n">libdir</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s1">'../..'</span><span class="p">,</span> <span class="bp">__FILE__</span><span class="p">)</span>
|
||||||
|
<span class="no">FileList</span><span class="o">[</span><span class="s2">"</span><span class="si">#{</span><span class="n">libdir</span><span class="si">}</span><span class="s2">/rocco.rb"</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="n">libdir</span><span class="si">}</span><span class="s2">/rocco/**"</span><span class="o">]</span>
|
||||||
|
<span class="k">end</span>
|
||||||
|
|
||||||
|
<span class="k">end</span>
|
||||||
|
<span class="k">end</span></pre></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
1
test/fixtures/issue10.iso-8859-1.rb
vendored
1
test/fixtures/issue10.iso-8859-1.rb
vendored
@ -1 +0,0 @@
|
|||||||
# hello wörld
|
|
1
test/fixtures/issue10.utf-8.rb
vendored
1
test/fixtures/issue10.utf-8.rb
vendored
@ -1 +0,0 @@
|
|||||||
# hello ąćęłńóśźż
|
|
@ -1,20 +0,0 @@
|
|||||||
rootdir = File.expand_path('../../lib', __FILE__)
|
|
||||||
$LOAD_PATH.unshift "#{rootdir}/lib"
|
|
||||||
|
|
||||||
require 'test/unit'
|
|
||||||
begin; require 'turn'; rescue LoadError; end
|
|
||||||
begin
|
|
||||||
require 'rdiscount'
|
|
||||||
rescue LoadError
|
|
||||||
if !defined?(Gem)
|
|
||||||
require 'rubygems'
|
|
||||||
retry
|
|
||||||
end
|
|
||||||
end
|
|
||||||
require 'rocco'
|
|
||||||
|
|
||||||
def roccoize( filename, contents, options = {} )
|
|
||||||
Rocco.new( filename, [ filename ], options ) {
|
|
||||||
contents
|
|
||||||
}
|
|
||||||
end
|
|
@ -1,5 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
require 'test/unit'
|
|
||||||
|
|
||||||
Dir[File.expand_path('../test_*.rb', __FILE__)].
|
|
||||||
each { |file| require file }
|
|
@ -1,63 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoBasicTests < Test::Unit::TestCase
|
|
||||||
def test_rocco_exists_and_is_instancable
|
|
||||||
roccoize( "filename.rb", "# Comment 1\ndef codeblock\nend\n" )
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_filename
|
|
||||||
r = roccoize( "filename.rb", "# Comment 1\ndef codeblock\nend\n" )
|
|
||||||
assert_equal "filename.rb", r.file
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sources
|
|
||||||
r = roccoize( "filename.rb", "# Comment 1\ndef codeblock\nend\n" )
|
|
||||||
assert_equal [ "filename.rb" ], r.sources
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sections
|
|
||||||
r = roccoize( "filename.rb", "# Comment 1\ndef codeblock\nend\n" )
|
|
||||||
assert_equal 1, r.sections.length
|
|
||||||
assert_equal 2, r.sections[ 0 ].length
|
|
||||||
assert_equal "<p>Comment 1</p>\n", r.sections[ 0 ][ 0 ]
|
|
||||||
assert_equal "<span class=\"k\">def</span> <span class=\"nf\">codeblock</span>\n<span class=\"k\">end</span>", r.sections[ 0 ][ 1 ]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_parsing
|
|
||||||
r = Rocco.new( 'test' ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "# Comment 1\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "# Comment 1\ndef codeblock\n# Comment 2\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_splitting
|
|
||||||
r = Rocco.new( 'test' ) { "" } # Generate throwaway instance so I can test `split`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ "Comment 1" ],
|
|
||||||
[ "def codeblock\nend" ]
|
|
||||||
],
|
|
||||||
r.split([ [ [ "Comment 1" ], [ "def codeblock", "end" ] ] ])
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ "Comment 1", "Comment 2" ],
|
|
||||||
[ "def codeblock", "end" ]
|
|
||||||
],
|
|
||||||
r.split( [
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "end" ] ]
|
|
||||||
] )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,64 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoBlockCommentTest < Test::Unit::TestCase
|
|
||||||
def test_one_liner
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/** Comment 1 */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_block_start_with_comment
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/** Comment 1a\n * Comment 1b\n */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_block_end_with_comment
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/**\n * Comment 1a\n Comment 1b */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_block_end_with_comment_and_middle
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/**\n * Comment 1a\n * Comment 1b */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_block_start_with_comment_and_end_with_comment
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/** Comment 1a\n Comment 1b */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_block_start_with_comment_and_end_with_comment_and_middle
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/** Comment 1a\n * Comment 1b */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,101 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoBlockCommentTest < Test::Unit::TestCase
|
|
||||||
def test_basics
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/**\n * Comment 1\n */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/**\n * Comment 1a\n * Comment 1b\n */\ndef codeblock\nend\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_multiple_blocks
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2" ], [] ]
|
|
||||||
],
|
|
||||||
r.parse( "/**\n * Comment 1\n */\ndef codeblock\nend\n/**\n * Comment 2\n */\n" )
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "if false", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/**\n * Comment 1\n */\ndef codeblock\nend\n/**\n * Comment 2\n */\nif false\nend" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_block_without_middle_character
|
|
||||||
r = Rocco.new( 'test', '', { :language => "python" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2" ], [] ]
|
|
||||||
],
|
|
||||||
r.parse( "\"\"\"\n Comment 1\n\"\"\"\ndef codeblock\nend\n\"\"\"\n Comment 2\n\"\"\"\n" )
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "if false", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "\"\"\"\n Comment 1\n\"\"\"\ndef codeblock\nend\n\"\"\"\n Comment 2\n\"\"\"\nif false\nend" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_language_without_single_line_comments_parse
|
|
||||||
r = Rocco.new( 'test', '', { :language => "css" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "if false", "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "/**\n * Comment 1\n */\ndef codeblock\nend\n/**\n * Comment 2\n */\nif false\nend" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_language_without_single_line_comments_split
|
|
||||||
r = Rocco.new( 'test', '', { :language => "css" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ "Comment 1", "Comment 2" ],
|
|
||||||
[ "def codeblock\nend", "if false\nend" ]
|
|
||||||
],
|
|
||||||
r.split( [
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "if false", "end" ] ]
|
|
||||||
] )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_language_without_single_line_comments_highlight
|
|
||||||
r = Rocco.new( 'test', '', { :language => "css" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
|
|
||||||
highlighted = r.highlight( r.split( r.parse( "/**\n * This is a comment!\n */\n.rule { goes: here; }\n/**\n * Comment 2\n */\n.rule2 { goes: here; }" ) ) )
|
|
||||||
assert_equal(
|
|
||||||
"<p>This is a comment!</p>",
|
|
||||||
highlighted[0][0]
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
"<p>Comment 2</p>\n",
|
|
||||||
highlighted[1][0]
|
|
||||||
)
|
|
||||||
assert(
|
|
||||||
!highlighted[0][1].include?("DIVIDER") &&
|
|
||||||
!highlighted[1][1].include?("DIVIDER"),
|
|
||||||
"`DIVIDER` stripped successfully."
|
|
||||||
)
|
|
||||||
assert( true )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
@ -1,25 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoCommentNormalization < Test::Unit::TestCase
|
|
||||||
def test_normal_comments
|
|
||||||
r = Rocco.new( 'test', '', { :language => "python" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2a", " Comment 2b" ], [] ]
|
|
||||||
],
|
|
||||||
r.parse( "\"\"\"\n Comment 1a\n Comment 1b\n\"\"\"\ndef codeblock\nend\n\"\"\"\n Comment 2a\n Comment 2b\n\"\"\"\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_single_line_comments
|
|
||||||
r = Rocco.new( 'test', '', { :language => "python" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1a", "Comment 1b" ], [ "def codeblock", "end" ] ],
|
|
||||||
[ [ "Comment 2a", " Comment 2b" ], [] ]
|
|
||||||
],
|
|
||||||
r.parse( "# Comment 1a\n# Comment 1b\ndef codeblock\nend\n# Comment 2a\n# Comment 2b\n" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,28 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoAutomaticCommentChars < Test::Unit::TestCase
|
|
||||||
def test_basic_detection
|
|
||||||
r = Rocco.new( 'filename.js' ) { "" }
|
|
||||||
assert_equal "//", r.options[:comment_chars][:single]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fallback_language
|
|
||||||
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever', '', { :language => "js" } ) { "" }
|
|
||||||
assert_equal "//", r.options[:comment_chars][:single]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fallback_default
|
|
||||||
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever' ) { "" }
|
|
||||||
assert_equal "#", r.options[:comment_chars][:single], "`:comment_chars` should be `#` when falling back to defaults."
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fallback_user
|
|
||||||
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever', '', { :comment_chars => "user" } ) { "" }
|
|
||||||
assert_equal "user", r.options[:comment_chars][:single], "`:comment_chars` should be the user's default when falling back to user-provided settings."
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fallback_user_with_unknown_language
|
|
||||||
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever', '', { :language => "not-a-language", :comment_chars => "user" } ) { "" }
|
|
||||||
assert_equal "user", r.options[:comment_chars][:single], "`:comment_chars` should be the user's default when falling back to user-provided settings."
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,30 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoDescriptiveSectionNamesTests < Test::Unit::TestCase
|
|
||||||
def test_section_name
|
|
||||||
r = roccoize( "filename.rb", "# # Comment 1\ndef codeblock\nend\n" )
|
|
||||||
html = r.to_html
|
|
||||||
assert(
|
|
||||||
html.include?( "<tr id='section-Comment_1'>" ),
|
|
||||||
"The first section should be named"
|
|
||||||
)
|
|
||||||
assert(
|
|
||||||
html.include?( '<a class="pilcrow" href="#section-Comment_1">' ),
|
|
||||||
"The rendered HTML should link to a named section"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_section_numbering
|
|
||||||
r = roccoize( "filename.rb", "# # Header 1\ndef codeblock\nend\n# Comment 1\ndef codeblock1\nend\n# # Header 2\ndef codeblock2\nend" )
|
|
||||||
html = r.to_html
|
|
||||||
assert(
|
|
||||||
html.include?( '<a class="pilcrow" href="#section-Header_1">' ) &&
|
|
||||||
html.include?( '<a class="pilcrow" href="#section-Header_2">' ),
|
|
||||||
"First and second headers should be named sections"
|
|
||||||
)
|
|
||||||
assert(
|
|
||||||
html.include?( '<a class="pilcrow" href="#section-2">' ),
|
|
||||||
"Sections should continue numbering as though headers were counted."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,22 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoDocblockAnnotationsTest < Test::Unit::TestCase
|
|
||||||
def test_basics
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c", :docblocks => true } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
"Comment\n\n> **param** mixed foo \n> **return** void "
|
|
||||||
],
|
|
||||||
r.docblock( ["Comment\n\n@param mixed foo\n@return void"] )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
def test_highlighted_in_blocks
|
|
||||||
r = Rocco.new( 'test', '', { :language => "c", :docblocks => true } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
highlighted = r.highlight( r.split( r.parse( "/**\n * Comment\n * @param type name\n */\ndef codeblock\nend\n" ) ) )
|
|
||||||
|
|
||||||
assert_equal(
|
|
||||||
"<p>Comment</p>\n\n<blockquote><p><strong>param</strong> type name</p></blockquote>\n",
|
|
||||||
highlighted[0][0]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,13 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoHeredocTest < Test::Unit::TestCase
|
|
||||||
def test_basics
|
|
||||||
r = Rocco.new( 'test', '', { :language => "rb" } ) { "" } # Generate throwaway instance so I can test `parse`
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "heredoc <<-EOH", "#comment", "code", "EOH" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "# Comment 1\nheredoc <<-EOH\n#comment\ncode\nEOH" )
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,27 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoLanguageDetection < Test::Unit::TestCase
|
|
||||||
def test_basic_detection
|
|
||||||
r = Rocco.new( 'filename.py' ) { "" }
|
|
||||||
if r.pygmentize?
|
|
||||||
assert_equal "python", r.detect_language(), "`detect_language()` should return the correct language"
|
|
||||||
assert_equal "python", r.options[:language], "`@options[:language]` should be set to the correct language"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fallback_default
|
|
||||||
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever' ) { "" }
|
|
||||||
if r.pygmentize?
|
|
||||||
assert_equal "text", r.detect_language(), "`detect_language()` should return `text` when nothing else is detected"
|
|
||||||
assert_equal "ruby", r.options[:language], "`@options[:language]` should be set to `ruby` when nothing else is detected"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fallback_user
|
|
||||||
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever', '', { :language => "c" } ) { "" }
|
|
||||||
if r.pygmentize?
|
|
||||||
assert_equal "text", r.detect_language(), "`detect_language()` should return `text` nothing else is detected"
|
|
||||||
assert_equal "c", r.options[:language], "`@options[:language]` should be set to the user's setting when nothing else is detected"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,86 +0,0 @@
|
|||||||
# encoding: utf-8
|
|
||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoIssueTests < Test::Unit::TestCase
|
|
||||||
def test_issue07_incorrect_parsing_in_c_mode
|
|
||||||
# Precursor to issue #13 below, Rocco incorrectly parsed C/C++
|
|
||||||
# http://github.com/rtomayko/rocco/issues#issue/7
|
|
||||||
r = Rocco.new( 'issue7.c', [], { :language => 'c' } ) {
|
|
||||||
"// *stdio.h* declares *puts*\n#include <stdio.h>\n\n//### code hello world\n\n// every C program contains function *main*.\nint main (int argc, char *argv[]) {\n puts('hello world');\n return 0;\n}\n\n// that's it!"
|
|
||||||
}
|
|
||||||
r.sections.each do | section |
|
|
||||||
if not section[1].nil?
|
|
||||||
assert(
|
|
||||||
!section[1].include?("<span class=\"c\"># DIVIDER</span>"),
|
|
||||||
"`# DIVIDER` present in code text, which means the highligher screwed up somewhere."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_issue10_utf8_processing
|
|
||||||
# Rocco has issues with strange UTF-8 characters: need to explicitly set the encoding for Pygments
|
|
||||||
# http://github.com/rtomayko/rocco/issues#issue/10
|
|
||||||
r = Rocco.new( File.dirname(__FILE__) + "/fixtures/issue10.utf-8.rb" )
|
|
||||||
assert_equal(
|
|
||||||
"<p>hello ąćęłńóśźż</p>\n",
|
|
||||||
r.sections[0][0],
|
|
||||||
"UTF-8 input files ought behave correctly."
|
|
||||||
)
|
|
||||||
# and, just for grins, ensure that iso-8859-1 works too.
|
|
||||||
# @TODO: Is this really the correct behavior? Converting text
|
|
||||||
# to UTF-8 on the way out is probably preferable.
|
|
||||||
r = Rocco.new( File.dirname(__FILE__) + "/fixtures/issue10.iso-8859-1.rb" )
|
|
||||||
assert_equal(
|
|
||||||
"<p>hello w\366rld</p>\n",
|
|
||||||
r.sections[0][0],
|
|
||||||
"ISO-8859-1 input should probably also behave correctly."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_issue12_css_octothorpe_classname_change
|
|
||||||
# Docco changed some CSS classes. Rocco needs to update its default template.
|
|
||||||
# http://github.com/rtomayko/rocco/issues#issue/12
|
|
||||||
r = Rocco.new( 'issue12.sh' ) {
|
|
||||||
"# Comment 1\n# Comment 1\nprint 'omg!'"
|
|
||||||
}
|
|
||||||
html = r.to_html
|
|
||||||
assert(
|
|
||||||
!html.include?( "<div class=\"octowrap\">" ),
|
|
||||||
"`octowrap` wrapper is present in rendered HTML. This ought be replaced with `pilwrap`."
|
|
||||||
)
|
|
||||||
assert(
|
|
||||||
!html.include?( "<a class=\"octothorpe\" href=\"#section-1\">" ),
|
|
||||||
"`octothorpe` link is present in rendered HTML. This ought be replaced with `pilcrow`."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_issue13_incorrect_code_divider_parsing
|
|
||||||
# In `bash` mode (among others), the comment class is `c`, not `c1`.
|
|
||||||
# http://github.com/rtomayko/rocco/issues#issue/13
|
|
||||||
r = Rocco.new( 'issue13.sh', [], { :language => 'bash' } ) {
|
|
||||||
"# Comment 1\necho 'code';\n# Comment 2\necho 'code';\n# Comment 3\necho 'code';\n"
|
|
||||||
}
|
|
||||||
r.sections.each do | section |
|
|
||||||
if not section[1].nil?
|
|
||||||
assert(
|
|
||||||
!section[1].include?("<span class=\"c\"># DIVIDER</span>"),
|
|
||||||
"`# DIVIDER` present in code text, which means the highligher screwed up somewhere."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_issue15_extra_space_after_comment_character_remains
|
|
||||||
# After the comment character, a single space should be removed.
|
|
||||||
# http://github.com/rtomayko/rocco/issues#issue/15
|
|
||||||
r = Rocco.new( 'issue15.sh') {
|
|
||||||
"# Comment 1\n# ---------\necho 'code';"
|
|
||||||
}
|
|
||||||
assert(
|
|
||||||
!r.sections[0][0].include?( "<hr />" ),
|
|
||||||
"`<hr />` present in rendered documentation text. It should be a header, not text followed by a horizontal rule."
|
|
||||||
)
|
|
||||||
assert_equal("<h2>Comment 1</h2>\n", r.sections[0][0])
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,64 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoSkippableLines < Test::Unit::TestCase
|
|
||||||
def test_shebang_first_line
|
|
||||||
r = Rocco.new( 'filename.sh' ) { "" }
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "#!/usr/bin/env bash\n# Comment 1\ndef codeblock\n# Comment 2\nend\n" ),
|
|
||||||
"Shebang should be stripped when it appears as the first line."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_shebang_in_content
|
|
||||||
r = Rocco.new( 'filename.sh' ) { "" }
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
# @TODO: `#!/` shouldn't be recognized as a comment.
|
|
||||||
[ [ "Comment 1", "!/usr/bin/env bash" ], [ "def codeblock" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "# Comment 1\n#!/usr/bin/env bash\ndef codeblock\n# Comment 2\nend\n" ),
|
|
||||||
"Shebang shouldn't be stripped anywhere other than as the first line."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_encoding_in_ruby
|
|
||||||
r = Rocco.new( 'filename.rb' ) { "" }
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "#!/usr/bin/env bash\n# encoding: utf-8\n# Comment 1\ndef codeblock\n# Comment 2\nend\n" ),
|
|
||||||
"Strings matching the PEP 263 encoding definition regex should be stripped when they appear at the top of a python document."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_encoding_in_python
|
|
||||||
r = Rocco.new( 'filename.py' ) { "" }
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "Comment 1" ], [ "def codeblock" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "#!/usr/bin/env bash\n# encoding: utf-8\n# Comment 1\ndef codeblock\n# Comment 2\nend\n" ),
|
|
||||||
"Strings matching the PEP 263 encoding definition regex should be stripped when they appear at the top of a python document."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_encoding_in_notpython
|
|
||||||
r = Rocco.new( 'filename.sh' ) { "" }
|
|
||||||
assert_equal(
|
|
||||||
[
|
|
||||||
[ [ "encoding: utf-8", "Comment 1" ], [ "def codeblock" ] ],
|
|
||||||
[ [ "Comment 2" ], [ "end" ] ]
|
|
||||||
],
|
|
||||||
r.parse( "#!/usr/bin/env bash\n# encoding: utf-8\n# Comment 1\ndef codeblock\n# Comment 2\nend\n" ),
|
|
||||||
"Strings matching the PEP 263 encoding definition regex should be stripped when they appear at the top of a python document."
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,29 +0,0 @@
|
|||||||
require File.expand_path('../helper', __FILE__)
|
|
||||||
|
|
||||||
class RoccoSourceListTests < Test::Unit::TestCase
|
|
||||||
def test_flat_sourcelist
|
|
||||||
r = Rocco.new( 'issue26.sh', [ 'issue26a.sh', 'issue26b.sh', 'issue26c.sh' ] ) {
|
|
||||||
"# Comment 1\n# Comment 1\nprint 'omg!'"
|
|
||||||
}
|
|
||||||
html = r.to_html
|
|
||||||
assert(
|
|
||||||
html.include?( '<a class="source" href="issue26a.html">issue26a.sh</a>' ) &&
|
|
||||||
html.include?( '<a class="source" href="issue26b.html">issue26b.sh</a>' ) &&
|
|
||||||
html.include?( '<a class="source" href="issue26c.html">issue26c.sh</a>' ),
|
|
||||||
"URLs correctly generated for files in a flat directory structure"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_heiarachical_sourcelist
|
|
||||||
r = Rocco.new( 'a/issue26.sh', [ 'a/issue26a.sh', 'b/issue26b.sh', 'c/issue26c.sh' ] ) {
|
|
||||||
"# Comment 1\n# Comment 1\nprint 'omg!'"
|
|
||||||
}
|
|
||||||
html = r.to_html
|
|
||||||
assert(
|
|
||||||
html.include?( '<a class="source" href="issue26a.html">issue26a.sh</a>' ) &&
|
|
||||||
html.include?( '<a class="source" href="../b/issue26b.html">issue26b.sh</a>' ) &&
|
|
||||||
html.include?( '<a class="source" href="../c/issue26c.html">issue26c.sh</a>' ),
|
|
||||||
"URLs correctly generated for files in a flat directory structure"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue
Block a user