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

JuliaLang / julia / #37549

pending completion
#37549

push

local

web-flow
Merge pull request #49679 from JuliaLang/pc/timing-events

71334 of 83561 relevant lines covered (85.37%)

33984623.12 hits per line

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

87.18
/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
1✔
16
end
17

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

22
struct PathCompletion <: Completion
23
    path::String
211✔
24
end
25

26
struct ModuleCompletion <: Completion
27
    parent::Module
6,414✔
28
    mod::String
29
end
30

31
struct PackageCompletion <: Completion
32
    package::String
67✔
33
end
34

35
struct PropertyCompletion <: Completion
36
    value
31✔
37
    property::Symbol
38
end
39

40
struct FieldCompletion <: Completion
41
    typ::DataType
21✔
42
    field::Symbol
43
end
44

45
struct MethodCompletion <: Completion
46
    tt # may be used by an external consumer to infer return type, etc.
47
    method::Method
48
    MethodCompletion(@nospecialize(tt), method::Method) = new(tt, method)
621✔
49
end
50

51
struct BslashCompletion <: Completion
52
    bslash::String
68✔
53
end
54

55
struct ShellCompletion <: Completion
56
    text::String
57
end
58

59
struct DictCompletion <: Completion
60
    dict::AbstractDict
118✔
61
    key::String
62
end
63

64
struct KeywordArgumentCompletion <: Completion
65
    kwarg::String
39✔
66
end
67

68
# interface definition
69
function Base.getproperty(c::Completion, name::Symbol)
130,695✔
70
    if name === :text
130,958✔
71
        return getfield(c, :text)::String
1✔
72
    elseif name === :keyword
130,957✔
73
        return getfield(c, :keyword)::String
92✔
74
    elseif name === :path
130,670✔
75
        return getfield(c, :path)::String
195✔
76
    elseif name === :parent
130,670✔
77
        return getfield(c, :parent)::Module
×
78
    elseif name === :mod
130,670✔
79
        return getfield(c, :mod)::String
128,841✔
80
    elseif name === :package
1,829✔
81
        return getfield(c, :package)::String
848✔
82
    elseif name === :property
981✔
83
        return getfield(c, :property)::Symbol
103✔
84
    elseif name === :field
878✔
85
        return getfield(c, :field)::Symbol
47✔
86
    elseif name === :method
294✔
87
        return getfield(c, :method)::Method
623✔
88
    elseif name === :bslash
226✔
89
        return getfield(c, :bslash)::String
68✔
90
    elseif name === :text
226✔
91
        return getfield(c, :text)::String
×
92
    elseif name === :key
226✔
93
        return getfield(c, :key)::String
118✔
94
    elseif name === :kwarg
108✔
95
        return getfield(c, :kwarg)::String
108✔
96
    end
97
    return getfield(c, name)
×
98
end
99

100
_completion_text(c::TextCompletion) = c.text
1✔
101
_completion_text(c::KeywordCompletion) = c.keyword
92✔
102
_completion_text(c::PathCompletion) = c.path
180✔
103
_completion_text(c::ModuleCompletion) = c.mod
128,841✔
104
_completion_text(c::PackageCompletion) = c.package
848✔
105
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
103✔
106
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
47✔
107
_completion_text(c::MethodCompletion) = repr(c.method)
526✔
108
_completion_text(c::BslashCompletion) = c.bslash
68✔
109
_completion_text(c::ShellCompletion) = c.text
×
110
_completion_text(c::DictCompletion) = c.key
118✔
111
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
108✔
112

113
completion_text(c) = _completion_text(c)::String
130,932✔
114

115
const Completions = Tuple{Vector{Completion}, UnitRange{Int}, Bool}
116

117
function completes_global(x, name)
78,130✔
118
    return startswith(x, name) && !('#' in x)
78,130✔
119
end
120

121
function appendmacro!(syms, macros, needle, endchar)
954✔
122
    for macsym in macros
1,850✔
123
        s = String(macsym)
920✔
124
        if endswith(s, needle)
920✔
125
            from = nextind(s, firstindex(s))
80✔
126
            to = prevind(s, sizeof(s)-sizeof(needle)+1)
80✔
127
            push!(syms, s[from:to]*endchar)
160✔
128
        end
129
    end
920✔
130
end
131

132
function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all::Bool = false, imported::Bool = false)
477✔
133
    ssyms = names(mod, all = all, imported = imported)
878✔
134
    filter!(ffunc, ssyms)
477✔
135
    macros = filter(x -> startswith(String(x), "@" * name), ssyms)
78,607✔
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)]
6,811✔
137
    appendmacro!(syms, macros, "_str", "\"")
477✔
138
    appendmacro!(syms, macros, "_cmd", "`")
477✔
139
    return [ModuleCompletion(mod, sym) for sym in syms]
477✔
140
end
141

142
# REPL Symbol Completions
143
function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), context_module::Module=Main)
131✔
144
    mod = context_module
×
145

146
    lookup_module = true
×
147
    t = Union{}
×
148
    val = nothing
×
149
    if ex !== nothing
131✔
150
        res = repl_eval_ex(ex, context_module)
66✔
151
        res === nothing && return Completion[]
66✔
152
        if res isa Const
48✔
153
            val = res.val
36✔
154
            if isa(val, Module)
36✔
155
                mod = val
11✔
156
                lookup_module = true
11✔
157
            else
158
                lookup_module = false
×
159
                t = typeof(val)
61✔
160
            end
161
        else
162
            lookup_module = false
×
163
            t = CC.widenconst(res)
12✔
164
        end
165
    end
166

167
    suggestions = Completion[]
113✔
168
    if lookup_module
113✔
169
        # We will exclude the results that the user does not want, as well
170
        # as excluding Main.Main.Main, etc., because that's most likely not what
171
        # the user wants
172
        p = let mod=mod, modname=nameof(mod)
76✔
173
            s->(!Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool)
86,490✔
174
        end
175
        # Looking for a binding in a module
176
        if mod == context_module
76✔
177
            # Also look in modules we got through `using`
178
            mods = ccall(:jl_module_usings, Any, (Any,), context_module)::Vector
66✔
179
            for m in mods
66✔
180
                append!(suggestions, filtered_mod_names(p, m::Module, name))
475✔
181
            end
467✔
182
            append!(suggestions, filtered_mod_names(p, mod, name, true, true))
