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

JuliaLang / julia / #37666

04 Nov 2023 02:27AM UTC coverage: 87.924% (+0.09%) from 87.831%
#37666

push

local

web-flow
Simplify, 16bit PDP-11 isn't going to be supported (#45763)

PDP_ENDIAN isn't used.

Co-authored-by: Viral B. Shah <ViralBShah@users.noreply.github.com>

74550 of 84789 relevant lines covered (87.92%)

15319904.67 hits per line

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

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

3
module REPLCompletions
2✔
4

5
export completions, shell_completions, bslash_completions, completion_text
6

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

12
abstract type Completion end
13

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

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

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

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

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

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

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

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

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

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

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

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

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

72
# interface definition
73
function Base.getproperty(c::Completion, name::Symbol)
2,750✔
74
    if name === :text
8,456,915✔
75
        return getfield(c, :text)::String
4✔
76
    elseif name === :keyword
8,455,162✔
77
        return getfield(c, :keyword)::String
1,749✔
78
    elseif name === :path
8,448,547✔
79
        return getfield(c, :path)::String
57,519✔
80
    elseif name === :parent
8,448,547✔
81
        return getfield(c, :parent)::Module
×
82
    elseif name === :mod
8,707✔
83
        return getfield(c, :mod)::String
8,439,840✔
84
    elseif name === :package
8,707✔
85
        return getfield(c, :package)::String
2,288✔
86
    elseif name === :property
6,419✔
87
        return getfield(c, :property)::Symbol
203✔
88
    elseif name === :field
6,216✔
89
        return getfield(c, :field)::Symbol
30✔
90
    elseif name === :method
5,460✔
91
        return getfield(c, :method)::Method
2,063✔
92
    elseif name === :bslash
296✔
93
        return getfield(c, :bslash)::String
5,164✔
94
    elseif name === :text
296✔
95
        return getfield(c, :text)::String
×
96
    elseif name === :key
296✔
97
        return getfield(c, :key)::String
124✔
98
    elseif name === :kwarg
172✔
99
        return getfield(c, :kwarg)::String
89✔
100
    end
101
    return getfield(c, name)
83✔
102
end
103

104
_completion_text(c::TextCompletion) = c.text
4✔
105
_completion_text(c::KeywordCompletion) = c.keyword
1,749✔
106
_completion_text(c::KeyvalCompletion) = c.keyval
83✔
107
_completion_text(c::PathCompletion) = c.path
6,594✔
108
_completion_text(c::ModuleCompletion) = c.mod
8,439,840✔
109
_completion_text(c::PackageCompletion) = c.package
2,288✔
110
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
203✔
111
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
30✔
112
_completion_text(c::MethodCompletion) = repr(c.method)
714✔
113
_completion_text(c::BslashCompletion) = c.bslash
5,164✔
114
_completion_text(c::ShellCompletion) = c.text
×
115
_completion_text(c::DictCompletion) = c.key
124✔
116
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
89✔
117

118
completion_text(c) = _completion_text(c)::String
8,456,882✔
119

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

122
function completes_global(x, name)
×
123
    return startswith(x, name) && !('#' in x)
3,266,086✔
124
end
125

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

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

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

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

173
    suggestions = Completion[]
1,622✔
174
    if lookup_module
1,622✔
175
        # We will exclude the results that the user does not want, as well
176
        # as excluding Main.Main.Main, etc., because that's most likely not what
177
        # the user wants
178
        p = let mod=mod, modname=nameof(mod)
1,555✔
179
            (s::Symbol) -> !Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool && !(mod === Main && s === :MainInclude)
1,897,026✔
180
        end
181
        # Looking for a binding in a module
182
        if mod == context_module
1,555✔
183
            # Also look in modules we got through `using`
184
            mods = ccall(:jl_module_usings, Any, (Any,), context_module)::Vector
1,262✔
185
            for m in mods
1,262✔
186
                append!(suggestions, filtered_mod_names(p, m::Module, name))
11,678✔
187
            end
9,949✔
188
            append!(suggestions, filtered_mod_names(p, mod, name, true, true))
1,982✔
189
        else
190
            append!(suggestions, filtered_mod_names(p, mod, name, true, false))
1,848✔
191
        end
192
    elseif val !== nothing # looking for a property of an instance
67✔
193
        for property in propertynames(val, false)
45✔
194
            # TODO: support integer arguments (#36872)
195
            if property isa Symbol && startswith(string(property), name)
83✔
196
                push!(suggestions, PropertyCompletion(val, property))
77✔
197
            end
198
        end
83✔
199
    elseif field_completion_eligible(t)
22✔
200
        # Looking for a member of a type
201
        add_field_completions!(suggestions, name, t)
13✔
202
    end
203
    return suggestions
1,622✔
204
end
205

206
function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t))
15✔
207
    if isa(t, Union)
15✔
208
        add_field_completions!(suggestions, name, t.a)
2✔
209
        add_field_completions!(suggestions, name, t.b)
2✔
210
    else
211
        @assert isconcretetype(t)
15✔
212
        fields = fieldnames(t)
15✔
213
        for field in fields
15✔
214
            isa(field, Symbol) || continue # Tuple type has ::Int field name
18✔
215
            s = string(field)
18✔
216
            if startswith(s, name)
18✔
217
                push!(suggestions, FieldCompletion(t, field))
18✔
218
            end
219
        end
18✔
220
    end
221
end
222

223
const GENERIC_PROPERTYNAMES_METHOD = which(propertynames, (Any,))
224

225
function field_completion_eligible(@nospecialize t)
4✔
226
    if isa(t, Union)
26✔
227
        return field_completion_eligible(t.a) && field_completion_eligible(t.b)
2✔
228
    end
229
    isconcretetype(t) || return false
31✔
230
    # field completion is correct only when `getproperty` fallbacks to `getfield`
231
    match = Base._which(Tuple{typeof(propertynames),t}; raise=false)
17✔
232
    match === nothing && return false
17✔
233
    return match.method === GENERIC_PROPERTYNAMES_METHOD
17✔
234
end
235

236
function complete_from_list(T::Type, list::Vector{String}, s::Union{String,SubString{String}})
1,226✔
237
    r = searchsorted(list, s)
1,226✔
238
    i = first(r)
1,226✔
239
    n = length(list)
1,226✔
240
    while i <= n && startswith(list[i],s)
1,399✔
241
        r = first(r):i
173✔
242
        i += 1
173✔
243
    end
173✔
244
    Completion[T(kw) for kw in list[r]]
1,226✔
245
end
246

