Skip to content

Commit 8780aa9

Browse files
authored
[REPLCompletions] use better version of listing module imports for completions (#58218)
The test for this crashes on my machine, leading me to notice that the implementation of this function was nonsense. Fortunately, we already had a more correct implementation of this function in the REPL docview code that we could borrow from to fix this. While here, also filter out macros, since those are rather strange looking to see appearing in the results. We could alternatively use `Base.is_valid_identifier`, since the main point here is to print functions that are accessible in the module by identifier. ``` julia> @eval Base.MainInclude export broken julia> broken = 2 2 julia> ?(┌ Error: Error in the keymap │ exception = │ UndefVarError: `broken` not defined in `Base.MainInclude` ```
1 parent 175ef3e commit 8780aa9

File tree

2 files changed

+30
-31
lines changed

2 files changed

+30
-31
lines changed

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -729,24 +729,12 @@ function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool
729729
end
730730

731731
MAX_ANY_METHOD_COMPLETIONS::Int = 10
732-
function recursive_explore_names!(seen::IdSet, callee_module::Module, initial_module::Module, exploredmodules::IdSet{Module}=IdSet{Module}())
733-
push!(exploredmodules, callee_module)
734-
for name in names(callee_module; all=true, imported=true)
735-
if !Base.isdeprecated(callee_module, name) && !startswith(string(name), '#') && isdefined(initial_module, name)
736-
func = getfield(callee_module, name)
737-
if !isa(func, Module)
738-
funct = Core.Typeof(func)
739-
push!(seen, funct)
740-
elseif isa(func, Module) && func exploredmodules
741-
recursive_explore_names!(seen, func, initial_module, exploredmodules)
742-
end
743-
end
744-
end
745-
end
746-
function recursive_explore_names(callee_module::Module, initial_module::Module)
747-
seen = IdSet{Any}()
748-
recursive_explore_names!(seen, callee_module, initial_module)
749-
seen
732+
733+
function accessible(mod::Module, private::Bool)
734+
bindings = IdSet{Any}(Core.Typeof(getglobal(mod, s)) for s in names(mod; all=private, imported=private, usings=private)
735+
if !Base.isdeprecated(mod, s) && !startswith(string(s), '#') && !startswith(string(s), '@') && isdefined(mod, s))
736+
delete!(bindings, Module)
737+
return collect(bindings)
750738
end
751739

752740
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
@@ -764,7 +752,7 @@ function complete_any_methods(ex_org::Expr, callee_module::Module, context_modul
764752
# semicolon for the ".?(" syntax
765753
moreargs && push!(args_ex, Vararg{Any})
766754

767-
for seen_name in recursive_explore_names(callee_module, callee_module)
755+
for seen_name in accessible(callee_module, callee_module === context_module)
768756
complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
769757
end
770758

@@ -1273,20 +1261,20 @@ function dict_eval(@nospecialize(e), context_module::Module=Main)
12731261
end
12741262

12751263
function method_search(partial::AbstractString, context_module::Module, shift::Bool)
1276-
rexm = match(r"(\w+\.|)\?\((.*)$", partial)
1264+
rexm = match(r"([\w.]+.)?\?\((.*)$", partial)
12771265
if rexm !== nothing
12781266
# Get the module scope
1279-
if isempty(rexm.captures[1])
1280-
callee_module = context_module
1281-
else
1282-
modname = Symbol(rexm.captures[1][1:end-1])
1283-
if isdefined(context_module, modname)
1284-
callee_module = getfield(context_module, modname)
1285-
if !isa(callee_module, Module)
1286-
callee_module = context_module
1267+
callee_module = context_module
1268+
if !isnothing(rexm.captures[1])
1269+
modnames = map(Symbol, split(something(rexm.captures[1]), '.'))
1270+
for m in modnames
1271+
if isdefined(callee_module, m)
1272+
callee_module = getfield(callee_module, m)
1273+
if !isa(callee_module, Module)
1274+
callee_module = context_module
1275+
break
1276+
end
12871277
end
1288-
else
1289-
callee_module = context_module
12901278
end
12911279
end
12921280
moreargs = !endswith(rexm.captures[2], ')')
@@ -1296,7 +1284,8 @@ function method_search(partial::AbstractString, context_module::Module, shift::B
12961284
end
12971285
ex_org = Meta.parse(callstr, raise=false, depwarn=false)
12981286
if isa(ex_org, Expr)
1299-
return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false
1287+
pos_q = isnothing(rexm.captures[1]) ? 1 : sizeof(something(rexm.captures[1]))+1 # position after ?
1288+
return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:pos_q) .+ rexm.offset, false
13001289
end
13011290
end
13021291
end

stdlib/REPL/test/replcompletions.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ let ex =
1717
module CompletionFoo
1818
using Random
1919
import Test
20+
# make everything public, so that nothing gets hidden unintentionally from completions
21+
public Test_y, Text_x, type_test, unicode_αΒγ, CompletionFoo2, bar,
22+
foo, @foobar, @barfoo, @error_expanding,
23+
@error_lowering_conditional, @error_throwing, NonStruct, x,
24+
CustomDict, NoLengthDict, test, test1, test2, test3, test4, test5,
25+
test6, test7, test8, test9, test10, test11, a, test!12, kwtest,
26+
kwtest2, kwtest3, kwtest4, kwtest5, named, fmsoebelkv, array,
27+
varfloat, tuple, test_y_array, test_dict, test_customdict,
28+
@teststr_str, @tϵsτstρ_str, @testcmd_cmd, @tϵsτcmδ_cmd,
29+
var"complicated symbol with spaces", WeirdNames, @ignoremacro
2030

2131
mutable struct Test_y
2232
yy

0 commit comments

Comments
 (0)