updated to work with miniunit 1.1.0
[augment.git] / lib / layer.rb
blobd657ae00244f7d85d5b3eb01a187a62693bea275
1 require 'json'
3 ##
4 # Stores metadata about a file. Fields stored are:
6 # * range - the character range the layer applies to
7 # * color - the color with which to highlight the piece of code
8 # * message - what to display to the user
9 # * backend - which backend generated this layer
11 # Layer data is stored and emitted in JSON format.
13 # Note that ranges are always exclusive, so always use three dots!
15 class Layer
16   attr_accessor :range, :color, :message, :backend
18   def initialize(where, color, message, backend, file = nil)
19     @range, @color, @message, @backend = [Layer.interpret_where(where, file),
20                                           color, message,
21                                           backend.to_s.downcase.gsub(/backend/, '')]
22   end
24   ##
25   # Determines the correct range as defined by a few rules:
26   #
27   # * Numbers get interpreted as line numbers
28   # * Strings get converted into Ranges if they include '...'
29   # * Otherwise Strings are interpreted as Class#method names
30   # * Ranges pass through untouched
31   #
32   def Layer.interpret_where where, file = nil
33     case where
34     when Fixnum
35       Layer.line_to_char_range file, where
36     when String
37       if where =~ /\.\.\./ # grabbing this from JSON strings
38         Layer.range_string_to_char_range where
39       else
40         Layer.method_to_char_range file, where
41       end
42     else
43       where
44     end
45   end
47   # Convert a line number to a character range given a file.
48   def self.line_to_char_range(file, line)
49     file = File.read(file).split("\n")
50     start = file[0 ... line - 1].join("\n").size + 2
51     (start ... start + file[line - 1].size)
52   end
54   # Basically implements the inverse of Range#to_s
55   def self.range_string_to_char_range(range)
56     (range.split('...').first.to_i ... range.split('...').last.to_i)
57   end
59   # Convert a method name to a character range given a file.
60   def self.method_to_char_range(file, method)
61     # TODO: get smart about what class the method is defined on... ugh
62     start = File.read(file) =~ Regexp.new("def .*#{method.split(/#/).last}")
63     finish = start + 5 # TODO: fix
64     (start ... finish) # we're just layering the word "def" for now
65   end
67   # Slurp up layer data from stored JSON given the original file.
68   def self.read(original_file)
69     return [] if !File.exist?(Augment.augment_path(original_file))
70     layers = JSON.parse(File.read(Augment.augment_path(original_file)))
71     layers.map!{ |l| Layer.new(l['range'], l['color'], l['message'], l['backend']) }
72     layers.sort_by{ |l| l.range.begin }.reverse
73   end
75   def to_json
76     { 'range' => @range, 'color' => @color, 'message' => @message,
77       'backend' => @backend}.to_json
78   end
79 end