247
const sorted_keywords = [
248
    "abstract type", "baremodule", "begin", "break", "catch", "ccall",
249
    "const", "continue", "do", "else", "elseif", "end", "export",
250
    "finally", "for", "function", "global", "if", "import",
251
    "let", "local", "macro", "module", "mutable struct",
252
    "primitive type", "quote", "return", "struct",
253
    "try", "using", "while"]
254

255
complete_keyword(s::Union{String,SubString{String}}) = complete_from_list(KeywordCompletion, sorted_keywords, s)
544✔
256

257
const sorted_keyvals = ["false", "true"]
258

259
complete_keyval(s::Union{String,SubString{String}}) = complete_from_list(KeyvalCompletion, sorted_keyvals, s)
682✔
260

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

288
    matches = Set{String}()
410✔
289
    for file in files
410✔
290
        if startswith(file, prefix)
43,134✔
291
            p = joinpath(dir, file)
6,374✔
292
            is_dir = try isdir(p) catch; false end
19,124✔
293
            push!(matches, is_dir ? joinpath(file, "") : file)
6,374✔
294
        end
295
    end
43,134✔
296

297
    if use_envpath && length(dir) == 0
410✔
298
        # Look for files in PATH as well
299
        local pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":")
19✔
300

301
        for pathdir in pathdirs
19✔
302
            local actualpath
×
303
            try
246✔
304
                actualpath = realpath(pathdir)
294✔
305
            catch
306
                # Bash doesn't expect every folder in PATH to exist, so neither shall we
307
                continue
48✔
308
            end
309

310
            if actualpath != pathdir && in(actualpath,pathdirs)
374✔
311
                # Remove paths which (after resolving links) are in the env path twice.
312
                # Many distros eg. point /bin to /usr/bin but have both in the env path.
313
                continue
48✔
314
            end
315

316
            local filesinpath
×
317
            try
150✔
318
                filesinpath = readdir(pathdir)
152✔
319
            catch e
320
                # Bash allows dirs in PATH that can't be read, so we should as well.
321
                if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
2✔
322
                    continue
2✔
323
                else
324
                    # We only handle IOError and ArgumentError here
325
                    rethrow()
×
326
                end
327
            end
328

329
            for file in filesinpath
148✔
330
                # In a perfect world, we would filter on whether the file is executable
331
                # here, or even on whether the current user can execute the file in question.
332
                try
34,344✔
333
                    if startswith(file, prefix) && isfile(joinpath(pathdir, file))
34,344✔
334
                        push!(matches, file)
590✔
335
                    end
336
                catch e
337
                    # `isfile()` can throw in rare cases such as when probing a
338
                    # symlink that points to a file within a directory we do not
339
                    # have read access to.
340
                    if isa(e, Base.IOError)
1✔
341
                        continue
1✔
342
                    else
343
                        rethrow()
×
344
                    end
345
                end
346
            end
34,344✔
347
        end
246✔
348
    end
349

350
    function do_escape(s)
7,412✔
351
        return shell_escape ? replace(s, r"(\s|\\)" => s"\\\0") :
7,002✔
352
               string_escape ? escape_string(s, ('\"','$')) :
353
               s
354
    end
355

356
    matchList = Completion[PathCompletion(do_escape(s)) for s in matches]
7,002✔
357
    startpos = pos - lastindex(do_escape(prefix)) + 1
768✔
358
    # The pos - lastindex(prefix) + 1 is correct due to `lastindex(prefix)-lastindex(prefix)==0`,
359
    # hence we need to add one to get the first index. This is also correct when considering
360
    # pos, because pos is the `lastindex` a larger string which `endswith(path)==true`.
361
    return matchList, startpos:pos, !isempty(matchList)
410✔
362
end
363

364
function complete_expanduser(path::AbstractString, r)
138✔
365
    expanded =
138✔
366
        try expanduser(path)
139✔
367
        catch e
368
            e isa ArgumentError || rethrow()
1✔
369
            path
139✔
370
        end
371
    return Completion[PathCompletion(expanded)], r, path != expanded
138✔
372
end
373

374
# Returns a range that includes the method name in front of the first non
375
# closed start brace from the end of the string.
376
function find_start_brace(s::AbstractString; c_start='(', c_end=')')
5,358✔
377
    r = reverse(s)
2,679✔
378
    i = firstindex(r)
×
379
    braces = in_comment = 0
×
380
    in_single_quotes = in_double_quotes = in_back_ticks = false
×
381
    while i <= ncodeunits(r)
62,107✔
382
        c, i = iterate(r, i)
120,208✔
383
        if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '='
60,104✔
384
            c, i = iterate(r, i) # consume '='
8✔
385
            new_comments = 1
×
386
            # handle #=#=#=#, by counting =# pairs
387
            while i <= ncodeunits(r) && iterate(r, i)[1] == '#'
6✔
388
                c, i = iterate(r, i) # consume '#'
6✔
389
                iterate(r, i)[1] == '=' || break
3✔
390
                c, i = iterate(r, i) # consume '='
4✔
391
                new_comments += 1
2✔
392
            end
2✔
393
            if c == '='
4✔
394
                in_comment += new_comments
3✔
395
            else
396
                in_comment -= new_comments
5✔
397
            end
398
        elseif !in_single_quotes && !in_double_quotes && !in_back_ticks && in_comment == 0
60,100✔
399
            if c == c_start
58,362✔
400
                braces += 1
930✔
401
            elseif c == c_end
57,432✔
402
                braces -= 1
254✔
403
            elseif c == '\''
57,178✔
404
                in_single_quotes = true
16✔
405
            elseif c == '"'
57,162✔
406
                in_double_quotes = true
271✔
407
            elseif c == '`'
56,891✔
408
                in_back_ticks = true
58,362✔
409
            end
410
        else
411
            if in_single_quotes &&
1,738✔
412
                c == '\'' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
413
                in_single_quotes = false
15✔
414
            elseif in_double_quotes &&
1,723✔
415
                c == '"' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
416
                in_double_quotes = false
228✔
417
            elseif in_back_ticks &&
1,495✔
418
                c == '`' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\'
419
                in_back_ticks = false
7✔
420
            elseif in_comment > 0 &&
1,488✔
421
                c == '=' && i <= ncodeunits(r) && iterate(r, i)[1] == '#'
422
                # handle =#=#=#=, by counting #= pairs
423
                c, i = iterate(r, i) # consume '#'
6✔
424
                old_comments = 1
×
425
                while i <= ncodeunits(r) && iterate(r, i)[1] == '='
