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

JuliaLang / julia / 1652

20 Apr 2026 08:38PM UTC coverage: 77.962% (+0.3%) from 77.623%
1652

push

buildkite

web-flow
codegen: Propagate `ipo_purity_bits` to LLVM function attributes (#61394)

Translate Julia's inferred effects (consistent, effect_free, nothrow,
terminates, notaskstate) into LLVM function attributes so that
middle-end passes like GVN, LICM, and DSE can exploit them.

The key design insight is that GC interactions don't need to be visible
before GC lowering. Call-site declarations get optimistic memory
attributes (e.g. memory(argmem: read)) that enable pre-GC optimizations,
then LateLowerGCFrame widens them to memory(readwrite) before safepoint
analysis so post-GC passes see correct semantics.

Attributes added:
- nounwind: for nothrow functions (with uwtable(async) on definitions
  to preserve .eh_frame for stack scanning)
- mustprogress: for terminating functions
- willreturn: for nothrow+terminating functions
- memory(argmem: read): for consistent+effect_free functions with no
  user-facing pointer arguments (call-site declarations only)
- readnone on gcstack param: for notaskstate functions, so LICM can
  hoist pure calls past heap stores
- "julia.safepoint" marker: on all call-site declarations, used by
  LateLowerGCFrame to identify and widen optimistic attrs

LateLowerGCFrame strips all optimistic attributes (memory effects,
readnone on gcstack) from both call instructions and function
declarations before safepoint analysis runs.

Previously explored in #47844

Developed with Claude

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Gabriel Baraldi <28694980+gbaraldi@users.noreply.github.com>

65490 of 84002 relevant lines covered (77.96%)

23535049.1 hits per line

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

95.14
/stdlib/REPL/src/REPLCompletions.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
module REPLCompletions
4

5
export completions, shell_completions, bslash_completions, completion_text, named_completion
6

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

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

20
abstract type Completion end
21

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

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

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

34
struct PathCompletion <: Completion
35
    path::String
3,800✔
36
end
37

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

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

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

52
struct FieldCompletion <: Completion
53
    typ::DataType
54✔
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)
211,836✔
61
end
62

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

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

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

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

82
# interface definition
83
function Base.getproperty(c::Completion, name::Symbol)
84
    if name === :text
547,351✔
85
        return getfield(c, :text)::String
576✔
86
    elseif name === :keyword
546,775✔
87
        return getfield(c, :keyword)::String
522✔
88
    elseif name === :path
546,253✔
89
        return getfield(c, :path)::String
23,526✔
90
    elseif name === :parent
522,727✔
91
        return getfield(c, :parent)::Module
×
92
    elseif name === :mod
522,727✔
93
        return getfield(c, :mod)::String
299,056✔
94
    elseif name === :package
223,671✔
95
        return getfield(c, :package)::String
4,788✔
96
    elseif name === :property
218,883✔
97
        return getfield(c, :property)::Symbol
621✔
98
    elseif name === :field
218,262✔
99
        return getfield(c, :field)::Symbol
105✔
100
    elseif name === :method
218,157✔
101
        return getfield(c, :method)::Method
211,842✔
102
    elseif name === :bslash
6,315✔
103
        return getfield(c, :bslash)::String
×
104
    elseif name === :text
6,315✔
105
        return getfield(c, :text)::String
×
106
    elseif name === :key
6,315✔
107
        return getfield(c, :key)::String
363✔
108
    elseif name === :kwarg
5,952✔
109
        return getfield(c, :kwarg)::String
369✔
110
    end
111
    return getfield(c, name)
5,583✔
112
end
113

114
_completion_text(c::TextCompletion) = c.text
576✔
115
_completion_text(c::KeywordCompletion) = c.keyword
522✔
116
_completion_text(c::KeyvalCompletion) = c.keyval
57✔
117
_completion_text(c::PathCompletion) = c.path
2,649✔
118
_completion_text(c::ModuleCompletion) = c.mod
299,056✔
119
_completion_text(c::PackageCompletion) = c.package
4,788✔
120
_completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property)
621✔
121
_completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field)
105✔
122
_completion_text(c::MethodCompletion) = repr(c.method)
9,129✔
123
_completion_text(c::ShellCompletion) = c.text
×
124
_completion_text(c::DictCompletion) = c.key
363✔
125
_completion_text(c::KeywordArgumentCompletion) = c.kwarg*'='
369✔
126

127
completion_text(c) = _completion_text(c)::String
318,235✔
128

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

131
function named_completion(c)
16,377✔
132
    text = completion_text(c)::String
318,233✔
133
    return NamedCompletion(text, text)
318,233✔
134
end
135

136
named_completion_completion(c) = named_completion(c).completion::String
274,560✔
137

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

312
function do_cmd_escape(s; escape_backticks::Bool=false)
4,484✔
313
    s = Base.shell_escape_posixly(s)
2,242✔
314
    escape_backticks && (s = Base.escape_raw_string(s, '`'))
2,242✔
315
    return s
2,242✔
316
end
317

318
# Cache the set of valid login shells from /etc/shells. Real users have their
319
# shell listed here; service/daemon accounts use /bin/false etc. which are not.
320
const _login_shells = Base.OncePerProcess{Vector{String}}() do
321
    @static if Sys.iswindows()
2✔
322
        String[]
323
    else
324
        try
2✔
325
            filter!(l -> !isempty(l) && !startswith(l, '#'), readlines("/etc/shells"))
39✔
326
        catch
327
            String[]  # no /etc/shells; skip shell filtering
×
328
        end
329
    end
330
end
331

332
# pw_shell byte offset in struct passwd — everything before the pw_shell field:
333
# Linux:     pw_name, pw_passwd (2 Ptrs), pw_uid, pw_gid (2 Cuints),
334
#            pw_gecos, pw_dir (2 Ptrs)
335
# macOS/BSD: same as Linux plus pw_change (Clong) and pw_class (Ptr)
336
#            between pw_gid and pw_gecos
337
const _pw_shell_offset = @static if Sys.islinux()
338
    4 * sizeof(Ptr{Cvoid}) + 2 * sizeof(Cuint)
339
else
340
    5 * sizeof(Ptr{Cvoid}) + 2 * sizeof(Cuint) + sizeof(Clong)
341
end
342

343
# Return a sorted, deduplicated list of real local usernames. When all=false
344
# (the default), service accounts are excluded by requiring their shell to be
345
# listed in /etc/shells, and on macOS names starting with '_' are also excluded.
346
function list_users(; all::Bool=false)
8✔
347
    seen = Set{String}()
4✔
348
    @static if !Sys.iswindows()
349
        valid_shells = all ? String[] : _login_shells()
8✔
350
        ccall(:setpwent, Cvoid, ())
4✔
351
        try
4✔
352
            while true
556✔
353
                ptr = ccall(:getpwent, Ptr{Cvoid}, ())
556✔
354
                ptr == C_NULL && break
556✔
355
                name_ptr = unsafe_load(Ptr{Ptr{UInt8}}(ptr))
552✔
356
                name_ptr == C_NULL && continue
552✔
357
                name = unsafe_string(name_ptr)
552✔
358
                if !all
552✔
359
                    @static Sys.isapple() && startswith(name, '_') && continue
1,028✔
360
                    if !isempty(valid_shells)
52✔
361
                        shell_ptr = unsafe_load(Ptr{Ptr{UInt8}}(ptr + _pw_shell_offset))
52✔
362
                        shell_ptr == C_NULL && continue
52✔
363
                        unsafe_string(shell_ptr) in valid_shells || continue
94✔
364
                    end
365
                end
366
                push!(seen, name)
10✔
367
            end
556✔
368
        finally
369
            ccall(:endpwent, Cvoid, ())
4✔
370
        end
371
    end
372
    return sort!(collect(seen))
4✔
373
end
374

