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

JuliaLang / julia / #37620

14 Sep 2023 12:24AM UTC coverage: 85.982% (-0.7%) from 86.645%
#37620

push

local

web-flow
EA: some noncritical updates on EA (#51292)

9 of 9 new or added lines in 3 files covered. (100.0%)

73376 of 85339 relevant lines covered (85.98%)

12810329.83 hits per line

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

88.01
/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
172✔
20
end
21

22
struct PathCompletion <: Completion
23
    path::String
6,392✔
24
end
25

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

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

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

40
struct FieldCompletion <: Completion
41
    typ::DataType
25✔
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)
2,242✔
49
end
50

51
struct BslashCompletion <: Completion
52
    bslash::String
5,164✔
53
end
54

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

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

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

68
# interface definition
69
function Base.getproperty(c::Completion, name::Symbol)
301✔
70
    if name === :text
8,321,565✔
71
        return getfield(c, :text)::String
4✔
72
    elseif name === :keyword
8,319,792✔
73
        return getfield(c, :keyword)::String
1,769✔
74
    elseif name === :path
8,313,520✔
75
        return getfield(c, :path)::String
59,824✔
76
    elseif name === :parent
8,313,520✔
77
        return getfield(c, :parent)::Module
×
78
    elseif name === :mod
9,028✔
79
        return getfield(c, :mod)::String
8,304,492✔
80
    elseif name === :package
6,472✔
81
        return getfield(c, :package)::String
2,556✔
82
    elseif name === :property
6,472✔
83
        return getfield(c, :property)::Symbol
103✔
84
    elseif name === :field
6,369✔
85
        return getfield(c, :field)::Symbol
55✔
86
    elseif name === :method
5,514✔
87
        return getfield(c, :method)::Method
2,244✔
88
    elseif name === :bslash
350✔
89
        return getfield(c, :bslash)::String
5,164✔
90
    elseif name === :text
350✔
91
        return getfield(c, :text)::String
×
92
    elseif name === :key
350✔
93
        return getfield(c, :key)::String
128✔
94
    elseif name === :kwarg
×
95
        return getfield(c, :kwarg)::String
222✔
96
    end
97
    return getfield(c, name)
×
98
end
99

100
_completion_text(c::TextCompletion) = c.text
4✔
101
_completion_text(c::KeywordCompletion) = c.keyword
1,769✔
102
_completion_text(c::PathCompletion) = c.path
6,251✔
103
_completion_text(c::ModuleCompletion) = c.mod
8,304,492✔
104
_completion_text(c::PackageCompletion) = c.package
2,556✔
105
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
103✔
106
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
55✔
107
_completion_text(c::MethodCompletion) = repr(c.method)
789✔
108
_completion_text(c::BslashCompletion) = c.bslash
5,164✔
109
_completion_text(c::ShellCompletion) = c.text
×
110
_completion_text(c::DictCompletion) = c.key
128✔
111
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
222✔
112

113
completion_text(c) = _completion_text(c)::String
8,321,533✔
114

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

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

121
function appendmacro!(syms, macros, needle, endchar)
20,964✔
122
    for macsym in macros
39,672✔
123
        s = String(macsym)
52,116✔
124
        if endswith(s, needle)
52,116✔
125
            from = nextind(s, firstindex(s))
3,530✔
126
            to = prevind(s, sizeof(s)-sizeof(needle)+1)
3,530✔
127
            push!(syms, s[from:to]*endchar)
7,060✔
128
        end
129
    end
52,116✔
130
end
131

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

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

146
    lookup_module = true
×
147
    t = Union{}
×
148
    val = nothing
×
149
    if ex !== nothing
1,644✔
150
        res = repl_eval_ex(ex, context_module)
655✔
151
        res === nothing && return Completion[]
655✔
152
        if res isa Const
637✔
153
            val = res.val
623✔
154
            if isa(val, Module)
623✔
155
                mod = val
598✔
156
                lookup_module = true
598✔
157
            else
158
                lookup_module = false
×
159
                t = typeof(val)
648✔
160
            end
161
        else
162
            lookup_module = false
×
163
            t = CC.widenconst(res)
14✔
164
        end
165
    end
166

167
    suggestions = Completion[]
1,626✔
168
    if lookup_module
1,626✔
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)
1,587✔
173
            (s::Symbol) -> !Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool
1,894,748✔
174
        end
175
        # Looking for a binding in a module
176
        if mod == context_module
1,587✔
177
            # Also look in modules we got through `using`
178
            mods = ccall(:jl_module_usings, Any, (Any,), context_module)::Vector
1,299✔
179
            for m in mods
1,299✔
180
                append!(suggestions, filtered_mod_names(p, m::Module, name))
12,003✔
181
            end
10,194✔
182
            append!(suggestions, filtered_mod_names(p, mod, name, true, true))
2,028✔
183
        else
184
            append!(suggestions, filtered_mod_names(p, mod, name, true, false))
1,875✔
185
        end
186
    elseif val !== nothing # looking for a property of an instance
39✔
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
        add_field_completions!(suggestions, name, t)
15✔
196
    end
197
    return suggestions
1,626✔
198
end
199

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

223
const sorted_keywords = [
224
    "abstract type", "baremodule", "begin", "break", "catch", "ccall",
225
    "const", "continue", "do", "else", "elseif", "end", "export", "false",
226
    "finally", "for", "function", "global", "if", "import",
227
    "let", "local", "macro", "module", "mutable struct",
228
    "primitive type", "quote", "return", "struct",
229
    "true", "try", "using", "while"]
