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

JuliaLang / julia / 1576

09 Feb 2026 02:05PM UTC coverage: 75.058% (-1.7%) from 76.711%
1576

push

buildkite

web-flow
Supporting sticky (generational) immix (#57327)

Adding support for sticky immix. Needs to be merged after:

- https://github.com/JuliaLang/julia/pull/57237
- https://github.com/JuliaLang/julia/pull/57176
- https://github.com/JuliaLang/julia/pull/57252
- https://github.com/JuliaLang/julia/pull/57769

---------

Co-authored-by: Luis Eduardo de Souza Amorim <eduardo@bear.moma>

57362 of 76424 relevant lines covered (75.06%)

7438320.97 hits per line

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

92.66
/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_first, char_last, children_nt, find_delim
19

20
abstract type Completion end
21

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

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

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

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

38
struct ModuleCompletion <: Completion
39
    parent::Module
5,037✔
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)
78,968✔
61
end
62

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

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

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

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

82
# interface definition
83
function Base.getproperty(c::Completion, name::Symbol)
84
    if name === :text
202,611✔
85
        return getfield(c, :text)::String
202✔
86
    elseif name === :keyword
202,409✔
87
        return getfield(c, :keyword)::String
170✔
88
    elseif name === :path
202,239✔
89
        return getfield(c, :path)::String
18,552✔
90
    elseif name === :parent
183,687✔
91
        return getfield(c, :parent)::Module
×
92
    elseif name === :mod
183,687✔
93
        return getfield(c, :mod)::String
97,378✔
94
    elseif name === :package
86,309✔
95
        return getfield(c, :package)::String
1,596✔
96
    elseif name === :property
84,713✔
97
        return getfield(c, :property)::Symbol
207✔
98
    elseif name === :field
84,506✔
99
        return getfield(c, :field)::Symbol
35✔
100
    elseif name === :method
84,471✔
101
        return getfield(c, :method)::Method
78,970✔
102
    elseif name === :bslash
5,501✔
103
        return getfield(c, :bslash)::String
×
104
    elseif name === :text
5,501✔
105
        return getfield(c, :text)::String
×
106
    elseif name === :key
5,501✔
107
        return getfield(c, :key)::String
121✔
108
    elseif name === :kwarg
5,380✔
109
        return getfield(c, :kwarg)::String
123✔
110
    end
111
    return getfield(c, name)
5,257✔
112
end
113

114
_completion_text(c::TextCompletion) = c.text
202✔
115
_completion_text(c::KeywordCompletion) = c.keyword
170✔
116
_completion_text(c::KeyvalCompletion) = c.keyval
19✔
117
_completion_text(c::PathCompletion) = c.path
830✔
118
_completion_text(c::ModuleCompletion) = c.mod
97,378✔
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,002✔
123
_completion_text(c::ShellCompletion) = c.text
×
124
_completion_text(c::DictCompletion) = c.key
121✔
125
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
123✔
126

127
completion_text(c) = _completion_text(c)::String
103,683✔
128

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

131
function named_completion(c)
5,268✔
132
    text = completion_text(c)::String
103,682✔
133
    return NamedCompletion(text, text)
103,682✔
134
end
135

136
named_completion_completion(c) = named_completion(c).completion::String
89,106✔
137

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

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

144
function appendmacro!(syms, macros, needle, endchar)
260✔
145
    for macsym in macros
260✔
146
        s = String(macsym)
440✔
147
        if endswith(s, needle)
440✔
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
440✔
153
end
154

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

162
    # don't complete string and command macros when the input matches the internal name like `r_` to `r"`
163
    if !startswith(name, "@")
130✔
164
        filter!(macros) do m
118✔
165
            s = String(m)
224✔
166
            if endswith(s, "_str") || endswith(s, "_cmd")
414✔
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,123✔
175
    appendmacro!(syms, macros, "_str", "\"")
130✔
176
    appendmacro!(syms, macros, "_cmd", "`")
130✔
177
    for sym in syms
130✔
178
        push!(suggestions, ModuleCompletion(mod, sym))
5,037✔
179
    end
5,037✔
180
    return suggestions
130✔
181
end
182

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

212
    if @isdefined(mod) # lookup names available within the module
