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

JuliaLang / julia / #38177

14 Aug 2025 11:50AM UTC coverage: 78.503% (+0.7%) from 77.785%
#38177

push

local

web-flow
Narrow drive letter matching for `splitdrive` on Windows to one character, fixup docstring for `joinpath` (#58951)

Fixes #58929

Only a single letter followed by a colon is a valid drive prefix for
this format, any other length of string prior to a colon isn't a valid
drive in Windows:
https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats

Also the docstring for `joinpath` rendered Windows paths as unescaped,
meaning they could not be copied and pasted on the REPL.

48594 of 61901 relevant lines covered (78.5%)

8669158.51 hits per line

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

94.08
/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, named_completion
6

7
using Core: Const
8
# We want to insulate the REPLCompletion module from any changes the user may
9
# make to the compiler, since it runs by default and the system becomes unusable
10
# if it breaks.
11
const CC = Base.Compiler
12
using Base.Meta
13
using Base: propertynames, something, IdSet
14
using Base.Filesystem: _readdirx
15
using Base.JuliaSyntax: @K_str, @KSet_str, parseall, byte_range, children, is_prefix_call, is_trivia, kind
16

17
using ..REPL.LineEdit: NamedCompletion
18
using ..REPL.SyntaxUtil: CursorNode, find_parent, seek_pos, char_range, char_last, children_nt, find_delim
19

20
abstract type Completion end
21

22
struct TextCompletion <: Completion
23
    text::String
186✔
24
end
25

26
struct KeywordCompletion <: Completion
27
    keyword::String
15✔
28
end
29

30
struct KeyvalCompletion <: Completion
31
    keyval::String
5✔
32
end
33

34
struct PathCompletion <: Completion
35
    path::String
1,018✔
36
end
37

38
struct ModuleCompletion <: Completion
39
    parent::Module
5,009✔
40
    mod::String
41
end
42

43
struct PackageCompletion <: Completion
44
    package::String
136✔
45
end
46

47
struct PropertyCompletion <: Completion
48
    value
57✔
49
    property::Symbol
50
end
51

52
struct FieldCompletion <: Completion
53
    typ::DataType
18✔
54
    field::Symbol
55
end
56

57
struct MethodCompletion <: Completion
58
    tt # may be used by an external consumer to infer return type, etc.
59
    method::Method
60
    MethodCompletion(@nospecialize(tt), method::Method) = new(tt, method)
4,198✔
61
end
62

63
struct BslashCompletion <: Completion
64
    completion::String # what is actually completed, for example "\trianglecdot"
2,619✔
65
    name::String # what is displayed, for example "â—¬ \trianglecdot"
66
end
67
BslashCompletion(completion::String) = BslashCompletion(completion, completion)
25✔
68

69
struct ShellCompletion <: Completion
70
    text::String
71
end
72

73
struct DictCompletion <: Completion
74
    dict::AbstractDict
119✔
75
    key::String
76
end
77

78
struct KeywordArgumentCompletion <: Completion
79
    kwarg::String
44✔
80
end
81

82
# interface definition
83
function Base.getproperty(c::Completion, name::Symbol)
84
    if name === :text
111,503✔
85
        return getfield(c, :text)::String
184✔
86
    elseif name === :keyword
111,319✔
87
        return getfield(c, :keyword)::String
140✔
88
    elseif name === :path
111,179✔
89
        return getfield(c, :path)::String
3,152✔
90
    elseif name === :parent
108,027✔
91
        return getfield(c, :parent)::Module
×
92
    elseif name === :mod
108,027✔
93
        return getfield(c, :mod)::String
101,621✔
94
    elseif name === :package
6,406✔
95
        return getfield(c, :package)::String
1,596✔
96
    elseif name === :property
4,810✔
97
        return getfield(c, :property)::Symbol
207✔
98
    elseif name === :field
4,603✔
99
        return getfield(c, :field)::Symbol
35✔
100
    elseif name === :method
4,568✔
101
        return getfield(c, :method)::Method
4,200✔
102
    elseif name === :bslash
368✔
103
        return getfield(c, :bslash)::String
×
104
    elseif name === :text
368✔
105
        return getfield(c, :text)::String
×
106
    elseif name === :key
368✔
107
        return getfield(c, :key)::String
119✔
108
    elseif name === :kwarg
249✔
109
        return getfield(c, :kwarg)::String
90✔
110
    end
111
    return getfield(c, name)
159✔
112
end
113

114
_completion_text(c::TextCompletion) = c.text
184✔
115
_completion_text(c::KeywordCompletion) = c.keyword
140✔
116
_completion_text(c::KeyvalCompletion) = c.keyval
19✔
117
_completion_text(c::PathCompletion) = c.path
833✔
118
_completion_text(c::ModuleCompletion) = c.mod
101,621✔
119
_completion_text(c::PackageCompletion) = c.package
1,596✔
120
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
207✔
121
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
35✔
122
_completion_text(c::MethodCompletion) = repr(c.method)
3,077✔
123
_completion_text(c::ShellCompletion) = c.text
×
124
_completion_text(c::DictCompletion) = c.key
119✔
125
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
90✔
126

127
completion_text(c) = _completion_text(c)::String
107,921✔
128

129
named_completion(c::BslashCompletion) = NamedCompletion(c.completion, c.name)
70✔
130

131
function named_completion(c)
5,509✔
132
    text = completion_text(c)::String
107,920✔
133
    return NamedCompletion(text, text)
107,920✔
134
end
135

136
named_completion_completion(c) = named_completion(c).completion::String
93,320✔
137

138
const Completions = Tuple{Vector{Completion}, UnitRange{Int}, Bool}
139

140
function completes_global(x, name)
141
    return startswith(x, name) && !('#' in x)
290,758✔
142
end
143

144
function appendmacro!(syms, macros, needle, endchar)
264✔
145
    for macsym in macros
264✔
146
        s = String(macsym)
434✔
147
        if endswith(s, needle)
434✔
148
            from = nextind(s, firstindex(s))
88✔
149
            to = prevind(s, sizeof(s)-sizeof(needle)+1)
44✔
150
            push!(syms, s[from:to]*endchar)
88✔
151
        end
152
    end
434✔
153
end
154

155
function append_filtered_mod_names!(ffunc::Function, suggestions::Vector{Completion},
132✔
156
                                    mod::Module, name::String, complete_internal_only::Bool)
157
    imported = usings = !complete_internal_only
132✔
158
    ssyms = names(mod; all=true, imported, usings)
132✔
159
    filter!(ffunc, ssyms)
132✔
160
    macros = filter(x -> startswith(String(x), "@" * name), ssyms)
150,044✔
161

162
    # don't complete string and command macros when the input matches the internal name like `r_` to `r"`
163
    if !startswith(name, "@")
132✔
164
        filter!(macros) do m
120✔
165
            s = String(m)
221✔
166
            if endswith(s, "_str") || endswith(s, "_cmd")
408✔
167
                occursin(name, first(s, length(s)-4))
48✔
168
            else
169
                true
170
            end
171
        end
172
    end
173

174
    syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)]
5,097✔
175
    appendmacro!(syms, macros, "_str", "\"")
132✔
176
    appendmacro!(syms, macros, "_cmd", "`")
132✔
177
    for sym in syms
132✔
178
        push!(suggestions, ModuleCompletion(mod, sym))
5,009✔
179
    end
5,009✔
180
    return suggestions
132✔
181
end
182

183
# REPL Symbol Completions
184
function complete_symbol!(suggestions::Vector{Completion},
408✔
185
                          @nospecialize(prefix), name::String, context_module::Module;
186
                          complete_modules_only::Bool=false,
187
                          shift::Bool=false)
188
    local mod, t, val
204✔
189
    complete_internal_only = isempty(name)
204✔
190
    if prefix !== nothing
204✔
191
        res = repl_eval_ex(prefix, context_module)
