blob: c00f9bbff3dbdb36ed6f1f0662d763207dcb2b2f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.java;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jdt.core.CompletionContext;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.ui.text.java.CompletionProposalCollector;
import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner;
import org.eclipse.jdt.internal.ui.text.Symbols;
/**
*
* @since 3.2
*/
public class JavaTypeCompletionProposalComputer extends JavaCompletionProposalComputer {
/*
* @see org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposalComputer#createCollector(org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext)
*/
@Override
protected CompletionProposalCollector createCollector(JavaContentAssistInvocationContext context) {
CompletionProposalCollector collector= super.createCollector(context);
collector.setIgnored(CompletionProposal.TYPE_REF, false);
return collector;
}
/*
* @see org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposalComputer#computeCompletionProposals(org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) {
List<ICompletionProposal> types= super.computeCompletionProposals(context, monitor);
if (!(context instanceof JavaContentAssistInvocationContext))
return types;
JavaContentAssistInvocationContext javaContext= (JavaContentAssistInvocationContext) context;
CompletionContext coreContext= javaContext.getCoreContext();
if (coreContext != null && coreContext.getTokenLocation() != CompletionContext.TL_CONSTRUCTOR_START)
return types;
try {
if (types.size() > 0 && context.computeIdentifierPrefix().length() == 0) {
IType expectedType= javaContext.getExpectedType();
if (expectedType != null) {
// empty prefix completion - insert LRU types if known, but prune if they already occur in the core list
// compute minmimum relevance and already proposed list
int relevance= Integer.MAX_VALUE;
Set<String> proposed= new HashSet<>();
for (ICompletionProposal iCompletionProposal : types) {
AbstractJavaCompletionProposal p= (AbstractJavaCompletionProposal) iCompletionProposal;
IJavaElement element= p.getJavaElement();
if (element instanceof IType)
proposed.add(((IType) element).getFullyQualifiedName());
relevance= Math.min(relevance, p.getRelevance());
}
// insert history types
List<String> history= JavaPlugin.getDefault().getContentAssistHistory().getHistory(expectedType.getFullyQualifiedName()).getTypes();
relevance-= history.size() + 1;
for (String type : history) {
if (proposed.contains(type))
continue;
IJavaCompletionProposal proposal= createTypeProposal(relevance, type, javaContext);
if (proposal != null)
types.add(proposal);
relevance++;
}
}
}
} catch (BadLocationException | JavaModelException x) {
// log & ignore
JavaPlugin.log(x);
}
return types;
}
private IJavaCompletionProposal createTypeProposal(int relevance, String fullyQualifiedType, JavaContentAssistInvocationContext context) throws JavaModelException {
IType type= context.getCompilationUnit().getJavaProject().findType(fullyQualifiedType);
if (type == null)
return null;
CompletionProposal proposal= CompletionProposal.create(CompletionProposal.TYPE_REF, context.getInvocationOffset());
proposal.setCompletion(fullyQualifiedType.toCharArray());
proposal.setDeclarationSignature(type.getPackageFragment().getElementName().toCharArray());
proposal.setFlags(type.getFlags());
proposal.setReplaceRange(context.getInvocationOffset(), context.getInvocationOffset());
proposal.setSignature(Signature.createTypeSignature(fullyQualifiedType, true).toCharArray());
if (shouldProposeGenerics(context.getProject())) {
LazyGenericTypeProposal p= new LazyGenericTypeProposal(proposal, context);
p.setRelevance(relevance);
return p;
} else {
LazyJavaTypeCompletionProposal p= new LazyJavaTypeCompletionProposal(proposal, context);
p.setRelevance(relevance);
return p;
}
}
/**
* Returns <code>true</code> if generic proposals should be allowed,
* <code>false</code> if not. Note that even though code (in a library)
* may be referenced that uses generics, it is still possible that the
* current source does not allow generics.
*
* @param project the Java project
* @return <code>true</code> if the generic proposals should be allowed,
* <code>false</code> if not
*/
protected final boolean shouldProposeGenerics(IJavaProject project) {
String sourceVersion;
if (project != null)
sourceVersion= project.getOption(JavaCore.COMPILER_SOURCE, true);
else
sourceVersion= JavaCore.getOption(JavaCore.COMPILER_SOURCE);
return sourceVersion != null && JavaCore.compareJavaVersions(JavaCore.VERSION_1_5, sourceVersion) <= 0;
}
@Override
protected int guessContextInformationPosition(ContentAssistInvocationContext context) {
final int contextPosition= context.getInvocationOffset();
IDocument document= context.getDocument();
JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
int bound= Math.max(-1, contextPosition - 200);
// try the innermost scope of angle brackets that looks like a generic type argument list
try {
int pos= contextPosition - 1;
do {
int angle= scanner.findOpeningPeer(pos, bound, '<', '>');
if (angle == JavaHeuristicScanner.NOT_FOUND)
break;
int token= scanner.previousToken(angle - 1, bound);
// next token must be a method name that is a generic type
if (token == Symbols.TokenIDENT) {
int off= scanner.getPosition() + 1;
int end= angle;
String ident= document.get(off, end - off).trim();
if (JavaHeuristicScanner.isGenericStarter(ident))
return angle + 1;
}
pos= angle - 1;
} while (true);
} catch (BadLocationException x) {
}
return super.guessContextInformationPosition(context);
}
}