From 2ad9d0befc54499b63441b728a848bc2cb23a620 Mon Sep 17 00:00:00 2001 From: John Mair Date: Fri, 29 Jun 2012 05:24:18 +1200 Subject: [PATCH] Initial implementation of fast show-source command. This requires an updated method_source gem. This approach works by reading all code between the start of the class definition and the method with the highest line number in one go. Since we know this chunk of code is valid we can avoid the expensive eval-based validity checks. Once we have primed the expression buffer with this chunk of code we then start the expensive checks looking for a complete expression, but there shouldn't be many lines of code left since we've already processed the last method in the class definition. Using this technique i was able to show-source the 2000 line definition of ActiveRecord::Base in under a second. --- lib/pry/code.rb | 4 ++-- lib/pry/module_candidate.rb | 20 ++++++++++++++------ lib/pry/wrapped_module.rb | 3 ++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/pry/code.rb b/lib/pry/code.rb index ad50465a..94392881 100644 --- a/lib/pry/code.rb +++ b/lib/pry/code.rb @@ -352,8 +352,8 @@ class Pry # # @param [Fixnum] line_number (1-based) # @return [String] the code. - def expression_at(line_number) - self.class.expression_at(raw, line_number) + def expression_at(line_number, consume=0) + self.class.expression_at(raw, line_number, :consume => consume) end # Return an unformatted String of the code. diff --git a/lib/pry/module_candidate.rb b/lib/pry/module_candidate.rb index f5b5528e..44c26ba3 100644 --- a/lib/pry/module_candidate.rb +++ b/lib/pry/module_candidate.rb @@ -52,8 +52,10 @@ class Pry return @source if @source raise CommandError, "Could not locate source for #{wrapped}!" if file.nil? + end_line = end_method_source_location.last - @source = strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line)) + num_lines = end_line - line + @source = strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line, num_lines)) end # @raise [Pry::CommandError] If documentation cannot be found. @@ -86,7 +88,7 @@ class Pry return @source_location if @source_location mod_type_string = wrapped.class.to_s.downcase - file, line = method_source_location + file, line = start_method_source_location return nil if !file.is_a?(String) @@ -110,16 +112,22 @@ class Pry # starting point for the search for the candidate's definition. # @return [Array] The source location of the base method used to # calculate the source location of the candidate. - def method_source_location - return @method_source_location if @method_source_location + def start_method_source_location + adjusted_source_location(method_candidates[@rank].first.source_location) + end - file, line = method_candidates[@rank].source_location + def end_method_source_location + adjusted_source_location(method_candidates[@rank].last.source_location) + end + + def adjusted_source_location(sl) + file, line = sl if file && RbxPath.is_core_path?(file) file = RbxPath.convert_path_to_full(file) end - @method_source_location = [file, line] + [file, line] end # @param [String] doc The raw docstring to process. diff --git a/lib/pry/wrapped_module.rb b/lib/pry/wrapped_module.rb index 0466d388..c257a478 100644 --- a/lib/pry/wrapped_module.rb +++ b/lib/pry/wrapped_module.rb @@ -213,7 +213,8 @@ class Pry # the search in uncovering the module definition. def method_candidates @method_candidates ||= all_source_locations_by_popularity.map do |group| - group.last.sort_by(&:source_line).first # best candidate for group + methods_sorted_by_source_line = group.last.sort_by(&:source_line) + [methods_sorted_by_source_line.first, methods_sorted_by_source_line.last] end end