Skip to content

Commit 59ad720

Browse files
xal-0giordanoJameson Nash
authored andcommitted
Fix completing positional arguments if a semicolon exists beyond the cursor (#58298)
This is a stopgap solution. For now, have `find_prefix_call` tell us whether the cursor is before the `;` (:positional) or after (:kwargs), and set `exact_nargs` only when it is :kwargs. Eventually, we should remove kwargs_flag entirely and have the method completions use our precise position information. Fixes #58296, and the related issue I mention in #58296 (comment). --------- Co-authored-by: Mosè Giordano <765740+giordano@users.noreply.github.com> Co-authored-by: Jameson Nash <jameson@juliacomputing.com> (cherry picked from commit da6356b)
1 parent 9aa677c commit 59ad720

File tree

2 files changed

+35
-13
lines changed

2 files changed

+35
-13
lines changed

stdlib/REPL/src/REPLCompletions.jl

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -697,9 +697,12 @@ function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool)
697697
return kwargs_flag, funct, args_ex, kwargs_ex
698698
end
699699

700-
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false)
700+
# cursor_pos: either :positional (complete either kwargs or positional) or :kwargs (beyond semicolon)
701+
function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false, cursor_pos::Symbol=:positional)
701702
kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
702703
out = Completion[]
704+
# Allow more arguments when cursor before semicolon, even if kwargs are present
705+
cursor_pos == :positional && kwargs_flag == 1 && (kwargs_flag = 0)
703706
kwargs_flag == 2 && return out # one of the kwargs is invalid
704707
kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon
705708
complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1)
@@ -876,11 +879,13 @@ end
876879
end
877880

878881
# Provide completion for keyword arguments in function calls
882+
# Returns true if the current argument must be a keyword because the cursor is beyond the semicolon
879883
function complete_keyword_argument!(suggestions::Vector{Completion},
880884
ex::Expr, last_word::String,
881-
context_module::Module; shift::Bool=false)
885+
context_module::Module,
886+
arg_pos::Symbol; shift::Bool=false)
882887
kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex, context_module, true)::Tuple{Int, Any, Vector{Any}, Set{Symbol}}
883-
kwargs_flag == 2 && false # one of the previous kwargs is invalid
888+
kwargs_flag == 2 && return false # one of the previous kwargs is invalid
884889

885890
methods = Completion[]
886891
# Limit kwarg completions to cases when function is concretely known; looking up
@@ -919,7 +924,7 @@ function complete_keyword_argument!(suggestions::Vector{Completion},
919924
for kwarg in kwargs
920925
push!(suggestions, KeywordArgumentCompletion(kwarg))
921926
end
922-
return kwargs_flag != 0
927+
return kwargs_flag != 0 && arg_pos == :kwargs
923928
end
924929

925930
function get_loading_candidates(pkgstarts::String, project_file::String)
@@ -1055,7 +1060,8 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
10551060
(kind(cur) in KSet"String Comment ErrorEofMultiComment" || inside_cmdstr) &&
10561061
return Completion[], 1:0, false
10571062

1058-
if (n = find_prefix_call(cur_not_ws)) !== nothing
1063+
n, arg_pos = find_prefix_call(cur_not_ws)
1064+
if n !== nothing
10591065
func = first(children_nt(n))
10601066
e = Expr(n)
10611067
# Remove arguments past the first parse error (allows unclosed parens)
@@ -1072,15 +1078,15 @@ function completions(string::String, pos::Int, context_module::Module=Main, shif
10721078
# foo(x, TAB => list of methods signatures for foo with x as first argument
10731079
if kind(cur_not_ws) in KSet"( , ;"
10741080
# Don't provide method completions unless the cursor is after: '(' ',' ';'
1075-
return complete_methods(e, context_module, shift), char_range(func), false
1081+
return complete_methods(e, context_module, shift, arg_pos), char_range(func), false
10761082

10771083
# Keyword argument completion:
10781084
# foo(ar TAB => keyword arguments like `arg1=`
10791085
elseif kind(cur) == K"Identifier"
10801086
r = char_range(cur)
10811087
s = string[intersect(r, 1:pos)]
10821088
# Return without adding more suggestions if kwargs only
1083-
complete_keyword_argument!(suggestions, e, s, context_module; shift) &&
1089+
complete_keyword_argument!(suggestions, e, s, context_module, arg_pos; shift) &&
10841090
return sort_suggestions(), r, true
10851091
end
10861092
end
@@ -1168,18 +1174,20 @@ function find_str(cur::CursorNode)
11681174
end
11691175

11701176
# Is the cursor directly inside of the arguments of a prefix call (no nested
1171-
# expressions)?
1177+
# expressions)? If so, return:
1178+
# - The call node
1179+
# - Either :positional or :kwargs, if the cursor is before or after the `;`
11721180
function find_prefix_call(cur::CursorNode)
11731181
n = cur.parent
1174-
n !== nothing || return nothing
1182+
n !== nothing || return nothing, nothing
11751183
is_call(n) = kind(n) in KSet"call dotcall" && is_prefix_call(n)
11761184
if kind(n) == K"parameters"
1177-
is_call(n.parent) || return nothing
1178-
n.parent
1185+
is_call(n.parent) || return nothing, nothing
1186+
n.parent, :kwargs
11791187
else
11801188
# Check that we are beyond the function name.
1181-
is_call(n) && cur.index > children_nt(n)[1].index || return nothing
1182-
n
1189+
is_call(n) && cur.index > children_nt(n)[1].index || return nothing, nothing
1190+
n, :positional
11831191
end
11841192
end
11851193

stdlib/REPL/test/replcompletions.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2679,3 +2679,17 @@ let s = "\"example: \$(named.len"
26792679
@test "len2" in c
26802680
@test r == 19:21
26812681
end
2682+
2683+
# #58296 - complete positional arguments before semicolon
2684+
let s = "string(findfi|; base=16)"
2685+
c, r = test_complete_pos(s)
2686+
@test "findfirst" in c
2687+
@test r == 8:13
2688+
end
2689+
2690+
# Unknown functions should not cause completions to fail
2691+
let s = "foo58296(findfi"
2692+
c, r = test_complete(s)
2693+
@test "findfirst" in c
2694+
@test r == 10:15
2695+
end

0 commit comments

Comments
 (0)