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.
|