Class: Refiner

Inherits:
Object
  • Object
show all
Includes:
Colors
Defined in:
lib/refiner.rb

Overview

Compute longest common substring based diff between two strings.

The diff format is first the old string:

  • in red

  • with each line prefixed with minuses

  • removed characters highlighted in inverse video

Then comes the new string:

  • in green

  • with each line prefixed with plusses

  • added characters highlighted in inverse video

Constant Summary collapse

REFINEMENT_THRESHOLD =

If either old or new would get more than this percentage of chars highlighted, consider this to be a replacement rather than a change and just don’t highlight anything.

30

Constants included from Colors

Colors::BOLD, Colors::CYAN, Colors::ESC, Colors::GREEN, Colors::NOT_REVERSE, Colors::RED, Colors::RESET, Colors::REVERSE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Colors

#reversed, #uncolor

Constructor Details

#initialize(old, new) ⇒ Refiner

Returns a new instance of Refiner.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/refiner.rb', line 65

def initialize(old, new)
  old_highlights = Set.new
  new_highlights = Set.new
  if should_highlight?(old, new)
    collect_highlights(Diff::LCS.diff(old, new),
                       old_highlights,
                       new_highlights)

    censor_highlights(old, new, old_highlights, new_highlights)
  end

  @refined_old = DiffString.new('-', RED)
  old.each_char.with_index do |char, index|
    @refined_old.add(char, old_highlights.include?(index))
  end

  @refined_new = DiffString.new('+', GREEN)
  new.each_char.with_index do |char, index|
    @refined_new.add(char, new_highlights.include?(index))
  end
end

Instance Attribute Details

#refined_newObject (readonly)

Returns the value of attribute refined_new.



20
21
22
# File 'lib/refiner.rb', line 20

def refined_new
  @refined_new
end

#refined_oldObject (readonly)

Returns the value of attribute refined_old.



19
20
21
# File 'lib/refiner.rb', line 19

def refined_old
  @refined_old
end

Instance Method Details

#censor_highlights(old, new, old_highlights, new_highlights) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/refiner.rb', line 42

def censor_highlights(old, new, old_highlights, new_highlights)
  old_highlights_percentage = 100 * old_highlights.size / old.length
  new_highlights_percentage = 100 * new_highlights.size / new.length

  if old_highlights_percentage > REFINEMENT_THRESHOLD \
     || new_highlights_percentage > REFINEMENT_THRESHOLD
    # We'll consider this a replacement rather than a change, don't
    # highlight it.
    old_highlights.clear
    new_highlights.clear
  end
end

#collect_highlights(diff, old_highlights, new_highlights) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/refiner.rb', line 27

def collect_highlights(diff, old_highlights, new_highlights)
  diff.each do |section|
    section.each do |highlight|
      case highlight.action
      when '-'
        old_highlights << highlight.position
      when '+'
        new_highlights << highlight.position
      else
        fail("Unsupported diff type: <#{type}>")
      end
    end
  end
end

#should_highlight?(old, new) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
58
59
60
61
62
63
# File 'lib/refiner.rb', line 55

def should_highlight?(old, new)
  return false if old.empty? || new.empty?

  # The 15_000 constant has been determined using the "benchmark"
  # program in our bin/ directory.
  return false if old.length + new.length > 15_000

  return true
end