forked from jashkenas/docco
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
382 lines (248 loc) · 17.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<!DOCTYPE html>
<html>
<head>
<title>Docco</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" media="all" href="public/stylesheets/normalize.css" />
<link rel="stylesheet" media="all" href="resources/linear/docco.css" />
</head>
<body>
<div class="container">
<div class="page">
<div class="header">
<h1>Docco</h1>
</div>
<p><strong>Docco</strong> is a quick-and-dirty documentation generator, written in
<a href="http://coffeescript.org/#literate">Literate CoffeeScript</a>.
It produces an HTML document that displays your comments intermingled with your
code. All prose is passed through
<a href="http://daringfireball.net/projects/markdown/syntax">Markdown</a>, and code is
passed through <a href="http://highlightjs.org/">Highlight.js</a> syntax highlighting.
This page is the result of running Docco against its own
<a href="https://github.com/jashkenas/docco/blob/master/docco.litcoffee">source file</a>.</p>
<ol>
<li><p>Install Docco with <strong>npm</strong>: <code>sudo npm install -g docco</code></p>
</li>
<li><p>Run it against your code: <code>docco src/*.coffee</code></p>
</li>
</ol>
<p>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 <code>docs</code> folder (configurable).</p>
<p>The <a href="http://github.com/jashkenas/docco">Docco source</a> is available on GitHub,
and is released under the <a href="http://opensource.org/licenses/MIT">MIT license</a>.</p>
<p>Docco can be used to process code written in any programming language. If it
doesn't handle your favorite yet, feel free to
<a href="https://github.com/jashkenas/docco/blob/master/resources/languages.json">add it to the list</a>.
Finally, the <a href="http://coffeescript.org/#literate">"literate" style</a> of <em>any</em>
language is also supported — just tack an <code>.md</code> extension on the end:
<code>.coffee.md</code>, <code>.py.md</code>, and so on.</p>
<h2>Partners in Crime:</h2>
<ul>
<li><p>If <strong>Node.js</strong> doesn't run on your platform, or you'd prefer a more
convenient package, get <a href="http://github.com/rtomayko">Ryan Tomayko</a>'s
<a href="http://rtomayko.github.com/rocco/rocco.html">Rocco</a>, the Ruby port that's
available as a gem.</p>
</li>
<li><p>If you're writing shell scripts, try
<a href="http://rtomayko.github.com/shocco/">Shocco</a>, a port for the <strong>POSIX shell</strong>,
also by Mr. Tomayko.</p>
</li>
<li><p>If <strong>Python</strong> is more your speed, take a look at
<a href="http://github.com/fitzgen">Nick Fitzgerald</a>'s <a href="http://fitzgen.github.com/pycco/">Pycco</a>.</p>
</li>
<li><p>For <strong>Clojure</strong> fans, <a href="http://blog.fogus.me/">Fogus</a>'s
<a href="http://fogus.me/fun/marginalia/">Marginalia</a> is a bit of a departure from
"quick-and-dirty", but it'll get the job done.</p>
</li>
<li><p>There's a <strong>Go</strong> port called <a href="http://nikhilm.github.com/gocco/">Gocco</a>,
written by <a href="https://github.com/nikhilm">Nikhil Marathe</a>.</p>
</li>
<li><p>Your all you <strong>PHP</strong> buffs out there, Fredi Bach's
<a href="http://jquery-jkit.com/sourcemakeup/">sourceMakeup</a> (we'll let the faux pas
with respect to our naming scheme slide), should do the trick nicely.</p>
</li>
<li><p><strong>Lua</strong> enthusiasts can get their fix with
<a href="https://github.com/rgieseke">Robert Gieseke</a>'s <a href="http://rgieseke.github.com/locco/">Locco</a>.</p>
</li>
<li><p>And if you happen to be a <strong>.NET</strong>
aficionado, check out <a href="https://github.com/dontangg">Don Wilson</a>'s
<a href="http://dontangg.github.com/nocco/">Nocco</a>.</p>
</li>
<li><p>Going further afield from the quick-and-dirty, <a href="http://nevir.github.com/groc/">Groc</a>
is a <strong>CoffeeScript</strong> fork of Docco that adds a searchable table of contents,
and aims to gracefully handle large projects with complex hierarchies of code.</p>
</li>
</ul>
<p>Note that not all ports will support all Docco features ... yet.</p>
<h2>Main Documentation Generation Functions</h2>
<p>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.</p>
<div class='highlight'><pre><span class="function"><span class="title">document</span></span> = (options = {}, callback) ->
configure options
fs.mkdirs config.output, ->
callback <span class="function"><span class="title">or</span></span>= (error) -> <span class="keyword">throw</span> error <span class="keyword">if</span> error
<span class="function"><span class="title">copyAsset</span></span> = (file, callback) ->
fs.copy file, path.join(config.output, path.basename(file)), callback
<span class="function"><span class="title">complete</span></span> = ->
copyAsset config.css, (error) ->
<span class="keyword">if</span> error <span class="keyword">then</span> callback error
<span class="keyword">else</span> <span class="keyword">if</span> fs.existsSync config.public <span class="keyword">then</span> copyAsset config.public, callback
<span class="keyword">else</span> callback()
files = config.sources.slice()
<span class="function"><span class="title">nextFile</span></span> = ->
source = files.shift()
fs.readFile source, (error, buffer) ->
<span class="keyword">return</span> callback error <span class="keyword">if</span> error
code = buffer.toString()
sections = parse source, code
format source, sections
write source, sections
<span class="keyword">if</span> files.length <span class="keyword">then</span> nextFile() <span class="keyword">else</span> complete()
nextFile()</pre></div>
<p>Given a string of source code, <strong>parse</strong> out each block of prose and the code that
follows it — by detecting which is which, line by line — and then create an
individual <strong>section</strong> for it. Each section is an object with <code>docsText</code> and
<code>codeText</code> properties, and eventually <code>docsHtml</code> and <code>codeHtml</code> as well.</p>
<div class='highlight'><pre><span class="function"><span class="title">parse</span></span> = (source, code) ->
lines = code.split <span class="string">'\n'</span>
sections = []
lang = getLanguage source
hasCode = docsText = codeText = <span class="string">''</span>
<span class="function"><span class="title">save</span></span> = ->
sections.push {docsText, codeText}
hasCode = docsText = codeText = <span class="string">''</span></pre></div>
<p>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.</p>
<div class='highlight'><pre> <span class="keyword">if</span> lang.literate
<span class="keyword">for</span> line, i <span class="keyword">in</span> lines
lines[i] = <span class="keyword">if</span> <span class="regexp">/^\s*$/</span>.test line
<span class="string">''</span>
<span class="keyword">else</span> <span class="keyword">if</span> match = (<span class="regexp">/^([ ]{4}|\t)/</span>).exec line
line[match[<span class="number">0</span>].length..]
<span class="keyword">else</span>
lang.symbol + <span class="string">' '</span> + line
<span class="keyword">for</span> line <span class="keyword">in</span> lines
<span class="keyword">if</span> (<span class="keyword">not</span> line <span class="keyword">and</span> prev <span class="keyword">is</span> <span class="string">'text'</span>) <span class="keyword">or</span>
(line.match(lang.commentMatcher) <span class="keyword">and</span> <span class="keyword">not</span> line.match(lang.commentFilter))
save() <span class="keyword">if</span> hasCode
docsText += (line = line.replace(lang.commentMatcher, <span class="string">''</span>)) + <span class="string">'\n'</span>
save() <span class="keyword">if</span> <span class="regexp">/^(---+|===+)$/</span>.test line
prev = <span class="string">'text'</span>
<span class="keyword">else</span>
hasCode = <span class="literal">yes</span>
codeText += line + <span class="string">'\n'</span>
prev = <span class="string">'code'</span>
save()
sections</pre></div>
<p>To <strong>format</strong> and highlight the now-parsed sections of code, we use <strong>Highlight.js</strong>
over stdio, and run the text of their corresponding comments through
<strong>Markdown</strong>, using <a href="https://github.com/chjj/marked">Marked</a>.</p>
<div class='highlight'><pre><span class="function"><span class="title">format</span></span> = (source, sections) ->
language = getLanguage source
<span class="keyword">for</span> section, i <span class="keyword">in</span> sections
code = highlight(language.name, section.codeText).value
code = code.replace(<span class="regexp">/\s+$/</span>, <span class="string">''</span>)
section.codeHtml = <span class="string">"<div class='highlight'><pre><span class="subst">#{code}</span></pre></div>"</span>
section.docsHtml = marked(section.docsText)</pre></div>
<p>Once all of the code has finished highlighting, we can <strong>write</strong> the resulting
documentation file by passing the completed HTML sections into the template,
and rendering it to the specified output path.</p>
<div class='highlight'><pre><span class="function"><span class="title">write</span></span> = (source, sections) ->
<span class="function"><span class="title">destination</span></span> = (file) ->
path.join(config.output, path.basename(file, path.extname(file)) + <span class="string">'.html'</span>)</pre></div>
<p>The <strong>title</strong> of the file is either the first heading in the prose, or the
name of the source file.</p>
<div class='highlight'><pre> first = marked.lexer(sections[<span class="number">0</span>].docsText)[<span class="number">0</span>]
hasTitle = first <span class="keyword">and</span> first.type <span class="keyword">is</span> <span class="string">'heading'</span> <span class="keyword">and</span> first.depth <span class="keyword">is</span> <span class="number">1</span>
title = <span class="keyword">if</span> hasTitle <span class="keyword">then</span> first.text <span class="keyword">else</span> path.basename source
html = config.template {sources: config.sources, css: path.basename(config.css),
title, hasTitle, sections, path, destination,}
console.log <span class="string">"docco: <span class="subst">#{source}</span> -> <span class="subst">#{destination source}</span>"</span>
fs.writeFileSync destination(source), html</pre></div>
<h2>Configuration</h2>
<p>Default configuration <strong>options</strong>. All of these may be overriden by command-line
options.</p>
<div class='highlight'><pre>config =
layout: <span class="string">'parallel'</span>
output: <span class="string">'docs'</span>
template: <span class="literal">null</span>
css: <span class="literal">null</span>
extension: <span class="literal">null</span></pre></div>
<p><strong>Configure</strong> this particular run of Docco. We might use a passed-in external
template, or one of the built-in <strong>layouts</strong>. We only attempt to process
source files for languages for which we have definitions.</p>
<div class='highlight'><pre><span class="function"><span class="title">configure</span></span> = (options) ->
_.extend config, _.pick(options, _.keys(config)...)
<span class="keyword">if</span> options.template
config.layout = <span class="literal">null</span>
<span class="keyword">else</span>
dir = config.layout = path.join __dirname, <span class="string">'resources'</span>, config.layout
config.public = path.join dir, <span class="string">'public'</span> <span class="keyword">if</span> fs.existsSync path.join dir, <span class="string">'public'</span>
config.template = path.join dir, <span class="string">'docco.jst'</span>
config.css = options.css <span class="keyword">or</span> path.join dir, <span class="string">'resources/linear/docco.css'</span>
config.template = _.template fs.readFileSync(config.template).toString()
config.sources = options.args.filter((source) ->
lang = getLanguage source, config
console.warn <span class="string">"docco: skipped unknown type (<span class="subst">#{path.basename source}</span>)"</span> <span class="keyword">unless</span> lang
lang
).sort()</pre></div>
<h2>Helpers & Initial Setup</h2>
<p>Require our external dependencies.</p>
<div class='highlight'><pre>_ = require <span class="string">'underscore'</span>
fs = require <span class="string">'fs-extra'</span>
path = require <span class="string">'path'</span>
marked = require <span class="string">'marked'</span>
commander = require <span class="string">'commander'</span>
{highlight} = require <span class="string">'highlight.js'</span></pre></div>
<p>Languages are stored in JSON in the file <code>resources/languages.json</code>.
Each item maps the file extension to the name of the language and the
<code>symbol</code> that indicates a line comment. To add support for a new programming
language to Docco, just add it to the file.</p>
<div class='highlight'><pre>languages = JSON.parse fs.readFileSync(path.join(__dirname, <span class="string">'resources'</span>, <span class="string">'languages.json'</span>))</pre></div>
<p>Build out the appropriate matchers and delimiters for each language.</p>
<div class='highlight'><pre><span class="keyword">for</span> ext, l <span class="keyword">of</span> languages</pre></div>
<p>Does the line begin with a comment?</p>
<div class='highlight'><pre> l.commentMatcher = <span class="regexp">///^\s*<span class="comment">#{l.symbol}\s?///</span></pre></div>
<p>Ignore <a href="http://en.wikipedia.org/wiki/Shebang_(Unix\">hashbangs</a>) and interpolations...</p>
<div class='highlight'><pre> l.commentFilter = <span class="regexp">/(^#![/]|^\s*#\{)/</span></pre></div>
<p>A function to get the current language we're documenting, based on the
file extension. Detect and tag "literate" <code>.ext.md</code> variants.</p>
<div class='highlight'><pre><span class="function"><span class="title">getLanguage</span></span> = (source) ->
ext = config.extension <span class="keyword">or</span> path.extname(source) <span class="keyword">or</span> path.basename(source)
lang = languages[ext]
<span class="keyword">if</span> lang <span class="keyword">and</span> lang.name <span class="keyword">is</span> <span class="string">'markdown'</span>
codeExt = path.extname(path.basename(source, ext))
<span class="keyword">if</span> codeExt <span class="keyword">and</span> codeLang = languages[codeExt]
lang = _.extend {}, codeLang, {literate: <span class="literal">yes</span>}
lang</pre></div>
<p>Keep it DRY. Extract the docco <strong>version</strong> from <code>package.json</code></p>
<div class='highlight'><pre>version = JSON.parse(fs.readFileSync(path.join(__dirname, <span class="string">'package.json'</span>))).version</pre></div>
<h2>Command Line Interface</h2>
<p>Finally, let's define the interface to run Docco from the command line.
Parse options using <a href="https://github.com/visionmedia/commander.js">Commander</a>.</p>
<div class='highlight'><pre><span class="function"><span class="title">run</span></span> = (args = process.argv) ->
c = config
commander.version(version)
.usage(<span class="string">'[options] files'</span>)
.option(<span class="string">'-l, --layout [name]'</span>, <span class="string">'choose a layout (parallel, linear or classic)'</span>, c.layout)
.option(<span class="string">'-o, --output [path]'</span>, <span class="string">'output to a given folder'</span>, c.output)
.option(<span class="string">'-c, --css [file]'</span>, <span class="string">'use a custom css file'</span>, c.css)
.option(<span class="string">'-t, --template [file]'</span>, <span class="string">'use a custom .jst template'</span>, c.template)
.option(<span class="string">'-e, --extension [ext]'</span>, <span class="string">'assume a file extension for all inputs'</span>, c.extension)
.parse(args)
.name = <span class="string">"docco"</span>
<span class="keyword">if</span> commander.args.length
document commander
<span class="keyword">else</span>
console.log commander.helpInformation()</pre></div>
<h2>Public API</h2>
<div class='highlight'><pre>Docco = module.exports = {run, document, parse, version}</pre></div>
<div class="fleur">h</div>
</div>
</div>
</body>
</html>