4✔
426
                    c, i = iterate(r, i) # consume '='
4✔
427
                    iterate(r, i)[1] == '#' || break
2✔
428
                    c, i = iterate(r, i) # consume '#'
2✔
429
                    old_comments += 1
1✔
430
                end
1✔
431
                if c == '#'
3✔
432
                    in_comment -= old_comments
2✔
433
                else
434
                    in_comment += old_comments
1✔
435
                end
436
            end
437
        end
438
        braces == 1 && break
60,104✔
439
    end
59,428✔
440
    braces != 1 && return 0:-1, -1
2,679✔
441
    method_name_end = reverseind(s, i)
1,344✔
442
    startind = nextind(s, something(findprev(in(non_identifier_chars), s, method_name_end), 0))::Int
1,042✔
443
    return (startind:lastindex(s), method_name_end)
676✔
444
end
445

446
struct REPLInterpreterCache
447
    dict::IdDict{MethodInstance,CodeInstance}
1✔
448
end
449
REPLInterpreterCache() = REPLInterpreterCache(IdDict{MethodInstance,CodeInstance}())
1✔
450
const REPL_INTERPRETER_CACHE = REPLInterpreterCache()
451

452
function get_code_cache()
×
453
    # XXX Avoid storing analysis results into the cache that persists across precompilation,
454
    #     as [sys|pkg]image currently doesn't support serializing externally created `CodeInstance`.
455
    #     Otherwise, `CodeInstance`s created by `REPLInterpreter`, that are much less optimized
456
    #     that those produced by `NativeInterpreter`, will leak into the native code cache,
457
    #     potentially causing runtime slowdown.
458
    #     (see https://github.com/JuliaLang/julia/issues/48453).
459
    if Base.generating_output()
494✔
460
        return REPLInterpreterCache()
×
461
    else
462
        return REPL_INTERPRETER_CACHE
494✔
463
    end
464
end
465

466
struct REPLInterpreter <: CC.AbstractInterpreter
467
    limit_aggressive_inference::Bool
468
    world::UInt
469
    inf_params::CC.InferenceParams
470
    opt_params::CC.OptimizationParams
471
    inf_cache::Vector{CC.InferenceResult}
472
    code_cache::REPLInterpreterCache
473
    function REPLInterpreter(limit_aggressive_inference::Bool=false;
988✔
474
                             world::UInt = Base.get_world_counter(),
475
                             inf_params::CC.InferenceParams = CC.InferenceParams(;
476
                                 aggressive_constant_propagation=true,
477
                                 unoptimize_throw_blocks=false),
478
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
479
                             inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[],
480
                             code_cache::REPLInterpreterCache = get_code_cache())
481
        return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache, code_cache)
494✔
482
    end
483
end
484
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
355,355✔
485
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
×
486
CC.get_world_counter(interp::REPLInterpreter) = interp.world
92,230✔
487
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
71,388✔
488
CC.code_cache(interp::REPLInterpreter) = CC.WorldView(interp.code_cache, CC.WorldRange(interp.world))
68,325✔
489
CC.get(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default)
117,212✔
490
CC.getindex(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi)
×
491
CC.haskey(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi)
3,802✔
492
CC.setindex!(wvc::CC.WorldView{REPLInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi)
3,802✔
493

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

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

500
# `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions
501
# for lines like `Mod.a.|` (where `|` is the cursor position).
502
# Aggressive binding resolution poses challenges for the inference cache validation
503
# (until https://github.com/JuliaLang/julia/issues/40399 is implemented).
504
# To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding
505
# resolution for top-level frame representing REPL input code and for child uncached frames
506
# that are constant propagated from the top-level frame ("repl-frame"s). This works, even if
507
# those global bindings are not constant and may be mutated in the future, since:
508
# a.) "repl-frame"s are never cached, and
509
# b.) mutable values are never observed by any cached frames.
510
#
511
# `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within
512
# "repl-frame" to provide reasonable completions for lines like `Ref(Some(42))[].|`.
513
# Aggressive concrete evaluation allows us to get accurate type information about complex
514
# expressions that otherwise can not be constant folded, in a safe way, i.e. it still
515
# doesn't evaluate effectful expressions like `pop!(xs)`.
516
# Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't
517
# present any cache validation issues because "repl-frame" is never cached.
518

519
# `REPLInterpreter` is specifically used by `repl_eval_ex`, where all top-level frames are
520
# `repl_frame` always. However, this assumption wouldn't stand if `REPLInterpreter` were to
521
# be employed, for instance, by `typeinf_ext_toplevel`.
522
is_repl_frame(sv::CC.InferenceState) = sv.linfo.def isa Module && sv.cache_mode === :no
362✔
523

524
function is_call_graph_uncached(sv::CC.InferenceState)
423,001✔
525
    sv.cache_mode === :global && return false
587,829✔
526
    parent = sv.parent
431,097✔
527
    parent === nothing && return true
431,097✔
528
    return is_call_graph_uncached(parent::CC.InferenceState)
423,001✔
529
end
530

531
# aggressive global binding resolution within `repl_frame`
532
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef,
×
533
                                    sv::CC.InferenceState)
534
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv))
103,506✔
535
        if CC.isdefined_globalref(g)
5,472✔
536
            return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
5,467✔
537
        end
538
        return Union{}
5✔
539
    end
540
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef,
97,910✔
541
                                              sv::CC.InferenceState)
542
end
543

544
function is_repl_frame_getproperty(sv::CC.InferenceState)
×
545
    def = sv.linfo.def
×
546
    def isa Method || return false
×
547
    def.name === :getproperty || return false
×
548
    sv.cached && return false
×
549
    return is_repl_frame(sv.parent)
×
550
end
551

552
# aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame`
553
function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f),
30,622✔
554
                              argtypes::Vector{Any}, sv::CC.InferenceState)
555
    if f === Core.getglobal && (interp.limit_aggressive_inference ? is_repl_frame_getproperty(sv) : is_call_graph_uncached(sv))
30,622✔
556
        if length(argtypes) == 2
48✔
557
            a1, a2 = argtypes
48✔
558
            if isa(a1, Const) && isa(a2, Const)
48✔
559
                a1val, a2val = a1.val, a2.val
48✔
560
                if isa(a1val, Module) && isa(a2val, Symbol)
48✔
561
                    g = GlobalRef(a1val, a2val)
48✔
562
                    if CC.isdefined_globalref(g)
48✔
563
                        return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
48✔
564
                    end
565
                    return Union{}
×
566
                end