230

231
function complete_keyword(s::Union{String,SubString{String}})
544✔
232
    r = searchsorted(sorted_keywords, s)
544✔
233
    i = first(r)
544✔
234
    n = length(sorted_keywords)
544✔
235
    while i <= n && startswith(sorted_keywords[i],s)
1,371✔
236
        r = first(r):i
172✔
237
        i += 1
172✔
238
    end
172✔
239
    Completion[KeywordCompletion(kw) for kw in sorted_keywords[r]]
544✔
240
end
241

242
function complete_path(path::AbstractString, pos::Int;
1,136✔
243
                       use_envpath=false, shell_escape=false,
244
                       string_escape=false)
245
    @assert !(shell_escape && string_escape)
568✔
246
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
568✔
247
        # if the path is just "~", don't consider the expanded username as a prefix
248
        if path == "~"
2✔
249
            dir, prefix = homedir(), ""
2✔
250
        else
251
            dir, prefix = splitdir(homedir() * path[2:end])
2✔
252
        end
253
    else
254
        dir, prefix = splitdir(path)
566✔
255
    end
256
    local files
×
257
    try
568✔
258
        if isempty(dir)
568✔
259
            files = readdir()
302✔
260
        elseif isdir(dir)
266✔
261
            files = readdir(dir)
107✔
262
        else
263
            return Completion[], 0:-1, false
568✔
264
        end
265
    catch
266
        return Completion[], 0:-1, false
×
267
    end
268

269
    matches = Set{String}()
409✔
270
    for file in files
414✔
271
        if startswith(file, prefix)
75,959✔
272
            p = joinpath(dir, file)
6,031✔
273
            is_dir = try isdir(p) catch; false end
18,095✔
274
            push!(matches, is_dir ? joinpath(file, "") : file)
6,031✔
275
        end
276
    end
42,110✔
277

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

282
        for pathdir in pathdirs
18✔
283
            local actualpath
×
284
            try
228✔
285
                actualpath = realpath(pathdir)
260✔
286
            catch
287
                # Bash doesn't expect every folder in PATH to exist, so neither shall we
288
                continue
32✔
289
            end
290

291
            if actualpath != pathdir && in(actualpath,pathdirs)
244✔
292
                # Remove paths which (after resolving links) are in the env path twice.
293
                # Many distros eg. point /bin to /usr/bin but have both in the env path.
294
                continue
48✔
295
            end
296

297
            local filesinpath
×
298
            try
148✔
299
                filesinpath = readdir(pathdir)
149✔
300
            catch e
301
                # Bash allows dirs in PATH that can't be read, so we should as well.
302
                if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
1✔
303
                    continue
1✔
304
                else
305
                    # We only handle IOError and ArgumentError here
306
                    rethrow()
×
307
                end
308
            end
309

310
            for file in filesinpath
195✔
311
                # In a perfect world, we would filter on whether the file is executable
312
                # here, or even on whether the current user can execute the file in question.
313
                if startswith(file, prefix) && isfile(joinpath(pathdir, file))
62,379✔
314
                    push!(matches, file)
589✔
315
                end
316
            end
34,089✔
317
        end
246✔
318
    end
319

320
    function do_escape(s)
409✔
321
        return shell_escape ? replace(s, r"(\s|\\)" => s"\\\0") :
10,026✔
322
               string_escape ? escape_string(s, ('\"','$')) :
323
               s
324
    end
325

326
    matchList = Completion[PathCompletion(do_escape(s)) for s in matches]
6,658✔
327
    startpos = pos - lastindex(do_escape(prefix)) + 1
543✔
328
    # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`,
329
    # hence we need to add one to get the first index. This is also correct when considering
330
    # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`.
331
    return matchList, startpos:pos, !isempty(matchList)
409✔
332
end
333

334
function complete_expanduser(path::AbstractString, r)
138✔
335
    expanded =
138✔
336
        try expanduser(path)
139✔
337
        catch e
338
            e isa ArgumentError || rethrow()
1✔
339
            path
139✔
340
        end
341
    return Completion[PathCompletion(expanded)], r, path != expanded
138✔
342
end
343

344
# Returns a range that includes the method name in front of the first non
345
# closed start brace from the end of the string.
346
function find_start_brace(s::AbstractString; c_start='(', c_end=')')
5,292✔
347
    braces = 0
×
348
    r = reverse(s)
2,646✔
349
    i = firstindex(r)
×
350
    in_single_quotes = false
×
351
    in_double_quotes = false
×
352
    in_back_ticks = false
×
353
    in_comment = 0
×
354
    while i <= ncodeunits(r)
61,366✔
355
        c, i = iterate(r, i)
118,756✔
356
        if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '='
59,378✔
357
            c, i = iterate(r, i) # consume '='
8✔
358
            new_comments = 1
×
359
            # handle #=#=#=#, by counting =# pairs
360
            while i <= ncodeunits(r) && iterate(r, i)[1] == '#'
6✔
361
                c, i = iterate(r, i) # consume '#'
6✔
362
                iterate(r, i)[1] == '=' || break
3✔
363
                c, i = iterate(r, i) # consume '='
4✔
364
                new_comments += 1
2✔
365
            end
2✔
366
            if c == '='
4✔
367
                in_comment += new_comments
3✔
368
            else
369
                in_comment -= new_comments
5✔
370
            end
371
        elseif !in_single_quotes && !in_double_quotes && !in_back_ticks && in_comment == 0
59,374✔
372
            if c == c_start
57,639✔
373
                braces += 1
