all repos — markup @ 5d5bc19e633ac22886f5afe86f59871a3ab882ba

The code we use to render README.your_favorite_markup

Include AsciiDoc header with document and remove asciidoc command clutter.
Tom Preston-Werner tom@mojombo.com
Fri, 23 Jul 2010 15:44:28 -0700
commit

5d5bc19e633ac22886f5afe86f59871a3ab882ba

parent

f65c5fdad47612d4218b6e37dbfe579179cd048a

6 files changed, 39 insertions(+), 378 deletions(-)

jump to
M .gitignore.gitignore

@@ -1,1 +1,2 @@

*.pyc +pkg
D lib/github/commands/asciidoc2html

@@ -1,119 +0,0 @@

-#!/usr/bin/env python - -"""A small wrapper file for parsing AsciiDoc files at Github.""" - -__author__ = "Devin Weaver" -__copyright__ = "Copyright (C) 2009 Devin Weaver" -__license__ = "Public Domain" -__version__ = "0.1" - -""" -github_asciidoc.py ------------------- - -This is a wrapper file for parsing AsciiDoc files at github. It wraps the -current AsciiDoc API. - -AsciiDoc specifications suggest using the file extension of `.txt` however this -causes conflict because there is no way to determine if a text file is an -AsciiDoc or not without pre-processing the file. This gives us two simple -options: - -1. **Parse all text files**. We could have all files ending in `.txt` or - ``README.txt`` be parsed through AsciiDoc. It will print pretty text fine - even if it isn't formatted as such. However this could be *not what the user - expects*. -2. **Pick a unique extension**. We could pick a unique extension (i.e. - `.asciidoc`) to prevent clashing. Although not directly suggested by the - author of AsciiDoc there is no standard or practice to the contrary. - -Option two is recommended by myself. - -Requirements -~~~~~~~~~~~~ - -The AsciiDoc API comes in two parts. The first is the system installation of -AsciiDoc which has a simple install_. The second part is the API script. You -can either copy this to the current directory or the application's lib folder. -There is more information on the `API page`_ - -The `re` package is imported here for the purpose to accomplish E-Mail address -cloaking. AsciiDoc does not offer it's own cloaking algorithm like docutils -does. So I made a simple one here to do the same. **If the expense of regex's -is too high it can be easily commented out.** - -.. tip:: - AsciiDoc by default runs in *safe mode* which means it will not include - external files that are **not** in the same directory as the `infile`. - However since we use a StringIO through the API it should be based on the - current working directory. - -.. _install: http://www.methods.co.nz/asciidoc/userguide.html -.. _API page: http://www.methods.co.nz/asciidoc/asciidocapi.html -""" - -try: - import locale - locale.setlocale(locale.LC_ALL, '') -except: - pass - -import sys -import cStringIO # faster then StringIO -from asciidocapi import AsciiDocAPI -from asciidocapi import AsciiDocError -import re # only needed to simulate cloak_email_addresses - -def main(): - """ - Parses the given AsciiDoc file or the redirected string input and returns - the HTML body. - - Usage: asciidoc2html < README.rst - asciidoc2html README.rst - """ - try: - text = open(sys.argv[1], 'r').read() - except IOError: # given filename could not be found - return '' - except IndexError: # no filename given - text = sys.stdin.read() - - infile = cStringIO.StringIO(text) - outfile = cStringIO.StringIO() - asciidoc = AsciiDocAPI() - asciidoc.options('-s') - - try: - asciidoc.execute(infile, outfile, 'xhtml11') - except AsciiDocError, strerror: - str = "%s" % (strerror) - str = str.replace("&", "&amp;") # Must be done first - str = str.replace("<", "%lt;") - str = str.replace(">", "%gt;") - outfile.write ("<blockquote><strong>AsciiDoc ERROR: %s</strong></blockquote>" % (str)) - - """ - Cloak email addresses - - AsciiDoc API does not have a `cloak_email_addresses` option. We can do the - same with a set of regex but that can be expensive. Keep section commented - to disable. So ``abc@mail.example.com`` becomes: - - ----------- - <a class="reference" href="mailto:abc&#37;&#52;&#48;mail&#46;example&#46;org"> - abc<span>&#64;</span>mail<span>&#46;</span>example<span>&#46;</span>org</a> - ----------- - """ - def mangleEmail(matches): - email1 = "%s&#37;&#52;&#48;%s" % (matches.group(1), matches.group(2)) - email1 = email1.replace(".", "&#46;") - email2 = "%s<span>&#64;</span>%s" % (matches.group(1), matches.group(2)) - email2 = email2.replace(".", "<span>&#46;</span>") - return "<a class=\"reference\" href=\"mailto:%s\">%s</a>" % (email1, email2) - - return re.sub(r'<a href="mailto:([^@]+)@([^@]+)">([^@]+)@([^@]+)</a>', mangleEmail, outfile.getvalue()) - #return outfile.getvalue() - -if __name__ == '__main__': - print main()
D lib/github/commands/asciidocapi.py

