Docco ===== **Docco** is a quick-and-dirty documentation generator, written in [Literate CoffeeScript](http://coffeescript.org/#literate). It produces an HTML document that displays your comments intermingled with your code. All prose is passed through [Markdown](http://daringfireball.net/projects/markdown/syntax), and code is passed through [Highlight.js](http://highlightjs.org/) syntax highlighting. This page is the result of running Docco against its own [source file](https://github.com/jashkenas/docco/blob/master/docco.litcoffee). 1. Install Docco with **npm**: `sudo npm install -g docco` 2. Run it against your code: `docco src/*.coffee` There is no "Step 3". This will generate an HTML page for each of the named source files, with a menu linking to the other pages, saving the whole mess into a `docs` folder (configurable). The [Docco source](http://github.com/jashkenas/docco) is available on GitHub, and is released under the [MIT license](http://opensource.org/licenses/MIT). Docco can be used to process code written in any programming language. If it doesn't handle your favorite yet, feel free to [add it to the list](https://github.com/jashkenas/docco/blob/master/resources/languages.json). Finally, the ["literate" style](http://coffeescript.org/#literate) of *any* language is also supported — just tack an `.md` extension on the end: `.coffee.md`, `.py.md`, and so on. Partners in Crime: ------------------ * If Node.js doesn't run on your platform, or you'd prefer a more convenient package, get [Ryan Tomayko](http://github.com/rtomayko)'s [Rocco](http://rtomayko.github.io/rocco/rocco.html), the **Ruby** port that's available as a gem. * If you're writing shell scripts, try [Shocco](http://rtomayko.github.io/shocco/), a port for the **POSIX shell**, also by Mr. Tomayko. * If **Python** is more your speed, take a look at [Nick Fitzgerald](http://github.com/fitzgen)'s [Pycco](http://fitzgen.github.io/pycco/). * For **Clojure** fans, [Fogus](http://blog.fogus.me/)'s [Marginalia](http://fogus.me/fun/marginalia/) is a bit of a departure from "quick-and-dirty", but it'll get the job done. * There's a **Go** port called [Gocco](http://nikhilm.github.io/gocco/), written by [Nikhil Marathe](https://github.com/nikhilm). * For all you **PHP** buffs out there, Fredi Bach's [sourceMakeup](http://jquery-jkit.com/sourcemakeup/) (we'll let the faux pas with respect to our naming scheme slide), should do the trick nicely. * **Lua** enthusiasts can get their fix with [Robert Gieseke](https://github.com/rgieseke)'s [Locco](http://rgieseke.github.io/locco/). * And if you happen to be a **.NET** aficionado, check out [Don Wilson](https://github.com/dontangg)'s [Nocco](http://dontangg.github.io/nocco/). * Going further afield from the quick-and-dirty, [Groc](http://nevir.github.io/groc/) is a **CoffeeScript** fork of Docco that adds a searchable table of contents, and aims to gracefully handle large projects with complex hierarchies of code. Note that not all ports will support all Docco features ... yet. Main Documentation Generation Functions --------------------------------------- Generate the documentation for our configured source file by copying over static assets, reading all the source files in, splitting them up into prose+code sections, highlighting each file in the appropriate language, and printing them out in an HTML template. document = (options = {}, callback) -> config = configure options fs.mkdirs config.output, -> callback or= (error) -> throw error if error copyAsset = (file, callback) -> fs.copy file, path.join(config.output, path.basename(file)), callback complete = -> copyAsset config.css, (error) -> if error then callback error else if fs.existsSync config.public then copyAsset config.public, callback else callback() files = config.sources.slice() nextFile = -> source = files.shift() fs.readFile source, (error, buffer) -> return callback error if error code = buffer.toString() sections = parse source, code, config format source, sections, config write source, sections, config if files.length then nextFile() else complete() nextFile() Given a string of source code, **parse** out each block of prose and the code that follows it — by detecting which is which, line by line — and then create an individual **section** for it. Each section is an object with `docsText` and `codeText` properties, and eventually `docsHtml` and `codeHtml` as well. parse = (source, code, config = {}) -> lines = code.split '\n' sections = [] lang = getLanguage source, config hasCode = docsText = codeText = '' save = -> sections.push {docsText, codeText} hasCode = docsText = codeText = '' Our quick-and-dirty implementation of the literate programming style. Simply invert the prose and code relationship on a per-line basis, and then continue as normal below. if lang.literate isText = maybeCode = yes for line, i in lines lines[i] = if maybeCode and match = /^([ ]{4}|[ ]{0,3}\t)/.exec line isText = no line[match[0].length..] else if maybeCode = /^\s*$/.test line if isText then lang.symbol else '' else isText = yes lang.symbol + ' ' + line for line in lines if line.match(lang.commentMatcher) and not line.match(lang.commentFilter) save() if hasCode docsText += (line = line.replace(lang.commentMatcher, '')) + '\n' save() if /^(---+|===+)$/.test line else hasCode = yes codeText += line + '\n' save() sections To **format** and highlight the now-parsed sections of code, we use **Highlight.js** over stdio, and run the text of their corresponding comments through **Markdown**, using [Marked](https://github.com/chjj/marked). format = (source, sections, config) -> language = getLanguage source, config Tell Marked how to highlight code blocks within comments, treating that code as either the language specified in the code block or the language of the file if not specified. marked.setOptions { highlight: (code, lang) -> lang or= language.name if highlightjs.getLanguage(lang) highlightjs.highlight(lang, code).value else console.warn "docco: couldn't highlight code block with unknown language '#{lang}' in #{source}" code } for section, i in sections code = highlightjs.highlight(language.name, section.codeText).value code = code.replace(/\s+$/, '') section.codeHtml = "
#{code}