@@ -410,12 +410,20 @@ def to_a
410410
411411 lines . each . with_index do |line , index |
412412 chomped_line = line . chomp
413-
414- # When the line ends with an odd number of backslashes, it must be a line continuation.
415- if chomped_line [ /\\ {1,}\z / ] &.length &.odd?
416- chomped_line . delete_suffix! ( "\\ " )
417- current_line << chomped_line
418- adjustment += 2
413+ backslash_count = chomped_line [ /\\ {1,}\z / ] &.length || 0
414+ is_interpolation = interpolation? ( quote_stack . last )
415+ is_percent_array = percent_array? ( quote_stack . last )
416+
417+ if backslash_count . odd? && ( is_interpolation || is_percent_array )
418+ if is_percent_array
419+ # Remove the last backslash, keep potential newlines
420+ current_line << line . sub ( /(\\ )(\r ?\n )\z / , '\2' )
421+ adjustment += 1
422+ else
423+ chomped_line . delete_suffix! ( "\\ " )
424+ current_line << chomped_line
425+ adjustment += 2
426+ end
419427 # If the string ends with a line continuation emit the remainder
420428 emit = index == lines . count - 1
421429 else
@@ -615,7 +623,13 @@ def unescape_string(string, quote)
615623 # TODO: Implement regexp escaping
616624 return string if quote == "/" || quote . start_with? ( "%r" )
617625
618- if quote == "'" || quote . start_with? ( "%q" ) || quote . start_with? ( "%w" ) || quote . start_with? ( "%i" )
626+ if interpolation? ( quote )
627+ # In interpolation, escape sequences can be written literally. For example, "\\n" becomes "\n",
628+ # and "\\\\n" becomes "\\n". Unknown escapes sequences, like "\\o" simply become "o".
629+ string . gsub ( /\\ ./ ) do |match |
630+ ESCAPES [ match [ 1 ] ] || match [ 1 ]
631+ end
632+ else
619633 if quote == "'"
620634 delimiter = "'"
621635 else
@@ -624,14 +638,18 @@ def unescape_string(string, quote)
624638
625639 delimiters = Regexp . escape ( "#{ delimiter } #{ DELIMITER_SYMETRY [ delimiter ] } " )
626640 string . gsub ( /\\ ([\\ #{ delimiters } ])/ , '\1' )
627- else
628- # When double-quoted, escape sequences can be written literally. For example, "\\n" becomes "\n",
629- # and "\\\\n" becomes "\\n". Unknown escapes sequences, like "\\o" simply become "o".
630- string . gsub ( /\\ ./ ) do |match |
631- ESCAPES [ match [ 1 ] ] || match [ 1 ]
632- end
633641 end
634642 end
643+
644+ # Determine if characters preceeded by a backslash should be escaped or not
645+ def interpolation? ( quote )
646+ quote != "'" && !quote . start_with? ( "%q" , "%w" , "%i" )
647+ end
648+
649+ # Determine if the string is part of a %-style array.
650+ def percent_array? ( quote )
651+ quote . start_with? ( "%w" , "%W" , "%i" , "%I" )
652+ end
635653 end
636654 end
637655 end
0 commit comments