Typeaware autocompletion


#1

Is somebody looking into typeaware completion? I think it would be really nice feature to have:

array=[1,2]
mean( #if you hit tab / ctrl+space now, 'array' shows between first candidates

It would be also nice to have the same thing for objects like OOP languages, but since julia does’t support syntax object.function, you cannot easily list all possible functions that can be applied to given object (aka methods). The only thing that comes to my mind is |>.

array=[1,2]
array |> #if you hit tab / ctrl+space now, 'mean', 'sort' etc shows between candidates

But that wouldn’t work for functions with multiple arguments…

So in case somebody already explored this a little bit, could you share with us some insights?

  1. How complicated is it to implement such a thing?
  2. Would it be lightweight enough to be included in REPL?
  3. Any workaround for the missing OOP syntax?
  4. Anything else that should be mentioned?

#2

In the first example, do you mean that the variable called array would autocomplete when the user types mean(<tab>? The issue there is that mean, like many other Julia functions, has several methods. As an example, consider the method mean(f, x), where f is a function. The REPL can’t know that you want the mean(x) method; you could just as easily want mean(f, x), in which case what should it autocomplete? The current behavior of showing methods seems like the most general and useful solution to me.

In order to autocomplete methods from a pipe, the expression on the left hand side would need to be evaluated so that its type could be determined and the appropriate methods suggested. That seems like a waste of computational resources, since the expression would need to be evaluated for the autocomplete then again for the actual call that’s made.

For exploration of applicable methods, check out method_exists and applicable.


#3

, do you mean that the variable called array would autocomplete when …

Exactly.

… in which case what should it autocomplete?

I would expect to get list of completions for all viable methods (that means both for mean(x) and for mean(f, x)). In this concrete case I would get list of all available arrays x with list of all functions f with list of all functions and macros which can generate valid x or f etc. Basically everything that doesn’t lead to type error.

The current behavior of showing methods seems like the most general …

The current solution offers you all declared variables and functions without blink of an eye even when it is not defined for them (e.g. String). I would say it is way too general.

That seems like a waste of computational resources…

I am not really an expert on this topic but I think we could look at how other mature languages do this (java, typescript).


#4

I think this would be a cool idea for a package. You could write functionality that implements these more sophisticated autocompletions and then you could just easily make the TAB key call this function instead of the default one.


#5

As well as the multiple methods issue mentioned above you have the problem that all function names ought still be included in the list of completions as well as all struct types that may have a field of the appropriate type (and their own fields, etc) and any type of variable that could be followed by a binary operator whose return type is the appropriate type for the argument. In the end you’ll probably end up with the same list of variables


#6

That is a fair point. Functions that take arguments of type Any are really getting in the way.

In statically typed languages typeaware completion can’t produce ill typed expression. It can only produce incomplete expression.

@$%(::String, ::String) -> Int
type Foo
  a :: String
  b :: Int
end
x = Foo(1, "2")
mean(#now the possible candidates will include x.b, x.a @$%, but not x.a and x on its own

Therefore if you keep hitting tab long enough, you should get expression that doesn’t throw type error.

The fact Julia is dynamically typed makes this a bit more complicated.


#7

I have something of the sort in GtkIDE, what I autocomplete is a tuple, basically I ask “what are the methods that can be applied to this tuple”, which I think is the correct equivalent of the dot completion in OOP languages.

So for example if I have

D = Poisson(1)
(D,1)

When I press tab after the ) I get as autocompletion:

promote{T,S}(x::T, y::S) at promotion.jl:153
quantile(d::Distributions.Poisson, q::Real) at /Users/jbieler/.julia/v0.5/Distributions/src/univariates.jl:328
quantile(d::Distributions.Distribution{Distributions.Univariate,S<:Distributions.ValueSupport}, p::Real) at /Users/jbieler/.julia/v0.5/Distributions/src/univariates.jl:163
rand(d::Distributions.Distribution{Distributions.Univariate,S<:Distributions.ValueSupport}, n::Int64) at /Users/jbieler/.julia/v0.5/Distributions/src/univariates.jl:67
range{T}(a::T, len::Integer) at range.jl:107
entropy(d::Distributions.Distribution{Distributions.Univariate,S<:Distributions.ValueSupport}, b::Real) at /Users/jbieler/.julia/v0.5/Distributions/src/univariates.jl:75
ccdf(d::Distributions.Poisson, x::Int64) at /Users/jbieler/.julia/v0.5/Distributions/src/univariates.jl:324
...

Or

(String,Float64)

poll_file(s::AbstractString, interval_seconds::Real) at poll.jl:409
promote{T,S}(x::T, y::S) at promotion.jl:153
watch_file(s::AbstractString, timeout_s::Real) at poll.jl:389
tex2pango(str::AbstractString, fontsize::Real) at /Users/jbieler/.julia/v0.5/Cairo/src/Cairo.jl:1094]

And the when you press Enter it prepends the method name to the tuple. In practice I don’t use it that much, but it’s been a bit buggy, a proper implementation might be quite useful.


#8

I think the REPL autocomplete does something similar already – when you have e.g.

struct Foo
  a::Int
end
[Foo(1), Foo(2)][1].\tab

you get a autocompleted. IIRC, Julia looks at the return type of the expression before the dot and then finds its return type, which you could also do for pipes.


#9

As already mentioned the REPL do already do autocompletion based on types. Here’s an example from the manual


julia> max([1,2],[TAB] # All methods where `Vector{Int}` matches as first argument
max{T1<:Real,T2<:Real}(x::AbstractArray{T1,N<:Any}, y::T2) at operators.jl:544
max{Tx<:Real,Ty<:Real}(x::Union{Base.ReshapedArray{Tx,1,A<:DenseArray,MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N<:Any}}},DenseArray{Tx,1},SubArray{Tx,1,A<:Union{Base.ReshapedArray{T<:Any,N<:Any,A<:DenseArray,MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N<:Any}}},DenseArray},I<:Tuple{Vararg{Union{Base.AbstractCartesianIndex,Colon,Int64,Range{Int64}},N<:Any}},L<:Any}}, y::AbstractSparseArray{Ty,Ti<:Any,1}) at sparse\sparsevector.jl:1127
max{T1<:Real,T2<:Real}(x::AbstractArray{T1,N<:Any}, y::AbstractArray{T2,N<:Any}) at operators.jl:548
max(x, y) at operators.jl:78
max(a, b, c, xs...) at operators.jl:119

julia> max([1,2], max(1,2),[TAB] # All methods matching the arguments.
max{T1<:Real,T2<:Real}(x::AbstractArray{T1,N<:Any}, y::T2) at operators.jl:544
max(x, y) at operators.jl:78
max(a, b, c, xs...) at operators.jl:119

The code blok is from the manual https://docs.julialang.org/en/stable/manual/interacting-with-julia/#tab-completion


#10

For the second case:
Is it difficult to have a type inference pass and a methodswith call with the inferred type to get possible completions?

Something like:

array=[1,2]
methodswith(typeof(array),Base,true)```

It gives a 1000+ possible methods with nice performances