87✔
183
        else
184
            append!(suggestions, filtered_mod_names(p, mod, name, true, false))
86✔
185
        end
186
    elseif val !== nothing # looking for a property of an instance
37✔
187
        for property in propertynames(val, false)
24✔
188
            # TODO: support integer arguments (#36872)
189
            if property isa Symbol && startswith(string(property), name)
66✔
190
                push!(suggestions, PropertyCompletion(val, property))
31✔
191
            end
192
        end
35✔
193
    else
194
        # Looking for a member of a type
195
        if t isa DataType && t != Any
13✔
196
            # Check for cases like Type{typeof(+)}
197
            if Base.isType(t)
12✔
198
                t = typeof(t.parameters[1])
×
199
            end
200
            # Only look for fields if this is a concrete type
201
            if isconcretetype(t)
12✔
202
                fields = fieldnames(t)
12✔
203
                for field in fields
12✔
204
                    isa(field, Symbol) || continue # Tuple type has ::Int field name
25✔
205
                    s = string(field)
25✔
206
                    if startswith(s, name)
50✔
207
                        push!(suggestions, FieldCompletion(t, field))
21✔
208
                    end
209
                end
25✔
210
            end
211
        end
212
    end
213
    suggestions
113✔
214
end
215

216
const sorted_keywords = [
217
    "abstract type", "baremodule", "begin", "break", "catch", "ccall",
218
    "const", "continue", "do", "else", "elseif", "end", "export", "false",
219
    "finally", "for", "function", "global", "if", "import",
220
    "let", "local", "macro", "module", "mutable struct",
221
    "primitive type", "quote", "return", "struct",
222
    "true", "try", "using", "while"]
223

224
function complete_keyword(s::Union{String,SubString{String}})
19✔
225
    r = searchsorted(sorted_keywords, s)
19✔
226
    i = first(r)
19✔
227
    n = length(sorted_keywords)
19✔
228
    while i <= n && startswith(sorted_keywords[i],s)
63✔
229
        r = first(r):i
13✔
230
        i += 1
13✔
231
    end
13✔
232
    Completion[KeywordCompletion(kw) for kw in sorted_keywords[r]]
19✔
233
end
234

235
function complete_path(path::AbstractString, pos::Int; use_envpath=false, shell_escape=false)
82✔
236
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
41✔
237
        # if the path is just "~", don't consider the expanded username as a prefix
238
        if path == "~"
×
239
            dir, prefix = homedir(), ""
×
240
        else
241
            dir, prefix = splitdir(homedir() * path[2:end])
×
242
        end
243
    else
244
        dir, prefix = splitdir(path)
41✔
245
    end
246
    local files
×
247
    try
41✔
248
        if isempty(dir)
41✔
249
            files = readdir()
29✔
250
        elseif isdir(dir)
12✔
251
            files = readdir(dir)
11✔
252
        else
253
            return Completion[], 0:-1, false
41✔
254
        end
255
    catch
256
        return Completion[], 0:-1, false
×
257
    end
258

259
    matches = Set{String}()
40✔
260
    for file in files
40✔
261
        if startswith(file, prefix)
4,866✔
262
            id = try isdir(joinpath(dir, file)) catch; false end
530✔
263
            # joinpath is not used because windows needs to complete with double-backslash
264
            push!(matches, id ? file * (@static Sys.iswindows() ? "\\\\" : "/") : file)
176✔
265
        end
266
    end
2,658✔
267

268
    if use_envpath && length(dir) == 0
40✔
269
        # Look for files in PATH as well
270
        local pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":")
6✔
271

272
        for pathdir in pathdirs
6✔
273
            local actualpath
×
274
            try
60✔
275
                actualpath = realpath(pathdir)
68✔
276
            catch
277
                # Bash doesn't expect every folder in PATH to exist, so neither shall we
278
                continue
8✔
279
            end
280

281
            if actualpath != pathdir && in(actualpath,pathdirs)
64✔
282
                # Remove paths which (after resolving links) are in the env path twice.
283
                # Many distros eg. point /bin to /usr/bin but have both in the env path.
284
                continue
12✔
285
            end
286

287
            local filesinpath
×
288
            try
40✔
289
                filesinpath = readdir(pathdir)
41✔
290
            catch e
291
                # Bash allows dirs in PATH that can't be read, so we should as well.
292
                if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
1✔
293
                    continue
1✔
294
                else
295
                    # We only handle IOError and ArgumentError here
296
                    rethrow()
×
297
                end
298
            end
299

300
            for file in filesinpath
51✔
301
                # In a perfect world, we would filter on whether the file is executable
302
                # here, or even on whether the current user can execute the file in question.
303
                if startswith(file, prefix) && isfile(joinpath(pathdir, file))
10,767✔
304
                    push!(matches, file)
3✔
305
                end
306
            end
7,930✔
307
        end
66✔
308
    end
309

310
    matchList = Completion[PathCompletion(shell_escape ? replace(s, r"\s" => s"\\\0") : s) for s in matches]
218✔
311
    startpos = pos - lastindex(prefix) + 1 - count(isequal(' '), prefix)
40✔
312
    # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`,
313
    # hence we need to add one to get the first index. This is also correct when considering
314
    # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`.
315
    return matchList, startpos:pos, !isempty(matchList)
40✔
316
end
317

318
function complete_expanduser(path::AbstractString, r)
31✔
319
    expanded =
31✔
320
        try expanduser(path)
32✔
321
        catch e
322
            e isa ArgumentError || rethrow()
1✔
323
            path
32✔
324
        end
325
    return Completion[PathCompletion(expanded)], r, path != expanded
31✔
326
end
327

328
# Returns a range that includes the method name in front of the first non
329
# closed start brace from the end of the string.
330
function find_start_brace(s::AbstractString; c_start='(', c_end=')')
968✔
331
    braces = 0
×
332
    r = reverse(s)
484✔
333
    i = firstindex(r)
×
334
    in_single_quotes = false
×
335
    in_double_quotes = false
×
336
    in_back_ticks = false
×
337
    in_comment = 0
×
338
    while i <= ncodeunits(r)
8,215✔
339
        c, i = iterate(r, i)
15,936✔
340
        if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '='
7,968✔
341
            c, i = iterate(r, i) # consume '='
8✔
342
            new_comments = 1
