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

JuliaLang / julia / #38049

23 Apr 2025 06:42AM UTC coverage: 25.767% (+2.5%) from 23.227%
#38049

push

local

web-flow
🤖 [master] Bump the LinearAlgebra stdlib from 1ce8426 to 07725da (#58180)

Stdlib: LinearAlgebra
URL: https://github.com/JuliaLang/LinearAlgebra.jl.git
Stdlib branch: master
Julia branch: master
Old commit: 1ce8426
New commit: 07725da
Julia version: 1.13.0-DEV
LinearAlgebra version: 1.12.0(Does not match)
Bump invoked by: @jishnub
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
https://github.com/JuliaLang/LinearAlgebra.jl/compare/1ce842652...07725da80

```
$ git log --oneline 1ce8426..07725da
07725da Branch on `Bool` `alpha` in scaling `mul!` (#1286)
61e444d Fix exponentiation with immutable matrix (#1289)
77475c1 Fix `lmul!`/`rmul!` for 0-sized matrices (#1290)
c3d35c0 Test for `versioninfo` with `ENV` variable (#1279)
830ea2f Fit broken matmul test (#1288)
222f7f2 Propagate inbounds in diagzero (#1285)
e65e75c Clean up `herk_wrapper!` and add 5-arg tests (#1254)
ece1962 `versioninfo`: simplify, improve type stability (#1274)
3e525a8 Speicalize `copy!` for triangular, and use `copy!` in `ldiv` (#1263)
763f19f Forward scaling `lmul!`/`rmul!` for `Tridiagonal` to bands (#1284)
2d27d1c Explicit loop in converting `Bidiagonal`/`Tridiagonal` to `Matrix` (#1283)
0671a7b Simplify small Bidiagonal-AbstractVecOrMat multiplication (#1278)
a8fd121 Test for empty `Symmetric` and `BlasFlag.NONE` (#1280)
6e2de14 Remove bounds-checking in `Bidiagonal` `rdiv!` (#1281)
4a8fc62 Test for 5-arg `mul!`
c437beb Test for empty `Symmetric` and `BlasFlag.NONE`
7a891e9 Test for `versioninfo` with `ENV` variable
84fd21b Test for `versioninfo` (#1276)
243efdb `AbstractQ` bugfix: define `Base.IteratorSize` (#1277)
6073f28 Ease inference in `Bidiagonal` divmul tests (#1273)
30cb5d6 Avoid copy in empty bidiag `ldiv!` (#1275)
5d3ef46 Skip symmetry check in converting Symmetric to SymTridiagonal (#1269)
ae5385b Specialize `isreal` for banded matrices (#1271)
0a253be Explicit loop in `_isz... (continued)

12876 of 49971 relevant lines covered (25.77%)

1074244.94 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
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false)
×
723
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
×
724
    out = Completion[]
×
725
    kwargs_flag == 2 && return out # one of the kwargs is invalid
×
726
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
×
727
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
×
728
    return out
×
729
end
730

731
MAX_ANY_METHOD_COMPLETIONS::Int = 10
732
function recursive_explore_names!(seen::IdSet, callee_module::Module, initial_module::Module, exploredmodules::IdSet{Module}=IdSet{Module}())
×
733
    push!(exploredmodules, callee_module)
×
734
    for name in names(callee_module; all=true, imported=true)
×
735
        if !Base.isdeprecated(callee_module, name) && !startswith(string(name), '#') && isdefined(initial_module, name)
×
736
            func = getfield(callee_module, name)
×
737
            if !isa(func, Module)
×
738
                funct = Core.Typeof(func)
×
739
                push!(seen, funct)
×
740
            elseif isa(func, Module) && func ∉ exploredmodules
×
741
                recursive_explore_names!(seen, func, initial_module, exploredmodules)
×
742
            end
743
        end
744
    end
×
745
end
746
function recursive_explore_names(callee_module::Module, initial_module::Module)
×
747
    seen = IdSet{Any}()
×
748
    recursive_explore_names!(seen, callee_module, initial_module)
×
749
    seen
×
750
end
751

752
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
×
753
    out = Completion[]
×
754
    args_ex, kwargs_ex, kwargs_flag = try
×
755
        # this may throw, since we set default_any to false
756
        complete_methods_args(ex_org, context_module, false, false)
×
757
    catch ex
758
        ex isa ArgumentError || rethrow()
×
759
        return out
×
760
    end
761
    kwargs_flag == 2 && return out # one of the kwargs is invalid
×
762

763
    # moreargs determines whether to accept more args, independently of the presence of a
764
    # semicolon for the ".?(" syntax
765
    moreargs && push!(args_ex, Vararg{Any})
×
766

767
    for seen_name in recursive_explore_names(callee_module, callee_module)
×
768
        complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
×
769
    end
×
770

771
    if !shift
