[ruby/reline] multiline_repl do not need to depend on RubyLex

(https://github.com/ruby/reline/pull/502)

* multiline_repl do not need to depend on RubyLex

* Add auto indent test
This commit is contained in:
tomoya ishida 2023-01-18 14:28:13 +09:00 committed by git
parent 46066d0b96
commit 2d7e639549
3 changed files with 51 additions and 23 deletions

View File

@ -53,7 +53,9 @@ opt.on('--color-bold') {
} }
} }
opt.on('--auto-indent') { opt.on('--auto-indent') {
AutoIndent.new Reline.auto_indent_proc = lambda do |lines, line_index, byte_pointer, is_newline|
AutoIndent.calculate_indent(lines, line_index, byte_pointer, is_newline)
end
} }
opt.on('--dialog VAL') { |v| opt.on('--dialog VAL') { |v|
Reline.add_dialog_proc(:simple_dialog, lambda { Reline.add_dialog_proc(:simple_dialog, lambda {
@ -194,8 +196,7 @@ end
begin begin
prompt = ENV['RELINE_TEST_PROMPT'] || 'prompt> ' prompt = ENV['RELINE_TEST_PROMPT'] || 'prompt> '
puts 'Multiline REPL.' puts 'Multiline REPL.'
checker = TerminationChecker.new while code = Reline.readmultiline(prompt, true) { |code| TerminationChecker.terminated?(code) }
while code = Reline.readmultiline(prompt, true) { |code| checker.terminated?(code) }
case code.chomp case code.chomp
when 'exit', 'quit', 'q' when 'exit', 'quit', 'q'
exit 0 exit 0

View File

@ -1,28 +1,26 @@
require 'ripper' require 'ripper'
require 'irb/ruby-lex'
class TerminationChecker < RubyLex module TerminationChecker
def terminated?(code) def self.terminated?(code)
code.gsub!(/\n*$/, '').concat("\n") Ripper.sexp(code) ? true : false
tokens = self.class.ripper_lex_without_warning(code) end
continue = process_continue(tokens) end
code_block_open = check_code_block(code, tokens)
indent = process_nesting_level(tokens) module AutoIndent
ltype = process_literal_type(tokens) def self.calculate_indent(lines, line_index, byte_pointer, is_newline)
if code_block_open or ltype or continue or indent > 0 if is_newline
false 2 * nesting_level(lines[0..line_index - 1])
else else
true lines = lines.dup
lines[line_index] = lines[line_index]&.byteslice(0, byte_pointer)
prev_level = nesting_level(lines[0..line_index - 1])
level = nesting_level(lines[0..line_index])
2 * level if level < prev_level
end end
end end
end
class AutoIndent < RubyLex def self.nesting_level(lines)
def initialize code = lines.join("\n")
@context = Struct.new("MockIRBContext", :auto_indent_mode, :workspace, :local_variables).new(true, nil, []) code.scan(/if|def|\(|\[|\{/).size - code.scan(/end|\)|\]|\}/).size
end
def auto_indent(&block)
Reline.auto_indent_proc = block
end end
end end

View File

@ -661,6 +661,35 @@ begin
EOC EOC
end end
def test_auto_indent
start_terminal(10, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
"def hoge\nputs(\n1,\n2\n)\nend".lines do |line|
write line
end
close
assert_screen(<<~EOC)
Multiline REPL.
prompt> def hoge
prompt> puts(
prompt> 1,
prompt> 2
prompt> )
prompt> end
EOC
end
def test_auto_indent_when_inserting_line
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
write 'aa(bb(cc(dd(ee('
write "\C-b" * 5 + "\n"
close
assert_screen(<<~EOC)
Multiline REPL.
prompt> aa(bb(cc(d
prompt> d(ee(
EOC
end
def test_suppress_auto_indent_just_after_pasted def test_suppress_auto_indent_just_after_pasted
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.') start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
write("def hoge\n [[\n 3]]\ned") write("def hoge\n [[\n 3]]\ned")