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.
@@ -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) {