×
772
        # Filter out methods where all arguments are `Any`
773
        filter!(out) do c
×
774
            isa(c, TextCompletion) && return false
×
775
            isa(c, MethodCompletion) || return true
×
776
            sig = Base.unwrap_unionall(c.method.sig)::DataType
×
777
            return !all(@nospecialize(T) -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
×
778
        end
779
    end
780

781
    return out
×
782
end
783

784
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
×
785
    n = isexpr(x, :kw) ? x.args[1] : x
×
786
    if n isa Symbol
×
787
        push!(kwargs_ex, n)
×
788
        return kwargs_flag
×
789
    end
790
    possible_splat && isexpr(x, :...) && return kwargs_flag
×
791
    return 2 # The kwarg is invalid
×
792
end
793

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

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

834
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
×
835
    if allow_broadcasting && is_broadcasting_expr(ex)
×
836
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
×
837
    end
838
    return detect_args_kwargs(ex.args, context_module, default_any, false)
×
839
end
840

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

857
include("latex_symbols.jl")
858
include("emoji_symbols.jl")
859

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

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

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

902
# This needs to be a separate non-inlined function, see #19441
903
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
×
904
    matches = String[]
×
905
    for key in keys(identifier)
×
906
        rkey = repr(key)
×
907
        startswith(rkey,partial_key) && push!(matches,rkey)
×
908
    end
×
909
    return matches
×
910
end
911

912
# Provide completion for keyword arguments in function calls
913
function complete_keyword_argument!(suggestions::Vector{Completion},
×
914
                                    ex::Expr, last_word::String,
915
                                    context_module::Module; shift::Bool=false)
916
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex, context_module, true)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
×
917
    kwargs_flag == 2 && false # one of the previous kwargs is invalid
×
918

919
    methods = Completion[]
×
920
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, shift ? -1 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
×
921
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
922
    # method calls compatible with the current arguments.
923

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

947
    for kwarg in kwargs
×
948
        push!(suggestions, KeywordArgumentCompletion(kwarg))
×
949
    end
×
950
    return kwargs_flag != 0
×
951
end
952

953
function get_loading_candidates(pkgstarts::String, project_file::String)
×
954
    loading_candidates = String[]
×
955
    d = Base.parsed_toml(project_file)
×
956
    pkg = get(d, "name", nothing)::Union{String, Nothing}
×
957
    if pkg !== nothing && startswith(pkg, pkgstarts)
×
958
        push!(loading_candidates, pkg)
×
959
    end
960
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
×
961
    if deps !== nothing
×
962
        for (pkg, _) in deps
×
963
            startswith(pkg, pkgstarts) && push!(loading_candidates, pkg)
×
964
        end
×
965
    end
966
    return loading_candidates
×
967
end
968

969
function complete_loading_candidates!(suggestions::Vector{Completion}, s::String)
×
970
    for name in ("Core", "Base")
×
971
        startswith(name, s) && push!(suggestions, PackageCompletion(name))
×
972
    end
×
973

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

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

1015
    # Back up before whitespace to get a more useful AST node.
1016
    pos_not_ws = findprev(!isspace, string, pos)
×
1017
    cur_not_ws = something(seek_pos(node, pos_not_ws), node)
×
1018

1019
    suggestions = Completion[]
×
1020
    sort_suggestions() = sort!(unique!(named_completion, suggestions), by=named_completion_completion)
×
1021

1022
    # Search for methods (requires tab press):
1023
    #   ?(x, y)TAB           lists methods you can call with these objects
1024
    #   ?(x, y TAB           lists methods that take these objects as the first two arguments
1025
    #   MyModule.?(x, y)TAB  restricts the search to names in MyModule
1026
    if !hint
×
1027
        cs = method_search(view(string, 1:pos), context_module, shift)
×
1028
        cs !== nothing && return cs
×
1029
    end
1030

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

1051
    # Complete Cmd strings:
1052
    #   `fil TAB                 => `file
1053
    #   `file ~/exa TAB          => `file ~/example.txt
1054
    #   `file ~/example.txt TAB  => `file /home/user/example.txt
1055
    if (n = find_parent(cur, K"CmdString")) !== nothing
×
1056
        off = n.position - 1
×
1057
        ret, r, success = shell_completions(string[char_range(n)], pos - off, hint, cmd_escape=true)
×
1058
        success && return ret, r .+ off, success
×
1059
    end
1060

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

1075
    # Backlash symbols:
1076
    #   \pi => π
1077
    # Comes after string completion so backslash escapes are not misinterpreted.
1078
    ok, ret = bslash_completions(string, pos)
×
1079
    ok && return ret
×
1080

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

1086
    if (n = find_prefix_call(cur_not_ws)) !== nothing
×
1087
        func = first(children_nt(n))
×
1088
        e = Expr(n)
×
1089
        # Remove arguments past the first parse error (allows unclosed parens)
