Skip to content

Commit 5c8d3df

Browse files
st0012hasumikin
authored andcommitted
Store context in RubyLex
Some background for this refactor: 1. Through a RubyLex instance's lifetime, the context passed to its methods should be the same. Given that `Context` is only initialised in `Irb#initialize`, this should be true. 2. When `RubyLex` is initialised, the context object should be accessible. This is also true in all 3 of `RubyLex.new`'s invocations. With the above observations, we should be able to store the context in `RubyLex` as an instance variable. And doing so will make `RubyLex`'s instance methods easier to use and maintain.
1 parent fa0fbb7 commit 5c8d3df

File tree

4 files changed

+48
-44
lines changed

4 files changed

+48
-44
lines changed

lib/irb.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ def initialize(workspace = nil, input_method = nil)
468468
@context = Context.new(self, workspace, input_method)
469469
@context.main.extend ExtendCommandBundle
470470
@signal_status = :IN_IRB
471-
@scanner = RubyLex.new
471+
@scanner = RubyLex.new(@context)
472472
end
473473

474474
# A hook point for `debug` command's TracePoint after :IRB_EXIT as well as its clean-up
@@ -538,7 +538,7 @@ def eval_input
538538
@context.io.prompt
539539
end
540540

541-
@scanner.set_input(@context.io, context: @context) do
541+
@scanner.set_input(@context.io) do
542542
signal_status(:IN_INPUT) do
543543
if l = @context.io.gets
544544
print l if @context.verbose?
@@ -556,9 +556,9 @@ def eval_input
556556
end
557557
end
558558

559-
@scanner.set_auto_indent(@context) if @context.auto_indent_mode
559+
@scanner.set_auto_indent
560560

561-
@scanner.each_top_level_statement(@context) do |line, line_no|
561+
@scanner.each_top_level_statement do |line, line_no|
562562
signal_status(:IN_EVAL) do
563563
begin
564564
line.untaint if RUBY_VERSION < '2.7'

lib/irb/cmd/show_source.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ def find_source(str, irb_context)
4040
file, line = receiver.method(method).source_location if receiver.respond_to?(method)
4141
end
4242
if file && line
43-
Source.new(file: file, first_line: line, last_line: find_end(file, line))
43+
Source.new(file: file, first_line: line, last_line: find_end(file, line, irb_context))
4444
end
4545
end
4646

4747
private
4848

49-
def find_end(file, first_line)
49+
def find_end(file, first_line, irb_context)
5050
return first_line unless File.exist?(file)
51-
lex = RubyLex.new
51+
lex = RubyLex.new(irb_context)
5252
lines = File.read(file).lines[(first_line - 1)..-1]
5353
tokens = RubyLex.ripper_lex_without_warning(lines.join)
5454
prev_tokens = []

lib/irb/ruby-lex.rb

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ def initialize
1616
end
1717
end
1818

19-
def initialize
19+
def initialize(context)
20+
@context = context
2021
@exp_line_no = @line_no = 1
2122
@indent = 0
2223
@continue = false
@@ -42,13 +43,13 @@ def self.compile_with_errors_suppressed(code, line_no: 1)
4243
end
4344

4445
# io functions
45-
def set_input(io, context:, &block)
46+
def set_input(io, &block)
4647
@io = io
4748
if @io.respond_to?(:check_termination)
4849
@io.check_termination do |code|
4950
if Reline::IOGate.in_pasting?
50-
lex = RubyLex.new
51-
rest = lex.check_termination_in_prev_line(code, context: context)
51+
lex = RubyLex.new(@context)
52+
rest = lex.check_termination_in_prev_line(code)
5253
if rest
5354
Reline.delete_text
5455
rest.bytes.reverse_each do |c|
@@ -61,13 +62,13 @@ def set_input(io, context:, &block)
6162
else
6263
# Accept any single-line input for symbol aliases or commands that transform args
6364
command = code.split(/\s/, 2).first
64-
if context.symbol_alias?(command) || context.transform_args?(command)
65+
if @context.symbol_alias?(command) || @context.transform_args?(command)
6566
next true
6667
end
6768