567
            end
568
        end
569
    end
570
    return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any,
30,574✔
571
                                        argtypes::Vector{Any}, sv::CC.InferenceState)
572
end
573

574
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
575
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
61,560✔
576
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
577
                                   sv::CC.InferenceState)
578
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv))
61,613✔
579
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
2,584✔
580
        result = CC.MethodCallResult(result.rt, result.edgecycle, result.edgelimited,
5,166✔
581
                                     result.edge, neweffects)
582
    end
583
    ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
123,120✔
584
                                            result::CC.MethodCallResult, arginfo::CC.ArgInfo,
585
                                            sv::CC.InferenceState)
586
    if ret === :semi_concrete_eval
61,560✔
587
        # while the base eligibility check probably won't permit semi-concrete evaluation
588
        # for `REPLInterpreter` (given it completely turns off optimization),
589
        # this ensures we don't inadvertently enter irinterp
590
        ret = :none
×
591
    end
592
    return ret
61,560✔
593
end
594

595
# allow constant propagation for mutable constants
596
function CC.const_prop_argument_heuristic(interp::REPLInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
×
597
    if !interp.limit_aggressive_inference
60,927✔
598
        any(@nospecialize(a)->isa(a, Const), arginfo.argtypes) && return true # even if mutable
121,849✔
599
    end
600
    return @invoke CC.const_prop_argument_heuristic(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
97✔
601
end
602

603
function resolve_toplevel_symbols!(src::Core.CodeInfo, mod::Module)
×
604
    @ccall jl_resolve_globals_in_ir(
494✔
605
        #=jl_array_t *stmts=# src.code::Any,
606
        #=jl_module_t *m=# mod::Any,
607
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
608
        #=int binding_effects=# 0::Int)::Cvoid
609
    return src
×
610
end
611

612
# lower `ex` and run type inference on the resulting top-level expression
613
function repl_eval_ex(@nospecialize(ex), context_module::Module; limit_aggressive_inference::Bool=false)
2,612✔
614
    if (isexpr(ex, :toplevel) || isexpr(ex, :tuple)) && !isempty(ex.args)
2,611✔
615
        # get the inference result for the last expression
616
        ex = ex.args[end]
3✔
617
    end
618
    lwr = try
1,306✔
619
        Meta.lower(context_module, ex)
1,311✔
620
    catch # macro expansion failed, etc.
621
        return nothing
5✔
622
    end
623
    if lwr isa Symbol
1,301✔
624
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
675✔
625
    end
626
    lwr isa Expr || return Const(lwr) # `ex` is literal
728✔
627
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
554✔
628
    src = lwr.args[1]::Core.CodeInfo
494✔
629

630
    # construct top-level `MethodInstance`
631
    mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ());
494✔
632
    mi.specTypes = Tuple{}
494✔
633

634
    mi.def = context_module
494✔
635
    resolve_toplevel_symbols!(src, context_module)
494✔
636
    @atomic mi.uninferred = src
494✔
637

638
    interp = REPLInterpreter(limit_aggressive_inference)
988✔
639
    result = CC.InferenceResult(mi)
494✔
640
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)
494✔
641

642
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
643
    #      potential invalidations of `Core.Compiler` methods.
644
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
494✔
645

646
    result = frame.result.result
494✔
647
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
494✔
648
    return result
486✔
649
end
650

651
# `COMPLETION_WORLD[]` will be initialized within `__init__`
652
# (to allow us to potentially remove REPL from the sysimage in the future).
653
# Note that inference from the `code_typed` call below will use the current world age
654
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
655
# when the given world age is higher than the current one.
656
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
657

658
# Generate code cache for `REPLInterpreter` now:
659
# This code cache will be available at the world of `COMPLETION_WORLD`,
660
# assuming no invalidation will happen before initializing REPL.
661
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
662
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
663

664
# Method completion on function call expression that look like :(max(1))
665
MAX_METHOD_COMPLETIONS::Int = 40
666
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
310✔
667
    funct = repl_eval_ex(ex_org.args[1], context_module)
310✔
668
    funct === nothing && return 2, nothing, [], Set{Symbol}()
310✔
669
    funct = CC.widenconst(funct)
306✔
670
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
306✔
671
    return kwargs_flag, funct, args_ex, kwargs_ex
306✔
672
end
673

674
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false)
2✔
675
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
140✔
676
    out = Completion[]
139✔
677
    kwargs_flag == 2 && return out # one of the kwargs is invalid
139✔
678
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
127✔
679
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
184✔
680
    return out
127✔
681
end
682

683
MAX_ANY_METHOD_COMPLETIONS::Int = 10
684
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
13✔
685
    out = Completion[]
13✔
686
    args_ex, kwargs_ex, kwargs_flag = try
13✔
687
        # this may throw, since we set default_any to false
688
        complete_methods_args(ex_org, context_module, false, false)
13✔
689
    catch ex
690
        ex isa ArgumentError || rethrow()
×
691
        return out
13✔
692
    end
693
    kwargs_flag == 2 && return out # one of the kwargs is invalid
13✔
694

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

699
    seen = Base.IdSet()
13✔
700
    for name in names(callee_module; all=true)
13✔
701
        if !Base.isdeprecated(callee_module, name) && isdefined(callee_module, name) && !startswith(string(name), '#')
1,326✔
702
            func = getfield(callee_module, name)
650✔
703
            if !isa(func, Module)
650✔
704
                funct = Core.Typeof(func)
1,183✔
705
                if !in(funct, seen)
637✔
706
                    push!(seen, funct)
611✔
707
                    complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
611✔
708
                end
709
            elseif callee_module === Main && isa(func, Module)
26✔
710
                callee_module2 = func
×
711
                for name in names(callee_module2)
×
712
                    if !Base.isdeprecated(callee_module2, name) && isdefined(callee_module2, name) && !startswith(string(name), '#')
×
713
                        func = getfield(callee_module, name)
×
714
                        if !isa(func, Module)
×
715
                            funct = Core.Typeof(func)
×
716
                            if !in(funct, seen)
×
717
                                push!(seen, funct)