375
# Return tilde-completion strings for usernames matching `prefix`.
376
# With an empty prefix, the current user is represented as "~/" (not "~name/")
377
# since that shorthand is always available. With a non-empty prefix the current
378
# user's actual name is included when it matches, and "~/" is not offered.
379
function _complete_tilde_usernames(prefix::AbstractString)::Vector{String}
4✔
380
    me = try Sys.username() catch; "" end
4✔
381
    users = list_users()
4✔
382
    completions = String[]
4✔
383
    if isempty(prefix)
4✔
384
        push!(completions, "~/")            # always offer ~/ for current user
2✔
385
        for name in users
2✔
386
            name == me && continue          # ~/  already covers current user
4✔
387
            push!(completions, "~$name/")
2✔
388
        end
4✔
389
    else
390
        for name in users
2✔
391
            startswith(name, prefix) && push!(completions, "~$name/")
4✔
392
        end
4✔
393
    end
394
    return completions
4✔
395
end
396

397
function do_cmd_escape_tilde(s; escape_backticks::Bool=false)
28✔
398
    # Tilde paths: ~[username] is handled by shell_parse and must not be
399
    # quoted.  Escape only the path component after the first slash.
400
    i = findfirst(isequal('/'), s)
14✔
401
    i === nothing && return s  # bare "~username" — no special chars possible
14✔
402
    tilde_prefix = SubString(s, 1, i - 1)  # "~" or "~alice"
14✔
403
    rest = SubString(s, i)                  # "/rest/of/path"
28✔
404
    return string(tilde_prefix, do_cmd_escape(rest; escape_backticks))
14✔
405
end
406
function do_string_escape(s)
407
    return escape_string(s, ('\"','$'))
465✔
408
end
409
function do_string_unescape(s)
183✔
410
    s = replace(s, "\\\$"=>"\$")
183✔
411
    try
183✔
412
        unescape_string(s)
183✔
413
    catch e
414
        e isa ArgumentError || rethrow()
8✔
415
        s # it is unlikely, but if it isn't a valid string, maybe it was a valid path, and just needs escape_string called?
8✔
416
    end
417
end
418

419
function joinpath_withsep(dir, path; dirsep)
3,836✔
420
    dir == "" && return path
1,918✔
421
    dir[end] == dirsep ? dir * path : dir * dirsep * path
1,255✔
422
end
423

424
const PATH_cache_lock = Base.ReentrantLock()
425
const PATH_cache = Set{String}()
426
PATH_cache_task::Union{Task,Nothing} = nothing
427
PATH_cache_condition::Union{Threads.Condition, Nothing} = nothing # used for sync in tests
428
next_cache_update::Float64 = 0.0
429
function maybe_spawn_cache_PATH()
21✔
430
    global PATH_cache_task, PATH_cache_condition, next_cache_update
21✔
431
    @lock PATH_cache_lock begin
21✔
432
        # Extract to local variables to enable flow-sensitive type inference for these global variables
433
        PATH_cache_task_local = PATH_cache_task
21✔
434
        PATH_cache_task_local isa Task && !istaskdone(PATH_cache_task_local) && return
21✔
435
        time() < next_cache_update && return
21✔
436
        PATH_cache_task = PATH_cache_task_local = Threads.@spawn begin
14✔
437
            try
7✔
438
                REPLCompletions.cache_PATH()
7✔
439
            finally
440
                @lock PATH_cache_lock begin
7✔
441
                    next_cache_update = time() + 10 # earliest next update can run is 10s after
7✔
442
                    PATH_cache_task = nothing # release memory when done
7✔
443
                    PATH_cache_condition_local = PATH_cache_condition
7✔
444
                    PATH_cache_condition_local !== nothing && notify(PATH_cache_condition_local)
7✔
445
                end
446
            end
447
        end
448
        Base.errormonitor(PATH_cache_task_local)
7✔
449
    end
450
end
451

452
# caches all reachable files in PATH dirs
453
function cache_PATH()
7✔
454
    path = get(ENV, "PATH", nothing)
7✔
455
    path isa String || return
7✔
456

457
    # Calling empty! on PATH_cache would be annoying for async typing hints as completions would temporarily disappear.
458
    # 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.
459
    this_PATH_cache = Set{String}()
7✔
460

461
    @debug "caching PATH files" PATH=path
7✔
462
    pathdirs = split(path, @static Sys.iswindows() ? ";" : ":")
7✔
463

464
    next_yield_time = time() + 0.01
7✔
465

466
    t = @elapsed for pathdir in pathdirs
7✔
467
        actualpath = try
44✔
468
            realpath(pathdir)
48✔
469
        catch ex
470
            ex isa Base.IOError || rethrow()
4✔
471
            # Bash doesn't expect every folder in PATH to exist, so neither shall we
472
            continue
4✔
473
        end
474

475
        if actualpath != pathdir && in(actualpath, pathdirs)
75✔
476
            # Remove paths which (after resolving links) are in the env path twice.
477
            # Many distros eg. point /bin to /usr/bin but have both in the env path.
478
            continue
3✔
479
        end
480

481
        path_entries = try
37✔
482
            _readdirx(pathdir)
39✔
483
        catch e
484
            # Bash allows dirs in PATH that can't be read, so we should as well.
485
            if isa(e, Base.IOError) || isa(e, Base.ArgumentError)
2✔
486
                continue
2✔
487
            else
488
                # We only handle IOError and ArgumentError here
489
                rethrow()
×
490
            end
491
        end
492
        for entry in path_entries
35✔
493
            # In a perfect world, we would filter on whether the file is executable
494
            # here, or even on whether the current user can execute the file in question.
495
            try
7,116✔
496
                if isfile(entry)
14,232✔
497
                    @lock PATH_cache_lock push!(PATH_cache, entry.name)
6,603✔
498
                    push!(this_PATH_cache, entry.name)
6,606✔
499
                end
500
            catch e
501
                # `isfile()` can throw in rare cases such as when probing a
502
                # symlink that points to a file within a directory we do not
503
                # have read access to.
504
                if isa(e, Base.IOError)
3✔
505
                    continue
3✔
506
                else
507
                    rethrow()
×
508
                end
509
            end
510
            if time() >= next_yield_time
7,113✔
511
                yield() # to avoid blocking typing when -t1
4✔
512
                next_yield_time = time() + 0.01
4✔
513
            end
514
        end
7,116✔
515
    end
516

517
    @lock PATH_cache_lock begin
7✔
518
        intersect!(PATH_cache, this_PATH_cache) # remove entries from PATH_cache that weren't found this time
7✔
519
    end
520

521
    @debug "caching PATH files took $t seconds" length(pathdirs) length(PATH_cache)
7✔
522
    return PATH_cache
7✔
523
end
524

525
function complete_path(path::AbstractString;
370✔
526
                       use_envpath=false,
527
                       cmd_escape=false,
528
                       escape_backticks=false,
529
                       string_escape=false,
530
                       contract_user=false,
531
                       dirsep=Sys.iswindows() ? '\\' : '/')
532
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
185✔
533
        # if the path is just "~", don't consider the expanded username as a prefix
534
        if path == "~"
×
535
            dir, prefix = homedir(), ""
×
536
        else
537
            dir, prefix = splitdir(homedir() * path[2:end])
×
538
        end
539
    else
540
        dir, prefix = splitdir(path)
185✔
541
    end
542
    entries = try
185✔
543
        if isempty(dir)
185✔
544
            _readdirx()
101✔
545
        elseif isdir(dir)
84✔
546
            _readdirx(dir)
80✔
547
        else
548
            return Completion[], dir, false
185✔
549
        end
550
    catch ex
551
        ex isa Base.IOError || rethrow()
×
552
        return Completion[], dir, false
×
553
    end
554

555
    matches = Set{String}()
181✔
556
    for entry in entries
