class Rake4LaTeX::TeX_Statistic
Get an overview on status of a generated TeX-Project.
-
Analoyse log for
*LaTeX *BibTeX if used *makeindex if used
end¶ ↑
Constants
- Defaults_texmessages2hash
Define the defaults for
TeX_Statistic#texmessages2hash
end¶ ↑
- FILEDATA_STRUCT
Container for some data
-
count: numbers of open ( in file, needed to detect end of file
-
logcontent: Extract of the log-file, contains only the parts for the actual source file
-
logstart: Line in log-file
-
- PAGE_PATTERN
Regexp to detect new pages in result file. New pages looks like ā[1.1nā or [9.9]
Attributes
Hash with a status overview on the logs of the TeX-project.
Array with TeXMessage-object. Contains messages of the TeX-log.
Public Class Methods
Define the main file for the log-analyse.
# File lib/rake4latex/tex_statistic.rb, line 93 def initialize( filename ) @stat = {} @texmessages = [] @texfile = filename.ext('tex') if ! File.exist?(@texfile) @stat['_'] ||= {} @stat['_'][:tex] = "No TeX File #{@texfile} found" end #Analyse TeX-log if File.exist?(@texfile.ext('log')) @texmessages = log_analyse_content( File.read(@texfile.ext('log')),nil,nil) @stat[@texfile.ext('log')] = texmessages2hash(:messages => @texmessages) #option , :page_info ?? else @stat[@texfile.ext('log')] ||= { :errors => "No log-file #{@texfile.ext('log')} found" } end #File.exist?(@texfile.ext('log')) #Analyse BibTeX-log if File.exist?(@texfile.ext('blg')) @stat[@texfile.ext('blg')] = blg_analyse(@texfile.ext('blg')) end #File.exist?(@texfile.ext('log')) #Analyse index-logs (multiple possible with splitindex) #Example for dirpattern: testdokument{-*,}.ilg # #Missing: Logs from glossaries.sty Dir["#{@texfile.ext}{-*,}".ext('ilg')].each{|ilg_filename| @stat[ilg_filename] = ilg_analyse(ilg_filename) } end
Public Instance Methods
Analyse a bibTeX-log.
end¶ ↑
# File lib/rake4latex/tex_statistic.rb, line 315 def blg_analyse(logfilename) stat = {} File.readlines(logfilename).each{ |logline| case logline when /Database file #(.*): (.*)/ stat[:source_information] ||= [] << "Database file #{$2} used" when /Warning--I didn't find a database entry for "(.*)"/ ( stat[:warnings] ||= [] ) << "Databaseentry #{$1} missing" when /Warning--Empty definition in (.*)/ ( stat[:warnings] ||= [] ) << "Empty definition #{$1}" when /Warning--(.*)/ ( stat[:warnings] ||= [] ) << "#{$1}" when /I couldn't open (.*) file (.*)/ ( stat[:errors] ||= [] ) << "#{$1} #{$2} not found" when /I found no (.*) commands---while reading file(.*)/ ( stat[:warnings] ||= [] ) << "found no #{$1} in #{$2}" when /(.*)---line (.*) of file(.*)/ #line-number ist unsinnig ( stat[:errors] ||= [] ) << "#{$1} in #{$3}" end } stat end
Analyse the makeindex-log.
end¶ ↑
# File lib/rake4latex/tex_statistic.rb, line 341 def ilg_analyse(logfilename) stat = {} error = nil File.readlines(logfilename).each{ |logline| if error #Index error announced in previous line ( stat[:errors] ||= [] ) << "#{logline.chomp.sub(/.*--/, "")} #{error}" error = nil else case logline when /Scanning input file (.*)\...done \((.*) entries accepted, (.*) rejected\)./ #~ stat[:source_information] ||= [] << "input file #{$1} (#{$2} entries accepted, #{$3} rejected)" stat[:source_information] ||= [] stat[:source_information] << "Input file: #{$1}" stat[:source_information] << "Entries accepted: #{$2}" stat[:source_information] << "Entries rejected: #{$3}" #~ when /done \((.*) entries accepted, (.*) rejected\)./ #~ result[:rejected] += $2.to_i() if $2.to_i() > 0 when /!! Input index error \(file = (.*), line = (.*)\):/ #Error-message on next line error = "(file #{$1} line #{$2}: #{logline})" end end #if error } stat end
Analyse the TeX-log file.
The log-text is splitted into files and pages and analysed separatly.
# File lib/rake4latex/tex_statistic.rb, line 148 def log_analyse_by_file(logfilename) if ! File.exist?(logfilename) return [ TeXMessage.new(:latex_errors, nil, nil, "No log-file #{logfilename} found", {}, 0, logfilename) ] end log = File.read(logfilename) stat = {} texmessages = [] #Analyse log for page and source file page = nil #store the actual page file = nil #store the actual file filestack = [] # filelist = { nil => FILEDATA_STRUCT.new(nil, 0, 0, 1, '' ) } logline = 0 #~ filecount = Hash.new(0) #numbers of open ( in file, needed to detect end of file #~ filestartpage = {} #Page, where the source is started. #~ filecontent = {nil => ''}#content of a file. The content of sub-includes are not part of this content. # inputs looks like "(./filename_en.toc \n" or "(./filename.lot)" file_pattern = %r{\((?:\.|c:)\/.*?[\)\n]} #Splitt at the file- and page-patterns and at ( and ) #If the ( ) are balanced, then we can detect a file end with ) # log.split(/(#{file_pattern}|\(|\)|#{PAGE_PATTERN})/ ).each{|x| logline += x.count("\n") case x #x[0] when file_pattern if x[-1,1] == ')' #~ puts "open and close #{file} on page #{page} #{filestack.inspect}" if file =~ /^\./ filelist[file].logcontent << x else filelist[file].logcontent << x file = x.strip.sub(/\(/,'') filestack << file filelist[file] = FILEDATA_STRUCT.new(file, page, logline, 1, '' ) #~ puts "open #{file} on page #{page} #{filestack.inspect}" if file =~ /^\./ end when PAGE_PATTERN page = $1 #~ if x =~ /76/ #~ puts x.inspect #~ puts page.inspect #~ puts logline #~ puts file #~ end filelist[file].logcontent << x #Add page to all open log collection. #Without this, we would ignore all pages between the insertion of an include. filelist.each{| name, filedata | filedata.logcontent << "\n#{x} ##ADDED to log #{logline + 1}##\n" } when '(' filelist[file].count+= 1 filelist[file].logcontent << x when ')' filelist[file].count = filelist[file].count - 1 if filelist[file].count == 0 filestack.pop #~ puts "close #{file} on page #{page} #{filestack.inspect}" if file =~ /^\./ texmessages.concat(log_analyse_content(filelist[file].logcontent, filelist[file].name, filelist[file].startpage, filelist[file].logstart )) if ! stat.empty? #hier stats sammeln (je vor/hinter input gab es meldungen) #~ puts '============' #~ puts file #~ puts stat.to_yaml end #~ filecontent.delete(file) filelist[file].logcontent = '' file = filestack.last else filelist[file].logcontent << x end else filelist[file].logcontent << x end #~ puts "%6s : %s" % [page, file ] } #Get unclear messages (top file?) texmessages.concat(log_analyse_content(filelist[nil].logcontent, filelist[nil].name, filelist[nil].startpage, filelist[nil].logstart )) #~ puts filestack.inspect texmessages end
Analyse the content of an extract of the TeX-log file and collect the messages as TeXMessage-objects.
file, startpage and logstart are only needed for the splitted analyse to get the initial values.
end¶ ↑
# File lib/rake4latex/tex_statistic.rb, line 240 def log_analyse_content(log, file, startpage, logstart = 0) page = startpage messages =[] linecorrection = logstart + 1 get_next_lines = nil log.split("\n").each_with_index{|logline, loc_lineno| logline.strip! #Check if the message is continued on next line case get_next_lines when Numeric #Counter to add the next x lines to the previous message messages.last << logline if get_next_lines ==1 get_next_lines = nil else get_next_lines = get_next_lines - 1 end next when Regexp if logline =~ get_next_lines messages.last << $~.post_match messages.last.line = $1 if logline =~ /on input line (\d+)/ next else get_next_lines = nil end end lineno = loc_lineno + linecorrection case logline when PAGE_PATTERN page = $1 if logline =~ /##ADDED to log (\d+)##/ linecorrection = $1.to_i - loc_lineno - 1 #resync the log line number end when /Package (.*) Error: (.*)/ messages << TeXMessage.new(:package_errors, lineno, $2, {:package => $1 }, page, file) when /LaTeX Error:\s*(.*)/ messages << TeXMessage.new(:latex_errors, lineno, $1, {}, page, file) when /Error:/ messages << TeXMessage.new(:error, lineno, logline, {}, page, file) when /Package (.*) Warning: (.*)/ get_next_lines = /\(#{$1}\)\s*/ messages << TeXMessage.new(:package_warnings, lineno, $2, {:package => $1 }, page, file) when /LaTeX Warning:\s*(.*)/ messages << TeXMessage.new(:latex_warnings, lineno, $1, {}, page, file) when /Warning/ messages << TeXMessage.new(:warnings, lineno, logline, {}, page, file) when /Package (.*) Info: (.*)/ #A lot of messages messages << TeXMessage.new(:package_info, lineno, $2, {:package => $1 }, page, file) when /Output written on (.*) \((\d*) pages?, (\d*) bytes\)/ raise "Double output in #{logfilename}" if stat[:output] messages << TeXMessage.new(:output, lineno, "Output written on #{$1} (#{$2} pages)", { :name => $1, :pages => $2, :bytes => $3, :kbytes => ($3.to_i / 1024 )}, page, file) when /Overfull \\hbox \((.*) too wide\) in paragraph at lines (.*)/ #~ messages << TeXMessage.new(:overfull_boxes, lineno, "#{$1} at lines #{$2}", {:wide => $1.to_i }, page, file, $2) messages << TeXMessage.new(:overfull_boxes, lineno, logline, {:wide => $1.to_i }, page, file, $2) get_next_lines = 1 when /Underfull (.*box) \(badness (.*)\) (detected|in paragraph) at lines? (.*)/ #~ messages << TeXMessage.new(:underfull_boxes, lineno, "badness #{$2} at lines #{$4}", {:badness => $2.to_i }, page, file, $4) messages << TeXMessage.new(:underfull_boxes, lineno, logline, {:badness => $2.to_i }, page, file, $4) get_next_lines = 1 #oder bis / []/ #fixme... when /^!\s*(.*)/ #Undefined control sequence. messages << TeXMessage.new(:latex_errors, lineno, $1, {}, page, file) else #~ puts " #{logline}" end } messages end
Build a more complex overview. Contains:
-
Summary (method
stat_summary
) -
All errors and warnings from TeX and the tools.
-
A detailed analyses for the TeX-log (including some buge in the analyse).
end¶ ↑
# File lib/rake4latex/tex_statistic.rb, line 526 def overview() overview = [] overview << "Log-overview for #{@texfile}" overview << stat_summary.map{|e| " #{e}"} overview << stat.to_yaml #And the TeX-errors in details (buggy) overview << "========\n Detailed analyse of #{@texfile.ext('log')}\n========" overview << "###########\n##The following list may contain wrong line assignments\n###########" overview << texmessages2hash({ :messages => log_analyse_by_file(@texfile.ext('log')), :page_info => true, #add page information :loglineno_info => true, :nextlines_info => true, :source_info => true, #add source code information, only in log_analyse_by_file_page }).to_yaml overview.join("\n") end
Build a very quick overview on actual status.
Each tool gets one line.
Example:
- "Document: 14 errors, 15 warnings, 44 overfull boxes, 70 underfull boxes (testdocument.log)" - "Bibliography: 1 error (testdocument.blg)" - "Index: 21 entries (testdocument-changes.ilg)" - "Index: 38 entries (testdocument-BMEcat.ilg)" - "Index: 1 error (testdocument-idx.ilg)" - "Index: 139 entries (testdocument-Fields.ilg)"
end¶ ↑
# File lib/rake4latex/tex_statistic.rb, line 427 def stat_summary() result = [] @stat.each{|key, values| stat = {} values.each{|logkey,logvalues| stat[key] ||= [] case logkey when :source_information, :output logvalues.each{|mess| case mess when /Entries accepted: (\d*)/ stat[:ok] = $1.to_i unless $1 == '0' when /Entries rejected: (\d*)/ stat[:reject] = $1.to_i unless $1 == '0' when /Database file (.+) used/ ( stat[:source] ||= [] ) << $1 #~ when /Output written on .* \((\d*) pages, (\d*) bytes\)/ when /Output written on .* \((\d*) page/ stat[:pages] = $1.to_i when /Input file:/ else #~ puts mess.inspect end } when String #~ stat[key] = logkey puts logkey.inspect raise "?Undefined String-Handling in stat_overview" else #count messages stat[logkey] = logvalues.size end } #values #Build summary line summary = [] if stat[:pages] summary << "#{stat[:pages]} page" summary.last << "s" if stat[:pages] > 1 end if stat[:source] summary << "Used #{stat[:source].join(', ')}" end errors = (stat[:error] ? stat[:error] : 0 ) + (stat[:latex_errors] ? stat[:latex_errors] : 0 ) + (stat[:package_errors] ? stat[:package_errors] : 0 ) if errors > 0 summary << "#{errors} error" summary.last << 's' if errors > 1 end case stat[:ok] when 0, nil #no report when 1; summary << "#{stat[:ok]} entry" else; summary << "#{stat[:ok]} entries" end case stat[:reject] when 0, nil when 1; summary << "#{stat[:reject]} rejected entry" else; summary << "#{stat[:reject]} rejected entries" end #fixme: stat[:package_warnings] counts packages with warnings, not number of warnings warnings = (stat[:warnings] ? stat[:warnings] : 0 ) + (stat[:latex_warnings] ? stat[:latex_warnings] : 0 ) + (stat[:package_warnings] ? stat[:package_warnings] : 0 ) if warnings > 0 summary << "#{warnings} warning" summary.last << 's' if warnings > 1 end if stat[:overfull_boxes] summary << "#{stat[:overfull_boxes]} overfull box" summary.last << 'es' if stat[:overfull_boxes] > 1 end if stat[:underfull_boxes] summary << "#{stat[:underfull_boxes]} underfull box" summary.last << 'es' if stat[:underfull_boxes] > 1 end #Put summary to result-array #The result contains a sort-number, which is deleted in the end case key when /log\Z/; result << "%-13s: %s (%s)" % [ '1Document', summary.join(', '), key] when /blg\Z/; result << "%-13s: %s (%s)" % [ '2Bibliography', summary.join(', '), key] when /ilg\Z/; result << "%-13s: %s (%s)" % [ '3Index', summary.join(', '), key] else #fixme puts "#{__FILE__}##{__LINE__}: Unknown #{key}" result << "?? #{key}: #{summary.join(', ') }" end } #@stat #Retrun the result in the wanted order, but without the sort-number. result.sort.map{|e| e[1..-1] } end
Take messages and convert them into a hash for quick analyses.
Options is a hash with flags, which informations should be added. Defaults can be seen in Defaults_texmessages2hash
.
end¶ ↑
# File lib/rake4latex/tex_statistic.rb, line 384 def texmessages2hash( options ) options = Defaults_texmessages2hash.dup.merge(options) options[:messages] = @texmessages if options[:messages] == :texmessages #Return-Hash. #Collects the datas. stat = {} options[:messages].sort.each{|mess| text = [ mess.message ] if mess.source and options[:source_info] text << " (#{mess.source}#{mess.line ? ":#{mess.line}" : '' })" end text << " [page#{mess.page}]" if mess.page and options[:page_info] text << " [log##{mess.logline}]" if options[:loglineno_info] text << " - #{mess.nextlines}" if options[:nextlines_info] text = text.join() case mess.type when :package_info next when :package_warnings, :package_errors stat[mess.type] ||= {} (stat[mess.type][mess.details[:package]] ||= [] ) << text else ( stat[mess.type] ||= [] ) << text end } stat end