×
343
            # handle #=#=#=#, by counting =# pairs
344
            while i <= ncodeunits(r) && iterate(r, i)[1] == '#'
6✔
345
                c, i = iterate(r, i) # consume '#'
6✔
346
                iterate(r, i)[1] == '=' || break
3✔
347
                c, i = iterate(r, i) # consume '='
4✔
348
                new_comments += 1
2✔
349
            end
2✔
350
            if c == '='
4✔
351
                in_comment += new_comments
3✔
352
            else
353
                in_comment -= new_comments
5✔
354
            end
355
        elseif !in_single_quotes && !in_double_quotes && !in_back_ticks && in_comment == 0
7,964✔
356
            if c == c_start
7,423✔
357
                braces += 1
280✔
358
            elseif c == c_end
7,143✔
359
                braces -= 1
43✔
360
            elseif c == '\''
7,100✔
361
                in_single_quotes = true
15✔
362
            elseif c == '"'
7,085✔
363
                in_double_quotes = true
77✔
364
            elseif c == '`'
7,008✔
365
                in_back_ticks = true
7,423✔
366
            end
367
        else
368
            if in_single_quotes &&
541✔
369
                c == '\'' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
370
                in_single_quotes = false
15✔
371
            elseif in_double_quotes &&
526✔
372
                c == '"' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
373
                in_double_quotes = false
63✔
374
            elseif in_back_ticks &&
463✔
375
                c == '`' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
376
                in_back_ticks = false
7✔
377
            elseif in_comment > 0 &&
456✔
378
                c == '=' && i <= ncodeunits(r) && iterate(r, i)[1] == '#'
379
                # handle =#=#=#=, by counting #= pairs
380
                c, i = iterate(r, i) # consume '#'
6✔
381
                old_comments = 1
×
382
                while i <= ncodeunits(r) && iterate(r, i)[1] == '='
4✔
383
                    c, i = iterate(r, i) # consume '='
4✔
384
                    iterate(r, i)[1] == '#' || break
2✔
385
                    c, i = iterate(r, i) # consume '#'
2✔
386
                    old_comments += 1
1✔
387
                end
1✔
388
                if c == '#'
3✔
389
                    in_comment -= old_comments
2✔
390
                else
391
                    in_comment += old_comments
1✔
392
                end
393
            end
394
        end
395
        braces == 1 && break
7,968✔
396
    end
7,731✔
397
    braces != 1 && return 0:-1, -1
484✔
398
    method_name_end = reverseind(s, i)
237✔
399
    startind = nextind(s, something(findprev(in(non_identifier_chars), s, method_name_end), 0))::Int
248✔
400
    return (startind:lastindex(s), method_name_end)
237✔
401
end
402

403
struct REPLInterpreterCache
404
    dict::IdDict{MethodInstance,CodeInstance}
×
405
end
406
REPLInterpreterCache() = REPLInterpreterCache(IdDict{MethodInstance,CodeInstance}())
×
407
const REPL_INTERPRETER_CACHE = REPLInterpreterCache()
408

409
function get_code_cache()
×
410
    # XXX Avoid storing analysis results into the cache that persists across precompilation,
411
    #     as [sys|pkg]image currently doesn't support serializing externally created `CodeInstance`.
412
    #     Otherwise, `CodeInstance`s created by `REPLInterpreter`, that are much less optimized
413
    #     that those produced by `NativeInterpreter`, will leak into the native code cache,
414
    #     potentially causing runtime slowdown.
415
    #     (see https://github.com/JuliaLang/julia/issues/48453).
416
    if (@ccall jl_generating_output()::Cint) == 1
149✔
417
        return REPLInterpreterCache()
×
418
    else
419
        return REPL_INTERPRETER_CACHE
149✔
420
    end
421
end
422

423
struct REPLInterpreter <: CC.AbstractInterpreter
424
    repl_frame::CC.InferenceResult
425
    world::UInt
426
    inf_params::CC.InferenceParams
427
    opt_params::CC.OptimizationParams
428
    inf_cache::Vector{CC.InferenceResult}
429
    code_cache::REPLInterpreterCache
430
    function REPLInterpreter(repl_frame::CC.InferenceResult;
298✔
431
                             world::UInt = Base.get_world_counter(),
432
                             inf_params::CC.InferenceParams = CC.InferenceParams(),
433
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
434
                             inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[],
435
                             code_cache::REPLInterpreterCache = get_code_cache())
436
        return new(repl_frame, world, inf_params, opt_params, inf_cache, code_cache)
149✔
437
    end
438
end
439
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
60,140✔
440
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
×
441
CC.get_world_counter(interp::REPLInterpreter) = interp.world
18,192✔
442
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
7,283✔
443
CC.code_cache(interp::REPLInterpreter) = CC.WorldView(interp.code_cache, CC.WorldRange(interp.world))
15,031✔
444
CC.get(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default)
19,649✔
445
CC.getindex(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi)
×
446
CC.haskey(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi)
2,070✔
447
CC.setindex!(wvc::CC.WorldView{REPLInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi)
2,070✔
448

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

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

455
# `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions
456
# for lines like `Mod.a.|` (where `|` is the cursor position).
457
# Aggressive binding resolution poses challenges for the inference cache validation
458
# (until https://github.com/JuliaLang/julia/issues/40399 is implemented).
459
# To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding
460
# resolution for top-level frame representing REPL input code (`repl_frame`) and for child
461
# `getproperty` frames that are constant propagated from the `repl_frame`. This works, since
462
# a.) these frames are never cached, and
463
# b.) their results are only observed by the non-cached `repl_frame`.
464
#
465
# `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within
466
# `repl_frame` to provide reasonable completions for lines like `Ref(Some(42))[].|`.
467
# Aggressive concrete evaluation allows us to get accurate type information about complex
468
# expressions that otherwise can not be constant folded, in a safe way, i.e. it still
469
# doesn't evaluate effectful expressions like `pop!(xs)`.
470
# Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't
471
# present any cache validation issues because `repl_frame` is never cached.
472

473
is_repl_frame(interp::REPLInterpreter, sv::CC.InferenceState) = interp.repl_frame === sv.result
27,762✔
474

475
# aggressive global binding resolution within `repl_frame`
476
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef,
×
477
                                    sv::CC.InferenceState)