192✔
213
        let mod_for_check = mod,
130✔
214
            modname = nameof(mod_for_check),
215
            is_main = mod_for_check === Main
216
            append_filtered_mod_names!(suggestions, mod, name, complete_internal_only) do s::Symbol
130✔
217
                if Base.isdeprecated(mod_for_check, s)
192,038✔
218
                    return false
×
219
                elseif s === modname
192,038✔
220
                    return false # exclude `Main.Main.Main`, etc.
180✔
221
                elseif complete_modules_only && !completes_module(mod_for_check, s)
191,858✔
222
                    return false
34,647✔
223
                elseif is_main && s === :MainInclude
157,211✔
224
                    return false
50✔
225
                end
226
                return true
157,161✔
227
            end
228
        end
229
    elseif @isdefined(val) # looking for a property of an instance
62✔
230
        try
39✔
231
            for property in propertynames(val, false)
39✔
232
                # TODO: support integer arguments (#36872)
233
                if property isa Symbol && startswith(string(property), name)
65✔
234
                    push!(suggestions, PropertyCompletion(val, property))
57✔
235
                end
236
            end
65✔
237
        catch
1✔
238
        end
239
    elseif @isdefined(t) && field_completion_eligible(t)
23✔
240
        # Looking for a member of a type
241
        add_field_completions!(suggestions, name, t)
14✔
242
    end
243
    return suggestions
192✔
244
end
245

246
completes_module(mod::Module, x::Symbol) = isdefined(mod, x) && isa(getglobal(mod, x), Module)
35,043✔
247

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

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

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

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

292
const sorted_keywords = let
293
    keywords = map(string, Base.JuliaSyntax.Tokenize.kws)
294
    excluded = ("type", "doc", "var", "VERSION")
295
    filter!(∉(excluded), keywords)
296
    compound = ("abstract", "mutable", "primitive")
297
    filter!(∉(compound), keywords)
298
    push!(keywords, "abstract type", "mutable struct", "primitive type")
299
    # Register additional keywords, not in JuliaSyntax keywords
300
    push!(keywords, "ccall")
301
    sort!(keywords)
302
end
303

304
complete_keyword!(suggestions::Vector{Completion}, s::String) =
67✔
305
    complete_from_list!(suggestions, KeywordCompletion, sorted_keywords, s)
306

307
const sorted_keyvals = ["false", "true"]
308

309
complete_keyval!(suggestions::Vector{Completion}, s::String) =
67✔
310
    complete_from_list!(suggestions, KeyvalCompletion, sorted_keyvals, s)
311

312
function do_cmd_escape(s)
×
313
    return Base.escape_raw_string(Base.shell_escape_posixly(s), '`')
×
314
end
315
function do_shell_escape(s)
316
    return Base.shell_escape_posixly(s)
406✔
317
end
318
function do_string_escape(s)
319
    return escape_string(s, ('\"','$'))
442✔
320
end
321
function do_string_unescape(s)
48✔
322
    s = replace(s, "\\\$"=>"\$")
48✔
323
    try
48✔
324
        unescape_string(s)
48✔
325
    catch e
326
        e isa ArgumentError || rethrow()
6✔
327
        s # it is unlikely, but if it isn't a valid string, maybe it was a valid path, and just needs escape_string called?
6✔
328
    end
329
end
330

331
function joinpath_withsep(dir, path; dirsep)
2,530✔
332
    dir == "" && return path
1,265✔
333
    dir[end] == dirsep ? dir * path : dir * dirsep * path
464✔
334
end
335

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

364
# caches all reachable files in PATH dirs
365
function cache_PATH()
1✔
366
    path = get(ENV, "PATH", nothing)
1✔
367
    path isa String || return
1✔
368

369
    # Calling empty! on PATH_cache would be annoying for async typing hints as completions would temporarily disappear.
370
    # 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.
371
    this_PATH_cache = Set{String}()
1✔
372

373
    @debug "caching PATH files" PATH=path
1✔
374
    pathdirs = split(path, @static Sys.iswindows() ? ";" : ":")
1✔
375

376
    next_yield_time = time() + 0.01
1✔
377

378
    t = @elapsed for pathdir in pathdirs
1✔
379
        actualpath = try