181✔
557
        if startswith(entry.name, prefix)
29,653✔
558
            is_dir = try isdir(entry) catch ex; ex isa Base.IOError ? false : rethrow() end
5,272✔
559
            push!(matches, is_dir ? joinpath_withsep(entry.name, ""; dirsep) : entry.name)
4,477✔
560
        end
561
    end
29,653✔
562

563
    if use_envpath && isempty(dir)
181✔
564
        # Look for files in PATH as well. These are cached in `cache_PATH` in an async task to not block typing.
565
        # If we cannot get lock because its still caching just pass over this so that typing isn't laggy.
566
        maybe_spawn_cache_PATH() # only spawns if enough time has passed and the previous caching task has completed
21✔
567
        @lock PATH_cache_lock begin
21✔
568
            for file in PATH_cache
42✔
569
                startswith(file, prefix) && push!(matches, file)
2,422✔
570
            end
4,844✔
571
        end
572
    end
573

574
    matches = ((string_escape ? do_string_escape(s) : s) for s in matches)
181✔
575
    matches = ((cmd_escape ? do_cmd_escape(s; escape_backticks) : s) for s in matches)
181✔
576
    matches = Completion[PathCompletion(contract_user ? contractuser(s) : s) for s in matches]
2,706✔
577
    return matches, dir, !isempty(matches)
181✔
578
end
579

580
function complete_path(path::AbstractString,
×
581
                       pos::Int;
582
                       use_envpath=false,
583
                       string_escape=false,
584
                       contract_user=false)
585
    ## TODO: enable this depwarn once Pkg is fixed
586
    #Base.depwarn("complete_path with pos argument is deprecated because the return value [2] is incorrect to use", :complete_path)
587
    paths, dir, success = complete_path(path; use_envpath, string_escape, dirsep='/')
×
588

589
    if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path)
×
590
        # if the path is just "~", don't consider the expanded username as a prefix
591
        if path == "~"
×
592
            dir, prefix = homedir(), ""
×
593
        else
594
            dir, prefix = splitdir(homedir() * path[2:end])
×
595
        end
596
    else
597
        dir, prefix = splitdir(path)
×
598
    end
599
    startpos = pos - lastindex(prefix) + 1
×
600
    Sys.iswindows() && map!(paths, paths) do c::PathCompletion
×
601
        # emulation for unnecessarily complicated return value, since / is a
602
        # perfectly acceptable path character which does not require quoting
603
        # but is required by Pkg's awkward parser handling
604
        return endswith(c.path, "/") ? PathCompletion(chop(c.path) * "\\\\") : c
×
605
    end
606
    return paths, startpos:pos, success
×
607
end
608

609
struct REPLCacheToken end
174,890✔
610

611
struct REPLInterpreter <: CC.AbstractInterpreter
612
    limit_aggressive_inference::Bool
613
    world::UInt
614
    inf_params::CC.InferenceParams
615
    opt_params::CC.OptimizationParams
616
    inf_cache::CC.InferenceCache
617
    function REPLInterpreter(limit_aggressive_inference::Bool=false;
752✔
618
                             world::UInt = Base.get_world_counter(),
619
                             inf_params::CC.InferenceParams = CC.InferenceParams(;
620
                                 aggressive_constant_propagation=true),
621
                             opt_params::CC.OptimizationParams = CC.OptimizationParams(),
622
                             inf_cache::CC.InferenceCache = CC.InferenceCache())
623
        return new(limit_aggressive_inference, world, inf_params, opt_params, inf_cache)
734✔
624
    end
625
end
626
CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params
535,372✔
627
CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params
273✔
628
CC.get_inference_world(interp::REPLInterpreter) = interp.world
851,566✔
629
CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache
291,494✔
630
CC.cache_owner(::REPLInterpreter) = REPLCacheToken()
174,890✔
631

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

635
# REPLInterpreter doesn't need any sources to be cached, so discard them aggressively
636
CC.transform_result_for_cache(::REPLInterpreter, ::CC.InferenceResult, edges::Core.SimpleVector) = nothing
10,958✔
637

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

641
# `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions
642
# for lines like `Mod.a.|` (where `|` is the cursor position).
643
# Aggressive binding resolution poses challenges for the inference cache validation
644
# (until https://github.com/JuliaLang/julia/issues/40399 is implemented).
645
# To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding
646
# resolution for top-level frame representing REPL input code and for child uncached frames
647
# that are constant propagated from the top-level frame ("repl-frame"s). This works, even if
648
# those global bindings are not constant and may be mutated in the future, since:
649
# a.) "repl-frame"s are never cached, and
650
# b.) mutable values are never observed by any cached frames.
651
#
652
# `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within
653
# "repl-frame" to provide reasonable completions for lines like `Ref(Some(42))[].|`.
654
# Aggressive concrete evaluation allows us to get accurate type information about complex
655
# expressions that otherwise can not be constant folded, in a safe way, i.e. it still
656
# doesn't evaluate effectful expressions like `pop!(xs)`.
657
# Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't
658
# present any cache validation issues because "repl-frame" is never cached.
659

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

665
function is_call_stack_uncached(sv::CC.InferenceState)
683,289✔
666
    CC.is_cached(sv) && return false
989,463✔
667
    parent = CC.frame_parent(sv)
1,394,089✔
668
    parent === nothing && return true
710,800✔
669
    return is_call_stack_uncached(parent::CC.InferenceState)
683,289✔
670
end
671

672
# aggressive global binding resolution within `repl_frame`
673
function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, bailed::Bool,
196,027✔
674
                                    sv::CC.InferenceState)
675
    # Ignore saw_latestworld
676
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_stack_uncached(sv))
391,532✔
677
        partition = CC.abstract_eval_binding_partition!(interp, g, sv)
18,680✔
678
        if CC.is_defined_const_binding(CC.binding_kind(partition))
24,184✔
679
            return CC.RTEffects(Const(CC.partition_restriction(partition)), Union{}, CC.EFFECTS_TOTAL)
18,458✔
680
        else
681
            b = convert(Core.Binding, g)
222✔
682
            if CC.binding_kind(partition) == CC.PARTITION_KIND_GLOBAL && isdefined(b, :value)
222✔
683
                return CC.RTEffects(Const(b.value), Union{}, CC.EFFECTS_TOTAL)
201✔
684
            end
685
        end
686
        return CC.RTEffects(Union{}, UndefVarError, CC.EFFECTS_THROWS)
21✔
687
    end
688
    return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, bailed::Bool,
177,347✔
689
                                              sv::CC.InferenceState)
690
end
691

692
# aggressive concrete evaluation for `:inconsistent` frames within `repl_frame`
693
function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f),
694
                                   result::CC.MethodCallResult, arginfo::CC.ArgInfo,
695
                                   sv::CC.InferenceState)
696
    if (interp.limit_aggressive_inference ? is_repl_frame(sv) : is_call_stack_uncached(sv))
221,599✔
697
        neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE)
8,861✔
698
        result = CC.MethodCallResult(result.rt, result.exct, neweffects, result.edge,
17,722✔
699
                                     result.edgecycle, result.edgelimited, result.call_result)
700
    end
701
    ret = @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any,
221,860✔
702
                                            result::CC.MethodCallResult, arginfo::CC.ArgInfo,
703
                                            sv::CC.InferenceState)
704
    if ret === :semi_concrete_eval
110,930✔
705
        # while the base eligibility check probably won't permit semi-concrete evaluation
706
        # for `REPLInterpreter` (given it completely turns off optimization),
707
        # this ensures we don't inadvertently enter irinterp
708
        ret = :none
×
709
    end
710
    return ret
110,930✔
711
end
712

