[
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