@@ -93,16 +93,11 @@ 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 !is_newline && lines [ line_index ] &.byteslice ( 0 , byte_pointer ) &.match? ( /\A \s *\z / )
97+ code = lines [ 0 ..line_index ] . map { |l | "#{ l } \n " } . join
98+ next nil if code == "\n "
99+ tokens = self . class . ripper_lex_without_warning ( code , context : @context )
100+ process_indent_level ( tokens , lines , line_index , is_newline )
106101 end
107102 end
108103 end
@@ -324,10 +319,16 @@ def check_code_block(code, tokens)
324319 def calc_nesting_depth ( opens )
325320 indent_level = 0
326321 nesting_level = 0
327- opens . each do |t |
322+ opens . each_with_index do |t , index |
328323 case t . event
329324 when :on_heredoc_beg
330- # TODO: indent heredoc
325+ if opens [ index + 1 ] &.event != :on_heredoc_beg
326+ if t . tok . match? ( /^<<[~-]/ )
327+ indent_level += 1
328+ else
329+ indent_level = 0
330+ end
331+ end
331332 when :on_tstring_beg , :on_regexp_beg , :on_symbeg
332333 # can be indented if t.tok starts with `%`
333334 when :on_words_beg , :on_qwords_beg , :on_symbols_beg , :on_qsymbols_beg , :on_embexpr_beg
@@ -342,42 +343,62 @@ def calc_nesting_depth(opens)
342343 [ indent_level , nesting_level ]
343344 end
344345
345- def free_indent_token ( opens , line_index )
346- last_token = opens . last
347- return unless last_token
348- if last_token . event == :on_heredoc_beg && last_token . pos . first < line_index + 1
349- # accept extra indent spaces inside heredoc
350- last_token
351- end
346+ def free_indent_token? ( token )
347+ return false unless token
348+ %i[ on_tstring_beg on_backtick on_regexp_beg on_symbeg ] . include? token &.event
352349 end
353350
354- def process_indent_level ( tokens , lines )
355- opens = IRB ::NestingParser . scan ( tokens )
356- depth , _nesting = calc_nesting_depth ( opens )
357- indent = depth * 2
358- line_index = lines . size - 2
359- if free_indent_token ( opens , line_index )
360- return [ indent , lines [ line_index ] [ /^ */ ] . length ] . max
361- end
362- indent
363- end
364-
365- def check_corresponding_token_depth ( tokens , lines , line_index )
351+ def process_indent_level ( tokens , lines , line_index , is_newline )
366352 line_results = IRB ::NestingParser . parse_by_line ( tokens )
367353 result = line_results [ line_index ]
368- return unless result
369- _tokens , prev_opens , opens , min_depth = result
370- depth , = calc_nesting_depth ( opens . take ( min_depth ) )
371- indent = depth * 2
372- free_indent_tok = free_indent_token ( opens , line_index )
373- prev_line_free_indent_tok = free_indent_token ( prev_opens , line_index - 1 )
374- if prev_line_free_indent_tok && prev_line_free_indent_tok != free_indent_tok
375- return indent
376- elsif free_indent_tok
377- return lines [ line_index ] [ /^ */ ] . length
354+ if result
355+ _tokens , prev_opens , next_opens , min_depth = result
356+ else
357+ # when last line is empty
358+ prev_opens = next_opens = line_results . last [ 2 ]
359+ min_depth = next_opens . size
378360 end
361+ depth , = calc_nesting_depth ( prev_opens . take ( min_depth ) )
379362 prev_depth , = calc_nesting_depth ( prev_opens )
380- indent if depth < prev_depth
363+ indent = 2 * [ depth , prev_depth ] . min
364+ preserve_indent = lines [ line_index - ( is_newline ? 1 : 0 ) ] [ /^ */ ] . size
365+ is_newline = false unless lines [ line_index ] . empty?
366+ if free_indent_token? ( prev_opens . last )
367+ if is_newline && prev_opens . last . pos [ 0 ] == line_index
368+ # first newline inside free-indent token
369+ indent
370+ else
371+ # accept any number of indent inside free-indent token
372+ preserve_indent
373+ end
374+ elsif prev_opens . last &.event == :on_embdoc_beg || next_opens . last &.event == :on_embdoc_beg
375+ if prev_opens . last . event == next_opens . last . event
376+ # accept any number of indent inside embdoc content
377+ preserve_indent
378+ else
379+ # =begin or =end
380+ 0
381+ end
382+ elsif prev_opens . last &.event == :on_heredoc_beg
383+ tok = prev_opens . last . tok
384+ if prev_opens . size < next_opens . size || prev_opens . last == next_opens . last
385+ if is_newline && lines [ line_index ] . empty? && line_results [ line_index - 1 ] [ 1 ] . last != prev_opens . last
386+ # first line in heredoc
387+ indent
388+ elsif tok . match? ( /^<<~/ )
389+ # accept extra indent spaces inside `<<~` heredoc
390+ [ indent , preserve_indent ] . max
391+ else
392+ # accept any number of indent inside other heredoc
393+ preserve_indent
394+ end
395+ else
396+ # heredoc close
397+ tok . match? ( /^<<[~-]/ ) ? 2 * ( prev_depth - 1 ) : 0
398+ end
399+ else
400+ indent
401+ end
381402 end
382403
383404 def ltype_from_open_tokens ( opens )
0 commit comments