• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37624

18 Sep 2023 02:15AM UTC coverage: 85.879% (-0.3%) from 86.162%
#37624

push

local

web-flow
testdefs: make sure that if a test set changes the active project, they change it back when they're done (#51029)

We already check the following for mutation by a test set:
1. DEPOT_PATH
2. LOAD_PATH
3. ENV

So this PR just adds the active project to the list of things we check.

Changing the active project during a test set can definitely have
negative effects on subsequent test sets that are run on the same
worker, so we want to make sure if a test set changes the active
project, that they change it back when they're done.

72351 of 84248 relevant lines covered (85.88%)

11376443.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.43
/stdlib/REPL/src/REPLCompletions.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
module REPLCompletions
4

5
export completions, shell_completions, bslash_completions, completion_text
6

7
using Core: CodeInfo, MethodInstance, CodeInstance, Const
8
const CC = Core.Compiler
9
using Base.Meta
10
using Base: propertynames, something
11

12
abstract type Completion end
13

14
struct TextCompletion <: Completion
15
    text::String
4✔
16
end
17

18
struct KeywordCompletion <: Completion
19
    keyword::String
158✔
20
end
21

22
struct KeyvalCompletion <: Completion
23
    keyval::String
15✔
24
end
25

26
struct PathCompletion <: Completion
27
    path::String
6,700✔
28
end
29

30
struct ModuleCompletion <: Completion
31
    parent::Module
380,074✔
32
    mod::String
33
end
34

35
struct PackageCompletion <: Completion
36
    package::String
195✔
37
end
38

39
struct PropertyCompletion <: Completion
40
    value
31✔
41
    property::Symbol
42
end
43

44
struct FieldCompletion <: Completion
45
    typ::DataType
25✔
46
    field::Symbol
47
end
48

49
struct MethodCompletion <: Completion
50
    tt # may be used by an external consumer to infer return type, etc.
51
    method::Method
52
    MethodCompletion(@nospecialize(tt), method::Method) = new(tt, method)
2,215✔
53
end
54

55
struct BslashCompletion <: Completion
56
    bslash::String
5,164✔
57
end
58

59
struct ShellCompletion <: Completion
60
    text::String
61
end
62

63
struct DictCompletion <: Completion
64
    dict::AbstractDict
128✔
65
    key::String
66
end
67

68
struct KeywordArgumentCompletion <: Completion
69
    kwarg::String
47✔
70
end
71

72
# interface definition
73
function Base.getproperty(c::Completion, name::Symbol)
301✔
74
    if name === :text
8,064,427✔
75
        return getfield(c, :text)::String
4✔
76
    elseif name === :keyword
8,062,716✔
77
        return getfield(c, :keyword)::String
1,707✔
78
    elseif name === :path
8,056,136✔
79
        return getfield(c, :path)::String
55,040✔
80
    elseif name === :parent
8,056,136✔
81
        return getfield(c, :parent)::Module
×
82
    elseif name === :mod
9,047✔
83
        return getfield(c, :mod)::String
8,047,089✔
84
    elseif name === :package
6,543✔
85
        return getfield(c, :package)::String
2,504✔
86
    elseif name === :property
6,543✔
87
        return getfield(c, :property)::Symbol
103✔
88
    elseif name === :field
6,440✔
89
        return getfield(c, :field)::Symbol
55✔
90
    elseif name === :method
5,597✔
91
        return getfield(c, :method)::Method
2,217✔
92
    elseif name === :bslash
433✔
93
        return getfield(c, :bslash)::String
5,164✔
94
    elseif name === :text
433✔
95
        return getfield(c, :text)::String
×
96
    elseif name === :key
433✔
97
        return getfield(c, :key)::String
128✔
98
    elseif name === :kwarg
83✔
99
        return getfield(c, :kwarg)::String
222✔
100
    end
101
    return getfield(c, name)
83✔
102
end
103

104
_completion_text(c::TextCompletion) = c.text
4✔
105
_completion_text(c::KeywordCompletion) = c.keyword
1,707✔
106
_completion_text(c::KeyvalCompletion) = c.keyval
83✔
107
_completion_text(c::PathCompletion) = c.path
6,559✔
108
_completion_text(c::ModuleCompletion) = c.mod
8,047,089✔
109
_completion_text(c::PackageCompletion) = c.package
2,504✔
110
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
103✔
111
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
55✔
112
_completion_text(c::MethodCompletion) = repr(c.method)
777✔
113
_completion_text(c::BslashCompletion) = c.bslash
5,164✔
114
_completion_text(c::ShellCompletion) = c.text
×
115
_completion_text(c::DictCompletion) = c.key
128✔
116
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
222✔
117

118
completion_text(c) = _completion_text(c)::String
8,064,395✔
119

120
const Completions = Tuple{Vector{Completion}, UnitRange{Int}, Bool}
121

122
function completes_global(x, name)
1,866,768✔
123
    return startswith(x, name) && !('#' in x)
1,866,768✔
124
end
125

126
function appendmacro!(syms, macros, needle, endchar)
20,918✔
127
    for macsym in macros
39,592✔
128
        s = String(macsym)
51,642✔
129
        if endswith(s, needle)
51,642✔
130
            from = nextind(s, firstindex(s))
3,458✔
131
            to = prevind(s, sizeof(s)-sizeof(needle)+1)
3,458✔
132
            push!(syms, s[from:to]*endchar)
6,916✔
133
        end
134
    end
51,642✔
135
end
136

137
function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all::Bool = false, imported::Bool = false)
10,459✔
138
    ssyms = names(mod, all = all, imported = imported)
19,337✔
139
    all || filter!(Base.Fix1(Base.isexported, mod), ssyms)
19,337✔
140
    filter!(ffunc, ssyms)
10,459✔
141
    macros = filter(x -> startswith(String(x), "@" * name), ssyms)
1,877,227✔
142
    syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)]
387,075✔
143
    appendmacro!(syms, macros, "_str", "\"")
10,459✔
144
    appendmacro!(syms, macros, "_cmd", "`")
10,459✔
145
    return [ModuleCompletion(mod, sym) for sym in syms]
10,459✔
146
end
147

148
# REPL Symbol Completions
149
function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), context_module::Module=Main)
1,638✔
150
    mod = context_module
×
151

152
    lookup_module = true
×
153
    t = Union{}
×
154
    val = nothing
×
155
    if ex !== nothing
1,638✔
156
        res = repl_eval_ex(ex, context_module)
656✔
157
        res === nothing && return Completion[]
656✔
158
        if res isa Const
638✔
159
            val = res.val
624✔
160
            if isa(val, Module)
624✔
161
                mod = val
599✔
162
                lookup_module = true
599✔
163
            else
164
                lookup_module = false
×
165
                t = typeof(val)
649✔
166
            end
167
        else
168
            lookup_module = false
×
169
            t = CC.widenconst(res)
14✔
170
        end
171
    end
172

173
    suggestions = Completion[]
1,620✔
174
    if lookup_module
