Skip to content

Commit

Permalink
Handle infix operators in REPL completion (#51366)
Browse files Browse the repository at this point in the history
Fix #51194

This PR fixes a regression introduced in
#49294, so I believe it should be
backported to v1.10.
In the current code, completion of `qux(foo, bar.` is detected by
parsing `foo(qux, bar` as an incomplete expression, and then looking for
the sub-expression to complete (here, `bar.`). This approach fails
however for infix calls, since completing `foo + bar.` starts by parsing
`foo + bar`, which is a complete call expression, and so the code
behaves as if completing `(foo + bar).` instead of `bar.`. This leads to
the current problematic behaviour:
```julia
julia> Complex(1, 3) + (4//5).#TAB
im
re
```
which would be correct for `(Complex(1, 3) + (4//5)).#TAB`, but here we
expect
```julia
julia> Complex(1, 3) + (4//5).#TAB
den
num
```

This PR fixes that by trying to detect infix calls. In the long term,
all this ad-hoc and probably somewhat wrong string processing should be
replaced by proper use of `JuliaSyntax` (as mentioned in
#49294 (comment),
#50817 (comment)
and probably other places), but for now at least this fixes the
regression.
  • Loading branch information
Liozou committed Oct 12, 2023
1 parent 71872d1 commit e949236
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 0 deletions.
11 changes: 11 additions & 0 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,17 @@ function complete_identifiers!(suggestions::Vector{Completion}, @nospecialize(ff
ex = Meta.parse(lookup_name, raise=false, depwarn=false)
end
isexpr(ex, :incomplete) && (ex = nothing)
elseif isexpr(ex, :call) && length(ex.args) > 1
isinfix = s[end] != ')'
# A complete call expression that does not finish with ')' is an infix call.
if !isinfix
# Handle infix call argument completion of the form bar + foo(qux).
frange, end_of_identifier = find_start_brace(@view s[1:prevind(s, end)])
isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == ex.args[end]
end
if isinfix
ex = ex.args[end]
end
end
end
append!(suggestions, complete_symbol(ex, name, ffunc, context_module))
Expand Down
15 changes: 15 additions & 0 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let ex = quote
(::Test_y)() = "", ""
unicode_αβγ = Test_y(1)

Base.:(+)(x::Test_x, y::Test_y) = Test_x(Test_y(x.xx.yy + y.yy))
module CompletionFoo2

end
Expand Down Expand Up @@ -2069,3 +2070,17 @@ end
# If this last test starts failing, that's okay, just pick a new example symbol:
@test !Base.isexported(Base, :ispublic)
end

# issue #51194
for (s, compl) in (("2*CompletionFoo.nam", "named"),
(":a isa CompletionFoo.test!1", "test!12"),
("-CompletionFoo.Test_y(3).", "yy"),
("99 ⨷⁻ᵨ⁷ CompletionFoo.type_test.", "xx"),
("CompletionFoo.type_test + CompletionFoo.Test_y(2).", "yy"),
("(CompletionFoo.type_test + CompletionFoo.Test_y(2)).", "xx"),
("CompletionFoo.type_test + CompletionFoo.unicode_αβγ.", "yy"),
("(CompletionFoo.type_test + CompletionFoo.unicode_αβγ).", "xx"),
("foo'CompletionFoo.test!1", "test!12"))
c, r = test_complete(s)
@test only(c) == compl
end

0 comments on commit e949236

Please sign in to comment.