diff options
author | robert <therobot.johnny5@gmail.com> | 2014-04-01 05:55:21 +0200 |
---|---|---|
committer | Ryan Fitzgerald <rwfitzge@gmail.com> | 2014-04-27 14:12:45 -0700 |
commit | 4bab51f53ccfc85b1cbc4d92c4f2c97059bf61b3 (patch) | |
tree | f792410b55db0bd07ff39cf35d05301ef58a059c | |
parent | cf0e8c1051324917f54e00dd34cd813f8aa705f5 (diff) | |
download | pry-4bab51f53ccfc85b1cbc4d92c4f2c97059bf61b3.tar.gz |
rewrite Pry::InputCompleter as a class who wraps '_pry_'.
this commit changes InputCompleter from a module implemented on top
of singleton/class methods to a class who wraps '_pry_' and reads
from _pry_.input, who is assumed to be Readline.
pry-bond will also need to be updated to have a similar style API.
-rw-r--r-- | lib/pry/config/default.rb | 2 | ||||
-rw-r--r-- | lib/pry/input_completer.rb | 234 | ||||
-rw-r--r-- | lib/pry/pry_instance.rb | 6 | ||||
-rw-r--r-- | spec/completion_spec.rb | 17 |
4 files changed, 129 insertions, 130 deletions
diff --git a/lib/pry/config/default.rb b/lib/pry/config/default.rb index b2b42ef1..7b6ef4d5 100644 --- a/lib/pry/config/default.rb +++ b/lib/pry/config/default.rb @@ -38,7 +38,7 @@ class Pry::Config::Default :ls => proc { Pry::Config.from_hash(Pry::Command::Ls::DEFAULT_OPTIONS) }, :completer => proc { require "pry/input_completer" - Pry::InputCompleter.start + Pry::InputCompleter } } diff --git a/lib/pry/input_completer.rb b/lib/pry/input_completer.rb index 82eafb79..7c013226 100644 --- a/lib/pry/input_completer.rb +++ b/lib/pry/input_completer.rb @@ -1,112 +1,116 @@ -class Pry - # taken from irb - # Implements tab completion for Readline in Pry - module InputCompleter - def self.start - if Readline.respond_to?("basic_word_break_characters=") - Readline.basic_word_break_characters = " \t\n\"\\'`><=;|&{(" - end - - Readline.completion_append_character = nil - self +# taken from irb +# Implements tab completion for Readline in Pry +class Pry::InputCompleter + + NUMERIC_REGEXP = /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/ + ARRAY_REGEXP = /^([^\]]*\])\.([^.]*)$/ + SYMBOL_REGEXP = /^(:[^:.]*)$/ + SYMBOL_METHOD_CALL_REGEXP = /^(:[^:.]+)\.([^.]*)$/ + REGEX_REGEXP = /^(\/[^\/]*\/)\.([^.]*)$/ + PROC_OR_HASH_REGEXP = /^([^\}]*\})\.([^.]*)$/ + TOPLEVEL_LOOKUP_REGEXP = /^::([A-Z][^:\.\(]*)$/ + CONSTANT_REGEXP = /^([A-Z][A-Za-z0-9]*)$/ + CONSTANT_OR_METHOD_REGEXP = /^([A-Z].*)::([^:.]*)$/ + HEX_REGEXP = /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/ + GLOBALVARIABLE_REGEXP = /^(\$[^.]*)$/ + VARIABLE_REGEXP = /^([^."].*)\.([^.]*)$/ + + ReservedWords = [ + "BEGIN", "END", + "alias", "and", + "begin", "break", + "case", "class", + "def", "defined", "do", + "else", "elsif", "end", "ensure", + "false", "for", + "if", "in", + "module", + "next", "nil", "not", + "or", + "redo", "rescue", "retry", "return", + "self", "super", + "then", "true", + "undef", "unless", "until", + "when", "while", + "yield" ] + + Operators = [ + "%", "&", "*", "**", "+", "-", "/", + "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>", + "[]", "[]=", "^", "!", "!=", "!~" + ] + + WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{(" + + def initialize(pry = nil) + if pry + @pry = pry + @input = pry.input + else + @input = Readline end + @input.basic_word_break_characters = WORD_ESCAPE_STR if @input.respond_to?(:basic_word_break_characters=) + @input.completion_append_character = nil if @input.respond_to?(:completion_append_character=) + end - NUMERIC_REGEXP = /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/ - ARRAY_REGEXP = /^([^\]]*\])\.([^.]*)$/ - SYMBOL_REGEXP = /^(:[^:.]*)$/ - SYMBOL_METHOD_CALL_REGEXP = /^(:[^:.]+)\.([^.]*)$/ - REGEX_REGEXP = /^(\/[^\/]*\/)\.([^.]*)$/ - PROC_OR_HASH_REGEXP = /^([^\}]*\})\.([^.]*)$/ - TOPLEVEL_LOOKUP_REGEXP = /^::([A-Z][^:\.\(]*)$/ - CONSTANT_REGEXP = /^([A-Z][A-Za-z0-9]*)$/ - CONSTANT_OR_METHOD_REGEXP = /^([A-Z].*)::([^:.]*)$/ - HEX_REGEXP = /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/ - GLOBALVARIABLE_REGEXP = /^(\$[^.]*)$/ - VARIABLE_REGEXP = /^([^."].*)\.([^.]*)$/ - - ReservedWords = [ - "BEGIN", "END", - "alias", "and", - "begin", "break", - "case", "class", - "def", "defined", "do", - "else", "elsif", "end", "ensure", - "false", "for", - "if", "in", - "module", - "next", "nil", "not", - "or", - "redo", "rescue", "retry", "return", - "self", "super", - "then", "true", - "undef", "unless", "until", - "when", "while", - "yield" ] - - Operators = [ - "%", "&", "*", "**", "+", "-", "/", - "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>", - "[]", "[]=", "^", "!", "!=", "!~" - ] - - # Return a new completion proc for use by Readline. - # @param [Binding] input The current binding context. - # @param [Array<String>] options The array of Pry commands. - def self.call(input, options) - custom_completions = options[:custom_completions] || [] - - # if there are multiple contexts e.g. cd 1/2/3 - # get new target for 1/2 and find candidates for 3 - path, input = build_path(input) - - if path.call.empty? - target = options[:target] - else - target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, options[:pry]) - target = target.last - end + # Return a new completion proc for use by Readline. + # @param [Binding] input The current binding context. + # @param [Array<String>] options The array of Pry commands. + def call(input, options) + custom_completions = options[:custom_completions] || [] + + # if there are multiple contexts e.g. cd 1/2/3 + # get new target for 1/2 and find candidates for 3 + path, input = build_path(input) + + if path.call.empty? + target = options[:target] + else + target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, @pry) + target = target.last + end - begin - bind = target - # Complete stdlib symbols - case input - when REGEX_REGEXP # Regexp - receiver = $1 - message = Regexp.quote($2) - candidates = Regexp.instance_methods.collect(&:to_s) - select_message(path, receiver, message, candidates) - when ARRAY_REGEXP # Array - receiver = $1 - message = Regexp.quote($2) - candidates = Array.instance_methods.collect(&:to_s) - select_message(path, receiver, message, candidates) - when PROC_OR_HASH_REGEXP # Proc or Hash - receiver = $1 - message = Regexp.quote($2) - candidates = Proc.instance_methods.collect(&:to_s) - candidates |= Hash.instance_methods.collect(&:to_s) - select_message(path, receiver, message, candidates) - when SYMBOL_REGEXP # Symbol - if Symbol.respond_to?(:all_symbols) - sym = Regexp.quote($1) - candidates = Symbol.all_symbols.collect{|s| ":" << s.id2name} - candidates.grep(/^#{sym}/) - else - [] - end - when TOPLEVEL_LOOKUP_REGEXP # Absolute Constant or class methods - receiver = $1 - candidates = Object.constants.collect(&:to_s) - candidates.grep(/^#{receiver}/).collect{|e| "::" << e} - when CONSTANT_REGEXP # Constant - message = $1 - begin - context = target.eval("self") - context = context.class unless context.respond_to? :constants - candidates = context.constants.collect(&:to_s) - rescue - candidates = [] - end + begin + bind = target + # Complete stdlib symbols + case input + when REGEX_REGEXP # Regexp + receiver = $1 + message = Regexp.quote($2) + candidates = Regexp.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) + when ARRAY_REGEXP # Array + receiver = $1 + message = Regexp.quote($2) + candidates = Array.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) + when PROC_OR_HASH_REGEXP # Proc or Hash + receiver = $1 + message = Regexp.quote($2) + candidates = Proc.instance_methods.collect(&:to_s) + candidates |= Hash.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) + when SYMBOL_REGEXP # Symbol + if Symbol.respond_to?(:all_symbols) + sym = Regexp.quote($1) + candidates = Symbol.all_symbols.collect{|s| ":" << s.id2name} + candidates.grep(/^#{sym}/) + else + [] + end + when TOPLEVEL_LOOKUP_REGEXP # Absolute Constant or class methods + receiver = $1 + candidates = Object.constants.collect(&:to_s) + candidates.grep(/^#{receiver}/).collect{|e| "::" << e} + when CONSTANT_REGEXP # Constant + message = $1 + begin + context = target.eval("self") + context = context.class unless context.respond_to? :constants + candidates = context.constants.collect(&:to_s) + rescue + candidates = [] + end candidates = candidates.grep(/^#{message}/).collect(&path) when CONSTANT_OR_METHOD_REGEXP # Constant or class methods receiver = $1 @@ -192,10 +196,10 @@ class Pry select_message(path, receiver, message, candidates) else candidates = eval( - "methods | private_methods | local_variables | " \ - "self.class.constants | instance_variables", - bind - ).collect(&:to_s) + "methods | private_methods | local_variables | " \ + "self.class.constants | instance_variables", + bind + ).collect(&:to_s) if eval("respond_to?(:class_variables)", bind) candidates += eval("class_variables", bind).collect(&:to_s) @@ -208,7 +212,7 @@ class Pry end end - def self.select_message(path, receiver, message, candidates) + def select_message(path, receiver, message, candidates) candidates.grep(/^#{message}/).collect { |e| case e when /^[a-zA-Z_]/ @@ -223,22 +227,18 @@ class Pry # build_path seperates the input into two parts: path and input. # input is the partial string that should be completed # path is a proc that takes an input and builds a full path. - def self.build_path(input) - + def build_path(input) # check to see if the input is a regex return proc {|i| i.to_s }, input if input[/\/\./] - trailing_slash = input.end_with?('/') contexts = input.chomp('/').split(/\//) input = contexts[-1] - path = proc do |i| p = contexts[0..-2].push(i).join('/') p += '/' if trailing_slash && !i.nil? p end - return path, input end - end + end diff --git a/lib/pry/pry_instance.rb b/lib/pry/pry_instance.rb index d7d09625..5d08f1cb 100644 --- a/lib/pry/pry_instance.rb +++ b/lib/pry/pry_instance.rb @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- ## # Pry is a powerful alternative to the standard IRB shell for Ruby. It # features syntax highlighting, a flexible plugin architecture, runtime @@ -125,9 +126,8 @@ class Pry def complete(input) return EMPTY_COMPLETIONS unless config.completer Pry.critical_section do - config.completer.call input, :target => current_binding, - :pry => self, - :custom_completions => custom_completions.call.push(*sticky_locals.keys) + completer = config.completer.new(self) + completer.call input, target: current_binding, custom_completions: custom_completions.call.push(*sticky_locals.keys) end end diff --git a/spec/completion_spec.rb b/spec/completion_spec.rb index 8168dbe1..7cb742d9 100644 --- a/spec/completion_spec.rb +++ b/spec/completion_spec.rb @@ -4,7 +4,7 @@ require "pry/input_completer" def completer_test(bind, pry=nil, assert_flag=true) test = proc {|symbol| - Pry::InputCompleter.call(symbol[0..-2], :target => Pry.binding_for(bind), :pry => pry).include?(symbol).should == assert_flag} + Pry::InputCompleter.new(pry).call(symbol[0..-2], :target => Pry.binding_for(bind)).include?(symbol).should == assert_flag} return proc {|*symbols| symbols.each(&test) } end @@ -18,24 +18,24 @@ describe Pry::InputCompleter do def self.name; :symboly_name; end end - $default_completer = Pry.config.completer + @before_completer = Pry.config.completer Pry.config.completer = Pry::InputCompleter end after do - Pry.config.completer = $default_completer + Pry.config.completer = @before_completer Object.remove_const :SymbolyName end # another jruby hack :(( if !Pry::Helpers::BaseHelpers.jruby? it "should not crash if there's a Module that has a symbolic name." do - lambda{ Pry::InputCompleter.call "a.to_s.", :target => Pry.binding_for(Object.new) }.should.not.raise Exception + lambda{ Pry::InputCompleter.new.call "a.to_s.", :target => Pry.binding_for(Object.new) }.should.not.raise Exception end end it 'should take parenthesis and other characters into account for symbols' do - lambda { Pry::InputCompleter.call(":class)", :target => Pry.binding_for(Object.new)) }.should.not.raise(RegexpError) + lambda { Pry::InputCompleter.new.call(":class)", :target => Pry.binding_for(Object.new)) }.should.not.raise(RegexpError) end it 'should complete instance variables' do @@ -114,7 +114,7 @@ describe Pry::InputCompleter do completer_test(binding).call('o.foo') # trailing slash - Pry::InputCompleter.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true + Pry::InputCompleter.new.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true end it 'should complete for arbitrary scopes' do @@ -185,8 +185,7 @@ describe Pry::InputCompleter do completer_test(binding).call('o.foo') # trailing slash - Pry::InputCompleter.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true - + Pry::InputCompleter.new.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true end it 'should complete for arbitrary scopes' do @@ -210,6 +209,6 @@ describe Pry::InputCompleter do it 'should not return nil in its output' do pry = Pry.new - Pry::InputCompleter.call("pry.", :target => binding).should.not.include nil + Pry::InputCompleter.new(pry).call("pry.", :target => binding).should.not.include nil end end |