1,620✔
175
        # We will exclude the results that the user does not want, as well
176
        # as excluding Main.Main.Main, etc., because that's most likely not what
177
        # the user wants
178
        p = let mod=mod, modname=nameof(mod)
1,581✔
179
            (s::Symbol) -> !Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool
1,893,967✔
180
        end
181
        # Looking for a binding in a module
182
        if mod == context_module
1,581✔
183
            # Also look in modules we got through `using`
184
            mods = ccall(:jl_module_usings, Any, (Any,), context_module)::Vector
1,292✔
185
            for m in mods
1,292✔
186
                append!(suggestions, filtered_mod_names(p, m::Module, name))
11,964✔
187
            end
10,170✔
188
            append!(suggestions, filtered_mod_names(p, mod, name, true, true))
2,018✔
189
        else
190
            append!(suggestions, filtered_mod_names(p, mod, name, true, false))
1,870✔
191
        end
192
    elseif val !== nothing # looking for a property of an instance
39✔
193
        for property in propertynames(val, false)
24✔
194
            # TODO: support integer arguments (#36872)
195
            if property isa Symbol && startswith(string(property), name)
66✔
196
                push!(suggestions, PropertyCompletion(val, property))
31✔
197
            end
198
        end
35✔
199
    else
200
        # Looking for a member of a type
201
        add_field_completions!(suggestions, name, t)
15✔
202
    end
203
    return suggestions
1,620✔
204
end
205

206
function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t))
19✔
207
    if isa(t, Union)
19✔
208
        add_field_completions!(suggestions, name, t.a)
2✔
209
        add_field_completions!(suggestions, name, t.b)
2✔
210
    elseif t isa DataType && t != Any
17✔
211
        # Check for cases like Type{typeof(+)}
212
        if Base.isType(t)
16✔
213
            t = typeof(t.parameters[1])
×
214
        end
215
        # Only look for fields if this is a concrete type
216
        if isconcretetype(t)
16✔
217
            fields = fieldnames(t)
16✔
218
            for field in fields
16✔
219
                isa(field, Symbol) || continue # Tuple type has ::Int field name
29✔
220
                s = string(field)
29✔
221
                if startswith(s, name)
58✔
222
                    push!(suggestions, FieldCompletion(t, field))
25✔
223
                end
224
            end
29✔
225
        end
226
    end
227
end
228

229
function complete_from_list(T::Type, list::Vector{String}, s::Union{String,SubString{String}})
1,253✔
230
    r = searchsorted(list, s)
1,253✔
231
    i = first(r)
1,253✔
232
    n = length(list)
1,253✔
233
    while i <= n && startswith(list[i],s)
2,600✔
234
        r = first(r):i
173✔
235
        i += 1
173✔
236
    end
173✔
237
    Completion[T(kw) for kw in list[r]]
1,253✔
238
end
239

240
const sorted_keywords = [
241
    "abstract type", "baremodule", "begin", "break", "catch", "ccall",
242
    "const", "continue", "do", "else", "elseif", "end", "export",
243
    "finally", "for", "function", "global", "if", "import",
244
    "let", "local", "macro", "module", "mutable struct",
245
    "primitive type", "quote", "return", "struct",
246
    "try", "using", "while"]
247

248
complete_keyword(s::Union{String,SubString{String}}) = complete_from_list(KeywordCompletion, sorted_keywords, s)
542✔
249

250
const sorted_keyvals = ["false", "true"]
251

252
complete_keyval(s::Union{String,SubString{String}}) = complete_from_list(KeyvalCompletion, sorted_keyvals, s)
711✔
253

254
function complete_path(path::AbstractString, pos::Int;
1,136✔
255
                       use_envpath=false, shell_escape=false,
256
                       string_escape=false)
257
    @assert !(shell_escape && string_escape)
568✔
258
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
568✔
259
        # if the path is just "~", don't consider the expanded username as a prefix
260
        if path == "~"
2✔
261
            dir, prefix = homedir(), ""
2✔
262
        else
263
            dir, prefix = splitdir(homedir() * path[2:end])
2✔
264
        end
265
    else
266
        dir, prefix = splitdir(path)
566✔
267
    end
268
    local files
×
269
    try
568✔
270
        if isempty(dir)
568✔
271
            files = readdir()
302✔
272
        elseif isdir(dir)
266✔
273
            files = readdir(dir)
107✔
274
        else
275
            return Completion[], 0:-1, false
568✔
276
        end
277
    catch
278
        return Completion[], 0:-1, false
×
279
    end
280

281
    matches = Set{String}()
409✔
282
    for file in files
414✔
283
        if startswith(file, prefix)
77,582✔
284
            p = joinpath(dir, file)
6,339✔
285
            is_dir = try isdir(p) catch; false end
19,019✔
286
            push!(matches, is_dir ? joinpath(file, "") : file)
6,339✔
287
        end
288
    end
42,932✔
289

290
    if use_envpath && length(dir) == 0
409✔
291
        # Look for files in PATH as well
292
        local pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":")
18✔
293

294
        for pathdir in pathdirs
18✔
295
            local actualpath
×
296
            try
228✔
297
                actualpath = realpath(pathdir)
260✔
298
            catch
299
                # Bash doesn't expect every folder in PATH to exist, so neither shall we
300
                continue
32✔
301
            end
302

303
            if actualpath != pathdir && in(actualpath,pathdirs)
244✔
304
                # Remove paths which (after resolving links) are in the env path twice.
305
                # Many distros eg. point /bin to /usr/bin but have both in the env path.
306
                continue
48✔
307
            end
308

309
            local filesinpath
×
310
            try
148✔
311
                filesinpath = readdir(pathdir)
149✔
312
            catch e
313
                # Bash allows dirs in PATH that can't be read, so we should as well.
314
                if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
1✔
315
                    continue
1✔
316
                else
317
                    # We only handle IOError and ArgumentError here
318
                    rethrow()
×
319
                end
320
            end
321

322
            for file in filesinpath
195✔
323
                # In a perfect world, we would filter on whether the file is executable
324
                # here, or even on whether the current user can execute the file in question.
325
                if startswith(file, prefix) && isfile(joinpath(pathdir, file))
63,195✔
326
                    push!(matches, file)
589✔
327
                end
328
            end
34,633✔
329
        end
246✔
330
    end
331

332
    function do_escape(s)
409✔
333
        return shell_escape ? replace(s, r"(\s|\\)" => s"\\\0") :
10,356✔
334
               string_escape ? escape_string(s, ('\"','$')) :
335
               s
336
    end
337

338
    matchList = Completion[PathCompletion(do_escape(s)) for s in matches]
6,966✔
339
    startpos = pos - lastindex(do_escape(prefix)) + 1
543✔
340
    # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`,
341
    # hence we need to add one to get the first index. This is also correct when considering
342
    # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`.
343
    return matchList, startpos:pos, !isempty(matchList)
409✔
344
end
345

346
function complete_expanduser(path::AbstractString, r)
138✔
347
    expanded =
