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
54 changes: 52 additions & 2 deletions base/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,64 @@ get_value(sym::Symbol, fn) = isdefined(fn, sym) ? (fn.(sym), true) : (nothing, f
get_value(sym::QuoteNode, fn) = isdefined(fn, sym.value) ? (fn.(sym.value), true) : (nothing, false)
get_value(sym, fn) = sym, true

# Return the value of a getfield call expression
function get_value_getfield(ex::Expr, fn)
# Example :((top(getfield))(Base,:max))
val, found = get_value_getfield(ex.args[2],fn) #Look up Base in Main and returns the module
found || return (nothing, false)
get_value_getfield(ex.args[3],val) #Look up max in Base and returns the function if found.
end
get_value_getfield(sym, fn) = get_value(sym, fn)
# Determines the return type with Base.return_types of a function call using the type information of the arguments.
function get_type_call(expr::Expr)
f_name = expr.args[1]
# The if statement should find the f function. How f is found depends on how f is referenced
if isa(f_name, TopNode)
f = Base.(f_name.name)
found = true
elseif isa(f_name, Expr) && f_name.args[1] === TopNode(:getfield)
f, found = get_value_getfield(f_name, Main)
else
f, found = get_value(f_name, Main)
end
found || return (Any, false) # If the function f is not found return Any.
args = Any[]
for ex in expr.args[2:end] # Find the type of the function arguments
typ, found = get_type(ex, Main)
found ? push!(args, typ) : push!(args, Any)
end
return_types = Base.return_types(f,Tuple{args...})
length(return_types) == 1 || return (Any, false)
return (return_types[1], true)
end
# Returns the return type. example: get_type(:(Base.strip("",' ')),Main) returns (ASCIIString,true)
function get_type(sym::Expr, fn)
sym=expand(sym)
val, found = get_value(sym, fn)
found && return Base.typesof(val).parameters[1], found
if sym.head === :call
# getfield call is special cased as the evaluation of getfield provides good type information,
# is inexpensive and it is also performed in the complete_symbol function.
if sym.args[1] === TopNode(:getfield)
val, found = get_value_getfield(sym, Main)
return found ? Base.typesof(val).parameters[1] : Any, found
end
return get_type_call(sym)
end
(Any, false)
end
function get_type(sym, fn)
val, found = get_value(sym, fn)
return found ? Base.typesof(val).parameters[1] : Any, found
end
# Method completion on function call expression that look like :(max(1))
function complete_methods(ex_org::Expr)
args_ex = DataType[]
func, found = get_value(ex_org.args[1], Main)
(!found || (found && !isgeneric(func))) && return UTF8String[]
for ex in ex_org.args[2:end]
val, found = get_value(ex, Main)
found ? push!(args_ex, Base.typesof(val).parameters[1]) : push!(args_ex, Any)
val, found = get_type(ex, Main)
push!(args_ex, val)
end
out = UTF8String[]
t_in = Tuple{args_ex...} # Input types
Expand Down
50 changes: 44 additions & 6 deletions test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ module CompletionFoo
test3(x::AbstractArray{Int}, y::Int) = pass
test3(x::AbstractArray{Float64}, y::Float64) = pass

test4(x::AbstractString, y::AbstractString) = pass
test4(x::AbstractString, y::Regex) = pass

test5(x::Array{Bool,1}) = pass
test5(x::BitArray{1}) = pass
test5(x::Float64) = pass

array = [1, 1]
varfloat = 0.1
end

function temp_pkg_dir(fn::Function)
Expand Down Expand Up @@ -186,7 +194,7 @@ c, r, res = test_complete(s)
@test r == 1:3
@test s[r] == "max"

# Test completion of methods with input args
# Test completion of methods with input concrete args and args where typeinference determine their type
s = "CompletionFoo.test(1,1, "
c, r, res = test_complete(s)
@test !res
Expand Down Expand Up @@ -239,24 +247,54 @@ for (T, arg) in [(ASCIIString,"\")\""),(Char, "')'")]
@test s[r] == "CompletionFoo.test2"
end

# This cannot find the correct method due to the backticks expands to a macro in the parser.
# Then the function argument is an expression which is not handled by current method completion logic.
s = "(1, CompletionFoo.test2(`)`,"
c, r, res = test_complete(s)
@test length(c) == 3
@test c[1] == string(methods(CompletionFoo.test2, Tuple{Cmd})[1])
@test length(c) == 1

s = "CompletionFoo.test3([1.,2.],"
s = "CompletionFoo.test3([1, 2] + CompletionFoo.varfloat,"
c, r, res = test_complete(s)
@test !res
@test length(c) == 2
@test c[1] == string(methods(CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64})[1])
@test length(c) == 1

s = "CompletionFoo.test3([1.,2.], 1.,"
c, r, res = test_complete(s)
@test !res
@test c[1] == string(methods(CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64})[1])
@test r == 1:19
@test length(c) == 1
@test s[r] == "CompletionFoo.test3"

s = "CompletionFoo.test4(\"e\",r\" \","
c, r, res = test_complete(s)
@test !res
@test c[1] == string(methods(CompletionFoo.test4, Tuple{ASCIIString, Regex})[1])
@test r == 1:19
@test length(c) == 1
@test s[r] == "CompletionFoo.test4"

s = "CompletionFoo.test5(push!(Base.split(\"\",' '),\"\",\"\").==\"\","
c, r, res = test_complete(s)
@test !res
@test length(c) == 1
@test c[1] == string(methods(CompletionFoo.test5, Tuple{BitArray{1}})[1])

########## Test where the current inference logic fails ########
# Fails due to inferrence fails to determine a concrete type from the map
# But it returns AbstractArray{T,N} and hence is able to remove test5(x::Float64) from the suggestions
s = "CompletionFoo.test5(map(x-> x==\"\",push!(Base.split(\"\",' '),\"\",\"\")),"
c, r, res = test_complete(s)
@test !res
@test length(c) == 2

# equivalent to above but due to the time macro the completion fails to find the concrete type
s = "CompletionFoo.test3(@time([1, 2] + CompletionFoo.varfloat),"
c, r, res = test_complete(s)
@test !res
@test length(c) == 2
#################################################################

# Test completion in multi-line comments
s = "#=\n\\alpha"
c, r, res = test_complete(s)
Expand Down