@@ -93,16 +93,12 @@ def configure_io(io)
9393
9494 if @io . respond_to? ( :auto_indent ) and @context . auto_indent_mode
9595 @io . auto_indent do |lines , line_index , byte_pointer , is_newline |
96- if is_newline
97- tokens = self . class . ripper_lex_without_warning ( lines [ 0 ..line_index ] . join ( "\n " ) , context : @context )
98- process_indent_level ( tokens , lines )
99- else
100- code = line_index . zero? ? '' : lines [ 0 ..( line_index - 1 ) ] . map { |l | l + "\n " } . join
101- last_line = lines [ line_index ] &.byteslice ( 0 , byte_pointer )
102- code += last_line if last_line
103- tokens = self . class . ripper_lex_without_warning ( code , context : @context )
104- check_corresponding_token_depth ( tokens , lines , line_index )
105- end
96+ next nil if lines == [ nil ] # Workaround for exit IRB with CTRL+d
97+ next nil if !is_newline && lines [ line_index ] &.byteslice ( 0 , byte_pointer ) &.match? ( /\A \s *\z / )
98+
99+ code = lines [ 0 ..line_index ] . map { |l | "#{ l } \n " } . join
100+ tokens = self . class . ripper_lex_without_warning ( code , context : @context )
101+ process_indent_level ( tokens , lines , line_index , is_newline )
106102 end
107103 end
108104 end
@@ -364,10 +360,16 @@ def check_code_block(code, tokens)
364360 def calc_nesting_depth ( opens )
365361 indent_level = 0
366362 nesting_level = 0
367- opens . each do |t |
363+ opens . each_with_index do |t , index |
368364 case t . event
369365 when :on_heredoc_beg
370- # TODO: indent heredoc
366+ if opens [ index + 1 ] &.event != :on_heredoc_beg
367+ if t . tok . match? ( /^<<[~-]/ )
368+ indent_level += 1
369+ else
370+ indent_level = 0
371+ end
372+ end
371373 when :on_tstring_beg , :on_regexp_beg , :on_symbeg
372374 # can be indented if t.tok starts with `%`
373375 when :on_words_beg , :on_qwords_beg , :on_symbols_beg , :on_qsymbols_beg , :on_embexpr_beg
@@ -382,46 +384,70 @@ def calc_nesting_depth(opens)
382384 [ indent_level , nesting_level ]
383385 end
384386
385- def free_indent_token ( opens , line_index )
386- last_token = opens . last
387- return unless last_token
388- if last_token . event == :on_heredoc_beg && last_token . pos . first < line_index + 1
389- # accept extra indent spaces inside heredoc
390- last_token
391- end
392- end
393-
394- def process_indent_level ( tokens , lines )
395- opens = IRB ::NestingParser . open_tokens ( tokens )
396- indent_level , _nesting_level = calc_nesting_depth ( opens )
397- indent = indent_level * 2
398- line_index = lines . size - 2
399- if free_indent_token ( opens , line_index )
400- return [ indent , lines [ line_index ] [ /^ */ ] . length ] . max
401- end
387+ FREE_INDENT_TOKENS = %i[ on_tstring_beg on_backtick on_regexp_beg on_symbeg ]
402388
403- indent
389+ def free_indent_token? ( token )
390+ FREE_INDENT_TOKENS . include? ( token &.event )
404391 end
405392
406- def check_corresponding_token_depth ( tokens , lines , line_index )
393+ def process_indent_level ( tokens , lines , line_index , is_newline )
407394 line_results = IRB ::NestingParser . parse_by_line ( tokens )
408395 result = line_results [ line_index ]
409- return unless result
396+ if result
397+ _tokens , prev_opens , next_opens , min_depth = result
398+ else
399+ # When last line is empty
400+ prev_opens = next_opens = line_results . last [ 2 ]
401+ min_depth = next_opens . size
402+ end
410403
411404 # To correctly indent line like `end.map do`, we use shortest open tokens on each line for indent calculation.
412405 # Shortest open tokens can be calculated by `opens.take(min_depth)`
413- _tokens , prev_opens , opens , min_depth = result
414- indent_level , _nesting_level = calc_nesting_depth ( opens . take ( min_depth ) )
415- indent = indent_level * 2
416- free_indent_tok = free_indent_token ( opens , line_index )
417- prev_line_free_indent_tok = free_indent_token ( prev_opens , line_index - 1 )
418- if prev_line_free_indent_tok && prev_line_free_indent_tok != free_indent_tok
419- return indent
420- elsif free_indent_tok
421- return lines [ line_index ] [ /^ */ ] . length
406+ indent_level , _nesting_level = calc_nesting_depth ( prev_opens . take ( min_depth ) )
407+ indent = 2 * indent_level
408+
409+ preserve_indent = lines [ line_index - ( is_newline ? 1 : 0 ) ] [ /^ */ ] . size
410+
411+ prev_open_token = prev_opens . last
412+ next_open_token = next_opens . last
413+
414+ if free_indent_token? ( prev_open_token )
415+ if is_newline && prev_open_token . pos [ 0 ] == line_index
416+ # First newline inside free-indent token
417+ indent
418+ else
419+ # Accept any number of indent inside free-indent token
420+ preserve_indent
421+ end
422+ elsif prev_open_token &.event == :on_embdoc_beg || next_open_token &.event == :on_embdoc_beg
423+ if prev_open_token &.event == next_open_token &.event
424+ # Accept any number of indent inside embdoc content
425+ preserve_indent
426+ else
427+ # =begin or =end
428+ 0
429+ end
430+ elsif prev_open_token &.event == :on_heredoc_beg
431+ tok = prev_open_token . tok
432+ if prev_opens . size <= next_opens . size
433+ if is_newline && lines [ line_index ] . empty? && line_results [ line_index - 1 ] [ 1 ] . last != next_open_token
434+ # First line in heredoc
435+ indent
436+ elsif tok . match? ( /^<<~/ )
437+ # Accept extra indent spaces inside `<<~` heredoc
438+ [ indent , preserve_indent ] . max
439+ else
440+ # Accept any number of indent inside other heredoc
441+ preserve_indent
442+ end
443+ else
444+ # Heredoc close
445+ prev_line_indent_level , _prev_line_nesting_level = calc_nesting_depth ( prev_opens )
446+ tok . match? ( /^<<[~-]/ ) ? 2 * ( prev_line_indent_level - 1 ) : 0
447+ end
448+ else
449+ indent
422450 end
423- prev_indent_level , _prev_nesting_level = calc_nesting_depth ( prev_opens )
424- indent if indent_level < prev_indent_level
425451 end
426452
427453 LTYPE_TOKENS = %i[
0 commit comments