docurium 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,3 +1,3 @@
1
1
  class Docurium
2
- Version = VERSION = '0.3.2'
2
+ Version = VERSION = '0.4.0'
3
3
  end
@@ -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
@@ -254,3 +254,7 @@ p.functionList a.introd {
254
254
  .changelog li.deletes {
255
255
  color: #933;
256
256
  }
257
+
258
+ .type-comment {
259
+ padding-left: 3em;
260
+ }
@@ -186,16 +186,7 @@
186
186
  <% }) %> <!-- loop through the groups -->
187
187
  </script>
188
188
 
189
- <!-- listing for a particular type -->
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 -->
@@ -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
- this.set('data', {tname: tname, data: data, returns: returns, needs: needs, fileLink: fileLink})
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
- template: _.template($('#type-template').html()),
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 content = this.template(this.model.get('data'))
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, 'groups': data.groups})
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('groups')[func]
571
+ return this.get('data')['functions'][func]['group']
549
572
  },
550
573
 
551
574
  github_file: function(file, line, lineto) {