124✔
192
        res === nothing && return Completion[]
124✔
193
        if res isa Const
113✔
194
            val = res.val
91✔
195
            if isa(val, Module)
91✔
196
                mod = val
52✔
197
                if !shift
52✔
198
                    # when module is explicitly accessed, show internal bindings that are
199
                    # defined by the module, unless shift key is pressed
200
                    complete_internal_only = true
6✔
201
                end
202
            else
203
                t = typeof(val)
39✔
204
            end
205
        else
206
            t = CC.widenconst(res)
22✔
207
        end
208
    else
209
        mod = context_module
80✔
210
    end
211

212
    if @isdefined(mod) # lookup names available within the module
193✔
213
        let modname = nameof(mod),
132✔
214
            is_main = mod===Main
215
            append_filtered_mod_names!(suggestions, mod, name, complete_internal_only) do s::Symbol
132✔
216
                if Base.isdeprecated(mod, s)
184,331✔
217
                    return false
×
218
                elseif s === modname
184,331✔
219
                    return false # exclude `Main.Main.Main`, etc.
182✔
220
                elseif complete_modules_only && !completes_module(mod, s)
184,149✔
221
                    return false
34,187✔
222
                elseif is_main && s === :MainInclude
149,962✔
223
                    return false
50✔
224
                end
225
                return true
149,912✔
226
            end
227
        end
228
    elseif @isdefined(val) # looking for a property of an instance
61✔
229
        try
39✔
230
            for property in propertynames(val, false)
39✔
231
                # TODO: support integer arguments (#36872)
232
                if property isa Symbol && startswith(string(property), name)
63✔
233
                    push!(suggestions, PropertyCompletion(val, property))
57✔
234
                end
235
            end
63✔
236
        catch
1✔
237
        end
238
    elseif @isdefined(t) && field_completion_eligible(t)
22✔
239
        # Looking for a member of a type
240
        add_field_completions!(suggestions, name, t)
13✔
241
    end
242
    return suggestions
193✔
243
end
244

245
completes_module(mod::Module, x::Symbol) = isdefined(mod, x) && isa(getglobal(mod, x), Module)
34,580✔
246

247
function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t))
15✔
248
    if isa(t, Union)
17✔
249
        add_field_completions!(suggestions, name, t.a)
2✔
250
        add_field_completions!(suggestions, name, t.b)
2✔
251
    else
252
        @assert isconcretetype(t)
15✔
253
        fields = fieldnames(t)
15✔
254
        for field in fields
15✔
255
            isa(field, Symbol) || continue # Tuple type has ::Int field name
18✔
256
            s = string(field)
18✔
257
            if startswith(s, name)
18✔
258
                push!(suggestions, FieldCompletion(t, field))
18✔
259
            end
260
        end
18✔
261
    end
262
end
263

264
const GENERIC_PROPERTYNAMES_METHOD = which(propertynames, (Any,))
265

266
function field_completion_eligible(@nospecialize t)
26✔
267
    if isa(t, Union)
26✔
268
        return field_completion_eligible(t.a) && field_completion_eligible(t.b)
2✔
269
    end
270
    isconcretetype(t) || return false
31✔
271
    # field completion is correct only when `getproperty` fallbacks to `getfield`
272
    match = Base._which(Tuple{typeof(propertynames),t}; raise=false)
17✔
273
    match === nothing && return false
17✔
274
    return match.method === GENERIC_PROPERTYNAMES_METHOD
17✔
275
end
276

277
function complete_from_list!(suggestions::Vector{Completion}, T::Type, list::Vector{String}, s::String)
126✔
278
    r = searchsorted(list, s)
126✔
279
    i = first(r)
126✔
280
    n = length(list)
126✔
281
    while i <= n && startswith(list[i],s)
146✔
282
        r = first(r):i
20✔
283
        i += 1
20✔
284
    end
20✔
285
    for kw in list[r]
126✔
286
        push!(suggestions, T(kw))
20✔
287
    end
20✔
288
    return suggestions
126✔
289
end
290

291
const sorted_keywords = [
292
    "abstract type", "baremodule", "begin", "break", "catch", "ccall",
293
    "const", "continue", "do", "else", "elseif", "end", "export",
294
    "finally", "for", "function", "global", "if", "import",
295
    "let", "local", "macro", "module", "mutable struct",
296
    "primitive type", "quote", "return", "struct",
297
    "try", "using", "while"]
298

299
complete_keyword!(suggestions::Vector{Completion}, s::String) =
63✔
300
    complete_from_list!(suggestions, KeywordCompletion, sorted_keywords, s)
301

302
const sorted_keyvals = ["false", "true"]
303

304
complete_keyval!(suggestions::Vector{Completion}, s::String) =
63✔
305
    complete_from_list!(suggestions, KeyvalCompletion, sorted_keyvals, s)
306

307
function do_cmd_escape(s)
1✔
308
    return Base.escape_raw_string(Base.shell_escape_posixly(s), '`')
1✔
309
end
310
function do_shell_escape(s)
311
    return Base.shell_escape_posixly(s)
810✔
312
end
313
function do_string_escape(s)
314
    return escape_string(s, ('\"','$'))
42✔
315
end
316
function do_string_unescape(s)
62✔
317
    s = replace(s, "\\\$"=>"\$")
62✔
318
    try
62✔
319
        unescape_string(s)
62✔
320
    catch e
321
        e isa ArgumentError || rethrow()
1✔
322
        s # it is unlikely, but if it isn't a valid string, maybe it was a valid path, and just needs escape_string called?
1✔
323
    end
324
end
325

326
function joinpath_withsep(dir, path; dirsep)
700✔
327
    dir == "" && return path
350✔
328
    dir[end] == dirsep ? dir * path : dir * dirsep * path
337✔
329
end
330

331
const PATH_cache_lock = Base.ReentrantLock()
332
const PATH_cache = Set{String}()
333
PATH_cache_task::Union{Task,Nothing} = nothing
334
PATH_cache_condition::Union{Threads.Condition, Nothing} = nothing # used for sync in tests
335
next_cache_update::Float64 = 0.0
336
function maybe_spawn_cache_PATH()
10✔
337
    global PATH_cache_task, PATH_cache_condition, next_cache_update
10✔
338
    @lock PATH_cache_lock begin
10✔
339
        # Extract to local variables to enable flow-sensitive type inference for these global variables
340
        PATH_cache_task_local = PATH_cache_task
10✔
341
        PATH_cache_task_local isa Task && !istaskdone(PATH_cache_task_local) && return
10✔
342
        time() < next_cache_update && return
10✔
343
        PATH_cache_task = PATH_cache_task_local = Threads.@spawn begin
6✔
344
            try
3✔
345
                REPLCompletions.cache_PATH()
3✔
346
            finally
347
                @lock PATH_cache_lock begin
3✔
348
                    next_cache_update = time() + 10 # earliest next update can run is 10s after
3✔
349
                    PATH_cache_task = nothing # release memory when done
3✔
350
                    PATH_cache_condition_local = PATH_cache_condition
3✔
351
                    PATH_cache_condition_local !== nothing && notify(PATH_cache_condition_local)
3✔
352
                end
353
            end
354
        end
355
        Base.errormonitor(PATH_cache_task_local)
3✔
356
    end
357
end
358

359
# caches all reachable files in PATH dirs
360
function cache_PATH()
3✔
361
    path = get(ENV, "PATH", nothing)
3✔
362
    path isa String || return
3✔
363

364
    # Calling empty! on PATH_cache would be annoying for async typing hints as completions would temporarily disappear.
365
    # So keep track of what's added this time and at the end remove any that didn't appear this time from the global cache.
366
    this_PATH_cache = Set{String}()
3✔
367

368
    @debug "caching PATH files" PATH=path
3✔
369
    pathdirs = split(path, @static Sys.iswindows() ? ";" : ":")
