Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle multiline within interpolation #428

Merged
merged 3 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions lib/haml_lint/ruby_extraction/chunk_extractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ def visit_script(node, &block)
# that contains interpolation.
indent = raw_first_line.index(/\S/)
@ruby_chunks << PlaceholderMarkerChunk.new(node, 'interpolation', indent: indent)
add_interpolation_chunks(node, raw_first_line, node.line - 1, indent: indent)
lines = extract_piped_plain_multilines(node.line - 1)
add_interpolation_chunks(node, lines.join("\n"), node.line - 1, indent: indent)
return
end

Expand Down Expand Up @@ -327,9 +328,16 @@ def visit_tag_script(node, line_index:, indent:)
# ex: %tag hello #{world}
# Sadly, the text with interpolation is escaped from the original, but this code
# needs the original.
interpolation_original = @document.unescape_interpolation_to_original_cache[node.script]

interpolation_original = @document.unescape_interpolation_to_original_cache[node.script]
line_start_index = @original_haml_lines[node.line - 1].rindex(interpolation_original)
if line_start_index.nil?
raw_lines = extract_piped_plain_multilines(node.line - 1)
equivalent_haml_code = "#{raw_lines.first} #{raw_lines[1..].map(&:lstrip).join(' ')}"
line_start_index = equivalent_haml_code.rindex(interpolation_original)

interpolation_original = raw_lines.join("\n")
end
add_interpolation_chunks(node, interpolation_original, node.line - 1,
line_start_index: line_start_index, indent: indent)
else
Expand Down Expand Up @@ -404,18 +412,22 @@ def add_interpolation_chunks(node, code, haml_line_index, indent:, line_start_in
# because Haml::Util.balance does a strip...
interpolated_code = code[char_index...scanner.charpos - 1]

interpolated_code = "#{' ' * indent}#{script_output_prefix}#{interpolated_code}"

if interpolated_code.include?("\n")
# We can't correct multiline interpolation.
# Finding meaningful code to generate and then transfer back is pretty complex

# Since we can't fix it, strip around the code to reduce RuboCop lints that we won't be able to fix.
interpolated_code = interpolated_code.strip
interpolated_code = "#{' ' * indent}#{script_output_prefix}#{interpolated_code}"

placeholder_code = interpolated_code.gsub(/\s*\n\s*/, ' ').rstrip
unless parse_ruby(placeholder_code)
placeholder_code = interpolated_code.gsub(/\s*\n\s*/, '; ').rstrip
end
@ruby_chunks << AdHocChunk.new(node, [placeholder_code],
haml_line_index: haml_line_index + line_index)
else
interpolated_code = "#{' ' * indent}#{script_output_prefix}#{interpolated_code}"
@ruby_chunks << InterpolationChunk.new(node, [interpolated_code],
haml_line_index: haml_line_index + line_index,
start_char_index: start_char_index,
Expand All @@ -433,6 +445,15 @@ def process_multiline!(line)
end
end

def process_plain_multiline!(line)
if line&.end_with?(' |')
line[-2..] = ''
true
else
false
end
end

# Returns the raw lines from the haml for the given index.
# Multiple lines are returned when a line ends with a comma as that is the only
# time HAMLs allows Ruby lines to be split.
Expand Down Expand Up @@ -517,6 +538,25 @@ def extract_raw_ruby_lines(haml_processed_ruby_code, first_line_index)
[first_line_offset, ruby_lines]
end

def extract_piped_plain_multilines(first_line_index)
lines = []

cur_line = @original_haml_lines[first_line_index].rstrip
cur_line_index = first_line_index

# The pipes must also be on the last line of the multi-line section
while cur_line && process_plain_multiline!(cur_line)
lines << cur_line
cur_line_index += 1
cur_line = @original_haml_lines[cur_line_index].rstrip
end

if lines.empty?
lines << cur_line
end
lines
end

# Tag attributes actually handle multiline differently than scripts.
# The basic system basically keeps considering more lines until it meets the closing braces, but still
# processes pipes too (same as extract_raw_ruby_lines).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,48 @@ Lorem Ipsum
Dolor #{foo(bar: 123)} Sit Amet
Consectetur Adipiscing

!!! doesn't fix plain's interpolations that is spread on multiple lines using pipes with many spaces before the pipes
!# Basically impossible to correct this cleanly, but at least don't fail.
Dolor #{foo(:bar => |
123)} Sit Amet |
---
haml_lint_interpolation_1
HL.out = foo(:bar => 123)
---
haml_lint_interpolation_1
HL.out = foo(bar: 123)
---
Dolor #{foo(:bar => |
123)} Sit Amet |

!!! doesn't fix plain's interpolations that is spread on multiple lines using pipes
!# Basically impossible to correct this cleanly, but at least don't fail.
Dolor #{foo(:bar => |
123)} Sit Amet |
---
haml_lint_interpolation_1
HL.out = foo(:bar => 123)
---
haml_lint_interpolation_1
HL.out = foo(bar: 123)
---
Dolor #{foo(:bar => |
123)} Sit Amet |

!!! doesn't fix plain's interpolations that is spread on multiple lines using pipes, keeping a space between the parts
!# Basically impossible to correct this cleanly, but at least don't fail.
Dolor #{foo |
bar} Sit Amet |
---
haml_lint_interpolation_1
HL.out = foo bar
---
haml_lint_interpolation_1
HL.out = foo bar
---
Dolor #{foo |
bar} Sit Amet |

!!! fixes non-ruby filter's interpolations on two consecutive lines
:filter
Lorem #{foo(:bar => 123)} Ipsum
Expand Down Expand Up @@ -841,6 +883,72 @@ end
---
%tag hello #{foo(bar: 123)} world

!!! doesn't fix tag's interpolations that is spread on multiple lines using pipes
!# Basically impossible to correct this cleanly, but at least don't fail.
%tag Dolor #{foo(:bar => |
123)} Sit Amet |
---
begin
haml_lint_tag_2
HL.out = foo(:bar => 123)
ensure
HL.noop
end
---
begin
haml_lint_tag_2
HL.out = foo(bar: 123)
ensure
HL.noop
end
---
%tag Dolor #{foo(:bar => |
123)} Sit Amet |

!!! doesn't fix tag's interpolations that is spread on multiple lines using pipes, with many spaces before the pipes
!# Basically impossible to correct this cleanly, but at least don't fail.
%tag Dolor #{foo(:bar => |
123)} Sit Amet |
---
begin
haml_lint_tag_2
HL.out = foo(:bar => 123)
ensure
HL.noop
end
---
begin
haml_lint_tag_2
HL.out = foo(bar: 123)
ensure
HL.noop
end
---
%tag Dolor #{foo(:bar => |
123)} Sit Amet |

!!! doesn't fix plain's interpolations that is spread on multiple lines using pipes, keeping a space between the parts
!# Basically impossible to correct this cleanly, but at least don't fail.
%tag Dolor #{foo |
bar} Sit Amet |
---
begin
haml_lint_tag_2
HL.out = foo bar
ensure
HL.noop
end
---
begin
haml_lint_tag_2
HL.out = foo bar
ensure
HL.noop
end
---
%tag Dolor #{foo |
bar} Sit Amet |

!!! fixes plain's interpolation in the middle of line that started with ==
!# This is an old feature that is not needed anymore since HAML 2.2... But it's still valid.
== Lorem #{foo(:bar => 123)} Ipsum
Expand Down