×
718
                                complete_methods!(out, funct, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
×
719
                            end
720
                        end
721
                    end
722
                end
×
723
            end
724
        end
725
    end
1,326✔
726

727
    if !shift
13✔
728
        # Filter out methods where all arguments are `Any`
729
        filter!(out) do c
2✔
730
            isa(c, TextCompletion) && return false
12✔
731
            isa(c, MethodCompletion) || return true
12✔
732
            sig = Base.unwrap_unionall(c.method.sig)::DataType
12✔
733
            return !all(T -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
20✔
734
        end
735
    end
736

737
    return out
13✔
738
end
739

740
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
×
741
    n = isexpr(x, :kw) ? x.args[1] : x
55✔
742
    if n isa Symbol
55✔
743
        push!(kwargs_ex, n)
41✔
744
        return kwargs_flag
41✔
745
    end
746
    possible_splat && isexpr(x, :...) && return kwargs_flag
14✔
747
    return 2 # The kwarg is invalid
10✔
748
end
749

750
function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, default_any::Bool, broadcasting::Bool)
319✔
751
    args_ex = Any[]
319✔
752
    kwargs_ex = Symbol[]
319✔
753
    kwargs_flag = 0
×
754
    # kwargs_flag is:
755
    # * 0 if there is no semicolon and no invalid kwarg
756
    # * 1 if there is a semicolon and no invalid kwarg
757
    # * 2 if there are two semicolons or more, or if some kwarg is invalid, which
758
    #        means that it is not of the form "bar=foo", "bar" or "bar..."
759
    for i in (1+!broadcasting):length(funargs)
483✔
760
        ex = funargs[i]
295✔
761
        if isexpr(ex, :parameters)
460✔
762
            kwargs_flag = ifelse(kwargs_flag == 0, 1, 2) # there should be at most one :parameters
59✔
763
            for x in ex.args
59✔
764
                kwargs_flag = detect_invalid_kwarg!(kwargs_ex, x, kwargs_flag, true)
46✔
765
            end
33✔
766
        elseif isexpr(ex, :kw)
401✔
767
            kwargs_flag = detect_invalid_kwarg!(kwargs_ex, ex, kwargs_flag, false)
22✔
768
        else
769
            if broadcasting
214✔
770
                # handle broadcasting, but only handle number of arguments instead of
771
                # argument types
772
                push!(args_ex, Any)
5✔
773
            else
774
                argt = repl_eval_ex(ex, context_module)
209✔
775
                if argt !== nothing
209✔
776
                    push!(args_ex, CC.widenconst(argt))
183✔
777
                elseif default_any
26✔
778
                    push!(args_ex, Any)
26✔
779
                else
780
                    throw(ArgumentError("argument not found"))
×
781
                end
782
            end
783
        end
784
    end
426✔
785
    return args_ex, Set{Symbol}(kwargs_ex), kwargs_flag
319✔
786
end
787

788
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
1,800✔
789

790
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
×
791
    if allow_broadcasting && is_broadcasting_expr(ex)
306✔
792
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
6✔
793
    end
794
    return detect_args_kwargs(ex.args, context_module, default_any, false)
313✔
795
end
796

797
function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool)
907✔
798
    # Input types and number of arguments
799
    t_in = Tuple{funct, args_ex...}
907✔
800
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
907✔
801
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
802
    if !isa(m, Vector)
907✔
803
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
4✔
804
        return
4✔
805
    end
806
    for match in m
903✔
807
        # TODO: if kwargs_ex, filter out methods without kwargs?
808
        push!(out, MethodCompletion(match.spec_types, match.method))
2,061✔
809
    end
2,061✔
810
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
811
end
812

813
include("latex_symbols.jl")
814
include("emoji_symbols.jl")
815

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

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

828
# Aux function to detect whether we're right after a
829
# using or import keyword
830
function afterusing(string::String, startpos::Int)
1,490✔
831
    (isempty(string) || startpos == 0) && return false
1,490✔
832
    str = string[1:prevind(string,startpos)]
2,658✔
833
    isempty(str) && return false
1,487✔
834
    rstr = reverse(str)
1,171✔
835
    r = findfirst(r"\s(gnisu|tropmi)\b", rstr)
1,171✔
836
    r === nothing && return false
1,171✔
837
    fr = reverseind(str, last(r))
60✔
838
    return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end])
30✔
839
end
840

841
function close_path_completion(str, startpos, r, paths, pos)
135✔
842
    length(paths) == 1 || return false  # Only close if there's a single choice...
254✔
843
    _path = str[startpos:prevind(str, first(r))] * (paths[1]::PathCompletion).path
16✔
844
    path = expanduser(unescape_string(replace(_path, "\\\$"=>"\$", "\\\""=>"\"")))
32✔
845
    # ...except if it's a directory...
846
    try
16✔
847
        isdir(path)
17✔
848
    catch e
849
        e isa Base.IOError || rethrow() # `path` cannot be determined to be a file
17✔
850
    end && return false
851
    # ...and except if there's already a " at the cursor.
852
    return lastindex(str) <= pos || str[nextind(str, pos)] != '"'
6✔
853
end
854

855
function bslash_completions(string::String, pos::Int)
1,905✔
856
    slashpos = something(findprev(isequal('\\'), string, pos), 0)
1,955✔
857
    if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos &&
1,933✔
858
        !(1 < slashpos && (string[prevind(string, slashpos)]=='\\')))
859
        # latex / emoji symbol substitution
860
        s = string[slashpos:pos]
96✔
861
        latex = get(latex_symbols, s, "")
69✔
862
        if !isempty(latex) # complete an exact match
48✔
863
            return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true))
21✔
864
        elseif occursin(subscript_regex, s)
27✔
865
            sub = map(c -> subscripts[c], s[3:end])
8✔
866
            return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true))
1✔
867
        elseif occursin(superscript_regex, s)
26✔
868
            sup = map(c -> superscripts[c], s[3:end])
8✔
869
            return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true))
1✔
870
        end
871
        emoji = get(emoji_symbols, s, "")
27✔
872
        if !isempty(emoji)
25✔
873
            return (true, (Completion[BslashCompletion(emoji)], slashpos:pos, true))
2✔
874
        end
875
        # return possible matches; these cannot be mixed with regular
876
        # Julian completions as only latex / emoji symbols contain the leading \
877
        if startswith(s, "\\:") # emoji
23✔
878
            namelist = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols))
1,186✔
879
        else # latex
880
            namelist = Iterators.filter(k -> startswith(k, s), keys(latex_symbols))
55,330✔
881
        end
882
        return (true, (Completion[BslashCompletion(name) for name in sort!(collect(namelist))], slashpos:pos, true))
23✔
883
    end
884
    return (false, (Completion[], 0:-1, false))
1,857✔
885
end
886

887
function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Main)
2,039✔
888
    if tag === :string
2,039✔
889
        str_close = str*"\""
158✔
890
    elseif tag === :cmd
1,880✔
891
        str_close = str*"`"