138✔
348
        try expanduser(path)
139✔
349
        catch e
350
            e isa ArgumentError || rethrow()
1✔
351
            path
139✔
352
        end
353
    return Completion[PathCompletion(expanded)], r, path != expanded
138✔
354
end
355

356
# Returns a range that includes the method name in front of the first non
357
# closed start brace from the end of the string.
358
function find_start_brace(s::AbstractString; c_start='(', c_end=')')
5,284✔
359
    braces = 0
×
360
    r = reverse(s)
2,642✔
361
    i = firstindex(r)
×
362
    in_single_quotes = false
×
363
    in_double_quotes = false
×
364
    in_back_ticks = false
×
365
    in_comment = 0
×
366
    while i <= ncodeunits(r)
61,349✔
367
        c, i = iterate(r, i)
118,734✔
368
        if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '='
59,367✔
369
            c, i = iterate(r, i) # consume '='
8✔
370
            new_comments = 1
×
371
            # handle #=#=#=#, by counting =# pairs
372
            while i <= ncodeunits(r) && iterate(r, i)[1] == '#'
6✔
373
                c, i = iterate(r, i) # consume '#'
6✔
374
                iterate(r, i)[1] == '=' || break
3✔
375
                c, i = iterate(r, i) # consume '='
4✔
376
                new_comments += 1
2✔
377
            end
2✔
378
            if c == '='
4✔
379
                in_comment += new_comments
3✔
380
            else
381
                in_comment -= new_comments
5✔
382
            end
383
        elseif !in_single_quotes && !in_double_quotes && !in_back_ticks && in_comment == 0
59,363✔
384
            if c == c_start
57,628✔
385
                braces += 1
910✔
386
            elseif c == c_end
56,718✔
387
                braces -= 1
250✔
388
            elseif c == '\''
56,468✔
389
                in_single_quotes = true
15✔
390
            elseif c == '"'
56,453✔
391
                in_double_quotes = true
271✔
392
            elseif c == '`'
56,182✔
393
                in_back_ticks = true
57,628✔
394
            end
395
        else
396
            if in_single_quotes &&
1,735✔
397
                c == '\'' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
398
                in_single_quotes = false
15✔
399
            elseif in_double_quotes &&
1,720✔
400
                c == '"' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
401
                in_double_quotes = false
228✔
402
            elseif in_back_ticks &&
1,492✔
403
                c == '`' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
404
                in_back_ticks = false
7✔
405
            elseif in_comment > 0 &&
1,485✔
406
                c == '=' && i <= ncodeunits(r) && iterate(r, i)[1] == '#'
407
                # handle =#=#=#=, by counting #= pairs
408
                c, i = iterate(r, i) # consume '#'
6✔
409
                old_comments = 1
×
410
                while i <= ncodeunits(r) && iterate(r, i)[1] == '='
4✔
411
                    c, i = iterate(r, i) # consume '='
4✔
412
                    iterate(r, i)[1] == '#' || break
2✔
413
                    c, i = iterate(r, i) # consume '#'
2✔
414
                    old_comments += 1
1✔
415
                end
1✔
416
                if c == '#'
3✔
417
                    in_comment -= old_comments
2✔
418
                else
419
                    in_comment += old_comments
1✔
420
                end
421
            end
422
        end
423
        braces == 1 && break
59,367✔
424
    end
58,707✔
425
    braces != 1 && return 0:-1, -1
2,642✔
426
    method_name_end = reverseind(s, i)
660✔
427
    startind = nextind(s, something(findprev(in(non_identifier_chars), s, method_name_end), 0))::Int
1,022✔
428
    return (startind:lastindex(s), method_name_end)
660✔
429
end
430

431
struct REPLInterpreterCache
432
    dict::IdDict{MethodInstance,CodeInstance}
×
433
end
434
REPLInterpreterCache() = REPLInterpreterCache(IdDict{MethodInstance,CodeInstance}())
×
435
const REPL_INTERPRETER_CACHE = REPLInterpreterCache()
436

437
function get_code_cache()
×
438
    # XXX Avoid storing analysis results into the cache that persists across precompilation,
439
    #     as [sys|pkg]image currently doesn't support serializing externally created `CodeInstance`.
440
    #     Otherwise, `CodeInstance`s created by `REPLInterpreter`, that are much less optimized
441
    #     that those produced by `NativeInterpreter`, will leak into the native code cache,
442
    #     potentially causing runtime slowdown.
443
    #     (see https://github.com/JuliaLang/julia/issues/48453).
444
    if Base.generating_output()
435✔
445
        return REPLInterpreterCache()
×
446
    else
447
        return REPL_INTERPRETER_CACHE
435✔
448
    end
449
end
450

451
struct REPLInterpreter <: CC.AbstractInterpreter
452
    repl_frame::CC.InferenceResult
453
    world::UInt
454
    inf_params::CC.InferenceParams
455
    opt_params::CC.OptimizationParams
456
    inf_cache::Vector{CC.InferenceResult}
457
    code_cache::REPLInterpreterCache
458
    function REPLInterpreter(repl_frame::CC.InferenceResult;
870✔
459
                             world::UInt = Base.get_world_counter(),
460
                             inf_params::CC.InferenceParams = CC.InferenceParams(),
461
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
462
                             inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[],
463
                             code_cache::REPLInterpreterCache = get_code_cache())
464
        return new(repl_frame, world, inf_params, opt_params, inf_cache, code_cache)
435✔
465
    end
466
end
467
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
89,005✔
468
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
×
469
CC.get_world_counter(interp::REPLInterpreter) = interp.world
25,933✔
470
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
9,230✔
471
CC.code_cache(interp::REPLInterpreter) = CC.WorldView(interp.code_cache, CC.WorldRange(interp.world))
22,361✔
472
CC.get(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default)
29,273✔
473
CC.getindex(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi)
×
474
CC.haskey(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi)
3,072✔
475
CC.setindex!(wvc::CC.WorldView{REPLInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi)
3,072✔
476

477
# REPLInterpreter is only used for type analysis, so it should disable optimization entirely
478
CC.may_optimize(::REPLInterpreter) = false
×
479

480
# REPLInterpreter analyzes a top-level frame, so better to not bail out from it
481
CC.bail_out_toplevel_call(::REPLInterpreter, ::CC.InferenceLoopState, ::CC.InferenceState) = false
×
482

483
# `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions
484
# for lines like `Mod.a.|` (where `|` is the cursor position).
485
# Aggressive binding resolution poses challenges for the inference cache validation
486
# (until https://github.com/JuliaLang/julia/issues/40399 is implemented).
487
# To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding
488
# resolution for top-level frame representing REPL input code (`repl_frame`) and for child
489
# `getproperty` frames that are constant propagated from the `repl_frame`. This works, since
490
# a.) these frames are never cached, and
491
# b.) their results are only observed by the non-cached `repl_frame`.
492
#
493
# `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within
494
# `repl_frame` to provide reasonable completions for lines like `Ref(Some(42))[].|`.
495
# Aggressive concrete evaluation allows us to get accurate type information about complex
496
# expressions that otherwise can not be constant folded, in a safe way, i.e. it still
497
# doesn't evaluate effectful expressions like `pop!(xs)`.
498
# Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't
499
# present any cache validation issues because `repl_frame` is never cached.
500

501
is_repl_frame(interp::REPLInterpreter, sv::CC.InferenceState) = interp.repl_frame === sv.result
41,570✔
502

503
# aggressive global binding resolution within `repl_frame`
504
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef,
×
505
                                    sv::CC.InferenceState)