908✔
374
            elseif c == c_end
56,731✔
375
                braces -= 1
250✔
376
            elseif c == '\''
56,481✔
377
                in_single_quotes = true
15✔
378
            elseif c == '"'
56,466✔
379
                in_double_quotes = true
271✔
380
            elseif c == '`'
56,195✔
381
                in_back_ticks = true
57,639✔
382
            end
383
        else
384
            if in_single_quotes &&
1,735✔
385
                c == '\'' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
386
                in_single_quotes = false
15✔
387
            elseif in_double_quotes &&
1,720✔
388
                c == '"' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
389
                in_double_quotes = false
228✔
390
            elseif in_back_ticks &&
1,492✔
391
                c == '`' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
392
                in_back_ticks = false
7✔
393
            elseif in_comment > 0 &&
1,485✔
394
                c == '=' && i <= ncodeunits(r) && iterate(r, i)[1] == '#'
395
                # handle =#=#=#=, by counting #= pairs
396
                c, i = iterate(r, i) # consume '#'
6✔
397
                old_comments = 1
×
398
                while i <= ncodeunits(r) && iterate(r, i)[1] == '='
4✔
399
                    c, i = iterate(r, i) # consume '='
4✔
400
                    iterate(r, i)[1] == '#' || break
2✔
401
                    c, i = iterate(r, i) # consume '#'
2✔
402
                    old_comments += 1
1✔
403
                end
1✔
404
                if c == '#'
3✔
405
                    in_comment -= old_comments
2✔
406
                else
407
                    in_comment += old_comments
1✔
408
                end
409
            end
410
        end
411
        braces == 1 && break
59,378✔
412
    end
58,720✔
413
    braces != 1 && return 0:-1, -1
2,646✔
414
    method_name_end = reverseind(s, i)
658✔
415
    startind = nextind(s, something(findprev(in(non_identifier_chars), s, method_name_end), 0))::Int
1,020✔
416
    return (startind:lastindex(s), method_name_end)
658✔
417
end
418

419
struct REPLInterpreterCache
420
    dict::IdDict{MethodInstance,CodeInstance}
×
421
end
422
REPLInterpreterCache() = REPLInterpreterCache(IdDict{MethodInstance,CodeInstance}())
×
423
const REPL_INTERPRETER_CACHE = REPLInterpreterCache()
424

425
function get_code_cache()
×
426
    # XXX Avoid storing analysis results into the cache that persists across precompilation,
427
    #     as [sys|pkg]image currently doesn't support serializing externally created `CodeInstance`.
428
    #     Otherwise, `CodeInstance`s created by `REPLInterpreter`, that are much less optimized
429
    #     that those produced by `NativeInterpreter`, will leak into the native code cache,
430
    #     potentially causing runtime slowdown.
431
    #     (see https://github.com/JuliaLang/julia/issues/48453).
432
    if Base.generating_output()
435✔
433
        return REPLInterpreterCache()
×
434
    else
435
        return REPL_INTERPRETER_CACHE
435✔
436
    end
437
end
438

439
struct REPLInterpreter <: CC.AbstractInterpreter
440
    repl_frame::CC.InferenceResult
441
    world::UInt
442
    inf_params::CC.InferenceParams
443
    opt_params::CC.OptimizationParams
444
    inf_cache::Vector{CC.InferenceResult}
445
    code_cache::REPLInterpreterCache
446
    function REPLInterpreter(repl_frame::CC.InferenceResult;
870✔
447
                             world::UInt = Base.get_world_counter(),
448
                             inf_params::CC.InferenceParams = CC.InferenceParams(),
449
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
450
                             inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[],
451
                             code_cache::REPLInterpreterCache = get_code_cache())
452
        return new(repl_frame, world, inf_params, opt_params, inf_cache, code_cache)
435✔
453
    end
454
end
455
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
89,005✔
456
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
×
457
CC.get_world_counter(interp::REPLInterpreter) = interp.world
25,933✔
458
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
9,230✔
459
CC.code_cache(interp::REPLInterpreter) = CC.WorldView(interp.code_cache, CC.WorldRange(interp.world))
22,361✔
460
CC.get(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default)
29,273✔
461
CC.getindex(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi)
×
462
CC.haskey(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi)
3,072✔
463
CC.setindex!(wvc::CC.WorldView{REPLInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi)
3,072✔
464

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

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

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

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

491
# aggressive global binding resolution within `repl_frame`
492
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef,
×
493
                                    sv::CC.InferenceState)
494
    if is_repl_frame(interp, sv)
26,572✔
495
        if CC.isdefined_globalref(g)
578✔
496
            return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
573✔
497
        end
498
        return Union{}
5✔
499
    end
500
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef,
25,994✔
501
                                              sv::CC.InferenceState)
502
end
503

504
function is_repl_frame_getproperty(interp::REPLInterpreter, sv::CC.InferenceState)
66✔
505
    def = sv.linfo.def
66✔
506
    def isa Method || return false
66✔
507
    def.name === :getproperty || return false
66✔
508
    sv.cached && return false
66✔
509
    return is_repl_frame(interp, sv.parent)
65✔
510
end
511

512
# aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame`
513
function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f),
8,136✔
514
                              argtypes::Vector{Any}, sv::CC.InferenceState)
515
    if f === Core.getglobal && is_repl_frame_getproperty(interp, sv)
8,136✔
516
        if length(argtypes) == 2
43✔
517
            a1, a2 = argtypes
43✔
518
            if isa(a1, Const) && isa(a2, Const)
