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

JuliaLang / julia / #38062

06 May 2025 07:43AM UTC coverage: 25.823% (-0.01%) from 25.834%
#38062

push

local

web-flow
improve robustness of `Vararg` checks in newly added `abstract_eval_xxx` (#58325)

Otherwise these subroutines may raise `unhandled Vararg` errors when
compiling e.g. CassetteOverlay for complex call graphs.

12950 of 50150 relevant lines covered (25.82%)

1074074.2 hits per line

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

0.42
/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
24
end
25

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

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

34
struct PathCompletion <: Completion
35
    path::String
36
end
37

38
struct ModuleCompletion <: Completion
39
    parent::Module
40
    mod::String
41
end
42

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

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

52
struct FieldCompletion <: Completion
53
    typ::DataType
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)
×
61
end
62

63
struct BslashCompletion <: Completion
64
    completion::String # what is actually completed, for example "\trianglecdot"
65
    name::String # what is displayed, for example "◬ \trianglecdot"
66
end
67
BslashCompletion(completion::String) = BslashCompletion(completion, completion)
×
68

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

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

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

82
# interface definition
83
function Base.getproperty(c::Completion, name::Symbol)
×
84
    if name === :text
×
85
        return getfield(c, :text)::String
×
86
    elseif name === :keyword
×
87
        return getfield(c, :keyword)::String
×
88
    elseif name === :path
×
89
        return getfield(c, :path)::String
×
90
    elseif name === :parent
×
91
        return getfield(c, :parent)::Module
×
92
    elseif name === :mod
×
93
        return getfield(c, :mod)::String
×
94
    elseif name === :package
×
95
        return getfield(c, :package)::String
×
96
    elseif name === :property
×
97
        return getfield(c, :property)::Symbol
×
98
    elseif name === :field
×
99
        return getfield(c, :field)::Symbol
×
100
    elseif name === :method
×
101
        return getfield(c, :method)::Method
×
102
    elseif name === :bslash
×
103
        return getfield(c, :bslash)::String
×
104
    elseif name === :text
×
105
        return getfield(c, :text)::String
×
106
    elseif name === :key
×
107
        return getfield(c, :key)::String
×
108
    elseif name === :kwarg
×
109
        return getfield(c, :kwarg)::String
×
110
    end
111
    return getfield(c, name)
×
112
end
113

114
_completion_text(c::TextCompletion) = c.text
×
115
_completion_text(c::KeywordCompletion) = c.keyword
×
116
_completion_text(c::KeyvalCompletion) = c.keyval
×
117
_completion_text(c::PathCompletion) = c.path
×
118
_completion_text(c::ModuleCompletion) = c.mod
×
119
_completion_text(c::PackageCompletion) = c.package
×
120
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
×
121
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
×
122
_completion_text(c::MethodCompletion) = repr(c.method)
×
123
_completion_text(c::ShellCompletion) = c.text
×
124
_completion_text(c::DictCompletion) = c.key
×
125
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
×
126

127
completion_text(c) = _completion_text(c)::String
×
128

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

131
function named_completion(c)
×
132
    text = completion_text(c)::String
×
133
    return NamedCompletion(text, text)
×
134
end
135

136
named_completion_completion(c) = named_completion(c).completion::String
×
137

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

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

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

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

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

183
# REPL Symbol Completions
184
function complete_symbol!(suggestions::Vector{Completion},
×
185
                          @nospecialize(prefix), name::String, context_module::Module;
186
                          complete_modules_only::Bool=false,
187
                          shift::Bool=false)
188
    local mod, t, val
×
189
    complete_internal_only = false
×
190
    if prefix !== nothing
×
191
        res = repl_eval_ex(prefix, context_module)
×
192
        res === nothing && return Completion[]
×
193
        if res isa Const
×
194
            val = res.val
×
195
            if isa(val, Module)
×
196
                mod = val
×
197
                if !shift
×
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
×
201
                end
202
            else
203
                t = typeof(val)
×
204
            end
205
        else
206
            t = CC.widenconst(res)
×
207
        end
208
    else
209
        mod = context_module
×
210
    end
211

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

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

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

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

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

277
function complete_from_list!(suggestions::Vector{Completion}, T::Type, list::Vector{String}, s::String)
×
278
    r = searchsorted(list, s)
×
279
    i = first(r)
×
280
    n = length(list)
×
281
    while i <= n && startswith(list[i],s)
×
282
        r = first(r):i
×
283
        i += 1
×
284
    end
×
285
    for kw in list[r]
