Class: Refiner
- Inherits:
-
Object
- Object
- Refiner
- Includes:
- Colors, WhitespaceLint
- 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::DEFAULT_COLOR, Colors::ESC, Colors::GREEN, Colors::NOT_REVERSE, Colors::RED, Colors::RESET, Colors::REVERSE
Instance Attribute Summary collapse
-
#refined_new ⇒ Object
readonly
Returns the value of attribute refined_new.
-
#refined_old ⇒ Object
readonly
Returns the value of attribute refined_old.
Instance Method Summary collapse
- #censor_highlights(old, new, old_highlights, new_highlights) ⇒ Object
- #collect_highlights(diff, old_highlights, new_highlights) ⇒ Object
-
#create_one_to_many_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) ⇒ Object
After returning from this method, both @refined_old and @refined_new must have been set to reasonable values.
-
#create_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) ⇒ Object
After returning from this method, both @refined_old and @refined_new must have been set to reasonable values.
-
#initialize(old, new) ⇒ Refiner
constructor
A new instance of Refiner.
-
#render_refinement(prefix, base_color, string, highlights, base_index: 0, highlight_color: '', ws: nil) ⇒ Object
ws: a set containing the whitespace errors we want to highlight.
- #should_highlight?(old, new) ⇒ Boolean
- #try_highlight(old, new) ⇒ Object
- #try_highlight_initial_lines(old, new) ⇒ Object
Methods included from WhitespaceLint
#add_line_highlights, #collect_ws_highlights
Methods included from Colors
Constructor Details
#initialize(old, new) ⇒ Refiner
Returns a new instance of Refiner.
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/refiner.rb', line 174 def initialize(old, new) old_highlights, new_highlights = try_highlight(old, new) if old_highlights.size == 0 && new_highlights.size == 0 old_highlights, new_highlights = try_highlight_initial_lines(old, new) end whitespace_highlights = collect_ws_highlights(new) if !create_one_to_many_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) create_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) end end |
Instance Attribute Details
#refined_new ⇒ Object (readonly)
Returns the value of attribute refined_new.
24 25 26 |
# File 'lib/refiner.rb', line 24 def refined_new @refined_new end |
#refined_old ⇒ Object (readonly)
Returns the value of attribute refined_old.
23 24 25 |
# File 'lib/refiner.rb', line 23 def refined_old @refined_old end |
Instance Method Details
#censor_highlights(old, new, old_highlights, new_highlights) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/refiner.rb', line 46 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
31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/refiner.rb', line 31 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 action: <#{action}>") end end end end |
#create_one_to_many_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) ⇒ Object
After returning from this method, both @refined_old and @refined_new must have been set to reasonable values.
Returns false if the preconditions for using this method aren’t fulfilled
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/refiner.rb', line 137 def create_one_to_many_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) # If things have been removed from the first line, the specialized # highlighting won't work return false if old_highlights.count > 0 # If the first line was replaced rather than updated, the specialized # highlighting won't work return false if new_highlights.count == 0 # Specialized highlighting requires exactly one old line return false if old.lines.count != 1 lines = new.lines # Specialized highlighting requires two or more new lines return false if lines.count < 2 @refined_old = '' refined_line_1 = render_refinement(' ', '', lines[0], new_highlights, highlight_color: GREEN, ws: whitespace_highlights) line_2_index_0 = lines[0].length refined_remaining_lines = render_refinement('+', GREEN, lines[1..-1].join, new_highlights, base_index: line_2_index_0, ws: whitespace_highlights) @refined_new = refined_line_1 + refined_remaining_lines return true end |
#create_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) ⇒ Object
After returning from this method, both @refined_old and @refined_new must have been set to reasonable values.
124 125 126 127 128 129 130 131 |
# File 'lib/refiner.rb', line 124 def create_refinements(old, new, old_highlights, new_highlights, whitespace_highlights) @refined_old = render_refinement('-', RED, old, old_highlights) @refined_new = render_refinement('+', GREEN, new, new_highlights, ws: whitespace_highlights) end |
#render_refinement(prefix, base_color, string, highlights, base_index: 0, highlight_color: '', ws: nil) ⇒ Object
ws: a set containing the whitespace errors we want to highlight
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/refiner.rb', line 103 def render_refinement(prefix, base_color, string, highlights, base_index: 0, highlight_color: '', ws: nil) return_me = DiffString.new(prefix, base_color) string.each_char.with_index do |char, index| highlight = highlights.include?(index + base_index) color = highlight ? highlight_color : '' if !ws.nil? && ws.include?(index + base_index) # Highlight whitespace error in inverse red color = RED highlight = true end return_me.add(char, highlight, color) end return return_me.to_s end |
#should_highlight?(old, new) ⇒ Boolean
59 60 61 62 63 64 65 66 67 |
# File 'lib/refiner.rb', line 59 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 |
#try_highlight(old, new) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/refiner.rb', line 69 def try_highlight(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 return old_highlights, new_highlights end |
#try_highlight_initial_lines(old, new) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/refiner.rb', line 83 def try_highlight_initial_lines(old, new) old_line_count = old.lines.count new_line_count = new.lines.count if old_line_count == new_line_count return Set.new, Set.new end min_line_count = [old_line_count, new_line_count].min if min_line_count == 0 return Set.new, Set.new end # Truncate old and new so they have the same number of lines old = old.lines[0..(min_line_count - 1)].join new = new.lines[0..(min_line_count - 1)].join return try_highlight(old, new) end |