From c63e4c40359092bc707ade29d7ce851dcef44afa Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Tue, 13 Feb 2024 13:33:33 +0000 Subject: [PATCH] Refactor eval_path and `SourceFinder::Source` (#870) * Assign `@eval_path` through `irb_path=` method This simplifies the original caching logic for the `eval_path` method and makes it easier to understand. * Refactor SourceFinder::Source --- lib/irb/context.rb | 42 +++++++++++++++++++++++++--------------- lib/irb/ext/loader.rb | 6 +++--- lib/irb/source_finder.rb | 13 ++++++++----- test/irb/test_context.rb | 11 +++++++---- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 814a8bd4a..e50958978 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -77,7 +77,7 @@ def initialize(irb, workspace = nil, input_method = nil) else @irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s end - @irb_path = "(" + @irb_name + ")" + self.irb_path = "(" + @irb_name + ")" case input_method when nil @@ -121,11 +121,11 @@ def initialize(irb, workspace = nil, input_method = nil) when '-' @io = FileInputMethod.new($stdin) @irb_name = '-' - @irb_path = '-' + self.irb_path = '-' when String @io = FileInputMethod.new(input_method) @irb_name = File.basename(input_method) - @irb_path = input_method + self.irb_path = input_method else @io = input_method end @@ -246,9 +246,27 @@ def main # Can be either name from IRB.conf[:IRB_NAME], or the number of # the current job set by JobManager, such as irb#2 attr_accessor :irb_name - # Can be either the #irb_name surrounded by parenthesis, or the - # +input_method+ passed to Context.new - attr_accessor :irb_path + + # Can be one of the following: + # - the #irb_name surrounded by parenthesis + # - the +input_method+ passed to Context.new + # - the file path of the current IRB context in a binding.irb session + attr_reader :irb_path + + # Sets @irb_path to the given +path+ as well as @eval_path + # @eval_path is used for evaluating code in the context of IRB session + # It's the same as irb_path, but with the IRB name postfix + # This makes sure users can distinguish the methods defined in the IRB session + # from the methods defined in the current file's context, especially with binding.irb + def irb_path=(path) + @irb_path = path + + if File.exist?(path) + @eval_path = "#{path}(#{IRB.conf[:IRB_NAME]})" + else + @eval_path = path + end + end # Whether multiline editor mode is enabled or not. # @@ -557,7 +575,7 @@ def evaluate(line, line_no) # :nodoc: if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty? last_proc = proc do - result = @workspace.evaluate(line, eval_path, line_no) + result = @workspace.evaluate(line, @eval_path, line_no) end IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item| _name, callback, arg = item @@ -568,20 +586,12 @@ def evaluate(line, line_no) # :nodoc: end end.call else - result = @workspace.evaluate(line, eval_path, line_no) + result = @workspace.evaluate(line, @eval_path, line_no) end set_last_value(result) end - private def eval_path - # We need to use differente path to distinguish source_location of method defined in the actual file and method defined in irb session. - if !defined?(@irb_path_existence) || @irb_path_existence[0] != irb_path - @irb_path_existence = [irb_path, File.exist?(irb_path)] - end - @irb_path_existence[1] ? "#{irb_path}(#{IRB.conf[:IRB_NAME]})" : irb_path - end - def inspect_last_value # :nodoc: @inspect_method.inspect_value(@last_value) end diff --git a/lib/irb/ext/loader.rb b/lib/irb/ext/loader.rb index d65695df3..5bd25546f 100644 --- a/lib/irb/ext/loader.rb +++ b/lib/irb/ext/loader.rb @@ -98,13 +98,13 @@ def load_file(path, priv = nil) def old # :nodoc: back_io = @io - back_path = @irb_path + back_path = irb_path back_name = @irb_name back_scanner = @irb.scanner begin @io = FileInputMethod.new(path) @irb_name = File.basename(path) - @irb_path = path + self.irb_path = path @irb.signal_status(:IN_LOAD) do if back_io.kind_of?(FileInputMethod) @irb.eval_input @@ -119,7 +119,7 @@ def old # :nodoc: ensure @io = back_io @irb_name = back_name - @irb_path = back_path + self.irb_path = back_path @irb.scanner = back_scanner end end diff --git a/lib/irb/source_finder.rb b/lib/irb/source_finder.rb index 26aae7643..07e864dad 100644 --- a/lib/irb/source_finder.rb +++ b/lib/irb/source_finder.rb @@ -27,7 +27,7 @@ def file_content def colorized_content if !binary_file? && file_exist? - end_line = Source.find_end(file_content, @line) + end_line = find_end # To correctly colorize, we need to colorize full content and extract the relevant lines. colored = IRB::Color.colorize_code(file_content) colored.lines[@line - 1...end_line].join @@ -36,9 +36,12 @@ def colorized_content end end - def self.find_end(code, first_line) + private + + def find_end lex = RubyLex.new - lines = code.lines[(first_line - 1)..-1] + code = file_content + lines = code.lines[(@line - 1)..-1] tokens = RubyLex.ripper_lex_without_warning(lines.join) prev_tokens = [] @@ -49,10 +52,10 @@ def self.find_end(code, first_line) continue = lex.should_continue?(prev_tokens) syntax = lex.check_code_syntax(code, local_variables: []) if !continue && syntax == :valid - return first_line + lnum + return @line + lnum end end - first_line + @line end end diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index 0fdd847a6..adf472baa 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -666,13 +666,16 @@ def test_lineno ], out) end - def test_eval_path + def test_irb_path_setter @context.irb_path = __FILE__ - assert_equal("#{__FILE__}(irb)", @context.send(:eval_path)) + assert_equal(__FILE__, @context.irb_path) + assert_equal("#{__FILE__}(irb)", @context.instance_variable_get(:@eval_path)) @context.irb_path = 'file/does/not/exist' - assert_equal('file/does/not/exist', @context.send(:eval_path)) + assert_equal('file/does/not/exist', @context.irb_path) + assert_equal('file/does/not/exist', @context.instance_variable_get(:@eval_path)) @context.irb_path = "#{__FILE__}(irb)" - assert_equal("#{__FILE__}(irb)", @context.send(:eval_path)) + assert_equal("#{__FILE__}(irb)", @context.irb_path) + assert_equal("#{__FILE__}(irb)", @context.instance_variable_get(:@eval_path)) end def test_build_completor