478
    if is_repl_frame(interp, sv)
17,716✔
479
        if CC.isdefined_globalref(g)
268✔
480
            return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
265✔
481
        end
482
        return Union{}
3✔
483
    end
484
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef,
17,448✔
485
                                              sv::CC.InferenceState)
486
end
487

488
function is_repl_frame_getproperty(interp::REPLInterpreter, sv::CC.InferenceState)
23✔
489
    def = sv.linfo.def
23✔
490
    def isa Method || return false
23✔
491
    def.name === :getproperty || return false
23✔
492
    sv.cached && return false
23✔
493
    return is_repl_frame(interp, sv.parent)
22✔
494
end
495

496
# aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame`
497
function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f),
5,281✔
498
                              argtypes::Vector{Any}, sv::CC.InferenceState)
499
    if f === Core.getglobal && is_repl_frame_getproperty(interp, sv)
5,281✔
500
        if length(argtypes) == 2
3✔
501
            a1, a2 = argtypes
3✔
502
            if isa(a1, Const) && isa(a2, Const)
3✔
503
                a1val, a2val = a1.val, a2.val
3✔
504
                if isa(a1val, Module) && isa(a2val, Symbol)
3✔
505
                    g = GlobalRef(a1val, a2val)
3✔
506
                    if CC.isdefined_globalref(g)
3✔
507
                        return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
3✔
508
                    end
509
                    return Union{}
×
510
                end
511
            end
512
        end
513
    end
514
    return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any,
5,278✔
515
                                        argtypes::Vector{Any}, sv::CC.InferenceState)
516
end
517

518
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
519
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
×
520
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
521
                                   sv::CC.InferenceState)
522
    if is_repl_frame(interp, sv)
10,024✔
523
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
107✔
524
        result = CC.MethodCallResult(result.rt, result.edgecycle, result.edgelimited,
214✔
525
                                     result.edge, neweffects)
526
    end
527
    return @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
10,024✔
528
                                             result::CC.MethodCallResult, arginfo::CC.ArgInfo,
529
                                             sv::CC.InferenceState)
530
end
531

532
function resolve_toplevel_symbols!(mod::Module, src::Core.CodeInfo)
×
533
    newsrc = copy(src)
149✔
534
    @ccall jl_resolve_globals_in_ir(
149✔
535
        #=jl_array_t *stmts=# newsrc.code::Any,
536
        #=jl_module_t *m=# mod::Any,
537
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
538
        #=int binding_effects=# 0::Int)::Cvoid
539
    return newsrc
×
540
end
541

542
# lower `ex` and run type inference on the resulting top-level expression
543
function repl_eval_ex(@nospecialize(ex), context_module::Module)
316✔
544
    lwr = try
316✔
545
        Meta.lower(context_module, ex)
329✔
546
    catch # macro expansion failed, etc.
547
        return nothing
13✔
548
    end
549
    if lwr isa Symbol
303✔
550
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
59✔
551
    end
552
    lwr isa Expr || return Const(lwr) # `ex` is literal
330✔
553
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
167✔
554
    src = lwr.args[1]::Core.CodeInfo
149✔
555

556
    # construct top-level `MethodInstance`
557
    mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ());
149✔
558
    mi.specTypes = Tuple{}
149✔
559

560
    mi.def = context_module
149✔
561
    src = resolve_toplevel_symbols!(context_module, src)
149✔
562
    @atomic mi.uninferred = src
149✔
563

564
    result = CC.InferenceResult(mi)
149✔
565
    interp = REPLInterpreter(result)
298✔
566
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)::CC.InferenceState
149✔
567

568
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
569
    #      potential invalidations of `Core.Compiler` methods.
570
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
149✔
571

572
    result = frame.result.result
149✔
573
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
149✔
574
    return result
144✔
575
end
576

577
# `COMPLETION_WORLD[]` will be initialized within `__init__`
578
# (to allow us to potentially remove REPL from the sysimage in the future).
579
# Note that inference from the `code_typed` call below will use the current world age
580
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
581
# when the given world age is higher than the current one.
582
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
583

584
# Generate code cache for `REPLInterpreter` now:
585
# This code cache will be available at the world of `COMPLETION_WORLD`,
586
# assuming no invalidation will happen before initializing REPL.
587
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
588
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
589

590
# Method completion on function call expression that look like :(max(1))
591
MAX_METHOD_COMPLETIONS::Int = 40
592
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
113✔
593
    funct = repl_eval_ex(ex_org.args[1], context_module)
113✔
594
    funct === nothing && return 2, nothing, [], Set{Symbol}()
113✔
595
    funct = CC.widenconst(funct)
113✔
596
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
220✔
597
    return kwargs_flag, funct, args_ex, kwargs_ex
113✔
598
end
599

600
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false)
×
601
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
80✔
602
    out = Completion[]
80✔
603
    kwargs_flag == 2 && return out # one of the kwargs is invalid
80✔
604
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
70✔
605
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
71✔
606
    return out
70✔
607
end
608

609
MAX_ANY_METHOD_COMPLETIONS::Int = 10
610
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
13✔
611
    out = Completion[]
13✔
612
    args_ex, kwargs_ex, kwargs_flag = try
13✔
613
        # this may throw, since we set default_any to false
614
        complete_methods_args(ex_org, context_module, false, false)
13✔
615
    catch ex
616
        ex isa ArgumentError || rethrow()
×
617
        return out
13✔
618
    end
619
    kwargs_flag == 2 && return out # one of the kwargs is invalid
13✔
620

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

625
    seen = Base.IdSet()
13✔
626
    for name in names(callee_module; all=true)
13✔
627
        if !Base.isdeprecated(callee_module, name) && isdefined(callee_module, name) && !startswith(string(name), '#')
1,313✔
628
            func = getfield(callee_module, name)
637✔
629
            if !isa(func, Module)
637✔
630
                funct = Core.Typeof(func)
1,157✔
631
                if !in(funct, seen)
624✔
632
                    push!(seen, funct)
598✔
633
                    complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
598✔
634
                end
635
            elseif callee_module === Main && isa(func, Module)
26✔
636
                callee_module2 = func
×
637
                for name in names(callee_module2)
×
638
                    if !Base.isdeprecated(callee_module2, name) && isdefined(callee_module2, name) && !startswith(string(name), '#')
