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

REPL completions: Enter import mode only when cursor beyond "import" #57473

Closed
wants to merge 3 commits into from
Closed
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
45 changes: 15 additions & 30 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -939,38 +939,23 @@ const superscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k,
const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(superscripts)) * "]+\\z")

# Aux function to detect whether we're right after a using or import keyword
function get_import_mode(s::String)
# allow all of these to start with leading whitespace and macros like @eval and @eval(
# ^\s*(?:@\w+\s*(?:\(\s*)?)?

# match simple cases like `using |` and `import |`
mod_import_match_simple = match(r"^\s*(?:@\w+\s*(?:\(\s*)?)?\b(using|import)\s*$", s)
if mod_import_match_simple !== nothing
if mod_import_match_simple[1] == "using"
return :using_module
else
return :import_module
end
end
# match module import statements like `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |`
mod_import_match = match(r"^\s*(?:@\w+\s*(?:\(\s*)?)?\b(using|import)\s+([\w\.]+(?:\s*,\s*[\w\.]+)*),?\s*$", s)
if mod_import_match !== nothing
if mod_import_match.captures[1] == "using"
return :using_module
else
return :import_module
function get_import_mode(s::String, pos::Int)
# Capture group 1 will be returned, group 2 is where the cursor should be.
function match_pos(re)
for m in eachmatch(re, s, overlap=true)
m !== nothing || continue
pos in range(m.offsets[2], length=sizeof(m[2])) || continue
return m[1]
end
end

# match module import statements like `using |`, `import |`, `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |`
m = match_pos(r"\b(using|import)(\s+(?:[\w\.]+(?:\s*,\s*[\w\.]+)*(:?\s*,)?\s*)?)")
m !== nothing && return m == "using" ? :using_module : :import_module

# now match explicit name import statements like `using Foo: |` and `import Foo: bar, baz|`
name_import_match = match(r"^\s*(?:@\w+\s*(?:\(\s*)?)?\b(using|import)\s+([\w\.]+)\s*:\s*([\w@!\s,]+)$", s)
if name_import_match !== nothing
if name_import_match[1] == "using"
return :using_name
else
return :import_name
end
end
return nothing
m = match_pos(r"\b(using|import)\s+(?:[\w\.]+(?:\s*,\s*[\w\.]+)*)\s*(:\s*(?:[\w@!\s,]+)*)")
m !== nothing && return m == "using" ? :using_name : :import_name
end

function close_path_completion(dir, path, str, pos)
Expand Down Expand Up @@ -1462,7 +1447,7 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
separatorpos = something(findprev(isequal('.'), string, pos), 0)
namepos = max(startpos, separatorpos+1)
name = string[namepos:pos]
import_mode = get_import_mode(string)
import_mode = get_import_mode(string, pos)
if import_mode === :using_module || import_mode === :import_module
# Given input lines like `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |`:
# Let's look only for packages and modules we can reach from here
Expand Down
22 changes: 22 additions & 0 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ end

test_complete(s) = map_completion_text(@inferred(completions(s, lastindex(s))))
test_scomplete(s) = map_completion_text(@inferred(shell_completions(s, lastindex(s))))
# | is reserved in test_complete_pos
test_complete_pos(s) = map_completion_text(@inferred(completions(replace(s, '|' => ""), findfirst('|', s)-1)))
test_complete_context(s, m=@__MODULE__; shift::Bool=true) =
map_completion_text(@inferred(completions(s,lastindex(s), m, shift)))
test_complete_foo(s) = test_complete_context(s, Main.CompletionFoo)
Expand Down Expand Up @@ -2484,3 +2486,23 @@ let (c, r, res) = test_complete_context("global xxx::Number = Base.", Main)
@test res
@test "pi" ∈ c
end

# #57473
let (c, r) = test_complete_pos("@tim| using Date")
@test "@time" in c
@test r == 1:4
end

# #56389
let s = "begin\n using Linear"
c, r = test_complete(s)
@test "LinearAlgebra" in c
@test r == 15:20
@test s[r] == "Linear"
end
let s = "using .CompletionFoo: bar, type_"
c, r = test_complete(s)
@test "type_test" in c
@test r == 28:32
@test s[r] == "type_"
end