713
# allow constant propagation for mutable constants
714
function CC.const_prop_argument_heuristic(interp::REPLInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
715
    if !interp.limit_aggressive_inference
109,307✔
716
        any(@nospecialize(a)->isa(a, Const), arginfo.argtypes) && return true # even if mutable
218,320✔
717
    end
718
    return @invoke CC.const_prop_argument_heuristic(interp::CC.AbstractInterpreter, arginfo::CC.ArgInfo, sv::CC.InferenceState)
330✔
719
end
720

721
# Perform some post-hoc mutation on lowered code, as expected by some abstract interpretation
722
# routines, especially for `:foreigncall` and `:cglobal`.
723
function resolve_toplevel_symbols!(src::Core.CodeInfo, mod::Module)
724
    @ccall jl_resolve_definition_effects_in_ir(
716✔
725
        #=jl_array_t *stmts=# src.code::Any,
726
        #=jl_module_t *m=# mod::Any,
727
        #=jl_svec_t *sparam_vals=# Core.svec()::Any,
728
        #=jl_value_t *binding_edge=# C_NULL::Ptr{Cvoid},
729
        #=int binding_effects=# 0::Int)::Cvoid
730
    return src
716✔
731
end
732

733
function construct_toplevel_mi(src::Core.CodeInfo, context_module::Module)
734
    resolve_toplevel_symbols!(src, context_module)
716✔
735
    return @ccall jl_method_instance_for_thunk(src::Any, context_module::Any)::Ref{Core.MethodInstance}
716✔
736
end
737

738
# lower `ex` and run type inference on the resulting top-level expression
739
function repl_eval_ex(@nospecialize(ex), context_module::Module; limit_aggressive_inference::Bool=false)
3,022✔
740
    expr_has_error(ex) && return nothing
2,296✔
741
    if (isexpr(ex, :toplevel) || isexpr(ex, :tuple)) && !isempty(ex.args)
2,986✔
742
        # get the inference result for the last expression
743
        ex = ex.args[end]
3✔
744
    end
745
    lwr = try
1,493✔
746
        Meta.lower(context_module, ex)
1,493✔
747
    catch # macro expansion failed, etc.
748
        return nothing
15✔
749
    end
750
    if lwr isa Symbol
1,478✔
751
        return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing
459✔
752
    end
753
    lwr isa Expr || return Const(lwr) # `ex` is literal
1,298✔
754
    isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar
764✔
755
    src = lwr.args[1]::Core.CodeInfo
716✔
756

757
    mi = construct_toplevel_mi(src, context_module)
716✔
758
    interp = REPLInterpreter(limit_aggressive_inference)
716✔
759
    result = CC.InferenceResult(mi)
716✔
760
    frame = CC.InferenceState(result, src, #=cache=#:no, interp)
716✔
761

762
    # NOTE Use the fixed world here to make `REPLInterpreter` robust against
763
    #      potential invalidations of `Core.Compiler` methods.
764
    Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame)
716✔
765

766
    result = frame.result.result
716✔
767
    result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
716✔
768
    return result
692✔
769
end
770

771
# `COMPLETION_WORLD[]` will be initialized within `__init__`
772
# (to allow us to potentially remove REPL from the sysimage in the future).
773
# Note that inference from the `code_typed` call below will use the current world age
774
# rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age
775
# when the given world age is higher than the current one.
776
const COMPLETION_WORLD = Ref{UInt}(typemax(UInt))
777

778
# Generate code cache for `REPLInterpreter` now:
779
# This code cache will be available at the world of `COMPLETION_WORLD`,
780
# assuming no invalidation will happen before initializing REPL.
781
# Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations.
782
code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
783

784
# Method completion on function call expression that look like :(max(1))
785
MAX_METHOD_COMPLETIONS::Int = 40
786
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
401✔
787
    isempty(ex_org.args) && return 2, nothing, [], Set{Symbol}()
401✔
788
    # Desugar do block call into call with lambda
789
    if ex_org.head === :do && length(ex_org.args) >= 2
401✔
790
        ex_call = ex_org.args[1]
3✔
791
        ex_args = [x for x in ex_call.args if !(x isa Expr && x.head === :parameters)]
3✔
792
        ex_params = findfirst(x -> x isa Expr && x.head === :parameters, ex_call.args)
9✔
793
        new_args = [ex_args[1], ex_org.args[end], ex_args[2:end]...]
3✔
794
        ex_params !== nothing && push!(new_args, ex_call.args[ex_params])
3✔
795
        ex_org = Expr(:call, new_args...)
3✔
796
    end
797
    funct = repl_eval_ex(ex_org.args[1], context_module)
401✔
798
    funct === nothing && return 2, nothing, [], Set{Symbol}()
401✔
799
    funct = CC.widenconst(funct)
398✔
800
    args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true)
398✔
801
    return kwargs_flag, funct, args_ex, kwargs_ex
398✔
802
end
803

804
# cursor_pos: either :positional (complete either kwargs or positional) or :kwargs (beyond semicolon)
805
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false, cursor_pos::Symbol=:positional)
2✔
806
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
250✔
807
    out = Completion[]
248✔
808
    # Allow more arguments when cursor before semicolon, even if kwargs are present
809
    cursor_pos == :positional && kwargs_flag == 1 && (kwargs_flag = 0)
248✔
810
    kwargs_flag == 2 && return out # one of the kwargs is invalid
248✔
811
    kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
218✔
812
    complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
227✔
813
    return out
218✔
814
end
815

816
MAX_ANY_METHOD_COMPLETIONS::Int = 10
817

818
function accessible(mod::Module, private::Bool)
42✔
819
    bindings = IdSet{Any}(Core.Typeof(getglobal(mod, s)) for s in names(mod; all=private, imported=private, usings=private)
42✔
820
                   if !Base.isdeprecated(mod, s) && !startswith(string(s), '#') && !startswith(string(s), '@') && isdefined(mod, s))
821
    delete!(bindings, Module)
42✔
822
    return collect(bindings)
42✔
823
end
824

825
function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool)
42✔
826
    out = Completion[]
42✔
827
    args_ex, kwargs_ex, kwargs_flag = try
42✔
828
        # this may throw, since we set default_any to false
829
        complete_methods_args(ex_org, context_module, false, false)
42✔
830
    catch ex
831
        ex isa ArgumentError || rethrow()
×
832
        return out
42✔
833
    end
834
    kwargs_flag == 2 && return out # one of the kwargs is invalid
42✔
835

836
    # moreargs determines whether to accept more args, independently of the presence of a
837
    # semicolon for the ".?(" syntax
838
    moreargs && push!(args_ex, Vararg{Any})
42✔
839

840
    for seen_name in accessible(callee_module, callee_module === context_module)
42✔
841
        complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false)
4,590✔
842
    end
4,590✔
843

844
    if !shift
42✔
845
        # Filter out methods where all arguments are `Any`
846
        filter!(out) do c
6✔
847
            isa(c, TextCompletion) && return false
33✔
848
            isa(c, MethodCompletion) || return true
33✔
849
            sig = Base.unwrap_unionall(c.method.sig)::DataType
33✔
850
            return !all(@nospecialize(T) -> T === Any || T === Vararg{Any}, sig.parameters[2:end])
57✔
851
        end
852
    end
853

854
    return out
42✔
855
end
856

857
function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool)
858
    n = isexpr(x, :kw) ? x.args[1] : x
261✔
859
    if n isa Symbol
261✔
860
        push!(kwargs_ex, n)
228✔
861
        return kwargs_flag
228✔
862
    end
863
    possible_splat && isexpr(x, :...) && return kwargs_flag
33✔
864
    return 2 # The kwarg is invalid
21✔
865
end
866

867
function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, default_any::Bool, broadcasting::Bool)
440✔
868
    args_ex = Any[]
440✔
869
    kwargs_ex = Symbol[]
440✔
870
    kwargs_flag = 0
440✔
871
    # kwargs_flag is:
