Skip to content

Commit ca6ab3a

Browse files
committed
Implement heredoc embdoc and string indentation with bugfix
1 parent b7b46ee commit ca6ab3a

File tree

1 file changed

+63
-42
lines changed

1 file changed

+63
-42
lines changed

lib/irb/ruby-lex.rb

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)