@@ -257,13 +257,68 @@ def self.loc_string(loc, compilation_dir, logger)
257257 # @param depth [Fixnum] Depth, for correct indentation
258258 # @return Array<String> Displayable result
259259 def self . diff_two_strings_with_diffy ( string1 , string2 , depth )
260- # prevent 'No newline at end of file' for single line strings
261- string1 += "\n " unless string1 =~ /\n /
262- string2 += "\n " unless string2 =~ /\n /
260+ # Single line strings?
261+ if single_lines? ( string1 , string2 )
262+ string1 , string2 = add_trailing_newlines ( string1 , string2 )
263+ diff = Diffy ::Diff . new ( string1 , string2 , context : 2 , include_diff_info : true ) . to_s . split ( "\n " )
264+ 3 . times { diff . shift }
265+ return diff . map { |x | left_pad ( 2 * depth + 2 , make_trailing_whitespace_visible ( adjust_position_of_plus_minus ( x ) ) ) }
266+ end
267+
268+ # Multiple line strings
269+ string1 , string2 = add_trailing_newlines ( string1 , string2 )
263270 diff = Diffy ::Diff . new ( string1 , string2 , context : 2 , include_diff_info : true ) . to_s . split ( "\n " )
264271 diff . shift # Remove first line of diff info (filename that makes no sense)
265272 diff . shift # Remove second line of diff info (filename that makes no sense)
266- diff . map { |x | left_pad ( 2 * depth + 2 , x ) }
273+ diff . map { |x | left_pad ( 2 * depth + 2 , make_trailing_whitespace_visible ( x ) ) }
274+ end
275+
276+ # Determine if two incoming strings are single lines. Returns true if both
277+ # incoming strings are single lines, false otherwise.
278+ # @param string_1 [String] First string
279+ # @param string_2 [String] Second string
280+ # @return [Boolean] Whether both incoming strings are single lines
281+ def self . single_lines? ( string_1 , string_2 )
282+ string_1 . strip !~ /\n / && string_2 . strip !~ /\n /
283+ end
284+
285+ # Add "\n" to the end of both strings, only if both strings are lacking it.
286+ # This prevents "\\ No newline at end of file" for single string comparison.
287+ # @param string_1 [String] First string
288+ # @param string_2 [String] Second string
289+ # @return [Array<String>] Adjusted string_1, string_2
290+ def self . add_trailing_newlines ( string_1 , string_2 )
291+ return [ string_1 , string_2 ] unless string_1 !~ /\n \Z / && string_2 !~ /\n \Z /
292+ [ string_1 + "\n " , string_2 + "\n " ]
293+ end
294+
295+ # Adjust the space after of the `-` / `+` in the diff for single line diffs.
296+ # Diffy prints diffs with no space between the `-` / `+` in the text, but for
297+ # single lines it's easier to read with that space added.
298+ # @param string_in [String] Input string, which is a line of a diff from diffy
299+ # @return [String] Modified string
300+ def self . adjust_position_of_plus_minus ( string_in )
301+ string_in . sub ( /\A (\e \[ \d +m)?([\- \+ ])/ , '\1\2 ' )
302+ end
303+
304+ # Convert trailing whitespace to underscore for display purposes. Also convert special
305+ # whitespace (\r, \n, \t, ...) to character representation.
306+ # @param string_in [String] Input string, which might contain trailing whitespace
307+ # @return [String] Modified string
308+ def self . make_trailing_whitespace_visible ( string_in )
309+ return string_in unless string_in =~ /\A ((?:.|\n )*?)(\s +)(\e \[ 0m)?\Z /
310+ beginning = Regexp . last_match ( 1 )
311+ trailing_space = Regexp . last_match ( 2 )
312+ end_escape = Regexp . last_match ( 3 )
313+
314+ # Trailing space adjustment for line endings
315+ trailing_space . gsub! "\n " , '\n'
316+ trailing_space . gsub! "\r " , '\r'
317+ trailing_space . gsub! "\t " , '\t'
318+ trailing_space . gsub! "\f " , '\f'
319+ trailing_space . tr! ' ' , '_'
320+
321+ [ beginning , trailing_space , end_escape ] . join ( '' )
267322 end
268323
269324 # Get the diff of two hashes. Call the 'diffy' gem for this.
0 commit comments