872
    # * 0 if there is no semicolon and no invalid kwarg
873
    # * 1 if there is a semicolon and no invalid kwarg
874
    # * 2 if there are two semicolons or more, or if some kwarg is invalid, which
875
    #        means that it is not of the form "bar=foo", "bar" or "bar..."
876
    for i in (1+!broadcasting):length(funargs)
490✔
877
        ex = funargs[i]
744✔
878
        if isexpr(ex, :parameters)
744✔
879
            kwargs_flag = ifelse(kwargs_flag == 0, 1, 2) # there should be at most one :parameters
192✔
880
            for x in ex.args
192✔
881
                kwargs_flag = detect_invalid_kwarg!(kwargs_ex, x, kwargs_flag, true)
225✔
882
            end
195✔
883
        elseif isexpr(ex, :kw)
552✔
884
            kwargs_flag = detect_invalid_kwarg!(kwargs_ex, ex, kwargs_flag, false)
66✔
885
        else
886
            if broadcasting
486✔
887
                # handle broadcasting, but only handle number of arguments instead of
888
                # argument types
889
                push!(args_ex, Any)
15✔
890
            else
891
                argt = repl_eval_ex(ex, context_module)
471✔
892
                if argt !== nothing
471✔
893
                    push!(args_ex, CC.widenconst(argt))
360✔
894
                elseif default_any
111✔
895
                    push!(args_ex, Any)
111✔
896
                else
897
                    throw(ArgumentError("argument not found"))
×
898
                end
899
            end
900
        end
901
    end
1,098✔
902
    return args_ex, Set{Symbol}(kwargs_ex), kwargs_flag
440✔
903
end
904

905
is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple)
797✔
906

907
function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool)
908
    if allow_broadcasting && is_broadcasting_expr(ex)
440✔
909
        return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true)
18✔
910
    end
911
    return detect_args_kwargs(ex.args, context_module, default_any, false)
422✔
912
end
913

914
function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool)
4,958✔
915
    # Input types and number of arguments
916
    t_in = Tuple{funct, args_ex...}
4,958✔
917
    m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(),
4,958✔
918
        #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL))
919
    if !isa(m, Vector)
4,958✔
920
        push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )"))
582✔
921
        return
582✔
922
    end
923
    for match in m
4,376✔
924
        # TODO: if kwargs_ex, filter out methods without kwargs?
925
        push!(out, MethodCompletion(match.spec_types, match.method))
211,836✔
926
    end
211,836✔
927
    # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set
928
end
929

930
include("latex_symbols.jl")
931
include("emoji_symbols.jl")
932

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

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

945
function bslash_completions(string::String, pos::Int, hint::Bool=false)
1,158✔
946
    slashpos = something(findprev(isequal('\\'), string, pos), 0)
2,446✔
947
    if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos &&
1,228✔
948
        !(1 < slashpos && (string[prevind(string, slashpos)]=='\\')))
949
        # latex / emoji symbol substitution
950
        s = string[slashpos:pos]
248✔
951
        latex = get(latex_symbols, s, "")
124✔
952
        if !isempty(latex) # complete an exact match
124✔
953
            return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true))
58✔
954
        elseif occursin(subscript_regex, s)
66✔
955
            sub = map(c -> subscripts[c], s[3:end])
66✔
956
            return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true))
9✔
957
        elseif occursin(superscript_regex, s)
57✔
958
            sup = map(c -> superscripts[c], s[3:end])
78✔
959
            return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true))
9✔
960
        end
961
        emoji = get(emoji_symbols, s, "")
48✔
962
        if !isempty(emoji)
48✔
963
            return (true, (Completion[BslashCompletion(emoji)], slashpos:pos, true))
6✔
964
        end
965
        # return possible matches; these cannot be mixed with regular
966
        # Julian completions as only latex / emoji symbols contain the leading \
967
        symbol_dict = startswith(s, "\\:") ? emoji_symbols : latex_symbols
42✔
968
        namelist = Iterators.filter(k -> startswith(k, s), keys(symbol_dict))
103,335✔
969
        completions = Completion[BslashCompletion(name, "$(symbol_dict[name]) $name") for name in sort!(collect(namelist))]
42✔
970
        return (true, (completions, slashpos:pos, true))
42✔
971
    end
972
    return (false, (Completion[], 1:0, false))
1,034✔
973
end
974

975
# This needs to be a separate non-inlined function, see #19441
976
@noinline function find_dict_matches(identifier::AbstractDict, partial_key)
213✔
977
    matches = String[]
213✔
978
    for key in keys(identifier)
324✔
979
        rkey = repr(key)
2,523✔
980
        startswith(rkey,partial_key) && push!(matches,rkey)
2,523✔
981
    end
3,822✔
982
    return matches
213✔
983
end
984

985
# Provide completion for keyword arguments in function calls
986
# Returns true if the current argument must be a keyword because the cursor is beyond the semicolon
987
function complete_keyword_argument!(suggestions::Vector{Completion},
306✔
988
                                    ex::Expr, last_word::String,
989
                                    context_module::Module,
990
                                    arg_pos::Symbol; shift::Bool=false)
991
    kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex, context_module, true)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
153✔
992
    kwargs_flag == 2 && return false # one of the previous kwargs is invalid
153✔
993

994
    methods = Completion[]
150✔
995
    complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, arg_pos == :kwargs)
150✔
996
    # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for
997
    # method calls compatible with the current arguments.
998

999
    # For each method corresponding to the function call, provide completion suggestions
1000
    # for each keyword that starts like the last word and that is not already used
1001
    # previously in the expression. The corresponding suggestion is "kwname=".
1002
    # If the keyword corresponds to an existing name, also include "kwname" as a suggestion
1003
    # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)".
1004
    kwargs = Set{String}()
150✔
1005
    for m in methods
150✔
1006
        # if MAX_METHOD_COMPLETIONS is hit a single TextCompletion is return by complete_methods! with an explanation
1007
        # which can be ignored here
1008
        m isa TextCompletion && continue
202,680✔
1009
        m::MethodCompletion
202,680✔
1010
        possible_kwargs = Base.kwarg_decl(m.method)
202,680✔
1011
        current_kwarg_candidates = String[]
202,680✔
1012
        for _kw in possible_kwargs
202,680✔
1013
            kw = String(_kw)
18,829✔
1014
            # HACK: Should consider removing current arg from AST.
1015
            if !endswith(kw, "...") && startswith(kw, last_word) && (_kw ∉ kwargs_ex || kw == last_word)
18,853✔
1016
                push!(current_kwarg_candidates, kw)
204✔
1017
            end
1018
        end
18,829✔
1019
        union!(kwargs, current_kwarg_candidates)
202,680✔
1020
    end
202,680✔
1021

1022
    for kwarg in kwargs
300✔
1023
        push!(suggestions, KeywordArgumentCompletion(kwarg))
165✔
1024
    end
330✔
1025
    return kwargs_flag != 0 && arg_pos == :kwargs
150✔
1026
end
1027

1028
function get_loading_candidates(pkgstarts::String, project_file::String)
3✔
1029
    loading_candidates = String[]
3✔
1030
    d = Base.parsed_toml(project_file)
3✔
1031
    pkg = get(d, "name", nothing)::Union{String, Nothing}
3✔
1032
    if pkg !== nothing && startswith(pkg, pkgstarts)
3✔
1033
        push!(loading_candidates, pkg)
3✔
1034
    end
1035
    deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing}
3✔
1036
    if deps !== nothing
3✔
1037
        for (pkg, _) in deps
6✔
1038
            startswith(pkg, pkgstarts) && push!(loading_candidates, pkg)
3✔
1039
        end
3✔
1040
    end
1041
    return loading_candidates
3✔
1042
end
1043

1044
function complete_loading_candidates!(suggestions::Vector{Completion}, s::String)
27✔
1045
    for name in ("Core", "Base")