1090
        if is_broadcasting_expr(e)
×
1091
            i = findfirst(x -> x isa Expr && x.head == :error, e.args[2].args)
×
1092
            i !== nothing && deleteat!(e.args[2].args, i:lastindex(e.args[2].args))
×
1093
        else
1094
            i = findfirst(x -> x isa Expr && x.head == :error, e.args)
×
1095
            i !== nothing && deleteat!(e.args, i:lastindex(e.args))
×
1096
        end
1097

1098
        # Method completion:
1099
        #   foo( TAB     => list of method signatures for foo
1100
        #   foo(x, TAB   => list of methods signatures for foo with x as first argument
1101
        if kind(cur_not_ws) in KSet"( , ;"
×
1102
            # Don't provide method completions unless the cursor is after: '(' ',' ';'
1103
            return complete_methods(e, context_module, shift), char_range(func), false
×
1104

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

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

1135
    complete_modules_only = false
×
1136
    prefix = node_prefix(cur, context_module)
×
1137
    comp_keywords = prefix === nothing
×
1138

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

1151
        # Allow completion for `import Mod.name` (where `name` is not a module)
1152
        complete_modules_only = prefix == nothing || kind(n.parent) == K"using"
×
1153
        comp_keywords = false
×
1154
    end
1155

1156
    if comp_keywords
×
1157
        complete_keyword!(suggestions, s)
×
1158
        complete_keyval!(suggestions, s)
×
1159
    end
1160

1161
    complete_symbol!(suggestions, prefix, s, context_module; complete_modules_only, shift)
×
1162
    return sort_suggestions(), r, true
×
1163
end
1164

1165
function close_path_completion(path)
×
1166
    path = expanduser(path)
×
1167
    path = do_string_unescape(path)
×
1168
    !Base.isaccessibledir(path)
×
1169
end
1170

1171
# Lowering can misbehave with nested error expressions.
1172
function expr_has_error(@nospecialize(e))
×
1173
    e isa Expr || return false
×
1174
    e.head === :error &&  return true
×
1175
    any(expr_has_error, e.args)
×
1176
end
1177

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

1190
# If the cursor is in a literal string, return the contents and char range
1191
# inside the quotes.  Ignores triple strings.
1192
function find_str(cur::CursorNode)
×
1193
    n = find_parent(cur, K"string")
×
1194
    n !== nothing || return nothing, nothing
×
1195
    find_delim(n, K"\"", K"\"")
×
1196
end
1197

1198
# Is the cursor directly inside of the arguments of a prefix call (no nested
1199
# expressions)?
1200
function find_prefix_call(cur::CursorNode)
×
1201
    n = cur.parent
×
1202
    n !== nothing || return nothing
×
1203
    is_call(n) = kind(n) in KSet"call dotcall" && is_prefix_call(n)
×
1204
    if kind(n) == K"parameters"
×
1205
        is_call(n.parent) || return nothing
×
1206
        n.parent
×
1207
    else
1208
        # Check that we are beyond the function name.
1209
        is_call(n) && cur.index > children_nt(n)[1].index || return nothing
×
1210
        n
×
1211
    end
1212
end
1213

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

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

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

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

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

1263
    nothing
×
1264
end
1265

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

1275
function method_search(partial::AbstractString, context_module::Module, shift::Bool)
×
1276
    rexm = match(r"(\w+\.|)\?\((.*)$", partial)
×
1277
    if rexm !== nothing
×
1278
        # Get the module scope
1279
        if isempty(rexm.captures[1])
×
1280
            callee_module = context_module
×
1281
        else
1282
            modname = Symbol(rexm.captures[1][1:end-1])
×
1283
            if isdefined(context_module, modname)
×
1284
                callee_module = getfield(context_module, modname)
×
1285
                if !isa(callee_module, Module)
×
1286
                    callee_module = context_module
×
1287
                end
1288
            else
1289
                callee_module = context_module
×
1290
            end
1291
        end
1292
        moreargs = !endswith(rexm.captures[2], ')')
×
1293
        callstr = "_(" * rexm.captures[2]
×
1294
        if moreargs
×
1295
            callstr *= ')'
×
1296
        end
1297
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
×
1298
        if isa(ex_org, Expr)
×
1299
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false
×
1300
        end
1301
    end
1302
end
1303

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

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

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

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

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

1363
    function escape(p)
×
1364
        shell_escape && (p = do_shell_escape(p))
×
1365
        string_escape && (p = do_string_escape(p))
×
1366
        cmd_escape && (p = do_cmd_escape(p))
×
1367
        p
×
1368
    end
1369

1370
    paths, dir, success = complete_path(path; dirsep, kws...)
×
1371

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

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

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

1398
function __init__()
2✔
1399
    COMPLETION_WORLD[] = Base.get_world_counter()
2✔
1400
    return nothing
2✔
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