Skip to content

Commit 93ca6a1

Browse files
committed
Preserve completion candidate groups when sorting
1 parent 849669f commit 93ca6a1

2 files changed

Lines changed: 46 additions & 37 deletions

File tree

lib/repl_type_completor/result.rb

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,23 @@ def completion_candidates
4646
in [:require_relative, name]
4747
RequirePaths.require_relative_completions(name, @source_file)
4848
in [:call_or_const, name, type, self_call]
49-
((self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
49+
((self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS).sort | type.constants.sort
5050
in [:const, name, type, scope]
5151
if type
5252
scope_constants = type.types.flat_map do |t|
5353
scope.table_module_constants(t.module_or_class) if t.is_a?(Types::SingletonType)
5454
end
55-
scope_constants.compact | type.constants.map(&:to_s)
55+
(scope_constants.compact | type.constants.map(&:to_s)).sort
5656
else
57-
scope.constants | RESERVED_WORDS
57+
scope.constants.sort | RESERVED_WORDS
5858
end
5959
in [:ivar, name, scope]
60-
ivars = scope.instance_variables
61-
name == '@' ? ivars + scope.class_variables : ivars
60+
ivars = scope.instance_variables.sort
61+
name == '@' ? ivars + scope.class_variables.sort : ivars
6262
in [:cvar, name, scope]
63-
scope.class_variables
63+
scope.class_variables.sort
6464
in [:gvar, name, scope]
65-
scope.global_variables
65+
scope.global_variables.sort
6666
in [:symbol, name]
6767
filter_symbol_candidates(Symbol.all_symbols, name, limit: 100)
6868
in [:aref, key_type, name, receiver_type]
@@ -76,23 +76,22 @@ def completion_candidates
7676
keys = Symbol.all_symbols if keys.empty? && name.size >= 1
7777
filter_symbol_candidates(keys, name, limit: 100)
7878
else
79-
keys
79+
keys.sort
8080
end
8181
in [:call, name, type, self_call]
8282
(self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
8383
in [:lvar_or_method, name, scope]
84-
scope.self_type.all_methods.map(&:to_s) | scope.local_variables | RESERVED_WORDS
84+
scope.local_variables.sort | scope.self_type.all_methods.map(&:to_s).sort | RESERVED_WORDS
8585
else
8686
[]
8787
end
8888

89-
candidates.filter_map do
89+
candidates = candidates.filter_map do
9090
_1 if _1.start_with?(name)
9191
rescue EncodingError
92-
end.sort_by do
93-
# Alphabetical order, but internal methods (leading underscore) last
94-
[_1.start_with?('_') ? 1 : 0, _1]
95-
end.map { _1[name.size..] }
92+
end
93+
candidates.sort_by! { [_1.start_with?('_') ? 1 : 0, _1] } if @analyze_result.first == :call
94+
candidates.map { _1[name.size..] }
9695
rescue Exception => e
9796
ReplTypeCompletor.handle_exception(e)
9897
[]

test/repl_type_completor/test_repl_type_completor.rb

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -293,31 +293,41 @@ def with_failing_method(klass, method_name, message)
293293
klass.define_method(method_name, original_method)
294294
end
295295

296-
def test_completion_candidates_are_sorted
297-
xzyb, xzya, xzyc = 'b', 'a', 'c'
298-
lvar_binding = binding
299-
300-
{
301-
'1.' => empty_binding, # [:call] with empty method name
302-
"''.s" => empty_binding, # [:call] with method name prefix
303-
'Math::' => empty_binding, # [:call_or_const]
304-
'$std' => empty_binding, # [:gvar]
305-
'xzy' => lvar_binding # [:lvar_or_method]
306-
}.each do |code, bind|
307-
candidates = ReplTypeCompletor.analyze(code, binding: bind).completion_candidates
308-
refute_empty candidates, "Expected completion candidates of #{code.inspect} not to be empty"
309-
underscore, regular = candidates.partition { _1.start_with?('_') }
310-
assert_equal regular.sort + underscore.sort, candidates, "Expected completion candidates of #{code.inspect} to be sorted with underscore methods last"
311-
end
296+
def test_call_candidates_are_sorted_with_underscore_methods_last
297+
type = Struct.new(:methods).new(%i[_zeta beta _alpha alpha])
298+
result = ReplTypeCompletor::Result.new([:call, '', type, false], binding, __FILE__)
299+
300+
assert_equal %w[alpha beta _alpha _zeta], result.completion_candidates
301+
end
302+
303+
def test_call_or_const_candidates_are_sorted_within_groups
304+
type = Struct.new(:methods, :constants).new(%i[z_method a_method], %i[ZConst AConst])
305+
result = ReplTypeCompletor::Result.new([:call_or_const, '', type, false], binding, __FILE__)
306+
307+
assert_equal %w[a_method z_method AConst ZConst], result.completion_candidates
308+
end
309+
310+
def test_const_candidates_are_sorted_before_reserved_words
311+
scope = Struct.new(:constants).new(%w[ZConst AConst])
312+
result = ReplTypeCompletor::Result.new([:const, '', nil, scope], binding, __FILE__)
313+
314+
assert_equal %w[AConst ZConst] + ReplTypeCompletor::Result::RESERVED_WORDS, result.completion_candidates
315+
end
316+
317+
def test_ivar_candidates_are_sorted_within_groups
318+
scope = Struct.new(:instance_variables, :class_variables).new(%w[@z @a], %w[@@z @@a])
319+
result = ReplTypeCompletor::Result.new([:ivar, '@', scope], binding, __FILE__)
320+
321+
assert_equal %w[a z @a @z], result.completion_candidates
322+
end
312323

313-
# Internal methods (leading underscore, e.g. __id__ and __send__) are listed last
314-
candidates = ReplTypeCompletor.analyze('1.', binding: empty_binding).completion_candidates
315-
assert candidates.any? { _1.start_with?('_') }
316-
refute candidates.first.start_with?('_')
317-
assert candidates.last.start_with?('_')
324+
def test_lvar_or_method_candidates_are_sorted_within_groups
325+
type = Struct.new(:all_methods).new(%i[z_method a_method])
326+
scope = Struct.new(:self_type, :local_variables).new(type, %w[z_local a_local])
327+
result = ReplTypeCompletor::Result.new([:lvar_or_method, '', scope], binding, __FILE__)
318328

319-
# Local variables xzyb, xzya, xzyc are defined in non-alphabetical order
320-
assert_equal [xzya, xzyb, xzyc], ReplTypeCompletor.analyze('xzy', binding: lvar_binding).completion_candidates
329+
expected = %w[a_local z_local a_method z_method] + ReplTypeCompletor::Result::RESERVED_WORDS
330+
assert_equal expected, result.completion_candidates
321331
end
322332

323333
def test_analyze_error

0 commit comments

Comments
 (0)