docurium 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +13 -0
- data/Gemfile +6 -0
- data/Rakefile +6 -0
- data/docurium.gemspec +5 -3
- data/lib/docurium.rb +186 -137
- data/lib/docurium/docparser.rb +292 -0
- data/lib/docurium/version.rb +1 -1
- data/lib/libdetect.rb +23 -0
- data/site/css/style.css +4 -0
- data/site/index.html +56 -10
- data/site/js/docurium.js +28 -5
- data/site/shared/css/documentation.css +1 -0
- data/test/fixtures/git2/errors.h +2 -0
- data/test/parser_test.rb +333 -46
- data/test/repo_test.rb +14 -6
- metadata +59 -44
@@ -0,0 +1,292 @@
|
|
1
|
+
require 'ffi/clang'
|
2
|
+
include FFI::Clang
|
3
|
+
|
4
|
+
class Docurium
|
5
|
+
class DocParser
|
6
|
+
# Entry point for this parser
|
7
|
+
# Parse `filename` out of the hash `files`
|
8
|
+
def parse_file(orig_filename, files)
|
9
|
+
|
10
|
+
# unfortunately Clang wants unsaved files to exist on disk, so
|
11
|
+
# we need to create at least empty files for each unsaved file
|
12
|
+
# we're given.
|
13
|
+
|
14
|
+
tmpdir = Dir.mktmpdir()
|
15
|
+
|
16
|
+
unsaved = files.map do |name, contents|
|
17
|
+
full_path = File.join(tmpdir, name)
|
18
|
+
dirname = File.dirname(full_path)
|
19
|
+
Dir.mkdir(dirname) unless Dir.exists? dirname
|
20
|
+
File.new(full_path, File::CREAT).close()
|
21
|
+
|
22
|
+
UnsavedFile.new(full_path, contents)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Override the path we want to filter by
|
26
|
+
filename = File.join(tmpdir, orig_filename)
|
27
|
+
tu = Index.new.parse_translation_unit(filename, [], unsaved, {:detailed_preprocessing_record => 1})
|
28
|
+
|
29
|
+
FileUtils.remove_entry(tmpdir)
|
30
|
+
|
31
|
+
cursor = tu.cursor
|
32
|
+
|
33
|
+
recs = []
|
34
|
+
|
35
|
+
cursor.visit_children do |cursor, parent|
|
36
|
+
#puts "visiting #{cursor.kind} - #{cursor.spelling}"
|
37
|
+
location = cursor.location
|
38
|
+
next :continue if location.file == nil
|
39
|
+
next :continue unless location.file == filename
|
40
|
+
|
41
|
+
#puts "for file #{location.file} #{cursor.kind} #{cursor.spelling} #{cursor.comment.kind} #{location.line}"
|
42
|
+
#cursor.visit_children do |c|
|
43
|
+
# puts " child #{c.kind}, #{c.spelling}, #{c.comment.kind}"
|
44
|
+
# :continue
|
45
|
+
#end
|
46
|
+
|
47
|
+
next :continue if cursor.comment.kind == :comment_null
|
48
|
+
next :continue if cursor.spelling == ""
|
49
|
+
|
50
|
+
extent = cursor.extent
|
51
|
+
rec = {
|
52
|
+
:file => orig_filename,
|
53
|
+
:line => extent.start.line,
|
54
|
+
:lineto => extent.end.line,
|
55
|
+
:tdef => nil,
|
56
|
+
}
|
57
|
+
|
58
|
+
case cursor.kind
|
59
|
+
when :cursor_function
|
60
|
+
#puts "have function"
|
61
|
+
rec.merge! extract_function(cursor)
|
62
|
+
when :cursor_enum_decl
|
63
|
+
rec.merge! extract_enum(cursor)
|
64
|
+
when :cursor_struct
|
65
|
+
#puts "raw struct"
|
66
|
+
rec.merge! extract_struct(cursor)
|
67
|
+
when :cursor_typedef_decl
|
68
|
+
rec.merge! extract_typedef(cursor)
|
69
|
+
else
|
70
|
+
raise "No idea how to deal with #{cursor.kind}"
|
71
|
+
end
|
72
|
+
|
73
|
+
recs << rec
|
74
|
+
:continue
|
75
|
+
end
|
76
|
+
|
77
|
+
recs
|
78
|
+
end
|
79
|
+
|
80
|
+
def extract_typedef(cursor)
|
81
|
+
child = nil
|
82
|
+
cursor.visit_children { |c| child = c; :break }
|
83
|
+
rec = {
|
84
|
+
:name => cursor.spelling,
|
85
|
+
:underlying_type => cursor.underlying_type.spelling,
|
86
|
+
:tdef => :typedef,
|
87
|
+
}
|
88
|
+
|
89
|
+
if not child
|
90
|
+
return rec
|
91
|
+
end
|
92
|
+
|
93
|
+
#puts "have typedef #{child.kind}, #{cursor.extent.start.line}"
|
94
|
+
case child.kind
|
95
|
+
when :cursor_type_ref
|
96
|
+
#puts "pure typedef, #{cursor.spelling}"
|
97
|
+
if child.type.kind == :type_record
|
98
|
+
rec[:type] = :struct
|
99
|
+
subject, desc = extract_subject_desc(cursor.comment)
|
100
|
+
rec[:decl] = cursor.spelling
|
101
|
+
rec[:description] = subject
|
102
|
+
rec[:comments] = desc
|
103
|
+
else
|
104
|
+
raise "typede of unhandled #{child.type.kind}"
|
105
|
+
end
|
106
|
+
when :cursor_enum_decl
|
107
|
+
rec.merge! extract_enum(child)
|
108
|
+
when :cursor_struct
|
109
|
+
#puts "typed struct, #{cursor.spelling}"
|
110
|
+
rec.merge! extract_struct(child)
|
111
|
+
when :cursor_parm_decl
|
112
|
+
#puts "have parm #{cursor.spelling}, #{cursor.display_name}"
|
113
|
+
subject, desc = extract_subject_desc(cursor.comment)
|
114
|
+
rec[:decl] = cursor.spelling
|
115
|
+
rec[:description] = subject
|
116
|
+
rec[:comments] = desc
|
117
|
+
when :cursor_type_ref
|
118
|
+
rec[:decl] = cursor.spelling
|
119
|
+
else
|
120
|
+
raise "No idea how to handle #{child.kind}"
|
121
|
+
end
|
122
|
+
# let's make sure we override the empty name the extract
|
123
|
+
# functions stored
|
124
|
+
rec[:name] = cursor.spelling
|
125
|
+
rec
|
126
|
+
end
|
127
|
+
|
128
|
+
def extract_subject_desc(comment)
|
129
|
+
subject = comment.child.text
|
130
|
+
desc = (comment.find_all { |cmt| cmt.kind == :comment_paragraph }).drop(1).map(&:text).join("\n\n")
|
131
|
+
return subject, desc
|
132
|
+
end
|
133
|
+
|
134
|
+
def extract_function(cursor)
|
135
|
+
comment = cursor.comment
|
136
|
+
|
137
|
+
#puts "looking at function #{cursor.spelling}, #{cursor.display_name}"
|
138
|
+
cmt = extract_function_comment(comment)
|
139
|
+
|
140
|
+
# We only want to look at parm_decl to avoid looking at a return
|
141
|
+
# struct as a parameter
|
142
|
+
args = children(cursor)
|
143
|
+
.select {|c| c.kind == :cursor_parm_decl }
|
144
|
+
.map do |arg|
|
145
|
+
{
|
146
|
+
:name => arg.display_name,
|
147
|
+
:type => arg.type.spelling,
|
148
|
+
:comment => cmt[:args][arg.display_name],
|
149
|
+
}
|
150
|
+
end
|
151
|
+
#args = args.reject { |arg| arg[:comment].nil? }
|
152
|
+
|
153
|
+
ret = {
|
154
|
+
:type => cursor.result_type.spelling,
|
155
|
+
:comment => cmt[:return]
|
156
|
+
}
|
157
|
+
|
158
|
+
# generate function signature
|
159
|
+
sig = args.map { |a| a[:type].to_s }.join('::')
|
160
|
+
|
161
|
+
argline = args.map { |a|
|
162
|
+
# pointers don't have a separation between '*' and the name
|
163
|
+
if a[:type].end_with? "*"
|
164
|
+
"#{a[:type]}#{a[:name]}"
|
165
|
+
else
|
166
|
+
"#{a[:type]} #{a[:name]}"
|
167
|
+
end
|
168
|
+
}.join(', ')
|
169
|
+
|
170
|
+
decl = "#{ret[:type]} #{cursor.spelling}(#{argline})"
|
171
|
+
body = "#{decl};"
|
172
|
+
|
173
|
+
#puts cursor.display_name
|
174
|
+
# Return the format that docurium expects
|
175
|
+
{
|
176
|
+
:type => :function,
|
177
|
+
:name => cursor.spelling,
|
178
|
+
:body => body,
|
179
|
+
:description => cmt[:description],
|
180
|
+
:comments => cmt[:comments],
|
181
|
+
:sig => sig,
|
182
|
+
:args => args,
|
183
|
+
:return => ret,
|
184
|
+
:decl => decl,
|
185
|
+
:argline => argline
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
def extract_function_comment(comment)
|
190
|
+
subject, desc = extract_subject_desc(comment)
|
191
|
+
|
192
|
+
args = {}
|
193
|
+
(comment.find_all { |cmt| cmt.kind == :comment_param_command }).each do |param|
|
194
|
+
args[param.name] = param.comment.strip
|
195
|
+
end
|
196
|
+
|
197
|
+
ret = nil
|
198
|
+
comment.each do |block|
|
199
|
+
next unless block.kind == :comment_block_command
|
200
|
+
next unless block.name == "return"
|
201
|
+
|
202
|
+
ret = block.paragraph.text
|
203
|
+
|
204
|
+
break
|
205
|
+
end
|
206
|
+
|
207
|
+
{
|
208
|
+
:description => subject,
|
209
|
+
:comments => desc,
|
210
|
+
:args => args,
|
211
|
+
:return => ret,
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
def extract_fields(cursor)
|
216
|
+
fields = []
|
217
|
+
cursor.visit_children do |cchild, cparent|
|
218
|
+
field = {
|
219
|
+
:type => cchild.type.spelling,
|
220
|
+
:name => cchild.spelling,
|
221
|
+
:comments => cchild.comment.find_all {|c| c.kind == :comment_paragraph }.map(&:text).join("\n\n")
|
222
|
+
}
|
223
|
+
|
224
|
+
if cursor.kind == :cursor_enum_decl
|
225
|
+
field.merge!({:value => cchild.enum_value})
|
226
|
+
end
|
227
|
+
|
228
|
+
fields << field
|
229
|
+
:continue
|
230
|
+
end
|
231
|
+
|
232
|
+
fields
|
233
|
+
end
|
234
|
+
|
235
|
+
def extract_enum(cursor)
|
236
|
+
subject, desc = extract_subject_desc(cursor.comment)
|
237
|
+
|
238
|
+
decl = []
|
239
|
+
cursor.visit_children do |cchild, cparent|
|
240
|
+
decl << cchild.spelling
|
241
|
+
:continue
|
242
|
+
end
|
243
|
+
|
244
|
+
block = decl.join("\n")
|
245
|
+
#return the docurium object
|
246
|
+
{
|
247
|
+
:type => :enum,
|
248
|
+
:name => cursor.spelling,
|
249
|
+
:description => subject,
|
250
|
+
:comments => desc,
|
251
|
+
:fields => extract_fields(cursor),
|
252
|
+
:block => block,
|
253
|
+
:decl => decl,
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
def extract_struct(cursor)
|
258
|
+
subject, desc = extract_subject_desc(cursor.comment)
|
259
|
+
|
260
|
+
values = []
|
261
|
+
cursor.visit_children do |cchild, cparent|
|
262
|
+
values << "#{cchild.type.spelling} #{cchild.spelling}"
|
263
|
+
:continue
|
264
|
+
end
|
265
|
+
|
266
|
+
#puts "struct value #{values}"
|
267
|
+
|
268
|
+
rec = {
|
269
|
+
:type => :struct,
|
270
|
+
:name => cursor.spelling,
|
271
|
+
:description => subject,
|
272
|
+
:comments => desc,
|
273
|
+
:fields => extract_fields(cursor),
|
274
|
+
:decl => values,
|
275
|
+
}
|
276
|
+
|
277
|
+
rec[:block] = values.join("\n") unless values.empty?
|
278
|
+
rec
|
279
|
+
end
|
280
|
+
|
281
|
+
def children(cursor)
|
282
|
+
list = []
|
283
|
+
cursor.visit_children do |ccursor, cparent|
|
284
|
+
list << ccursor
|
285
|
+
:continue
|
286
|
+
end
|
287
|
+
|
288
|
+
list
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
end
|
data/lib/docurium/version.rb
CHANGED
data/lib/libdetect.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'mkmf'
|
3
|
+
|
4
|
+
# This is pretty terrible, but we need to have the environment set up
|
5
|
+
# before we require the ffi-clang module.
|
6
|
+
|
7
|
+
module LibDetect
|
8
|
+
|
9
|
+
DARWIN_LIBCLANG = '/Library/Developer/CommandLineTools/usr/lib/libclang.dylib'
|
10
|
+
|
11
|
+
host_os = RbConfig::CONFIG['host_os']
|
12
|
+
case host_os
|
13
|
+
when /darwin/
|
14
|
+
File.exist? DARWIN_LIBCLANG
|
15
|
+
ENV['LIBCLANG'] = DARWIN_LIBCLANG
|
16
|
+
when /linux/
|
17
|
+
prog = 'llvm-config-3.4'
|
18
|
+
if find_executable(prog)
|
19
|
+
ENV['LLVM_CONFIG'] = prog
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/site/css/style.css
CHANGED
data/site/index.html
CHANGED
@@ -186,16 +186,7 @@
|
|
186
186
|
<% }) %> <!-- loop through the groups -->
|
187
187
|
</script>
|
188
188
|
|
189
|
-
|
190
|
-
<script type="text/template" id="type-template">
|
191
|
-
<h1 class="funcTitle"><%= tname %><small><%= data.type %></small></h1>
|
192
|
-
<p><%= data.value %></p>
|
193
|
-
<% if (data.comments) { %>
|
194
|
-
<div><%= data.comments %></div>
|
195
|
-
<% } %>
|
196
|
-
<% if (data.block) { %>
|
197
|
-
<pre><%= data.block %></pre>
|
198
|
-
<% } %>
|
189
|
+
<script type="text/template" id="uses-template">
|
199
190
|
<% if (returns.length > 0) { %>
|
200
191
|
<h3>Returns</h3>
|
201
192
|
<% _.each(_.initial(returns), function(fun) { %>
|
@@ -220,6 +211,61 @@
|
|
220
211
|
</div>
|
221
212
|
</script>
|
222
213
|
|
214
|
+
<!-- listing for an enum -->
|
215
|
+
<script type="text/template" id="enum-template">
|
216
|
+
<h1 class="funcTitle"><%= type.tname %><small><%= type.type %></small></h1>
|
217
|
+
<p><%= type.value %></p>
|
218
|
+
|
219
|
+
<% if (type.data.description) { %>
|
220
|
+
<p><%= type.data.description %></p>
|
221
|
+
<% } %>
|
222
|
+
<% if (type.data.comments) { %>
|
223
|
+
<p><%= type.data.comments %></p>
|
224
|
+
<% } %>
|
225
|
+
|
226
|
+
<table>
|
227
|
+
<% _.each(type.data.fields, function(field) { %>
|
228
|
+
<tr>
|
229
|
+
<td><code><%= field.name %></code></td>
|
230
|
+
<tr>
|
231
|
+
<% if (field.comments) { %>
|
232
|
+
<tr>
|
233
|
+
<td class="type-comment"><%= field.comments %></td>
|
234
|
+
</tr>
|
235
|
+
<% } %>
|
236
|
+
<% }) %>
|
237
|
+
</table>
|
238
|
+
<%= uses %>
|
239
|
+
</script>
|
240
|
+
<!-- listing for a struct -->
|
241
|
+
<script type="text/template" id="struct-template">
|
242
|
+
<h1 class="funcTitle"><%= type.tname %><small><%= type.type %></small></h1>
|
243
|
+
<p><%= type.value %></p>
|
244
|
+
|
245
|
+
<% if (type.data.description) { %>
|
246
|
+
<p><%= type.data.description %></p>
|
247
|
+
<% } %>
|
248
|
+
<% if (type.data.comments) { %>
|
249
|
+
<p><%= type.data.comments %></p>
|
250
|
+
<% } %>
|
251
|
+
|
252
|
+
<table>
|
253
|
+
<% _.each(type.data.fields, function(field) { %>
|
254
|
+
<tr>
|
255
|
+
<td><code><%= field.type %></code></td>
|
256
|
+
<td><code><%= field.name %></code></td>
|
257
|
+
<tr>
|
258
|
+
<% if (field.comments) { %>
|
259
|
+
<tr>
|
260
|
+
<td colspan="2" class="type-comment"><%= field.comments %></td>
|
261
|
+
</tr>
|
262
|
+
<% } %>
|
263
|
+
<% }) %>
|
264
|
+
</table>
|
265
|
+
|
266
|
+
<%= uses %>
|
267
|
+
</script>
|
268
|
+
|
223
269
|
<script type="text/template" id="group-template">
|
224
270
|
<h1><%= gname %> functions</h1>
|
225
271
|
<!-- table with all the functions -->
|
data/site/js/docurium.js
CHANGED
@@ -337,15 +337,38 @@ $(function() {
|
|
337
337
|
var needs = _.map(data.used.needs, toPair, docurium)
|
338
338
|
var fileLink = {name: data.file, url: docurium.github_file(data.file, data.line, data.lineto)}
|
339
339
|
|
340
|
-
|
340
|
+
// so it doesn't look crap, we build up a block with fields
|
341
|
+
// without a comment
|
342
|
+
var had_comment = false
|
343
|
+
var blocks = []
|
344
|
+
var tmp = []
|
345
|
+
_.each(data.fields, function(f) {
|
346
|
+
if (had_comment) {
|
347
|
+
blocks.push(tmp)
|
348
|
+
tmp = []
|
349
|
+
}
|
350
|
+
|
351
|
+
tmp.push(f)
|
352
|
+
had_comment = f.comments
|
353
|
+
})
|
354
|
+
blocks.push(tmp)
|
355
|
+
|
356
|
+
this.set('data', {tname: tname, data: data, blocks: blocks, returns: returns, needs: needs, fileLink: fileLink})
|
341
357
|
}
|
342
358
|
})
|
343
359
|
|
344
360
|
var TypeView = Backbone.View.extend({
|
345
|
-
|
361
|
+
enumTemplate: _.template($('#enum-template').html()),
|
362
|
+
structTemplate: _.template($('#struct-template').html()),
|
363
|
+
usesTemplate: _.template($('#uses-template').html()),
|
346
364
|
|
347
365
|
render: function() {
|
348
|
-
var
|
366
|
+
var type = this.model.get('data')
|
367
|
+
var uses = this.usesTemplate(type)
|
368
|
+
|
369
|
+
var template = type.data.type == 'struct' ? this.structTemplate : this.enumTemplate
|
370
|
+
var content = template({type: type, uses: uses})
|
371
|
+
|
349
372
|
this.el = content
|
350
373
|
return this
|
351
374
|
}
|
@@ -503,7 +526,7 @@ $(function() {
|
|
503
526
|
|
504
527
|
loadVersions: function() {
|
505
528
|
$.getJSON("project.json").then(function(data) {
|
506
|
-
docurium.set({'versions': data.versions, 'github': data.github, 'signatures': data.signatures, 'name': data.name
|
529
|
+
docurium.set({'versions': data.versions, 'github': data.github, 'signatures': data.signatures, 'name': data.name})
|
507
530
|
docurium.setVersion()
|
508
531
|
})
|
509
532
|
},
|
@@ -545,7 +568,7 @@ $(function() {
|
|
545
568
|
},
|
546
569
|
|
547
570
|
groupOf: function (func) {
|
548
|
-
return this.get('
|
571
|
+
return this.get('data')['functions'][func]['group']
|
549
572
|
},
|
550
573
|
|
551
574
|
github_file: function(file, line, lineto) {
|