×
286
        push!(suggestions, T(kw))
×
287
    end
×
288
    return suggestions
×
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) =
×
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) =
×
305
    complete_from_list!(suggestions, KeyvalCompletion, sorted_keyvals, s)
306

307
function do_cmd_escape(s)
×
308
    return Base.escape_raw_string(Base.shell_escape_posixly(s), '`')
×
309
end
310
function do_shell_escape(s)
×
311
    return Base.shell_escape_posixly(s)
×
312
end
313
function do_string_escape(s)
×
314
    return escape_string(s, ('\"','$'))
×
315
end
316
function do_string_unescape(s)
×
317
    s = replace(s, "\\\$"=>"\$")
×
318
    try
×
319
        unescape_string(s)
×
320
    catch e
321
        e isa ArgumentError || rethrow()
×
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?
×
323
    end
324
end
325

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

331
const PATH_cache_lock = Base.ReentrantLock()
332
const PATH_cache = Set{String}()
333
PATH_cache_task::Union{Task,Nothing} = nothing # used for sync in tests
334
next_cache_update::Float64 = 0.0
335
function maybe_spawn_cache_PATH()
×
336
    global PATH_cache_task, next_cache_update
×
337
    @lock PATH_cache_lock begin
×
338
        PATH_cache_task isa Task && !istaskdone(PATH_cache_task) && return
×
339
        time() < next_cache_update && return
×
340
        PATH_cache_task = Threads.@spawn begin
×
341
            REPLCompletions.cache_PATH()
×
342
            @lock PATH_cache_lock PATH_cache_task = nothing # release memory when done
×
343
        end
344
        Base.errormonitor(PATH_cache_task)
×
345
    end
346
end
347

348
# caches all reachable files in PATH dirs
349
function cache_PATH()
×
350
    path = get(ENV, "PATH", nothing)
×
351
    path isa String || return
×
352

353
    global next_cache_update
×
354

355
    # Calling empty! on PATH_cache would be annoying for async typing hints as completions would temporarily disappear.
356
    # 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.
357
    this_PATH_cache = Set{String}()
×
358

359
    @debug "caching PATH files" PATH=path
×
360
    pathdirs = split(path, @static Sys.iswindows() ? ";" : ":")
×
361

362
    next_yield_time = time() + 0.01
×
363

364
    t = @elapsed for pathdir in pathdirs
×
365
        actualpath = try
×
366
            realpath(pathdir)
×
367
        catch ex
368
            ex isa Base.IOError || rethrow()
×
369
            # Bash doesn't expect every folder in PATH to exist, so neither shall we
370
            continue
×
371
        end
372

373
        if actualpath != pathdir && in(actualpath, pathdirs)
×
374
            # Remove paths which (after resolving links) are in the env path twice.
375
            # Many distros eg. point /bin to /usr/bin but have both in the env path.
376
            continue
×
377
        end
378

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

415
    @lock PATH_cache_lock begin
×
416
        intersect!(PATH_cache, this_PATH_cache) # remove entries from PATH_cache that weren't found this time
×
417
        next_cache_update = time() + 10 # earliest next update can run is 10s after
×
418
    end
419

420
    @debug "caching PATH files took $t seconds" length(pathdirs) length(PATH_cache)
×
421
    return PATH_cache
×
422
end
423

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

455
    matches = Set{String}()
×
456
    for entry in entries
×
457
        if startswith(entry.name, prefix)
×
458
            is_dir = try isdir(entry) catch ex; ex isa Base.IOError ? false : rethrow() end
×
459
            push!(matches, is_dir ? joinpath_withsep(entry.name, ""; dirsep) : entry.name)
×
460
        end
461
    end
×
462

463
    if use_envpath && isempty(dir)
×
464
        # Look for files in PATH as well. These are cached in `cache_PATH` in an async task to not block typing.
465
        # If we cannot get lock because its still caching just pass over this so that typing isn't laggy.
466
        maybe_spawn_cache_PATH() # only spawns if enough time has passed and the previous caching task has completed
×
467
        @lock PATH_cache_lock begin
×
468
            for file in PATH_cache
×
469
                startswith(file, prefix) && push!(matches, file)
×
470
            end
×
471
        end
472
    end
473

474
    matches = ((shell_escape ? do_shell_escape(s) : string_escape ? do_string_escape(s) : s) for s in matches)
×
475
    matches = ((cmd_escape ? do_cmd_escape(s) : s) for s in matches)
×
476
    matches = Completion[PathCompletion(contract_user ? contractuser(s) : s) for s in matches]
