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 Chrisitian and everyone,

I am currently trying to wrap my head around the refactoring you suggested. Although it seemed straight forward at first, the more I think about it the more problems arise.

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.

Constructor:
  • Java class constructor
  • Constructor for class from method
  • Constructor for local class
Call:
  • Local function
  • Class method
  • Module method
Instance:
  • Local object
  • Class field
  • Module field
  • Class
  • Package
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.

It is no problem to use dynamic analysis with the script shell because I already have all the information there. If I want to reuse this for editor autocompletion though I do not have this information in the same form.

What I am currently doing is first gather the static information and then refine it with the information from the shell. In the end I only need the dynamic information about the first item in the chain. I want to implement it in a way, that multiple context providers can register and are called in order until one of them manages to match the item. So for example we have a ModuleContextProvider that can be used for loaded (and possibly not loaded) modules, another for local variables, and so on. The dynamic analysis will call all of these providers in order until it finds a match for "foo".

With this solution it would be possible to reuse most of the code and just add different context providers for the editor. It might also be possible to also reuse some of the context providers. E.G.: If we have a provider for all (unloaded) modules this can be reused directly.

Do you understand what I want to do here and if so, do you think this would be a good idea?



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.

As I mentioned in previous mails I split the information in a call chain and discard everything that is not relevant to the current chain. I can also gather information that is not relevant to the current chain itself but gives context. I hope some examples will help me explain.

foo = "IO 1234";
include("System." + foo.toLower().subS

In this case the call chain would be [Local object String foo -> Java method String.toLower] and the filter would be "subS". I do not care about the "System." and for the chain the previous "include" is also not of interest. If I gather context information though, I would also save [Module method include]. This is very simple and no problem to do.


My first problem can be seen if we do something like this:

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

Here the call chain is the same but the context becomes more complicated. So the call chain is in the context of [Java constructor String] but the String constructor is in the context of [Module method include]. For the whole line the context would be [Module method include, Java constructor String]. Still not a problem to parse, but now we have a lot more context information we need to care about.


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

Now the context for the StringBuilder would be [Java constructor StringBuilder, Java method StringBuilder.append] and the whole context would be [Module method include, [Java constructor StringBuilder, Java method StringBuilder.append]].

My current implementation returns a list of call chains for the context and this is working fine. I am not sure though this is really easy to use for completion providers...
The current solution is the only one I could come up with that takes care of all the mentioned situations. Maybe it's a bit of an overkill but if we really want a generic solution I don't see how we could do it any other way. Do you have any suggestions?



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.

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.

Theoretically the best solution would be to modify the original ContentProposalAdapter to allow subclassing and call our own ICompletionProviders from that subclass. The problem is that this is out of the scope of EASE (I guess).

How should I proceed? Should I create the actual mentioned class and wrap the methods accordingly or should every ICompletionProvider get the information in the desired form itself.



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.

Best regards,
Martin



Am 2015-07-07 um 16:41 schrieb Christian Pontesegger:
Hi Martin,

now that mid term evaluations are passed and you have your first deliverable done I would like to do some refactorings on the completion code:

I want these proposals to be generic and extensible. Therefore we already have an extension point 'completionProcessor' and an interface 'ICompletionProvider'.
In its current form it is very simple and leaves the task to decode content information to each completion provider.

The refactoring should provide a helper method capable of parsing and understanding the source code left of the cursor position. A created context information then could be passed to ICompletionProviders. As script code always depends on the script language a good place for those helper method(s) would be the ModuleWrappers that exist for each script language.

Information that should be gathered:
content proposal source type (enum): eg when we need proposals for a java class then we would need an indicator for this
content proposal source: the java class to use
filter: parts of the text that is to be completed.
context: relevant code left from the cursor

Example:
Packages.java.io.
-> source type: java package
-> source: "java.io"
-> filter: null/empty

Packages.java.io.Fi
-> source type: java package
-> source: "java.io"
-> filter: "Fi"

include("file://
-> source type: module function
-> source: Module Definition instance
-> filter: "file://"

Together with the context this will allow to create content proposals for
* Java classes
* java instances
* methods exported from modules
* string literals (eg for includes, loadModule, ...)

Getting the relevant context from the cursor position will also help Vidura to provide relevant help information. The information to be gathered might need some extensions. Please discuss this together with Vidura as you both should use the same helper methods for this task.



Back to the top