27✔
1046
        startswith(name, s) && push!(suggestions, PackageCompletion(name))
54✔
1047
    end
81✔
1048

1049
    # If there's no dot, we're in toplevel, so we should
1050
    # also search for packages
1051
    for dir in Base.load_path()
27✔
1052
        if basename(dir) in Base.project_names && isfile(dir)
72✔
1053
            for name in get_loading_candidates(s, dir)
3✔
1054
                push!(suggestions, PackageCompletion(name))
6✔
1055
            end
6✔
1056
        end
1057
        isdir(dir) || continue
36✔
1058
        for entry in _readdirx(dir)
30✔
1059
            pname = entry.name
1,680✔
1060
            if pname[1] != '.' && pname != "METADATA" &&
3,360✔
1061
                pname != "REQUIRE" && startswith(pname, s)
1062
                # Valid file paths are
1063
                #   <Mod>.jl
1064
                #   <Mod>/src/<Mod>.jl
1065
                #   <Mod>.jl/src/<Mod>.jl
1066
                if isfile(entry)
786✔
1067
                    endswith(pname, ".jl") && push!(suggestions,
×
1068
                                                    PackageCompletion(pname[1:prevind(pname, end-2)]))
1069
                else
1070
                    mod_name = if endswith(pname, ".jl")
393✔
1071
                        pname[1:prevind(pname, end-2)]
×
1072
                    else
1073
                        pname
786✔
1074
                    end
1075
                    if isfile(joinpath(entry, "src",
393✔
1076
                                       "$mod_name.jl"))
1077
                        push!(suggestions, PackageCompletion(mod_name))
390✔
1078
                    end
1079
                end
1080
            end
1081
        end
1,680✔
1082
    end
36✔
1083
end
1084

1085
function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true, hint::Bool=false)
1,510✔
1086
    # filename needs to be string so macro can be evaluated
1087
    # TODO: JuliaSyntax version API here
1088
    node = parseall(CursorNode, string, ignore_errors=true, keep_parens=true, filename="none")
2,867✔
1089
    cur = @something seek_pos(node, pos) node
1,475✔
1090

1091
    # Back up before whitespace to get a more useful AST node.
1092
    pos_not_ws = findprev(!isspace, string, pos)
1,460✔
1093
    cur_not_ws = something(seek_pos(node, pos_not_ws), node)
2,905✔
1094

1095
    suggestions = Completion[]
1,460✔
1096
    sort_suggestions() = sort!(unique!(named_completion, suggestions), by=named_completion_completion)
2,213✔
1097

1098
    # Search for methods (requires tab press):
1099
    #   ?(x, y)TAB           lists methods you can call with these objects
1100
    #   ?(x, y TAB           lists methods that take these objects as the first two arguments
1101
    #   MyModule.?(x, y)TAB  restricts the search to names in MyModule
1102
    if !hint
1,460✔
1103
        cs = method_search(view(string, 1:pos), context_module, shift)
1,427✔
1104
        cs !== nothing && return cs
1,427✔
1105
    end
1106

1107
    # Complete keys in a Dict:
1108
    #   my_dict[ TAB
1109
    n, key, closed = find_ref_key(cur_not_ws, pos)
1,643✔
1110
    if n !== nothing
1,418✔
1111
        key::UnitRange{Int}
225✔
1112
        obj = dict_eval(Expr(n), context_module)
225✔
1113
        if obj !== nothing
225✔
1114
            # Skip leading whitespace inside brackets.
1115
            i = @something findnext(!isspace, string, first(key)) nextind(string, last(key))
228✔
1116
            key = intersect(i:last(key), 1:pos)
228✔
1117
            s = string[key]
411✔
1118
            matches = find_dict_matches(obj, s)
213✔
1119
            length(matches) == 1 && !closed && (matches[1] *= ']')
213✔
1120
            if length(matches) > 0
213✔
1121
                ret = Completion[DictCompletion(obj, match) for match in sort!(matches)]
189✔
1122
                return ret, key, true
189✔
1123
            end
1124
        end
1125
    end
1126

1127
    # Complete Cmd strings:
1128
    #   `fil TAB                 => `file
1129
    #   `file ~/exa TAB          => `file ~/example.txt
1130
    #   `file ~/example.txt TAB  => `file /home/user/example.txt
1131
    if (n = find_parent(cur, K"CmdString")) !== nothing
1,229✔
1132
        off = char_first(n) - 1
15✔
1133
        ret, r, success = shell_completions(string[char_range(n)], pos - off, hint, escape_backticks=true)
30✔
1134
        success && return ret, r .+ off, success
15✔
1135
    end
1136

1137
    # Complete ordinary strings:
1138
    #  "~/exa TAB         => "~/example.txt"
1139
    #  "~/example.txt TAB => "/home/user/example.txt"
1140
    r, closed = find_str(cur)
1,346✔
1141
    if r !== nothing
1,217✔
1142
        r = intersect(r, 1:pos)
129✔
1143
        s = do_string_unescape(string[r])
258✔
1144
        ret, success = complete_path_string(s, hint; string_escape=true,
129✔
1145
                                            dirsep=Sys.iswindows() ? '\\' : '/')
1146
        if length(ret) == 1 && !closed && close_path_completion(ret[1].path)
129✔
1147
            ret[1] = PathCompletion(ret[1].path * '"')
22✔
1148
        end
1149
        success && return ret, r, success
129✔
1150
    end
1151

1152
    # Backlash symbols:
1153
    #   \pi => π
1154
    # Comes after string completion so backslash escapes are not misinterpreted.
1155
    ok, ret = bslash_completions(string, pos)
1,143✔
1156
    ok && return ret
1,143✔
1157

1158
    # Don't fall back to symbol completion inside strings or comments.
1159
    inside_cmdstr = find_parent(cur, K"cmdstring") !== nothing
1,034✔
1160
    (kind(cur) in KSet"String Comment ErrorEofMultiComment" || inside_cmdstr) &&
2,051✔
1161
         return Completion[], 1:0, false
1162

1163
    n, arg_pos = find_prefix_call(cur_not_ws)
1,398✔
1164
    if n !== nothing
999✔
1165
        func = first(children_nt(n))
399✔
1166
        e = Expr(n)
399✔
1167
        # Remove arguments past the first parse error (allows unclosed parens)
1168
        if is_broadcasting_expr(e)
399✔
1169
            i = findfirst(x -> x isa Expr && x.head == :error, e.args[2].args)
72✔
1170
            i !== nothing && deleteat!(e.args[2].args, i:lastindex(e.args[2].args))
18✔
1171
        else
1172
            i = findfirst(x -> x isa Expr && x.head == :error, e.args)
3,201✔
1173
            i !== nothing && deleteat!(e.args, i:lastindex(e.args))
381✔
1174
        end
1175

1176
        # Method completion:
1177
        #   foo( TAB     => list of method signatures for foo
1178
        #   foo(x, TAB   => list of methods signatures for foo with x as first argument
1179
        if kind(cur_not_ws) in KSet"( , ;"
765✔
1180
            # Don't provide method completions unless the cursor is after: '(' ',' ';'
1181
            return complete_methods(e, context_module, shift, arg_pos), char_range(func), false
246✔
1182

1183
        # Keyword argument completion:
1184
        #   foo(ar TAB   => keyword arguments like `arg1=`
1185
        elseif kind(cur) == K"Identifier"
153✔
1186
            r = intersect(char_range(cur), 1:pos)
153✔
1187
            s = string[r]
306✔
1188
            # Return without adding more suggestions if kwargs only
1189
            complete_keyword_argument!(suggestions, e, s, context_module, arg_pos; shift) &&
153✔
1190
                return sort_suggestions(), r, true
1191
        end
1192
    end
1193

1194
    # Symbol completion