506
    if is_repl_frame(interp, sv)
26,572✔
507
        if CC.isdefined_globalref(g)
578✔
508
            return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
573✔
509
        end
510
        return Union{}
5✔
511
    end
512
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef,
25,994✔
513
                                              sv::CC.InferenceState)
514
end
515

516
function is_repl_frame_getproperty(interp::REPLInterpreter, sv::CC.InferenceState)
66✔
517
    def = sv.linfo.def
66✔
518
    def isa Method || return false
66✔
519
    def.name === :getproperty || return false
66✔
520
    sv.cached && return false
66✔
521
    return is_repl_frame(interp, sv.parent)
65✔
522
end
523

524
# aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame`
525
function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f),
8,136✔
526
                              argtypes::Vector{Any}, sv::CC.InferenceState)
527
    if f === Core.getglobal && is_repl_frame_getproperty(interp, sv)
8,136✔
528
        if length(argtypes) == 2
43✔
529
            a1, a2 = argtypes
43✔
530
            if isa(a1, Const) && isa(a2, Const)
43✔
531
                a1val, a2val = a1.val, a2.val
43✔
532
                if isa(a1val, Module) && isa(a2val, Symbol)
43✔
533
                    g = GlobalRef(a1val, a2val)
43✔
534
                    if CC.isdefined_globalref(g)
43✔
535
                        return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
43✔
536
                    end
537
                    return Union{}
×
538
                end
539
            end
540
        end
541
    end
542
    return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any,
8,093✔
543
                                        argtypes::Vector{Any}, sv::CC.InferenceState)
544
end
545

546
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
547
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
×
548
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
549
                                   sv::CC.InferenceState)
550
    if is_repl_frame(interp, sv)
14,933✔
551
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
153✔
552
        result = CC.MethodCallResult(result.rt, result.edgecycle, result.edgelimited,
306✔
553
                                     result.edge, neweffects)
554
    end
555
    return @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
14,933✔
556
                                             result::CC.MethodCallResult, arginfo::CC.ArgInfo,
557
                                             sv::CC.InferenceState)
558
end
559

560
function resolve_toplevel_symbols!(mod::Module, src::Core.CodeInfo)
×
561
    newsrc = copy(src)
435✔
562
    @ccall jl_resolve_globals_in_ir(
435✔
563
        #=jl_array_t *stmts=# newsrc.code::Any,
564
        #=jl_module_t *m=# mod::Any,
565
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
566
        #=int binding_effects=# 0::Int)::Cvoid
567
    return newsrc
×
568
end
569

570
# lower `ex` and run type inference on the resulting top-level expression
571
function repl_eval_ex(@nospecialize(ex), context_module::Module)
1,174✔
572
    lwr = try
1,174✔
573
        Meta.lower(context_module, ex)
1,187✔
574
    catch # macro expansion failed, etc.
575
        return nothing
13✔
576
    end
577
    if lwr isa Symbol
1,161✔
578
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
626✔
579
    end
580
    lwr isa Expr || return Const(lwr) # `ex` is literal
626✔
581
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
453✔
582
    src = lwr.args[1]::Core.CodeInfo
435✔
583

584
    # construct top-level `MethodInstance`
585
    mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ());
435✔
586
    mi.specTypes = Tuple{}
435✔
587

588
    mi.def = context_module
435✔
589
    src = resolve_toplevel_symbols!(context_module, src)
435✔
590
    @atomic mi.uninferred = src
435✔
591

592
    result = CC.InferenceResult(mi)
435✔
593
    interp = REPLInterpreter(result)
870✔
594
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)::CC.InferenceState
435✔
595

596
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
597
    #      potential invalidations of `Core.Compiler` methods.
598
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
435✔
599

600
    result = frame.result.result
435✔
601
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
435✔
602
    return result
428✔
603
end
604

605
# `COMPLETION_WORLD[]` will be initialized within `__init__`
606
# (to allow us to potentially remove REPL from the sysimage in the future).
607
# Note that inference from the `code_typed` call below will use the current world age
608
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
609
# when the given world age is higher than the current one.
610
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
611

612
# Generate code cache for `REPLInterpreter` now:
613
# This code cache will be available at the world of `COMPLETION_WORLD`,
614
# assuming no invalidation will happen before initializing REPL.
615
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
616
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
617

618
# Method completion on function call expression that look like :(max(1))
619
MAX_METHOD_COMPLETIONS::Int = 40
620
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
309✔
621
    funct = repl_eval_ex(ex_org.args[1], context_module)
309✔
622
    funct === nothing && return 2, nothing, [], Set{Symbol}()
309✔
623
    funct = CC.widenconst(funct)
305✔
624
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
604✔
625
    return kwargs_flag, funct, args_ex, kwargs_ex
305✔
626
end
627

628
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false)
×
629
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
138✔
630
    out = Completion[]
138✔
631
    kwargs_flag == 2 && return out # one of the kwargs is invalid
138✔
632
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
126✔
633
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
183✔
634
    return out
126✔
635
end
636

637
MAX_ANY_METHOD_COMPLETIONS::Int = 10
638
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
13✔
639
    out = Completion[]
13✔
640
    args_ex, kwargs_ex, kwargs_flag = try
13✔
641
        # this may throw, since we set default_any to false
642
        complete_methods_args(ex_org, context_module, false, false)
13✔
643
    catch ex
644
        ex isa ArgumentError || rethrow()
×
645
        return out
13✔
646
    end
647
    kwargs_flag == 2 && return out # one of the kwargs is invalid
13✔
648

649
    # moreargs determines whether to accept more args, independently of the presence of a
650
    # semicolon for the ".?(" syntax
651
    moreargs && push!(args_ex, Vararg{Any})
13✔
652

653
    seen = Base.IdSet()
13✔
654
    for name in names(callee_module; all=true)
13✔
655
        if !Base.isdeprecated(callee_module, name) && isdefined(callee_module, name) && !startswith(string(name), '#')
1,313✔
656
            func = getfield(callee_module, name)
637✔
657
            if !isa(func, Module)
637✔
658
                funct = Core.Typeof(func)
1,157✔
659
                if !in(funct, seen)
624✔
660
                    push!(seen, funct)
598✔
661
                    complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
598✔
662
                end
663
            elseif callee_module === Main && isa(func, Module)
26✔
664
                callee_module2 = func
×
665
                for name in names(callee_module2)