12✔
380
            realpath(pathdir)
12✔
381
        catch ex
382
            ex isa Base.IOError || rethrow()
×
383
            # Bash doesn't expect every folder in PATH to exist, so neither shall we
384
            continue
×
385
        end
386

387
        if actualpath != pathdir && in(actualpath, pathdirs)
36✔
388
            # Remove paths which (after resolving links) are in the env path twice.
389
            # Many distros eg. point /bin to /usr/bin but have both in the env path.
390
            continue
×
391
        end
392

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

429
    @lock PATH_cache_lock begin
1✔
430
        intersect!(PATH_cache, this_PATH_cache) # remove entries from PATH_cache that weren't found this time
1✔
431
    end
432

433
    @debug "caching PATH files took $t seconds" length(pathdirs) length(PATH_cache)
1✔
434
    return PATH_cache
1✔
435
end
436

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

468
    matches = Set{String}()
42✔
469
    for entry in entries
42✔
470
        if startswith(entry.name, prefix)
7,224✔
471
            is_dir = try isdir(entry) catch ex; ex isa Base.IOError ? false : rethrow() end
1,660✔
472
            push!(matches, is_dir ? joinpath_withsep(entry.name, ""; dirsep) : entry.name)
1,225✔
473
        end
474
    end
7,224✔
475

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

487
    matches = ((shell_escape ? do_shell_escape(s) : string_escape ? do_string_escape(s) : s) for s in matches)
42✔
488
    matches = ((cmd_escape ? do_cmd_escape(s) : s) for s in matches)
42✔
489
    matches = Completion[PathCompletion(contract_user ? contractuser(s) : s) for s in matches]
847✔
490
    return matches, dir, !isempty(matches)
42✔
491
end
492

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

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

523
struct REPLCacheToken end
58,041✔
524

525
struct REPLInterpreter <: CC.AbstractInterpreter
526
    limit_aggressive_inference::Bool
527
    world::UInt
528
    inf_params::CC.InferenceParams
529
    opt_params::CC.OptimizationParams
530
    inf_cache::CC.InferenceCache
531
    function REPLInterpreter(limit_aggressive_inference::Bool=false;
251✔
532
                             world::UInt = Base.get_world_counter(),
533
                             inf_params::CC.InferenceParams = CC.InferenceParams(;
534
                                 aggressive_constant_propagation=true),
535
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
536
                             inf_cache::CC.InferenceCache = CC.InferenceCache())
537
        return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache)
245✔
538
    end
539
end
540
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
176,558✔
541
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
91✔
542
CC.get_inference_world(interp::REPLInterpreter) = interp.world
280,600✔
543
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
96,605✔
544
CC.cache_owner(::REPLInterpreter) = REPLCacheToken()
58,041✔
545

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

549
# REPLInterpreter doesn't need any sources to be cached, so discard them aggressively
550
CC.transform_result_for_cache(::REPLInterpreter, ::CC.InferenceResult, edges::Core.SimpleVector) = nothing
3,643✔
551

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

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

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

579
function is_call_stack_uncached(sv::CC.InferenceState)
223,931✔
580
    CC.is_cached(sv) && return false
323,972✔
581
    parent = CC.frame_parent(sv)
456,872✔
582
    parent === nothing && return true
232,941✔
583
    return is_call_stack_uncached(parent::CC.InferenceState)
223,931✔
584
end
585

586
# aggressive global binding resolution within `repl_frame`
587
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, bailed::Bool,
63,549✔
588
                                    sv::CC.InferenceState)
589
    # Ignore saw_latestworld
590
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_stack_uncached(sv))
126,937✔
591
        partition = CC.abstract_eval_binding_partition!(interp, g, sv)
6,034✔
592
        if CC.is_defined_const_binding(CC.binding_kind(partition))
7,829✔
593
            return CC.RTEffects(Const(CC.partition_restriction(partition)), Union{}, CC.EFFECTS_TOTAL)
5,960✔
594
        else
595
            b = convert(Core.Binding, g)
74✔
596
            if CC.binding_kind(partition) == CC.PARTITION_KIND_GLOBAL && isdefined(b, :value)
74✔
597
                return CC.RTEffects(Const(b.value), Union{}, CC.EFFECTS_TOTAL)