1195
    # TODO: Should completions replace the identifier at the cursor?
1196
    looks_like_ident = Base.isidentifier(@view string[intersect(char_range(cur), 1:pos)])
669✔
1197
    if cur.parent !== nothing && kind(cur.parent) === K"var"
1,293✔
1198
        # Replace the entire var"foo", but search using only "foo".
1199
        r = intersect(char_range(cur.parent), 1:pos)
9✔
1200
        r2 = char_range(children_nt(cur.parent)[1])
12✔
1201
        s = string[intersect(r2, 1:pos)]
15✔
1202
    elseif cur.parent !== nothing && kind(cur.parent) === K"macro_name"
1,275✔
1203
        # Include the `@`
1204
        r = intersect(prevind(string, char_first(cur)):char_last(cur), 1:pos)
36✔
1205
        s = string[r]
36✔
1206
    elseif looks_like_ident || kind(cur) in KSet"Bool Identifier @"
1,112✔
1207
        r = intersect(char_range(cur), 1:pos)
363✔
1208
        s = string[r]
363✔
1209
    else
1210
        r = nextind(string, pos):pos
492✔
1211
        s = ""
246✔
1212
    end
1213

1214
    complete_modules_only = false
654✔
1215
    prefix = node_prefix(cur, context_module)
654✔
1216
    comp_keywords = prefix === nothing && !isempty(s)
654✔
1217

1218
    # Complete loadable module names:
1219
    #   import Mod TAB
1220
    #   import Mod1, Mod2 TAB
1221
    #   using Mod TAB
1222
    if (n = find_parent(cur, K"importpath")) !== nothing
654✔
1223
        # Given input lines like `using Foo|`, `import Foo, Bar|` and `using Foo.Bar, Baz, |`:
1224
        # Let's look only for packages and modules we can reach from here
1225
        if prefix === nothing
96✔
1226
            complete_loading_candidates!(suggestions, s)
27✔
1227
            return sort_suggestions(), r, true
27✔
1228
        end
1229

1230
        # Allow completion for `import Mod.name` (where `name` is not a module)
1231
        complete_modules_only = prefix === nothing || kind(n.parent) === K"using"
138✔
1232
        comp_keywords = false
69✔
1233
    end
1234

1235
    if comp_keywords
627✔
1236
        complete_keyword!(suggestions, s)
201✔
1237
        complete_keyval!(suggestions, s)
201✔
1238
    end
1239

1240
    complete_symbol!(suggestions, prefix, s, context_module; complete_modules_only, shift)
627✔
1241
    return sort_suggestions(), r, true
627✔
1242
end
1243

1244
function close_path_completion(path)
1245
    path = expanduser(path)
54✔
1246
    path = do_string_unescape(path)
54✔
1247
    !Base.isaccessibledir(path)
54✔
1248
end
1249

1250
# Lowering can misbehave with nested error expressions.
1251
function expr_has_error(@nospecialize(e))
2,215✔
1252
    e isa Expr || return false
6,307✔
1253
    e.head === :error &&  return true
1,145✔
1254
    any(expr_has_error, e.args)
1,127✔
1255
end
1256

1257
# Is the cursor inside the square brackets of a ref expression?  If so, returns:
1258
# - The ref node
1259
# - The range of characters for the brackets
1260
# - A flag indicating if the closing bracket is present
1261
function find_ref_key(cur::CursorNode, pos::Int)
1262
    n = find_parent(cur, K"ref")
1,418✔
1263
    n !== nothing || return nothing, nothing, nothing
2,608✔
1264
    key, closed = find_delim(n, K"[", K"]")
456✔
1265
    if key === nothing || !(first(key) - 1 <= pos <= last(key))
456✔
1266
        return nothing, nothing, nothing
3✔
1267
    end
1268
    return n, key, closed
225✔
1269
end
1270

1271
# If the cursor is in a literal string, return the contents and char range
1272
# inside the quotes.  Ignores triple strings.
1273
function find_str(cur::CursorNode)
1274
    n = find_parent(cur, K"string")
1,217✔
1275
    n !== nothing || return nothing, nothing
2,305✔
1276
    find_delim(n, K"\"", K"\"")
129✔
1277
end
1278

1279
# Is the cursor directly inside of the arguments of a prefix call (no nested
1280
# expressions)?  If so, return:
1281
#   - The call node
1282
#   - Either :positional or :kwargs, if the cursor is before or after the `;`
1283
function find_prefix_call(cur::CursorNode)
1284
    n = cur.parent
999✔
1285
    n !== nothing || return nothing, nothing
1,014✔
1286
    is_call(n) = kind(n) in KSet"call dotcall" && is_prefix_call(n)
2,550✔
1287
    if kind(n) == K"parameters"
984✔
1288
        is_call(n.parent) || return nothing, nothing
180✔
1289
        n.parent, :kwargs
174✔
1290
    else
1291
        # Check that we are beyond the function name.
1292
        is_call(n) && cur.index > children_nt(n)[1].index || return nothing, nothing
1,389✔
1293
        n, :positional
225✔
1294
    end
1295
end
1296

1297
# If node is the field in a getfield-like expression, return the value
1298
# complete_symbol! should use as the prefix.
1299
function node_prefix(node::CursorNode, context_module::Module)
654✔
1300
    node.parent !== nothing || return nothing
669✔
1301
    p = node.parent
639✔
1302
    # In x.var"y", the parent is the "var" when the cursor is on "y".
1303
    kind(p) == K"var" && (p = p.parent)
1,278✔
1304
    kind(p) == K"macro_name" && (p = p.parent)
1,278✔
1305

1306
    # expr.node => expr
1307
    if kind(p) == K"."
1,278✔
1308
        n = children_nt(p)[1]
324✔
1309
        # Don't use prefix if we are the value
1310
        n !== node || return nothing
324✔
1311
        return Expr(n)
324✔
1312
    end
1313

1314
    if kind(p) == K"importpath"
630✔
1315
        if p.parent !== nothing && kind(p.parent) == K":" && p.index_nt > 1
192✔
1316
            # import A.B: C.node
1317
            chain = children_nt(children_nt(p.parent)[1])
36✔
1318
            append!(chain, children_nt(p)[1:end-1])
18✔
1319
        else
1320
            # import A.node
1321
            # import A.node: ...
1322
            chain = children_nt(p)[1:node.index_nt]
156✔
1323
            # Don't include the node under cursor in prefix unless it is `.`
1324
            kind(chain[end]) != K"." && deleteat!(chain, lastindex(chain))
78✔
1325
        end
1326
        length(chain) > 0 || return nothing
123✔
1327

1328
        # (:importpath :x :y :z) => (:. (:. :x :y) :z)
1329
        # (:importpath :. :. :z) => (:. (parentmodule context_module) :z)
1330
        if (i = findlast(x -> kind(x) == K".", chain)) !== nothing
291✔
1331
            init = context_module
57✔
1332
            for j in 2:i
57✔
1333
                init = parentmodule(init)
12✔
1334
            end
15✔
1335
            deleteat!(chain, 1:i)
114✔
1336
        else
1337
            # No leading `.`, init is the first element of the path
1338
            init = chain[1].val
12✔
1339
            deleteat!(chain, 1)
12✔
1340
        end
1341

1342
        # Convert the "chain" into nested (. a b) expressions.
1343
        all(x -> kind(x) == K"Identifier", chain) || return nothing
111✔
1344
        return foldl((x, y) -> Expr(:., x, Expr(:quote, y.val)), chain; init)
111✔
1345
    end
1346

1347
    nothing
219✔
1348
end
1349

1350
function dict_eval(@nospecialize(e), context_module::Module=Main)
225✔
1351
    objt = repl_eval_ex(e.args[1], context_module)
225✔
1352
    isa(objt, Core.Const) || return nothing
234✔
1353
    obj = objt.val