43✔
519
                a1val, a2val = a1.val, a2.val
43✔
520
                if isa(a1val, Module) && isa(a2val, Symbol)
43✔
521
                    g = GlobalRef(a1val, a2val)
43✔
522
                    if CC.isdefined_globalref(g)
43✔
523
                        return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
43✔
524
                    end
525
                    return Union{}
×
526
                end
527
            end
528
        end
529
    end
530
    return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any,
8,093✔
531
                                        argtypes::Vector{Any}, sv::CC.InferenceState)
532
end
533

534
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
535
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
×
536
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
537
                                   sv::CC.InferenceState)
538
    if is_repl_frame(interp, sv)
14,933✔
539
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
153✔
540
        result = CC.MethodCallResult(result.rt, result.edgecycle, result.edgelimited,
306✔
541
                                     result.edge, neweffects)
542
    end
543
    return @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
14,933✔
544
                                             result::CC.MethodCallResult, arginfo::CC.ArgInfo,
545
                                             sv::CC.InferenceState)
546
end
547

548
function resolve_toplevel_symbols!(mod::Module, src::Core.CodeInfo)
×
549
    newsrc = copy(src)
435✔
550
    @ccall jl_resolve_globals_in_ir(
435✔
551
        #=jl_array_t *stmts=# newsrc.code::Any,
552
        #=jl_module_t *m=# mod::Any,
553
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
554
        #=int binding_effects=# 0::Int)::Cvoid
555
    return newsrc
×
556
end
557

558
# lower `ex` and run type inference on the resulting top-level expression
559
function repl_eval_ex(@nospecialize(ex), context_module::Module)
1,172✔
560
    lwr = try
1,172✔
561
        Meta.lower(context_module, ex)
1,185✔
562
    catch # macro expansion failed, etc.
563
        return nothing
13✔
564
    end
565
    if lwr isa Symbol
1,159✔
566
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
624✔
567
    end
568
    lwr isa Expr || return Const(lwr) # `ex` is literal
626✔
569
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
453✔
570
    src = lwr.args[1]::Core.CodeInfo
435✔
571

572
    # construct top-level `MethodInstance`
573
    mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ());
435✔
574
    mi.specTypes = Tuple{}
435✔
575

576
    mi.def = context_module
435✔
577
    src = resolve_toplevel_symbols!(context_module, src)
435✔
578
    @atomic mi.uninferred = src
435✔
579

580
    result = CC.InferenceResult(mi)
435✔
581
    interp = REPLInterpreter(result)
870✔
582
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)::CC.InferenceState
435✔
583

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

588
    result = frame.result.result
435✔
589
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
435✔
590
    return result
428✔
591
end
592

593
# `COMPLETION_WORLD[]` will be initialized within `__init__`
594
# (to allow us to potentially remove REPL from the sysimage in the future).
595
# Note that inference from the `code_typed` call below will use the current world age
596
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
597
# when the given world age is higher than the current one.
598
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
599

600
# Generate code cache for `REPLInterpreter` now:
601
# This code cache will be available at the world of `COMPLETION_WORLD`,
602
# assuming no invalidation will happen before initializing REPL.
603
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
604
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
605

606
# Method completion on function call expression that look like :(max(1))
607
MAX_METHOD_COMPLETIONS::Int = 40
608
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
308✔
609
    funct = repl_eval_ex(ex_org.args[1], context_module)
308✔
610
    funct === nothing && return 2, nothing, [], Set{Symbol}()
308✔
611
    funct = CC.widenconst(funct)
304✔
612
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
602✔
613
    return kwargs_flag, funct, args_ex, kwargs_ex
304✔
614
end
615

616
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false)
×
617
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
138✔
618
    out = Completion[]
138✔
619
    kwargs_flag == 2 && return out # one of the kwargs is invalid
138✔
620
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
126✔
621
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
183✔
622
    return out
126✔
623
end
624

625
MAX_ANY_METHOD_COMPLETIONS::Int = 10
626
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
13✔
627
    out = Completion[]
13✔
628
    args_ex, kwargs_ex, kwargs_flag = try
13✔
629
        # this may throw, since we set default_any to false
630
        complete_methods_args(ex_org, context_module, false, false)
13✔
631
    catch ex
632
        ex isa ArgumentError || rethrow()
×
633
        return out
13✔
634
    end
635
    kwargs_flag == 2 && return out # one of the kwargs is invalid
13✔
636

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

641
    seen = Base.IdSet()
13✔
642
    for name in names(callee_module; all=true)
13✔
643
        if !Base.isdeprecated(callee_module, name) && isdefined(callee_module, name) && !startswith(string(name), '#')
1,313✔
644
            func = getfield(callee_module, name)
637✔
645
            if !isa(func, Module)
637✔
646
                funct = Core.Typeof(func)
1,157✔
647
                if !in(funct, seen)
624✔
648
                    push!(seen, funct)
598✔
649
                    complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
598✔
650
                end
651
            elseif callee_module === Main && isa(func, Module)
26✔
652
                callee_module2 = func
×
653
                for name in names(callee_module2)
×
654
                    if !Base.isdeprecated(callee_module2, name) && isdefined(callee_module2, name) && !startswith(string(name), '#')
×
655
                        func = getfield(callee_module, name)
×
656
                        if !isa(func, Module)
×
657
                            funct = Core.Typeof(func)
×
658
                            if !in(funct, seen)
×
659
                                push!(seen, funct)
×
660
                                complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
×
661
                            end
662
                        end
663
                    end
664
                end
×
665
            end
666
        end