×
666
                    if !Base.isdeprecated(callee_module2, name) && isdefined(callee_module2, name) && !startswith(string(name), '#')
×
667
                        func = getfield(callee_module, name)
×
668
                        if !isa(func, Module)
×
669
                            funct = Core.Typeof(func)
×
670
                            if !in(funct, seen)
×
671
                                push!(seen, funct)
×
672
                                complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
×
673
                            end
674
                        end
675
                    end
676
                end
×
677
            end
678
        end
679
    end
1,326✔
680

681
    if !shift
13✔
682
        # Filter out methods where all arguments are `Any`
683
        filter!(out) do c
2✔
684
            isa(c, TextCompletion) && return false
11✔
685
            isa(c, MethodCompletion) || return true
11✔
686
            sig = Base.unwrap_unionall(c.method.sig)::DataType
11✔
687
            return !all(T -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
19✔
688
        end
689
    end
690

691
    return out
13✔
692
end
693

694
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
×
695
    n = isexpr(x, :kw) ? x.args[1] : x
57✔
696
    if n isa Symbol
55✔
697
        push!(kwargs_ex, n)
41✔
698
        return kwargs_flag
41✔
699
    end
700
    possible_splat && isexpr(x, :...) && return kwargs_flag
14✔
701
    return 2 # The kwarg is invalid
10✔
702
end
703

704
function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, default_any::Bool, broadcasting::Bool)
318✔
705
    args_ex = Any[]
318✔
706
    kwargs_ex = Symbol[]
318✔
707
    kwargs_flag = 0
×
708
    # kwargs_flag is:
709
    # * 0 if there is no semicolon and no invalid kwarg
710
    # * 1 if there is a semicolon and no invalid kwarg
711
    # * 2 if there are two semicolons or more, or if some kwarg is invalid, which
712
    #        means that it is not of the form "bar=foo", "bar" or "bar..."
713
    for i in (1+!broadcasting):length(funargs)
482✔
714
        ex = funargs[i]
295✔
715
        if isexpr(ex, :parameters)
460✔
716
            kwargs_flag = ifelse(kwargs_flag == 0, 1, 2) # there should be at most one :parameters
59✔
717
            for x in ex.args
89✔
718
                kwargs_flag = detect_invalid_kwarg!(kwargs_ex, x, kwargs_flag, true)
46✔
719
            end
62✔
720
        elseif isexpr(ex, :kw)
401✔
721
            kwargs_flag = detect_invalid_kwarg!(kwargs_ex, ex, kwargs_flag, false)
22✔
722
        else
723
            if broadcasting
214✔
724
                # handle broadcasting, but only handle number of arguments instead of
725
                # argument types
726
                push!(args_ex, Any)
5✔
727
            else
728
                argt = repl_eval_ex(ex, context_module)
209✔
729
                if argt !== nothing
209✔
730
                    push!(args_ex, CC.widenconst(argt))
183✔
731
                elseif default_any
26✔
732
                    push!(args_ex, Any)
26✔
733
                else
734
                    throw(ArgumentError("argument not found"))
×
735
                end
736
            end
737
        end
738
    end
426✔
739
    return args_ex, Set{Symbol}(kwargs_ex), kwargs_flag
318✔
740
end
741

742
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
1,775✔
743

744
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
×
745
    if allow_broadcasting && is_broadcasting_expr(ex)
305✔
746
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
6✔
747
    end
748
    return detect_args_kwargs(ex.args, context_module, default_any, false)
312✔
749
end
750

751
function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool)
893✔
752
    # Input types and number of arguments
753
    t_in = Tuple{funct, args_ex...}
893✔
754
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
893✔
755
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
756
    if !isa(m, Vector)
893✔
757
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
4✔
758
        return
4✔
759
    end
760
    for match in m
1,353✔
761
        # TODO: if kwargs_ex, filter out methods without kwargs?
762
        push!(out, MethodCompletion(match.spec_types, match.method))
2,215✔
763
    end
2,215✔
764
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
765
end
766

767
include("latex_symbols.jl")
768
include("emoji_symbols.jl")
769

770
const non_identifier_chars = [" \t\n\r\"\\'`\$><=:;|&{}()[],+-*/?%^~"...]
771
const whitespace_chars = [" \t\n\r"...]
772
# "\"'`"... is added to whitespace_chars as non of the bslash_completions
773
# characters contain any of these characters. It prohibits the
774
# bslash_completions function to try and complete on escaped characters in strings
775
const bslash_separators = [whitespace_chars..., "\"'`"...]
776

777
const subscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\_") && length(k)==3)
778
const subscript_regex = Regex("^\\\\_[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(subscripts)) * "]+\\z")
779
const superscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\^") && length(k)==3)
780
const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(superscripts)) * "]+\\z")
781

782
# Aux function to detect whether we're right after a
783
# using or import keyword
784
function afterusing(string::String, startpos::Int)
1,466✔
785
    (isempty(string) || startpos == 0) && return false
1,466✔
786
    str = string[1:prevind(string,startpos)]
2,616✔
787
    isempty(str) && return false
1,463✔
788
    rstr = reverse(str)
1,153✔
789
    r = findfirst(r"\s(gnisu|tropmi)\b", rstr)
1,153✔
790
    r === nothing && return false
1,153✔
791
    fr = reverseind(str, last(r))
30✔
792
    return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end])
30✔
793
end
794

795
function close_path_completion(str, startpos, r, paths, pos)
135✔
796
    length(paths) == 1 || return false  # Only close if there's a single choice...
254✔
797
    _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path
25✔
798
    path = expanduser(unescape_string(replace(_path, "\\\$"=>"\$", "\\\""=>"\"")))
16✔
799
    # ...except if it's a directory...
800
    try
16✔
801
        isdir(path)
17✔
802
    catch e
803
        e isa Base.IOError || rethrow() # `path` cannot be determined to be a file
17✔
804
    end && return false
805
    # ...and except if there's already a " at the cursor.
806
    return lastindex(str) <= pos || str[nextind(str, pos)] != '"'
6✔
807
end
808

809
function bslash_completions(string::String, pos::Int)
1,881✔
810
    slashpos = something(findprev(isequal('\\'), string, pos), 0)
1,931✔
811
    if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos &&
1,909✔
812
        !(1 < slashpos && (string[prevind(string, slashpos)]=='\\')))
813
        # latex / emoji symbol substitution
814
        s = string[slashpos:pos]
96✔
815
        latex = get(latex_symbols, s, "")
69✔
816
        if !isempty(latex) # complete an exact match
48✔
817
            return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true))
21✔
818
        elseif occursin(subscript_regex, s)
27✔
819
            sub = map(c -> subscripts[c], s[3:end])
8✔
820
            return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true))
1✔
821
        elseif occursin(superscript_regex, s)
26✔
822
            sup = map(c -> superscripts[c], s[3:end])
8✔
823
            return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true))
1✔
824
        end
825
        emoji = get(emoji_symbols, s, "")