×
639
                        func = getfield(callee_module, name)
×
640
                        if !isa(func, Module)
×
641
                            funct = Core.Typeof(func)
×
642
                            if !in(funct, seen)
×
643
                                push!(seen, funct)
×
644
                                complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
×
645
                            end
646
                        end
647
                    end
648
                end
×
649
            end
650
        end
651
    end
1,326✔
652

653
    if !shift
13✔
654
        # Filter out methods where all arguments are `Any`
655
        filter!(out) do c
2✔
656
            isa(c, TextCompletion) && return false
11✔
657
            isa(c, MethodCompletion) || return true
11✔
658
            sig = Base.unwrap_unionall(c.method.sig)::DataType
11✔
659
            return !all(T -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
19✔
660
        end
661
    end
662

663
    return out
13✔
664
end
665

666
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
×
667
    n = isexpr(x, :kw) ? x.args[1] : x
57✔
668
    if n isa Symbol
55✔
669
        push!(kwargs_ex, n)
41✔
670
        return kwargs_flag
41✔
671
    end
672
    possible_splat && isexpr(x, :...) && return kwargs_flag
14✔
673
    return 2 # The kwarg is invalid
10✔
674
end
675

676
function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, default_any::Bool, broadcasting::Bool)
126✔
677
    args_ex = Any[]
126✔
678
    kwargs_ex = Symbol[]
126✔
679
    kwargs_flag = 0
×
680
    # kwargs_flag is:
681
    # * 0 if there is no semicolon and no invalid kwarg
682
    # * 1 if there is a semicolon and no invalid kwarg
683
    # * 2 if there are two semicolons or more, or if some kwarg is invalid, which
684
    #        means that it is not of the form "bar=foo", "bar" or "bar..."
685
    for i in (1+!broadcasting):length(funargs)
236✔
686
        ex = funargs[i]
218✔
687
        if isexpr(ex, :parameters)
332✔
688
            kwargs_flag = ifelse(kwargs_flag == 0, 1, 2) # there should be at most one :parameters
54✔
689
            for x in ex.args
79✔
690
                kwargs_flag = detect_invalid_kwarg!(kwargs_ex, x, kwargs_flag, true)
46✔
691
            end
62✔
692
        elseif isexpr(ex, :kw)
278✔
693
            kwargs_flag = detect_invalid_kwarg!(kwargs_ex, ex, kwargs_flag, false)
22✔
694
        else
695
            if broadcasting
142✔
696
                # handle broadcasting, but only handle number of arguments instead of
697
                # argument types
698
                push!(args_ex, Any)
5✔
699
            else
700
                argt = repl_eval_ex(ex, context_module)
137✔
701
                if argt !== nothing
137✔
702
                    push!(args_ex, CC.widenconst(argt))
103✔
703
                elseif default_any
34✔
704
                    push!(args_ex, Any)
34✔
705
                else
706
                    throw(ArgumentError("argument not found"))
×
707
                end
708
            end
709
        end
710
    end
326✔
711
    return args_ex, Set{Symbol}(kwargs_ex), kwargs_flag
126✔
712
end
713

714
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
214✔
715

716
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
×
717
    if allow_broadcasting && is_broadcasting_expr(ex)
113✔
718
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
6✔
719
    end
720
    return detect_args_kwargs(ex.args, context_module, default_any, false)
120✔
721
end
722

723
function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool)
701✔
724
    # Input types and number of arguments
725
    t_in = Tuple{funct, args_ex...}
701✔
726
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
701✔
727
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
728
    if !isa(m, Vector)
701✔
729
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
1✔
730
        return
1✔
731
    end
732
    for match in m
1,161✔
733
        # TODO: if kwargs_ex, filter out methods without kwargs?
734
        push!(out, MethodCompletion(match.spec_types, match.method))
621✔
735
    end
621✔
736
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
737
end
738

739
include("latex_symbols.jl")
740
include("emoji_symbols.jl")
741

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

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

754
# Aux function to detect whether we're right after a
755
# using or import keyword
756
function afterusing(string::String, startpos::Int)
95✔
757
    (isempty(string) || startpos == 0) && return false
95✔
758
    str = string[1:prevind(string,startpos)]
144✔
759
    isempty(str) && return false
92✔
760
    rstr = reverse(str)
52✔
761
    r = findfirst(r"\s(gnisu|tropmi)\b", rstr)
52✔
762
    r === nothing && return false
52✔
763
    fr = reverseind(str, last(r))
9✔
764
    return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end])
9✔
765
end
766

767
function close_path_completion(str, startpos, r, paths, pos)
28✔
768
    length(paths) == 1 || return false  # Only close if there's a single choice...
43✔
769
    _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path
19✔
770
    path = expanduser(replace(_path, r"\\ " => " "))
13✔
771
    # ...except if it's a directory...
772
    try
13✔
773
        isdir(path)
14✔
774
    catch e
775
        e isa Base.IOError || rethrow() # `path` cannot be determined to be a file
14✔
776
    end && return false
777
    # ...and except if there's already a " at the cursor.
778
    return lastindex(str) <= pos || str[nextind(str, pos)] != '"'
3✔
779
end
780

781
function bslash_completions(string::String, pos::Int)
253✔
782
    slashpos = something(findprev(isequal('\\'), string, pos), 0)
291✔
783
    if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos &&
275✔
784
        !(1 < slashpos && (string[prevind(string, slashpos)]=='\\')))
785
        # latex / emoji symbol substitution
786
        s = string[slashpos:pos]
72✔
787
        latex = get(latex_symbols, s, "")
55✔
788
        if !isempty(latex) # complete an exact match
36✔
789
            return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true))
19✔
790
        elseif occursin(subscript_regex, s)
17✔
791
            sub = map(c -> subscripts[c], s[3:end])
8✔
792
            return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true))
1✔
793
        elseif occursin(superscript_regex, s)
16✔
794
            sup = map(c -> superscripts[c], s[3:end])
8✔
795
            return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true))
1✔
796
        end
797
        emoji = get(emoji_symbols, s, "")
17✔
798
        if !isempty(emoji)
15✔
799
            return (true, (Completion[BslashCompletion(emoji)], slashpos:pos, true))
2✔
800
        end
801
        # return possible matches; these cannot be mixed with regular
802
        # Julian completions as only latex / emoji symbols contain the leading \