667
    end
1,326✔
668

669
    if !shift
13✔
670
        # Filter out methods where all arguments are `Any`
671
        filter!(out) do c
2✔
672
            isa(c, TextCompletion) && return false
11✔
673
            isa(c, MethodCompletion) || return true
11✔
674
            sig = Base.unwrap_unionall(c.method.sig)::DataType
11✔
675
            return !all(T -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
19✔
676
        end
677
    end
678

679
    return out
13✔
680
end
681

682
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
×
683
    n = isexpr(x, :kw) ? x.args[1] : x
57✔
684
    if n isa Symbol
55✔
685
        push!(kwargs_ex, n)
41✔
686
        return kwargs_flag
41✔
687
    end
688
    possible_splat && isexpr(x, :...) && return kwargs_flag
14✔
689
    return 2 # The kwarg is invalid
10✔
690
end
691

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

730
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
1,781✔
731

732
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
×
733
    if allow_broadcasting && is_broadcasting_expr(ex)
304✔
734
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
6✔
735
    end
736
    return detect_args_kwargs(ex.args, context_module, default_any, false)
311✔
737
end
738

739
function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool)
892✔
740
    # Input types and number of arguments
741
    t_in = Tuple{funct, args_ex...}
892✔
742
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
892✔
743
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
744
    if !isa(m, Vector)
892✔
745
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
4✔
746
        return
4✔
747
    end
748
    for match in m
1,352✔
749
        # TODO: if kwargs_ex, filter out methods without kwargs?
750
        push!(out, MethodCompletion(match.spec_types, match.method))
2,242✔
751
    end
2,242✔
752
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
753
end
754

755
include("latex_symbols.jl")
756
include("emoji_symbols.jl")
757

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

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

770
# Aux function to detect whether we're right after a
771
# using or import keyword
772
function afterusing(string::String, startpos::Int)
1,473✔
773
    (isempty(string) || startpos == 0) && return false
1,473✔
774
    str = string[1:prevind(string,startpos)]
2,633✔
775
    isempty(str) && return false
1,470✔
776
    rstr = reverse(str)
1,163✔
777
    r = findfirst(r"\s(gnisu|tropmi)\b", rstr)
1,163✔
778
    r === nothing && return false
1,163✔
779
    fr = reverseind(str, last(r))
30✔
780
    return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end])
30✔
781
end
782

783
function close_path_completion(str, startpos, r, paths, pos)
135✔
784
    length(paths) == 1 || return false  # Only close if there's a single choice...
254✔
785
    _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path
25✔
786
    path = expanduser(unescape_string(replace(_path, "\\\$"=>"\$", "\\\""=>"\"")))
16✔
787
    # ...except if it's a directory...
788
    try
16✔
789
        isdir(path)
17✔
790
    catch e
791
        e isa Base.IOError || rethrow() # `path` cannot be determined to be a file
17✔
792
    end && return false
793
    # ...and except if there's already a " at the cursor.
794
    return lastindex(str) <= pos || str[nextind(str, pos)] != '"'
6✔
795
end
796

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

829
function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Main)
2,022✔
830
    if tag === :string
2,022✔
831
        str_close = str*"\""
158✔
832
    elseif tag === :cmd
1,863✔
833
        str_close = str*"`"
5✔
834
    else
835
        str_close = str
×
836
    end
837

838
    frange, end_of_identifier = find_start_brace(str_close, c_start='[', c_end=']')
2,021✔
839
    isempty(frange) && return (nothing, nothing, nothing)
2,021✔
840
    obj = context_module
×
841
    for name in split(str[frange[1]:end_of_identifier], '.')
104✔
842
        Base.isidentifier(name) || return (nothing, nothing, nothing)
163✔
843
        sym = Symbol(name)
121✔
844
        isdefined(obj, sym) || return (nothing, nothing, nothing)
121✔
845
        obj = getfield(obj, sym)
121✔
846
    end
204✔
847
    (isa(obj, AbstractDict) && length(obj)::Int < 1_000_000) || return (nothing, nothing, nothing)
84✔
848
    begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [
158✔
849
                             lastindex(str)+1)
850
    return (obj::AbstractDict, str[begin_of_key:end], begin_of_key)
82✔
851
end
852

853
# This needs to be a separate non-inlined function, see #19441
854
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
81✔
855
    matches = String[]
81✔
856
    for key in keys(identifier)
124✔
857
        rkey = repr(key)
940✔
858
        startswith(rkey,partial_key) && push!(matches,rkey)
956✔
859
    end
1,462✔
860
    return matches
81✔
861
end
862

863
# Identify an argument being completed in a method call. If the argument is empty, method
864
# suggestions will be provided instead of argument completions.
865
function identify_possible_method_completion(partial, last_idx)
2,604✔
866
    fail = 0:-1, Expr(:nothing), 0:-1, 0
2,604✔
867

868
    # First, check that the last punctuation is either ',', ';' or '('
869
    idx_last_punct = something(findprev(x -> ispunct(x) && x != '_' && x != '!', partial, last_idx), 0)::Int
18,518✔
870
    idx_last_punct == 0 && return fail
2,604✔
871
    last_punct = partial[idx_last_punct]
4,216✔
872
    last_punct == ',' || last_punct == ';' || last_punct == '(' || return fail
4,006✔
873

874
    # Then, check that `last_punct` is only followed by an identifier or nothing
875
    before_last_word_start = something(findprev(in(non_identifier_chars), partial, last_idx), 0)
1,410✔
876
    before_last_word_start == 0 && return fail