@@ -1,240 +0,0 @@

-#!/usr/bin/env python -""" -asciidocapi - AsciiDoc API wrapper class. - -The AsciiDocAPI class provides an API for executing asciidoc. Minimal example -compiles `mydoc.txt` to `mydoc.html`: - - import asciidocapi - asciidoc = asciidocapi.AsciiDocAPI() - asciidoc.execute('mydoc.txt') - -- Full documentation in asciidocapi.txt. -- See the doctests below for more examples. - -Doctests: - -1. Check execution: - - >>> import StringIO - >>> infile = StringIO.StringIO('Hello *{author}*') - >>> outfile = StringIO.StringIO() - >>> asciidoc = AsciiDocAPI() - >>> asciidoc.options('--no-header-footer') - >>> asciidoc.attributes['author'] = 'Joe Bloggs' - >>> asciidoc.execute(infile, outfile, backend='html4') - >>> print outfile.getvalue() - <p>Hello <strong>Joe Bloggs</strong></p> - - >>> asciidoc.attributes['author'] = 'Bill Smith' - >>> infile = StringIO.StringIO('Hello _{author}_') - >>> outfile = StringIO.StringIO() - >>> asciidoc.execute(infile, outfile, backend='docbook') - >>> print outfile.getvalue() - <simpara>Hello <emphasis>Bill Smith</emphasis></simpara> - -2. Check error handling: - - >>> import StringIO - >>> asciidoc = AsciiDocAPI() - >>> infile = StringIO.StringIO('---------') - >>> outfile = StringIO.StringIO() - >>> asciidoc.execute(infile, outfile) - Traceback (most recent call last): - File "<stdin>", line 1, in <module> - File "asciidocapi.py", line 189, in execute - raise AsciiDocError(self.messages[-1]) - AsciiDocError: ERROR: <stdin>: line 1: [blockdef-listing] missing closing delimiter - - -Copyright (C) 2009 Stuart Rackham. Free use of this software is granted -under the terms of the GNU General Public License (GPL). - -""" - -import sys,os,re - -API_VERSION = '0.1.1' -MIN_ASCIIDOC_VERSION = '8.4.1' # Minimum acceptable AsciiDoc version. - - -def find_in_path(fname, path=None): - """ - Find file fname in paths. Return None if not found. - """ - if path is None: - path = os.environ.get('PATH', '') - for dir in path.split(os.pathsep): - fpath = os.path.join(dir, fname) - if os.path.isfile(fpath): - return fpath - else: - return None - - -class AsciiDocError(Exception): - pass - - -class Options(object): - """ - Stores asciidoc(1) command options. - """ - def __init__(self, values=[]): - self.values = values[:] - def __call__(self, name, value=None): - """Shortcut for append method.""" - self.append(name, value) - def append(self, name, value=None): - if type(value) in (int,float): - value = str(value) - self.values.append((name,value)) - - -class Version(object): - """ - Parse and compare AsciiDoc version numbers. Instance attributes: - - string: String version number '<major>.<minor>[.<micro>][suffix]'. - major: Integer major version number. - minor: Integer minor version number. - micro: Integer micro version number. - suffix: Suffix (begins with non-numeric character) is ignored when - comparing. - - Doctest examples: - - >>> Version('8.2.5') < Version('8.3 beta 1') - True - >>> Version('8.3.0') == Version('8.3. beta 1') - True - >>> Version('8.2.0') < Version('8.20') - True - >>> Version('8.20').major - 8 - >>> Version('8.20').minor - 20 - >>> Version('8.20').micro - 0 - >>> Version('8.20').suffix - '' - >>> Version('8.20 beta 1').suffix - 'beta 1' - - """ - def __init__(self, version): - self.string = version - reo = re.match(r'^(\d+)\.(\d+)(\.(\d+))?\s*(.*?)\s*$', self.string) - if not reo: - raise ValueError('invalid version number: %s' % self.string) - groups = reo.groups() - self.major = int(groups[0]) - self.minor = int(groups[1]) - self.micro = int(groups[3] or '0') - self.suffix = groups[4] or '' - def __cmp__(self, other): - result = cmp(self.major, other.major) - if result == 0: - result = cmp(self.minor, other.minor) - if result == 0: - result = cmp(self.micro, other.micro) - return result - - -class AsciiDocAPI(object): - """ - AsciiDoc API class. - """ - def __init__(self, asciidoc_py=None): - """ - Locate and import asciidoc.py. - Initialize instance attributes. - """ - self.options = Options() - self.attributes = {} - self.messages = [] - # Search for the asciidoc command file. - # Try ASCIIDOC_PY environment variable first. - cmd = os.environ.get('ASCIIDOC_PY') - if cmd: - if not os.path.isfile(cmd): - raise AsciiDocError('missing ASCIIDOC_PY file: %s' % cmd) - elif asciidoc_py: - # Next try path specified by caller. - cmd = asciidoc_py - if not os.path.isfile(cmd): - raise AsciiDocError('missing file: %s' % cmd) - else: - # Try shell search paths. - for fname in ['asciidoc.py','asciidoc.pyc','asciidoc']: - cmd = find_in_path(fname) - if cmd: break - else: - # Finally try current working directory. - for cmd in ['asciidoc.py','asciidoc.pyc','asciidoc']: - if os.path.isfile(cmd): break - else: - raise AsciiDocError('failed to locate asciidoc.py[c]') - cmd = os.path.realpath(cmd) - if os.path.splitext(cmd)[1] not in ['.py','.pyc']: - raise AsciiDocError('invalid Python module name: %s' % cmd) - sys.path.insert(0, os.path.dirname(cmd)) - try: - try: - import asciidoc - except ImportError: - raise AsciiDocError('failed to import asciidoc') - finally: - del sys.path[0] - if Version(asciidoc.VERSION) < Version(MIN_ASCIIDOC_VERSION): - raise AsciiDocError( - 'asciidocapi %s requires asciidoc %s or better' - % (API_VERSION, MIN_ASCIIDOC_VERSION)) - self.asciidoc = asciidoc - self.cmd = cmd - - def execute(self, infile, outfile=None, backend=None): - """ - Compile infile to outfile using backend format. - infile can outfile can be file path strings or file like objects. - """ - self.messages = [] - opts = Options(self.options.values) - if outfile is not None: - opts('--out-file', outfile) - if backend is not None: - opts('--backend', backend) - for k,v in self.attributes.items(): - if v == '' or k[-1] in '!@': - s = k - elif v is None: # A None value undefines the attribute. - s = k + '!' - else: - s = '%s=%s' % (k,v) - opts('--attribute', s) - args = [infile] - sys.path.insert(0, os.path.dirname(self.cmd)) - try: - # The AsciiDoc command was designed to process source text then - # exit, there are globals and statics in asciidoc.py that have - # to be reinitialized before each run -- hence the reload. - reload(self.asciidoc) - finally: - del sys.path[0] - try: - try: - self.asciidoc.execute(self.cmd, opts.values, args) - finally: - self.messages = self.asciidoc.messages[:] - except SystemExit, e: - if e.code: - raise AsciiDocError(self.messages[-1]) - - -if __name__ == "__main__": - """ - Run module doctests. - """ - import doctest - options = doctest.NORMALIZE_WHITESPACE + doctest.ELLIPSIS - doctest.testmod(optionflags=options)
M lib/github/markups.rblib/github/markups.rb

