aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Fitzgerald <fitzgen@gmail.com>2010-06-29 00:03:43 -0700
committerNick Fitzgerald <fitzgen@gmail.com>2010-06-29 00:03:43 -0700
commit90faaccdb98a7cfdcebc5d0a8101b664604acef0 (patch)
tree1ebcbff2ad7f41e279eca2631c2591cc430dd2f8
downloadpycco-90faaccdb98a7cfdcebc5d0a8101b664604acef0.tar.gz
pycco-90faaccdb98a7cfdcebc5d0a8101b664604acef0.zip
Initial commit. TODO: update README, make setup.py script, make gh-pages
-rw-r--r--README13
-rwxr-xr-xbin/pocco9
-rw-r--r--pocco.py192
-rw-r--r--resources/pocco.css185
-rw-r--r--resources/pocco.html46
5 files changed, 445 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..777bb9d
--- /dev/null
+++ b/README
@@ -0,0 +1,13 @@
+ ____
+/\ _`\
+\ \ \/\ \ ___ ___ ___ ___
+ \ \ \ \ \ / __`\ /'___\ /'___\ / __`\
+ \ \ \_\ \ /\ \ \ \ /\ \__/ /\ \__/ /\ \ \ \
+ \ \____/ \ \____/ \ \____\ \ \____\ \ \____/
+ \/___/ \/___/ \/____/ \/____/ \/___/
+
+
+Docco is a quick-and-dirty, hundred-line-long, literate-programming-style
+documentation generator. For more information, see:
+
+http://jashkenas.github.com/docco/ \ No newline at end of file
diff --git a/bin/pocco b/bin/pocco
new file mode 100755
index 0000000..61531a4
--- /dev/null
+++ b/bin/pocco
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+import os
+import sys
+
+lib = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
+cmd = 'python ' + os.path.join(lib, 'pocco.py') + " ".join(sys.argv[2:])
+
+print os.popen(cmd).read(),
diff --git a/pocco.py b/pocco.py
new file mode 100644
index 0000000..0f12260
--- /dev/null
+++ b/pocco.py
@@ -0,0 +1,192 @@
+# **Pocco** is a Python port of [Docco](http://jashkenas.github.com/docco/ ):
+# the original quick-and-dirty, hundred-line-long, literate-programming-style
+# documentation generator. It produces HTML that displays your comments
+# alongside your code. Comments are passed through
+# [Markdown](http://daringfireball.net/projects/markdown/syntax), and code is
+# passed through [Pygments](http://pygments.org/) syntax highlighting. This
+# page is the result of running Pocco against its own source file.
+#
+# If you install Pocco, you can run it from the command-line:
+#
+# pocco src/*.py
+#
+# ...will generate linked HTML documentation for the named source files, saving
+# it into a `docs` folder.
+#
+# To install Pocco, simply
+#
+# sudo setup.py install
+#
+
+#### Main Documentation Generation Functions
+
+# Generate the documentation for a source file by reading it in, splitting it
+# up into comment/code sections, highlighting them for the appropriate language,
+# and merging them into an HTML template.
+def generate_documentation(source):
+ fh = open(source, "r")
+ sections = parse(source, fh.read())
+ highlight(source,
+ sections)
+ generate_html(source, sections)
+
+# Given a string of source code, parse out each comment and the code that
+# follows it, and create an individual **section** for it.
+# Sections take the form:
+#
+# { "docs_text": ...,
+# "docs_html": ...,
+# "code_text": ...,
+# "code_html": ...,
+# "num": ...
+# }
+#
+def parse(source, code):
+ lines = code.split("\n")
+ sections = []
+ language = get_language(source)
+ has_code = docs_text = code_text = ""
+
+ def save(docs, code):
+ sections.append({
+ "docs_text": docs,
+ "code_text": code
+ })
+
+ for line in lines:
+ if re.match(language["comment_matcher"], line):
+ if has_code:
+ save(docs_text, code_text)
+ has_code = docs_text = code_text = ''
+ docs_text += re.sub(language["comment_matcher"], "", line) + "\n"
+ else:
+ has_code = True
+ code_text += line + '\n'
+ save(docs_text, code_text)
+ return sections
+
+# Highlights a single chunk of code, using **Pygments** over stdio, and runs the
+# text of its corresponding comment through **Markdown**.
+#
+# We process the entire file in a single call to Pygments by inserting little
+# marker comments between each section and then splitting the result string
+# wherever our markers occur.
+def highlight(source, sections):
+ language = get_language(source)
+ pygments = Popen(["pygmentize", "-l", language["name"], "-f", "html"],
+ stderr=PIPE,
+ stdin=PIPE,
+ stdout=PIPE)
+
+ pygments.stdin.write(language["divider_text"].join(section["code_text"] for section in sections))
+ output = ""
+ while pygments.returncode is None:
+ stdout, stderr = pygments.communicate()
+ if stderr:
+ print stderr
+ if stdout:
+ output += stdout
+
+ output = output.replace(highlight_start, "").replace(highlight_end, "")
+ fragments = re.split(language["divider_html"], output)
+ for i, section in enumerate(sections):
+ section["code_html"] = highlight_start + fragments[i] + highlight_end
+ section["docs_html"] = markdown(section["docs_text"])
+ section["num"] = i
+
+# Once all of the code is finished highlighting, we can generate the HTML file
+# and write out the documentation. Pass the completed sections into the template
+# found in `resources/pocco.jst`
+def generate_html(source, sections):
+ title = path.basename(source)
+ dest = destination(source)
+ html = pocco_template({
+ "title": title,
+ "sections": sections,
+ "sources": sources,
+ "path": path,
+ "destination": destination
+ })
+ print "pocco = %s -> %s" % (source, dest)
+ fh = open(dest, "w")
+ fh.write(html)
+ fh.close()
+
+#### Helpers & Setup
+
+# Import our external dependencies.
+import pystache
+import re
+import sys
+from markdown import markdown
+from os import path
+from subprocess import Popen, PIPE
+
+# A list of the languages that Pocco supports, mapping the file extension to
+# the name of the Pygments lexer and the symbol that indicates a comment. To
+# add another language to Pocco's repertoire, add it here.
+languages = {
+ ".coffee": { "name": "coffee-script", "symbol": "#" },
+ ".js": { "name": "javascript", "symbol": "//" },
+ ".rb": { "name": "ruby", "symbol": "#" },
+ ".py": { "name": "python", "symbol": "#" }
+}
+
+# Build out the appropriate matchers and delimiters for each language.
+for ext, l in languages.items():
+ # Does the line begin with a comment?
+ l["comment_matcher"] = re.compile(r"^\s*" + l["symbol"] + "\s?")
+
+ # The dividing token we feed into Pygments, to delimit the boundaries between
+ # sections.
+ l["divider_text"] = "\n" + l["symbol"] + "DIVIDER\n"
+
+ # The mirror of `divider_text` that we expect Pygments to return. We can split
+ # on this to recover the original sections.
+ l["divider_html"] = re.compile(r'\n*<span class="c">' + l["symbol"] + 'DIVIDER</span>\n*')
+
+# Get the current language we're documenting, based on the extension.
+def get_language(source):
+ return languages[ source[source.rindex("."):] ]
+
+# Compute the destination HTML path for an input source file path. If the source
+# is `lib/example.coffee`, the HTML will be at `docs/example.html`
+def destination(filepath):
+ name = filepath.replace(filepath[ filepath.rindex("."): ], "")
+ return "docs/" + path.basename(name) + ".html"
+
+# Ensure that the destination directory exists.
+def ensure_directory():
+ Popen(["mkdir", "-p", "docs"]).wait()
+
+def template(source):
+ return lambda context: pystache.render(source, context)
+
+# Create the template that we will use to generate the Pocco HTML page.
+pocco_template = template(open(path.dirname(__file__) + "/resources/pocco.html").read())
+
+# The CSS styles we"d like to apply to the documentation.
+pocco_styles = open(path.dirname(__file__) + "/resources/pocco.css").read()
+
+# The start of each Pygments highlight block.
+highlight_start = "<div class=\"highlight\"><pre>"
+
+# The end of each Pygments highlight block.
+highlight_end = "</pre></div>"
+
+# Run the script.
+# For each source file passed in as an argument, generate the documentation.
+if __name__ == "__main__":
+ sources = list(sys.argv)
+ sources.sort()
+ if sources:
+ ensure_directory()
+ css = open("docs/pocco.css", "w")
+ css.write(pocco_styles)
+ css.close()
+
+ def next_file():
+ generate_documentation(sources.pop(0))
+ if sources:
+ next_file()
+ next_file()
diff --git a/resources/pocco.css b/resources/pocco.css
new file mode 100644
index 0000000..921633b
--- /dev/null
+++ b/resources/pocco.css
@@ -0,0 +1,185 @@
+/*--------------------- Layout and Typography ----------------------------*/
+body {
+ font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
+ font-size: 16px;
+ line-height: 24px;
+ 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: 40px 0 15px 0;
+}
+ h3, h4, h5, h6 {
+ margin-top: 20px;
+ }
+#container {
+ position: relative;
+}
+#background {
+ position: fixed;
+ top: 0; left: 580px; 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;
+ right: 0; top: 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;
+ }
+ #jump_page .source {
+ display: block;
+ 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: 500px;
+ min-width: 500px;
+ min-height: 5px;
+ padding: 10px 25px 1px 50px;
+ 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;
+ }
+ .octowrap {
+ position: relative;
+ }
+ .octothorpe {
+ 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 .octothorpe {
+ opacity: 1;
+ }
+ td.code, th.code {
+ padding: 14px 15px 16px 50px;
+ width: 100%;
+ vertical-align: top;
+ background: #f5f5ff;
+ border-left: 1px solid #e5e5ee;
+ }
+ pre, tt, code {
+ font-size: 12px; line-height: 18px;
+ font-family: 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 */ \ No newline at end of file
diff --git a/resources/pocco.html b/resources/pocco.html
new file mode 100644
index 0000000..9855a52
--- /dev/null
+++ b/resources/pocco.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <title>{{ title }}</title>
+ <link rel="stylesheet" href="pocco.css">
+</head>
+<body>
+<div id='container'>
+ <div id="background"></div>
+ {{#sources?}}
+ <div id="jump_to">
+ Jump To &hellip;
+ <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-{{ num }}'>
+ <td class=docs>
+ <div class="octowrap">
+ <a class="octothorpe" href="#section-{{ num }}">#</a>
+ </div>
+ {{{ docs_html }}}
+ </td>
+ <td class=code>
+ <div class='highlight'><pre>{{{ code_html }}}</pre></div>
+ </td>
+ </tr>
+ {{/sections}}
+ </table>
+</div>
+</body>