3✔
370

371
    next_yield_time = time() + 0.01
3✔
372

373
    t = @elapsed for pathdir in pathdirs
3✔
374
        actualpath = try
19✔
375
            realpath(pathdir)
22✔
376
        catch ex
377
            ex isa Base.IOError || rethrow()
3✔
378
            # Bash doesn't expect every folder in PATH to exist, so neither shall we
379
            continue
3✔
380
        end
381

382
        if actualpath != pathdir && in(actualpath, pathdirs)
27✔
383
            # Remove paths which (after resolving links) are in the env path twice.
384
            # Many distros eg. point /bin to /usr/bin but have both in the env path.
385
            continue
3✔
386
        end
387

388
        path_entries = try
13✔
389
            _readdirx(pathdir)
14✔
390
        catch e
391
            # Bash allows dirs in PATH that can't be read, so we should as well.
392
            if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
1✔
393
                continue
1✔
394
            else
395
                # We only handle IOError and ArgumentError here
396
                rethrow()
×
397
            end
398
        end
399
        for entry in path_entries
12✔
400
            # In a perfect world, we would filter on whether the file is executable
401
            # here, or even on whether the current user can execute the file in question.
402
            try
2,276✔
403
                if isfile(entry)
2,276✔
404
                    @lock PATH_cache_lock push!(PATH_cache, entry.name)
2,151✔
405
                    push!(this_PATH_cache, entry.name)
2,152✔
406
                end
407
            catch e
408
                # `isfile()` can throw in rare cases such as when probing a
409
                # symlink that points to a file within a directory we do not
410
                # have read access to.
411
                if isa(e, Base.IOError)
1✔
412
                    continue
1✔
413
                else
414
                    rethrow()
×
415
                end
416
            end
417
            if time() >= next_yield_time
2,275✔
418
                yield() # to avoid blocking typing when -t1
×
419
                next_yield_time = time() + 0.01
×
420
            end
421
        end
2,276✔
422
    end
423

424
    @lock PATH_cache_lock begin
3✔
425
        intersect!(PATH_cache, this_PATH_cache) # remove entries from PATH_cache that weren't found this time
3✔
426
    end
427

428
    @debug "caching PATH files took $t seconds" length(pathdirs) length(PATH_cache)
3✔
429
    return PATH_cache
3✔
430
end
431

432
function complete_path(path::AbstractString;
122✔
433
                       use_envpath=false,
434
                       shell_escape=false,
435
                       cmd_escape=false,
436
                       string_escape=false,
437
                       contract_user=false,
438
                       dirsep=Sys.iswindows() ? '\\' : '/')
439
    @assert !(shell_escape && string_escape)
61✔
440
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
61✔
441
        # if the path is just "~", don't consider the expanded username as a prefix
442
        if path == "~"
×
443
            dir, prefix = homedir(), ""
×
444
        else
445
            dir, prefix = splitdir(homedir() * path[2:end])
×
446
        end
447
    else
448
        dir, prefix = splitdir(path)
61✔
449
    end
450
    entries = try
61✔
451
        if isempty(dir)
61✔
452
            _readdirx()
38✔
453
        elseif isdir(dir)
23✔
454
            _readdirx(dir)
22✔
455
        else
456
            return Completion[], dir, false
61✔
457
        end
458
    catch ex
459
        ex isa Base.IOError || rethrow()
×
460
        return Completion[], dir, false
×
461
    end
462

463
    matches = Set{String}()
60✔
464
    for entry in entries
60✔
465
        if startswith(entry.name, prefix)
10,097✔
466
            is_dir = try isdir(entry) catch ex; ex isa Base.IOError ? false : rethrow() end
831✔
467
            push!(matches, is_dir ? joinpath_withsep(entry.name, ""; dirsep) : entry.name)
1,483✔
468
        end
469
    end
10,097✔
470

471
    if use_envpath && isempty(dir)
60✔
472
        # Look for files in PATH as well. These are cached in `cache_PATH` in an async task to not block typing.
473
        # If we cannot get lock because its still caching just pass over this so that typing isn't laggy.
474
        maybe_spawn_cache_PATH() # only spawns if enough time has passed and the previous caching task has completed
10✔
475
        @lock PATH_cache_lock begin
10✔
476
            for file in PATH_cache
20✔
477
                startswith(file, prefix) && push!(matches, file)
876✔
478
            end
1,752✔
479
        end
480
    end
481

482
    matches = ((shell_escape ? do_shell_escape(s) : string_escape ? do_string_escape(s) : s) for s in matches)
60✔
483
    matches = ((cmd_escape ? do_cmd_escape(s) : s) for s in matches)
60✔
484
    matches = Completion[PathCompletion(contract_user ? contractuser(s) : s) for s in matches]
856✔
485
    return matches, dir, !isempty(matches)
60✔
486
end
487

488
function complete_path(path::AbstractString,
×
489
                       pos::Int;
490
                       use_envpath=false,
491
                       shell_escape=false,
492
                       string_escape=false,
493
                       contract_user=false)
494
    ## TODO: enable this depwarn once Pkg is fixed
495
    #Base.depwarn("complete_path with pos argument is deprecated because the return value [2] is incorrect to use", :complete_path)
496
    paths, dir, success = complete_path(path; use_envpath, shell_escape, string_escape, dirsep='/')
×
497

498
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
×
499
        # if the path is just "~", don't consider the expanded username as a prefix
500
        if path == "~"
×
501
            dir, prefix = homedir(), ""
×
502
        else
503
            dir, prefix = splitdir(homedir() * path[2:end])
×
504
        end
505
    else
506
        dir, prefix = splitdir(path)
×
507
    end
508
    startpos = pos - lastindex(prefix) + 1
×
509
    Sys.iswindows() && map!(paths, paths) do c::PathCompletion
×
510
        # emulation for unnecessarily complicated return value, since / is a
511
        # perfectly acceptable path character which does not require quoting
512
        # but is required by Pkg's awkward parser handling
513
        return endswith(c.path, "/") ? PathCompletion(chop(c.path) * "\\\\") : c
×
514
    end
515
    return paths, startpos:pos, success
×
516
end
517

518
struct REPLCacheToken end
519

520
struct REPLInterpreter <: CC.AbstractInterpreter
521
    limit_aggressive_inference::Bool
522
    world::UInt
523
    inf_params::CC.InferenceParams
524
    opt_params::CC.OptimizationParams
525
    inf_cache::Vector{CC.InferenceResult}
526
    function REPLInterpreter(limit_aggressive_inference::Bool=false;
245✔
527
                             world::UInt = Base.get_world_counter(),
528
                             inf_params::CC.InferenceParams = CC.InferenceParams(;
529
                                 aggressive_constant_propagation=true),
530
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
531
                             inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[])
532
        return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache)
239✔
533
    end
534
end
535
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
177,102✔
536
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
106✔
537
CC.get_inference_world(interp::REPLInterpreter) = interp.world
121,966✔
538
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
44,821✔
539
CC.cache_owner(::REPLInterpreter) = REPLCacheToken()
54,248✔
540

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

544
# REPLInterpreter doesn't need any sources to be cached, so discard them aggressively
545
CC.transform_result_for_cache(::REPLInterpreter, ::CC.InferenceResult, edges::Core.SimpleVector) = nothing
3,475✔
546

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

550
# `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions
551
# for lines like `Mod.a.|` (where `|` is the cursor position).
552
# Aggressive binding resolution poses challenges for the inference cache validation
553
# (until https://github.com/JuliaLang/julia/issues/40399 is implemented).
554
# To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding
555
# resolution for top-level frame representing REPL input code and for child uncached frames
556
# that are constant propagated from the top-level frame ("repl-frame"s). This works, even if
557
# those global bindings are not constant and may be mutated in the future, since:
558
# a.) "repl-frame"s are never cached, and
559
# b.) mutable values are never observed by any cached frames.
560
#
561
# `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within
562
# "repl-frame" to provide reasonable completions for lines like `Ref(Some(42))[].|`.
563
# Aggressive concrete evaluation allows us to get accurate type information about complex
564
# expressions that otherwise can not be constant folded, in a safe way, i.e. it still
565
# doesn't evaluate effectful expressions like `pop!(xs)`.
566
# Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't
567
# present any cache validation issues because "repl-frame" is never cached.
568

