From 407b20470ab956d6b37012d23873f2aa58e48cbf Mon Sep 17 00:00:00 2001 From: John Mair Date: Tue, 25 Dec 2012 13:47:33 +0100 Subject: [PATCH] improve show-source/show-doc via Pry::CodeObject::Helpers mixin Pry::CodeObject::Helpers mixin provides the following methods: command? module_with_yard_docs? real_method_object? c_method? which are then mixed into the code objects: Pry::Command, Pry::Method, Pry::WrappedModule, Pry::WrappedModule::Candidate --- lib/pry.rb | 6 +- lib/pry/code_object.rb | 22 ++++++- lib/pry/command.rb | 1 + lib/pry/commands/show_doc.rb | 101 +++++++++++++++----------------- lib/pry/commands/show_source.rb | 77 ++++++++++++------------ lib/pry/method.rb | 1 + lib/pry/module_candidate.rb | 1 + lib/pry/wrapped_module.rb | 3 +- 8 files changed, 115 insertions(+), 97 deletions(-) diff --git a/lib/pry.rb b/lib/pry.rb index 76bbd904..3e7211ce 100644 --- a/lib/pry.rb +++ b/lib/pry.rb @@ -230,10 +230,11 @@ require 'pry/version' require 'pry/rbx_method' require 'pry/rbx_path' require 'pry/code' -require 'pry/method' -require 'pry/wrapped_module' require 'pry/history_array' require 'pry/helpers' +require 'pry/code_object' +require 'pry/method' +require 'pry/wrapped_module' require 'pry/history' require 'pry/command' require 'pry/command_set' @@ -247,4 +248,3 @@ require 'pry/pry_instance' require 'pry/cli' require 'pry/pager' require 'pry/terminal_info' -require 'pry/code_object' diff --git a/lib/pry/code_object.rb b/lib/pry/code_object.rb index 0fb4bc81..6575a8d7 100644 --- a/lib/pry/code_object.rb +++ b/lib/pry/code_object.rb @@ -1,6 +1,26 @@ class Pry class CodeObject - include Helpers::CommandHelpers + module Helpers + # we need this helper as some Pry::Method objects can wrap Procs + # @return [Boolean] + def real_method_object? + is_a?(::Method) || is_a?(::UnboundMethod) + end + + def c_method? + real_method_object? && source_type == :c + end + + def module_with_yard_docs? + is_a?(WrappedModule) && yard_docs? + end + + def command? + is_a?(Module) && self <= Pry::Command + end + end + + include Pry::Helpers::CommandHelpers class << self def lookup(str, target, _pry_, options={}) diff --git a/lib/pry/command.rb b/lib/pry/command.rb index ed096ded..530873f6 100644 --- a/lib/pry/command.rb +++ b/lib/pry/command.rb @@ -8,6 +8,7 @@ class Pry # which creates a ClassCommand. Please don't use this class directly. class Command extend Helpers::DocumentationHelpers + extend CodeObject::Helpers # represents a void return value for a command VOID_VALUE = Object.new diff --git a/lib/pry/commands/show_doc.rb b/lib/pry/commands/show_doc.rb index cf3aec84..23ff011f 100644 --- a/lib/pry/commands/show_doc.rb +++ b/lib/pry/commands/show_doc.rb @@ -30,32 +30,54 @@ class Pry opt.on :a, :all, "Show docs for all definitions and monkeypatches of the module/class" end - def process - code_object = Pry::CodeObject.lookup obj_name, target, _pry_, :super => opts[:super] + code_object = Pry::CodeObject.lookup(obj_name, target, _pry_, :super => opts[:super]) + raise Pry::CommandError, "Couldn't locate #{obj_name}!" if !code_object - if !code_object - raise Pry::CommandError, "Couldn't locate #{obj_name}!" - end - - if code_object.is_a?(Pry::WrappedModule) && opts.present?(:all) - # show all monkey patches for a module - # docs_for_all_modules_candidates writes its own headers - result = docs_for_all_module_candidates(code_object) + if show_docs_for_all_modules?(code_object) + # show docs for all monkey patches for a module + result = docs_and_headers_for_all_module_candidates(code_object) else # show the source for a specific code object - result = header(code_object) - result << Code.new(render_doc_markup_for(code_object), - opts.present?(:b) ? 1 : start_line_for(code_object), - :text). - with_line_numbers(use_line_numbers?).to_s - set_file_and_dir_locals(code_object.source_file) + result = docs_and_header_for_code_object(code_object) end + set_file_and_dir_locals(code_object.source_file) stagger_output result end - # simple utility function to piece together the name of the object + def docs_and_header_for_code_object(code_object) + result = header(code_object) + result << Code.new(render_doc_markup_for(code_object), + opts.present?(:b) ? 1 : start_line_for(code_object), + :text). + with_line_numbers(use_line_numbers?).to_s + end + + def docs_and_headers_for_all_module_candidates(mod) + result = "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n" + mod.number_of_candidates.times do |v| + candidate = mod.candidate(v) + begin + result << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{candidate.source_file} @ line #{candidate.source_line}:\n" + doc = Code.new(render_doc_markup_for(candidate), + opts.present?(:b) ? 1 : candidate.source_line, + :text).with_line_numbers(use_line_numbers?).to_s + result << "Number of lines: #{doc.lines.count}\n\n" << doc + rescue Pry::RescuableException + result << "\nNo code found.\n" + + next + end + end + result + end + + def show_docs_for_all_modules?(code_object) + code_object.is_a?(Pry::WrappedModule) && opts.present?(:all) + end + + # simple function to piece together the name of the object # passed in from the arg list def obj_name @obj_name ||= args.empty? ? nil : args.join(" ") @@ -65,8 +87,8 @@ class Pry def render_doc_markup_for(code_object) docs = docs_for(code_object) - if code_object_is_command?(code_object) - # command 'help' doesn't want markup highlighting + if code_object.command? + # command '--help' shouldn't use markup highlighting docs else process_comment_markup(docs) @@ -79,7 +101,7 @@ class Pry # have multiple docs, but methods can only be doc'd once so we # dont need to check them) def docs_for(code_object) - if code_object.is_a?(WrappedModule) && code_object.yard_docs? + if code_object.module_with_yard_docs? # yard docs code_object.yard_doc else @@ -88,17 +110,11 @@ class Pry end end - # we need this helper as some Pry::Method objects can wrap Procs - # @return [Boolean] - def real_method_object?(code_object) - code_object.is_a?(::Method) || code_object.is_a?(::UnboundMethod) - end - # takes into account possible yard docs, and returns yard_file / yard_line # Also adjusts for start line of comments (using start_line_for), which it has to infer # by subtracting number of lines of comment from start line of code_object def file_and_line_for(code_object) - if code_object.is_a?(WrappedModule) && code_object.yard_docs? + if code_object.module_with_yard_docs? [code_object.yard_file, code_object.yard_line] else [code_object.source_file, start_line_for(code_object)] @@ -110,13 +126,13 @@ class Pry def header(code_object) file_name, line_num = file_and_line_for(code_object) h = "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} " - if real_method_object?(code_object) && code_object.source_type == :c + if code_object.c_method? h << "(C Method):" else h << "@ line #{line_num}:" end - if real_method_object?(code_object) + if code_object.real_method_object? h << "\n#{text.bold("Owner:")} #{code_object.owner || "N/A"}\n" h << "#{text.bold("Visibility:")} #{code_object.visibility}\n" h << "#{text.bold("Signature:")} #{code_object.signature}" @@ -125,33 +141,11 @@ class Pry "#{docs_for(code_object).lines.count}\n\n" end - def docs_for_all_module_candidates(mod) - result = "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n" - mod.number_of_candidates.times do |v| - candidate = mod.candidate(v) - begin - result << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{candidate.source_file} @ line #{candidate.source_line}:\n" - doc = Code.new(render_doc_markup_for(candidate), - opts.present?(:b) ? 1 : candidate.source_line, - :text).with_line_numbers(use_line_numbers?).to_s - result << "Number of lines: #{doc.lines.count}\n\n" << doc - rescue Pry::RescuableException - result << "\nNo code found.\n" - next - end - end - result - end - - def code_object_is_command?(code_object) - code_object.is_a?(Module) && code_object <= Pry::Command - end - - # figure out start line of docs by back calculating based on + # figure out start line of docs by back-calculating based on # number of lines in the comment and the start line of the code_object # @return [Fixnum] start line of docs def start_line_for(code_object) - if code_object_is_command?(code_object) + if code_object.command? 1 else code_object.source_line.nil? ? 1 : @@ -179,5 +173,6 @@ class Pry end end end + Pry::Commands.alias_command "?", "show-doc" end diff --git a/lib/pry/commands/show_source.rb b/lib/pry/commands/show_source.rb index 44bb1c10..f9f970f2 100644 --- a/lib/pry/commands/show_source.rb +++ b/lib/pry/commands/show_source.rb @@ -37,56 +37,28 @@ class Pry end def process - code_object = Pry::CodeObject.lookup obj_name, target, _pry_, :super => opts[:super] + code_object = Pry::CodeObject.lookup(obj_name, target, _pry_, :super => opts[:super]) + raise Pry::CommandError, "Couldn't locate #{obj_name}!" if !code_object - if !code_object - raise Pry::CommandError, "Couldn't locate #{obj_name}!" - end - - if code_object.is_a?(Pry::WrappedModule) && opts.present?(:all) + if show_source_for_all_modules?(code_object) # show all monkey patches for a module - result = source_for_all_module_candidates(code_object) + result = source_and_headers_for_all_module_candidates(code_object) else # show the source for a specific code object - result = header(code_object) - result << Code.new(code_object.source, start_line_for(code_object)). - with_line_numbers(use_line_numbers?).to_s - - set_file_and_dir_locals(code_object.source_file) + result = source_and_header_for_code_object(code_object) end + set_file_and_dir_locals(code_object.source_file) stagger_output result end - def obj_name - @obj_name ||= args.empty? ? nil : args.join(" ") + def source_and_header_for_code_object(code_object) + result = header(code_object) + result << Code.new(code_object.source, start_line_for(code_object)). + with_line_numbers(use_line_numbers?).to_s end - # we need this helper as some Pry::Method objects can wrap Procs - # @return [Boolean] - def real_method_object?(code_object) - code_object.is_a?(::Method) || code_object.is_a?(::UnboundMethod) - end - - # Generate a header (meta-data information) for all the code - # object types: methods, modules, commands, procs... - def header(code_object) - file_name, line_num = code_object.source_file, code_object.source_line - h = "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} " - if real_method_object?(code_object) && code_object.source_type == :c - h << "(C Method):" - else - h << "@ line #{line_num}:" - end - - if real_method_object?(code_object) - h << "\n#{text.bold("Owner:")} #{code_object.owner || "N/A"}\n" - h << "#{text.bold("Visibility:")} #{code_object.visibility}" - end - h << "\n#{Pry::Helpers::Text.bold('Number of lines:')} #{code_object.source.lines.count}\n\n" - end - - def source_for_all_module_candidates(mod) + def source_and_headers_for_all_module_candidates(mod) result = "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n" mod.number_of_candidates.times do |v| candidate = mod.candidate(v) @@ -102,6 +74,33 @@ class Pry result end + def show_source_for_all_modules?(code_object) + code_object.is_a?(Pry::WrappedModule) && opts.present?(:all) + end + + def obj_name + @obj_name ||= args.empty? ? nil : args.join(" ") + end + + # Generate a header (meta-data information) for all the code + # object types: methods, modules, commands, procs... + def header(code_object) + file_name, line_num = code_object.source_file, code_object.source_line + h = "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} " + if code_object.c_method? + h << "(C Method):" + else + h << "@ line #{line_num}:" + end + + if code_object.real_method_object? + h << "\n#{text.bold("Owner:")} #{code_object.owner || "N/A"}\n" + h << "#{text.bold("Visibility:")} #{code_object.visibility}" + end + h << "\n#{Pry::Helpers::Text.bold('Number of lines:')} " << + "#{code_object.source.lines.count}\n\n" + end + def start_line_for(code_object) if opts.present?(:'base-one') 1 diff --git a/lib/pry/method.rb b/lib/pry/method.rb index 890c5f0b..d645e88e 100644 --- a/lib/pry/method.rb +++ b/lib/pry/method.rb @@ -21,6 +21,7 @@ class Pry include Helpers::BaseHelpers include RbxMethod if Helpers::BaseHelpers.rbx? include Helpers::DocumentationHelpers + include CodeObject::Helpers class << self # Given a string representing a method name and optionally a binding to diff --git a/lib/pry/module_candidate.rb b/lib/pry/module_candidate.rb index e35fdc89..1da0968e 100644 --- a/lib/pry/module_candidate.rb +++ b/lib/pry/module_candidate.rb @@ -9,6 +9,7 @@ class Pry # for a monkeypatch (reopening) of a class/module. class Candidate include Pry::Helpers::DocumentationHelpers + include Pry::CodeObject::Helpers extend Forwardable # @return [String] The file where the module definition is located. diff --git a/lib/pry/wrapped_module.rb b/lib/pry/wrapped_module.rb index ad63701e..14dcfa03 100644 --- a/lib/pry/wrapped_module.rb +++ b/lib/pry/wrapped_module.rb @@ -14,7 +14,8 @@ class Pry end class WrappedModule - include Pry::Helpers::BaseHelpers + include Helpers::BaseHelpers + include CodeObject::Helpers attr_reader :wrapped