Bug 340945 - Extension Request of JavaContentAssistInvocationContext
Summary: Extension Request of JavaContentAssistInvocationContext
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.7   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: 3.8 M4   Edit
Assignee: Ayushman Jain CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-25 08:14 EDT by Marcel Bruch CLA
Modified: 2012-01-05 08:24 EST (History)
8 users (show)

See Also:


Attachments
proposed fix + tests (22.14 KB, patch)
2011-11-03 05:35 EDT, Ayushman Jain CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Marcel Bruch CLA 2011-03-25 08:14:54 EDT
Current JavaContentAssistInvocationContext provides access to the expected type of a completion request, the prefix the developer has already typed, as well as the enclosing IJavaElement code completion was triggered in.

The Eclipse Code Recommenders project aims to provide more fine-grained proposals based on what has been done with the object before code completion was triggered on. For instance, given that a constructor has been invoked on a, say, SWT Button Code Recommenders can infer that only a few proposals make sense in the current situation and  thus may filter the number of proposals made by JDT to just those actually relevant in the given completion context. 

For this computation, we currently have to re-run the CompletionParser and recompute the JDT proposals to determine the information on which variable completion was triggered on etc.

Since this process is rather costly we would like to ask the JDT team whether the current completion context could be extended to provide read access to little more information than available yet.

Information that would be beneficial in JavaContentAssistInvocationContext:

1. Variable code completion was triggered on (IType and Name would be great)
2. Access to all default completion proposals offered by JDT (we use that to create our own subset of proposals + decorations)
3. Information of all available locals, and fields in the given context (required for chain completion). Thus would also include the type and name of the variables.
4. Access to the requested completion type 



Proposed extensions to JavaContentAssistInvocationContext could look as follows:

getAccessibleFieldDeclarations(): List<FieldDeclaration>
getAccessibleLocalDeclarations(): List<LocalDeclaration>
getReceiverVariable() : VariableDeclaration
getCompletionNode(): ASTNode (currently taken from Compiler AST)
getCompletionNodeParent():  ASTNode (currently taken from Compiler AST)
isReceiverImplicitThis(): boolean

Code Recommenders currently has it's own completion context which does roughly this:

IIntelligentCompletionContext.java: http://goo.gl/7rAs2
IntelligentCompletionContext.java: http://goo.gl/imOiK
ASTCompletionNodeFinder.java: http://goo.gl/2boRm

What do you think?
Comment 1 Dani Megert CLA 2011-03-25 10:35:53 EDT
Let's see what we can do for the next release.
Comment 2 Marcel Bruch CLA 2011-09-29 05:07:54 EDT
If 3.8 M3 is targeted: I'll ask Marko Martin (the student who implemented JDT chain completion) to contribute his code asap and to test it with 3.8 M3

Thanks!
Comment 3 Ayushman Jain CLA 2011-10-04 02:32:22 EDT
Marcel, have you looked at org.eclipse.jdt.core.CompletionContext and org.eclipse.jdt.core.CompletionProposal. They seem to have most of the information you need. See org.eclipse.jdt.core.tests.model.CompletionTestsRequestor2 for usage examples. You can pass your own requestor in the org.eclipse.jdt.core.ICodeAssist.codeComplete(int, CompletionRequestor, WorkingCopyOwner, IProgressMonitor) API. 
Or if you're using jdt.ui's requestor, you caan query the completion proposals for the info as given in CompletionProposal and CompletionContext. 