27✔
826
        if !isempty(emoji)
25✔
827
            return (true, (Completion[BslashCompletion(emoji)], slashpos:pos, true))
2✔
828
        end
829
        # return possible matches; these cannot be mixed with regular
830
        # Julian completions as only latex / emoji symbols contain the leading \
831
        if startswith(s, "\\:") # emoji
44✔
832
            namelist = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols))
2,371✔
833
        else # latex
834
            namelist = Iterators.filter(k -> startswith(k, s), keys(latex_symbols))
104,984✔
835
        end
836
        return (true, (Completion[BslashCompletion(name) for name in sort!(collect(namelist))], slashpos:pos, true))
23✔
837
    end
838
    return (false, (Completion[], 0:-1, false))
1,833✔
839
end
840

841
function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Main)
2,016✔
842
    if tag === :string
2,016✔
843
        str_close = str*"\""
158✔
844
    elseif tag === :cmd
1,857✔
845
        str_close = str*"`"
5✔
846
    else
847
        str_close = str
×
848
    end
849

850
    frange, end_of_identifier = find_start_brace(str_close, c_start='[', c_end=']')
2,015✔
851
    isempty(frange) && return (nothing, nothing, nothing)
2,015✔
852
    obj = context_module
×
853
    for name in split(str[frange[1]:end_of_identifier], '.')
104✔
854
        Base.isidentifier(name) || return (nothing, nothing, nothing)
163✔
855
        sym = Symbol(name)
121✔
856
        isdefined(obj, sym) || return (nothing, nothing, nothing)
121✔
857
        obj = getfield(obj, sym)
121✔
858
    end
204✔
859
    (isa(obj, AbstractDict) && length(obj)::Int < 1_000_000) || return (nothing, nothing, nothing)
84✔
860
    begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [
158✔
861
                             lastindex(str)+1)
862
    return (obj::AbstractDict, str[begin_of_key:end], begin_of_key)
82✔
863
end
864

865
# This needs to be a separate non-inlined function, see #19441
866
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
81✔
867
    matches = String[]
81✔
868
    for key in keys(identifier)
124✔
869
        rkey = repr(key)
940✔
870
        startswith(rkey,partial_key) && push!(matches,rkey)
956✔
871
    end
1,462✔
872
    return matches
81✔
873
end
874

875
# Identify an argument being completed in a method call. If the argument is empty, method
876
# suggestions will be provided instead of argument completions.
877
function identify_possible_method_completion(partial, last_idx)
2,595✔
878
    fail = 0:-1, Expr(:nothing), 0:-1, 0
2,595✔
879

880
    # First, check that the last punctuation is either ',', ';' or '('
881
    idx_last_punct = something(findprev(x -> ispunct(x) && x != '_' && x != '!', partial, last_idx), 0)::Int
18,485✔
882
    idx_last_punct == 0 && return fail
2,595✔
883
    last_punct = partial[idx_last_punct]
4,206✔
884
    last_punct == ',' || last_punct == ';' || last_punct == '(' || return fail
3,996✔
885

886
    # Then, check that `last_punct` is only followed by an identifier or nothing
887
    before_last_word_start = something(findprev(in(non_identifier_chars), partial, last_idx), 0)
1,414✔
888
    before_last_word_start == 0 && return fail
707✔
889
    all(isspace, @view partial[nextind(partial, idx_last_punct):before_last_word_start]) || return fail
787✔
890

891
    # Check that `last_punct` is either the last '(' or placed after a previous '('
892
    frange, method_name_end = find_start_brace(@view partial[1:idx_last_punct])
627✔
893
    method_name_end ∈ frange || return fail
772✔
894

895
    # Strip the preceding ! operators, if any, and close the expression with a ')'
896
    s = replace(partial[frange], r"\G\!+([^=\(]+)" => s"\1"; count=1) * ')'
964✔
897
    ex = Meta.parse(s, raise=false, depwarn=false)
482✔
898
    isa(ex, Expr) || return fail
482✔
899

900
    # `wordrange` is the position of the last argument to complete
901
    wordrange = nextind(partial, before_last_word_start):last_idx
620✔
902
    return frange, ex, wordrange, method_name_end
482✔
903
end
904

905
# Provide completion for keyword arguments in function calls
906
function complete_keyword_argument(partial, last_idx, context_module)
1,635✔
907
    frange, ex, wordrange, = identify_possible_method_completion(partial, last_idx)
1,635✔
908
    fail = Completion[], 0:-1, frange
1,635✔
909
    ex.head === :call || is_broadcasting_expr(ex) || return fail
3,102✔
910

911
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex, context_module, true)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
171✔
912
    kwargs_flag == 2 && return fail # one of the previous kwargs is invalid
171✔
913

914
    methods = Completion[]
169✔
915
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, kwargs_flag == 1)
169✔
916
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
917
    # method calls compatible with the current arguments.
918

919
    # For each method corresponding to the function call, provide completion suggestions
920
    # for each keyword that starts like the last word and that is not already used
921
    # previously in the expression. The corresponding suggestion is "kwname=".
922
    # If the keyword corresponds to an existing name, also include "kwname" as a suggestion
923
    # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)".
924
    last_word = partial[wordrange] # the word to complete
338✔
925
    kwargs = Set{String}()
169✔
926
    for m in methods
169✔
927
        m::MethodCompletion
1,429✔
928
        possible_kwargs = Base.kwarg_decl(m.method)
1,429✔
929
        current_kwarg_candidates = String[]
1,429✔
930
        for _kw in possible_kwargs
2,600✔
931
            kw = String(_kw)
472✔
932
            if !endswith(kw, "...") && startswith(kw, last_word) && _kw ∉ kwargs_ex
797✔
933
                push!(current_kwarg_candidates, kw)
67✔
934
            end
935
        end
730✔
936
        union!(kwargs, current_kwarg_candidates)
1,429✔
937
    end
1,598✔
938

939
    suggestions = Completion[KeywordArgumentCompletion(kwarg) for kwarg in kwargs]
216✔
940
    append!(suggestions, complete_symbol(nothing, last_word, Returns(true), context_module))
169✔
941
    append!(suggestions, complete_keyval(last_word))
169✔
942

943
    return sort!(suggestions, by=completion_text), wordrange
169✔
944
end
945

946
function project_deps_get_completion_candidates(pkgstarts::String, project_file::String)
1✔
947
    loading_candidates = String[]
1✔
948
    d = Base.parsed_toml(project_file)
1✔
949
    pkg = get(d, "name", nothing)::Union{String, Nothing}
2✔
950
    if pkg !== nothing && startswith(pkg, pkgstarts)
2✔
951
        push!(loading_candidates, pkg)
1✔
952
    end
953
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
2✔
954
    if deps !== nothing
1✔
955
        for (pkg, _) in deps
2✔
956
            startswith(pkg, pkgstarts) && push!(loading_candidates, pkg)
2✔
957
        end
1✔
958
    end
959
    return Completion[PackageCompletion(name) for name in loading_candidates]