569
# `REPLInterpreter` is specifically used by `repl_eval_ex`, where all top-level frames are
570
# `repl_frame` always. However, this assumption wouldn't stand if `REPLInterpreter` were to
571
# be employed, for instance, by `typeinf_ext_toplevel`.
572
is_repl_frame(sv::CC.InferenceState) = sv.linfo.def isa Module && sv.cache_mode === CC.CACHE_MODE_NULL
245✔
573

574
function is_call_stack_uncached(sv::CC.InferenceState)
241,810✔
575
    CC.is_cached(sv) && return false
340,734✔
576
    parent = CC.frame_parent(sv)
492,365✔
577
    parent === nothing && return true
250,555✔
578
    return is_call_stack_uncached(parent::CC.InferenceState)
241,810✔
579
end
580

581
# aggressive global binding resolution within `repl_frame`
582
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, bailed::Bool,
61,971✔
583
                                    sv::CC.InferenceState)
584
    # Ignore saw_latestworld
585
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_stack_uncached(sv))
123,781✔
586
        partition = CC.abstract_eval_binding_partition!(interp, g, sv)
5,890✔
587
        if CC.is_defined_const_binding(CC.binding_kind(partition))
7,671✔
588
            return CC.RTEffects(Const(CC.partition_restriction(partition)), Union{}, CC.EFFECTS_TOTAL)
5,816✔
589
        else
590
            b = convert(Core.Binding, g)
74✔
591
            if CC.binding_kind(partition) == CC.PARTITION_KIND_GLOBAL && isdefined(b, :value)
74✔
592
                return CC.RTEffects(Const(b.value), Union{}, CC.EFFECTS_TOTAL)
67✔
593
            end
594
        end
595
        return CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS)
7✔
596
    end
597
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, bailed::Bool,
56,081✔
598
                                              sv::CC.InferenceState)
599
end
600

601
function is_repl_frame_getproperty(sv::CC.InferenceState)
×
602
    def = sv.linfo.def
×
603
    def isa Method || return false
×
604
    def.name === :getproperty || return false
×
605
    CC.is_cached(sv) && return false
×
606
    return is_repl_frame(CC.frame_parent(sv))
×
607
end
608

609
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
610
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
37,198✔
611
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
612
                                   sv::CC.InferenceState)
613
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_stack_uncached(sv))
74,312✔
614
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
2,865✔
615
        result = CC.MethodCallResult(result.rt, result.exct, neweffects, result.edge,
5,728✔
616
                                     result.edgecycle, result.edgelimited, result.volatile_inf_result)
617
    end
618
    ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
74,396✔
619
                                            result::CC.MethodCallResult, arginfo::CC.ArgInfo,
620
                                            sv::CC.InferenceState)
621
    if ret === :semi_concrete_eval
37,198✔
622
        # while the base eligibility check probably won't permit semi-concrete evaluation
623
        # for `REPLInterpreter` (given it completely turns off optimization),
624
        # this ensures we don't inadvertently enter irinterp
625
        ret = :none
×
626
    end
627
    return ret
37,198✔
628
end
629

