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

JuliaLang / julia / #38193

22 Aug 2025 06:29AM UTC coverage: 77.913% (+0.06%) from 77.852%
#38193

push

local

web-flow
test: add missing closewrite call (#59356)

This test would previously just hang if there was any error, instead of
printing the error as intended.

This might be better as an IOBuffer so closewrite is implicit, but
that's behavior is not well-specified right now with multiple writers,
as is done here.

48533 of 62291 relevant lines covered (77.91%)

9669197.75 hits per line

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

95.03
/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
192✔
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,232✔
36
end
37

38
struct ModuleCompletion <: Completion
39
    parent::Module
5,443✔
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,165✔
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
114,756✔
85
        return getfield(c, :text)::String
190✔
86
    elseif name === :keyword
114,566✔
87
        return getfield(c, :keyword)::String
140✔
88
    elseif name === :path
114,426✔
89
        return getfield(c, :path)::String
5,114✔
90
    elseif name === :parent
109,312✔
91
        return getfield(c, :parent)::Module
×
92
    elseif name === :mod
109,312✔
93
        return getfield(c, :mod)::String
102,939✔
94
    elseif name === :package
6,373✔
95
        return getfield(c, :package)::String
1,596✔
96
    elseif name === :property
4,777✔
97
        return getfield(c, :property)::Symbol
207✔
98
    elseif name === :field
4,570✔
99
        return getfield(c, :field)::Symbol
35✔
100
    elseif name === :method
4,535✔
101
        return getfield(c, :method)::Method
4,167✔
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
190✔
115
_completion_text(c::KeywordCompletion) = c.keyword
140✔
116
_completion_text(c::KeyvalCompletion) = c.keyval
19✔
117
_completion_text(c::PathCompletion) = c.path
984✔
118
_completion_text(c::ModuleCompletion) = c.mod
102,939✔
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,042✔
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
109,361✔
128

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

131
function named_completion(c)
6,052✔
132
    text = completion_text(c)::String
109,360✔
133
    return NamedCompletion(text, text)
109,360✔
134
end
135

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

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

140
function completes_global(x, name)
141
    return startswith(x, name) && !('#' in x)
294,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)
152,256✔
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,531✔
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,443✔
179
    end
5,443✔
180
    return suggestions
132✔
181
end
182

183
# REPL Symbol Completions
184
function complete_symbol!(suggestions::Vector{Completion},
410✔
185
                          @nospecialize(prefix), name::String, context_module::Module;
186
                          complete_modules_only::Bool=false,
187
                          shift::Bool=false)
188
    local mod, t, val
205✔
189
    complete_internal_only = isempty(name)
205✔
190
    if prefix !== nothing
205✔
191
        res = repl_eval_ex(prefix, context_module)
125✔
192
        res === nothing && return Completion[]
125✔
193
        if res isa Const
114✔
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)
23✔
207
        end
208
    else
209
        mod = context_module
80✔
210
    end
211

212
    if @isdefined(mod) # lookup names available within the module
194✔
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)
186,533✔
217
                    return false
×
218
                elseif s === modname
186,533✔
219
                    return false # exclude `Main.Main.Main`, etc.
182✔
220
                elseif complete_modules_only && !completes_module(mod, s)
186,351✔
221
                    return false
34,177✔
222
                elseif is_main && s === :MainInclude
152,174✔
223
                    return false
50✔
224
                end
225
                return true
152,124✔
226
            end
227
        end
228
    elseif @isdefined(val) # looking for a property of an instance
62✔
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)
23✔
239
        # Looking for a member of a type
240
        add_field_completions!(suggestions, name, t)
14✔
241
    end
242
    return suggestions
194✔
243
end
244

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

247
function add_field_completions!(suggestions::Vector{Completion}, name::String, @nospecialize(t))
16✔
248
    if isa(t, Union)
18✔
249
        add_field_completions!(suggestions, name, t.a)
2✔
250
        add_field_completions!(suggestions, name, t.b)
2✔
251
    else
252
        @assert isconcretetype(t)
16✔
253
        fields = fieldnames(t)
16✔
254
        for field in fields
