Description: Replace rak hack with our own context-output plugin
Author: Stefano Rivera <stefanor@debian.org>
Forwarded: https://github.com/acrmp/foodcritic/pull/146
Last-Updated: 2013-06-09

--- a/features/support/command_helpers.rb
+++ b/features/support/command_helpers.rb
@@ -298,7 +298,6 @@
     #
     # @param [Array] cmd_args The command line arguments.
     def run_lint(cmd_args)
-      set_env 'RAK_TEST', true.to_s
       run_simple(unescape("foodcritic #{cmd_args.join(' ')}"), false)
     end
   end
--- a/foodcritic.gemspec
+++ b/foodcritic.gemspec
@@ -12,7 +12,6 @@
   s.executables << 'foodcritic'
   s.add_dependency('gherkin', '~> 2.11.7')
   s.add_dependency('nokogiri', '~> 1.5.4')
-  s.add_dependency('rak', '~> 1.4')
   s.add_dependency('treetop', '~> 1.4.10')
   s.add_dependency('yajl-ruby', '~> 1.1.0')
   s.add_dependency('erubis')
--- a/lib/foodcritic.rb
+++ b/lib/foodcritic.rb
@@ -1,7 +1,6 @@
 require 'pathname'
 require 'gherkin'
 require 'treetop'
-require 'rak'
 require 'ripper'
 require 'yajl'
 require 'erubis'
--- a/lib/foodcritic/output.rb
+++ b/lib/foodcritic/output.rb
@@ -1,3 +1,5 @@
+require 'set'
+
 module FoodCritic
 
   # Default output showing a summary view.
@@ -22,34 +24,54 @@
         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 no
-          rule_name = opt[:colour] ? RULE_COLOUR : ''
-          rule_name += "#{@warnings[fn][$.].to_a.join("\n")}#{CLEAR_COLOURS}"
-          if ! displayed_filename
-            fn = "#{fn}\n#{rule_name}"
-          else
-            puts rule_name
-          end
-        else
+      context = 3
+
+      print_fn = lambda { |fn| ansi_print(fn, :red, nil, :bold) }
+      print_rule = lambda { |warn| ansi_print(warn, :cyan, nil, :bold) }
+      print_line = lambda { |line| ansi_print(line, nil, :red, :bold) }
+
+      key_by_file_and_line(review).each do |fn, warnings|
+        print_fn.call fn
+        unless File.exists?(fn)
+          print_rule.call warnings[1].to_a.join("\n")
           next
         end
-      }] + review.cookbook_paths)
-      Rak.send(:remove_const, :VERSION) # Prevent duplicate VERSION warning
-      load Gem.bin_path('rak', 'rak') # Assumes Rubygems
+
+        # Set of line numbers with warnings
+        warn_lines = warnings.keys.to_set
+        # Moving set of line numbers within the context of our position
+        context_set = (0..context).to_set
+        # The last line number we printed a warning for
+        last_warn = -1
+
+        File.open(fn) do |file|
+          file.each do |line|
+            context_set.add(file.lineno + context)
+            context_set.delete(file.lineno - context - 1)
+
+            # Find the first warning within our context
+            context_warns = context_set & warn_lines
+            next_warn = context_warns.min
+            # We may need to interrupt the trailing context of a previous warning
+            next_warn = file.lineno if warn_lines.include? file.lineno
+
+            # Display a warning
+            if next_warn && next_warn > last_warn
+              print_rule.call warnings[next_warn].to_a.join("\n")
+              last_warn = next_warn
+            end
+
+            # Display any relevant lines
+            if warn_lines.include? file.lineno
+              print '%4i|' % file.lineno
+              print_line.call line.chomp
+            elsif not context_warns.empty?
+              print '%4i|' % file.lineno
+              puts line.chomp
+            end
+          end
+        end
+      end
     end
 
     private
@@ -73,6 +95,32 @@
       warn_hash
     end
 
+    # Print an ANSI escape-code formatted string (and a newline)
+    #
+    # @param text [String] the string to format
+    # @param fg [String] foreground color
+    # @param bg [String] background color
+    # @param attr [String] any formatting options
+    def ansi_print(text, fg, bg = nil, attr = nil)
+      unless STDOUT.tty?
+        puts text
+        return
+      end
+
+      colors = %w(black red green yellow blue magenta cyan white)
+      attrs = %w(reset bold dim underscore blink reverse hidden)
+      escape = "\033[%sm"
+      fmt = []
+      fmt << 30 + colors.index(fg.to_s) if fg
+      fmt << 40 + colors.index(bg.to_s) if bg
+      fmt << attrs.index(attr.to_s) if attr
+      if fmt
+        puts "#{escape % fmt.join(';')}#{text}#{escape % 0}"
+      else
+        puts text
+      end
+    end
+
   end
 
 end
