diff options
| author | Joshua Peek <josh@joshpeek.com> | 2012-08-03 15:47:50 -0500 |
|---|---|---|
| committer | Joshua Peek <josh@joshpeek.com> | 2012-08-03 15:47:50 -0500 |
| commit | 41b7d13aa7541bc03b3bfd4e3eda36f6cde9cca1 (patch) | |
| tree | 74a8b56a5cb2dbe7971797ec265d2b9338d78632 /lib | |
| parent | 453110303393f46a046ae13c7c2b260b2ed52705 (diff) | |
Extract generated blob check into its own module
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/linguist.rb | 1 | ||||
| -rw-r--r-- | lib/linguist/blob_helper.rb | 132 | ||||
| -rw-r--r-- | lib/linguist/generated.rb | 161 |
3 files changed, 165 insertions, 129 deletions
diff --git a/lib/linguist.rb b/lib/linguist.rb index 36702e3..0f1b0f8 100644 --- a/lib/linguist.rb +++ b/lib/linguist.rb @@ -1,4 +1,5 @@ require 'linguist/blob_helper' +require 'linguist/generated' require 'linguist/language' require 'linguist/mime' require 'linguist/repository' diff --git a/lib/linguist/blob_helper.rb b/lib/linguist/blob_helper.rb index af0150c..d805df1 100644 --- a/lib/linguist/blob_helper.rb +++ b/lib/linguist/blob_helper.rb @@ -1,4 +1,5 @@ require 'linguist/classifier' +require 'linguist/generated' require 'linguist/language' require 'linguist/mime' require 'linguist/samples' @@ -212,143 +213,16 @@ module Linguist lines.grep(/\S/).size end - # Internal: Compute average line length. - # - # Returns Integer. - def average_line_length - if lines.any? - lines.inject(0) { |n, l| n += l.length } / lines.length - else - 0 - end - end - # Public: Is the blob a generated file? # # Generated source code is supressed in diffs and is ignored by # language statistics. # - # Requires Blob#data - # - # Includes: - # - XCode project XML files - # - Minified JavaScript - # - Compiled CoffeeScript - # - PEG.js-generated parsers - # - # Please add additional test coverage to - # `test/test_blob.rb#test_generated` if you make any changes. + # May load Blob#data # # Return true or false def generated? - if name == 'Gemfile.lock' || minified_javascript? || compiled_coffeescript? || - xcode_project_file? || generated_net_docfile? || generated_parser? - true - else - false - end - end - - # Internal: Is the blob an XCode project file? - # - # Generated if the file extension is an XCode project - # file extension. - # - # Returns true of false. - def xcode_project_file? - ['.xib', '.nib', '.storyboard', '.pbxproj', '.xcworkspacedata', '.xcuserstate'].include?(extname) - end - - # Internal: Is the blob minified JS? - # - # Consider JS minified if the average line length is - # greater then 100c. - # - # Returns true or false. - def minified_javascript? - return unless extname == '.js' - average_line_length > 100 - end - - # Internal: Is the blob of JS a parser generated by PEG.js? - # - # Requires Blob#data - # - # PEG.js-generated parsers are not meant to be consumed by humans. - # - # Return true or false - def generated_parser? - return false unless extname == '.js' - - # PEG.js-generated parsers include a comment near the top of the file - # that marks them as such. - if lines[0..4].join('') =~ /^(?:[^\/]|\/[^\*])*\/\*(?:[^\*]|\*[^\/])*Generated by PEG.js/ - return true - end - - false - end - - # Internal: Is the blob of JS generated by CoffeeScript? - # - # Requires Blob#data - # - # CoffeScript is meant to output JS that would be difficult to - # tell if it was generated or not. Look for a number of patterns - # output by the CS compiler. - # - # Return true or false - def compiled_coffeescript? - return false unless extname == '.js' - - # CoffeeScript generated by > 1.2 include a comment on the first line - if lines[0] =~ /^\/\/ Generated by / - return true - end - - if lines[0] == '(function() {' && # First line is module closure opening - lines[-2] == '}).call(this);' && # Second to last line closes module closure - lines[-1] == '' # Last line is blank - - score = 0 - - lines.each do |line| - if line =~ /var / - # Underscored temp vars are likely to be Coffee - score += 1 * line.gsub(/(_fn|_i|_len|_ref|_results)/).count - - # bind and extend functions are very Coffee specific - score += 3 * line.gsub(/(__bind|__extends|__hasProp|__indexOf|__slice)/).count - end - end - - # Require a score of 3. This is fairly arbitrary. Consider - # tweaking later. - score >= 3 - else - false - end - end - - # Internal: Is this a generated documentation file for a .NET assembly? - # - # Requires Blob#data - # - # .NET developers often check in the XML Intellisense file along with an - # assembly - however, these don't have a special extension, so we have to - # dig into the contents to determine if it's a docfile. Luckily, these files - # are extremely structured, so recognizing them is easy. - # - # Returns true or false - def generated_net_docfile? - return false unless extname.downcase == ".xml" - return false unless lines.count > 3 - - # .NET Docfiles always open with <doc> and their first tag is an - # <assembly> tag - return lines[1].include?("<doc>") && - lines[2].include?("<assembly>") && - lines[-2].include?("</doc>") + @_generated ||= Generated.generated?(name, lambda { data }) end # Public: Should the blob be indexed for searching? diff --git a/lib/linguist/generated.rb b/lib/linguist/generated.rb new file mode 100644 index 0000000..1b05e91 --- /dev/null +++ b/lib/linguist/generated.rb @@ -0,0 +1,161 @@ +module Linguist + class Generated + # Public: Is the blob a generated file? + # + # name - String filename + # data - String blob data. A block also maybe passed in for lazy + # loading. This behavior is deprecated and you should always + # pass in a String. + # + # Return true or false + def self.generated?(name, data) + new(name, data).generated? + end + + # Internal: Initialize Generated instance + # + # name - String filename + # data - String blob data + def initialize(name, data) + @name = name + @extname = File.extname(name) + @_data = data + end + + attr_reader :name, :extname + + # Lazy load blob data if block was passed in. + # + # Awful, awful stuff happening here. + # + # Returns String data. + def data + @data ||= @_data.respond_to?(:call) ? @_data.call() : @_data + end + + # Public: Get each line of data + # + # Returns an Array of lines + def lines + @lines ||= data.split("\n", -1) + end + + # Internal: Is the blob a generated file? + # + # Generated source code is supressed in diffs and is ignored by + # language statistics. + # + # Please add additional test coverage to + # `test/test_blob.rb#test_generated` if you make any changes. + # + # Return true or false + def generated? + name == 'Gemfile.lock' || + minified_javascript? || + compiled_coffeescript? || + xcode_project_file? || + generated_net_docfile? || + generated_parser? + end + + # Internal: Is the blob an XCode project file? + # + # Generated if the file extension is an XCode project + # file extension. + # + # Returns true of false. + def xcode_project_file? + ['.xib', '.nib', '.storyboard', '.pbxproj', '.xcworkspacedata', '.xcuserstate'].include?(extname) + end + + # Internal: Is the blob minified JS? + # + # Consider JS minified if the average line length is + # greater then 100c. + # + # Returns true or false. + def minified_javascript? + return unless extname == '.js' + if lines.any? + (lines.inject(0) { |n, l| n += l.length } / lines.length) > 100 + else + false + end + end + + # Internal: Is the blob of JS generated by CoffeeScript? + # + # CoffeScript is meant to output JS that would be difficult to + # tell if it was generated or not. Look for a number of patterns + # output by the CS compiler. + # + # Return true or false + def compiled_coffeescript? + return false unless extname == '.js' + + # CoffeeScript generated by > 1.2 include a comment on the first line + if lines[0] =~ /^\/\/ Generated by / + return true + end + + if lines[0] == '(function() {' && # First line is module closure opening + lines[-2] == '}).call(this);' && # Second to last line closes module closure + lines[-1] == '' # Last line is blank + + score = 0 + + lines.each do |line| + if line =~ /var / + # Underscored temp vars are likely to be Coffee + score += 1 * line.gsub(/(_fn|_i|_len|_ref|_results)/).count + + # bind and extend functions are very Coffee specific + score += 3 * line.gsub(/(__bind|__extends|__hasProp|__indexOf|__slice)/).count + end + end + + # Require a score of 3. This is fairly arbitrary. Consider + # tweaking later. + score >= 3 + else + false + end + end + + # Internal: Is this a generated documentation file for a .NET assembly? + # + # .NET developers often check in the XML Intellisense file along with an + # assembly - however, these don't have a special extension, so we have to + # dig into the contents to determine if it's a docfile. Luckily, these files + # are extremely structured, so recognizing them is easy. + # + # Returns true or false + def generated_net_docfile? + return false unless extname.downcase == ".xml" + return false unless lines.count > 3 + + # .NET Docfiles always open with <doc> and their first tag is an + # <assembly> tag + return lines[1].include?("<doc>") && + lines[2].include?("<assembly>") && + lines[-2].include?("</doc>") + end + + # Internal: Is the blob of JS a parser generated by PEG.js? + # + # PEG.js-generated parsers are not meant to be consumed by humans. + # + # Return true or false + def generated_parser? + return false unless extname == '.js' + + # PEG.js-generated parsers include a comment near the top of the file + # that marks them as such. + if lines[0..4].join('') =~ /^(?:[^\/]|\/[^\*])*\/\*(?:[^\*]|\*[^\/])*Generated by PEG.js/ + return true + end + + false + end + end +end |