67✔
598
            end
599
        end
600
        return CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS)
7✔
601
    end
602
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, bailed::Bool,
57,515✔
603
                                              sv::CC.InferenceState)
604
end
605

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

627
# allow constant propagation for mutable constants
628
function CC.const_prop_argument_heuristic(interp::REPLInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
629
    if !interp.limit_aggressive_inference
36,196✔
630
        any(@nospecialize(a)->isa(a, Const), arginfo.argtypes) && return true # even if mutable
72,288✔
631
    end
632
    return @invoke CC.const_prop_argument_heuristic(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
103✔
633
end
634

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

647
function construct_toplevel_mi(src::Core.CodeInfo, context_module::Module)
648
    resolve_toplevel_symbols!(src, context_module)
239✔
649
    return @ccall jl_method_instance_for_thunk(src::Any, context_module::Any)::Ref{Core.MethodInstance}
239✔
650
end
651

652
# lower `ex` and run type inference on the resulting top-level expression
653
function repl_eval_ex(@nospecialize(ex), context_module::Module; limit_aggressive_inference::Bool=false)
1,008✔
654
    expr_has_error(ex) && return nothing
766✔
655
    if (isexpr(ex, :toplevel) || isexpr(ex, :tuple)) && !isempty(ex.args)
996✔
656
        # get the inference result for the last expression
657
        ex = ex.args[end]
1✔
658
    end
659
    lwr = try
498✔
660
        Meta.lower(context_module, ex)
498✔
661
    catch # macro expansion failed, etc.
662
        return nothing
5✔
663
    end
664
    if lwr isa Symbol
493✔
665
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
153✔
666
    end
667
    lwr isa Expr || return Const(lwr) # `ex` is literal
433✔
668
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
255✔
669
    src = lwr.args[1]::Core.CodeInfo
239✔
670

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

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

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

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

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

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

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

730
MAX_ANY_METHOD_COMPLETIONS::Int = 10
731

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

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

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

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

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

768
    return out
14✔
769
end
770

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

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

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

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

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

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

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

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

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

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

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

908
    methods = Completion[]
50✔
909
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, arg_pos == :kwargs)
50✔
910
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
911
    # method calls compatible with the current arguments.
912

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

936
    for kwarg in kwargs
100✔
937
        push!(suggestions, KeywordArgumentCompletion(kwarg))
55✔
938
    end
110✔
939
    return kwargs_flag != 0 && arg_pos == :kwargs
50✔
940
end
941

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

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

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

999
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true, hint::Bool=false)
494✔
1000
    # filename needs to be string so macro can be evaluated
1001
    # TODO: JuliaSyntax version API here
1002
    node = parseall(CursorNode, string, ignore_errors=true, keep_parens=true, filename="none")
939✔
1003
    cur = @something seek_pos(node, pos) node
483✔
1004

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

1009
    suggestions = Completion[]
478✔
1010
    sort_suggestions() = sort!(unique!(named_completion, suggestions), by=named_completion_completion)
729✔
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
478✔
1017
        cs = method_search(view(string, 1:pos), context_module, shift)
467✔
1018
        cs !== nothing && return cs
467✔
1019
    end
1020

1021
    # Complete keys in a Dict:
1022
    #   my_dict[ TAB
1023
    n, key, closed = find_ref_key(cur_not_ws, pos)
539✔
1024
    if n !== nothing
464✔
1025
        key::UnitRange{Int}
75✔
1026
        obj = dict_eval(Expr(n), context_module)
75✔
1027
        if obj !== nothing
75✔
1028
            # Skip leading whitespace inside brackets.
1029
            i = @something findnext(!isspace, string, first(key)) nextind(string, last(key))
76✔
1030
            key = intersect(i:last(key), 1:pos)
76✔
1031
            s = string[key]
137✔
1032
            matches = find_dict_matches(obj, s)
71✔
1033
            length(matches) == 1 && !closed && (matches[1] *= ']')
71✔
1034
            if length(matches) > 0
71✔
1035
                ret = Completion[DictCompletion(obj, match) for match in sort!(matches)]
63✔
1036
                return ret, key, true
63✔
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
401✔
1046
        off = char_first(n) - 1