803
        if startswith(s, "\\:") # emoji
26✔
804
            namelist = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols))
2,371✔
805
        else # latex
806
            namelist = Iterators.filter(k -> startswith(k, s), keys(latex_symbols))
55,930✔
807
        end
808
        return (true, (Completion[BslashCompletion(name) for name in sort!(collect(namelist))], slashpos:pos, true))
13✔
809
    end
810
    return (false, (Completion[], 0:-1, false))
217✔
811
end
812

813
function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Main)
327✔
814
    if tag === :string
327✔
815
        str_close = str*"\""
54✔
816
    elseif tag === :cmd
272✔
817
        str_close = str*"`"
5✔
818
    else
819
        str_close = str
×
820
    end
821

822
    frange, end_of_identifier = find_start_brace(str_close, c_start='[', c_end=']')
326✔
823
    isempty(frange) && return (nothing, nothing, nothing)
326✔
824
    obj = context_module
×
825
    for name in split(str[frange[1]:end_of_identifier], '.')
81✔
826
        Base.isidentifier(name) || return (nothing, nothing, nothing)
121✔
827
        sym = Symbol(name)
117✔
828
        isdefined(obj, sym) || return (nothing, nothing, nothing)
117✔
829
        obj = getfield(obj, sym)
117✔
830
    end
196✔
831
    (isa(obj, AbstractDict) && length(obj)::Int < 1_000_000) || return (nothing, nothing, nothing)
80✔
832
    begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [
152✔
833
                             lastindex(str)+1)
834
    return (obj::AbstractDict, str[begin_of_key:end], begin_of_key)
78✔
835
end
836

837
# This needs to be a separate non-inlined function, see #19441
838
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
77✔
839
    matches = String[]
77✔
840
    for key in keys(identifier)
116✔
841
        rkey = repr(key)
924✔
842
        startswith(rkey,partial_key) && push!(matches,rkey)
924✔
843
    end
1,430✔
844
    return matches
77✔
845
end
846

847
# Identify an argument being completed in a method call. If the argument is empty, method
848
# suggestions will be provided instead of argument completions.
849
function identify_possible_method_completion(partial, last_idx)
293✔
850
    fail = 0:-1, Expr(:nothing), 0:-1, 0
293✔
851

852
    # First, check that the last punctuation is either ',', ';' or '('
853
    idx_last_punct = something(findprev(x -> ispunct(x) && x != '_' && x != '!', partial, last_idx), 0)::Int
1,460✔
854
    idx_last_punct == 0 && return fail
293✔
855
    last_punct = partial[idx_last_punct]
548✔
856
    last_punct == ',' || last_punct == ';' || last_punct == '(' || return fail
454✔
857

858
    # Then, check that `last_punct` is only followed by an identifier or nothing
859
    before_last_word_start = something(findprev(in(non_identifier_chars), partial, last_idx), 0)
328✔
860
    before_last_word_start == 0 && return fail
164✔
861
    all(isspace, @view partial[nextind(partial, idx_last_punct):before_last_word_start]) || return fail
170✔
862

863
    # Check that `last_punct` is either the last '(' or placed after a previous '('
864
    frange, method_name_end = find_start_brace(@view partial[1:idx_last_punct])
158✔
865
    method_name_end ∈ frange || return fail
168✔
866

867
    # Strip the preceding ! operators, if any, and close the expression with a ')'
868
    s = replace(partial[frange], r"\G\!+([^=\(]+)" => s"\1"; count=1) * ')'
296✔
869
    ex = Meta.parse(s, raise=false, depwarn=false)
148✔
870
    isa(ex, Expr) || return fail
148✔
871

872
    # `wordrange` is the position of the last argument to complete
873
    wordrange = nextind(partial, before_last_word_start):last_idx
228✔
874
    return frange, ex, wordrange, method_name_end
148✔
875
end
876

877
# Provide completion for keyword arguments in function calls
878
function complete_keyword_argument(partial, last_idx, context_module)
128✔
879
    frange, ex, wordrange, = identify_possible_method_completion(partial, last_idx)
128✔
880
    fail = Completion[], 0:-1, frange
128✔
881
    ex.head === :call || is_broadcasting_expr(ex) || return fail
226✔
882

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

886
    methods = Completion[]
33✔
887
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, kwargs_flag == 1)
33✔
888
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
889
    # method calls compatible with the current arguments.
890

891
    # For each method corresponding to the function call, provide completion suggestions
892
    # for each keyword that starts like the last word and that is not already used
893
    # previously in the expression. The corresponding suggestion is "kwname=".
894
    # If the keyword corresponds to an existing name, also include "kwname" as a suggestion
895
    # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)".
896
    last_word = partial[wordrange] # the word to complete
66✔
897
    kwargs = Set{String}()
33✔
898
    for m in methods
33✔
899
        m::MethodCompletion
86✔
900
        possible_kwargs = Base.kwarg_decl(m.method)
86✔
901
        current_kwarg_candidates = String[]
86✔
902
        for _kw in possible_kwargs
88✔
903
            kw = String(_kw)
198✔
904
            if !endswith(kw, "...") && startswith(kw, last_word) && _kw ∉ kwargs_ex
338✔
905
                push!(current_kwarg_candidates, kw)
51✔
906
            end
907
        end
282✔
908
        union!(kwargs, current_kwarg_candidates)
86✔
909
    end
119✔
910

911
    suggestions = Completion[KeywordArgumentCompletion(kwarg) for kwarg in kwargs]
72✔
912
    append!(suggestions, complete_symbol(nothing, last_word, Returns(true), context_module))
33✔
913

914
    return sort!(suggestions, by=completion_text), wordrange
33✔
915
end
916

917
function project_deps_get_completion_candidates(pkgstarts::String, project_file::String)
8✔
918
    loading_candidates = String[]
8✔
919
    d = Base.parsed_toml(project_file)
8✔
920
    pkg = get(d, "name", nothing)::Union{String, Nothing}
9✔
921
    if pkg !== nothing && startswith(pkg, pkgstarts)
9✔
922
        push!(loading_candidates, pkg)
1✔
923
    end
924
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
16✔
925
    if deps !== nothing
8✔
926
        for (pkg, _) in deps
16✔
927
            startswith(pkg, pkgstarts) && push!(loading_candidates, pkg)
13✔
928
        end
