From 6073643bc264e64653dbfab92976e0b5f8cd054d Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 24 Oct 2024 23:13:38 -0400 Subject: [PATCH] know when to release the prefix context --- stdlib/REPL/src/REPLCompletions.jl | 17 ++++++++++++++++- stdlib/REPL/test/replcompletions.jl | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index d59a18e6d4f16..29efb25f722a0 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -1120,6 +1120,21 @@ function complete_loading_candidates!(suggestions::Vector{Completion}, pkgstarts return suggestions end +# Check whether we should still be limiting completions to the scope of the prefix +# i.e. `Base.@time myvar` should complete to `Base.@time myvariable` because `myvariable` +# is a valid expression in the active module, but not Base +function in_scope_of_prefix(string::String, pos::Int, separatorpos::Int) + sep = string[separatorpos] + sep == ':' && return true # `using Base: foo` etc. + sep == '.' || return false + pos <= separatorpos && return true + after_separator = string[separatorpos+1:pos] + isempty(after_separator) && return true + startswith(after_separator, "@") || return true + # is a macro, so check for space or `(` that signifies the start of the macro call + return !any(in((' ', '(')), after_separator) +end + function complete_identifiers!(suggestions::Vector{Completion}, context_module::Module, string::String, name::String, pos::Int, separatorpos::Int, startpos::Int; @@ -1130,7 +1145,7 @@ function complete_identifiers!(suggestions::Vector{Completion}, complete_keyword!(suggestions, name) complete_keyval!(suggestions, name) end - if separatorpos > 1 && (string[separatorpos] == '.' || string[separatorpos] == ':') + if separatorpos > 1 && in_scope_of_prefix(string, pos, separatorpos) s = string[1:prevind(string, separatorpos)] # First see if the whole string up to `pos` is a valid expression. If so, use it. prefix = Meta.parse(s, raise=false, depwarn=false) diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 1355f74c9bfff..6fb7509a4d24e 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -2472,3 +2472,19 @@ let (c, r, res) = test_complete_context("global xxx::Number = Base.", Main) @test res @test "pi" ∈ c end + +# release context once past a qualified name +for s in ("Base.@time TestInternalBinding", "Base.@time Base.@time TestInternalBinding", + "Base.@time(TestInternalBinding", "@time(Base.@time TestInternalBinding", "Base.Base.@time TestInternalBinding") + let (c, r, res) = test_complete_context(s; shift=false) + @test res + @test "TestInternalBindingOnly" ∈ c + end +end +for s in ("Base.@time TestInternalBindingOnly.bind", "Base.@time Base.@time TestInternalBindingOnly.bind", + "Base.@time(TestInternalBindingOnly.bind", "@time(Base.@time TestInternalBindingOnly.bind") + let (c, r, res) = test_complete_context(s; shift=false) + @test res + @test "binding" ∈ c + end +end