1✔
960
end
961

962
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)
1,469✔
963
    ex = nothing
3✔
964
    if comp_keywords
1,469✔
965
        append!(suggestions, complete_keyword(name))
542✔
966
        append!(suggestions, complete_keyval(name))
542✔
967
    end
968
    if dotpos > 1 && string[dotpos] == '.'
2,575✔
969
        s = string[1:dotpos-1]
1,312✔
970
        # First see if the whole string up to `pos` is a valid expression. If so, use it.
971
        ex = Meta.parse(s, raise=false, depwarn=false)
656✔
972
        if isexpr(ex, :incomplete)
701✔
973
            s = string[startpos:pos]
1,090✔
974
            # Heuristic to find the start of the expression. TODO: This would be better
975
            # done with a proper error-recovering parser.
976
            if 0 < startpos <= lastindex(string) && string[startpos] == '.'
1,090✔
977
                i = prevind(string, startpos)
1✔
978
                while 0 < i
1✔
979
                    c = string[i]
2✔
980
                    if c in (')', ']')
4✔
981
                        if c == ')'
×
982
                            c_start = '('
×
983
                            c_end = ')'
×
984
                        elseif c == ']'
×
985
                            c_start = '['
×
986
                            c_end = ']'
×
987
                        end
988
                        frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end)
×
989
                        isempty(frange) && break # unbalanced parens
×
990
                        startpos = first(frange)
×
991
                        i = prevind(string, startpos)
×
992
                    elseif c in ('\'', '\"', '\`')
6✔
993
                        s = "$c$c"*string[startpos:pos]
×
994
                        break
×
995
                    else
996
                        break
×
997
                    end
998
                    s = string[startpos:pos]
×
999
                end
×
1000
            end
1001
            if something(findlast(in(non_identifier_chars), s), 0) < something(findlast(isequal('.'), s), 0)
1,090✔
1002
                lookup_name, name = rsplit(s, ".", limit=2)
545✔
1003
                name = String(name)
545✔
1004

1005
                ex = Meta.parse(lookup_name, raise=false, depwarn=false)
545✔
1006
            end
1007
            isexpr(ex, :incomplete) && (ex = nothing)
873✔
1008
        end
1009
    end
1010
    append!(suggestions, complete_symbol(ex, name, ffunc, context_module))
1,469✔
1011
    return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true
1,469✔
1012
end
1013

1014
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true)
2,365✔
1015
    # First parse everything up to the current position
1016
    partial = string[1:pos]
4,391✔
1017
    inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false))
2,027✔
1018

1019
    # ?(x, y)TAB lists methods you can call with these objects
1020
    # ?(x, y TAB lists methods that take these objects as the first two arguments
1021
    # MyModule.?(x, y)TAB restricts the search to names in MyModule
1022
    rexm = match(r"(\w+\.|)\?\((.*)$", partial)
2,027✔
1023
    if rexm !== nothing
2,027✔
1024
        # Get the module scope
1025
        if isempty(rexm.captures[1])
26✔
1026
            callee_module = context_module
×
1027
        else
1028
            modname = Symbol(rexm.captures[1][1:end-1])
13✔
1029
            if isdefined(context_module, modname)
13✔
1030
                callee_module = getfield(context_module, modname)
13✔
1031
                if !isa(callee_module, Module)
13✔
1032
                    callee_module = context_module
13✔
1033
                end
1034
            else
1035
                callee_module = context_module
×
1036
            end
1037
        end
1038
        moreargs = !endswith(rexm.captures[2], ')')
14✔
1039
        callstr = "_(" * rexm.captures[2]
13✔
1040
        if moreargs
13✔
1041
            callstr *= ')'
7✔
1042
        end
1043
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
13✔
1044
        if isa(ex_org, Expr)
13✔
1045
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false
13✔
1046
        end
1047
    end
1048

1049
    # if completing a key in a Dict
1050
    identifier, partial_key, loc = dict_identifier_key(partial, inc_tag, context_module)
2,095✔
1051
    if identifier !== nothing
2,014✔
1052
        matches = find_dict_matches(identifier, partial_key)
81✔
1053
        length(matches)==1 && (lastindex(string) <= pos || string[nextind(string,pos)] != ']') && (matches[1]*=']')
85✔
1054
        length(matches)>0 && return Completion[DictCompletion(identifier, match) for match in sort!(matches)], loc::Int:pos, true
81✔
1055
    end
1056

1057
    ffunc = Returns(true)
×
1058
    suggestions = Completion[]
1,949✔
1059

1060
    # Check if this is a var"" string macro that should be completed like
1061
    # an identifier rather than a string.
1062
    # TODO: It would be nice for the parser to give us more information here
1063
    # so that we can lookup the macro by identity rather than pattern matching
1064
    # its invocation.
1065
    varrange = findprev("var\"", string, pos)
1,949✔
1066

1067
    if varrange !== nothing
1,949✔
1068
        ok, ret = bslash_completions(string, pos)
3✔
1069
        ok && return ret
3✔
1070
        startpos = first(varrange) + 4
3✔
1071
        dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0)
5✔
1072
        return complete_identifiers!(Completion[], ffunc, context_module, string,
3✔
1073
            string[startpos:pos], pos, dotpos, startpos)
1074
    elseif inc_tag === :cmd
1,946✔
1075
        m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial))
1✔
1076
        startpos = nextind(partial, reverseind(partial, m.offset))
2✔
1077
        r = startpos:pos
1✔
1078

1079
        # This expansion with "\\ "=>' ' replacement and shell_escape=true
1080
        # assumes the path isn't further quoted within the cmd backticks.
1081
        expanded = complete_expanduser(replace(string[r], r"\\ " => " "), r)
2✔
1082
        expanded[3] && return expanded  # If user expansion available, return it
1✔
1083

1084
        paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos,
2✔
1085
                                          shell_escape=true)
1086

1087
        return sort!(paths, by=p->p.path), r, success
1✔
1088
    elseif inc_tag === :string
1,945✔
1089
        # Find first non-escaped quote
1090
        m = match(r"\"(?!\\)", reverse(partial))
137✔
1091
        startpos = nextind(partial, reverseind(partial, m.offset))
274✔
1092
        r = startpos:pos
159✔
1093

1094
        expanded = complete_expanduser(string[r], r)
252✔
1095
        expanded[3] && return expanded  # If user expansion available, return it
137✔
1096

1097
        path_prefix = try
135✔
1098
            unescape_string(replace(string[r], "\\\$"=>"\$", "\\\""=>"\""))
248✔
1099
        catch
1100
            nothing
135✔
1101
        end
1102
        if !isnothing(path_prefix)
270✔
1103
            paths, r, success = complete_path(path_prefix, pos, string_escape=true)
135✔
1104

1105
            if close_path_completion(string, startpos, r, paths, pos)
135✔
1106
                paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"")
5✔
1107
            end
1108

1109
            # Fallthrough allowed so that Latex symbols can be completed in strings