×
477
    return matches, dir, !isempty(matches)
×
478
end
479

480
function complete_path(path::AbstractString,
×
481
                       pos::Int;
482
                       use_envpath=false,
483
                       shell_escape=false,
484
                       string_escape=false,
485
                       contract_user=false)
486
    ## TODO: enable this depwarn once Pkg is fixed
487
    #Base.depwarn("complete_path with pos argument is deprecated because the return value [2] is incorrect to use", :complete_path)
488
    paths, dir, success = complete_path(path; use_envpath, shell_escape, string_escape, dirsep='/')
×
489

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

510
struct REPLCacheToken end
511

512
struct REPLInterpreter <: CC.AbstractInterpreter
513
    limit_aggressive_inference::Bool
514
    world::UInt
515
    inf_params::CC.InferenceParams
516
    opt_params::CC.OptimizationParams
517
    inf_cache::Vector{CC.InferenceResult}
518
    function REPLInterpreter(limit_aggressive_inference::Bool=false;
×
519
                             world::UInt = Base.get_world_counter(),
520
                             inf_params::CC.InferenceParams = CC.InferenceParams(;
521
                                 aggressive_constant_propagation=true),
522
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
523
                             inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[])
524
        return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache)
×
525
    end
526
end
527
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
×
528
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
×
529
CC.get_inference_world(interp::REPLInterpreter) = interp.world
×
530
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
×
531
CC.cache_owner(::REPLInterpreter) = REPLCacheToken()
×
532

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

536
# REPLInterpreter doesn't need any sources to be cached, so discard them aggressively
537
CC.transform_result_for_cache(::REPLInterpreter, ::CC.InferenceResult, edges::Core.SimpleVector) = nothing
×
538

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

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

561
# `REPLInterpreter` is specifically used by `repl_eval_ex`, where all top-level frames are
562
# `repl_frame` always. However, this assumption wouldn't stand if `REPLInterpreter` were to
563
# be employed, for instance, by `typeinf_ext_toplevel`.
564
is_repl_frame(sv::CC.InferenceState) = sv.linfo.def isa Module && sv.cache_mode === CC.CACHE_MODE_NULL
×
565

566
function is_call_graph_uncached(sv::CC.InferenceState)
×
567
    CC.is_cached(sv) && return false
×
568
    parent = CC.frame_parent(sv)
×
569
    parent === nothing && return true
×
570
    return is_call_graph_uncached(parent::CC.InferenceState)
×
571
end
572

573
# aggressive global binding resolution within `repl_frame`
574
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, bailed::Bool,
×
575
                                    sv::CC.InferenceState)
576
    # Ignore saw_latestworld
577
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv))
×
578
        partition = CC.abstract_eval_binding_partition!(interp, g, sv)
×
579
        if CC.is_defined_const_binding(CC.binding_kind(partition))
×
580
            return CC.RTEffects(Const(CC.partition_restriction(partition)), Union{}, CC.EFFECTS_TOTAL)
×
581
        else
582
            b = convert(Core.Binding, g)
×
583
            if CC.binding_kind(partition) == CC.PARTITION_KIND_GLOBAL && isdefined(b, :value)
×
584
                return CC.RTEffects(Const(b.value), Union{}, CC.EFFECTS_TOTAL)
×
585
            end
586
        end
587
        return CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS)
×
588
    end
589
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, bailed::Bool,
×
590
                                              sv::CC.InferenceState)
591
end
592

593
function is_repl_frame_getproperty(sv::CC.InferenceState)
×
594
    def = sv.linfo.def
×
595
    def isa Method || return false
×
596
    def.name === :getproperty || return false
×
597
    CC.is_cached(sv) && return false
×
598
    return is_repl_frame(CC.frame_parent(sv))
×
599
end
600

601
# aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame`
602
function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f),
×
603
                              argtypes::Vector{Any}, sv::CC.InferenceState)
604
    if f === Core.getglobal && (interp.limit_aggressive_inference ? is_repl_frame_getproperty(sv) : is_call_graph_uncached(sv))
×
605
        if length(argtypes) == 2
×
606
            a1, a2 = argtypes
×
607
            if isa(a1, Const) && isa(a2, Const)
×
608
                a1val, a2val = a1.val, a2.val
×
609
                if isa(a1val, Module) && isa(a2val, Symbol)
×
610
                    g = GlobalRef(a1val, a2val)
×
611
                    if isdefined_globalref(g)
×
612
                        return Const(ccall(:jl_get_globalref_value, Any, (Any,), g))
×
613
                    end
