Code Completion Proposal

Code Completion Proposal

The contents of this documents are subject to change at any time!

Note that the underscore _ is used to indicate the current location of the caret.

Reconciling Changes

The Quick Path

In order to get up to speed with actual completions, we can take advantage of the dynamic nature of groovy. That is, any expression that looks like a variable or property will compile to an AST. So say we want to complete "toString", then mystr.toS is a valid Groovy expression and will compile.
The reconciler will attempt to recompile the Groovy class whenever a completion is requested. In many cases the class will compile without error, and an ASTNode is available with which to compute possible completions.

The Best Path

An error correcting parser is required to deal with cases where the source code cannot be compiled. For example:

for (my_) { } // for expression is not complete
for (i in my_) // no braces
for (i in my_) { // unclosed braces.

All of the above can be recovered from with various changes to the groovy.g grammar and custom error correcting code where needed.

Code Completion

Completion Contexts

Completion only makes sense in certain contexts: Outside a class: A package:

package com._

An import:

import a._

A variable access/method call/class name:

my_

A property access/method call:

a.my_

Inside a class:
Completion for overriding methods, super class fields and method calls. Inside a method: fields, methods, local, parameters Inside a closure: as in inside a method, but also specialized, for example, Grails constraints definitions.

Code completion

Given an ASTNode, we want to know its type. A single magic method, getType(ASTNode node), is needed. This method returns the type if it is known, or request it from other sources. Some examples or type sources: a type inference engine, using a database of predefined completions for that name, or even asking the user. h3. Completion Processor Plug-ins The completion engine uses a collection of completion processor plugins to create a completion list. For any completion case, a completion processor which is linked to some completion context and some ASTNode class, can be implemented. For example:

class MyFrame extend JFrame {
  int party;
  getP_ // completions: add getParty() getter, or override getPreferredSize()
}

In this way, completions can be implemented by individuals without affecting the main code base, and when they are ready for release, they can be released as a plugin or rolled into a main completion plugin. Completion plugins will be sent the following information: If the completion is in the form of a single identifier (like a variable name or method name), then the ASTNode and the partial name will be sent to the completion processor. For example:

def myMethod() { do_ }

The completion processor will get the ASTNode representing 'do' as well as the string 'do'.
For completions that look like property accesses, the ASTNode of the parent will be sent to the processor:

def myMethod(){ thing.do_ }

The ASTNode for 'thing' and the prefix 'do' is sent to the processor. If there is not prefix like in the case of 'thing._', then the prefix is null.

Completion Without Inference

This is quite easy: getType(ASTNode node) simply returns the type of the ASTNode. If the node represents a statically typed name, then the type is returned. Else java.lang.Object is returned.

Type Inference

Unless the Groovy code is being used from Java, quite often names are not statically typed. Luckily, at some point types can be found because of Groovy's close ties with Java.

Simple Inference

Local variable initializers and assignments:
def myInt = 10 // An Integer
myInt = "Twenty" // A String
Field initializers and assignments:
As above.
Return types of method calls.
Parameters:
def myMethod(a, b) { }
Searching for calls within the same class will often give us a type.

Conundrum: Methods may return different types in Groovy. Does getType() return an array?

The Fun Stuff aka, Not So Easy Inference

Complex assignments:
def myInt = 10 + 20 + thing[20] / otherThing
Keeping track of list types:
def list = [10]
list[0].toH_ // complete with toHexString()
Completing on Subtypes:
java.awt.Shape myShape = ...
myShape.width // assumes shape might be a Rectangle2D
How far does one go? How fast is this? What happens in this case:
Object myShape // Yikes, the whole class path can be a completion.

Labels

 
(None)