5✔
892
    else
893
        str_close = str
×
894
    end
895
    frange, end_of_identifier = find_start_brace(str_close, c_start='[', c_end=']')
2,038✔
896
    isempty(frange) && return (nothing, nothing, nothing)
2,038✔
897
    objstr = str[1:end_of_identifier]
212✔
898
    objex = Meta.parse(objstr, raise=false, depwarn=false)
106✔
899
    objt = repl_eval_ex(objex, context_module)
106✔
900
    isa(objt, Core.Const) || return (nothing, nothing, nothing)
130✔
901
    obj = objt.val
82✔
902
    isa(obj, AbstractDict) || return (nothing, nothing, nothing)
83✔
903
    length(obj)::Int < 1_000_000 || return (nothing, nothing, nothing)
81✔
904
    begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [
156✔
905
                             lastindex(str)+1)
906
    return (obj, str[begin_of_key:end], begin_of_key)
81✔
907
end
908

909
# This needs to be a separate non-inlined function, see #19441
910
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
80✔
911
    matches = String[]
80✔
912
    for key in keys(identifier)
122✔
913
        rkey = repr(key)
933✔
914
        startswith(rkey,partial_key) && push!(matches,rkey)
933✔
915
    end
1,410✔
916
    return matches
80✔
917
end
918

919
# Identify an argument being completed in a method call. If the argument is empty, method
920
# suggestions will be provided instead of argument completions.
921
function identify_possible_method_completion(partial, last_idx)
2,622✔
922
    fail = 0:-1, Expr(:nothing), 0:-1, 0
2,622✔
923

924
    # First, check that the last punctuation is either ',', ';' or '('
925
    idx_last_punct = something(findprev(x -> ispunct(x) && x != '_' && x != '!', partial, last_idx), 0)::Int
18,587✔
926
    idx_last_punct == 0 && return fail
2,622✔
927
    last_punct = partial[idx_last_punct]
4,258✔
928
    last_punct == ',' || last_punct == ';' || last_punct == '(' || return fail
4,048✔
929

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

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

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

944
    # `wordrange` is the position of the last argument to complete
945
    wordrange = nextind(partial, before_last_word_start):last_idx
620✔
946
    return frange, ex, wordrange, method_name_end
482✔
947
end
948

949
# Provide completion for keyword arguments in function calls
950
function complete_keyword_argument(partial, last_idx, context_module)
1,659✔
951
    frange, ex, wordrange, = identify_possible_method_completion(partial, last_idx)
1,659✔
952
    fail = Completion[], 0:-1, frange
1,659✔
953
    ex.head === :call || is_broadcasting_expr(ex) || return fail
3,150✔
954

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

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

963
    # For each method corresponding to the function call, provide completion suggestions
964
    # for each keyword that starts like the last word and that is not already used
965
    # previously in the expression. The corresponding suggestion is "kwname=".
966
    # If the keyword corresponds to an existing name, also include "kwname" as a suggestion
967
    # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)".
968
    last_word = partial[wordrange] # the word to complete
338✔
969
    kwargs = Set{String}()
169✔
970
    for m in methods
169✔
971
        m::MethodCompletion
1,337✔
972
        possible_kwargs = Base.kwarg_decl(m.method)
1,337✔
973
        current_kwarg_candidates = String[]
1,337✔
974
        for _kw in possible_kwargs
1,337✔
975
            kw = String(_kw)
472✔
976
            if !endswith(kw, "...") && startswith(kw, last_word) && _kw ∉ kwargs_ex
505✔
977
                push!(current_kwarg_candidates, kw)
67✔
978
            end
979
        end
472✔
980
        union!(kwargs, current_kwarg_candidates)
1,337✔
981
    end
1,337✔
982

983
    suggestions = Completion[KeywordArgumentCompletion(kwarg) for kwarg in kwargs]
216✔
984

985
    # Only add these if not in kwarg space. i.e. not in `foo(; `
986
    if kwargs_flag == 0
169✔
987
        append!(suggestions, complete_symbol(nothing, last_word, Returns(true), context_module))
230✔
988
        append!(suggestions, complete_keyval(last_word))
139✔
989
    end
990

991
    return sort!(suggestions, by=completion_text), wordrange
169✔
992
end
993

994
function project_deps_get_completion_candidates(pkgstarts::String, project_file::String)
1✔
995
    loading_candidates = String[]
1✔
996
    d = Base.parsed_toml(project_file)
1✔
997
    pkg = get(d, "name", nothing)::Union{String, Nothing}
2✔
998
    if pkg !== nothing && startswith(pkg, pkgstarts)
1✔
999
        push!(loading_candidates, pkg)
1✔
1000
    end
1001
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
2✔
1002
    if deps !== nothing
1✔
1003
        for (pkg, _) in deps
2✔
1004
            startswith(pkg, pkgstarts) && push!(loading_candidates, pkg)
1✔
1005
        end
1✔
1006
    end
1007
    return Completion[PackageCompletion(name) for name in loading_candidates]
1✔
1008
end
1009

1010
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,493✔
1011
    ex = nothing
3✔
1012
    if comp_keywords
1,493✔
1013
        append!(suggestions, complete_keyword(name))
643✔
1014
        append!(suggestions, complete_keyval(name))
558✔
1015
    end
1016
    if dotpos > 1 && string[dotpos] == '.'
2,622✔
1017
        s = string[1:prevind(string, dotpos)]
1,358✔
1018
        # First see if the whole string up to `pos` is a valid expression. If so, use it.
1019
        ex = Meta.parse(s, raise=false, depwarn=false)
679✔
1020
        if isexpr(ex, :incomplete)
725✔
1021
            s = string[startpos:pos]
1,090✔
1022
            # Heuristic to find the start of the expression. TODO: This would be better
1023
            # done with a proper error-recovering parser.
1024
            if 0 < startpos <= lastindex(string) && string[startpos] == '.'
1,090✔
1025
                i = prevind(string, startpos)
1✔
1026
                while 0 < i
1✔
1027
                    c = string[i]
2✔
1028
                    if c in (')', ']')
4✔
1029
                        if c == ')'
×
1030
                            c_start = '('
×
1031
                            c_end = ')'
×
1032
                        elseif c == ']'
×
1033
                            c_start = '['
×
1034
                            c_end = ']'
×
1035
                        end
1036
                        frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end)
×
1037
                        isempty(frange) && break # unbalanced parens
×
1038
                        startpos = first(frange)
×
1039
                        i = prevind(string, startpos)
