Skip to content

Commit 382a665

Browse files
authored
REPL: enable MethodCompletions after keyword arguments (#38106)
* enable `MethodCompletion`s after keyword arguments * improve type stability
1 parent 9b0232e commit 382a665

File tree

2 files changed

+56
-10
lines changed

2 files changed

+56
-10
lines changed

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct MethodCompletion <: Completion
4040
func
4141
input_types::Type
4242
method::Method
43+
orig_method::Union{Nothing,Method} # if `method` is a keyword method, keep the original method for sensible printing
4344
end
4445

4546
struct BslashCompletion <: Completion
@@ -89,7 +90,7 @@ _completion_text(c::ModuleCompletion) = c.mod
8990
_completion_text(c::PackageCompletion) = c.package
9091
_completion_text(c::PropertyCompletion) = string(c.property)
9192
_completion_text(c::FieldCompletion) = string(c.field)
92-
_completion_text(c::MethodCompletion) = sprint(io -> show(io, c.method))
93+
_completion_text(c::MethodCompletion) = sprint(io -> show(io, isnothing(c.orig_method) ? c.method : c.orig_method::Method))
9394
_completion_text(c::BslashCompletion) = c.bslash
9495
_completion_text(c::ShellCompletion) = c.text
9596
_completion_text(c::DictCompletion) = c.key
@@ -314,7 +315,7 @@ end
314315
function should_method_complete(s::AbstractString)
315316
method_complete = false
316317
for c in reverse(s)
317-
if c in [',', '(']
318+
if c in [',', '(', ';']
318319
method_complete = true
319320
break
320321
elseif !(c in whitespace_chars)
@@ -465,34 +466,58 @@ end
465466

466467
# Method completion on function call expression that look like :(max(1))
467468
function complete_methods(ex_org::Expr, context_module::Module=Main)
468-
args_ex = Any[]
469469
func, found = get_value(ex_org.args[1], context_module)::Tuple{Any,Bool}
470470
!found && return Completion[]
471471

472472
funargs = ex_org.args[2:end]
473473
# handle broadcasting, but only handle number of arguments instead of
474474
# argument types
475+
args_ex = Any[]
476+
kwargs_ex = Pair{Symbol,Any}[]
475477
if ex_org.head === :. && ex_org.args[2] isa Expr
476478
for _ in (ex_org.args[2]::Expr).args
477479
push!(args_ex, Any)
478480
end
479481
else
480482
for ex in funargs
481-
val, found = get_type(ex, context_module)
482-
push!(args_ex, val)
483+
if isexpr(ex, :parameters)
484+
for x in ex.args
485+
n, v = isexpr(x, :kw) ? (x.args...,) : (x, x)
486+
push!(kwargs_ex, n => first(get_type(v, context_module)))
487+
end
488+
elseif isexpr(ex, :kw)
489+
n, v = (ex.args...,)
490+
push!(kwargs_ex, n => first(get_type(v, context_module)))
491+
else
492+
push!(args_ex, first(get_type(ex, context_module)))
493+
end
483494
end
484495
end
485496

486497
out = Completion[]
487-
t_in = Tuple{Core.Typeof(func), args_ex...} # Input types
488-
na = length(args_ex)+1
489498
ml = methods(func)
490-
for method in ml
499+
# Input types and number of arguments
500+
if isempty(kwargs_ex)
501+
t_in = Tuple{Core.Typeof(func), args_ex...}
502+
na = length(t_in.parameters)::Int
503+
orig_ml = fill(nothing, length(ml))
504+
else
505+
isdefined(ml.mt, :kwsorter) || return out
506+
kwfunc = ml.mt.kwsorter
507+
kwargt = NamedTuple{(first.(kwargs_ex)...,), Tuple{last.(kwargs_ex)...}}
508+
t_in = Tuple{Core.Typeof(kwfunc), kwargt, Core.Typeof(func), args_ex...}
509+
na = length(t_in.parameters)::Int
510+
orig_ml = ml # this method is supposed to be used for printing
511+
ml = methods(kwfunc)
512+
func = kwfunc
513+
end
514+
515+
for (method::Method, orig_method) in zip(ml, orig_ml)
491516
ms = method.sig
492517

493518
# Check if the method's type signature intersects the input types
494-
if typeintersect(Base.rewrap_unionall(Tuple{(Base.unwrap_unionall(ms)::DataType).parameters[1 : min(na, end)]...}, ms), t_in) !== Union{}
495-
push!(out, MethodCompletion(func, t_in, method))
519+
if typeintersect(Base.rewrap_unionall(Tuple{(Base.unwrap_unionall(ms)::DataType).parameters[1 : min(na, end)]...}, ms), t_in) != Union{}
520+
push!(out, MethodCompletion(func, t_in, method, orig_method))
496521
end
497522
end
498523
return out

stdlib/REPL/test/replcompletions.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ let ex = quote
6565
test7() = rand(Bool) ? 1 : 1.0
6666
test8() = Any[1][1]
6767
kwtest(; x=1, y=2, w...) = pass
68+
kwtest2(a; x=1, y=2, w...) = pass
6869

6970
array = [1, 1]
7071
varfloat = 0.1
@@ -483,13 +484,33 @@ let s = "CompletionFoo.test3(@time([1, 2] + CompletionFoo.varfloat),"
483484
end
484485
#################################################################
485486

487+
# method completions with kwargs
486488
let s = "CompletionFoo.kwtest( "
487489
c, r, res = test_complete(s)
488490
@test !res
489491
@test length(c) == 1
490492
@test occursin("x, y, w...", c[1])
491493
end
492494

495+
for s in ("CompletionFoo.kwtest(;",
496+
"CompletionFoo.kwtest(; x=1, ",
497+
"CompletionFoo.kwtest(; kw=1, ",
498+
)
499+
c, r, res = test_complete(s)
500+
@test !res
501+
@test length(c) == 1
502+
@test occursin("x, y, w...", c[1])
503+
end
504+
505+
for s in ("CompletionFoo.kwtest2(1; x=1,",
506+
"CompletionFoo.kwtest2(1; kw=1, ",
507+
)
508+
c, r, res = test_complete(s)
509+
@test !res
510+
@test length(c) == 1
511+
@test occursin("a; x, y, w...", c[1])
512+
end
513+
493514
# Test of inference based getfield completion
494515
let s = "(1+2im)."
495516
c,r = test_complete(s)

0 commit comments

Comments
 (0)