@@ -165,7 +165,6 @@ function complete_symbol!(suggestions::Vector{Completion},
165165 val = res. val
166166 if isa (val, Module)
167167 mod = val
168- lookup_module = true
169168 else
170169 lookup_module = false
171170 t = typeof (val)
@@ -242,7 +241,7 @@ function field_completion_eligible(@nospecialize t)
242241 return match. method === GENERIC_PROPERTYNAMES_METHOD
243242end
244243
245- function complete_from_list! (suggestions:: Vector{Completion} , T:: Type , list:: Vector{String} , s:: Union{ String,SubString{String}} )
244+ function complete_from_list! (suggestions:: Vector{Completion} , T:: Type , list:: Vector{String} , s:: String )
246245 r = searchsorted (list, s)
247246 i = first (r)
248247 n = length (list)
@@ -264,12 +263,12 @@ const sorted_keywords = [
264263 " primitive type" , " quote" , " return" , " struct" ,
265264 " try" , " using" , " while" ]
266265
267- complete_keyword! (suggestions:: Vector{Completion} , s:: Union{ String,SubString{String}} ) =
266+ complete_keyword! (suggestions:: Vector{Completion} , s:: String ) =
268267 complete_from_list! (suggestions, KeywordCompletion, sorted_keywords, s)
269268
270269const sorted_keyvals = [" false" , " true" ]
271270
272- complete_keyval! (suggestions:: Vector{Completion} , s:: Union{ String,SubString{String}} ) =
271+ complete_keyval! (suggestions:: Vector{Completion} , s:: String ) =
273272 complete_from_list! (suggestions, KeyvalCompletion, sorted_keyvals, s)
274273
275274function do_raw_escape (s)
@@ -892,17 +891,36 @@ const subscript_regex = Regex("^\\\\_[" * join(isdigit(k) || isletter(k) ? "$k"
892891const superscripts = Dict (k[3 ]=> v[1 ] for (k,v) in latex_symbols if startswith (k, " \\ ^" ) && length (k)== 3 )
893892const superscript_regex = Regex (" ^\\\\\\ ^[" * join (isdigit (k) || isletter (k) ? " $k " : " \\ $k " for k in keys (superscripts)) * " ]+\\ z" )
894893
895- # Aux function to detect whether we're right after a
896- # using or import keyword
897- function afterusing (string:: String , startpos:: Int )
898- (isempty (string) || startpos == 0 ) && return false
899- str = string[1 : prevind (string,startpos)]
900- isempty (str) && return false
901- rstr = reverse (str)
902- r = findfirst (r" \s (gnisu|tropmi)\b " , rstr)
903- r === nothing && return false
904- fr = reverseind (str, last (r))
905- return occursin (r" ^\b (using|import)\s *((\w +[.])*\w +\s *,\s *)*$" , str[fr: end ])
894+ # Aux function to detect whether we're right after a using or import keyword
895+ function get_import_mode (s:: String )
896+ # match simple cases like `using |` and `import |`
897+ mod_import_match_simple = match (r" ^\b (using|import)\s *$" , s)
898+ if mod_import_match_simple != = nothing
899+ if mod_import_match_simple[1 ] == " using"
900+ return :using_module
901+ else
902+ return :import_module
903+ end
904+ end
905+ # match module import statements like `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |`
906+ mod_import_match = match (r" ^\b (using|import)\s +([\w\. ]+(?:\s *,\s *[\w\. ]+)*),?\s *$" , s)
907+ if mod_import_match != = nothing
908+ if mod_import_match. captures[1 ] == " using"
909+ return :using_module
910+ else
911+ return :import_module
912+ end
913+ end
914+ # now match explicit name import statements like `using Foo: |` and `import Foo: bar, baz|`
915+ name_import_match = match (r" ^\b (using|import)\s +([\w\. ]+)\s *:\s *([\w @!\s ,]+)$" , s)
916+ if name_import_match != = nothing
917+ if name_import_match[1 ] == " using"
918+ return :using_name
919+ else
920+ return :import_name
921+ end
922+ end
923+ return nothing
906924end
907925
908926function close_path_completion (dir, paths, str, pos)
@@ -1084,16 +1102,16 @@ end
10841102
10851103function complete_identifiers! (suggestions:: Vector{Completion} ,
10861104 context_module:: Module , string:: String , name:: String ,
1087- pos:: Int , dotpos :: Int , startpos:: Int ;
1105+ pos:: Int , separatorpos :: Int , startpos:: Int ;
10881106 comp_keywords:: Bool = false ,
10891107 complete_modules_only:: Bool = false )
10901108 ex = nothing
10911109 if comp_keywords
10921110 complete_keyword! (suggestions, name)
10931111 complete_keyval! (suggestions, name)
10941112 end
1095- if dotpos > 1 && string[dotpos ] == ' .'
1096- s = string[1 : prevind (string, dotpos )]
1113+ if separatorpos > 1 && ( string[separatorpos ] == ' .' || string[separatorpos] == ' : ' )
1114+ s = string[1 : prevind (string, separatorpos )]
10971115 # First see if the whole string up to `pos` is a valid expression. If so, use it.
10981116 ex = Meta. parse (s, raise= false , depwarn= false )
10991117 if isexpr (ex, :incomplete )
@@ -1132,10 +1150,6 @@ function complete_identifiers!(suggestions::Vector{Completion},
11321150 end
11331151 isexpr (ex, :incomplete ) && (ex = nothing )
11341152 elseif isexpr (ex, (:using , :import ))
1135- if isexpr (ex, :import )
1136- # allow completion for `import Mod.name` (where `name` is not a module)
1137- complete_modules_only = false
1138- end
11391153 arglast = ex. args[end ] # focus on completion to the last argument
11401154 if isexpr (arglast, :.)
11411155 # We come here for cases like:
@@ -1186,7 +1200,7 @@ function complete_identifiers!(suggestions::Vector{Completion},
11861200 end
11871201 end
11881202 complete_symbol! (suggestions, ex, name, context_module; complete_modules_only)
1189- return sort! ( unique ( suggestions), by = completion_text), (dotpos + 1 ) : pos, true
1203+ return suggestions
11901204end
11911205
11921206function completions (string:: String , pos:: Int , context_module:: Module = Main, shift:: Bool = true , hint:: Bool = false )
@@ -1248,10 +1262,11 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
12481262 ok, ret = bslash_completions (string, pos)
12491263 ok && return ret
12501264 startpos = first (varrange) + 4
1251- dotpos = something (findprev (isequal (' .' ), string, first (varrange)- 1 ), 0 )
1265+ separatorpos = something (findprev (isequal (' .' ), string, first (varrange)- 1 ), 0 )
12521266 name = string[startpos: pos]
1253- return complete_identifiers! (Completion[], context_module, string, name, pos,
1254- dotpos, startpos)
1267+ complete_identifiers! (suggestions, context_module, string, name,
1268+ pos, separatorpos, startpos)
1269+ return sort! (unique! (completion_text, suggestions), by= completion_text), (separatorpos+ 1 ): pos, true
12551270 elseif inc_tag === :cmd
12561271 # TODO : should this call shell_completions instead of partially reimplementing it?
12571272 let m = match (r" [\t\n\r\" `><=*?|]| (?!\\ )" , reverse (partial)) # fuzzy shell_parse in reverse
@@ -1383,22 +1398,24 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
13831398 kwarg_completion, wordrange = complete_keyword_argument (partial, pos, context_module)
13841399 isempty (wordrange) || return kwarg_completion, wordrange, ! isempty (kwarg_completion)
13851400
1386- dotpos = something (findprev (isequal (' .' ), string, pos), 0 )
13871401 startpos = nextind (string, something (findprev (in (non_identifier_chars), string, pos), 0 ))
13881402 # strip preceding ! operator
13891403 if (m = match (r" \G\! +" , partial, startpos)) isa RegexMatch
13901404 startpos += length (m. match)
13911405 end
13921406
1393- name = string[max (startpos, dotpos+ 1 ): pos]
1394- if afterusing (string, startpos)
1395- # We're right after using or import. Let's look only for packages
1396- # and modules we can reach from here
1407+ separatorpos = something (findprev (isequal (' .' ), string, pos), 0 )
1408+ namepos = max (startpos, separatorpos+ 1 )
1409+ name = string[namepos: pos]
1410+ import_mode = get_import_mode (string)
1411+ if import_mode === :using_module || import_mode === :import_module
1412+ # Given input lines like `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |`:
1413+ # Let's look only for packages and modules we can reach from here
13971414
13981415 # If there's no dot, we're in toplevel, so we should
13991416 # also search for packages
14001417 s = string[startpos: pos]
1401- if dotpos <= startpos
1418+ if separatorpos <= startpos
14021419 for dir in Base. load_path ()
14031420 if basename (dir) in Base. project_names && isfile (dir)
14041421 complete_loading_candidates! (suggestions, s, dir)
@@ -1431,17 +1448,21 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
14311448 end
14321449 end
14331450 comp_keywords = false
1434- complete_modules_only = true
1451+ complete_modules_only = import_mode === :using_module # allow completion for `import Mod.name` (where `name` is not a module)
1452+ elseif import_mode === :using_name || import_mode === :import_name
1453+ # `using Foo: |` and `import Foo: bar, baz|`
1454+ separatorpos = findprev (isequal (' :' ), string, pos):: Int
1455+ comp_keywords = false
1456+ complete_modules_only = false
14351457 else
1436- comp_keywords = ! isempty (name) && startpos > dotpos
1458+ comp_keywords = ! isempty (name) && startpos > separatorpos
14371459 complete_modules_only = false
14381460 end
14391461
1440- startpos == 0 && (pos = - 1 )
1441- dotpos < startpos && (dotpos = startpos - 1 )
1442- return complete_identifiers! (suggestions, context_module, string, name, pos,
1443- dotpos, startpos;
1444- comp_keywords, complete_modules_only)
1462+ complete_identifiers! (suggestions, context_module, string, name,
1463+ pos, separatorpos, startpos;
1464+ comp_keywords, complete_modules_only)
1465+ return sort! (unique! (completion_text, suggestions), by= completion_text), namepos: pos, true
14451466end
14461467
14471468function shell_completions (string, pos, hint:: Bool = false )
0 commit comments