Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 368074

Summary: [content assist] CoreContext is extended for first content assist list only
Product: [Eclipse Project] JDT Reporter: Marcel Bruch <marcel.bruch>
Component: TextAssignee: Dani Megert <daniel_megert>
Status: RESOLVED INVALID QA Contact:
Severity: normal    
Priority: P3 CC: daniel_megert
Version: 3.8Keywords: investigate
Target Milestone: 3.8 M5   
Hardware: PC   
OS: Mac OS X - Carbon (unsup.)   
Whiteboard:

Description Marcel Bruch CLA 2012-01-07 01:02:58 EST
In 3.8 M4, the core context returned by JavaContentAssistInvocationContext differs for the 'default content assist list' (the one you get when triggering ctrl+space once) and any other subsequent content assist list (all subsequent content assist lists you get when triggering ctrl+space twice and more, i.e., when cycling through the proposal engines).

For 

@Override
public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext javaContext, IProgressMonitor monitor) {

boolean isExtended = ((JavaContentAssistInvocationContext) javaContext).getCoreContext().isExtended()

// isExtended == true iff proposal computer was configured to run on the first ('default') content assist, and false otherwise.

Any idea what's going wrong here?

A stacktrace that might help to nail down the problem:

java.lang.UnsupportedOperationException: Operation only supported in extended context
	at org.eclipse.jdt.internal.codeassist.InternalCompletionContext.getCompletionNode(InternalCompletionContext.java:387)
	at org.eclipse.recommenders.internal.completion.rcp.RecommendersCompletionContext.getCompletionNode(RecommendersCompletionContext.java:45)
	at org.eclipse.recommenders.internal.completion.rcp.calls.engine.CallsCompletionProposalComputer.isCompletionRequestSupported(CallsCompletionProposalComputer.java:126)
	at org.eclipse.recommenders.internal.completion.rcp.calls.engine.CallsCompletionProposalComputer.computeCompletionProposals(CallsCompletionProposalComputer.java:100)
	at org.eclipse.jdt.internal.ui.text.java.CompletionProposalComputerDescriptor.computeCompletionProposals(CompletionProposalComputerDescriptor.java:318)
	at org.eclipse.jdt.internal.ui.text.java.CompletionProposalCategory.computeCompletionProposals(CompletionProposalCategory.java:267)
	at org.eclipse.jdt.internal.ui.text.java.ContentAssistProcessor.collectProposals(ContentAssistProcessor.java:283)
	at org.eclipse.jdt.internal.ui.text.java.ContentAssistProcessor.computeCompletionProposals(ContentAssistProcessor.java:243)
	at org.eclipse.jface.text.contentassist.ContentAssistant.computeCompletionProposals(ContentAssistant.java:1830)
	at org.eclipse.jface.text.contentassist.CompletionProposalPopup.computeProposals(CompletionProposalPopup.java:556)
	at org.eclipse.jface.text.contentassist.CompletionProposalPopup.handleRepeatedInvocation(CompletionProposalPopup.java:541)
	at org.eclipse.jface.text.contentassist.CompletionProposalPopup.showProposals(CompletionProposalPopup.java:506)
	at org.eclipse.jface.text.contentassist.ContentAssistant.showPossibleCompletions(ContentAssistant.java:1656)
	at org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor$AdaptedSourceViewer.doOperation(CompilationUnitEditor.java:183)
	at org.eclipse.ui.texteditor.ContentAssistAction$1.run(ContentAssistAction.java:82)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
	at org.eclipse.ui.texteditor.ContentAssistAction.run(ContentAssistAction.java:80)
	at org.eclipse.jface.action.Action.runWithEvent(Action.java:498)
	at org.eclipse.ui.commands.ActionHandler.execute(ActionHandler.java:185)
	at org.eclipse.ui.internal.handlers.LegacyHandlerWrapper.execute(LegacyHandlerWrapper.java:109)
	at org.eclipse.core.commands.Command.executeWithChecks(Command.java:476)
	at org.eclipse.core.commands.ParameterizedCommand.executeWithChecks(ParameterizedCommand.java:508)
	at org.eclipse.ui.internal.handlers.HandlerService.executeCommand(HandlerService.java:169)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.executeCommand(WorkbenchKeyboard.java:468)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.press(WorkbenchKeyboard.java:786)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.processKeyEvent(WorkbenchKeyboard.java:885)
Comment 1 Marcel Bruch CLA 2012-01-07 01:06:11 EST
FWIW, this bug report has its origins here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=340945
Comment 2 Dani Megert CLA 2012-01-10 09:40:06 EST
Whether the extended context is available in the first invocation already depends on the enabled categories and whether argument names are requested. It's up to each processor to request the extended context by calling setRequireExtendedContext(true) on its own collector.
Comment 3 Marcel Bruch CLA 2012-01-10 10:37:51 EST
I wasn't aware that it's the responsibility of the engine to recreate the context if it's not extended. After digging into JavaCompletionProposalComputer#internalComputeCompletionProposals and its subclasses this became clearer to me. I added the some additional logic to my code that creates and  extended context if the currently given one isn't an extended one. This still looks odd to me but it seems to work. Please let me know if this violates or breaks some JDT concepts. Thanks for your assistance!


    public BaseRecommendersCompletionContext(final JavaContentAssistInvocationContext jdtContext) {
        this.javaContext = jdtContext;
        this.coreContext = cast(jdtContext.getCoreContext());
        if (!coreContext.isExtended()) {
            requestExtendedContext();
        }
    }

    private void requestExtendedContext() {
        CompletionProposalCollector collector = new CompletionProposalCollector(getCompilationUnit()) {
            @Override
            public void acceptContext(final CompletionContext context) {
                super.acceptContext(context);
                coreContext = (InternalCompletionContext) context;
            }
        };
        collector.setInvocationContext(javaContext);
        collector.setRequireExtendedContext(true);
        try {
            getCompilationUnit().codeComplete(getInvocationOffset(), collector);
        } catch (JavaModelException e) {
            log(e)
        }
    }
Comment 4 Dani Megert CLA 2012-01-11 06:20:03 EST
(In reply to comment #3)
> I wasn't aware that it's the responsibility of the engine to recreate the
> context if it's not extended. After digging into
> JavaCompletionProposalComputer#internalComputeCompletionProposals and its
> subclasses this became clearer to me.

> I added the some additional logic to my
> code that creates and  extended context if the currently given one isn't an
> extended one.
You don't need to check. Simply request it. Don't you already need/have a collector to get the proposals from JDT Core? If so, simply require it on this collector. If not, how do you get the proposals from JDT Core without collector?

> This still looks odd to me
The main purpose is to avoid additional computation if it is not needed.

> but it seems to work.
Of course ;-)
Comment 5 Marcel Bruch CLA 2012-01-11 06:29:58 EST
(In reply to comment #4)
> Don't you already need/have a
> collector to get the proposals from JDT Core? If so, simply require it on this
> collector. If not, how do you get the proposals from JDT Core without
> collector?

I changed that part for Juno. I now create my own proposals w/o using cu.codeComplete. I though this would be 'smarter' than requesting all proposals first and filtering them afterwards.
Comment 6 Dani Megert CLA 2012-01-11 06:32:57 EST
> I now create my own proposals w/o using
> cu.codeComplete.
Why do you then need the context from JDT Core, then?
Comment 7 Marcel Bruch CLA 2012-01-11 06:38:43 EST
(In reply to comment #6)
> Why do you then need the context from JDT Core, then?
To get access of all visible locals, methods, fields and the *Completion node.

The latter is used to figure out on which variable (name and type) or type (static) completion was triggered and what kind of proposals we want to make. For call completion a pure filtering could work (given that we get simple access to the variable's type completion was triggered on. Other engines like template completion need more information to filter which templates may apply in the given completion context.
Comment 8 Dani Megert CLA 2012-01-11 06:43:31 EST
(In reply to comment #7)
> (In reply to comment #6)
> > Why do you then need the context from JDT Core, then?
> To get access of all visible locals, methods, fields and the *Completion node.
> 
> The latter is used to figure out on which variable (name and type) or type
> (static) completion was triggered and what kind of proposals we want to make.
> For call completion a pure filtering could work (given that we get simple
> access to the variable's type completion was triggered on. Other engines like
> template completion need more information to filter which templates may apply
> in the given completion context.

I see. Makes sense.