@@ -1,34 +1,41 @@

-markup(:markdown, /md|mkdn?|mdown|markdown/) do |content| - Markdown.new(content).to_html +# asciidoc +command('asciidoc --safe --backend=xhtml11 -o - -', /asciidoc/) do |rendered| + if rendered =~ /<body>\n(.*)<div id="footnotes">/m + $1 + end end -markup(:redcloth, /textile/) do |content| - RedCloth.new(content).to_html +# creole +markup(:creole, /creole/) do |content| + Creole.creolize(content) end -markup('github/markup/rdoc', /rdoc/) do |content| - GitHub::Markup::RDoc.new(content).to_html +# markdown +markup(:markdown, /md|mkdn?|mdown|markdown/) do |content| + Markdown.new(content).to_html end +# org markup('org-ruby', /org/) do |content| Orgmode::Parser.new(content).to_html end -markup(:creole, /creole/) do |content| - Creole.creolize(content) +# pod +command("/usr/bin/env perl -MPod::Simple::HTML -e Pod::Simple::HTML::go", /pod/) do |rendered| + if rendered =~ /<!-- start doc -->\s*(.+)\s*<!-- end doc -->/mi + $1 + end +end + +# rdoc +markup('github/markup/rdoc', /rdoc/) do |content| + GitHub::Markup::RDoc.new(content).to_html end +# rest command(:rest2html, /re?st(\.txt)?/) -command('asciidoc -s --backend=xhtml11 -o - -', /asciidoc/) - -# pod2html is nice enough to generate a full-on HTML document for us, -# so we return the favor by ripping out the good parts. -# -# Any block passed to `command` will be handed the command's STDOUT for -# post processing. -command("/usr/bin/env perl -MPod::Simple::HTML -e Pod::Simple::HTML::go", /pod/) do |rendered| - if rendered =~ /<!-- start doc -->\s*(.+)\s*<!-- end doc -->/mi - $1 - end +# textile +markup(:redcloth, /textile/) do |content| + RedCloth.new(content).to_html end
M test/markups/README.asciidoctest/markups/README.asciidoc

@@ -1,2 +1,5 @@

+AsciiDoc Test Doc +================= + * One * Two
M test/markups/README.asciidoc.htmltest/markups/README.asciidoc.html

@@ -1,3 +1,9 @@

+<div id="header"> +<h1>AsciiDoc Test Doc</h1> +</div> +<div id="content"> +<div id="preamble"> +<div class="sectionbody"> <div class="ulist"><ul> <li> <p>

@@ -10,3 +16,6 @@ Two

</p> </li> </ul></div> +</div> +</div> +</div>