216✔
1354
    isa(obj, AbstractDict) || return nothing
219✔
1355
    (Base.haslength(obj) && length(obj)::Int < 1_000_000) || return nothing
213✔
1356
    return obj
213✔
1357
end
1358

1359
function method_search(partial::AbstractString, context_module::Module, shift::Bool)
1,427✔
1360
    rexm = match(r"([\w.]+.)?\?\((.*)$", partial)
1,427✔
1361
    if rexm !== nothing
1,427✔
1362
        # Get the module scope
1363
        callee_module = context_module
42✔
1364
        if !isnothing(rexm.captures[1])
81✔
1365
            modnames = map(Symbol, split(something(rexm.captures[1]), '.'))
78✔
1366
            for m in modnames
39✔
1367
                if isdefined(callee_module, m)
78✔
1368
                    callee_module = getfield(callee_module, m)
39✔
1369
                    if !isa(callee_module, Module)
39✔
1370
                        callee_module = context_module
×
1371
                        break
×
1372
                    end
1373
                end
1374
            end
78✔
1375
        end
1376
        moreargs = !endswith(rexm.captures[2], ')')
42✔
1377
        callstr = "_(" * rexm.captures[2]
42✔
1378
        if moreargs
42✔
1379
            callstr *= ')'
24✔
1380
        end
1381
        ex_org = Meta.parse(callstr, raise=false, depwarn=false)
42✔
1382
        if isa(ex_org, Expr)
42✔
1383
            pos_q = isnothing(rexm.captures[1]) ? 1 : sizeof(something(rexm.captures[1]))+1 # position after ?
81✔
1384
            return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:pos_q) .+ rexm.offset, false
42✔
1385
        end
1386
    end
1387
end
1388

1389
function shell_completions(str, pos, hint::Bool=false; escape_backticks::Bool=false)
194✔
1390
    # First parse everything up to the current position
1391
    scs = str[1:pos]
144✔
1392
    args, last_arg_start = try
72✔
1393
        Base.shell_parse(scs, true)::Tuple{Expr,Int}
76✔
1394
    catch ex
1395
        ex isa ArgumentError || ex isa ErrorException || rethrow()
8✔
1396
        return Completion[], 1:0, false
72✔
1397
    end
1398
    ex = args.args[end]::Expr
68✔
1399
    # Now look at the last thing we parsed
1400
    isempty(ex.args) && return Completion[], 1:0, false
68✔
1401
    # Concatenate every string fragment so dir\file completes correctly.
1402
    lastarg = all(x -> x isa String, ex.args) ? string(ex.args...) : ex.args[end]
204✔
1403

1404
    # As Base.shell_parse throws away trailing spaces (unless they are escaped),
1405
    # we need to special case here.
1406
    # If the last char was a space, but shell_parse ignored it search on "".
1407
    if isexpr(lastarg, :incomplete) || isexpr(lastarg, :error)
131✔
1408
        partial = str[last_arg_start:pos]
10✔
1409
        ret, range = completions(partial, lastindex(partial), Main, true, hint)
10✔
1410
        range = range .+ (last_arg_start - 1)
5✔
1411
        return ret, range, true
5✔
1412
    elseif endswith(scs, ' ') && !endswith(scs, "\\ ")
63✔
1413
        r = pos+1:pos
8✔
1414
        paths, dir, success = complete_path(""; use_envpath=false, cmd_escape=true, escape_backticks, dirsep='/')
4✔
1415
        return paths, r, success
4✔
1416
    elseif all(@nospecialize(arg) -> arg isa AbstractString, ex.args)
135✔
1417
        path = join(ex.args)
44✔
1418
    elseif Meta.isexpr(get(ex.args, 1, nothing), :call) &&
15✔
1419
           (call = ex.args[1]::Expr; call.args[1] === :expanduser) &&
36✔
1420
           (tilde_idx = findfirst(a -> a isa String, call.args); tilde_idx !== nothing) &&
36✔
1421
           all(@nospecialize(arg) -> arg isa AbstractString, ex.args[2:end])
8✔
1422
        tilde_str = call.args[tilde_idx]::String  # "~" or "~alice"
12✔
1423
        rest = join(ex.args[2:end])
20✔
1424
        r = last_arg_start:pos
12✔
1425
        if isempty(rest)
24✔
1426
            # No path after the tilde: complete usernames.
1427
            username_prefix = SubString(tilde_str, 2)  # everything after "~"
8✔
1428
            usernames = _complete_tilde_usernames(username_prefix)
4✔
1429
            paths = Completion[PathCompletion(do_cmd_escape_tilde(u; escape_backticks))
4✔
1430
                               for u in usernames]
1431
            return paths, r, !isempty(paths)
4✔
1432
        end
1433
        path = tilde_str * rest
8✔
1434
    else
1435
        return Completion[], 1:0, false
3✔
1436
    end
1437
    r = last_arg_start:pos
52✔
1438
    use_envpath = length(args.args) < 2
52✔
1439
    paths, success = complete_path_string(path, hint; use_envpath, cmd_escape=true, escape_backticks, dirsep='/')
104✔
1440
    return paths, r, success
52✔
1441
end
1442

1443
function complete_path_string(path, hint::Bool=false;
362✔
1444
                              cmd_escape::Bool=false,
1445
                              escape_backticks::Bool=false,
1446
                              string_escape::Bool=false,
1447
                              dirsep='/',
1448
                              kws...)
1449
    # Expand "~" and remember if we expanded it.
1450
    unexpanded_path = path
181✔
1451
    path = try expanduser(path) catch e; e isa ArgumentError || rethrow(); path end
181✔
1452
    expanded = path != unexpanded_path
181✔
1453

1454
    function escape(p)
181✔
1455
        # When the original input used a tilde (expanded=true), completions are
1456
        # returned in "~/..." form.  Use tilde-aware escaping so the "~[user]"
1457
        # prefix is left unquoted while the rest of the path is properly escaped.
1458
        string_escape && (p = do_string_escape(p))
1,132✔
1459
        cmd_escape && (p = expanded ? do_cmd_escape_tilde(p; escape_backticks) : do_cmd_escape(p; escape_backticks))
1,849✔
1460
        p
1,132✔
1461
    end
1462

1463
    paths, dir, success = complete_path(path; dirsep, kws...)
181✔
1464

1465
    # For string literals (not backtick commands): when the path already exists
1466
    # or has no completions, expand ~ to the absolute path so the user sees the
1467
    # resolved location. (In backtick context shell_parse handles ~ at runtime,
1468
    # so the contracted tilde form is kept; normal processing produces it.)
1469
    if !cmd_escape && expanded && !hint && !endswith(path, '/')
181✔
1470
        full_path = try
13✔
1471
            ispath(path) || isempty(paths)
20✔
1472
        catch err
1473
            if err isa Base.IOError
×
1474
                false
×
1475
            elseif err isa Base.ArgumentError && occursin("embedded NULs", err.msg)
×
1476
                false
×
1477
            else
1478
                rethrow()
13✔
1479
            end
1480
        end
1481
        full_path && return Completion[PathCompletion(escape(path))], true
13✔
1482
    end
1483

1484
    # Contract the directory back to tilde form when the input used a tilde.
1485
    expanded && (dir = contractuser(dir))
172✔
1486
    local dir_for_paths = dir
172✔
1487

1488
    map!(paths) do c::PathCompletion
172✔
1489
        p = joinpath_withsep(dir_for_paths, c.path; dirsep)
1,123✔
1490
        PathCompletion(escape(p))
1,840✔
1491
    end
1492
    return sort!(paths, by=p->p.path), success
19,850✔
1493
end
1494

1495
function __init__()
20✔
1496
    COMPLETION_WORLD[] = Base.get_world_counter()
20✔
1497
    return nothing
20✔
1498
end
1499

1500
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