Would that serve your purpose?
Comment 4 Marcel Bruch CLA 2011-10-05 01:41:20 EDT
(In reply to comment #3)

Let's see:

> getAccessibleFieldDeclarations(): List<FieldDeclaration>

We may implement it using  org.eclipse.jdt.internal.codeassist.InternalCompletionContext.getVisibleElements(String) where String is "java.lang.Object" + Filtering for IFields. Would this work?

> getAccessibleLocalDeclarations(): List<LocalDeclaration>

Same as above.

> getReceiverVariable() : VariableDeclaration

Is there any equivalent way to express this? Yet, I didn't find any.

> getCompletionNode(): ASTNode (currently taken from Compiler AST)

These nodes are essential because we need to know where/which type of code completion was requested. This information is hidden somewhere in the internal context already:
org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext.assistNode but not accessible from CompletionProposalComputer classes via CompletionContext. If I remember correctly, the assistNode is always of kind CompletionOn*** which is what we need.

> getCompletionNodeParent():  ASTNode (currently taken from Compiler AST)

Similar to the above?

> isReceiverImplicitThis(): boolean

I don't know yet if this could be implemented based on the above information. Likely.


I might try to implement another completion context based on reflection to obtain the internal completion context and see how far I get? Maybe it would work to make this field accessible and add few more information.


Another, related question: Are proposal computations cached?
Comment 5 Ayushman Jain CLA 2011-10-05 05:58:24 EDT
(In reply to comment #4)
> (In reply to comment #3)
> 
> Let's see:
> 
> > getAccessibleFieldDeclarations(): List<FieldDeclaration>
> 
> We may implement it using 
> org.eclipse.jdt.internal.codeassist.InternalCompletionContext.getVisibleElements(String)
> where String is "java.lang.Object" + Filtering for IFields. Would this work?

The argument should be the type signature, such as Ljava/lang/Object;

> > getCompletionNode(): ASTNode (currently taken from Compiler AST)
> 
> These nodes are essential because we need to know where/which type of code
> completion was requested. This information is hidden somewhere in the internal
> context already:
> org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext.assistNode
> but not accessible from CompletionProposalComputer classes via
> CompletionContext. If I remember correctly, the assistNode is always of kind
> CompletionOn*** which is what we need.
> 
> > getCompletionNodeParent():  ASTNode (currently taken from Compiler AST)

For this I can provide APIs getCompletionNode() and getCompletionNodeParent(). Should be straightforward.

> > getReceiverVariable() : VariableDeclaration
> > isReceiverImplicitThis(): boolean

These two I'm not sure if you can find using the available APIs. So, basically these new APIs will return the VariableDeclaration in case the completion is of the form
someField.<CTRL-SPC>
someLocal.<CTRL-SPC>

and null otherwise (eg. in someMethod().<CTRL-SPC> or <CTRL-SPC>)

What would isReceiverImplicitThis() do exactly? Maybe you can infer an implicit this every time you find an empty completion token, no?

 
> I might try to implement another completion context based on reflection to
> obtain the internal completion context and see how far I get? 

Umm. Maybe, if you need still more info than what CompletionContext already provides. But otherwise just implementing your own CompletionRequestor should do. You can see how CompletionRequestor2 is implemented. Put a breakpoint somewhere in the getContext(..) method and debug the CompletionTest2 suite. Or extend JDT.Ui's requestor.

> Another, related question: Are proposal computations cached?
Not in jdt.core. The requestor collects/caches them.
Comment 6 Ayushman Jain CLA 2011-10-05 07:29:19 EDT
(In reply to comment #5)

> > > getCompletionNodeParent():  ASTNode (currently taken from Compiler AST)
> 
> For this I can provide APIs getCompletionNode() and getCompletionNodeParent().
> Should be straightforward.

Actually this isn't so straightforward. The CompletionOn** nodes are internal compiler AST nodes and can't be provided through an API. Can you tell me what information do you need from the completion node and the parent? Maybe we can give these specific pieces of info from APIs.
Comment 7 Marcel Bruch CLA 2011-10-05 07:58:24 EDT
(In reply to comment #6)
> > > > getCompletionNodeParent():  ASTNode (currently taken from Compiler AST)
> > 
> > For this I can provide APIs getCompletionNode() and getCompletionNodeParent().
> > Should be straightforward.
> 
> Actually this isn't so straightforward. The CompletionOn** nodes are internal
> compiler AST nodes and can't be provided through an API. Can you tell me what
> information do you need from the completion node and the parent? Maybe we can
> give these specific pieces of info from APIs.

Does it make a difference if it becomes part of the official API?

Anyways, I use the completion node (statement) 


1. to determine the type of the receiver (Button, Text, etc.) and to determine the name of the variable (if any). Then, given the type and name, I can lookup which methods have been invoked on this variable before.

2. to determine the expected return type of a completion (not sure how ATM).

3. to determine which proposals I should offer. For instance, it makes a difference if I trigger a completion on a method composite.setLayoutData(|^Space) or in composite.setLayout(|^Space). In the first case I could propose new GridData() or FormData if a formLayout has been used before in the latter I should propose the use of an prexisting layout object. 

Do you need more examples? Does it make sense to specify a set of completion scenarios?
Comment 8 Ayushman Jain CLA 2011-10-14 04:01:54 EDT
(In reply to comment #7)

> Does it make a difference if it becomes part of the official API?

These AST nodes use a lot of internal stuff and thus cannot be provided as API.

Anyhow, I've been playing around with ways to give you all the information that you need and it looks not just inefficient to convert all the internal stuff to something that can be returned as an API, but may also not be too useable to you. Ex: I was trying to return an IJavaElement for the completion node, but then that would work only for simple cases i.e. completion on a local variable or a field, but not on when its done on a method, because then you will also need the receiver and its type.

I think we can take the non-API route here, so that all info is available to you  from the InternalExtendedCompletionContext without having to re-run the CompletionParser. So, I can just provide :

InternalExtendedCompletionContext#getCompletionNode() {
 return this.assistNode
}

and

InternalExtendedCompletionContext#getCompletionNodeParent() {
  return this.completionParser.assistNodeParent;
}

to return these private fields to you.

Then you can use your current ASTCompletionNodeFinder.java's evaluate...() methods to calculate the rest of the info.
 
What do you think?
Comment 9 Marcel Bruch CLA 2011-10-14 17:52:30 EDT
(In reply to comment #8)

> What do you think?

Would be fine. How can I obtain the list of accessible fields and locals then? This would be needed for the callchain completion and the next iteration of the subwords completion (see http://vimeo.com/11664433 for a related work)
Comment 10 Ayushman Jain CLA 2011-10-20 05:00:13 EDT
Moving this to M4 since I was busy with critical compiler issues in M3. 
Marcel, I'll need to check about the visible fields and locals, because even though we do have the org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext.visibleFields
and org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext.visibleLocalVariables

However, I think these do not reliably provide the info, since we sometimes reject parts of the CU that are not relevant for the completion proposal. Eg: 
void foo() {
   int i = 1;
   Object o = new Object {
        void running() {
              int j = 1;
              <CTRL-SPC>
         }
}

Here j is not available. So I can guarantee about providing completion node and its parent as specified in the above comment, but not so sure about visible fields and locals.
Comment 11 Marcel Bruch CLA 2011-10-21 06:51:56 EDT
(In reply to comment #10)
> Here j is not available. 

I don't understand why j wouldn't be available here. Is there some documentation/explanation that explains when a local variable is available or not? 

At the moment, we use ASTVisitor.visit(final LocalDeclaration localDeclaration, final BlockScope scope) to collect all local variables known to the completion(!) parser. This worked out well - but we may have missed some details? Is there anything I can do?
Comment 12 Ayushman Jain CLA 2011-10-21 09:51:19 EDT
(In reply to comment #11)
> This worked out well - but we may have missed some
> details? Is there anything I can do?

Let me investigate more on this and come back to you with concrete examples. I couldn't spend much time on this in M3.
Comment 13 Ayushman Jain CLA 2011-11-03 05:35:11 EDT
Created attachment 206388 [details]
proposed fix + tests

Here's a patch which provides access to the completion node, its parent, and the visible fields, locals, and methods.

Marcel, I was wrong in the above comment about some locals not being available. I was looking at other usages from jdt core and in those cases the assignableType parameter of the getVisibleElements(..) method was set to values which restricted the kind of locals we want.

Anyway, this patch should be sufficient. I made sure here that even the assist node parent is cached as soon as the extended context is constructed, in addition to the assist node itself. Let me know if this works for you and i'll commit it.
Comment 14 Srikanth Sankaran CLA 2011-11-16 22:06:33 EST
(In reply to comment #13)
> Created attachment 206388 [details]
> proposed fix + tests

[...]
 
> Let me know if this works for you and i'll
> commit it.

Hello Marcel, It has been two weeks since this patch was posted. Have you
had a chance to check it out ? If you are not in a position to apply source
patches and experiment with them, let us know.
Comment 15 Ayushman Jain CLA 2011-11-17 00:17:01 EST
(In reply to comment #14)
> Hello Marcel, It has been two weeks since this patch was posted. Have you
> had a chance to check it out ? If you are not in a position to apply source
> patches and experiment with them, let us know.

Thats ok Srikanth. Marcel has been in touch on twitter, and is testing the patch these days. :)
Comment 16 Marcel Bruch CLA 2011-11-17 05:26:53 EST
I just run the patch with eclipse 3.7.1 and jdt.core 3.8 (only) from git master. 

It seems, that the completion context that gets handed over to
ICompletionProposalComputer.computeCompletionProposals(ContentAssistInvocationContext, IProgressMonitor) is not extended. Am I supposed to reuse this context? 

My test code looks like this:

    @Override
    public List<ICompletionProposal> computeCompletionProposals(final ContentAssistInvocationContext context, final IProgressMonitor monitor) {
        final JavaContentAssistInvocationContext jCtx = (JavaContentAssistInvocationContext) context;
        final InternalCompletionContext coreContext = (InternalCompletionContext) jCtx.getCoreContext();
        if (coreContext.isExtended()) {
            // didn't manage to get in here:
            final ASTNode completionNode = coreContext.getCompletionNode();
            final ASTNode completionNodeParent = coreContext.getCompletionNodeParent();
            final ObjectVector visibleFields = coreContext.getVisibleFields();
        }

I think the collector responsible for this is created in  org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext.computeKeywordsAndContext():280. It does not request an extended context.

Am I supposed to re-run code completion again in my own collector that requests the extended context?
Comment 17 Ayushman Jain CLA 2011-11-21 06:32:07 EST
(In reply to comment #16)
> I think the collector responsible for this is created in 
> org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext.computeKeywordsAndContext():280.
> It does not request an extended context.

This is only called when no collector is found in the JavaContentAssistInvocationContext. Usually if i debug using another Eclipse instance, I can always see the FillArgumentNamesCompletionProposalCollector requesting the proposals, and this always has the extended context. This is created in org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposalComputer.createCollector(JavaContentAssistInvocationContext)

How did you end up inside computeKeywordsAndContext() ?

> Am I supposed to re-run code completion again in my own collector that requests
> the extended context?

If you're using your own collector, then you have to request extended context. But you won't need to re-run completion since, as shown above, jdt.ui's collector already has the extended context.(In reply to comment #16)


> It seems, that the completion context that gets handed over to
> ICompletionProposalComputer.computeCompletionProposals(ContentAssistInvocationContext,
> IProgressMonitor) is not extended. Am I supposed to reuse this context? 

You can look at JDT/UI's implementation - org.eclipse.jdt.internal.ui.text.java.JavaTypeCompletionProposalComputer.computeCompletionProposals(ContentAssistInvocationContext, IProgressMonitor)

The context here is JavaContentAssistInvocationContext which has a collector in which extended information is present.

Is there any specific test case when you don't get the extended context?
Comment 18 Marcel Bruch CLA 2011-11-22 10:22:30 EST
(In reply to comment #17)

> How did you end up inside computeKeywordsAndContext() ?

No idea. I was just plain wrong. Sorry for that :)


I run the completion again and it works *very* nicely:
The completion nodes look perfect; the completionNodeParents are there when needed (local variable declaration and field declarations); visible fields and locals seem to work - what else can I need?!

We probably get rid of our complete AST parsing code now. Thanks!


Let me get you a beer at EclipseCon :)

Best,
Marcel
Comment 19 Ayushman Jain CLA 2011-11-22 11:07:29 EST
(In reply to comment #18)
> (In reply to comment #17)
> 
> > How did you end up inside computeKeywordsAndContext() ?
> 
> No idea. I was just plain wrong. Sorry for that :)
> 
> 
> I run the completion again and it works *very* nicely:
> The completion nodes look perfect; the completionNodeParents are there when
> needed (local variable declaration and field declarations); visible fields and
> locals seem to work - what else can I need?!

That's great news! Thanks a lot for your patience and more thanks for all the good work you've been doing in Code Recommenders. Let me get this patch released. :)

> Let me get you a beer at EclipseCon :)
Looking forward to it :)
Comment 20 Ayushman Jain CLA 2011-11-28 12:50:40 EST
Released to HEAD via commit a123639b0e67de0eec1abd2bc005d5cc75ac4787.
Comment 21 Srikanth Sankaran CLA 2011-12-06 07:04:21 EST
Fix verified by submitter (see comment#18)
Comment 22 Marcel Bruch CLA 2011-12-07 14:47:34 EST
I run into an issue. When the code completion engine is not on the "default" content assists list, the given InternalCoreContext is not an extended context anymore.

When activating my engine on both, default and any secondary content assist list, it works on the default tab but not on the secondary tab.  Can you reproduce this?
Comment 23 Ayushman Jain CLA 2011-12-07 15:01:25 EST
(In reply to comment #22)
> I run into an issue. When the code completion engine is not on the "default"
> content assists list, the given InternalCoreContext is not an extended context
> anymore.
> 
> When activating my engine on both, default and any secondary content assist
> list, it works on the default tab but not on the secondary tab.  Can you
> reproduce this?

I couldn't really understand this. What exactly do you mean by 'default' and 'secondary' lists? Are you talking about the cycling between different proposal kinds in the content assist suggestions tooltip?
Comment 24 Marcel Bruch CLA 2011-12-07 15:09:17 EST
(In reply to comment #23)
> I couldn't really understand this. What exactly do you mean by 'default' and
> 'secondary' lists? Are you talking about the cycling between different proposal
> kinds in the content assist suggestions tooltip?

Sorry for the bad naming convention. I think you understood it correctly.

default content assist list -> trigger ctrl+space once
second content assist list -> trigger ctrl+space twice

I subsumed all content assists that are not on the default content assist list as "secondary content assists lists"

If my callchain prototype is configured to the default list, all completions are there.
If it runs in a separate content list (2nd or 3rd etc.) the context is not extended anymore.
Comment 25 Ayushman Jain CLA 2012-01-05 08:24:29 EST
(In reply to comment #24)
> Sorry for the bad naming convention. I think you understood it correctly.
> 
> default content assist list -> trigger ctrl+space once
> second content assist list -> trigger ctrl+space twice
> 
> I subsumed all content assists that are not on the default content assist list
> as "secondary content assists lists"
> 
> If my callchain prototype is configured to the default list, all completions
> are there.
> If it runs in a separate content list (2nd or 3rd etc.) the context is not
> extended anymore.

Oops. I just now saw this comment. Terribly sorry for the delay. Can you file a bug for this against JDT/UI, and check why an extended context is not requested by the collector in that case? The behaviour wrt JDT/Core will not depend on the primary or secondary lists, but the way the completion APIs are called changes from JDT/UI. I don't have much knowledge about how that second list uses JDT/Core. Thanks!