docurium 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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) {
|