Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REPLCompletions: allow completions for explicitly using-ed names #54610

Merged
merged 1 commit into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ Standard library changes

#### REPL

- Using the new `usings=true` feature of the `names()` function, REPL completions can now
complete names that have been explicitly `using`-ed. ([#54610])

#### SuiteSparse

#### SparseArrays
Expand Down
38 changes: 18 additions & 20 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,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)
ssyms = names(mod, all = all, imported = imported)
all || filter!(Base.Fix1(Base.isexported, mod), ssyms)
function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString; kwargs...)
ssyms = names(mod; kwargs...)
filter!(ffunc, ssyms)
macros = filter(x -> startswith(String(x), "@" * name), ssyms)
syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)]
Expand All @@ -147,7 +146,7 @@ function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString,
end

# REPL Symbol Completions
function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), context_module::Module=Main)
function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), context_module::Module)
mod = context_module

lookup_module = true
Expand All @@ -173,23 +172,22 @@ function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc),

suggestions = Completion[]
if lookup_module
# We will exclude the results that the user does not want, as well
# as excluding Main.Main.Main, etc., because that's most likely not what
# the user wants
p = let mod=mod, modname=nameof(mod)
(s::Symbol) -> !Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool && !(mod === Main && s === :MainInclude)
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))
fmnames = let modname = nameof(mod),
is_main = mod===Main
filtered_mod_names(mod, name; all=true, imported=true, usings=true) do s::Symbol
if Base.isdeprecated(mod, s)
return false
elseif s === modname
return false # exclude `Main.Main.Main`, etc.
elseif !ffunc(mod, s)::Bool
return false
elseif is_main && s === :MainInclude
return false
end
return true
end
append!(suggestions, filtered_mod_names(p, mod, name, true, true))
else
append!(suggestions, filtered_mod_names(p, mod, name, true, false))
end
append!(suggestions, fmnames)
elseif val !== nothing # looking for a property of an instance
try
for property in propertynames(val, false)
Expand Down Expand Up @@ -1065,7 +1063,7 @@ end
function complete_identifiers!(suggestions::Vector{Completion}, @nospecialize(ffunc),
context_module::Module, string::String, name::String,
pos::Int, dotpos::Int, startpos::Int;
comp_keywords=false)
comp_keywords::Bool=false)
ex = nothing
if comp_keywords
append!(suggestions, complete_keyword(name))
Expand Down
54 changes: 51 additions & 3 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ let ex = quote
kwtest5(a::Char, b; xyz) = pass

const named = (; len2=3)
const fmsoebelkv = (; len2=3)

array = [1, 1]
varfloat = 0.1
Expand Down Expand Up @@ -2129,7 +2130,7 @@ end
end

# issue #51194
for (s, compl) in (("2*CompletionFoo.nam", "named"),
for (s, compl) in (("2*CompletionFoo.fmsoe", "fmsoebelkv"),
(":a isa CompletionFoo.test!1", "test!12"),
("-CompletionFoo.Test_y(3).", "yy"),
("99 ⨷⁻ᵨ⁷ CompletionFoo.type_test.", "xx"),
Expand All @@ -2138,8 +2139,11 @@ for (s, compl) in (("2*CompletionFoo.nam", "named"),
("CompletionFoo.type_test + CompletionFoo.unicode_αβγ.", "yy"),
("(CompletionFoo.type_test + CompletionFoo.unicode_αβγ).", "xx"),
("foo'CompletionFoo.test!1", "test!12"))
c, r = test_complete(s)
@test only(c) == compl
@testset let s=s, compl=compl
c, r = test_complete_noshift(s)
@test length(c) == 1
@test only(c) == compl
end
end

# allows symbol completion within incomplete :macrocall
Expand Down Expand Up @@ -2260,3 +2264,47 @@ let s = "Issue53126()."
@test res
@test isempty(c)
end

# complete explicitly `using`ed names
baremodule TestExplicitUsing
using Base: @assume_effects
end # baremodule TestExplicitUsing
let s = "@assu"
c, r, res = test_complete_context(s, TestExplicitUsing)
@test res
@test "@assume_effects" in c
end
let s = "TestExplicitUsing.@assu"
c, r, res = test_complete_context(s)
@test res
@test "@assume_effects" in c
end
baremodule TestExplicitUsingNegative end
let s = "@assu"
c, r, res = test_complete_context(s, TestExplicitUsingNegative)
@test res
@test "@assume_effects" ∉ c
end
let s = "TestExplicitUsingNegative.@assu"
c, r, res = test_complete_context(s)
@test res
@test "@assume_effects" ∉ c
end
# should complete implicitly `using`ed names
module TestImplicitUsing end
let s = "@asse"
c, r, res = test_complete_context(s, TestImplicitUsing)
@test res
@test "@assert" in c
end
let s = "TestImplicitUsing.@asse"
c, r, res = test_complete_context(s)
@test res
@test "@assert" in c
end
# JuliaLang/julia#53999
let s = "using Base.Thre"
c, r, res = test_complete_context(s)
@test res
@test "Threads" in c
end