Permalink
Browse files

Add -C option to print matched lines with context.

  • Loading branch information...
1 parent 7802018 commit df07ba0cd87da0fb1904e65cbb32102f5d818eef @acrmp committed Jan 22, 2012
View
3 bin/foodcritic
@@ -3,6 +3,7 @@ require_relative '../lib/foodcritic'
module FoodCritic
cmd_line = CommandLine.new(ARGV)
review, status = Linter.check(cmd_line)
- SummaryOutput.new.output(review)
+ printer = cmd_line.show_context? ? ContextOutput.new : SummaryOutput.new
+ printer.output(review)
exit status.to_i
end
View
1 foodcritic.gemspec
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
s.add_dependency('nokogiri', '~> 1.5.0')
s.add_dependency('pry', '~> 0.9.7.4')
s.add_dependency('pry-doc', '~> 0.3.0')
+ s.add_dependency('rak', '~> 1.4')
s.files = Dir['lib/**/*.rb']
s.required_ruby_version = '>= 1.9.2'
end
View
1 lib/foodcritic.rb
@@ -1,5 +1,6 @@
require 'chef'
require 'pry'
+require 'rak'
require_relative 'foodcritic/command_line'
require_relative 'foodcritic/domain'
require_relative 'foodcritic/error_checker'
View
8 lib/foodcritic/command_line.rb
@@ -15,6 +15,7 @@ def initialize(args)
opts.on("-r", "--[no-]repl", "Drop into a REPL for interactive rule editing.") {|r|options[:repl] = r}
opts.on("-t", "--tags TAGS", "Only check against rules with the specified tags.") {|t|options[:tags] << t}
opts.on("-f", "--epic-fail TAGS", "Fail the build if any of the specified tags are matched.") {|t|options[:fail_tags] << t}
+ opts.on("-C", "--[no-]context", "Show lines matched against rather than the default summary.") {|c|options[:context] = c}
end
@parser.parse!(args) unless show_help?
end
@@ -47,6 +48,13 @@ def cookbook_path
@args[0]
end
+ # If matches should be shown with context rather than the default summary display.
+ #
+ # @return [Boolean] True if matches should be shown with context.
+ def show_context?
+ @options[:context]
+ end
+
# Parsed command-line options
#
# @return [Hash] The parsed command-line options.
View
6 lib/foodcritic/domain.rb
@@ -19,13 +19,15 @@ def initialize(rule, match={})
# The collected warnings (if any) raised against a cookbook tree.
class Review
- attr_reader :warnings
+ attr_reader :cookbook_path, :warnings
# Create a new review
#
+ # @param [String] cookbook_path The path this review was performed against
# @param [Array] warnings The warnings raised in this review
# @param [Boolean] is_failed Have warnings been raised that mean this should be considered failed?
- def initialize(warnings, is_failed)
+ def initialize(cookbook_path, warnings, is_failed)
+ @cookbook_path = cookbook_path
@warnings = warnings
@is_failed = is_failed
end
View
2 lib/foodcritic/linter.rb
@@ -56,7 +56,7 @@ def check(cookbook_path, options)
last_dir = cookbook_dir
end
- @review = Review.new(warnings, should_fail_build?(options[:fail_tags], matched_rule_tags))
+ @review = Review.new(cookbook_path, warnings, should_fail_build?(options[:fail_tags], matched_rule_tags))
binding.pry if options[:repl]
@review
View
56 lib/foodcritic/output.rb
@@ -10,4 +10,60 @@ def output(review)
end
end
+ # Display rule matches with surrounding context.
+ class ContextOutput
+
+ # Output the review showing matching lines with context.
+ #
+ # @param [Review] review The review to output.
+ def output(review)
+ unless review.respond_to?(:warnings)
+ puts review; return
+ end
+
+ # Cheating here and mis-using Rak (Ruby port of Ack) to generate pretty colourised context.
+ #
+ # Rak supports evaluating a custom expression as an alternative to a regex. Our expression consults a hash of the
+ # matches found and then we let Rak take care of the presentation.
+ line_lookup = key_by_file_and_line(review)
+ Rak.class_eval do
+ const_set(:RULE_COLOUR, "\033[1;36m")
+ @warnings = line_lookup
+ end
+ ARGV.replace(['--context', '--eval', %q{
+ # This code will be evaluated inline by Rak.
+ fn = fn.split("\n").first
+ if @warnings.key?(fn) and @warnings[fn].key?($.) # filename and line number
+ rule_name = "#{RULE_COLOUR if opt[:colour]}#{@warnings[fn][$.].to_a.join("\n")}#{CLEAR_COLOURS}"
+ if ! displayed_filename
+ fn = "#{fn}\n#{rule_name}"
+ else
+ puts rule_name
+ end
+ else
+ next
+ end
+ }, review.cookbook_path])
+ load Gem.bin_path('rak', 'rak') # Assumes Rubygems
+ end
+
+ private
+
+ # Build a hash lookup by filename and line number for warnings found in the specified review.
+ #
+ # @param [Review] review The review to convert.
+ # @return [Hash] Nested hashes keyed by filename and line number.
+ def key_by_file_and_line(review)
+ warn_hash = {}
+ review.warnings.each do |warning|
+ filename = Pathname.new(warning.match[:filename]).cleanpath.to_s; line_num = warning.match[:line].to_i
+ warn_hash[filename] = {} unless warn_hash.key?(filename)
+ warn_hash[filename][line_num] = Set.new unless warn_hash[filename].key?(line_num)
+ warn_hash[filename][line_num] << warning.rule
+ end
+ warn_hash
+ end
+
+ end
+
end

0 comments on commit df07ba0

Please sign in to comment.