16✔
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)
27✔
267
    if isa(t, Union)
27✔
268
        return field_completion_eligible(t.a) && field_completion_eligible(t.b)
2✔
269
    end
270
    isconcretetype(t) || return false
32✔
271
    # field completion is correct only when `getproperty` fallbacks to `getfield`
272
    match = Base._which(Tuple{typeof(propertynames),t}; raise=false)
18✔
273
    match === nothing && return false
18✔
274
    return match.method === GENERIC_PROPERTYNAMES_METHOD
18✔
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)
961✔
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)
948✔
327
    dir == "" && return path
474✔
328
    dir[end] == dirsep ? dir * path : dir * dirsep * path
461✔
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
14✔
375
            realpath(pathdir)
16✔
376
        catch ex
377
            ex isa Base.IOError || rethrow()
2✔
378
            # Bash doesn't expect every folder in PATH to exist, so neither shall we
379
            continue
2✔
380
        end
381

382
        if actualpath != pathdir && in(actualpath, pathdirs)
12✔
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
×
386
        end
387

388
        path_entries = try
12✔
389
            _readdirx(pathdir)
13✔
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
11✔
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
1,627✔
403
                if isfile(entry)
1,627✔
404
                    @lock PATH_cache_lock push!(PATH_cache, entry.name)
1,437✔
405
                    push!(this_PATH_cache, entry.name)
1,439✔
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)
2✔
412
                    continue
2✔
413
                else
414
                    rethrow()
×
415
                end
416
            end
417
            if time() >= next_yield_time
1,625✔
418
                yield() # to avoid blocking typing when -t1
2✔
419
                next_yield_time = time() + 0.01
2✔
420
            end
421
        end
1,627✔
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)
11,457✔
466
            is_dir = try isdir(entry) catch ex; ex isa Base.IOError ? false : rethrow() end
982✔
467
            push!(matches, is_dir ? joinpath_withsep(entry.name, ""; dirsep) : entry.name)
1,724✔
468
        end
469
    end
11,457✔
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)
1,527✔
478
            end
3,054✔
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]
1,007✔
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;
246✔
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)
240✔
533
    end
534
end
535
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
193,716✔
536
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
106✔
537
CC.get_inference_world(interp::REPLInterpreter) = interp.world
132,667✔
538
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
49,208✔
539
CC.cache_owner(::REPLInterpreter) = REPLCacheToken()
58,759✔
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,667✔
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)
262,990✔
575
    CC.is_cached(sv) && return false
371,549✔
576
    parent = CC.frame_parent(sv)
535,286✔
577
    parent === nothing && return true
272,296✔
578
    return is_call_stack_uncached(parent::CC.InferenceState)
262,990✔
579
end
580

581
# aggressive global binding resolution within `repl_frame`
582
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, bailed::Bool,
68,078✔
583
                                    sv::CC.InferenceState)
584
    # Ignore saw_latestworld
585
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_stack_uncached(sv))
135,995✔
586
        partition = CC.abstract_eval_binding_partition!(interp, g, sv)
6,248✔
587
        if CC.is_defined_const_binding(CC.binding_kind(partition))
8,071✔
588
            return CC.RTEffects(Const(CC.partition_restriction(partition)), Union{}, CC.EFFECTS_TOTAL)
6,174✔
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,
61,830✔
598
                                              sv::CC.InferenceState)
599
end
600

601
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
602
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
40,726✔
603
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
604
                                   sv::CC.InferenceState)
605
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_stack_uncached(sv))
81,368✔
606
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
3,068✔
607
        result = CC.MethodCallResult(result.rt, result.exct, neweffects, result.edge,
6,134✔
608
                                     result.edgecycle, result.edgelimited, result.volatile_inf_result)
609
    end
610
    ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
81,452✔
611
                                            result::CC.MethodCallResult, arginfo::CC.ArgInfo,
612
                                            sv::CC.InferenceState)
613
    if ret === :semi_concrete_eval
40,726✔
614
        # while the base eligibility check probably won't permit semi-concrete evaluation
615
        # for `REPLInterpreter` (given it completely turns off optimization),
