@@ -119,7 +119,8 @@ function completes_global(x, name)
119119end
120120
121121function appendmacro! (syms, macros, needle, endchar)
122- for s in macros
122+ for macsym in macros
123+ s = String (macsym)
123124 if endswith (s, needle)
124125 from = nextind (s, firstindex (s))
125126 to = prevind (s, sizeof (s)- sizeof (needle)+ 1 )
@@ -131,28 +132,21 @@ end
131132function filtered_mod_names (ffunc:: Function , mod:: Module , name:: AbstractString , all:: Bool = false , imported:: Bool = false )
132133 ssyms = names (mod, all = all, imported = imported)
133134 filter! (ffunc, ssyms)
134- syms = String[ string (s) for s in ssyms]
135- macros = filter (x -> startswith (x, " @ " * name), syms)
135+ macros = filter (x -> startswith ( String (x), " @ " * name), ssyms)
136+ syms = String[ sprint ((io,s) -> Base . show_sym (io, s; allow_macroname = true ), s) for s in ssyms if completes_global ( String (s), name)]
136137 appendmacro! (syms, macros, " _str" , " \" " )
137138 appendmacro! (syms, macros, " _cmd" , " `" )
138- filter! (x-> completes_global (x, name), syms)
139139 return [ModuleCompletion (mod, sym) for sym in syms]
140140end
141141
142142# REPL Symbol Completions
143- function complete_symbol (sym :: String , @nospecialize (ffunc), context_module:: Module = Main)
143+ function complete_symbol (@nospecialize (ex), name :: String , @nospecialize (ffunc), context_module:: Module = Main)
144144 mod = context_module
145- name = sym
146145
147146 lookup_module = true
148147 t = Union{}
149148 val = nothing
150- if something (findlast (in (non_identifier_chars), sym), 0 ) < something (findlast (isequal (' .' ), sym), 0 )
151- # Find module
152- lookup_name, name = rsplit (sym, " ." , limit= 2 )
153-
154- ex = Meta. parse (lookup_name, raise= false , depwarn= false )
155-
149+ if ex != = nothing
156150 res = repl_eval_ex (ex, context_module)
157151 res === nothing && return Completion[]
158152 if res isa Const
@@ -898,7 +892,7 @@ function complete_keyword_argument(partial, last_idx, context_module)
898892 end
899893
900894 suggestions = Completion[KeywordArgumentCompletion (kwarg) for kwarg in kwargs]
901- append! (suggestions, complete_symbol (last_word, Returns (true ), context_module))
895+ append! (suggestions, complete_symbol (nothing , last_word, Returns (true ), context_module))
902896
903897 return sort! (suggestions, by= completion_text), wordrange
904898end
@@ -919,6 +913,55 @@ function project_deps_get_completion_candidates(pkgstarts::String, project_file:
919913 return Completion[PackageCompletion (name) for name in loading_candidates]
920914end
921915
916+ function complete_identifiers! (suggestions:: Vector{Completion} , @nospecialize (ffunc:: Function ), context_module:: Module , string:: String , name:: String , pos:: Int , dotpos:: Int , startpos:: Int , comp_keywords= false )
917+ ex = nothing
918+ comp_keywords && append! (suggestions, complete_keyword (name))
919+ if dotpos > 1 && string[dotpos] == ' .'
920+ s = string[1 : dotpos- 1 ]
921+ # First see if the whole string up to `pos` is a valid expression. If so, use it.
922+ ex = Meta. parse (s, raise= false , depwarn= false )
923+ if isexpr (ex, :incomplete )
924+ s = string[startpos: pos]
925+ # Heuristic to find the start of the expression. TODO : This would be better
926+ # done with a proper error-recovering parser.
927+ if 0 < startpos <= lastindex (string) && string[startpos] == ' .'
928+ i = prevind (string, startpos)
929+ while 0 < i
930+ c = string[i]
931+ if c in (' )' , ' ]' )
932+ if c == ' )'
933+ c_start = ' ('
934+ c_end = ' )'
935+ elseif c == ' ]'
936+ c_start = ' ['
937+ c_end = ' ]'
938+ end
939+ frange, end_of_identifier = find_start_brace (string[1 : prevind (string, i)], c_start= c_start, c_end= c_end)
940+ isempty (frange) && break # unbalanced parens
941+ startpos = first (frange)
942+ i = prevind (string, startpos)
943+ elseif c in (' \' ' , ' \" ' , ' \` ' )
944+ s = " $c$c " * string[startpos: pos]
945+ break
946+ else
947+ break
948+ end
949+ s = string[startpos: pos]
950+ end
951+ end
952+ if something (findlast (in (non_identifier_chars), s), 0 ) < something (findlast (isequal (' .' ), s), 0 )
953+ lookup_name, name = rsplit (s, " ." , limit= 2 )
954+ name = String (name)
955+
956+ ex = Meta. parse (lookup_name, raise= false , depwarn= false )
957+ end
958+ isexpr (ex, :incomplete ) && (ex = nothing )
959+ end
960+ end
961+ append! (suggestions, complete_symbol (ex, name, ffunc, context_module))
962+ return sort! (unique (suggestions), by= completion_text), (dotpos+ 1 ): pos, true
963+ end
964+
922965function completions (string:: String , pos:: Int , context_module:: Module = Main, shift:: Bool = true )
923966 # First parse everything up to the current position
924967 partial = string[1 : pos]
@@ -962,8 +1005,25 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
9621005 length (matches)> 0 && return Completion[DictCompletion (identifier, match) for match in sort! (matches)], loc:: Int : pos, true
9631006 end
9641007
1008+ ffunc = Returns (true )
1009+ suggestions = Completion[]
1010+
1011+ # Check if this is a var"" string macro that should be completed like
1012+ # an identifier rather than a string.
1013+ # TODO : It would be nice for the parser to give us more information here
1014+ # so that we can lookup the macro by identity rather than pattern matching
1015+ # its invocation.
1016+ varrange = findprev (" var\" " , string, pos)
1017+
1018+ if varrange != = nothing
1019+ ok, ret = bslash_completions (string, pos)
1020+ ok && return ret
1021+ startpos = first (varrange) + 4
1022+ dotpos = something (findprev (isequal (' .' ), string, startpos), 0 )
1023+ return complete_identifiers! (Completion[], ffunc, context_module, string,
1024+ string[startpos: pos], pos, dotpos, startpos)
9651025 # otherwise...
966- if inc_tag in [:cmd , :string ]
1026+ elseif inc_tag in [:cmd , :string ]
9671027 m = match (r" [\t\n\r\" `><=*?|]| (?!\\ )" , reverse (partial))
9681028 startpos = nextind (partial, reverseind (partial, m. offset))
9691029 r = startpos: pos
@@ -1010,9 +1070,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
10101070 startpos += length (m. match)
10111071 end
10121072
1013- ffunc = Returns (true )
1014- suggestions = Completion[]
1015- comp_keywords = true
1073+ name = string[max (startpos, dotpos+ 1 ): pos]
1074+ comp_keywords = ! isempty (name) && startpos > dotpos
10161075 if afterusing (string, startpos)
10171076 # We're right after using or import. Let's look only for packages
10181077 # and modules we can reach from here
@@ -1054,38 +1113,11 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
10541113 ffunc = (mod,x)-> (Base. isbindingresolved (mod, x) && isdefined (mod, x) && isa (getfield (mod, x), Module))
10551114 comp_keywords = false
10561115 end
1116+
10571117 startpos == 0 && (pos = - 1 )
10581118 dotpos < startpos && (dotpos = startpos - 1 )
1059- s = string[startpos: pos]
1060- comp_keywords && append! (suggestions, complete_keyword (s))
1061- # if the start of the string is a `.`, try to consume more input to get back to the beginning of the last expression
1062- if 0 < startpos <= lastindex (string) && string[startpos] == ' .'
1063- i = prevind (string, startpos)
1064- while 0 < i
1065- c = string[i]
1066- if c in (' )' , ' ]' )
1067- if c == ' )'
1068- c_start = ' ('
1069- c_end = ' )'
1070- elseif c == ' ]'
1071- c_start = ' ['
1072- c_end = ' ]'
1073- end
1074- frange, end_of_identifier = find_start_brace (string[1 : prevind (string, i)], c_start= c_start, c_end= c_end)
1075- isempty (frange) && break # unbalanced parens
1076- startpos = first (frange)
1077- i = prevind (string, startpos)
1078- elseif c in (' \' ' , ' \" ' , ' \` ' )
1079- s = " $c$c " * string[startpos: pos]
1080- break
1081- else
1082- break
1083- end
1084- s = string[startpos: pos]
1085- end
1086- end
1087- append! (suggestions, complete_symbol (s, ffunc, context_module))
1088- return sort! (unique (suggestions), by= completion_text), (dotpos+ 1 ): pos, true
1119+ return complete_identifiers! (suggestions, ffunc, context_module, string,
1120+ name, pos, dotpos, startpos, comp_keywords)
10891121end
10901122
10911123function shell_completions (string, pos)
0 commit comments