×
1040
                    elseif c in ('\'', '\"', '\`')
6✔
1041
                        s = "$c$c"*string[startpos:pos]
×
1042
                        break
×
1043
                    else
1044
                        break
×
1045
                    end
1046
                    s = string[startpos:pos]
×
1047
                end
×
1048
            end
1049
            if something(findlast(in(non_identifier_chars), s), 0) < something(findlast(isequal('.'), s), 0)
1,090✔
1050
                lookup_name, name = rsplit(s, ".", limit=2)
545✔
1051
                name = String(name)
545✔
1052

1053
                ex = Meta.parse(lookup_name, raise=false, depwarn=false)
545✔
1054
            end
1055
            isexpr(ex, :incomplete) && (ex = nothing)
873✔
1056
        elseif isexpr(ex, :call) && length(ex.args) > 1
180✔
1057
            isinfix = s[end] != ')'
37✔
1058
            # A complete call expression that does not finish with ')' is an infix call.
1059
            if !isinfix
19✔
1060
                # Handle infix call argument completion of the form bar + foo(qux).
1061
                frange, end_of_identifier = find_start_brace(@view s[1:prevind(s, end)])
14✔
1062
                isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == ex.args[end]
14✔
1063
            end
1064
            if isinfix
19✔
1065
                ex = ex.args[end]
8✔
1066
            end
1067
        elseif isexpr(ex, :macrocall) && length(ex.args) > 1
161✔
1068
            # allow symbol completions within potentially incomplete macrocalls
1069
            if s[end] ≠ '`' && s[end] ≠ ')'
43✔
1070
                ex = ex.args[end]
12✔
1071
            end
1072
        end
1073
    end
1074
    append!(suggestions, complete_symbol(ex, name, ffunc, context_module))
2,807✔
1075
    return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true
1,493✔
1076
end
1077

1078
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true)
2,414✔
1079
    # First parse everything up to the current position
1080
    partial = string[1:pos]
4,463✔
1081
    inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false))
2,050✔
1082

1083
    # ?(x, y)TAB lists methods you can call with these objects
1084
    # ?(x, y TAB lists methods that take these objects as the first two arguments
1085
    # MyModule.?(x, y)TAB restricts the search to names in MyModule
1086
    rexm = match(r"(\w+\.|)\?\((.*)$", partial)
2,050✔
1087
    if rexm !== nothing
2,050✔
1088
        # Get the module scope
1089
        if isempty(rexm.captures[1])
26✔
1090
            callee_module = context_module
×
1091
        else
1092
            modname = Symbol(rexm.captures[1][1:end-1])
13✔
1093
            if isdefined(context_module, modname)
13✔
1094
                callee_module = getfield(context_module, modname)
13✔
1095
                if !isa(callee_module, Module)
13✔
1096
                    callee_module = context_module
13✔
1097
                end
1098
            else
1099
                callee_module = context_module
×
1100
            end
1101
        end
1102
        moreargs = !endswith(rexm.captures[2], ')')
14✔
1103
        callstr = "_(" * rexm.captures[2]
13✔
1104
        if moreargs
13✔
1105
            callstr *= ')'
7✔
1106
        end
1107
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
13✔
1108
        if isa(ex_org, Expr)
13✔
1109
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false
13✔
1110
        end
1111
    end
1112

1113
    # if completing a key in a Dict
1114
    identifier, partial_key, loc = dict_identifier_key(partial, inc_tag, context_module)
2,117✔
1115
    if identifier !== nothing
2,037✔
1116
        matches = find_dict_matches(identifier, partial_key)
80✔
1117
        length(matches)==1 && (lastindex(string) <= pos || string[nextind(string,pos)] != ']') && (matches[1]*=']')
80✔
1118
        length(matches)>0 && return Completion[DictCompletion(identifier, match) for match in sort!(matches)], loc::Int:pos, true
80✔
1119
    end
1120

1121
    ffunc = Returns(true)
×
1122
    suggestions = Completion[]
1,973✔
1123

1124
    # Check if this is a var"" string macro that should be completed like
1125
    # an identifier rather than a string.
1126
    # TODO: It would be nice for the parser to give us more information here
1127
    # so that we can lookup the macro by identity rather than pattern matching
1128
    # its invocation.
1129
    varrange = findprev("var\"", string, pos)
1,973✔
1130

1131
    if varrange !== nothing
1,973✔
1132
        ok, ret = bslash_completions(string, pos)
3✔
1133
        ok && return ret
3✔
1134
        startpos = first(varrange) + 4
3✔
1135
        dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0)
5✔
1136
        return complete_identifiers!(Completion[], ffunc, context_module, string,
3✔
1137
            string[startpos:pos], pos, dotpos, startpos)
1138
    elseif inc_tag === :cmd
1,970✔
1139
        m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial))
1✔
1140
        startpos = nextind(partial, reverseind(partial, m.offset))
2✔
1141
        r = startpos:pos
1✔
1142

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

1148
        paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos,
2✔
1149
                                          shell_escape=true)
1150

1151
        return sort!(paths, by=p->p.path), r, success
1✔
1152
    elseif inc_tag === :string
1,969✔
1153
        # Find first non-escaped quote
1154
        m = match(r"\"(?!\\)", reverse(partial))
137✔
1155
        startpos = nextind(partial, reverseind(partial, m.offset))
274✔
1156
        r = startpos:pos
159✔
1157

1158
        expanded = complete_expanduser(string[r], r)
252✔
1159
        expanded[3] && return expanded  # If user expansion available, return it
137✔
1160

1161
        path_prefix = try
135✔
1162
            unescape_string(replace(string[r], "\\\$"=>"\$", "\\\""=>"\""))
248✔
1163
        catch
1164
            nothing
135✔
1165
        end
1166
        if !isnothing(path_prefix)
270✔
1167
            paths, r, success = complete_path(path_prefix, pos, string_escape=true)
135✔
1168

1169
            if close_path_completion(string, startpos, r, paths, pos)
135✔
1170
                paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"")
5✔
1171
            end
1172

1173
            # Fallthrough allowed so that Latex symbols can be completed in strings
1174
            success && return sort!(paths, by=p->p.path), r, success
51,039✔
1175
        end
1176
    end
1177

1178
    ok, ret = bslash_completions(string, pos)
1,897✔
1179
    ok && return ret
1,897✔
1180

1181
    # Make sure that only bslash_completions is working on strings
1182
    inc_tag === :string && return Completion[], 0:-1, false
1,854✔
1183
    if inc_tag === :other
