Skip to content

Commit 1e98521

Browse files
authored
Rename current completor to RegexpCompletor and refactored for future extension (#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
1 parent 202efdb commit 1e98521

File tree

4 files changed

+376
-337
lines changed

4 files changed

+376
-337
lines changed

lib/irb/completion.rb

Lines changed: 74 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,14 @@
88
require_relative 'ruby-lex'
99

1010
module IRB
11-
module InputCompletor # :nodoc:
12-
using Module.new {
13-
refine ::Binding do
14-
def eval_methods
15-
::Kernel.instance_method(:methods).bind(eval("self")).call
16-
end
17-
18-
def eval_private_methods
19-
::Kernel.instance_method(:private_methods).bind(eval("self")).call
20-
end
21-
22-
def eval_instance_variables
23-
::Kernel.instance_method(:instance_variables).bind(eval("self")).call
24-
end
25-
26-
def eval_global_variables
27-
::Kernel.instance_method(:global_variables).bind(eval("self")).call
28-
end
29-
30-
def eval_class_constants
31-
::Module.instance_method(:constants).bind(eval("self.class")).call
32-
end
33-
end
34-
}
35-
36-
# Set of reserved words used by Ruby, you should not use these for
37-
# constants or variables
38-
ReservedWords = %w[
39-
__ENCODING__ __LINE__ __FILE__
40-
BEGIN END
41-
alias and
42-
begin break
43-
case class
44-
def defined? do
45-
else elsif end ensure
46-
false for
47-
if in
48-
module
49-
next nil not
50-
or
51-
redo rescue retry return
52-
self super
53-
then true
54-
undef unless until
55-
when while
56-
yield
57-
]
11+
class BaseCompletor # :nodoc:
12+
def completion_candidates(preposing, target, postposing, bind:)
13+
raise NotImplementedError
14+
end
5815

59-
BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
16+
def doc_namespace(preposing, matched, postposing, bind:)
17+
raise NotImplementedError
18+
end
6019

6120
GEM_PATHS =
6221
if defined?(Gem::Specification)
@@ -73,7 +32,7 @@ def defined? do
7332
[]
7433
end.freeze
7534

76-
def self.retrieve_gem_and_system_load_path
35+
def retrieve_gem_and_system_load_path
7736
candidates = (GEM_PATHS | $LOAD_PATH)
7837
candidates.map do |p|
7938
if p.respond_to?(:to_path)
@@ -84,8 +43,8 @@ def self.retrieve_gem_and_system_load_path
8443
end.compact.sort
8544
end
8645

87-
def self.retrieve_files_to_require_from_load_path
88-
@@files_from_load_path ||=
46+
def retrieve_files_to_require_from_load_path
47+
@files_from_load_path ||=
8948
(
9049
shortest = []
9150
rest = retrieve_gem_and_system_load_path.each_with_object([]) { |path, result|
@@ -103,13 +62,62 @@ def self.retrieve_files_to_require_from_load_path
10362
)
10463
end
10564

106-
def self.retrieve_files_to_require_relative_from_current_dir
107-
@@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
65+
def retrieve_files_to_require_relative_from_current_dir
66+
@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
10867
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
10968
}
11069
end
70+
end
11171

112-
CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
72+
class RegexpCompletor < BaseCompletor # :nodoc:
73+
using Module.new {
74+
refine ::Binding do
75+
def eval_methods
76+
::Kernel.instance_method(:methods).bind(eval("self")).call
77+
end
78+
79+
def eval_private_methods
80+
::Kernel.instance_method(:private_methods).bind(eval("self")).call
81+
end
82+
83+
def eval_instance_variables
84+
::Kernel.instance_method(:instance_variables).bind(eval("self")).call
85+
end
86+
87+
def eval_global_variables
88+
::Kernel.instance_method(:global_variables).bind(eval("self")).call
89+
end
90+
91+
def eval_class_constants
92+
::Module.instance_method(:constants).bind(eval("self.class")).call
93+
end
94+
end
95+
}
96+
97+
# Set of reserved words used by Ruby, you should not use these for
98+
# constants or variables
99+
ReservedWords = %w[
100+
__ENCODING__ __LINE__ __FILE__
101+
BEGIN END
102+
alias and
103+
begin break
104+
case class
105+
def defined? do
106+
else elsif end ensure
107+
false for
108+
if in
109+
module
110+
next nil not
111+
or
112+
redo rescue retry return
113+
self super
114+
then true
115+
undef unless until
116+
when while
117+
yield
118+
]
119+
120+
def complete_require_path(target, preposing, postposing)
113121
if target =~ /\A(['"])([^'"]+)\Z/
114122
quote = $1
115123
actual_target = $2
@@ -142,21 +150,21 @@ def self.retrieve_files_to_require_relative_from_current_dir
142150
end
143151
end
144152
result
145-
}
153+
end
146154

147-
CompletionProc = lambda { |target, preposing = nil, postposing = nil|
155+
def completion_candidates(preposing, target, postposing, bind:)
148156
if preposing && postposing
149-
result = CompletionRequireProc.(target, preposing, postposing)
150-
unless result
151-
result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
152-
end
153-
result
154-
else
155-
retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
157+
result = complete_require_path(target, preposing, postposing)
158+
return result if result
156159
end
157-
}
160+
retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
161+
end
158162

159-
def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
163+
def doc_namespace(_preposing, matched, _postposing, bind:)
164+
retrieve_completion_data(matched, bind: bind, doc_namespace: true)
165+
end
166+
167+
def retrieve_completion_data(input, bind:, doc_namespace:)
160168
case input
161169
# this regexp only matches the closing character because of irb's Reline.completer_quote_characters setting
162170
# details are described in: https://github.com/ruby/irb/pull/523
@@ -394,44 +402,10 @@ def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace
394402
end
395403
end
396404

397-
PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
398-
begin
399-
require 'rdoc'
400-
rescue LoadError
401-
return
402-
end
403-
404-
RDocRIDriver ||= RDoc::RI::Driver.new
405-
406-
if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
407-
IRB.__send__(:easter_egg)
408-
return
409-
end
410-
411-
namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
412-
return unless namespace
413-
414-
if namespace.is_a?(Array)
415-
out = RDoc::Markup::Document.new
416-
namespace.each do |m|
417-
begin
418-
RDocRIDriver.add_method(out, m)
419-
rescue RDoc::RI::Driver::NotFoundError
420-
end
421-
end
422-
RDocRIDriver.display(out)
423-
else
424-
begin
425-
RDocRIDriver.display_names([namespace])
426-
rescue RDoc::RI::Driver::NotFoundError
427-
end
428-
end
429-
}
430-
431405
# Set of available operators in Ruby
432406
Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
433407

434-
def self.select_message(receiver, message, candidates, sep = ".")
408+
def select_message(receiver, message, candidates, sep = ".")
435409
candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
436410
case e
437411
when /^[a-zA-Z_]/

0 commit comments

Comments
 (0)