Bug 368074 - [content assist] CoreContext is extended for first content assist list only
Summary: [content assist] CoreContext is extended for first content assist list only
Status: RESOLVED INVALID
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Text (show other bugs)
Version: 3.8   Edit
Hardware: PC Mac OS X - Carbon (unsup.)
: P3 normal (vote)
Target Milestone: 3.8 M5   Edit
Assignee: Dani Megert CLA
QA Contact:
URL:
Whiteboard:
Keywords: investigate
Depends on:
Blocks:
 
Reported: 2012-01-07 01:02 EST by Marcel Bruch CLA
Modified: 2012-01-11 06:43 EST (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
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.