1,798✔
1184
        frange, ex, wordrange, method_name_end = identify_possible_method_completion(partial, pos)
963✔
1185
        if last(frange) != -1 && all(isspace, @view partial[wordrange]) # no last argument to complete
963✔
1186
            if ex.head === :call
138✔
1187
                return complete_methods(ex, context_module, shift), first(frange):method_name_end, false
135✔
1188
            elseif is_broadcasting_expr(ex)
3✔
1189
                return complete_methods(ex, context_module, shift), first(frange):(method_name_end - 1), false
3✔
1190
            end
1191
        end
1192
    elseif inc_tag === :comment
835✔
1193
        return Completion[], 0:-1, false
1✔
1194
    end
1195

1196
    # Check whether we can complete a keyword argument in a function call
1197
    kwarg_completion, wordrange = complete_keyword_argument(partial, pos, context_module)
3,149✔
1198
    isempty(wordrange) || return kwarg_completion, wordrange, !isempty(kwarg_completion)
1,828✔
1199

1200
    dotpos = something(findprev(isequal('.'), string, pos), 0)
2,292✔
1201
    startpos = nextind(string, something(findprev(in(non_identifier_chars), string, pos), 0))
2,659✔
1202
    # strip preceding ! operator
1203
    if (m = match(r"\G\!+", partial, startpos)) isa RegexMatch
1,490✔
1204
        startpos += length(m.match)
2✔
1205
    end
1206

1207
    name = string[max(startpos, dotpos+1):pos]
2,596✔
1208
    comp_keywords = !isempty(name) && startpos > dotpos
1,490✔
1209
    if afterusing(string, startpos)
1,490✔
1210
        # We're right after using or import. Let's look only for packages
1211
        # and modules we can reach from here
1212

1213
        # If there's no dot, we're in toplevel, so we should
1214
        # also search for packages
1215
        s = string[startpos:pos]
33✔
1216
        if dotpos <= startpos
18✔
1217
            for dir in Base.load_path()
17✔
1218
                if basename(dir) in Base.project_names && isfile(dir)
93✔
1219
                    append!(suggestions, project_deps_get_completion_candidates(s, dir))
2✔
1220
                end
1221
                isdir(dir) || continue
37✔
1222
                for pname in readdir(dir)
18✔
1223
                    if pname[1] != '.' && pname != "METADATA" &&
2,044✔
1224
                        pname != "REQUIRE" && startswith(pname, s)
1225
                        # Valid file paths are
1226
                        #   <Mod>.jl
1227
                        #   <Mod>/src/<Mod>.jl
1228
                        #   <Mod>.jl/src/<Mod>.jl
1229
                        if isfile(joinpath(dir, pname))
197✔
1230
                            endswith(pname, ".jl") && push!(suggestions,
×
1231
                                                            PackageCompletion(pname[1:prevind(pname, end-2)]))
1232
                        else
1233
                            mod_name = if endswith(pname, ".jl")
197✔
1234
                                pname[1:prevind(pname, end-2)]
×
1235
                            else
1236
                                pname
197✔
1237
                            end
1238
                            if isfile(joinpath(dir, pname, "src",
197✔
1239
                                               "$mod_name.jl"))
1240
                                push!(suggestions, PackageCompletion(mod_name))
196✔
1241
                            end
1242
                        end
1243
                    end
1244
                end
1,022✔
1245
            end
37✔
1246
        end
1247
        ffunc = (mod,x)->(Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getfield(mod, x), Module))
20,406✔
1248
        comp_keywords = false
×
1249
    end
1250

1251
    startpos == 0 && (pos = -1)
1,490✔
1252
    dotpos < startpos && (dotpos = startpos - 1)
1,490✔
1253
    return complete_identifiers!(suggestions, ffunc, context_module, string,
1,490✔
1254
        name, pos, dotpos, startpos, comp_keywords)
1255
end
1256

1257
function shell_completions(string, pos)
453✔
1258
    # First parse everything up to the current position
1259
    scs = string[1:pos]
906✔
1260
    local args, last_parse
×
1261
    try
453✔
1262
        args, last_parse = Base.shell_parse(scs, true)::Tuple{Expr,UnitRange{Int}}
470✔
1263
    catch
1264
        return Completion[], 0:-1, false
17✔
1265
    end
1266
    ex = args.args[end]::Expr
436✔
1267
    # Now look at the last thing we parsed
1268
    isempty(ex.args) && return Completion[], 0:-1, false
436✔
1269
    arg = ex.args[end]
436✔
1270
    if all(s -> isa(s, AbstractString), ex.args)
877✔
1271
        arg = arg::AbstractString
433✔
1272
        # Treat this as a path
1273

1274
        # As Base.shell_parse throws away trailing spaces (unless they are escaped),
1275
        # we need to special case here.
1276
        # If the last char was a space, but shell_parse ignored it search on "".
1277
        ignore_last_word = arg != " " && scs[end] == ' '
865✔
1278
        prefix = ignore_last_word ? "" : join(ex.args)
849✔
1279

1280
        # Also try looking into the env path if the user wants to complete the first argument
1281
        use_envpath = !ignore_last_word && length(args.args) < 2
433✔
1282

1283
        return complete_path(prefix, pos, use_envpath=use_envpath, shell_escape=true)
433✔
1284
    elseif isexpr(arg, :incomplete) || isexpr(arg, :error)
5✔
1285
        partial = scs[last_parse]
4✔
1286
        ret, range = completions(partial, lastindex(partial))
4✔
1287
        range = range .+ (first(last_parse) - 1)
2✔
1288
        return ret, range, true
2✔
1289
    end
1290
    return Completion[], 0:-1, false
1✔
1291
end
1292

1293
function UndefVarError_hint(io::IO, ex::UndefVarError)
3✔
1294
    var = ex.var
3✔
1295
    if var === :or
3✔
1296
        print(io, "\nsuggestion: Use `||` for short-circuiting boolean OR.")
×
1297
    elseif var === :and
3✔
1298
        print(io, "\nsuggestion: Use `&&` for short-circuiting boolean AND.")
×
1299
    elseif var === :help
3✔
1300
        println(io)
×
1301
        # Show friendly help message when user types help or help() and help is undefined
1302
        show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help]))
×
1303
    elseif var === :quit
3✔
1304
        print(io, "\nsuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.")
×
1305
    end
1306
end
1307

1308
function __init__()
16✔
1309
    Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError)
16✔
1310
    COMPLETION_WORLD[] = Base.get_world_counter()
16✔
1311
    nothing
16✔
1312
end
1313

1314
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