diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 2a16250f6a427..06afe84e2c37e 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -134,9 +134,8 @@ function appendmacro!(syms, macros, needle, endchar) end end -function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString; - all::Bool=false, imported::Bool=false, usings::Bool=false) - ssyms = names(mod, all = all, imported = imported, usings = usings) +function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString; kwargs...) + ssyms = names(mod; kwargs...) all || filter!(Base.Fix1(Base.isexported, mod), ssyms) # TODO revisit filter!(ffunc, ssyms) macros = filter(x -> startswith(String(x), "@" * name), ssyms) @@ -181,14 +180,14 @@ function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), end # Looking for a binding in a module if mod == context_module - # Also look in modules we got through `using` - mods = ccall(:jl_module_usings, Any, (Any,), context_module)::Vector - for m in mods - append!(suggestions, filtered_mod_names(p, m::Module, name) - end - append!(suggestions, filtered_mod_names(p, mod, name, all=true, imported=true, usings=true)) - else - append!(suggestions, filtered_mod_names(p, mod, name, all=true, usings=true)) + # special case `Core` and `Base` bindings + # - `Core` bindings should be added to every modules + # - `Base` bindings should be added to non-bare modules + append!(suggestions, filtered_mod_names(p, Core, name)) + isdefined(mod, :Base) && append!(suggestions, filtered_mod_names(p, Base, name)) + append!(suggestions, filtered_mod_names(p, mod, name; all=true, imported=true, usings=true)) + else # dot-accessed module context + append!(suggestions, filtered_mod_names(p, mod, name; all=true, usings=true)) end elseif val !== nothing # looking for a property of an instance try diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 0a73a944ec8ea..b0a00f914218c 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -2260,3 +2260,40 @@ let s = "Issue53126()." @test res @test isempty(c) end + +@testset "complete names from `using`" begin + test_complete_context1(args...; kwargs...) = first(test_complete_context(args...; kwargs...)) + + let # should work for arbitrary module + M = Module() + + use = @eval M module use end + @eval use module def; foo = 42; end + + @assert count(==("foo"), test_complete_context1("f", use)) ≠ 1 + @assert count(==("foo"), test_complete_context1("use.f", M)) ≠ 1 + @eval use using .def: foo + @test count(==("foo"), test_complete_context1("f", use)) == 1 + @test count(==("foo"), test_complete_context1("use.f", M)) == 1 # should work for even dot-accessed module context + end + + let # should work for packages + M = Module() + + @assert count(==("fuzzyscore"), test_complete_context1("fuzzy", M)) ≠ 1 + @eval M using REPL: fuzzyscore + @test count(==("fuzzyscore"), test_complete_context1("fuzzy", M)) == 1 + + @assert count(==("completions"), test_complete_context1("comp", M)) ≠ 1 + @eval M using REPL.REPLCompletions: completions + @test count(==("completions"), test_complete_context1("comp", M)) == 1 + end + + let # should for `Base` binding + M = Module() + + @assert count(==("@aggressive_constprop"), test_complete_context1("@aggressive_", M)) ≠ 1 + @eval M using Base: @aggressive_constprop + @test count(==("@aggressive_constprop"), test_complete_context1("@aggressive_", M)) == 1 + end +end