705✔
877
    all(isspace, @view partial[nextind(partial, idx_last_punct):before_last_word_start]) || return fail
785✔
878

879
    # Check that `last_punct` is either the last '(' or placed after a previous '('
880
    frange, method_name_end = find_start_brace(@view partial[1:idx_last_punct])
625✔
881
    method_name_end ∈ frange || return fail
770✔
882

883
    # Strip the preceding ! operators, if any, and close the expression with a ')'
884
    s = replace(partial[frange], r"\G\!+([^=\(]+)" => s"\1"; count=1) * ')'
960✔
885
    ex = Meta.parse(s, raise=false, depwarn=false)
480✔
886
    isa(ex, Expr) || return fail
480✔
887

888
    # `wordrange` is the position of the last argument to complete
889
    wordrange = nextind(partial, before_last_word_start):last_idx
618✔
890
    return frange, ex, wordrange, method_name_end
480✔
891
end
892

893
# Provide completion for keyword arguments in function calls
894
function complete_keyword_argument(partial, last_idx, context_module)
1,641✔
895
    frange, ex, wordrange, = identify_possible_method_completion(partial, last_idx)
1,641✔
896
    fail = Completion[], 0:-1, frange
1,641✔
897
    ex.head === :call || is_broadcasting_expr(ex) || return fail
3,115✔
898

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

902
    methods = Completion[]
168✔
903
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, kwargs_flag == 1)
168✔
904
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
905
    # method calls compatible with the current arguments.
906

907
    # For each method corresponding to the function call, provide completion suggestions
908
    # for each keyword that starts like the last word and that is not already used
909
    # previously in the expression. The corresponding suggestion is "kwname=".
910
    # If the keyword corresponds to an existing name, also include "kwname" as a suggestion
911
    # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)".
912
    last_word = partial[wordrange] # the word to complete
336✔
913
    kwargs = Set{String}()
168✔
914
    for m in methods
168✔
915
        m::MethodCompletion
1,444✔
916
        possible_kwargs = Base.kwarg_decl(m.method)
1,444✔
917
        current_kwarg_candidates = String[]
1,444✔
918
        for _kw in possible_kwargs
2,630✔
919
            kw = String(_kw)
472✔
920
            if !endswith(kw, "...") && startswith(kw, last_word) && _kw ∉ kwargs_ex
797✔
921
                push!(current_kwarg_candidates, kw)
67✔
922
            end
923
        end
730✔
924
        union!(kwargs, current_kwarg_candidates)
1,444✔
925
    end
1,612✔
926

927
    suggestions = Completion[KeywordArgumentCompletion(kwarg) for kwarg in kwargs]
215✔
928
    append!(suggestions, complete_symbol(nothing, last_word, Returns(true), context_module))
168✔
929

930
    return sort!(suggestions, by=completion_text), wordrange
168✔
931
end
932

933
function project_deps_get_completion_candidates(pkgstarts::String, project_file::String)
1✔
934
    loading_candidates = String[]
1✔
935
    d = Base.parsed_toml(project_file)
1✔
936
    pkg = get(d, "name", nothing)::Union{String, Nothing}
2✔
937
    if pkg !== nothing && startswith(pkg, pkgstarts)
2✔
938
        push!(loading_candidates, pkg)
1✔
939
    end
940
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
2✔
941
    if deps !== nothing
1✔
942
        for (pkg, _) in deps
2✔
943
            startswith(pkg, pkgstarts) && push!(loading_candidates, pkg)
2✔
944
        end
1✔
945
    end
946
    return Completion[PackageCompletion(name) for name in loading_candidates]
1✔
947
end
948

949
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,476✔
950
    ex = nothing
3✔
951
    comp_keywords && append!(suggestions, complete_keyword(name))
1,476✔
952
    if dotpos > 1 && string[dotpos] == '.'
2,590✔
953
        s = string[1:dotpos-1]
1,310✔
954
        # First see if the whole string up to `pos` is a valid expression. If so, use it.
955
        ex = Meta.parse(s, raise=false, depwarn=false)
655✔
956
        if isexpr(ex, :incomplete)
699✔
957
            s = string[startpos:pos]
1,090✔
958
            # Heuristic to find the start of the expression. TODO: This would be better
959
            # done with a proper error-recovering parser.
960
            if 0 < startpos <= lastindex(string) && string[startpos] == '.'
1,090✔
961
                i = prevind(string, startpos)
1✔
962
                while 0 < i
1✔
963
                    c = string[i]
2✔
964
                    if c in (')', ']')
4✔
965
                        if c == ')'
×
966
                            c_start = '('
×
967
                            c_end = ')'
×
968
                        elseif c == ']'
×
969
                            c_start = '['
×
970
                            c_end = ']'
×
971
                        end
972
                        frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end)
×
973
                        isempty(frange) && break # unbalanced parens
×
974
                        startpos = first(frange)
×
975
                        i = prevind(string, startpos)
×
976
                    elseif c in ('\'', '\"', '\`')
6✔
977
                        s = "$c$c"*string[startpos:pos]
×
978
                        break
×
979
                    else
980
                        break
×
981
                    end
982
                    s = string[startpos:pos]
×
983
                end
×
984
            end
985
            if something(findlast(in(non_identifier_chars), s), 0) < something(findlast(isequal('.'), s), 0)
1,090✔
986
                lookup_name, name = rsplit(s, ".", limit=2)
545✔
987
                name = String(name)
545✔
988

989
                ex = Meta.parse(lookup_name, raise=false, depwarn=false)
