diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index e70eb8dd97927..d634241086aef 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -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) @@ -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 diff --git a/stdlib/REPL/test/replcompletions.jl b/stdlib/REPL/test/replcompletions.jl index 59e994f88945b..11154323d704a 100644 --- a/stdlib/REPL/test/replcompletions.jl +++ b/stdlib/REPL/test/replcompletions.jl @@ -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) @@ -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