├── .gitignore ├── .npmignore ├── Cakefile ├── LICENSE ├── README ├── bin └── docco ├── docco.js ├── docco.litcoffee ├── index.html ├── package-lock.json ├── package.json └── resources ├── classic ├── docco.css ├── docco.jst └── public │ ├── fonts │ ├── aller-bold.eot │ ├── aller-bold.ttf │ ├── aller-bold.woff │ ├── aller-light.eot │ ├── aller-light.ttf │ ├── aller-light.woff │ ├── fleurons.eot │ ├── fleurons.ttf │ ├── fleurons.woff │ ├── roboto-black.eot │ ├── roboto-black.ttf │ └── roboto-black.woff │ ├── images │ └── gray.png │ └── stylesheets │ └── normalize.css ├── languages.json ├── linear ├── docco.css ├── docco.jst └── public │ ├── fonts │ ├── aller-bold.eot │ ├── aller-bold.ttf │ ├── aller-bold.woff │ ├── aller-light.eot │ ├── aller-light.ttf │ ├── aller-light.woff │ ├── fleurons.eot │ ├── fleurons.ttf │ ├── fleurons.woff │ ├── roboto-black.eot │ ├── roboto-black.ttf │ └── roboto-black.woff │ ├── images │ └── gray.png │ └── stylesheets │ └── normalize.css ├── parallel ├── docco.css ├── docco.jst └── public │ ├── fonts │ ├── aller-bold.eot │ ├── aller-bold.ttf │ ├── aller-bold.woff │ ├── aller-light.eot │ ├── aller-light.ttf │ ├── aller-light.woff │ ├── roboto-black.eot │ ├── roboto-black.ttf │ └── roboto-black.woff │ └── stylesheets │ └── normalize.css └── plain-markdown ├── README.md └── docco.jst /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | output 3 | docs 4 | node_modules 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .git* 3 | Cakefile 4 | docco.litcoffee 5 | index.html 6 | -------------------------------------------------------------------------------- /Cakefile: -------------------------------------------------------------------------------- 1 | {spawn, exec} = require 'child_process' 2 | fs = require 'fs' 3 | path = require 'path' 4 | 5 | option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`' 6 | option '-w', '--watch', 'continually build the docco library' 7 | option '-l', '--layout [LAYOUT]', 'specify the layout for Docco\'s docs' 8 | 9 | task 'build', 'build the docco library', (options) -> 10 | coffee = spawn 'coffee', ['-c' + (if options.watch then 'w' else ''), '.'] 11 | coffee.stdout.on 'data', (data) -> console.log data.toString().trim() 12 | coffee.stderr.on 'data', (data) -> console.log data.toString().trim() 13 | 14 | task 'install', 'install the `docco` command into /usr/local (or --prefix)', (options) -> 15 | base = options.prefix or '/usr/local' 16 | lib = base + '/lib/docco' 17 | exec([ 18 | 'mkdir -p ' + lib + ' ' + base + '/bin' 19 | 'cp -rf bin README resources ' + lib 20 | 'ln -sf ' + lib + '/bin/docco ' + base + '/bin/docco' 21 | ].join(' && '), (err, stdout, stderr) -> 22 | if err then console.error stderr 23 | ) 24 | 25 | task 'doc', 'rebuild the Docco documentation', (options) -> 26 | layout = options.layout or 'linear' 27 | exec([ 28 | "bin/docco --layout #{layout} docco.litcoffee" 29 | "sed \"s/docco.css/resources\\/#{layout}\\/docco.css/\" < docs/docco.html > index.html" 30 | 'rm -r docs' 31 | ].join(' && '), (err) -> 32 | throw err if err 33 | ) 34 | 35 | task 'loc', 'count the lines of code in Docco', -> 36 | code = fs.readFileSync('docco.litcoffee').toString() 37 | lines = code.split('\n').filter (line) -> /^ /.test line 38 | console.log "Docco LOC: #{lines.length}" 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Lil License v1 2 | 3 | Copyright (c) 2009-2025 Jeremy Ashkenas 4 | 5 | Permission is hereby granted by the authors of this software, to any person, 6 | to use the software for any purpose, free of charge, including the rights 7 | to run, read, copy, change, distribute and sell it, and including usage rights 8 | to any patents the authors may hold on it, subject to the following conditions: 9 | 10 | This license, or a link to its text, must be included with all copies of 11 | the software and any derivative works. 12 | 13 | Any modification to the software submitted to the authors may be incorporated 14 | into the software under the terms of this license. 15 | 16 | The software is provided "as is", without warranty of any kind, including 17 | but not limited to the warranties of title, fitness, merchantability and 18 | non-infringement. The authors have no obligation to provide support or updates 19 | for the software, and may not be held liable for any damages, claims or other 20 | liability arising from its use. 21 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ____ 2 | /\ _`\ 3 | \ \ \/\ \ ___ ___ ___ ___ 4 | \ \ \ \ \ / __`\ /'___\ /'___\ / __`\ 5 | \ \ \_\ \ /\ \ \ \ /\ \__/ /\ \__/ /\ \ \ \ 6 | \ \____/ \ \____/ \ \____\ \ \____\ \ \____/ 7 | \/___/ \/___/ \/____/ \/____/ \/___/ 8 | 9 | 10 | Docco is a quick-and-dirty, hundred-line-long, literate-programming-style 11 | documentation generator. For more information, see: 12 | 13 | http://ashkenas.com/docco/ 14 | 15 | Installation: 16 | 17 | sudo npm install -g docco 18 | 19 | Usage: docco [options] FILES 20 | 21 | Options: 22 | 23 | -h, --help output usage information 24 | -V, --version output the version number 25 | -l, --layout [layout] choose a built-in layouts (parallel, linear) 26 | -c, --css [file] use a custom css file 27 | -o, --output [path] use a custom output path 28 | -t, --template [file] use a custom .jst template 29 | -e, --extension [ext] use the given file extension for all inputs 30 | -L, --languages [file] use a custom languages.json 31 | -m, --marked [file] use custom marked options 32 | -------------------------------------------------------------------------------- /bin/docco: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var dir = path.join(path.dirname(fs.realpathSync(__filename)), '../'); 6 | require(dir + 'docco.js').run(); 7 | -------------------------------------------------------------------------------- /docco.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.7.0 2 | (function() { 3 | // Docco 4 | // ===== 5 | 6 | // **Docco** is a quick-and-dirty documentation generator, written in 7 | // [Literate CoffeeScript](http://coffeescript.org/#literate). 8 | // It produces an HTML document that displays your comments intermingled with your 9 | // code. All prose is passed through 10 | // [Markdown](http://daringfireball.net/projects/markdown/syntax), and code is 11 | // passed through [Highlight.js](http://highlightjs.org/) syntax highlighting. 12 | // This page is the result of running Docco against its own 13 | // [source file](https://github.com/jashkenas/docco/blob/master/docco.litcoffee). 14 | 15 | // 1. Install Docco with **npm**: `sudo npm install -g docco` 16 | 17 | // 2. Run it against your code: `docco src/*.coffee` 18 | 19 | // There is no "Step 3". This will generate an HTML page for each of the named 20 | // source files, with a menu linking to the other pages, saving the whole mess 21 | // into a `docs` folder (configurable). 22 | 23 | // The [Docco source](http://github.com/jashkenas/docco) is available on GitHub, 24 | // and is released under the [Lil License](http://lillicense.org/v1.html). 25 | 26 | // Docco can be used to process code written in any programming language. If it 27 | // doesn't handle your favorite yet, feel free to 28 | // [add it to the list](https://github.com/jashkenas/docco/blob/master/resources/languages.json). 29 | // Finally, the ["literate" style](http://coffeescript.org/#literate) of *any* 30 | // language listed in [languages.json](https://github.com/jashkenas/docco/blob/master/resources/languages.json) 31 | // is also supported — just tack an `.md` extension on the end: 32 | // `.coffee.md`, `.py.md`, and so on. 33 | 34 | // Partners in Crime: 35 | // ------------------ 36 | 37 | // * If Node.js doesn't run on your platform, or you'd prefer a more 38 | // convenient package, get [Ryan Tomayko](http://github.com/rtomayko)'s 39 | // [Rocco](http://rtomayko.github.io/rocco/rocco.html), the **Ruby** port that's 40 | // available as a gem. 41 | 42 | // * If you're writing shell scripts, try 43 | // [Shocco](http://rtomayko.github.io/shocco/), a port for the **POSIX shell**, 44 | // also by Mr. Tomayko. 45 | 46 | // * If **Python** is more your speed, take a look at 47 | // [Nick Fitzgerald](http://github.com/fitzgen)'s [Pycco](https://pycco-docs.github.io/pycco/). 48 | 49 | // * For **Clojure** fans, [Fogus](http://blog.fogus.me/)'s 50 | // [Marginalia](http://fogus.me/fun/marginalia/) is a bit of a departure from 51 | // "quick-and-dirty", but it'll get the job done. 52 | 53 | // * There's a **Go** port called [Gocco](http://nikhilm.github.io/gocco/), 54 | // written by [Nikhil Marathe](https://github.com/nikhilm). 55 | 56 | // * For all you **PHP** buffs out there, Fredi Bach's 57 | // [sourceMakeup](http://jquery-jkit.com/sourcemakeup/) (we'll let the faux pas 58 | // with respect to our naming scheme slide), should do the trick nicely. 59 | 60 | // * **Lua** enthusiasts can get their fix with 61 | // [Robert Gieseke](https://github.com/rgieseke)'s [Locco](http://rgieseke.github.io/locco/). 62 | 63 | // * And if you happen to be a **.NET** 64 | // aficionado, check out [Don Wilson](https://github.com/dontangg)'s 65 | // [Nocco](http://dontangg.github.io/nocco/). 66 | 67 | // * Going further afield from the quick-and-dirty, [Groc](http://nevir.github.io/groc/) 68 | // is a **CoffeeScript** fork of Docco that adds a searchable table of contents, 69 | // and aims to gracefully handle large projects with complex hierarchies of code. 70 | 71 | // * For **ES6** fans, [Docco Next](https://github.com/mobily-enterprises/docco-next) 72 | // is an expanded rewrite of Docco in modern JavaScript, thoroughly commented, 73 | // and with the bonus that your existing templates will still work. 74 | 75 | // Note that not all ports support all Docco features. 76 | 77 | // Main Documentation Generation Functions 78 | // --------------------------------------- 79 | 80 | // Generate the documentation for our configured source file by copying over static 81 | // assets, reading all the source files in, splitting them up into prose+code 82 | // sections, highlighting each file in the appropriate language, and printing them 83 | // out in an HTML template. 84 | var Docco, _, buildMatchers, commander, configure, defaults, document, format, fs, getLanguage, highlightjs, languages, marked, parse, path, run, version, write; 85 | 86 | document = function(options = {}, callback) { 87 | var config; 88 | config = configure(options); 89 | return fs.mkdirs(config.output, function() { 90 | var complete, copyAsset, files, nextFile; 91 | callback || (callback = function(error) { 92 | if (error) { 93 | throw error; 94 | } 95 | }); 96 | copyAsset = function(file, callback) { 97 | if (!fs.existsSync(file)) { 98 | return callback(); 99 | } 100 | return fs.copy(file, path.join(config.output, path.basename(file)), callback); 101 | }; 102 | complete = function() { 103 | return copyAsset(config.css, function(error) { 104 | if (error) { 105 | return callback(error); 106 | } 107 | if (fs.existsSync(config.public)) { 108 | return copyAsset(config.public, callback); 109 | } 110 | return callback(); 111 | }); 112 | }; 113 | files = config.sources.slice(); 114 | nextFile = function() { 115 | var source; 116 | source = files.shift(); 117 | return fs.readFile(source, function(error, buffer) { 118 | var code, sections; 119 | if (error) { 120 | return callback(error); 121 | } 122 | code = buffer.toString(); 123 | sections = parse(source, code, config); 124 | format(source, sections, config); 125 | write(source, sections, config); 126 | if (files.length) { 127 | return nextFile(); 128 | } else { 129 | return complete(); 130 | } 131 | }); 132 | }; 133 | return nextFile(); 134 | }); 135 | }; 136 | 137 | // Given a string of source code, **parse** out each block of prose and the code that 138 | // follows it — by detecting which is which, line by line — and then create an 139 | // individual **section** for it. Each section is an object with `docsText` and 140 | // `codeText` properties, and eventually `docsHtml` and `codeHtml` as well. 141 | parse = function(source, code, config = {}) { 142 | var codeText, docsText, hasCode, i, isText, j, k, lang, len, len1, line, lines, match, maybeCode, save, sections; 143 | lines = code.split('\n'); 144 | sections = []; 145 | lang = getLanguage(source, config); 146 | hasCode = docsText = codeText = ''; 147 | save = function() { 148 | sections.push({docsText, codeText}); 149 | return hasCode = docsText = codeText = ''; 150 | }; 151 | // Our quick-and-dirty implementation of the literate programming style. Simply 152 | // invert the prose and code relationship on a per-line basis, and then continue as 153 | // normal below. 154 | if (lang.literate) { 155 | isText = maybeCode = true; 156 | for (i = j = 0, len = lines.length; j < len; i = ++j) { 157 | line = lines[i]; 158 | lines[i] = maybeCode && (match = /^([ ]{4}|[ ]{0,3}\t)/.exec(line)) ? (isText = false, line.slice(match[0].length)) : (maybeCode = /^\s*$/.test(line)) ? isText ? lang.symbol : '' : (isText = true, lang.symbol + ' ' + line); 159 | } 160 | } 161 | for (k = 0, len1 = lines.length; k < len1; k++) { 162 | line = lines[k]; 163 | if (line.match(lang.commentMatcher) && !line.match(lang.commentFilter)) { 164 | if (hasCode) { 165 | save(); 166 | } 167 | docsText += (line = line.replace(lang.commentMatcher, '')) + '\n'; 168 | if (/^(---+|===+)$/.test(line)) { 169 | save(); 170 | } 171 | } else { 172 | hasCode = true; 173 | codeText += line + '\n'; 174 | } 175 | } 176 | save(); 177 | return sections; 178 | }; 179 | 180 | // To **format** and highlight the now-parsed sections of code, we use **Highlight.js** 181 | // over stdio, and run the text of their corresponding comments through 182 | // **Markdown**, using [Marked](https://github.com/chjj/marked). 183 | format = function(source, sections, config) { 184 | var code, i, j, language, len, markedOptions, results, section; 185 | language = getLanguage(source, config); 186 | // Pass any user defined options to Marked if specified via command line option 187 | markedOptions = { 188 | smartypants: true 189 | }; 190 | if (config.marked) { 191 | markedOptions = config.marked; 192 | } 193 | marked.setOptions(markedOptions); 194 | // Tell Marked how to highlight code blocks within comments, treating that code 195 | // as either the language specified in the code block or the language of the file 196 | // if not specified. 197 | marked.setOptions({ 198 | highlight: function(code, lang) { 199 | lang || (lang = language.name); 200 | if (highlightjs.getLanguage(lang)) { 201 | return highlightjs.highlight(code, { 202 | language: lang 203 | }).value; 204 | } else { 205 | console.warn(`docco: couldn't highlight code block with unknown language '${lang}' in ${source}`); 206 | return code; 207 | } 208 | } 209 | }); 210 | results = []; 211 | for (i = j = 0, len = sections.length; j < len; i = ++j) { 212 | section = sections[i]; 213 | code = highlightjs.highlight(section.codeText, { 214 | language: language.name 215 | }).value; 216 | code = code.replace(/\s+$/, ''); 217 | section.codeHtml = `
${code}
#{code}
Docco is a quick-and-dirty documentation generator, written in 28 | Literate CoffeeScript. 29 | It produces an HTML document that displays your comments intermingled with your 30 | code. All prose is passed through 31 | Markdown, and code is 32 | passed through Highlight.js syntax highlighting. 33 | This page is the result of running Docco against its own 34 | source file.
35 |Install Docco with npm: sudo npm install -g docco
Run it against your code: docco src/*.coffee
There is no “Step 3”. This will generate an HTML page for each of the named
42 | source files, with a menu linking to the other pages, saving the whole mess
43 | into a docs
folder (configurable).
The Docco source is available on GitHub, 45 | and is released under the Lil License.
46 |Docco can be used to process code written in any programming language. If it
47 | doesn’t handle your favorite yet, feel free to
48 | add it to the list.
49 | Finally, the “literate” style of any
50 | language listed in languages.json
51 | is also supported — just tack an .md
extension on the end:
52 | .coffee.md
, .py.md
, and so on.
If Node.js doesn’t run on your platform, or you’d prefer a more 60 | convenient package, get Ryan Tomayko‘s 61 | Rocco, the Ruby port that’s 62 | available as a gem.
63 |If you’re writing shell scripts, try 65 | Shocco, a port for the POSIX shell, 66 | also by Mr. Tomayko.
67 |If Python is more your speed, take a look at 69 | Nick Fitzgerald‘s Pycco.
70 |For Clojure fans, Fogus‘s 72 | Marginalia is a bit of a departure from 73 | “quick-and-dirty”, but it’ll get the job done.
74 |There’s a Go port called Gocco, 76 | written by Nikhil Marathe.
77 |For all you PHP buffs out there, Fredi Bach’s 79 | sourceMakeup (we’ll let the faux pas 80 | with respect to our naming scheme slide), should do the trick nicely.
81 |Lua enthusiasts can get their fix with 83 | Robert Gieseke‘s Locco.
84 |And if you happen to be a .NET 86 | aficionado, check out Don Wilson‘s 87 | Nocco.
88 |Going further afield from the quick-and-dirty, Groc 90 | is a CoffeeScript fork of Docco that adds a searchable table of contents, 91 | and aims to gracefully handle large projects with complex hierarchies of code.
92 |For ES6 fans, Docco Next 94 | is an expanded rewrite of Docco in modern JavaScript, thoroughly commented, 95 | and with the bonus that your existing templates will still work.
96 |Note that not all ports support all Docco features.
99 |Generate the documentation for our configured source file by copying over static 105 | assets, reading all the source files in, splitting them up into prose+code 106 | sections, highlighting each file in the appropriate language, and printing them 107 | out in an HTML template.
108 | 109 | 110 |document = (options = {}, callback) ->
111 | config = configure options
112 |
113 | fs.mkdirs config.output, ->
114 |
115 | callback or= (error) -> throw error if error
116 | copyAsset = (file, callback) ->
117 | return callback() unless fs.existsSync file
118 | fs.copy file, path.join(config.output, path.basename(file)), callback
119 | complete = ->
120 | copyAsset config.css, (error) ->
121 | return callback error if error
122 | return copyAsset config.public, callback if fs.existsSync config.public
123 | callback()
124 |
125 | files = config.sources.slice()
126 |
127 | nextFile = ->
128 | source = files.shift()
129 | fs.readFile source, (error, buffer) ->
130 | return callback error if error
131 |
132 | code = buffer.toString()
133 | sections = parse source, code, config
134 | format source, sections, config
135 | write source, sections, config
136 | if files.length then nextFile() else complete()
137 |
138 | nextFile()
Given a string of source code, parse out each block of prose and the code that
143 | follows it — by detecting which is which, line by line — and then create an
144 | individual section for it. Each section is an object with docsText
and
145 | codeText
properties, and eventually docsHtml
and codeHtml
as well.
parse = (source, code, config = {}) ->
149 | lines = code.split '\n'
150 | sections = []
151 | lang = getLanguage source, config
152 | hasCode = docsText = codeText = ''
153 |
154 | save = ->
155 | sections.push {docsText, codeText}
156 | hasCode = docsText = codeText = ''
Our quick-and-dirty implementation of the literate programming style. Simply 161 | invert the prose and code relationship on a per-line basis, and then continue as 162 | normal below.
163 | 164 | 165 | if lang.literate
166 | isText = maybeCode = yes
167 | for line, i in lines
168 | lines[i] = if maybeCode and match = /^([ ]{4}|[ ]{0,3}\t)/.exec line
169 | isText = no
170 | line[match[0].length..]
171 | else if maybeCode = /^\s*$/.test line
172 | if isText then lang.symbol else ''
173 | else
174 | isText = yes
175 | lang.symbol + ' ' + line
176 |
177 | for line in lines
178 | if line.match(lang.commentMatcher) and not line.match(lang.commentFilter)
179 | save() if hasCode
180 | docsText += (line = line.replace(lang.commentMatcher, '')) + '\n'
181 | save() if /^(---+|===+)$/.test line
182 | else
183 | hasCode = yes
184 | codeText += line + '\n'
185 | save()
186 |
187 | sections
To format and highlight the now-parsed sections of code, we use Highlight.js 192 | over stdio, and run the text of their corresponding comments through 193 | Markdown, using Marked.
194 | 195 | 196 |format = (source, sections, config) ->
197 | language = getLanguage source, config
Pass any user defined options to Marked if specified via command line option
202 | 203 | 204 | markedOptions =
205 | smartypants: true
206 |
207 | if config.marked
208 | markedOptions = config.marked
209 |
210 | marked.setOptions markedOptions
Tell Marked how to highlight code blocks within comments, treating that code 215 | as either the language specified in the code block or the language of the file 216 | if not specified.
217 | 218 | 219 | marked.setOptions {
220 | highlight: (code, lang) ->
221 | lang or= language.name
222 |
223 | if highlightjs.getLanguage(lang)
224 | highlightjs.highlight(code, {language: lang}).value
225 | else
226 | console.warn "docco: couldn't highlight code block with unknown language '#{lang}' in #{source}"
227 | code
228 | }
229 |
230 | for section, i in sections
231 | code = highlightjs.highlight(section.codeText, {language: language.name}).value
232 | code = code.replace(/\s+$/, '')
233 | section.codeHtml = "<div class='highlight'><pre>#{code}</pre></div>"
234 | section.docsHtml = marked(section.docsText)
Once all of the code has finished highlighting, we can write the resulting 239 | documentation file by passing the completed HTML sections into the template, 240 | and rendering it to the specified output path.
241 | 242 | 243 |write = (source, sections, config) ->
244 |
245 | destination = (file) ->
246 | path.join(config.output, path.dirname(file), path.basename(file, path.extname(file)) + '.html')
247 |
248 | relative = (file) ->
249 | to = path.dirname(path.resolve(file))
250 | from = path.dirname(path.resolve(destination(source)))
251 | path.join(path.relative(from, to), path.basename(file))
The title of the file is either the first heading in the prose, or the 256 | name of the source file.
257 | 258 | 259 | firstSection = _.find sections, (section) ->
260 | section.docsText.length > 0
261 | first = marked.lexer(firstSection.docsText)[0] if firstSection
262 | hasTitle = first and first.type is 'heading' and first.depth is 1
263 | title = if hasTitle then first.text else path.basename source
264 | css = relative path.join(config.output, path.basename(config.css))
265 |
266 | html = config.template {sources: config.sources, css,
267 | title, hasTitle, sections, path, destination, relative}
268 |
269 | console.log "docco: #{source} -> #{destination source}"
270 | fs.outputFileSync destination(source), html
Default configuration options. All of these may be extended by 280 | user-specified options.
281 | 282 | 283 |defaults =
284 | layout: 'parallel'
285 | output: 'docs'
286 | template: null
287 | css: null
288 | extension: null
289 | languages: {}
290 | marked: null
Configure this particular run of Docco. We might use a passed-in external 295 | template, or one of the built-in layouts. We only attempt to process 296 | source files for languages for which we have definitions.
297 | 298 | 299 |configure = (options) ->
300 | config = _.extend {}, defaults, _.pick(options.opts(), _.keys(defaults)...)
301 |
302 | config.languages = buildMatchers config.languages
The user is able to override the layout file used with the --template
parameter.
307 | In this case, it is also neccessary to explicitly specify a stylesheet file.
308 | These custom templates are compiled exactly like the predefined ones, but the public
folder
309 | is only copied for the latter.
if config.template
313 | unless config.css
314 | console.warn "docco: no stylesheet file specified"
315 | config.layout = null
316 | else
317 | dir = config.layout = path.join __dirname, 'resources', config.layout
318 | config.public = path.join dir, 'public' if fs.existsSync path.join dir, 'public'
319 | config.template = path.join dir, 'docco.jst'
320 | config.css = options.css or path.join dir, 'resources/linear/docco.css'
321 | config.template = _.template fs.readFileSync(config.template).toString()
322 |
323 | if options.marked
324 | config.marked = JSON.parse fs.readFileSync(options.marked)
325 |
326 | config.sources = options.args.filter((source) ->
327 | lang = getLanguage source, config
328 | console.warn "docco: skipped unknown type (#{path.basename source})" unless lang
329 | lang
330 | ).sort()
331 |
332 | config
Require our external dependencies.
342 | 343 | 344 |_ = require 'underscore'
345 | fs = require 'fs-extra'
346 | path = require 'path'
347 | marked = require('marked').marked
348 | commander = require 'commander'
349 | highlightjs = require 'highlight.js'
Languages are stored in JSON in the file resources/languages.json
.
354 | Each item maps the file extension to the name of the language and the
355 | symbol
that indicates a line comment. To add support for a new programming
356 | language to Docco, just add it to the file.
languages = JSON.parse fs.readFileSync(path.join(__dirname, 'resources', 'languages.json'))
Build out the appropriate matchers and delimiters for each language.
364 | 365 | 366 |buildMatchers = (languages) ->
367 | for ext, l of languages
Does the line begin with a comment?
372 | 373 | 374 | l.commentMatcher = ///^\s*#{l.symbol}\s?///
Ignore hashbangs and interpolations…
379 | 380 | 381 | l.commentFilter = /(^#![/]|^\s*#\{)/
382 | languages
383 | languages = buildMatchers languages
A function to get the current language we’re documenting, based on the
388 | file extension. Detect and tag “literate” .ext.md
variants.
getLanguage = (source, config) ->
392 | ext = config.extension or path.extname(source) or path.basename(source)
393 | lang = config.languages?[ext] or languages[ext]
394 | if lang and lang.name is 'markdown'
395 | codeExt = path.extname(path.basename(source, ext))
396 | codeLang = config.languages?[codeExt] or languages[codeExt]
397 | if codeExt and codeLang
398 | lang = _.extend {}, codeLang, {literate: yes}
399 | lang
Keep it DRY. Extract the docco version from package.json
version = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'))).version
Finally, let’s define the interface to run Docco from the command line. 416 | Parse options using Commander.
417 | 418 | 419 |run = (args = process.argv) ->
420 | c = defaults
421 | commander.version(version)
422 | .usage('[options] files')
423 | .option('-L, --languages [file]', 'use a custom languages.json', _.compose JSON.parse, fs.readFileSync)
424 | .option('-l, --layout [name]', 'choose a layout (parallel, linear or classic)', c.layout)
425 | .option('-o, --output [path]', 'output to a given folder', c.output)
426 | .option('-c, --css [file]', 'use a custom css file', c.css)
427 | .option('-t, --template [file]', 'use a custom .jst template', c.template)
428 | .option('-e, --extension [ext]', 'assume a file extension for all inputs', c.extension)
429 | .option('-m, --marked [file]', 'use custom marked options', c.marked)
430 | .parse(args)
431 | .name = "docco"
432 | if commander.args.length
433 | document commander
434 | else
435 | console.log commander.helpInformation()
Docco = module.exports = {run, document, parse, format, version}
```
),
7 | which is compatible with Github- and pandoc-flavoured markdown.
8 |
9 | Example PDF output:
10 |
11 | # fetch some annotated source code
12 | cd /tmp
13 | wget https://raw.github.com/documentcloud/underscore/master/underscore.js
14 | # make plain with docco
15 | docco -l plain-markdown underscore.js
16 | cd ./docs
17 | # make PDF from plaintext with pandoc
18 | pandoc -f markdown docs/underscore.html -o underscore.pdf
19 |
20 |
21 | `pandoc` has a ton of options and output formats, you could also export to ePub, iBook- and Kindle-eBooks, Mediawiki markup, etc. [RTFM][pandoc-man]!
22 |
23 | An full-fledged scientific-looking PDF could use these options:
24 |
25 | pandoc -f markdown \
26 | --table-of-contents \
27 | --highlight-style=pygments \
28 | -V title="Underscore.js" \
29 | -V author="Prof. Dr. J. Ashkenas" \
30 | -V date="$(date +%d-%m-%Y)" \
31 | -V documentclass="book" \
32 | --chapters \
33 | -o docs/underscore.pdf \
34 | docs/underscore.html
35 |
36 |
37 | This is as quick-and-dirty as the original docco,
38 | so there are some **bugs**:
39 |
40 | - The code blocks don't always fit on paper (if the lines are too long). This is a serious issue, but could be fixed "easily" in [pandoc's LaTeX template][] (where "easy" is relative, as always when dealing with LaTeX)
41 |
42 | - No syntax higlighting. Could maybe built into the template -- it would be just a matter of printing something like ```javascript
as the start of each code fence.
43 |
44 | - The plaintext output file is markdown, but the filename has .html (not really important since it is just an intermediary file)
45 |
46 |
47 |
48 | [pandoc]: http://johnmacfarlane.net/pandoc/index.html
49 | [pandoc-man]: http://johnmacfarlane.net/pandoc/README.html
50 | [pandoc's LaTeX template]: https://github.com/jgm/pandoc-templates/blob/master/default.latex
51 |
--------------------------------------------------------------------------------
/resources/plain-markdown/docco.jst:
--------------------------------------------------------------------------------
1 | <% for (var i = 0, l = sections.length; i