|
1 #!/usr/bin/env python |
|
2 """ |
|
3 Parser for Javascript comments. |
|
4 """ |
|
5 from __future__ import with_statement |
|
6 |
|
7 import sys, os, getopt, re |
|
8 |
|
9 def clean_comment(match): |
|
10 comment = match.group() |
|
11 comment = strip_stars(comment) |
|
12 return comment |
|
13 |
|
14 # Rest utilities |
|
15 def rest_title(title, level, level_markups=['=', '=', '-', '~', '+', '`']): |
|
16 size = len(title) |
|
17 if level == 0: |
|
18 return '\n'.join((level_markups[level] * size, title, level_markups[0] * size)) + '\n' |
|
19 return '\n'.join(('\n' + title, level_markups[level] * size)) + '\n' |
|
20 |
|
21 def get_doc_comments(text): |
|
22 """ |
|
23 Return a list of all documentation comments in the file text. Each |
|
24 comment is a pair, with the first element being the comment text and |
|
25 the second element being the line after it, which may be needed to |
|
26 guess function & arguments. |
|
27 |
|
28 >>> get_doc_comments(read_file('examples/module.js'))[0][0][:40] |
|
29 '/**\n * This is the module documentation.' |
|
30 >>> get_doc_comments(read_file('examples/module.js'))[1][0][7:50] |
|
31 'This is documentation for the first method.' |
|
32 >>> get_doc_comments(read_file('examples/module.js'))[1][1] |
|
33 'function the_first_function(arg1, arg2) ' |
|
34 >>> get_doc_comments(read_file('examples/module.js'))[2][0] |
|
35 '/** This is the documentation for the second function. */' |
|
36 |
|
37 """ |
|
38 return [clean_comment(match) for match in re.finditer('/\*\*.*?\*/', |
|
39 text, re.DOTALL|re.MULTILINE)] |
|
40 |
|
41 RE_STARS = re.compile('^\s*?\* ?', re.MULTILINE) |
|
42 |
|
43 |
|
44 def strip_stars(doc_comment): |
|
45 """ |
|
46 Strip leading stars from a doc comment. |
|
47 |
|
48 >>> strip_stars('/** This is a comment. */') |
|
49 'This is a comment.' |
|
50 >>> strip_stars('/**\n * This is a\n * multiline comment. */') |
|
51 'This is a\n multiline comment.' |
|
52 >>> strip_stars('/** \n\t * This is a\n\t * multiline comment. \n*/') |
|
53 'This is a\n multiline comment.' |
|
54 |
|
55 """ |
|
56 return RE_STARS.sub('', doc_comment[3:-2]).strip() |
|
57 |
|
58 def parse_js_files(args=sys.argv): |
|
59 """ |
|
60 Main command-line invocation. |
|
61 """ |
|
62 try: |
|
63 opts, args = getopt.gnu_getopt(args[1:], 'p:o:h', [ |
|
64 'jspath=', 'output=', 'help']) |
|
65 opts = dict(opts) |
|
66 except getopt.GetoptError: |
|
67 usage() |
|
68 sys.exit(2) |
|
69 |
|
70 rst_dir = opts.get('--output') or opts.get('-o') |
|
71 if rst_dir is None and len(args) != 1: |
|
72 rst_dir = 'apidocs' |
|
73 js_dir = opts.get('--jspath') or opts.get('-p') |
|
74 if not os.path.exists(os.path.join(rst_dir)): |
|
75 os.makedirs(os.path.join(rst_dir)) |
|
76 |
|
77 f_index = open(os.path.join(rst_dir, 'index.rst'), 'wb') |
|
78 f_index.write(''' |
|
79 .. toctree:: |
|
80 :maxdepth: 1 |
|
81 |
|
82 ''' |
|
83 ) |
|
84 for js_path, js_dirs, js_files in os.walk(js_dir): |
|
85 rst_path = re.sub('%s%s*' % (js_dir, os.path.sep), '', js_path) |
|
86 for js_file in js_files: |
|
87 if not js_file.endswith('.js'): |
|
88 continue |
|
89 if not os.path.exists(os.path.join(rst_dir, rst_path)): |
|
90 os.makedirs(os.path.join(rst_dir, rst_path)) |
|
91 rst_content = extract_rest(js_path, js_file) |
|
92 filename = os.path.join(rst_path, js_file[:-3]) |
|
93 # add to index |
|
94 f_index.write(' %s\n' % filename) |
|
95 # save rst file |
|
96 with open(os.path.join(rst_dir, filename) + '.rst', 'wb') as f_rst: |
|
97 f_rst.write(rst_content) |
|
98 f_index.close() |
|
99 |
|
100 def extract_rest(js_dir, js_file): |
|
101 js_filepath = os.path.join(js_dir, js_file) |
|
102 filecontent = open(js_filepath, 'U').read() |
|
103 comments = get_doc_comments(filecontent) |
|
104 rst = rest_title(js_file, 0) |
|
105 rst += '.. module:: %s\n\n' % js_file |
|
106 rst += '\n\n'.join(comments) |
|
107 return rst |
|
108 |
|
109 if __name__ == '__main__': |
|
110 parse_js_files() |