8✔
929
    end
930
    return Completion[PackageCompletion(name) for name in loading_candidates]
8✔
931
end
932

933
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)
98✔
934
    ex = nothing
3✔
935
    comp_keywords && append!(suggestions, complete_keyword(name))
98✔
936
    if dotpos > 1 && string[dotpos] == '.'
181✔
937
        s = string[1:dotpos-1]
132✔
938
        # First see if the whole string up to `pos` is a valid expression. If so, use it.
939
        ex = Meta.parse(s, raise=false, depwarn=false)
66✔
940
        if isexpr(ex, :incomplete)
79✔
941
            s = string[startpos:pos]
6✔
942
            # Heuristic to find the start of the expression. TODO: This would be better
943
            # done with a proper error-recovering parser.
944
            if 0 < startpos <= lastindex(string) && string[startpos] == '.'
6✔
945
                i = prevind(string, startpos)
1✔
946
                while 0 < i
1✔
947
                    c = string[i]
2✔
948
                    if c in (')', ']')
3✔
949
                        if c == ')'
×
950
                            c_start = '('
×
951
                            c_end = ')'
×
952
                        elseif c == ']'
×
953
                            c_start = '['
×
954
                            c_end = ']'
×
955
                        end
956
                        frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end)
×
957
                        isempty(frange) && break # unbalanced parens
×
958
                        startpos = first(frange)
×
959
                        i = prevind(string, startpos)
×
960
                    elseif c in ('\'', '\"', '\`')
4✔
961
                        s = "$c$c"*string[startpos:pos]
×
962
                        break
×
963
                    else
964
                        break
×
965
                    end
966
                    s = string[startpos:pos]
×
967
                end
×
968
            end
969
            if something(findlast(in(non_identifier_chars), s), 0) < something(findlast(isequal('.'), s), 0)
6✔
970
                lookup_name, name = rsplit(s, ".", limit=2)
3✔
971
                name = String(name)
3✔
972

973
                ex = Meta.parse(lookup_name, raise=false, depwarn=false)
3✔
974
            end
975
            isexpr(ex, :incomplete) && (ex = nothing)
5✔
976
        end
977
    end
978
    append!(suggestions, complete_symbol(ex, name, ffunc, context_module))
98✔
979
    return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true
98✔
980
end
981

982
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true)
670✔
983
    # First parse everything up to the current position
984
    partial = string[1:pos]
1,005✔
985
    inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false))
338✔
986

987
    # ?(x, y)TAB lists methods you can call with these objects
988
    # ?(x, y TAB lists methods that take these objects as the first two arguments
989
    # MyModule.?(x, y)TAB restricts the search to names in MyModule
990
    rexm = match(r"(\w+\.|)\?\((.*)$", partial)
338✔
991
    if rexm !== nothing
338✔
992
        # Get the module scope
993
        if isempty(rexm.captures[1])
26✔
994
            callee_module = context_module
×
995
        else
996
            modname = Symbol(rexm.captures[1][1:end-1])
13✔
997
            if isdefined(context_module, modname)
13✔
998
                callee_module = getfield(context_module, modname)
13✔
999
                if !isa(callee_module, Module)
13✔
1000
                    callee_module = context_module
13✔
1001
                end
1002
            else
1003
                callee_module = context_module
×
1004
            end
1005
        end
1006
        moreargs = !endswith(rexm.captures[2], ')')
14✔
1007
        callstr = "_(" * rexm.captures[2]
13✔
1008
        if moreargs
13✔
1009
            callstr *= ')'
7✔
1010
        end
1011
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
13✔
1012
        if isa(ex_org, Expr)
13✔
1013
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false
13✔
1014
        end
1015
    end
1016

1017
    # if completing a key in a Dict
1018
    identifier, partial_key, loc = dict_identifier_key(partial, inc_tag, context_module)
402✔
1019
    if identifier !== nothing
325✔
1020
        matches = find_dict_matches(identifier, partial_key)
77✔
1021
        length(matches)==1 && (lastindex(string) <= pos || string[nextind(string,pos)] != ']') && (matches[1]*=']')
81✔
1022
        length(matches)>0 && return Completion[DictCompletion(identifier, match) for match in sort!(matches)], loc::Int:pos, true
77✔
1023
    end
1024

1025
    ffunc = Returns(true)
×
1026
    suggestions = Completion[]
264✔
1027

1028
    # Check if this is a var"" string macro that should be completed like
1029
    # an identifier rather than a string.
1030
    # TODO: It would be nice for the parser to give us more information here
1031
    # so that we can lookup the macro by identity rather than pattern matching
1032
    # its invocation.
1033
    varrange = findprev("var\"", string, pos)
264✔
1034

1035
    if varrange !== nothing
264✔
1036
        ok, ret = bslash_completions(string, pos)
3✔
1037
        ok && return ret
3✔
1038
        startpos = first(varrange) + 4
3✔
1039
        dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0)
5✔
1040
        return complete_identifiers!(Completion[], ffunc, context_module, string,
3✔
1041
            string[startpos:pos], pos, dotpos, startpos)
1042
    # otherwise...
1043
    elseif inc_tag in [:cmd, :string]
491✔
1044
        m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial))
31✔
1045
        startpos = nextind(partial, reverseind(partial, m.offset))
62✔
1046
        r = startpos:pos
31✔
1047

1048
        expanded = complete_expanduser(replace(string[r], r"\\ " => " "), r)
62✔
1049
        expanded[3] && return expanded  # If user expansion available, return it
31✔
1050

1051
        paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos)
58✔
1052

1053
        if inc_tag === :string && close_path_completion(string, startpos, r, paths, pos)
29✔
1054
            paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"")
2✔
1055
        end
1056

1057
        #Latex symbols can be completed for strings
1058
        (success || inc_tag === :cmd) && return sort!(paths, by=p->p.path), r, success
29✔
1059
    end
1060

1061
    ok, ret = bslash_completions(string, pos)
245✔
1062
    ok && return ret
245✔
1063

1064
    # Make sure that only bslash_completions is working on strings
1065
    inc_tag === :string && return Completion[], 0:-1, false
214✔
1066
    if inc_tag === :other
209✔
1067
        frange, ex, wordrange, method_name_end = identify_possible_method_completion(partial, pos)
