1818Rocco ::Markdown = RedcarpetCompat
1919
2020class Docurium
21- attr_accessor :branch , :output_dir , :data
21+ attr_accessor :branch , :output_dir , :data , :head_data
2222
2323 def initialize ( config_file , cli_options = { } , repo = nil )
2424 raise "You need to specify a config file" if !config_file
2525 raise "You need to specify a valid config file" if !valid_config ( config_file )
2626 @sigs = { }
27+ @head_data = nil
2728 @repo = repo || Rugged ::Repository . discover ( config_file )
2829 @cli_options = cli_options
2930 end
@@ -121,22 +122,7 @@ def generate_doc_for(version)
121122 data
122123 end
123124
124- def generate_docs
125- output_index = Rugged ::Index . new
126- write_site ( output_index )
127- @tf = File . expand_path ( File . join ( File . dirname ( __FILE__ ) , 'docurium' , 'layout.mustache' ) )
128- versions = get_versions
129- versions << 'HEAD'
130- # If the user specified versions, validate them and overwrite
131- if !( vers = @cli_options [ :for ] ) . empty?
132- vers . each do |v |
133- next if versions . include? ( v )
134- puts "Unknown version #{ v } "
135- exit ( false )
136- end
137- versions = vers
138- end
139-
125+ def process_project ( versions )
140126 nversions = versions . size
141127 output = Queue . new
142128 pipes = { }
@@ -159,7 +145,6 @@ def generate_docs
159145 end
160146
161147 print "Generating documentation [0/#{ nversions } ]\r "
162- head_data = nil
163148
164149 # This may seem odd, but we need to keep reading from the pipe or
165150 # the buffer will fill and they'll block and never exit. Therefore
@@ -180,27 +165,46 @@ def generate_docs
180165 # There's still some work we need to do serially
181166 tally_sigs! ( version , data )
182167 force_utf8 ( data )
183- sha = @repo . write ( data . to_json , :blob ) unless dry_run?
184-
185- print "Generating documentation [#{ i } /#{ nversions } ]\r "
186168
187169 # Store it so we can show it at the end
188- if version == 'HEAD'
189- head_data = data
170+ @head_data = data if version == 'HEAD'
171+
172+ yield i , nversions , version , data , examples if block_given?
173+ end
174+ end
175+
176+ def generate_docs ( options )
177+ output_index = Rugged ::Index . new
178+ write_site ( output_index )
179+ @tf = File . expand_path ( File . join ( File . dirname ( __FILE__ ) , 'docurium' , 'layout.mustache' ) )
180+ versions = get_versions
181+ versions << 'HEAD'
182+ # If the user specified versions, validate them and overwrite
183+ if !( vers = ( options [ :for ] || [ ] ) ) . empty?
184+ vers . each do |v |
185+ next if versions . include? ( v )
186+ puts "Unknown version #{ v } "
187+ exit ( false )
190188 end
189+ versions = vers
190+ end
191+
192+ process_project ( versions ) do |i , version , data , examples |
193+ @repo . write ( data . to_json , :blob )
194+
195+ print "Generating documentation [#{ i } /#{ versions . count } ]\r "
191196
192197 unless dry_run?
193198 output_index . add ( :path => "#{ version } .json" , :oid => sha , :mode => 0100644 )
194199 examples . each do |path , id |
195200 output_index . add ( :path => path , :oid => id , :mode => 0100644 )
196201 end
197202 end
203+ end
198204
199- if head_data
200- puts ''
201- show_warnings ( data )
202- end
203-
205+ if head_data
206+ puts ''
207+ show_warnings ( head_data )
204208 end
205209
206210 return if dry_run?
@@ -257,29 +261,122 @@ def force_utf8(data)
257261 end
258262 end
259263
260- def show_warnings ( data )
261- out '* checking your api'
264+ class Warning
265+ class UnmatchedParameter < Warning
266+ def initialize ( function , opts = { } )
267+ super :unmatched_param , :function , function , opts
268+ end
269+
270+ def _message ; "unmatched param" ; end
271+ end
272+
273+ class SignatureChanged < Warning
274+ def initialize ( function , opts = { } )
275+ super :signature_changed , :function , function , opts
276+ end
277+
278+ def _message ; "signature changed" ; end
279+ end
280+
281+ class MissingDocumentation < Warning
282+ def initialize ( type , identifier , opts = { } )
283+ super :missing_documentation , type , identifier , opts
284+ end
285+
286+ def _message
287+ [ "%s %s is missing documentation" , :type , :identifier ]
288+ end
289+ end
290+
291+ WARNINGS = [
292+ :unmatched_param ,
293+ :signature_changed ,
294+ :missing_documentation ,
295+ ]
296+
297+ attr_reader :warning , :type , :identifier , :file , :line , :column
298+
299+ def initialize ( warning , type , identifier , opts = { } )
300+ raise ArgumentError . new ( "invalid warning class" ) unless WARNINGS . include? ( warning )
301+ @warning = warning
302+ @type = type
303+ @identifier = identifier
304+ if type = opts . delete ( :type )
305+ @file = type [ :file ]
306+ if input_dir = opts . delete ( :input_dir )
307+ File . expand_path ( File . join ( input_dir , @file ) )
308+ end
309+ @file ||= "<missing>"
310+ @line = type [ :line ] || 1
311+ @column = type [ :column ] || 1
312+ end
313+ end
314+
315+ def message
316+ msg = self . _message
317+ msg . shift % msg . map { |a | self . send ( a ) . to_s } if msg . kind_of? ( Array )
318+ end
319+ end
320+
321+ def collect_warnings ( data )
322+ warnings = [ ]
323+ input_dir = File . join ( @project_dir , option_version ( "HEAD" , 'input' ) )
262324
263325 # check for unmatched paramaters
264- unmatched = [ ]
265326 data [ :functions ] . each do |f , fdata |
266- unmatched << f if fdata [ :comments ] =~ /@param/
267- end
268- if unmatched . size > 0
269- out ' - unmatched params in'
270- unmatched . sort . each { |p | out ( "\t " + p ) }
327+ warnings << Warning ::UnmatchedParameter . new ( f , type : fdata , input_dir : input_dir ) if fdata [ :comments ] =~ /@param/
271328 end
272329
273330 # check for changed signatures
274331 sigchanges = [ ]
275332 @sigs . each do |fun , sig_data |
276- if sig_data [ :changes ] [ 'HEAD' ]
277- sigchanges << fun
333+ warnings << Warning ::SignatureChanged . new ( fun ) if sig_data [ :changes ] [ 'HEAD' ]
334+ end
335+
336+ # check for undocumented things
337+ types = [ :functions , :callbacks , :globals , :types ]
338+ types . each do |type_id |
339+ under_type = type_id . tap { |t | break t . to_s [ 0 ..-2 ] . to_sym }
340+ data [ type_id ] . each do |ident , type |
341+ under_type = type [ :type ] if type_id == :types
342+
343+ warnings << Warning ::MissingDocumentation . new ( under_type , ident , type : type , input_dir : input_dir ) if type [ :description ] . empty?
344+
345+ case type [ :type ]
346+ when :struct
347+ if type [ :fields ]
348+ type [ :fields ] . each do |field |
349+ warnings << Warning ::MissingDocumentation . new ( :field , "#{ ident } .#{ field [ :name ] } " , type : type , input_dir : input_dir ) if field [ :comments ] . empty?
350+ end
351+ end
352+ end
278353 end
279354 end
280- if sigchanges . size > 0
281- out ' - signature changes in'
282- sigchanges . sort . each { |p | out ( "\t " + p ) }
355+ warnings
356+ end
357+
358+ def check_warnings ( options )
359+ versions = [ ]
360+ versions << get_versions . pop
361+ versions << 'HEAD'
362+
363+ process_project ( versions )
364+
365+ collect_warnings ( head_data ) . each do |warning |
366+ puts "#{ warning . file } :#{ warning . line } :#{ warning . column } : #{ warning . message } "
367+ end
368+ end
369+
370+ def show_warnings ( data )
371+ out '* checking your api'
372+
373+ collect_warnings ( data ) . group_by { |w | w . warning } . each do |klass , klass_warnings |
374+ klass_warnings . group_by { |w | w . type } . each do |type , type_warnings |
375+ out " - " + type_warnings [ 0 ] . message
376+ type_warnings . sort_by { |w | w . identifier } . each do |warning |
377+ out "\t " + warning . identifier
378+ end
379+ end
283380 end
284381 end
285382
0 commit comments