545✔
990
            end
991
            isexpr(ex, :incomplete) && (ex = nothing)
873✔
992
        end
993
    end
994
    append!(suggestions, complete_symbol(ex, name, ffunc, context_module))
1,476✔
995
    return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true
1,476✔
996
end
997

998
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true)
2,368✔
999
    # First parse everything up to the current position
1000
    partial = string[1:pos]
4,400✔
1001
    inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false))
2,033✔
1002

1003
    # ?(x, y)TAB lists methods you can call with these objects
1004
    # ?(x, y TAB lists methods that take these objects as the first two arguments
1005
    # MyModule.?(x, y)TAB restricts the search to names in MyModule
1006
    rexm = match(r"(\w+\.|)\?\((.*)$", partial)
2,033✔
1007
    if rexm !== nothing
2,033✔
1008
        # Get the module scope
1009
        if isempty(rexm.captures[1])
26✔
1010
            callee_module = context_module
×
1011
        else
1012
            modname = Symbol(rexm.captures[1][1:end-1])
13✔
1013
            if isdefined(context_module, modname)
13✔
1014
                callee_module = getfield(context_module, modname)
13✔
1015
                if !isa(callee_module, Module)
13✔
1016
                    callee_module = context_module
13✔
1017
                end
1018
            else
1019
                callee_module = context_module
×
1020
            end
1021
        end
1022
        moreargs = !endswith(rexm.captures[2], ')')
14✔
1023
        callstr = "_(" * rexm.captures[2]
13✔
1024
        if moreargs
13✔
1025
            callstr *= ')'
7✔
1026
        end
1027
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
13✔
1028
        if isa(ex_org, Expr)
13✔
1029
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false
13✔
1030
        end
1031
    end
1032

1033
    # if completing a key in a Dict
1034
    identifier, partial_key, loc = dict_identifier_key(partial, inc_tag, context_module)
2,101✔
1035
    if identifier !== nothing
2,020✔
1036
        matches = find_dict_matches(identifier, partial_key)
81✔
1037
        length(matches)==1 && (lastindex(string) <= pos || string[nextind(string,pos)] != ']') && (matches[1]*=']')
85✔
1038
        length(matches)>0 && return Completion[DictCompletion(identifier, match) for match in sort!(matches)], loc::Int:pos, true
81✔
1039
    end
1040

1041
    ffunc = Returns(true)
×
1042
    suggestions = Completion[]
1,955✔
1043

1044
    # Check if this is a var"" string macro that should be completed like
1045
    # an identifier rather than a string.
1046
    # TODO: It would be nice for the parser to give us more information here
1047
    # so that we can lookup the macro by identity rather than pattern matching
1048
    # its invocation.
1049
    varrange = findprev("var\"", string, pos)
1,955✔
1050

1051
    if varrange !== nothing
1,955✔
1052
        ok, ret = bslash_completions(string, pos)
3✔
1053
        ok && return ret
3✔
1054
        startpos = first(varrange) + 4
3✔
1055
        dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0)
5✔
1056
        return complete_identifiers!(Completion[], ffunc, context_module, string,
3✔
1057
            string[startpos:pos], pos, dotpos, startpos)
1058
    elseif inc_tag === :cmd
1,952✔
1059
        m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial))
1✔
1060
        startpos = nextind(partial, reverseind(partial, m.offset))
2✔
1061
        r = startpos:pos
1✔
1062

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

1068
        paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos,
2✔
1069
                                          shell_escape=true)
1070

1071
        return sort!(paths, by=p->p.path), r, success
1✔
1072
    elseif inc_tag === :string
1,951✔
1073
        # Find first non-escaped quote
1074
        m = match(r"\"(?!\\)", reverse(partial))
137✔
1075
        startpos = nextind(partial, reverseind(partial, m.offset))
274✔
1076
        r = startpos:pos
159✔
1077

1078
        expanded = complete_expanduser(string[r], r)
252✔
1079
        expanded[3] && return expanded  # If user expansion available, return it
137✔
1080

1081
        path_prefix = try
135✔
1082
            unescape_string(replace(string[r], "\\\$"=>"\$", "\\\""=>"\""))
248✔
1083
        catch
1084
            nothing
135✔
1085
        end
1086
        if !isnothing(path_prefix)
270✔
1087
            paths, r, success = complete_path(path_prefix, pos, string_escape=true)
135✔
1088

1089
            if close_path_completion(string, startpos, r, paths, pos)
135✔
1090
                paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"")
5✔
1091
            end
1092

1093
            # Fallthrough allowed so that Latex symbols can be completed in strings
1094
            success && return sort!(paths, by=p->p.path), r, success
53,687✔
1095
        end
1096
    end
1097

1098
    ok, ret = bslash_completions(string, pos)
1,879✔
1099
    ok && return ret
1,879✔
1100

1101
    # Make sure that only bslash_completions is working on strings
1102
    inc_tag === :string && return Completion[], 0:-1, false
1,836✔
1103
    if inc_tag === :other
1,780✔
1104
        frange, ex, wordrange, method_name_end = identify_possible_method_completion(partial, pos)
963✔
1105
        if last(frange) != -1 && all(isspace, @view partial[wordrange]) # no last argument to complete
963✔
1106
            if ex.head === :call
138✔
1107
                return complete_methods(ex, context_module, shift), first(frange):method_name_end, false
135✔
1108
            elseif is_broadcasting_expr(ex)
3✔
1109
                return complete_methods(ex, context_module, shift), first(frange):(method_name_end - 1), false
