Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [ease-dev] GSoC Code completion refactorings

Hi Martin,

lets see...

First off, there are 2 different types of information I can gather. There is static information where I don't need to know anything about loaded modules, local object, etc. Then there is dynamic information that needs said knowledge. With static information I can only distinguish between an instance , a constructor and a call (and string literals). The following table will hopefully explain what I mean with instance.

[...]
So if I use static analysis on something like:
"foo.replace("\r", "").subString(10).toLow"
I can tell that "foo" is an instance of some sort and "replace" as well as "subString" are calls.

With dynamic analysis it looks a lot better:
"foo" is an object of type "String" and "replace" as well as "subString" are class methods of String.
In the end it boils down to the question if you can find out the java type of the first element in your chain, as you state below. So your static analysis will not get you anywhere when it just tells you that 'foo' is 'an instance of some sort'. In that case you cannot provide context proposals for the example above.

Lets see what the first element could be

In the shell:
* a variable
-> could be converted to a java class type by querying the script engine

* a function call
-> already quite hard. You might want to check for loaded module functions where you could depict the return type from the according java class. If it is a pure script function you are lost already

* a package name (eg: Packages.java.lang.)
-> as there is no registry for available packages and classes you cannot provide code completion here. At least you can detect that your root element is of type java package, then you can stop.

* nothing
-> there might not exist a root element yet, so completion proposals could provide variable names or module functions.


Now lets see for the editor:
it's a bit harder to parse for the root node here as you might have line breaks, include statements before your cursor position and so on. Still we could have the same root elements as before

* a variable:
-> as we have no engine available we need to look for variable definition/assignment statements in our code. For the sake of simplicity I suggest you scan the code that comes before the cursor position. First look for a variable definition and a type declaration as you suggested in your GSoC proposal. eg
// @type java.lang.String
var foo = ...

If you cannot find such a declaration, try to locate the last assignment to the variable and try to evaluate the right hand side of that expression. If that expression gets too complex, stop parsing. I suggest you try to evaluate java calls/constructors and module function calls. I would not even try to parse more complicated stuff (like operators, other variables used in the assignment, ...)
to have some examples, you might try to resolve
foo = new java.lang.String("...");
foo = getVersion();    // this one comes from a module

* a function call
-> try to locate all loadModule() commands prior to your cursor position. This will give you a list of loaded Modules (also check for their dependencies!) which you may use to provide module function suggestions.

* package name
-> same as above

* nothing
-> for variable names you could look for assignments and create a list of all left hand side expressions. Do not bother about the scope of such variables.

Next difficulty:
You want to get the relevant context for the completion providers. This is not a real problem but my current solution can become difficult to use.

include( new String("system." + foo.toLower().subS

Your context to be take into account is 'foo.toLower().subS'
Do not parse anything outside of your context. Still it would be usable that you provide the string data of anything you used for parsing to the completion provider for an eventual further analysis. So store the whole text in a field. Your parser then should only care about the relevant content.

The last problem with this is that the context can also contain a call chain.
include( new StringBuilder().append( foo.toLower().subS

Same as above, your context is 'foo.toLower().subS', the rest can be passed as plain text data.

Last issue:
We are currently using "org.eclipse.jface.fieldassist.ContentProposalAdapter" to display the completion proposals. This adapter class takes care of getting the completion proposals and displaying the corresponding popups. If we want to use our ICompletionProvider interface with a custom getProposals method that takes the gathered context information as a parameter, we can't do it directly. If we want to diplay a custom pop-up I am not sure we can either.

The problem is that it is not really possible to subclass the ContentProposalAdapter and just override the getProposals method (and maybe the pop-up) because the methods are private.

The class gets an "org.eclipse.jface.fieldassist.IContentProposalProvider" that our ICompletionProvider implements but the called interface method is defined as "getProposals(String contents, int position)". It would be no problem to just stick to the original eclipse interface and every ICompletionProvider would have to take care to get the context information on its own.

Would not go that way as we will get quite some ICompletionProviders over time. I do not think we should do the parsing over and over again.

Another solution would be to write a CompeltionProvider class where ICompletionProvider objects could register. The created CompletionProvider would conform to the eclipse interface and simply call our getProposals method with the calculated context information. The benefit would be that we could have several ICompletionProvider objects registered at once. If we ever come up with a new idea for a completion provider we would not have to modify the existing code but rather just create a new class and register it.

That's what we should do. We register a meta-CompletionProvider that delegates the job of collecting proposals to registered ICompletionProviders. But first it creates a context object that can be passed on to the different implementations of ICompletionProvider. I am quite certain that you need to alter that interface a little to make this work. Feel free to change the existing code here. it was just a proof of concept.

I know this was quite a lot, but if we really want a generic well thought-through and well working solution these are the things (from my perspective) that we need to take care of.

No deal. I hope my explanations will help a little. I guess the most tricky part is the context creation. Let me know if I can be of any help.

best regards
Christian


Back to the top