1✔
1047
        ret, r, success = shell_completions(string[char_range(n)], pos - off, hint, cmd_escape=true)
2✔
1048
        success && return ret, r .+ off, success
1✔
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)
438✔
1055
    if r !== nothing
401✔
1056
        r = intersect(r, 1:pos)
37✔
1057
        s = do_string_unescape(string[r])
74✔
1058
        ret, success = complete_path_string(s, hint; string_escape=true,
37✔
1059
                                            dirsep=Sys.iswindows() ? '\\' : '/')
1060
        if length(ret) == 1 && !closed && close_path_completion(ret[1].path)
37✔
1061
            ret[1] = PathCompletion(ret[1].path * '"')
3✔
1062
        end
1063
        success && return ret, r, success
37✔
1064
    end
1065

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

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

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

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

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

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

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

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

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

1149
    if comp_keywords
209✔
1150
        complete_keyword!(suggestions, s)
67✔
1151
        complete_keyval!(suggestions, s)
67✔
1152
    end
1153

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

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

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

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

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

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

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

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

1228
    if kind(p) == K"importpath"
210✔
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
73✔
1262
end
1263

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

1273
function method_search(partial::AbstractString, context_module::Module, shift::Bool)
467✔
1274
    rexm = match(r"([\w.]+.)?\?\((.*)$", partial)
467✔
1275
    if rexm !== nothing
467✔
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)
32✔
1304
    # First parse everything up to the current position
1305
    scs = str[1:pos]
24✔
1306
    args, last_arg_start = try
12✔
1307
        Base.shell_parse(scs, true)::Tuple{Expr,Int}
14✔
1308
    catch ex
1309
        ex isa ArgumentError || ex isa ErrorException || rethrow()
4✔
1310
        return Completion[], 1:0, false
12✔
1311
    end
1312
    ex = args.args[end]::Expr
10✔
1313
    # Now look at the last thing we parsed
1314
    isempty(ex.args) && return Completion[], 1:0, false
10✔
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]
30✔
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)
19✔
1322
        partial = str[last_arg_start:pos]
2✔
1323
        ret, range = completions(partial, lastindex(partial), Main, true, hint)
2✔
1324
        range = range .+ (last_arg_start - 1)
1✔
1325
        return ret, range, true
1✔
1326
    elseif endswith(scs, ' ') && !endswith(scs, "\\ ")
9✔
1327
        r = pos+1:pos
×
1328
        paths, dir, success = complete_path(""; use_envpath=false, shell_escape=!cmd_escape, cmd_escape, dirsep='/')
×
1329
        return paths, r, success
×
1330
    elseif all(@nospecialize(arg) -> arg isa AbstractString, ex.args)
25✔
1331
        # Join these and treat this as a path
1332
        path::String = join(ex.args)
8✔
1333
        r = last_arg_start:pos
8✔
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
8✔
1337

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

1344
function complete_path_string(path, hint::Bool=false;
90✔
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
45✔
1352
    try
45✔
1353
        let p = expanduser(path)
45✔
1354
            expanded = path != p
45✔
1355
            path = p
45✔
1356
        end
1357
    catch e
1358
        e isa ArgumentError || rethrow()
×
1359
        expanded = false
×
1360
    end
1361

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

1369
    paths, dir, success = complete_path(path; dirsep, kws...)
45✔
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
45✔
1374
        ispath(path) || isempty(paths)
78✔
1375
    catch err
1376
        # access(2) errors unhandled by ispath: EACCES, EIO, ELOOP, ENAMETOOLONG
1377
        if err isa Base.IOError
×
1378
            false
×
1379
        elseif err isa Base.ArgumentError && occursin("embedded NULs", err.msg)
×
1380
            false
×
1381
        else
1382
            rethrow()
45✔
1383
        end
1384
    end
1385
    expanded && !hint && full_path && return Completion[PathCompletion(escape(path))], true
45✔
1386

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

1391
    map!(paths) do c::PathCompletion
850✔
1392
        p = joinpath_withsep(dir_for_paths, c.path; dirsep)
830✔
1393
        PathCompletion(escape(p))
830✔
1394
    end
1395
    return sort!(paths, by=p->p.path), success
16,923✔
1396
end
1397

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

1403
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