6869
code.gsub!(/\s*\z/, '').concat("\n")
69-
tokens = self.class.ripper_lex_without_warning(code, context: context)
70-
ltype, indent, continue, code_block_open = check_state(code, tokens, context: context)
70+
tokens = self.class.ripper_lex_without_warning(code, context: @context)
71+
ltype, indent, continue, code_block_open = check_state(code, tokens)
7172
if ltype or indent > 0 or continue or code_block_open
7273
false
7374
else
@@ -80,7 +81,7 @@ def set_input(io, context:, &block)
8081
@io.dynamic_prompt do |lines|
8182
lines << '' if lines.empty?
8283
result = []
83-
tokens = self.class.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, context: context)
84+
tokens = self.class.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, context: @context)
8485
code = String.new
8586
partial_tokens = []
8687
unprocessed_tokens = []
@@ -93,7 +94,7 @@ def set_input(io, context:, &block)
9394
t_str.each_line("\n") do |s|
9495
code << s
9596
next unless s.include?("\n")
96-
ltype, indent, continue, code_block_open = check_state(code, partial_tokens, context: context)
97+
ltype, indent, continue, code_block_open = check_state(code, partial_tokens)
9798
result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + line_num_offset)
9899
line_num_offset += 1
99100
end
@@ -104,7 +105,7 @@ def set_input(io, context:, &block)
104105
end
105106

106107
unless unprocessed_tokens.empty?
107-
ltype, indent, continue, code_block_open = check_state(code, unprocessed_tokens, context: context)
108+
ltype, indent, continue, code_block_open = check_state(code, unprocessed_tokens)
108109
result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + line_num_offset)
109110
end
110111
result
@@ -187,11 +188,11 @@ def find_prev_spaces(line_index)
187188
prev_spaces
188189
end
189190

190-
def set_auto_indent(context)
191-
if @io.respond_to?(:auto_indent) and context.auto_indent_mode
191+
def set_auto_indent
192+
if @io.respond_to?(:auto_indent) and @context.auto_indent_mode
192193
@io.auto_indent do |lines, line_index, byte_pointer, is_newline|
193194
if is_newline
194-
@tokens = self.class.ripper_lex_without_warning(lines[0..line_index].join("\n"), context: context)
195+
@tokens = self.class.ripper_lex_without_warning(lines[0..line_index].join("\n"), context: @context)
195196
prev_spaces = find_prev_spaces(line_index)
196197
depth_difference = check_newline_depth_difference
197198
depth_difference = 0 if depth_difference < 0
@@ -200,18 +201,18 @@ def set_auto_indent(context)
200201
code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
201202
last_line = lines[line_index]&.byteslice(0, byte_pointer)
202203
code += last_line if last_line
203-
@tokens = self.class.ripper_lex_without_warning(code, context: context)
204+
@tokens = self.class.ripper_lex_without_warning(code, context: @context)
204205
check_corresponding_token_depth(lines, line_index)
205206
end
206207
end
207208
end
208209
end
209210

210-
def check_state(code, tokens, context:)
211+
def check_state(code, tokens)
211212
ltype = process_literal_type(tokens)
212213
indent = process_nesting_level(tokens)
213214
continue = process_continue(tokens)
214-
lvars_code = self.class.generate_local_variables_assign_code(context.local_variables)
215+
lvars_code = self.class.generate_local_variables_assign_code(@context.local_variables)
215216
code = "#{lvars_code}\n#{code}" if lvars_code
216217
code_block_open = check_code_block(code, tokens)
217218
[ltype, indent, continue, code_block_open]
@@ -232,13 +233,13 @@ def initialize_input
232233
@code_block_open = false
233234
end
234235

235-
def each_top_level_statement(context)
236+
def each_top_level_statement
236237
initialize_input
237238
catch(:TERM_INPUT) do
238239
loop do
239240
begin
240241
prompt
241-
unless l = lex(context)
242+
unless l = lex
242243
throw :TERM_INPUT if @line == ''
243244
else
244245
@line_no += l.count("\n")
@@ -268,15 +269,15 @@ def each_top_level_statement(context)
268269
end
269270
end
270271

271-
def lex(context)
272+
def lex
272273
line = @input.call
273274
if @io.respond_to?(:check_termination)
274275
return line # multiline
275276
end
276277
code = @line + (line.nil? ? '' : line)
277278
code.gsub!(/\s*\z/, '').concat("\n")
278-
@tokens = self.class.ripper_lex_without_warning(code, context: context)
279-
@ltype, @indent, @continue, @code_block_open = check_state(code, @tokens, context: context)
279+
@tokens = self.class.ripper_lex_without_warning(code, context: @context)
280+
@ltype, @indent, @continue, @code_block_open = check_state(code, @tokens)
280281
line
281282
end
282283

@@ -777,8 +778,8 @@ def process_literal_type(tokens)
777778
end
778779
end
779780

780-
def check_termination_in_prev_line(code, context:)
781-
tokens = self.class.ripper_lex_without_warning(code, context: context)
781+
def check_termination_in_prev_line(code)
782+
tokens = self.class.ripper_lex_without_warning(code, context: @context)
782783
past_first_newline = false
783784
index = tokens.rindex do |t|
784785
# traverse first token before last line

