@@ -103,10 +103,12 @@ def maybe_translate_assertion(node)
103103 # Otherwise, replace up to the end of the node
104104 end_offset = comment_end_offset || node . location . end_offset
105105
106+ heredoc_body = heredoc_body_within_range ( value , end_offset )
107+
106108 replacement = if node . name == :bind
107109 "#{ rbs_annotation } #{ trailing_comment } "
108110 else
109- "#{ dedent_value ( node , value ) } #{ rbs_annotation } #{ trailing_comment } "
111+ "#{ dedent_value ( node , value ) } #{ rbs_annotation } #{ trailing_comment } #{ heredoc_body } "
110112 end
111113
112114 @rewriter << Source ::Replace . new ( start_offset , end_offset - 1 , replacement )
@@ -212,6 +214,53 @@ def extract_trailing_comment(node)
212214 [ " #{ range . pack ( "C*" ) } " , end_offset ]
213215 end
214216
217+ #: (Prism::Node, Integer) -> String?
218+ def heredoc_body_within_range ( node , replace_end_offset )
219+ heredoc_end = heredoc_end_offsets ( node )
220+ . select { |offset | offset <= replace_end_offset }
221+ . max
222+ return unless heredoc_end
223+
224+ opener_line_end = adjust_to_line_end ( node . location . end_offset )
225+ body_bytes = @ruby_bytes [ ( opener_line_end + 1 ) ...heredoc_end ] #: as !nil
226+ body = body_bytes . pack ( "C*" )
227+ body . chomp! if @ruby_bytes [ replace_end_offset ] == LINE_BREAK
228+ "\n #{ body } "
229+ end
230+
231+ #: (Prism::Node) -> Array[Integer]
232+ def heredoc_end_offsets ( node )
233+ offsets = [ ] #: Array[Integer]
234+
235+ case node
236+ when Prism ::StringNode , Prism ::InterpolatedStringNode , Prism ::XStringNode , Prism ::InterpolatedXStringNode
237+ opening = node . opening_loc
238+ closing = node . closing_loc
239+ if opening && closing && opening . start_line != closing . start_line && opening . slice . start_with? ( "<<" )
240+ offsets << closing . end_offset
241+ end
242+ end
243+
244+ node . child_nodes . each do |child |
245+ next unless child
246+
247+ offsets . concat ( heredoc_end_offsets ( child ) )
248+ end
249+
250+ offsets
251+ end
252+
253+ #: (Prism::Node) -> bool
254+ def string_literal? ( node )
255+ case node
256+ when Prism ::StringNode , Prism ::InterpolatedStringNode ,
257+ Prism ::XStringNode , Prism ::InterpolatedXStringNode
258+ true
259+ else
260+ false
261+ end
262+ end
263+
215264 #: (Prism::Node, Prism::Node) -> String
216265 def dedent_value ( assign , value )
217266 if value . location . start_line == assign . location . start_line
@@ -243,7 +292,7 @@ def dedent_value(assign, value)
243292 # ```
244293 indent = value . location . start_line - assign . location . start_line
245294 lines = value . slice . lines
246- if lines . size > 1
295+ if lines . size > 1 && ! string_literal? ( value )
247296 lines [ 1 ..] &.each_with_index do |line , i |
248297 lines [ i + 1 ] = line . delete_prefix ( " " * indent )
249298 end
0 commit comments