/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.completion.handlers;
import com.intellij.codeInsight.completion.AllClassesGetter;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.lookup.PsiTypeLookupItem;
import com.intellij.codeInsight.lookup.VariableLookupItem;
import com.intellij.openapi.util.Key;
import com.intellij.patterns.PsiJavaPatterns;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.PsiVariable;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.TrueFilter;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IModule;
import gw.plugin.ij.completion.GosuAdditionalSyntaxLookupElement;
import gw.plugin.ij.completion.GosuClassNameInsertHandler;
import gw.plugin.ij.completion.GosuFeatureCallLookupElement;
import gw.plugin.ij.completion.GosuFeatureInfoLookupItem;
import gw.plugin.ij.completion.InitializerCompletionProposalLookupElement;
import gw.plugin.ij.completion.RawSymbolLookupItem;
import gw.plugin.ij.completion.handlers.filter.CompletionFilter;
import gw.plugin.ij.completion.handlers.filter.CompletionFilterExtensionPointBean;
import gw.plugin.ij.completion.proposals.GosuCompletionProposal;
import gw.plugin.ij.completion.proposals.ICompletionHasAdditionalSyntax;
import gw.plugin.ij.completion.proposals.InitializerCompletionProposal;
import gw.plugin.ij.completion.proposals.PathCompletionProposal;
import gw.plugin.ij.completion.proposals.PrimitiveCompletionProposal;
import gw.plugin.ij.completion.proposals.SymbolCompletionProposal;
import gw.plugin.ij.lang.psi.impl.AbstractGosuClassFileImpl;
import gw.plugin.ij.util.GosuModuleUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class AbstractCompletionHandler implements IPathCompletionHandler {
public static final Key<Integer> COMPLETION_WEIGHT = new Key<>("_gosuCompletionWeight");
protected final CompletionParameters _params;
private final CompletionResultSet _resultSet;
private int _completionCount;
private boolean _bFilterByInvocationCount;
public AbstractCompletionHandler(CompletionParameters params, CompletionResultSet resultSet) {
_params = params;
_resultSet = resultSet;
_bFilterByInvocationCount = true;
}
protected void stopFilterByInvocationCount() {
_bFilterByInvocationCount = false;
}
public CompletionParameters getContext() {
return _params;
}
public CompletionResultSet getResult() {
return _resultSet;
}
@Nullable
public String getStatusMessage() {
return null;
}
public int getTotalCompletions() {
return _completionCount;
}
@Nullable
private static LookupElement createLookup(@NotNull CompletionParameters parameters, @NotNull GosuCompletionProposal gosuCompletionProposal) {
return createLookup(parameters, gosuCompletionProposal, true);
}
@Nullable
private static LookupElement createLookup(@NotNull CompletionParameters parameters, @NotNull GosuCompletionProposal proposal, boolean inJavaContext) {
if (proposal instanceof InitializerCompletionProposal) {
return new InitializerCompletionProposalLookupElement((InitializerCompletionProposal) proposal);
}
if (proposal instanceof ICompletionHasAdditionalSyntax) {
return new GosuAdditionalSyntaxLookupElement((ICompletionHasAdditionalSyntax) proposal);
}
if (proposal instanceof PathCompletionProposal) {
return new GosuFeatureInfoLookupItem(((PathCompletionProposal) proposal).getBeanTree(), parameters.getPosition());
}
if (proposal instanceof PrimitiveCompletionProposal) {
return PsiTypeLookupItem.createLookupItem(((PrimitiveCompletionProposal) proposal).getPrimitiveType(), null);
}
LookupElement completion = null;
final PsiElement element = proposal.resolve(parameters.getPosition());
if (element != null) {
if (element instanceof PsiClass) {
completion = AllClassesGetter.createLookupItem((PsiClass) element, inJavaContext ? GosuClassNameInsertHandler.GOSU_CLASS_INSERT_HANDLER : AllClassesGetter.TRY_SHORTENING);
} else if (element instanceof PsiVariable) {
completion = new VariableLookupItem((PsiVariable) element);
} else if (element instanceof PsiMethod) {
completion = null; // let the feature info fill this completion in
} else if (element instanceof PsiNamedElement) {
completion = LookupElementBuilder.create((PsiNamedElement) element);
} else {
completion = LookupElementBuilder.create(element.getText());
}
}
if (completion == null) {
final IFeatureInfo featureInfo = proposal.getFeatureInfo();
if (featureInfo != null) {
completion = new GosuFeatureCallLookupElement(featureInfo);
}
}
if (proposal instanceof SymbolCompletionProposal) {
final IModule module = GosuModuleUtil.findModuleForPsiElement(parameters.getPosition());
completion = new RawSymbolLookupItem(((SymbolCompletionProposal) proposal).getSymbol(), module);
}
if (completion == null) {
completion = LookupElementBuilder.create(proposal.getGenericName());
}
completion.putUserData(COMPLETION_WEIGHT, proposal.getWeight());
return completion;
}
public void addCompletion(@NotNull GosuCompletionProposal gosuCompletionProposal, boolean javaCtx) {
addCompletion(_resultSet, gosuCompletionProposal, javaCtx);
}
public void addCompletion(@NotNull GosuCompletionProposal gosuCompletionProposal) {
addCompletion(_resultSet, gosuCompletionProposal);
}
public void addCompletion(@NotNull CompletionResultSet customResult, @NotNull GosuCompletionProposal gosuCompletionProposal) {
addCompletion(customResult, gosuCompletionProposal, false);
}
// Stupid telescoping methods
public void addCompletion(@NotNull CompletionResultSet customResult, @NotNull GosuCompletionProposal gosuCompletionProposal, boolean javaCtx) {
if (!canBeShown(gosuCompletionProposal)) {
return;
}
_completionCount++;
final LookupElement completion = createLookup(_params, gosuCompletionProposal, javaCtx);
if (completion != null) {
customResult.addElement(completion);
}
}
public boolean canBeShown(GosuCompletionProposal proposal) {
List<CompletionFilter> filters = CompletionFilterExtensionPointBean.getFilters();
for (CompletionFilter filter : filters) {
if (!filter.allows(proposal)) {
return false;
}
}
return true;
}
public IType getCtxType() {
return ((AbstractGosuClassFileImpl)getContext().getPosition().getContainingFile()).getParseData().getContextType();
}
public boolean atAnnotation(PsiElement position) {
return PsiJavaPatterns.psiElement().afterLeaf("@").accepts(position);
}
public static boolean isAnnotation(IType type) {
return JavaTypes.ANNOTATION().isAssignableFrom(type) ||
JavaTypes.IANNOTATION().isAssignableFrom(type);
}
protected ElementFilter getLocalFilter( PsiElement insertedElement ) {
PsiFile file = insertedElement.getContainingFile();
if( file instanceof AbstractGosuClassFileImpl ) {
final IGosuClass gsClass = ((AbstractGosuClassFileImpl)file).getParseData().getClassFileStatement().getGosuClass();
if( gsClass != null ) {
final ITypeUsesMap typeUsesMap = gsClass.getTypeUsesMap();
if( typeUsesMap != null ) {
return new ElementFilter() {
@Override
public boolean isAcceptable( Object element, PsiElement context ) {
String fqn;
if( element instanceof PsiClass ) {
PsiClass element1 = (PsiClass)element;
fqn = element1.getQualifiedName();
}
else if( element instanceof CharSequence ) {
fqn = element.toString();
}
else {
return false;
}
int iLastDot = fqn.lastIndexOf( '.' );
String simpleName = iLastDot > 0 ? fqn.substring( iLastDot + 1 ) : fqn;
if( simpleName.startsWith( getResult().getPrefixMatcher().getPrefix() ) ) {
if( _bFilterByInvocationCount && _params.getInvocationCount() < 2 ) {
// Limit to Local scope if this is the first Ctrl-Space
return isFirstCtrlSpaceWorthy( fqn );
}
// Second Ctrl-Space widens scope of completion proposals to All types
return true;
}
return false;
}
private boolean isFirstCtrlSpaceWorthy( String fqn ) {
if( fqn.startsWith( gsClass.getName() ) ) {
// inner class of the file
return true;
}
if( typeUsesMap.containsType( fqn ) ) {
// reachable via uses-statements
return true;
}
if( isCommonType( fqn ) ) {
// is java.lang.* and the like
return true;
}
return false;
}
private boolean isCommonType( String fqn ) {
return fqn.startsWith( "java.lang." ) ||
fqn.startsWith( "java.util." );
}
@Override
public boolean isClassAcceptable( Class hintClass ) {
return true;
}
};
}
}
}
return TrueFilter.INSTANCE;
}
}