165✔
1068
        if last(frange) != -1 && all(isspace, @view partial[wordrange]) # no last argument to complete
165✔
1069
            if ex.head === :call
80✔
1070
                return complete_methods(ex, context_module, shift), first(frange):method_name_end, false
77✔
1071
            elseif is_broadcasting_expr(ex)
3✔
1072
                return complete_methods(ex, context_module, shift), first(frange):(method_name_end - 1), false
3✔
1073
            end
1074
        end
1075
    elseif inc_tag === :comment
44✔
1076
        return Completion[], 0:-1, false
1✔
1077
    end
1078

1079
    # Check whether we can complete a keyword argument in a function call
1080
    kwarg_completion, wordrange = complete_keyword_argument(partial, pos, context_module)
223✔
1081
    isempty(wordrange) || return kwarg_completion, wordrange, !isempty(kwarg_completion)
161✔
1082

1083
    dotpos = something(findprev(isequal('.'), string, pos), 0)
166✔
1084
    startpos = nextind(string, something(findprev(in(non_identifier_chars), string, pos), 0))
145✔
1085
    # strip preceding ! operator
1086
    if (m = match(r"\G\!+", partial, startpos)) isa RegexMatch
95✔
1087
        startpos += length(m.match)
2✔
1088
    end
1089

1090
    name = string[max(startpos, dotpos+1):pos]
140✔
1091
    comp_keywords = !isempty(name) && startpos > dotpos
95✔
1092
    if afterusing(string, startpos)
95✔
1093
        # We're right after using or import. Let's look only for packages
1094
        # and modules we can reach from here
1095

1096
        # If there's no dot, we're in toplevel, so we should
1097
        # also search for packages
1098
        s = string[startpos:pos]
15✔
1099
        if dotpos <= startpos
8✔
1100
            for dir in Base.load_path()
7✔
1101
                if basename(dir) in Base.project_names && isfile(dir)
43✔
1102
                    append!(suggestions, project_deps_get_completion_candidates(s, dir))
8✔
1103
                end
1104
                isdir(dir) || continue
17✔
1105
                for pname in readdir(dir)
8✔
1106
                    if pname[1] != '.' && pname != "METADATA" &&
800✔
1107
                        pname != "REQUIRE" && startswith(pname, s)
1108
                        # Valid file paths are
1109
                        #   <Mod>.jl
1110
                        #   <Mod>/src/<Mod>.jl
1111
                        #   <Mod>.jl/src/<Mod>.jl
1112
                        if isfile(joinpath(dir, pname))
65✔
1113
                            endswith(pname, ".jl") && push!(suggestions,
×
1114
                                                            PackageCompletion(pname[1:prevind(pname, end-2)]))
1115
                        else
1116
                            mod_name = if endswith(pname, ".jl")
65✔
1117
                                pname[1:prevind(pname, end-2)]
×
1118
                            else
1119
                                pname
65✔
1120
                            end
1121
                            if isfile(joinpath(dir, pname, "src",
65✔
1122
                                               "$mod_name.jl"))
1123
                                push!(suggestions, PackageCompletion(mod_name))
64✔
1124
                            end
1125
                        end
1126
                    end
1127
                end
423✔
1128
            end
24✔
1129
        end
1130
        ffunc = (mod,x)->(Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getfield(mod, x), Module))
8,176✔
1131
        comp_keywords = false
×
1132
    end
1133

1134
    startpos == 0 && (pos = -1)
95✔
1135
    dotpos < startpos && (dotpos = startpos - 1)
95✔
1136
    return complete_identifiers!(suggestions, ffunc, context_module, string,
95✔
1137
        name, pos, dotpos, startpos, comp_keywords)
1138
end
1139

1140
function shell_completions(string, pos)
15✔
1141
    # First parse everything up to the current position
1142
    scs = string[1:pos]
30✔
1143
    local args, last_parse
×
1144
    try
15✔
1145
        args, last_parse = Base.shell_parse(scs, true)::Tuple{Expr,UnitRange{Int}}
15✔
1146
    catch
1147
        return Completion[], 0:-1, false
×
1148
    end
1149
    ex = args.args[end]::Expr
15✔
1150
    # Now look at the last thing we parsed
1151
    isempty(ex.args) && return Completion[], 0:-1, false
15✔
1152
    arg = ex.args[end]
15✔
1153
    if all(s -> isa(s, AbstractString), ex.args)
44✔
1154
        arg = arg::AbstractString
12✔
1155
        # Treat this as a path
1156

1157
        # As Base.shell_parse throws away trailing spaces (unless they are escaped),
1158
        # we need to special case here.
1159
        # If the last char was a space, but shell_parse ignored it search on "".
1160
        ignore_last_word = arg != " " && scs[end] == ' '
23✔
1161
        prefix = ignore_last_word ? "" : join(ex.args)
23✔
1162

1163
        # Also try looking into the env path if the user wants to complete the first argument
1164
        use_envpath = !ignore_last_word && length(args.args) < 2
12✔
1165

1166
        return complete_path(prefix, pos, use_envpath=use_envpath, shell_escape=true)
12✔
1167
    elseif isexpr(arg, :incomplete) || isexpr(arg, :error)
5✔
1168
        partial = scs[last_parse]
4✔
1169
        ret, range = completions(partial, lastindex(partial))
2✔
1170
        range = range .+ (first(last_parse) - 1)
2✔
1171
        return ret, range, true
2✔
1172
    end
1173
    return Completion[], 0:-1, false
1✔
1174
end
1175

1176
function UndefVarError_hint(io::IO, ex::UndefVarError)
6✔
1177
    var = ex.var
6✔
1178
    if var === :or
6✔
1179
        print(io, "\nsuggestion: Use `||` for short-circuiting boolean OR.")
×
1180
    elseif var === :and
6✔
1181
        print(io, "\nsuggestion: Use `&&` for short-circuiting boolean AND.")
×
1182
    elseif var === :help
6✔
1183
        println(io)
×
1184
        # Show friendly help message when user types help or help() and help is undefined
1185
        show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
×
1186
    elseif var === :quit
6✔
1187
        print(io, "\nsuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
×
1188
    end
1189
end
1190

1191
function __init__()
471✔
1192
    Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
471✔
1193
    COMPLETION_WORLD[] = Base.get_world_counter()
471✔
1194
    nothing
471✔
1195
end
1196

1197
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