Skip to content

Commit daff750

Browse files
authored
Move IO configuration to IRB::Irb (#681)
It shouldn't be `RubyLex`'s responsibility to handle IO. So this moves the configuration to `IRB::Irb`.
1 parent 8fa688e commit daff750

File tree

4 files changed

+766
-731
lines changed

4 files changed

+766
-731
lines changed

lib/irb.rb

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ def eval_input
537537
end
538538
end
539539

540-
@scanner.configure_io(@context.io)
540+
configure_io
541541

542542
@scanner.each_top_level_statement do |line, line_no, is_assignment|
543543
signal_status(:IN_EVAL) do
@@ -564,6 +564,58 @@ def eval_input
564564
end
565565
end
566566

567+
def configure_io
568+
if @context.io.respond_to?(:check_termination)
569+
@context.io.check_termination do |code|
570+
if Reline::IOGate.in_pasting?
571+
rest = @scanner.check_termination_in_prev_line(code)
572+
if rest
573+
Reline.delete_text
574+
rest.bytes.reverse_each do |c|
575+
Reline.ungetc(c)
576+
end
577+
true
578+
else
579+
false
580+
end
581+
else
582+
# Accept any single-line input for symbol aliases or commands that transform args
583+
next true if @scanner.single_line_command?(code)
584+
585+
_tokens, _opens, terminated = @scanner.check_code_state(code)
586+
terminated
587+
end
588+
end
589+
end
590+
if @context.io.respond_to?(:dynamic_prompt)
591+
@context.io.dynamic_prompt do |lines|
592+
lines << '' if lines.empty?
593+
tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, context: @context)
594+
line_results = IRB::NestingParser.parse_by_line(tokens)
595+
tokens_until_line = []
596+
line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
597+
line_tokens.each do |token, _s|
598+
# Avoid appending duplicated token. Tokens that include "\n" like multiline tstring_content can exist in multiple lines.
599+
tokens_until_line << token if token != tokens_until_line.last
600+
end
601+
continue = @scanner.should_continue?(tokens_until_line)
602+
@scanner.prompt(next_opens, continue, line_num_offset)
603+
end
604+
end
605+
end
606+
607+
if @context.io.respond_to?(:auto_indent) and @context.auto_indent_mode
608+
@context.io.auto_indent do |lines, line_index, byte_pointer, is_newline|
609+
next nil if lines == [nil] # Workaround for exit IRB with CTRL+d
610+
next nil if !is_newline && lines[line_index]&.byteslice(0, byte_pointer)&.match?(/\A\s*\z/)
611+
612+
code = lines[0..line_index].map { |l| "#{l}\n" }.join
613+
tokens = RubyLex.ripper_lex_without_warning(code, context: @context)
614+
@scanner.process_indent_level(tokens, lines, line_index, is_newline)
615+
end
616+
end
617+
end
618+
567619
def evaluate_line(line, line_no)
568620
# Transform a non-identifier alias (@, $) or keywords (next, break)
569621
command, args = line.split(/\s/, 2)

lib/irb/ruby-lex.rb

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -74,59 +74,6 @@ def set_input(&block)
7474
@input = block
7575
end
7676

77-
def configure_io(io)
78-
@io = io
79-
if @io.respond_to?(:check_termination)
80-
@io.check_termination do |code|
81-
if Reline::IOGate.in_pasting?
82-
rest = check_termination_in_prev_line(code)
83-
if rest
84-
Reline.delete_text
85-
rest.bytes.reverse_each do |c|
86-
Reline.ungetc(c)
87-
end
88-
true
89-
else
90-
false
91-
end
92-
else
93-
# Accept any single-line input for symbol aliases or commands that transform args
94-
next true if single_line_command?(code)
95-
96-
_tokens, _opens, terminated = check_code_state(code)
97-
terminated
98-
end
99-
end
100-
end
101-
if @io.respond_to?(:dynamic_prompt)
102-
@io.dynamic_prompt do |lines|
103-
lines << '' if lines.empty?
104-
tokens = self.class.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, context: @context)
105-
line_results = IRB::NestingParser.parse_by_line(tokens)
106-
tokens_until_line = []
107-
line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
108-
line_tokens.each do |token, _s|
109-
# Avoid appending duplicated token. Tokens that include "\n" like multiline tstring_content can exist in multiple lines.
110-
tokens_until_line << token if token != tokens_until_line.last
111-
end
112-
continue = should_continue?(tokens_until_line)
113-
prompt(next_opens, continue, line_num_offset)
114-
end
115-
end
116-
end
117-
118-
if @io.respond_to?(:auto_indent) and @context.auto_indent_mode
119-
@io.auto_indent do |lines, line_index, byte_pointer, is_newline|
120-
next nil if lines == [nil] # Workaround for exit IRB with CTRL+d
121-
next nil if !is_newline && lines[line_index]&.byteslice(0, byte_pointer)&.match?(/\A\s*\z/)
122-
123-
code = lines[0..line_index].map { |l| "#{l}\n" }.join
124-
tokens = self.class.ripper_lex_without_warning(code, context: @context)
125-
process_indent_level(tokens, lines, line_index, is_newline)
126-
end
127-
end
128-
end
129-
13077
def set_prompt(&block)
13178
@prompt = block
13279
end
@@ -240,7 +187,7 @@ def readmultiline
240187
save_prompt_to_context_io([], false, 0)
241188

242189
# multiline
243-
return @input.call if @io.respond_to?(:check_termination)
190+
return @input.call if @context.io.respond_to?(:check_termination)
244191

245192
# nomultiline
246193
code = ''
@@ -270,7 +217,7 @@ def each_top_level_statement
270217
break unless code
271218

272219
if code != "\n"
273-
code.force_encoding(@io.encoding)
220+
code.force_encoding(@context.io.encoding)
274221
yield code, @line_no, assignment_expression?(code)
275222
end
276223
@line_no += code.count("\n")

0 commit comments

Comments
 (0)