Skip to content

Commit 6c761c2

Browse files
committed
Re-implement prev_spaces feature for pasted code
1 parent b7f4bfa commit 6c761c2

File tree

2 files changed

+93
-6
lines changed

2 files changed

+93
-6
lines changed

lib/irb/ruby-lex.rb

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ def calc_indent_level(opens)
367367
indent_level = 0
368368
end
369369
end
370-
when :on_tstring_beg, :on_regexp_beg, :on_symbeg
370+
when :on_tstring_beg, :on_regexp_beg, :on_symbeg, :on_backtick
371371
# can be indented if t.tok starts with `%`
372372
when :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_embexpr_beg
373373
# can be indented but not indented in current implementation
@@ -386,6 +386,26 @@ def free_indent_token?(token)
386386
FREE_INDENT_TOKENS.include?(token&.event)
387387
end
388388

389+
# Calculates the difference of pasted code's indent and indent calculated from tokens
390+
def indent_difference(lines, line_results, line_index)
391+
loop do
392+
_tokens, prev_opens, _next_opens, min_depth = line_results[line_index]
393+
open_token = prev_opens.last
394+
if !open_token || (open_token.event != :on_heredoc_beg && !free_indent_token?(open_token))
395+
# If the leading whitespace is an indent, return the difference
396+
indent_level = calc_indent_level(prev_opens.take(min_depth))
397+
calculated_indent = 2 * indent_level
398+
actual_indent = lines[line_index][/^ */].size
399+
return actual_indent - calculated_indent
400+
elsif open_token.event == :on_heredoc_beg && open_token.tok.match?(/^<<[^-~]/)
401+
return 0
402+
end
403+
# If the leading whitespace is not an indent but part of a multiline token
404+
# Calculate base_indent of the multiline token's beginning line
405+
line_index = open_token.pos[0] - 1
406+
end
407+
end
408+
389409
def process_indent_level(tokens, lines, line_index, is_newline)
390410
line_results = IRB::NestingParser.parse_by_line(tokens)
391411
result = line_results[line_index]
@@ -406,10 +426,20 @@ def process_indent_level(tokens, lines, line_index, is_newline)
406426
prev_open_token = prev_opens.last
407427
next_open_token = next_opens.last
408428

429+
# Calculates base indent for pasted code on the line where prev_open_token is located
430+
# irb(main):001:1* if a # base_indent is 2, indent calculated from tokens is 0
431+
# irb(main):002:1* if b # base_indent is 6, indent calculated from tokens is 2
432+
# irb(main):003:0> c # base_indent is 6, indent calculated from tokens is 4
433+
if prev_open_token
434+
base_indent = [0, indent_difference(lines, line_results, prev_open_token.pos[0] - 1)].max
435+
else
436+
base_indent = 0
437+
end
438+
409439
if free_indent_token?(prev_open_token)
410440
if is_newline && prev_open_token.pos[0] == line_index
411441
# First newline inside free-indent token
412-
indent
442+
base_indent + indent
413443
else
414444
# Accept any number of indent inside free-indent token
415445
preserve_indent
@@ -427,21 +457,21 @@ def process_indent_level(tokens, lines, line_index, is_newline)
427457
if prev_opens.size <= next_opens.size
428458
if is_newline && lines[line_index].empty? && line_results[line_index - 1][1].last != next_open_token
429459
# First line in heredoc
430-
indent
460+
tok.match?(/^<<[-~]/) ? base_indent + indent : indent
431461
elsif tok.match?(/^<<~/)
432462
# Accept extra indent spaces inside `<<~` heredoc
433-
[indent, preserve_indent].max
463+
[base_indent + indent, preserve_indent].max
434464
else
435465
# Accept any number of indent inside other heredoc
436466
preserve_indent
437467
end
438468
else
439469
# Heredoc close
440470
prev_line_indent_level = calc_indent_level(prev_opens)
441-
tok.match?(/^<<[~-]/) ? 2 * (prev_line_indent_level - 1) : 0
471+
tok.match?(/^<<[~-]/) ? base_indent + 2 * (prev_line_indent_level - 1) : 0
442472
end
443473
else
444-
indent
474+
base_indent + indent
445475
end
446476
end
447477

test/irb/test_ruby_lex.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,63 @@ def dynamic_prompt(&block)
706706
end
707707
end
708708

709+
def test_pasted_code_keep_base_indent_spaces
710+
input_with_correct_indents = [
711+
Row.new(%q( def foo), 0, 6, 1),
712+
Row.new(%q( if bar), 6, 10, 2),
713+
Row.new(%q( [1), 10, 12, 3),
714+
Row.new(%q( ]+[["a), 10, 14, 4),
715+
Row.new(%q(b" + `c), 0, 14, 4),
716+
Row.new(%q(d` + /e), 0, 14, 4),
717+
Row.new(%q(f/ + :"g), 0, 14, 4),
718+
Row.new(%q(h".tap do), 0, 16, 5),
719+
Row.new(%q( 1), 16, 16, 5),
720+
Row.new(%q( end), 14, 14, 4),
721+
Row.new(%q( ]), 12, 12, 3),
722+
Row.new(%q( ]), 10, 10, 2),
723+
Row.new(%q( end), 8, 6, 1),
724+
Row.new(%q( end), 4, 0, 0),
725+
]
726+
lines = []
727+
input_with_correct_indents.each do |row|
728+
lines << row.content
729+
assert_row_indenting(lines, row)
730+
assert_indent_level(lines, row.indent_level)
731+
end
732+
end
733+
734+
def test_pasted_code_keep_base_indent_spaces_with_heredoc
735+
input_with_correct_indents = [
736+
Row.new(%q( def foo), 0, 6, 1),
737+
Row.new(%q( if bar), 6, 10, 2),
738+
Row.new(%q( [1), 10, 12, 3),
739+
Row.new(%q( ]+[["a), 10, 14, 4),
740+
Row.new(%q(b" + <<~A + <<-B + <<C), 0, 16, 5),
741+
Row.new(%q( a#{), 16, 16, 5),
742+
Row.new(%q( 1), 16, 16, 5),
743+
Row.new(%q( }), 16, 16, 5),
744+
Row.new(%q( A), 14, 16, 5),
745+
Row.new(%q( b#{), 16, 16, 5),
746+
Row.new(%q( 1), 16, 16, 5),
747+
Row.new(%q( }), 16, 16, 5),
748+
Row.new(%q( B), 14, 0, 0),
749+
Row.new(%q(c#{), 0, 0, 0),
750+
Row.new(%q(1), 0, 0, 0),
751+
Row.new(%q(}), 0, 0, 0),
752+
Row.new(%q(C), 0, 14, 4),
753+
Row.new(%q( ]), 12, 12, 3),
754+
Row.new(%q( ]), 10, 10, 2),
755+
Row.new(%q( end), 8, 6, 1),
756+
Row.new(%q( end), 4, 0, 0),
757+
]
758+
lines = []
759+
input_with_correct_indents.each do |row|
760+
lines << row.content
761+
assert_row_indenting(lines, row)
762+
assert_indent_level(lines, row.indent_level)
763+
end
764+
end
765+
709766
def assert_dynamic_prompt(lines, expected_prompt_list)
710767
pend if RUBY_ENGINE == 'truffleruby'
711768
context = build_context

0 commit comments

Comments
 (0)