3✔
1110
            end
1111
        end
1112
    elseif inc_tag === :comment
817✔
1113
        return Completion[], 0:-1, false
1✔
1114
    end
1115

1116
    # Check whether we can complete a keyword argument in a function call
1117
    kwarg_completion, wordrange = complete_keyword_argument(partial, pos, context_module)
3,114✔
1118
    isempty(wordrange) || return kwarg_completion, wordrange, !isempty(kwarg_completion)
1,809✔
1119

1120
    dotpos = something(findprev(isequal('.'), string, pos), 0)
2,251✔
1121
    startpos = nextind(string, something(findprev(in(non_identifier_chars), string, pos), 0))
2,634✔
1122
    # strip preceding ! operator
1123
    if (m = match(r"\G\!+", partial, startpos)) isa RegexMatch
1,473✔
1124
        startpos += length(m.match)
2✔
1125
    end
1126

1127
    name = string[max(startpos, dotpos+1):pos]
2,573✔
1128
    comp_keywords = !isempty(name) && startpos > dotpos
1,473✔
1129
    if afterusing(string, startpos)
1,473✔
1130
        # We're right after using or import. Let's look only for packages
1131
        # and modules we can reach from here
1132

1133
        # If there's no dot, we're in toplevel, so we should
1134
        # also search for packages
1135
        s = string[startpos:pos]
33✔
1136
        if dotpos <= startpos
18✔
1137
            for dir in Base.load_path()
17✔
1138
                if basename(dir) in Base.project_names && isfile(dir)
93✔
1139
                    append!(suggestions, project_deps_get_completion_candidates(s, dir))
1✔
1140
                end
1141
                isdir(dir) || continue
37✔
1142
                for pname in readdir(dir)
18✔
1143
                    if pname[1] != '.' && pname != "METADATA" &&
1,974✔
1144
                        pname != "REQUIRE" && startswith(pname, s)
1145
                        # Valid file paths are
1146
                        #   <Mod>.jl
1147
                        #   <Mod>/src/<Mod>.jl
1148
                        #   <Mod>.jl/src/<Mod>.jl
1149
                        if isfile(joinpath(dir, pname))
194✔
1150
                            endswith(pname, ".jl") && push!(suggestions,
×
1151
                                                            PackageCompletion(pname[1:prevind(pname, end-2)]))
1152
                        else
1153
                            mod_name = if endswith(pname, ".jl")
194✔
1154
                                pname[1:prevind(pname, end-2)]
×
1155
                            else
1156
                                pname
194✔
1157
                            end
1158
                            if isfile(joinpath(dir, pname, "src",
194✔
1159
                                               "$mod_name.jl"))
1160
                                push!(suggestions, PackageCompletion(mod_name))
193✔
1161
                            end
1162
                        end
1163
                    end
1164
                end
1,023✔
1165
            end
54✔
1166
        end
1167
        ffunc = (mod,x)->(Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getfield(mod, x), Module))
20,226✔
1168
        comp_keywords = false
×
1169
    end
1170

1171
    startpos == 0 && (pos = -1)
1,473✔
1172
    dotpos < startpos && (dotpos = startpos - 1)
1,473✔
1173
    return complete_identifiers!(suggestions, ffunc, context_module, string,
1,473✔
1174
        name, pos, dotpos, startpos, comp_keywords)
1175
end
1176

1177
function shell_completions(string, pos)
452✔
1178
    # First parse everything up to the current position
1179
    scs = string[1:pos]
904✔
1180
    local args, last_parse
×
1181
    try
452✔
1182
        args, last_parse = Base.shell_parse(scs, true)::Tuple{Expr,UnitRange{Int}}
469✔
1183
    catch
1184
        return Completion[], 0:-1, false
17✔
1185
    end
1186
    ex = args.args[end]::Expr
435✔
1187
    # Now look at the last thing we parsed
1188
    isempty(ex.args) && return Completion[], 0:-1, false
435✔
1189
    arg = ex.args[end]
435✔
1190
    if all(s -> isa(s, AbstractString), ex.args)
1,307✔
1191
        arg = arg::AbstractString
432✔
1192
        # Treat this as a path
1193

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

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

1203
        return complete_path(prefix, pos, use_envpath=use_envpath, shell_escape=true)
432✔
1204
    elseif isexpr(arg, :incomplete) || isexpr(arg, :error)
5✔
1205
        partial = scs[last_parse]
4✔
1206
        ret, range = completions(partial, lastindex(partial))
2✔
1207
        range = range .+ (first(last_parse) - 1)
2✔
1208
        return ret, range, true
2✔
1209
    end
1210
    return Completion[], 0:-1, false
1✔
1211
end
1212

1213
function UndefVarError_hint(io::IO, ex::UndefVarError)
4✔
1214
    var = ex.var
4✔
1215
    if var === :or
4✔
1216
        print(io, "\nsuggestion: Use `||` for short-circuiting boolean OR.")
×
1217
    elseif var === :and
4✔
1218
        print(io, "\nsuggestion: Use `&&` for short-circuiting boolean AND.")
×
1219
    elseif var === :help
4✔
1220
        println(io)
×
1221
        # Show friendly help message when user types help or help() and help is undefined
1222
        show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
×
1223
    elseif var === :quit
4✔
1224
        print(io, "\nsuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
×
1225
    end
1226
end
1227

1228
function __init__()
452✔
1229
    Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
452✔
1230
    COMPLETION_WORLD[] = Base.get_world_counter()
452✔
1231
    nothing
452✔
1232
end
1233

1234
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