diff options
author | tomoya ishida <[email protected]> | 2023-10-12 02:08:59 +0900 |
---|---|---|
committer | git <[email protected]> | 2023-10-11 17:09:05 +0000 |
commit | 94cb5765e2e44716800cd466c64f81c048aaf95b (patch) | |
tree | 8e372dac7478a36582b9de656731361924515788 /lib/irb/completion.rb | |
parent | b9a6fca67d97f7319b20b24427d6dc4b8290cd24 (diff) |
[ruby/irb] Rename current completor to RegexpCompletor and
refactored for future extension
(https://github.com/ruby/irb/pull/707)
* Move completion implementation to completion/regexp_completor for future extension
* Remove constant CompletionProc and PerfectMatchedProc and add a class method
* Move document display logic to InputCompletor. Each completor only need to implement `completion_caididates` and `doc_namespace`
* Move display_document logic to RelineInputMethod
* Use RegexpCompletor directly. Not through class method of InputCompletor.
* RegexpCompletor extends BaseCompletor, move back definition to completion.rb
* Move display_document test to input_method test
* Stop re-initialize completor on each completion phase
* Store completor to ReadlineInputMethod's iver
https://github.com/ruby/irb/commit/1e98521483
Diffstat (limited to 'lib/irb/completion.rb')
-rw-r--r-- | lib/irb/completion.rb | 174 |
1 files changed, 74 insertions, 100 deletions
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index a143d1b3e1..26215f6fe1 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -8,55 +8,14 @@ require_relative 'ruby-lex' module IRB - module InputCompletor # :nodoc: - using Module.new { - refine ::Binding do - def eval_methods - ::Kernel.instance_method(:methods).bind(eval("self")).call - end - - def eval_private_methods - ::Kernel.instance_method(:private_methods).bind(eval("self")).call - end - - def eval_instance_variables - ::Kernel.instance_method(:instance_variables).bind(eval("self")).call - end - - def eval_global_variables - ::Kernel.instance_method(:global_variables).bind(eval("self")).call - end - - def eval_class_constants - ::Module.instance_method(:constants).bind(eval("self.class")).call - end - end - } - - # Set of reserved words used by Ruby, you should not use these for - # constants or variables - ReservedWords = %w[ - __ENCODING__ __LINE__ __FILE__ - 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 - ] + class BaseCompletor # :nodoc: + def completion_candidates(preposing, target, postposing, bind:) + raise NotImplementedError + end - BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{(" + def doc_namespace(preposing, matched, postposing, bind:) + raise NotImplementedError + end GEM_PATHS = if defined?(Gem::Specification) @@ -73,7 +32,7 @@ module IRB [] end.freeze - def self.retrieve_gem_and_system_load_path + def retrieve_gem_and_system_load_path candidates = (GEM_PATHS | $LOAD_PATH) candidates.map do |p| if p.respond_to?(:to_path) @@ -84,8 +43,8 @@ module IRB end.compact.sort end - def self.retrieve_files_to_require_from_load_path - @@files_from_load_path ||= + def retrieve_files_to_require_from_load_path + @files_from_load_path ||= ( shortest = [] rest = retrieve_gem_and_system_load_path.each_with_object([]) { |path, result| @@ -103,13 +62,62 @@ module IRB ) end - def self.retrieve_files_to_require_relative_from_current_dir - @@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path| + def retrieve_files_to_require_relative_from_current_dir + @files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path| path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '') } end + end - CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil| + class RegexpCompletor < BaseCompletor # :nodoc: + using Module.new { + refine ::Binding do + def eval_methods + ::Kernel.instance_method(:methods).bind(eval("self")).call + end + + def eval_private_methods + ::Kernel.instance_method(:private_methods).bind(eval("self")).call + end + + def eval_instance_variables + ::Kernel.instance_method(:instance_variables).bind(eval("self")).call + end + + def eval_global_variables + ::Kernel.instance_method(:global_variables).bind(eval("self")).call + end + + def eval_class_constants + ::Module.instance_method(:constants).bind(eval("self.class")).call + end + end + } + + # Set of reserved words used by Ruby, you should not use these for + # constants or variables + ReservedWords = %w[ + __ENCODING__ __LINE__ __FILE__ + 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 + ] + + def complete_require_path(target, preposing, postposing) if target =~ /\A(['"])([^'"]+)\Z/ quote = $1 actual_target = $2 @@ -142,21 +150,21 @@ module IRB end end result - } + end - CompletionProc = lambda { |target, preposing = nil, postposing = nil| + def completion_candidates(preposing, target, postposing, bind:) if preposing && postposing - result = CompletionRequireProc.(target, preposing, postposing) - unless result - result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) } - end - result - else - retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) } + result = complete_require_path(target, preposing, postposing) + return result if result end - } + retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) } + end - def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false) + def doc_namespace(_preposing, matched, _postposing, bind:) + retrieve_completion_data(matched, bind: bind, doc_namespace: true) + end + + def retrieve_completion_data(input, bind:, doc_namespace:) case input # this regexp only matches the closing character because of irb's Reline.completer_quote_characters setting # details are described in: https://github.com/ruby/irb/pull/523 @@ -394,44 +402,10 @@ module IRB end end - PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) { - begin - require 'rdoc' - rescue LoadError - return - end - - RDocRIDriver ||= RDoc::RI::Driver.new - - if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER'] - IRB.__send__(:easter_egg) - return - end - - namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true) - return unless namespace - - if namespace.is_a?(Array) - out = RDoc::Markup::Document.new - namespace.each do |m| - begin - RDocRIDriver.add_method(out, m) - rescue RDoc::RI::Driver::NotFoundError - end - end - RDocRIDriver.display(out) - else - begin - RDocRIDriver.display_names([namespace]) - rescue RDoc::RI::Driver::NotFoundError - end - end - } - # Set of available operators in Ruby Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~] - def self.select_message(receiver, message, candidates, sep = ".") + def select_message(receiver, message, candidates, sep = ".") candidates.grep(/^#{Regexp.quote(message)}/).collect do |e| case e when /^[a-zA-Z_]/ |