616
        # this ensures we don't inadvertently enter irinterp
617
        ret = :none
×
618
    end
619
    return ret
40,726✔
620
end
621

622
# allow constant propagation for mutable constants
623
function CC.const_prop_argument_heuristic(interp::REPLInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
624
    if !interp.limit_aggressive_inference
40,180✔
625
        any(@nospecialize(a)->isa(a, Const), arginfo.argtypes) && return true # even if mutable
80,253✔
626
    end
627
    return @invoke CC.const_prop_argument_heuristic(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
102✔
628
end
629

630
# Perform some post-hoc mutation on lowered code, as expected by some abstract interpretation
631
# routines, especially for `:foreigncall` and `:cglobal`.
632
function resolve_toplevel_symbols!(src::Core.CodeInfo, mod::Module)
633
    @ccall jl_resolve_definition_effects_in_ir(
234✔
634
        #=jl_array_t *stmts=# src.code::Any,
635
        #=jl_module_t *m=# mod::Any,
636
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
637
        #=jl_value_t *binding_edge=# C_NULL::Ptr{Cvoid},
638
        #=int binding_effects=# 0::Int)::Cvoid
639
    return src
234✔
640
end
641

642
function construct_toplevel_mi(src::Core.CodeInfo, context_module::Module)
643
    resolve_toplevel_symbols!(src, context_module)
234✔
644
    return @ccall jl_method_instance_for_thunk(src::Any, context_module::Any)::Ref{Core.MethodInstance}
234✔
645
end
646

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

666
    mi = construct_toplevel_mi(src, context_module)
234✔
667
    interp = REPLInterpreter(limit_aggressive_inference)
234✔
668
    result = CC.InferenceResult(mi)
234✔
669
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)
234✔
670

671
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
672
    #      potential invalidations of `Core.Compiler` methods.
673
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
234✔
674

675
    result = frame.result.result
234✔
676
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
234✔
677
    return result
226✔
678
end
679

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

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

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

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

725
MAX_ANY_METHOD_COMPLETIONS::Int = 10
726

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

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

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

749
    for seen_name in accessible(callee_module, callee_module === context_module)
14✔
750
        complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
1,524✔
751
    end
1,524✔
752

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

763
    return out
14✔
764
end
765

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

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

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

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

823
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✔
824
    # Input types and number of arguments
825
    t_in = Tuple{funct, args_ex...}
1,636✔
826
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
1,636✔
827
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
828
    if !isa(m, Vector)
1,636✔
829
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
192✔
830
        return
192✔
831
    end
832
    for match in m
1,444✔
833
        # TODO: if kwargs_ex, filter out methods without kwargs?
834
        push!(out, MethodCompletion(match.spec_types, match.method))
4,165✔
835
    end
4,165✔
836
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
837
end
838

839
include("latex_symbols.jl")
840
include("emoji_symbols.jl")
841

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

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

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

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

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

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

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

937
    for kwarg in kwargs
78✔
938
        push!(suggestions, KeywordArgumentCompletion(kwarg))
42✔
939
    end
84✔
940
    return kwargs_flag != 0 && arg_pos == :kwargs
39✔
941
end
942

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

959
function complete_loading_candidates!(suggestions::Vector{Completion}, s::String)
9✔
960
    for name in ("Core", "Base")
9✔
961
        startswith(name, s) && push!(suggestions, PackageCompletion(name))
18✔
962
    end
27✔
963

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

1000
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true, hint::Bool=false)
474✔
1001
    # filename needs to be string so macro can be evaluated
1002
    node = parseall(CursorNode, string, ignore_errors=true, keep_parens=true, filename="none")
916✔
1003
    cur = @something seek_pos(node, pos) node
470✔
1004

1005
    # Back up before whitespace to get a more useful AST node.
1006
    pos_not_ws = findprev(!isspace, string, pos)
465✔
1007
    cur_not_ws = something(seek_pos(node, pos_not_ws), node)
925✔
1008

1009
    suggestions = Completion[]
465✔
1010
    sort_suggestions() = sort!(unique!(named_completion, suggestions), by=named_completion_completion)
707✔
1011

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

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

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

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

1065
    # Backlash symbols:
1066
    #   \pi => π
1067
    # Comes after string completion so backslash escapes are not misinterpreted.
1068
    ok, ret = bslash_completions(string, pos)
366✔
1069
    ok && return ret
366✔
1070

1071
    # Don't fall back to symbol completion inside strings or comments.
1072
    inside_cmdstr = find_parent(cur, K"cmdstring") !== nothing
332✔
1073
    (kind(cur) in KSet"String Comment ErrorEofMultiComment" || inside_cmdstr) &&
658✔
1074
         return Completion[], 1:0, false
1075

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

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

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

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

1127
    complete_modules_only = false
214✔
1128
    prefix = node_prefix(cur, context_module)
214✔
1129
    comp_keywords = prefix === nothing && !isempty(s)
214✔
1130

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

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

1148
    if comp_keywords
205✔
1149
        complete_keyword!(suggestions, s)
63✔
1150
        complete_keyval!(suggestions, s)
63✔
1151
    end
1152

1153
    complete_symbol!(suggestions, prefix, s, context_module; complete_modules_only, shift)
205✔
1154
    return sort_suggestions(), r, true
205✔
1155
end
1156

1157
function close_path_completion(path)
1158
    path = expanduser(path)
21✔
1159
    path = do_string_unescape(path)
21✔
1160
    !Base.isaccessibledir(path)
21✔
1161
end
1162

1163
# Lowering can misbehave with nested error expressions.
1164
function expr_has_error(@nospecialize(e))
717✔
1165
    e isa Expr || return false
2,035✔
1166
    e.head === :error &&  return true
365✔
1167
    any(expr_has_error, e.args)
365✔
1168
end
1169

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

1182
# If the cursor is in a literal string, return the contents and char range
1183
# inside the quotes.  Ignores triple strings.
1184
function find_str(cur::CursorNode)
1185
    n = find_parent(cur, K"string")
388✔
1186
    n !== nothing || return nothing, nothing
735✔
1187
    find_delim(n, K"\"", K"\"")
41✔
1188
end
1189

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

1208
# If node is the field in a getfield-like expression, return the value
1209
# complete_symbol! should use as the prefix.
1210
function node_prefix(node::CursorNode, context_module::Module)
214✔
1211
    node.parent !== nothing || return nothing
219✔
1212
    p = node.parent
209✔
1213
    # In x.var"y", the parent is the "var" when the cursor is on "y".
1214
    kind(p) == K"var" && (p = p.parent)
418✔
1215

1216
    # expr.node => expr
1217
    if kind(p) == K"."
418✔
1218
        n = children_nt(p)[1]
102✔
1219
        # Don't use prefix if we are the value
1220
        n !== node || return nothing
102✔
1221
        return Expr(n)
102✔
1222
    end
1223

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

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

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

1257
    nothing
75✔
1258
end
1259

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

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

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

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

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

1334
        paths, success = complete_path_string(path, hint; use_envpath, shell_escape=!cmd_escape, cmd_escape, dirsep='/')
18✔
1335
        return paths, r, success
18✔
1336
    end
1337
    return Completion[], 1:0, false
1✔
1338
end
1339

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

1358
    function escape(p)
299✔
1359
        shell_escape && (p = do_shell_escape(p))
240✔
1360
        string_escape && (p = do_string_escape(p))
240✔
1361
        cmd_escape && (p = do_cmd_escape(p))
240✔
1362
        p
240✔
1363
    end
1364

1365
    paths, dir, success = complete_path(path; dirsep, kws...)
59✔
1366

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

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

1386
    map!(paths) do c::PathCompletion
260✔
1387
        p = joinpath_withsep(dir, c.path; dirsep)
236✔
1388
        PathCompletion(escape(p))
236✔
1389
    end
1390
    return sort!(paths, by=p->p.path), success
3,919✔
1391
end
1392

1393
function __init__()
4✔
1394
    COMPLETION_WORLD[] = Base.get_world_counter()
4✔
1395
    return nothing
4✔
1396
end
1397

1398
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