630
# allow constant propagation for mutable constants
631
function CC.const_prop_argument_heuristic(interp::REPLInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
632
    if !interp.limit_aggressive_inference
36,708✔
633
        any(@nospecialize(a)->isa(a, Const), arginfo.argtypes) && return true # even if mutable
73,309✔
634
    end
635
    return @invoke CC.const_prop_argument_heuristic(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
102✔
636
end
637

638
function resolve_toplevel_symbols!(src::Core.CodeInfo, mod::Module)
639
    @ccall jl_resolve_definition_effects_in_ir(
233✔
640
        #=jl_array_t *stmts=# src.code::Any,
641
        #=jl_module_t *m=# mod::Any,
642
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
643
        #=jl_value_t *binding_edge=# C_NULL::Ptr{Cvoid},
644
        #=int binding_effects=# 0::Int)::Cvoid
645
    return src
233✔
646
end
647

648
# lower `ex` and run type inference on the resulting top-level expression
649
function repl_eval_ex(@nospecialize(ex), context_module::Module; limit_aggressive_inference::Bool=false)
964✔
650
    expr_has_error(ex) && return nothing
732✔
651
    if (isexpr(ex, :toplevel) || isexpr(ex, :tuple)) && !isempty(ex.args)
964✔
652
        # get the inference result for the last expression
653
        ex = ex.args[end]
1✔
654
    end
655
    lwr = try
482✔
656
        Meta.lower(context_module, ex)
482✔
657
    catch # macro expansion failed, etc.
658
        return nothing
5✔
659
    end
660
    if lwr isa Symbol
477✔
661
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
143✔
662
    end
663
    lwr isa Expr || return Const(lwr) # `ex` is literal
427✔
664
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
249✔
665
    src = lwr.args[1]::Core.CodeInfo
233✔
666

667
    resolve_toplevel_symbols!(src, context_module)
233✔
668
    # construct top-level `MethodInstance`
669
    mi = ccall(:jl_method_instance_for_thunk, Ref{Core.MethodInstance}, (Any, Any), src, context_module)
233✔
670

671
    interp = REPLInterpreter(limit_aggressive_inference)
233✔
672
    result = CC.InferenceResult(mi)
233✔
673
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)
233✔
674

675
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
676
    #      potential invalidations of `Core.Compiler` methods.
677
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
233✔
678

679
    result = frame.result.result
233✔
680
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
233✔
681
    return result
225✔
682
end
683

684
# `COMPLETION_WORLD[]` will be initialized within `__init__`
685
# (to allow us to potentially remove REPL from the sysimage in the future).
686
# Note that inference from the `code_typed` call below will use the current world age
687
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
688
# when the given world age is higher than the current one.
689
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
690

691
# Generate code cache for `REPLInterpreter` now:
692
# This code cache will be available at the world of `COMPLETION_WORLD`,
693
# assuming no invalidation will happen before initializing REPL.
694
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
695
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
696

697
# Method completion on function call expression that look like :(max(1))
698
MAX_METHOD_COMPLETIONS::Int = 40
699
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
125✔
700
    isempty(ex_org.args) && return 2, nothing, [], Set{Symbol}()
125✔
701
    # Desugar do block call into call with lambda
702
    if ex_org.head === :do && length(ex_org.args) >= 2
125✔
703
        ex_call = ex_org.args[1]
1✔
704
        ex_args = [x for x in ex_call.args if !(x isa Expr && x.head === :parameters)]
1✔
705
        ex_params = findfirst(x -> x isa Expr && x.head === :parameters, ex_call.args)
3✔
706
        new_args = [ex_args[1], ex_org.args[end], ex_args[2:end]...]
1✔
707
        ex_params !== nothing && push!(new_args, ex_call.args[ex_params])
1✔
708
        ex_org = Expr(:call, new_args...)
1✔
709
    end
710
    funct = repl_eval_ex(ex_org.args[1], context_module)
125✔
711
    funct === nothing && return 2, nothing, [], Set{Symbol}()
125✔
712
    funct = CC.widenconst(funct)
124✔
713
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
124✔
714
    return kwargs_flag, funct, args_ex, kwargs_ex
124✔
715
end
716

717
# cursor_pos: either :positional (complete either kwargs or positional) or :kwargs (beyond semicolon)
718
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false, cursor_pos::Symbol=:positional)
1✔
719
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
84✔
720
    out = Completion[]
83✔
721
    # Allow more arguments when cursor before semicolon, even if kwargs are present
722
    cursor_pos == :positional && kwargs_flag == 1 && (kwargs_flag = 0)
83✔
723
    kwargs_flag == 2 && return out # one of the kwargs is invalid
83✔
724
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
73✔
725
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
76✔
726
    return out
73✔
727
end
728

729
MAX_ANY_METHOD_COMPLETIONS::Int = 10
730

731
function accessible(mod::Module, private::Bool)
14✔
732
    bindings = IdSet{Any}(Core.Typeof(getglobal(mod, s)) for s in names(mod; all=private, imported=private, usings=private)
14✔
733
                   if !Base.isdeprecated(mod, s) && !startswith(string(s), '#') && !startswith(string(s), '@') && isdefined(mod, s))
734
    delete!(bindings, Module)
14✔
735
    return collect(bindings)
14✔
736
end
737

738
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
14✔
739
    out = Completion[]
14✔
740
    args_ex, kwargs_ex, kwargs_flag = try
14✔
741
        # this may throw, since we set default_any to false
742
        complete_methods_args(ex_org, context_module, false, false)
14✔
743
    catch ex
744
        ex isa ArgumentError || rethrow()
×
745
        return out
14✔
746
    end
747
    kwargs_flag == 2 && return out # one of the kwargs is invalid
14✔
748

749
    # moreargs determines whether to accept more args, independently of the presence of a
750
    # semicolon for the ".?(" syntax
751
    moreargs && push!(args_ex, Vararg{Any})
14✔
752

753
    for seen_name in accessible(callee_module, callee_module === context_module)
14✔
754
        complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
1,524✔
755
    end
1,524✔
756

757
    if !shift
14✔
758
        # Filter out methods where all arguments are `Any`
759
        filter!(out) do c
2✔
760
            isa(c, TextCompletion) && return false
11✔
761
            isa(c, MethodCompletion) || return true
11✔
762
            sig = Base.unwrap_unionall(c.method.sig)::DataType
11✔
763
            return !all(@nospecialize(T) -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
19✔
764
        end
765
    end
766

767
    return out
14✔
768
end
769

770
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
771
    n = isexpr(x, :kw) ? x.args[1] : x
82✔
772
    if n isa Symbol
82✔
773
        push!(kwargs_ex, n)
71✔
774
        return kwargs_flag
71✔
775
    end
776
    possible_splat && isexpr(x, :...) && return kwargs_flag
11✔
777
    return 2 # The kwarg is invalid
7✔
778
end
779

780
function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, default_any::Bool, broadcasting::Bool)
138✔
781
    args_ex = Any[]
138✔
782
    kwargs_ex = Symbol[]
138✔
783
    kwargs_flag = 0
138✔
784
    # kwargs_flag is:
785
    # * 0 if there is no semicolon and no invalid kwarg
786
    # * 1 if there is a semicolon and no invalid kwarg
787
    # * 2 if there are two semicolons or more, or if some kwarg is invalid, which
788
    #        means that it is not of the form "bar=foo", "bar" or "bar..."
789
    for i in (1+!broadcasting):length(funargs)
155✔
790
        ex = funargs[i]
238✔
791
        if isexpr(ex, :parameters)
238✔
792
            kwargs_flag = ifelse(kwargs_flag == 0, 1, 2) # there should be at most one :parameters
59✔
793
            for x in ex.args
59✔
794
                kwargs_flag = detect_invalid_kwarg!(kwargs_ex, x, kwargs_flag, true)
70✔
795
            end
60✔
796
        elseif isexpr(ex, :kw)
179✔
797
            kwargs_flag = detect_invalid_kwarg!(kwargs_ex, ex, kwargs_flag, false)
22✔
798
        else
799
            if broadcasting
157✔
800
                # handle broadcasting, but only handle number of arguments instead of
801
                # argument types
802
                push!(args_ex, Any)
5✔
803
            else
804
                argt = repl_eval_ex(ex, context_module)
152✔
805
                if argt !== nothing
152✔
806
                    push!(args_ex, CC.widenconst(argt))
119✔
807
                elseif default_any
33✔
808
                    push!(args_ex, Any)
33✔
809
                else
810
                    throw(ArgumentError("argument not found"))
×
811
                end
812
            end
813
        end
814
    end
355✔
815
    return args_ex, Set{Symbol}(kwargs_ex), kwargs_flag
138✔
816
end
817

818
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
248✔
819

820
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
821
    if allow_broadcasting && is_broadcasting_expr(ex)
138✔
822
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
6✔
823
    end
824
    return detect_args_kwargs(ex.args, context_module, default_any, false)
132✔
825
end
826

827
function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool)
1,636✔
828
    # Input types and number of arguments
829
    t_in = Tuple{funct, args_ex...}
1,636✔
830
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
1,636✔
831
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
832
    if !isa(m, Vector)
1,636✔
833
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
186✔
834
        return
186✔
835
    end
836
    for match in m
1,450✔
837
        # TODO: if kwargs_ex, filter out methods without kwargs?
838
        push!(out, MethodCompletion(match.spec_types, match.method))
4,198✔
839
    end
4,198✔
840
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
841
end
842

843
include("latex_symbols.jl")
844
include("emoji_symbols.jl")
845

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

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

858
function bslash_completions(string::String, pos::Int, hint::Bool=false)
370✔
859
    slashpos = something(findprev(isequal('\\'), string, pos), 0)
781✔
860
    if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos &&
393✔
861
        !(1 < slashpos && (string[prevind(string, slashpos)]=='\\')))
862
        # latex / emoji symbol substitution
863
        s = string[slashpos:pos]
78✔
864
        latex = get(latex_symbols, s, "")
39✔
865
        if !isempty(latex) # complete an exact match
39✔
866
            return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true))
19✔
867
        elseif occursin(subscript_regex, s)
20✔
868
            sub = map(c -> subscripts[c], s[3:end])
22✔
869
            return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true))
3✔
870
        elseif occursin(superscript_regex, s)
17✔
871
            sup = map(c -> superscripts[c], s[3:end])
8✔
872
            return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true))
1✔
873
        end
874
        emoji = get(emoji_symbols, s, "")
16✔
875
        if !isempty(emoji)
16✔
876
            return (true, (Completion[BslashCompletion(emoji)], slashpos:pos, true))
2✔
877
        end
878
        # return possible matches; these cannot be mixed with regular
879
        # Julian completions as only latex / emoji symbols contain the leading \
880
        symbol_dict = startswith(s, "\\:") ? emoji_symbols : latex_symbols
14✔
881
        namelist = Iterators.filter(k -> startswith(k, s), keys(symbol_dict))
34,393✔
882
        completions = Completion[BslashCompletion(name, "$(symbol_dict[name]) $name") for name in sort!(collect(namelist))]
14✔
883
        return (true, (completions, slashpos:pos, true))
14✔
884
    end
885
    return (false, (Completion[], 1:0, false))
331✔
886
end
887

888
# This needs to be a separate non-inlined function, see #19441
889
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
70✔
890
    matches = String[]
70✔
891
    for key in keys(identifier)
106✔
892
        rkey = repr(key)
829✔
893
        startswith(rkey,partial_key) && push!(matches,rkey)
829✔
894
    end
1,250✔
895
    return matches
70✔
896
end
897

898
# Provide completion for keyword arguments in function calls
899
# Returns true if the current argument must be a keyword because the cursor is beyond the semicolon
900
function complete_keyword_argument!(suggestions::Vector{Completion},
84✔
901
                                    ex::Expr, last_word::String,
902
                                    context_module::Module,
903
                                    arg_pos::Symbol; shift::Bool=false)
904
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex, context_module, true)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
42✔
905
    kwargs_flag == 2 && return false # one of the previous kwargs is invalid
42✔
906

