@@ -339,6 +339,7 @@ def to_a
339
339
when :tRATIONAL
340
340
value = parse_rational ( value )
341
341
when :tSPACE
342
+ location = range ( token . location . start_offset , token . location . start_offset + percent_array_leading_whitespace ( value ) )
342
343
value = nil
343
344
when :tSTRING_BEG
344
345
next_token = lexed [ index ] [ 0 ]
@@ -395,12 +396,16 @@ def to_a
395
396
quote_stack . push ( value )
396
397
end
397
398
when :tSTRING_CONTENT
399
+ is_percent_array = percent_array? ( quote_stack . last )
400
+
398
401
if ( lines = token . value . lines ) . one?
399
402
# Heredoc interpolation can have multiple STRING_CONTENT nodes on the same line.
400
403
is_first_token_on_line = lexed [ index - 1 ] && token . location . start_line != lexed [ index - 2 ] [ 0 ] . location &.start_line
401
404
# The parser gem only removes indentation when the heredoc is not nested
402
405
not_nested = heredoc_stack . size == 1
403
- if is_first_token_on_line && not_nested && ( current_heredoc = heredoc_stack . last ) . common_whitespace > 0
406
+ if is_percent_array
407
+ value = percent_array_unescape ( value )
408
+ elsif is_first_token_on_line && not_nested && ( current_heredoc = heredoc_stack . last ) . common_whitespace > 0
404
409
value = trim_heredoc_whitespace ( value , current_heredoc )
405
410
end
406
411
@@ -417,12 +422,10 @@ def to_a
417
422
chomped_line = line . chomp
418
423
backslash_count = chomped_line [ /\\ {1,}\z / ] &.length || 0
419
424
is_interpolation = interpolation? ( quote_stack . last )
420
- is_percent_array = percent_array? ( quote_stack . last )
421
425
422
426
if backslash_count . odd? && ( is_interpolation || is_percent_array )
423
427
if is_percent_array
424
- # Remove the last backslash, keep potential newlines
425
- current_line << line . sub ( /(\\ )(\r ?\n )\z / , '\2' )
428
+ current_line << percent_array_unescape ( line )
426
429
adjustment += 1
427
430
else
428
431
chomped_line . delete_suffix! ( "\\ " )
@@ -701,6 +704,27 @@ def unescape_string(string, quote)
701
704
end
702
705
end
703
706
707
+ # In a percent array, certain whitespace can be preceeded with a backslash,
708
+ # causing the following characters to be part of the previous element.
709
+ def percent_array_unescape ( string )
710
+ string . gsub ( /(\\ )+[ \f \n \r \t \v ]/ ) do |full_match |
711
+ full_match . delete_prefix! ( "\\ " ) if Regexp . last_match [ 1 ] . length . odd?
712
+ full_match
713
+ end
714
+ end
715
+
716
+ # For %-arrays whitespace, the parser gem only considers whitespace before the newline.
717
+ def percent_array_leading_whitespace ( string )
718
+ return 1 if string . start_with? ( "\n " )
719
+
720
+ leading_whitespace = 0
721
+ string . each_char do |c |
722
+ break if c == "\n "
723
+ leading_whitespace += 1
724
+ end
725
+ leading_whitespace
726
+ end
727
+
704
728
# Determine if characters preceeded by a backslash should be escaped or not
705
729
def interpolation? ( quote )
706
730
quote != "'" && !quote . start_with? ( "%q" , "%w" , "%i" )
0 commit comments