614
                    return Union{}
×
615
                end
616
            end
617
        end
618
    end
619
    return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any,
×
620
                                        argtypes::Vector{Any}, sv::CC.InferenceState)
621
end
622

623
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
624
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
×
625
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
626
                                   sv::CC.InferenceState)
627
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_graph_uncached(sv))
×
628
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
×
629
        result = CC.MethodCallResult(result.rt, result.exct, neweffects, result.edge,
×
630
                                     result.edgecycle, result.edgelimited, result.volatile_inf_result)
631
    end
632
    ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
×
633
                                            result::CC.MethodCallResult, arginfo::CC.ArgInfo,
634
                                            sv::CC.InferenceState)
635
    if ret === :semi_concrete_eval
×
636
        # while the base eligibility check probably won't permit semi-concrete evaluation
637
        # for `REPLInterpreter` (given it completely turns off optimization),
638
        # this ensures we don't inadvertently enter irinterp
639
        ret = :none
×
640
    end
641
    return ret
×
642
end
643

644
# allow constant propagation for mutable constants
645
function CC.const_prop_argument_heuristic(interp::REPLInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
×
646
    if !interp.limit_aggressive_inference
×
647
        any(@nospecialize(a)->isa(a, Const), arginfo.argtypes) && return true # even if mutable
×
648
    end
649
    return @invoke CC.const_prop_argument_heuristic(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
×
650
end
651

652
function resolve_toplevel_symbols!(src::Core.CodeInfo, mod::Module)
×
653
    @ccall jl_resolve_definition_effects_in_ir(
×
654
        #=jl_array_t *stmts=# src.code::Any,
655
        #=jl_module_t *m=# mod::Any,
656
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
657
        #=jl_value_t *binding_edge=# C_NULL::Ptr{Cvoid},
658
        #=int binding_effects=# 0::Int)::Cvoid
659
    return src
×
660
end
661

662
# lower `ex` and run type inference on the resulting top-level expression
663
function repl_eval_ex(@nospecialize(ex), context_module::Module; limit_aggressive_inference::Bool=false)
×
664
    expr_has_error(ex) && return nothing
×
665
    if (isexpr(ex, :toplevel) || isexpr(ex, :tuple)) && !isempty(ex.args)
×
666
        # get the inference result for the last expression
667
        ex = ex.args[end]
×
668
    end
669
    lwr = try
×
670
        Meta.lower(context_module, ex)
×
671
    catch # macro expansion failed, etc.
672
        return nothing
×
673
    end
674
    if lwr isa Symbol
×
675
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
×
676
    end
677
    lwr isa Expr || return Const(lwr) # `ex` is literal
×
678
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
×
679
    src = lwr.args[1]::Core.CodeInfo
×
680

681
    resolve_toplevel_symbols!(src, context_module)
×
682
    # construct top-level `MethodInstance`
683
    mi = ccall(:jl_method_instance_for_thunk, Ref{Core.MethodInstance}, (Any, Any), src, context_module)
×
684

685
    interp = REPLInterpreter(limit_aggressive_inference)
×
686
    result = CC.InferenceResult(mi)
×
687
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)
×
688

689
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
690
    #      potential invalidations of `Core.Compiler` methods.
691
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
×
692

693
    result = frame.result.result
×
694
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
×
695
    return result
×
696
end
697

698
# `COMPLETION_WORLD[]` will be initialized within `__init__`
699
# (to allow us to potentially remove REPL from the sysimage in the future).
700
# Note that inference from the `code_typed` call below will use the current world age
701
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
702
# when the given world age is higher than the current one.
703
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
704

705
# Generate code cache for `REPLInterpreter` now:
706
# This code cache will be available at the world of `COMPLETION_WORLD`,
707
# assuming no invalidation will happen before initializing REPL.
708
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
709
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
710

711
# Method completion on function call expression that look like :(max(1))
712
MAX_METHOD_COMPLETIONS::Int = 40
713
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
×
714
    isempty(ex_org.args) && return 2, nothing, [], Set{Symbol}()
×
715
    funct = repl_eval_ex(ex_org.args[1], context_module)
×
716
    funct === nothing && return 2, nothing, [], Set{Symbol}()
×
717
    funct = CC.widenconst(funct)
×
718
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
×
719
    return kwargs_flag, funct, args_ex, kwargs_ex
×
720
end
721

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

734
MAX_ANY_METHOD_COMPLETIONS::Int = 10
735

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

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

754
    # moreargs determines whether to accept more args, independently of the presence of a
755
    # semicolon for the ".?(" syntax
756
    moreargs && push!(args_ex, Vararg{Any})
×
757

758
    for seen_name in accessible(callee_module, callee_module === context_module)
×
759
        complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
×
760
    end
×
761

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

772
    return out
×
773
end
774

775
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
×
776
    n = isexpr(x, :kw) ? x.args[1] : x
×
777
    if n isa Symbol
×
778
        push!(kwargs_ex, n)
×
779
        return kwargs_flag
×
780
    end
781
    possible_splat && isexpr(x, :...) && return kwargs_flag
×
782
    return 2 # The kwarg is invalid
×
783
end
784

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

823
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
×
824

825
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
×
826
    if allow_broadcasting && is_broadcasting_expr(ex)
×
827
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
×
828
    end
829
    return detect_args_kwargs(ex.args, context_module, default_any, false)
×
830
end
831

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

848
include("latex_symbols.jl")
849
include("emoji_symbols.jl")
850

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

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

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

893
# This needs to be a separate non-inlined function, see #19441
894
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
×
895
    matches = String[]
×
896
    for key in keys(identifier)
×
897
        rkey = repr(key)
×
898
        startswith(rkey,partial_key) && push!(matches,rkey)
×
899
    end
×
900
    return matches
×
901
end
902

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

912
    methods = Completion[]
×
913
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, shift ? -1 : MAX_METHOD_COMPLETIONS, arg_pos == :kwargs)
×
914
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
915
    # method calls compatible with the current arguments.
916

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

940
    for kwarg in kwargs
×
941
        push!(suggestions, KeywordArgumentCompletion(kwarg))
×
942
    end
×
943
    return kwargs_flag != 0 && arg_pos == :kwargs
×
944
end
945

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

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

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

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

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

1012
    suggestions = Completion[]
×
1013
    sort_suggestions() = sort!(unique!(named_completion, suggestions), by=named_completion_completion)
×
1014

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

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

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

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

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

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

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

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

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

1110
    # Symbol completion
1111
    # TODO: Should completions replace the identifier at the cursor?
1112
    if cur.parent !== nothing && kind(cur.parent) == K"var"
×
1113
        # Replace the entire var"foo", but search using only "foo".
1114
        r = intersect(char_range(cur.parent), 1:pos)
×
1115
        r2 = char_range(children_nt(cur.parent)[1])
×
1116
        s = string[intersect(r2, 1:pos)]
×
1117
    elseif kind(cur) in KSet"Identifier @"
×
1118
        r = intersect(char_range(cur), 1:pos)
×
1119
        s = string[r]
×
1120
    elseif kind(cur) == K"MacroName"
×
1121
        # Include the `@`
1122
        r = intersect(prevind(string, cur.position):char_last(cur), 1:pos)
×
1123
        s = string[r]
×
1124
    else
1125
        r = nextind(string, pos):pos
×
1126
        s = ""
×
1127
    end
1128

1129
    complete_modules_only = false
×
1130
    prefix = node_prefix(cur, context_module)
×
1131
    comp_keywords = prefix === nothing
×
1132

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

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

1150
    if comp_keywords
×
1151
        complete_keyword!(suggestions, s)
×
1152
        complete_keyval!(suggestions, s)
×
1153
    end
1154

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

1159
function close_path_completion(path)
×
1160
    path = expanduser(path)
×
1161
    path = do_string_unescape(path)
×
1162
    !Base.isaccessibledir(path)
×
1163
end
1164

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

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

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

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

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

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

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

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

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

1259
    nothing
×
1260
end
1261

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

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

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

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

1333
        # Also try looking into the env path if the user wants to complete the first argument
1334
        use_envpath = length(args.args) < 2
×
1335

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

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

1360
    function escape(p)
×
1361
        shell_escape && (p = do_shell_escape(p))
×
1362
        string_escape && (p = do_string_escape(p))
×
1363
        cmd_escape && (p = do_cmd_escape(p))
×
1364
        p
×
1365
    end
1366

1367
    paths, dir, success = complete_path(path; dirsep, kws...)
×
1368

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

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

1388
    map!(paths) do c::PathCompletion
×
1389
        p = joinpath_withsep(dir, c.path; dirsep)
×
1390
        PathCompletion(escape(p))
×
1391
    end
1392
    return sort!(paths, by=p->p.path), success
×
1393
end
1394

1395
function __init__()
2✔
1396
    COMPLETION_WORLD[] = Base.get_world_counter()
2✔
1397
    return nothing
2✔
1398
end
1399

1400
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