907
    methods = Completion[]
41✔
908
    # Limit kwarg completions to cases when function is concretely known; looking up
909
    # matching methods for abstract functions — particularly `Any` or `Function` — can
910
    # take many seconds to run over the thousands of possible methods. Note that
911
    # isabstracttype would return naively return true for common constructor calls
912
    # like Array, but the REPL's introspection here may know their Type{T}.
913
    isconcretetype(funct) || return false
43✔
914
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, arg_pos == :kwargs)
39✔
915
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
916
    # method calls compatible with the current arguments.
917

918
    # For each method corresponding to the function call, provide completion suggestions
919
    # for each keyword that starts like the last word and that is not already used
920
    # previously in the expression. The corresponding suggestion is "kwname=".
921
    # If the keyword corresponds to an existing name, also include "kwname" as a suggestion
922
    # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)".
923
    kwargs = Set{String}()
39✔
924
    for m in methods
39✔
925
        # if MAX_METHOD_COMPLETIONS is hit a single TextCompletion is return by complete_methods! with an explanation
926
        # which can be ignored here
927
        m isa TextCompletion && continue
1,112✔
928
        m::MethodCompletion
1,112✔
929
        possible_kwargs = Base.kwarg_decl(m.method)
1,112✔
930
        current_kwarg_candidates = String[]
1,112✔
931
        for _kw in possible_kwargs
1,112✔
932
            kw = String(_kw)
206✔
933
            # HACK: Should consider removing current arg from AST.
934
            if !endswith(kw, "...") && startswith(kw, last_word) && (_kw ∉ kwargs_ex || kw == last_word)
214✔
935
                push!(current_kwarg_candidates, kw)
54✔
936
            end
937
        end
206✔
938
        union!(kwargs, current_kwarg_candidates)
1,112✔
939
    end
1,112✔
940

941
    for kwarg in kwargs
78✔
942
        push!(suggestions, KeywordArgumentCompletion(kwarg))
42✔
943
    end
84✔
944
    return kwargs_flag != 0 && arg_pos == :kwargs
39✔
945
end
946

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

963
function complete_loading_candidates!(suggestions::Vector{Completion}, s::String)
9✔
964
    for name in ("Core", "Base")
9✔
965
        startswith(name, s) && push!(suggestions, PackageCompletion(name))
18✔
966
    end
27✔
967

968
    # If there's no dot, we're in toplevel, so we should
969
    # also search for packages
970
    for dir in Base.load_path()
9✔
971
        if basename(dir) in Base.project_names && isfile(dir)
24✔
972
            for name in get_loading_candidates(s, dir)
1✔
973
                push!(suggestions, PackageCompletion(name))
2✔
974
            end
2✔
975
        end
976
        isdir(dir) || continue
12✔
977
        for entry in _readdirx(dir)
10✔
978
            pname = entry.name
560✔
979
            if pname[1] != '.' && pname != "METADATA" &&
1,120✔
980
                pname != "REQUIRE" && startswith(pname, s)
981
                # Valid file paths are
982
                #   <Mod>.jl
983
                #   <Mod>/src/<Mod>.jl
984
                #   <Mod>.jl/src/<Mod>.jl
985
                if isfile(entry)
131✔
986
                    endswith(pname, ".jl") && push!(suggestions,
×
987
                                                    PackageCompletion(pname[1:prevind(pname, end-2)]))
988
                else
989
                    mod_name = if endswith(pname, ".jl")
131✔
990
                        pname[1:prevind(pname, end-2)]
×
991
                    else
992
                        pname
262✔
993
                    end
994
                    if isfile(joinpath(entry, "src",
131✔
995
                                       "$mod_name.jl"))
996
                        push!(suggestions, PackageCompletion(mod_name))
130✔
997
                    end
998
                end
999
            end
1000
        end
560✔
1001
    end
12✔
1002
end
1003

1004
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true, hint::Bool=false)
473✔
1005
    # filename needs to be string so macro can be evaluated
1006
    node = parseall(CursorNode, string, ignore_errors=true, keep_parens=true, filename="none")
914✔
1007
    cur = @something seek_pos(node, pos) node
469✔
1008

1009
    # Back up before whitespace to get a more useful AST node.
1010
    pos_not_ws = findprev(!isspace, string, pos)
464✔
1011
    cur_not_ws = something(seek_pos(node, pos_not_ws), node)
923✔
1012

1013
    suggestions = Completion[]
464✔
1014
    sort_suggestions() = sort!(unique!(named_completion, suggestions), by=named_completion_completion)
705✔
1015

1016
    # Search for methods (requires tab press):
1017
    #   ?(x, y)TAB           lists methods you can call with these objects
1018
    #   ?(x, y TAB           lists methods that take these objects as the first two arguments
1019
    #   MyModule.?(x, y)TAB  restricts the search to names in MyModule
1020
    if !hint
464✔
1021
        cs = method_search(view(string, 1:pos), context_module, shift)
457✔
1022
        cs !== nothing && return cs
457✔
1023
    end
1024

1025
    # Complete keys in a Dict:
1026
    #   my_dict[ TAB
1027
    n, key, closed = find_ref_key(cur_not_ws, pos)
524✔
1028
    if n !== nothing
450✔
1029
        key::UnitRange{Int}
74✔
1030
        obj = dict_eval(Expr(n), context_module)
74✔
1031
        if obj !== nothing
74✔
1032
            # Skip leading whitespace inside brackets.
1033
            i = @something findnext(!isspace, string, first(key)) nextind(string, last(key))
75✔
1034
            key = i:last(key)
75✔
1035
            s = string[intersect(key, 1:pos)]
135✔
1036
            matches = find_dict_matches(obj, s)
70✔
1037
            length(matches) == 1 && !closed && (matches[1] *= ']')
70✔
1038
            if length(matches) > 0
70✔
1039
                ret = Completion[DictCompletion(obj, match) for match in sort!(matches)]
62✔
1040
                return ret, key, true
62✔
1041
            end
1042
        end
1043
    end
1044

1045
    # Complete Cmd strings:
1046
    #   `fil TAB                 => `file
1047
    #   `file ~/exa TAB          => `file ~/example.txt
1048
    #   `file ~/example.txt TAB  => `file /home/user/example.txt
1049
    if (n = find_parent(cur, K"CmdString")) !== nothing
388✔
1050
        off = n.position - 1
2✔
1051
        ret, r, success = shell_completions(string[char_range(n)], pos - off, hint, cmd_escape=true)
4✔
1052
        success && return ret, r .+ off, success
2✔
1053
    end
1054

1055
    # Complete ordinary strings:
1056
    #  "~/exa TAB         => "~/example.txt"
1057
    #  "~/example.txt TAB => "/home/user/example.txt"
1058
    r, closed = find_str(cur)
428✔
1059
    if r !== nothing
387✔
1060
        s = do_string_unescape(string[r])
82✔
1061
        ret, success = complete_path_string(s, hint; string_escape=true,
41✔
1062
                                            dirsep=Sys.iswindows() ? '\\' : '/')
1063
        if length(ret) == 1 && !closed && close_path_completion(ret[1].path)
41✔
1064
            ret[1] = PathCompletion(ret[1].path * '"')
9✔
1065
        end
1066
        success && return ret, r, success
41✔
1067
    end
1068

1069
    # Backlash symbols:
1070
    #   \pi => π
1071
    # Comes after string completion so backslash escapes are not misinterpreted.
1072
    ok, ret = bslash_completions(string, pos)
365✔
1073
    ok && return ret
365✔
1074

1075
    # Don't fall back to symbol completion inside strings or comments.
1076
    inside_cmdstr = find_parent(cur, K"cmdstring") !== nothing
331✔
1077
    (kind(cur) in KSet"String Comment ErrorEofMultiComment" || inside_cmdstr) &&
656✔
1078
         return Completion[], 1:0, false