test/irb/test_ruby_lex.rb

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ def assert_indenting(lines, correct_space_count, add_new_line)
3636

3737
context = build_context
3838
context.auto_indent_mode = true
39-
ruby_lex = RubyLex.new()
39+
ruby_lex = RubyLex.new(context)
4040
io = MockIO_AutoIndent.new([lines, last_line_index, byte_pointer, add_new_line]) do |auto_indent|
4141
error_message = "Calculated the wrong number of spaces for:\n #{lines.join("\n")}"
4242
assert_equal(correct_space_count, auto_indent, error_message)
4343
end
44-
ruby_lex.set_input(io, context: context)
45-
ruby_lex.set_auto_indent(context)
44+
ruby_lex.set_input(io)
45+
ruby_lex.set_auto_indent
4646
end
4747

4848
def assert_nesting_level(lines, expected, local_variables: [])
@@ -58,14 +58,14 @@ def assert_code_block_open(lines, expected, local_variables: [])
5858
end
5959

6060
def ruby_lex_for_lines(lines, local_variables: [])
61-
ruby_lex = RubyLex.new()
62-
6361
context = build_context(local_variables)
62+
ruby_lex = RubyLex.new(context)
63+
6464
io = proc{ lines.join("\n") }
65-
ruby_lex.set_input(io, context: context) do
65+
ruby_lex.set_input(io) do
6666
lines.join("\n")
6767
end
68-
ruby_lex.lex(context)
68+
ruby_lex.lex
6969
ruby_lex
7070
end
7171

@@ -633,7 +633,8 @@ def dynamic_prompt(&block)
633633

634634
def assert_dynamic_prompt(lines, expected_prompt_list)
635635
pend if RUBY_ENGINE == 'truffleruby'
636-
ruby_lex = RubyLex.new()
636+
context = build_context
637+
ruby_lex = RubyLex.new(context)
637638
io = MockIO_DynamicPrompt.new(lines) do |prompt_list|
638639
error_message = <<~EOM
639640
Expected dynamic prompt:
@@ -647,8 +648,7 @@ def assert_dynamic_prompt(lines, expected_prompt_list)
647648
ruby_lex.set_prompt do |ltype, indent, continue, line_no|
648649
'%03d:%01d:%1s:%s ' % [line_no, indent, ltype, continue ? '*' : '>']
649650
end
650-
context = build_context
651-
ruby_lex.set_input(io, context: context)
651+
ruby_lex.set_input(io)
652652
end
653653

654654
def test_dyanmic_prompt
@@ -751,9 +751,10 @@ def test_unterminated_code
751751
end
752752

753753
def test_unterminated_heredoc_string_literal
754+
context = build_context
754755
['<<A;<<B', "<<A;<<B\n", "%W[\#{<<A;<<B", "%W[\#{<<A;<<B\n"].each do |code|
755756
tokens = RubyLex.ripper_lex_without_warning(code)
756-
string_literal = RubyLex.new.check_string_literal(tokens)
757+
string_literal = RubyLex.new(context).check_string_literal(tokens)
757758
assert_equal('<<A', string_literal&.tok)
758759
end
759760
end
@@ -779,16 +780,17 @@ def test_corresponding_token_depth_with_heredoc_and_embdoc
779780
p(
780781
)
781782
EOC
783+
context = build_context
782784
[reference_code, code_with_heredoc, code_with_embdoc].each do |code|
783-
lex = RubyLex.new
785+
lex = RubyLex.new(context)
784786
lines = code.lines
785787
lex.instance_variable_set('@tokens', RubyLex.ripper_lex_without_warning(code))
786788
assert_equal 2, lex.check_corresponding_token_depth(lines, lines.size)
787789
end
788790
end
789791

790792
def test_find_prev_spaces_with_multiline_literal
791-
lex = RubyLex.new
793+
lex = RubyLex.new(build_context)
792794
reference_code = <<~EOC.chomp
793795
if true
794796
1
@@ -813,8 +815,9 @@ def test_find_prev_spaces_with_multiline_literal
813815
world
814816
end
815817
EOC
818+
context = build_context
816819
[reference_code, code_with_percent_string, code_with_quoted_string].each do |code|
817-
lex = RubyLex.new
820+
lex = RubyLex.new(context)
818821
lex.instance_variable_set('@tokens', RubyLex.ripper_lex_without_warning(code))
819822
prev_spaces = (1..code.lines.size).map { |index| lex.find_prev_spaces index }
820823
assert_equal [0, 2, 2, 2, 2, 0], prev_spaces

0 commit comments

Comments
 (0)