Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,15 @@ code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState))
MAX_METHOD_COMPLETIONS::Int = 40
function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
isempty(ex_org.args) && return 2, nothing, [], Set{Symbol}()
# Desugar do block call into call with lambda
if ex_org.head === :do && length(ex_org.args) >= 2
ex_call = ex_org.args[1]
ex_args = [x for x in ex_call.args if !(x isa Expr && x.head === :parameters)]
ex_params = findfirst(x -> x isa Expr && x.head === :parameters, ex_call.args)
new_args = [ex_args[1], ex_org.args[end], ex_args[2:end]...]
ex_params !== nothing && push!(new_args, ex_call.args[ex_params])
ex_org = Expr(:call, new_args...)
end
Comment on lines +695 to +703
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The special-casing of the do block makes me slightly nervous and I wonder if a more general solution would be possible (at the juliacon hackathon you mentioned the option of reading just up to position for the purpose of completion, but that also has drawbacks), but I could confirm this fixes the issue (as confirmed by the tests as well) and that makes me happy!

Copy link
Member Author

@xal-0 xal-0 Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I'd call it a special case; I was just unaware when I changed this that JuliaSyntax puts the do inside the call node rather than outside, like Expr:

julia> Base.JuliaSyntax.parsestmt(Base.JuliaSyntax.SyntaxNode, "foo(a) do b; c end")
SyntaxNode:
[call]
  foo                                    :: Identifier
  a                                      :: Identifier
  [do]
    [tuple]
      b                                  :: Identifier
    [block]
      c                                  :: Identifier

julia> Meta.show_sexpr(Expr(Base.JuliaSyntax.parsestmt(Base.JuliaSyntax.SyntaxNode, "foo(a) do b; c end")))
(:do, (:call, :foo, :a), (:->, (:tuple, :b), (:block,
      :(#= line 1 =#),
      :c
    )))

The previous REPL completion code would have ignored the do entirely, iirc. This change actually "fixes" method completion with a do block for free:

julia> foo(f, x::Int, y::Int) = 1
       foo(f, x::String, y::String) = 2
foo (generic function with 2 methods)

julia> foo(3, ) do x; x end
foo(f, x::Int64, y::Int64) @ Main REPL[1]:1

Is this useful enough to justify parsing the input beyond the cursor? I don't know, but so far I haven't seen anything that really causes it to explode.

funct = repl_eval_ex(ex_org.args[1], context_module)
funct === nothing && return 2, nothing, [], Set{Symbol}()
funct = CC.widenconst(funct)
Expand Down
10 changes: 10 additions & 0 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ let ex =
kwtest4(a::SubString; x23, _something) = pass
kwtest5(a::Int, b, x...; somekwarg, somekotherkwarg) = pass
kwtest5(a::Char, b; xyz) = pass
kwtest6(f::Function, arg1; somekwarg) = pass

const named = (; len2=3)
const fmsoebelkv = (; len2=3)
Expand Down Expand Up @@ -198,6 +199,8 @@ test_scomplete(s) = map_completion_text(@inferred(shell_completions(s, lastinde
test_complete_pos(s) = map_completion_text(@inferred(completions(replace(s, '|' => ""), findfirst('|', s)-1)))
test_complete_context(s, m=@__MODULE__; shift::Bool=true) =
map_completion_text(@inferred(completions(s,lastindex(s), m, shift)))
test_complete_context_pos(s, m=@__MODULE__; shift::Bool=true) =
map_completion_text(@inferred(completions(replace(s, '|' => ""), findfirst('|', s)-1, m, shift)))
test_complete_foo(s; shift::Bool=true) = test_complete_context(s, Main.CompletionFoo; shift)
test_complete_noshift(s) = map_completion_text(@inferred(completions(s, lastindex(s), Main, false)))

Expand Down Expand Up @@ -2742,3 +2745,10 @@ let s = "for"
@test "foreach" in c
@test !("rand" in c)
end

# #58833 - Autocompletion of keyword arguments with do-blocks is broken
let s = "kwtest6(123; som|) do x; x + 3 end"
c, r = test_complete_context_pos(s, Main.CompletionFoo)
@test "somekwarg=" in c
@test r == 14:16
end