1079

1080
    n, arg_pos = find_prefix_call(cur_not_ws)
447✔
1081
    if n !== nothing
323✔
1082
        func = first(children_nt(n))
124✔
1083
        e = Expr(n)
124✔
1084
        # Remove arguments past the first parse error (allows unclosed parens)
1085
        if is_broadcasting_expr(e)
124✔
1086
            i = findfirst(x -> x isa Expr && x.head == :error, e.args[2].args)
24✔
1087
            i !== nothing && deleteat!(e.args[2].args, i:lastindex(e.args[2].args))
6✔
1088
        else
1089
            i = findfirst(x -> x isa Expr && x.head == :error, e.args)
1,004✔
1090
            i !== nothing && deleteat!(e.args, i:lastindex(e.args))
118✔
1091
        end
1092

1093
        # Method completion:
1094
        #   foo( TAB     => list of method signatures for foo
1095
        #   foo(x, TAB   => list of methods signatures for foo with x as first argument
1096
        if kind(cur_not_ws) in KSet"( , ;"
237✔
1097
            # Don't provide method completions unless the cursor is after: '(' ',' ';'
1098
            return complete_methods(e, context_module, shift, arg_pos), char_range(func), false
82✔
1099

1100
        # Keyword argument completion:
1101
        #   foo(ar TAB   => keyword arguments like `arg1=`
1102
        elseif kind(cur) == K"Identifier"
42✔
1103
            r = char_range(cur)
42✔
1104
            s = string[intersect(r, 1:pos)]
84✔
1105
            # Return without adding more suggestions if kwargs only
1106
            complete_keyword_argument!(suggestions, e, s, context_module, arg_pos; shift) &&
42✔
1107
                return sort_suggestions(), r, true
1108
        end
1109
    end
1110

1111
    # Symbol completion
1112
    # TODO: Should completions replace the identifier at the cursor?
1113
    looks_like_ident = Base.isidentifier(@view string[intersect(char_range(cur), 1:pos)])
218✔
1114
    if cur.parent !== nothing && kind(cur.parent) == K"var"
421✔
1115
        # Replace the entire var"foo", but search using only "foo".
1116
        r = intersect(char_range(cur.parent), 1:pos)
3✔
1117
        r2 = char_range(children_nt(cur.parent)[1])
4✔
1118
        s = string[intersect(r2, 1:pos)]
5✔
1119
    elseif kind(cur) == K"MacroName"
210✔
1120
        # Include the `@`
1121
        r = intersect(prevind(string, cur.position):char_last(cur), 1:pos)
12✔
1122
        s = string[r]
12✔
1123
    elseif looks_like_ident || kind(cur) in KSet"Bool Identifier @"
363✔
1124
        r = intersect(char_range(cur), 1:pos)
117✔
1125
        s = string[r]
117✔
1126
    else
1127
        r = nextind(string, pos):pos
162✔
1128
        s = ""
81✔
1129
    end
1130

1131
    complete_modules_only = false
213✔
1132
    prefix = node_prefix(cur, context_module)
213✔
1133
    comp_keywords = prefix === nothing && !isempty(s)
213✔
1134

1135
    # Complete loadable module names:
1136
    #   import Mod TAB
1137
    #   import Mod1, Mod2 TAB
1138
    #   using Mod TAB
1139
    if (n = find_parent(cur, K"importpath")) !== nothing
213✔
1140
        # Given input lines like `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |`:
1141
        # Let's look only for packages and modules we can reach from here
1142
        if prefix == nothing
32✔
1143
            complete_loading_candidates!(suggestions, s)
9✔
1144
            return sort_suggestions(), r, true
9✔
1145
        end
1146

1147
        # Allow completion for `import Mod.name` (where `name` is not a module)
1148
        complete_modules_only = prefix == nothing || kind(n.parent) == K"using"
46✔
1149
        comp_keywords = false
23✔
1150
    end
1151

1152
    if comp_keywords
204✔
1153
        complete_keyword!(suggestions, s)
63✔
1154
        complete_keyval!(suggestions, s)
63✔
1155
    end
1156

1157
    complete_symbol!(suggestions, prefix, s, context_module; complete_modules_only, shift)
204✔
1158
    return sort_suggestions(), r, true
204✔
1159
end
1160

1161
function close_path_completion(path)
1162
    path = expanduser(path)
21✔
1163
    path = do_string_unescape(path)
21✔
1164
    !Base.isaccessibledir(path)
21✔
1165
end
1166

1167
# Lowering can misbehave with nested error expressions.
1168
function expr_has_error(@nospecialize(e))
708✔
1169
    e isa Expr || return false
2,019✔
1170
    e.head === :error &&  return true
361✔
1171
    any(expr_has_error, e.args)
361✔
1172
end
1173

1174
# Is the cursor inside the square brackets of a ref expression?  If so, returns:
1175
# - The ref node
1176
# - The range of characters for the brackets
1177
# - A flag indicating if the closing bracket is present
1178
function find_ref_key(cur::CursorNode, pos::Int)
1179
    n = find_parent(cur, K"ref")
450✔
1180
    n !== nothing || return nothing, nothing, nothing
826✔
1181
    key, closed = find_delim(n, K"[", K"]")
148✔
1182
    first(key) - 1 <= pos <= last(key) || return nothing, nothing, nothing
74✔
1183
    n, key, closed
74✔
1184
end
1185

1186
# If the cursor is in a literal string, return the contents and char range
1187
# inside the quotes.  Ignores triple strings.
1188
function find_str(cur::CursorNode)
1189
    n = find_parent(cur, K"string")
387✔
1190
    n !== nothing || return nothing, nothing
733✔
1191
    find_delim(n, K"\"", K"\"")
41✔
1192
end
1193

1194
# Is the cursor directly inside of the arguments of a prefix call (no nested
1195
# expressions)?  If so, return:
1196
#   - The call node
1197
#   - Either :positional or :kwargs, if the cursor is before or after the `;`
1198
function find_prefix_call(cur::CursorNode)
1199
    n = cur.parent
323✔
1200
    n !== nothing || return nothing, nothing
328✔
1201
    is_call(n) = kind(n) in KSet"call dotcall" && is_prefix_call(n)
829✔
1202
    if kind(n) == K"parameters"
318✔
1203
        is_call(n.parent) || return nothing, nothing
55✔
1204
        n.parent, :kwargs
53✔
1205
    else
1206
        # Check that we are beyond the function name.
1207
        is_call(n) && cur.index > children_nt(n)[1].index || return nothing, nothing
457✔
1208
        n, :positional
71✔
1209
    end
1210
end
1211

1212
# If node is the field in a getfield-like expression, return the value
1213
# complete_symbol! should use as the prefix.
1214
function node_prefix(node::CursorNode, context_module::Module)
213✔
1215
    node.parent !== nothing || return nothing
218✔
1216
    p = node.parent
208✔
1217
    # In x.var"y", the parent is the "var" when the cursor is on "y".
1218
    kind(p) == K"var" && (p = p.parent)
416✔
1219

1220
    # expr.node => expr
1221
    if kind(p) == K"."
416✔
1222
        n = children_nt(p)[1]
101✔
1223
        # Don't use prefix if we are the value
1224
        n !== node || return nothing
101✔
1225
        return Expr(n)
101✔
1226
    end
1227

1228
    if kind(p) == K"importpath"
214✔
1229
        if p.parent !== nothing && kind(p.parent) == K":" && p.index_nt > 1
64✔
1230
            # import A.B: C.node
1231
            chain = children_nt(children_nt(p.parent)[1])
12✔
1232
            append!(chain, children_nt(p)[1:end-1])
6✔
1233
        else
1234
            # import A.node
1235
            # import A.node: ...
1236
            chain = children_nt(p)[1:node.index_nt]
52✔
1237
            # Don't include the node under cursor in prefix unless it is `.`
1238
            kind(chain[end]) != K"." && deleteat!(chain, lastindex(chain))
26✔
1239
        end
1240
        length(chain) > 0 || return nothing
41✔
1241

1242
        # (:importpath :x :y :z) => (:. (:. :x :y) :z)
1243
        # (:importpath :. :. :z) => (:. (parentmodule context_module) :z)
1244
        if (i = findlast(x -> kind(x) == K".", chain)) !== nothing
97✔
1245
            init = context_module
19✔
1246
            for j in 2:i
19✔
1247
                init = parentmodule(init)
4✔
1248
            end
5✔
1249
            deleteat!(chain, 1:i)
38✔
1250
        else
1251
            # No leading `.`, init is the first element of the path
1252
            init = chain[1].val
4✔
1253
            deleteat!(chain, 1)
4✔
1254
        end
1255

1256
        # Convert the "chain" into nested (. a b) expressions.
1257
        all(x -> kind(x) == K"Identifier", chain) || return nothing
37✔
1258
        return foldl((x, y) -> Expr(:., x, Expr(:quote, y.val)), chain; init)
37✔
1259
    end
1260

1261
    nothing
75✔
1262
end
1263

1264
function dict_eval(@nospecialize(e), context_module::Module=Main)
74✔
1265
    objt = repl_eval_ex(e.args[1], context_module)
74✔
1266
    isa(objt, Core.Const) || return nothing
77✔
1267
    obj = objt.val
71✔
1268
    isa(obj, AbstractDict) || return nothing
72✔
1269
    (Base.haslength(obj) && length(obj)::Int < 1_000_000) || return nothing
70✔
1270
    return obj
70✔
1271
end
1272

1273
function method_search(partial::AbstractString, context_module::Module, shift::Bool)
457✔
1274
    rexm = match(r"([\w.]+.)?\?\((.*)$", partial)
457✔
1275
    if rexm !== nothing
457✔
1276
        # Get the module scope
1277
        callee_module = context_module
14✔
1278
        if !isnothing(rexm.captures[1])
27✔
1279
            modnames = map(Symbol, split(something(rexm.captures[1]), '.'))
26✔
1280
            for m in modnames
13✔
1281
                if isdefined(callee_module, m)
26✔
1282
                    callee_module = getfield(callee_module, m)
13✔
1283
                    if !isa(callee_module, Module)
13✔
1284
                        callee_module = context_module
×
1285
                        break
×
1286
                    end
1287
                end
1288
            end
26✔
1289
        end
1290
        moreargs = !endswith(rexm.captures[2], ')')
14✔
1291
        callstr = "_(" * rexm.captures[2]
14✔
1292
        if moreargs
14✔
1293
            callstr *= ')'
8✔
1294
        end
1295
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
14✔
1296
        if isa(ex_org, Expr)
14✔
1297
            pos_q = isnothing(rexm.captures[1]) ? 1 : sizeof(something(rexm.captures[1]))+1 # position after ?
27✔
1298
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:pos_q) .+ rexm.offset, false
14✔
1299
        end
1300
    end
1301
end
1302

1303
function shell_completions(str, pos, hint::Bool=false; cmd_escape::Bool=false)
68✔
1304
    # First parse everything up to the current position
1305
    scs = str[1:pos]
48✔
1306
    args, last_arg_start = try
24✔
1307
        Base.shell_parse(scs, true)::Tuple{Expr,Int}
25✔
1308
    catch ex
1309
        ex isa ArgumentError || ex isa ErrorException || rethrow()
2✔
1310
        return Completion[], 1:0, false
24✔
1311
    end
1312
    ex = args.args[end]::Expr
23✔
1313
    # Now look at the last thing we parsed
1314
    isempty(ex.args) && return Completion[], 1:0, false
23✔
1315
    # Concatenate every string fragment so dir\file completes correctly.
1316
    lastarg = all(x -> x isa String, ex.args) ? string(ex.args...) : ex.args[end]
69✔
1317

1318
    # As Base.shell_parse throws away trailing spaces (unless they are escaped),
1319
    # we need to special case here.
1320
    # If the last char was a space, but shell_parse ignored it search on "".
1321
    if isexpr(lastarg, :incomplete) || isexpr(lastarg, :error)
44✔
1322
        partial = str[last_arg_start:pos]
4✔
1323
        ret, range = completions(partial, lastindex(partial), Main, true, hint)
4✔
1324
        range = range .+ (last_arg_start - 1)
2✔
1325
        return ret, range, true
2✔
1326
    elseif endswith(scs, ' ') && !endswith(scs, "\\ ")
21✔
1327
        r = pos+1:pos
4✔
1328
        paths, dir, success = complete_path(""; use_envpath=false, shell_escape=!cmd_escape, cmd_escape, dirsep='/')
2✔
1329
        return paths, r, success
2✔
1330
    elseif all(@nospecialize(arg) -> arg isa AbstractString, ex.args)
43✔
1331
        # Join these and treat this as a path
1332
        path::String = join(ex.args)
18✔
1333
        r = last_arg_start:pos
18✔
1334

1335
        # Also try looking into the env path if the user wants to complete the first argument
1336
        use_envpath = length(args.args) < 2
18✔
1337

1338
        paths, success = complete_path_string(path, hint; use_envpath, shell_escape=!cmd_escape, cmd_escape, dirsep='/')
18✔
1339
        return paths, r, success
18✔
1340
    end
1341
    return Completion[], 1:0, false
1✔
1342
end
1343

1344
function complete_path_string(path, hint::Bool=false;
118✔
1345
                              shell_escape::Bool=false,
1346
                              cmd_escape::Bool=false,
1347
                              string_escape::Bool=false,
1348
                              dirsep='/',
1349
                              kws...)
1350
    # Expand "~" and remember if we expanded it.
1351
    local expanded
59✔
1352
    try
59✔
1353
        let p = expanduser(path)
59✔
1354
            expanded = path != p
58✔
1355
            path = p
59✔
1356
        end
1357
    catch e
1358
        e isa ArgumentError || rethrow()
1✔
1359
        expanded = false
1✔
1360
    end
1361

1362
    function escape(p)
236✔
1363
        shell_escape && (p = do_shell_escape(p))
177✔
1364
        string_escape && (p = do_string_escape(p))
177✔
1365
        cmd_escape && (p = do_cmd_escape(p))
177✔
1366
        p
177✔
1367
    end
1368

1369
    paths, dir, success = complete_path(path; dirsep, kws...)
59✔
1370

1371
    # Expand '~' if the user hits TAB after exhausting completions (either
1372
    # because we have found an existing file, or there is no such file).
1373
    full_path = try
59✔
1374
        ispath(path) || isempty(paths)
101✔
1375
    catch err
1376
        # access(2) errors unhandled by ispath: EACCES, EIO, ELOOP, ENAMETOOLONG
1377
        if err isa Base.IOError
1✔
1378
            false
1✔
1379
        elseif err isa Base.ArgumentError && occursin("embedded NULs", err.msg)
×
1380
            false
×
1381
        else
1382
            rethrow()
60✔
1383
        end
1384
    end
1385
    expanded && !hint && full_path && return Completion[PathCompletion(escape(path))], true
59✔
1386

1387
    # Expand '~' if the user hits TAB on a path ending in '/'.
1388
    expanded && (hint || path != dir * "/") && (dir = contractuser(dir))
57✔
1389

1390
    map!(paths) do c::PathCompletion
197✔
1391
        p = joinpath_withsep(dir, c.path; dirsep)
173✔
1392
        PathCompletion(escape(p))
173✔
1393
    end
1394
    return sort!(paths, by=p->p.path), success
2,171✔
1395
end
1396

1397
function __init__()
5✔
1398
    COMPLETION_WORLD[] = Base.get_world_counter()
5✔
1399
    return nothing
5✔
1400
end
1401

1402
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