1110
            success && return sort!(paths, by=p->p.path), r, success
48,595✔
1111
        end
1112
    end
1113

1114
    ok, ret = bslash_completions(string, pos)
1,873✔
1115
    ok && return ret
1,873✔
1116

1117
    # Make sure that only bslash_completions is working on strings
1118
    inc_tag === :string && return Completion[], 0:-1, false
1,830✔
1119
    if inc_tag === :other
1,774✔
1120
        frange, ex, wordrange, method_name_end = identify_possible_method_completion(partial, pos)
960✔
1121
        if last(frange) != -1 && all(isspace, @view partial[wordrange]) # no last argument to complete
960✔
1122
            if ex.head === :call
138✔
1123
                return complete_methods(ex, context_module, shift), first(frange):method_name_end, false
135✔
1124
            elseif is_broadcasting_expr(ex)
3✔
1125
                return complete_methods(ex, context_module, shift), first(frange):(method_name_end - 1), false
3✔
1126
            end
1127
        end
1128
    elseif inc_tag === :comment
814✔
1129
        return Completion[], 0:-1, false
1✔
1130
    end
1131

1132
    # Check whether we can complete a keyword argument in a function call
1133
    kwarg_completion, wordrange = complete_keyword_argument(partial, pos, context_module)
3,101✔
1134
    isempty(wordrange) || return kwarg_completion, wordrange, !isempty(kwarg_completion)
1,804✔
1135

1136
    dotpos = something(findprev(isequal('.'), string, pos), 0)
2,245✔
1137
    startpos = nextind(string, something(findprev(in(non_identifier_chars), string, pos), 0))
2,617✔
1138
    # strip preceding ! operator
1139
    if (m = match(r"\G\!+", partial, startpos)) isa RegexMatch
1,466✔
1140
        startpos += length(m.match)
2✔
1141
    end
1142

1143
    name = string[max(startpos, dotpos+1):pos]
2,565✔
1144
    comp_keywords = !isempty(name) && startpos > dotpos
1,466✔
1145
    if afterusing(string, startpos)
1,466✔
1146
        # We're right after using or import. Let's look only for packages
1147
        # and modules we can reach from here
1148

1149
        # If there's no dot, we're in toplevel, so we should
1150
        # also search for packages
1151
        s = string[startpos:pos]
33✔
1152
        if dotpos <= startpos
18✔
1153
            for dir in Base.load_path()
17✔
1154
                if basename(dir) in Base.project_names && isfile(dir)
93✔
1155
                    append!(suggestions, project_deps_get_completion_candidates(s, dir))
1✔
1156
                end
1157
                isdir(dir) || continue
37✔
1158
                for pname in readdir(dir)
18✔
1159
                    if pname[1] != '.' && pname != "METADATA" &&
1,974✔
1160
                        pname != "REQUIRE" && startswith(pname, s)
1161
                        # Valid file paths are
1162
                        #   <Mod>.jl
1163
                        #   <Mod>/src/<Mod>.jl
1164
                        #   <Mod>.jl/src/<Mod>.jl
1165
                        if isfile(joinpath(dir, pname))
194✔
1166
                            endswith(pname, ".jl") && push!(suggestions,
×
1167
                                                            PackageCompletion(pname[1:prevind(pname, end-2)]))
1168
                        else
1169
                            mod_name = if endswith(pname, ".jl")
194✔
1170
                                pname[1:prevind(pname, end-2)]
×
1171
                            else
1172
                                pname
194✔
1173
                            end
1174
                            if isfile(joinpath(dir, pname, "src",
194✔
1175
                                               "$mod_name.jl"))
1176
                                push!(suggestions, PackageCompletion(mod_name))
193✔
1177
                            end
1178
                        end
1179
                    end
1180
                end
1,023✔
1181
            end
54✔
1182
        end
1183
        ffunc = (mod,x)->(Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getfield(mod, x), Module))
20,249✔
1184
        comp_keywords = false
×
1185
    end
1186

1187
    startpos == 0 && (pos = -1)
1,466✔
1188
    dotpos < startpos && (dotpos = startpos - 1)
1,466✔
1189
    return complete_identifiers!(suggestions, ffunc, context_module, string,
1,466✔
1190
        name, pos, dotpos, startpos, comp_keywords)
1191
end
1192

1193
function shell_completions(string, pos)
452✔
1194
    # First parse everything up to the current position
1195
    scs = string[1:pos]
904✔
1196
    local args, last_parse
×
1197
    try
452✔
1198
        args, last_parse = Base.shell_parse(scs, true)::Tuple{Expr,UnitRange{Int}}
469✔
1199
    catch
1200
        return Completion[], 0:-1, false
17✔
1201
    end
1202
    ex = args.args[end]::Expr
435✔
1203
    # Now look at the last thing we parsed
1204
    isempty(ex.args) && return Completion[], 0:-1, false
435✔
1205
    arg = ex.args[end]
435✔
1206
    if all(s -> isa(s, AbstractString), ex.args)
1,307✔
1207
        arg = arg::AbstractString
432✔
1208
        # Treat this as a path
1209

1210
        # As Base.shell_parse throws away trailing spaces (unless they are escaped),
1211
        # we need to special case here.
1212
        # If the last char was a space, but shell_parse ignored it search on "".
1213
        ignore_last_word = arg != " " && scs[end] == ' '
863✔
1214
        prefix = ignore_last_word ? "" : join(ex.args)
847✔
1215

1216
        # Also try looking into the env path if the user wants to complete the first argument
1217
        use_envpath = !ignore_last_word && length(args.args) < 2
432✔
1218

1219
        return complete_path(prefix, pos, use_envpath=use_envpath, shell_escape=true)
432✔
1220
    elseif isexpr(arg, :incomplete) || isexpr(arg, :error)
5✔
1221
        partial = scs[last_parse]
4✔
1222
        ret, range = completions(partial, lastindex(partial))
2✔
1223
        range = range .+ (first(last_parse) - 1)
2✔
1224
        return ret, range, true
2✔
1225
    end
1226
    return Completion[], 0:-1, false
1✔
1227
end
1228

1229
function UndefVarError_hint(io::IO, ex::UndefVarError)
5✔
1230
    var = ex.var
5✔
1231
    if var === :or
5✔
1232
        print(io, "\nsuggestion: Use `||` for short-circuiting boolean OR.")
×
1233
    elseif var === :and
5✔
1234
        print(io, "\nsuggestion: Use `&&` for short-circuiting boolean AND.")
×
1235
    elseif var === :help
5✔
1236
        println(io)
×
1237
        # Show friendly help message when user types help or help() and help is undefined
1238
        show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
×
1239
    elseif var === :quit
5✔
1240
        print(io, "\nsuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
×
1241
    end
1242
end
1243

1244
function __init__()
466✔
1245
    Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
466✔
1246
    COMPLETION_WORLD[] = Base.get_world_counter()
466✔
1247
    nothing
466✔
1248
end
1249

1250
end # module
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc