Skip to content

Commit 4bfb5c6

Browse files
authored
Merge pull request #78 from kryzhovnik/sort-completion-candidates
Sort completion candidates
2 parents cf89f1f + 788a0b9 commit 4bfb5c6

2 files changed

Lines changed: 48 additions & 7 deletions

File tree

lib/repl_type_completor/result.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ 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|
@@ -60,28 +60,32 @@ def completion_candidates
6060
ivars = scope.instance_variables.sort
6161
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]
6969
key_type = key_type == :string ? String : Symbol
7070
keys = receiver_type.types.grep(Types::InstanceType).select do |t|
7171
Hash == t.klass
7272
end.flat_map do |t|
73-
t.instances.flat_map(&:keys).grep(key_type).uniq.sort
73+
t.instances.flat_map(&:keys).grep(key_type).uniq
7474
end
7575
if key_type == Symbol
7676
keys = Symbol.all_symbols if keys.empty? && name.size >= 1
7777
filter_symbol_candidates(keys, name, limit: 100)
7878
else
79-
keys.select { _1.start_with?(name) }.sort
79+
keys.sort
8080
end
8181
in [:call, name, type, self_call]
82-
(self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
82+
methods = (self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
83+
# Alphabetical order, but internal methods (leading underscore) last.
84+
# `_1[0] == '_'` because `start_with?('_')` raises EncodingError if the
85+
# method name's encoding is incompatible with US-ASCII.
86+
methods.sort_by { [_1[0] == '_' ? 1 : 0, _1] }
8387
in [:lvar_or_method, name, scope]
84-
scope.self_type.all_methods.map(&:to_s) | scope.local_variables | RESERVED_WORDS
88+
scope.local_variables.sort | scope.self_type.all_methods.map(&:to_s).sort | RESERVED_WORDS
8589
else
8690
[]
8791
end

test/repl_type_completor/test_repl_type_completor.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,43 @@ def with_failing_method(klass, method_name, message)
293293
klass.define_method(method_name, original_method)
294294
end
295295

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
323+
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__)
328+
329+
expected = %w[a_local z_local a_method z_method] + ReplTypeCompletor::Result::RESERVED_WORDS
330+
assert_equal expected, result.completion_candidates
331+
end
332+
296333
def test_analyze_error
297334
with_failing_method(ReplTypeCompletor.singleton_class, :analyze_code, 'error_in_analyze_code') do
298335
assert_nil ReplTypeCompletor.analyze('1.', binding: binding)

0 commit comments

Comments
 (0)