/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.completion;
import com.google.common.base.Joiner;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.DefaultLookupItemRenderer;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiUtil;
import com.intellij.ui.LayeredIcon;
import com.intellij.ui.RowIcon;
import com.intellij.util.PlatformIcons;
import com.intellij.util.VisibilityIcons;
import gw.lang.reflect.*;
import gw.lang.reflect.gs.IGosuEnhancement;
import gw.lang.reflect.gs.IGosuVarPropertyInfo;
import gw.lang.reflect.java.IJavaFieldPropertyInfo;
import gw.lang.reflect.module.IModule;
import gw.plugin.ij.completion.model.BeanInfoNode;
import gw.plugin.ij.completion.model.BeanTree;
import gw.plugin.ij.completion.model.MethodNode;
import gw.plugin.ij.completion.model.PackagePropertyInfo;
import gw.plugin.ij.completion.model.PropertyInfoNode;
import gw.plugin.ij.completion.model.TypeInPackageType;
import gw.plugin.ij.completion.model.TypePropertyInfo;
import gw.plugin.ij.icons.GosuIcons;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* We define our own FeatureInfo based lookup item because:
* 1. We already have *all* the type information provided by our parser, including parameterized types (no Substitutor shenanigans)
* 2. Our type information is different from Java's in IJ e.g., we have properties, enhancements, and closures
* 3. It's a lot easier and faster because we don't have to *resolve* everything in the completion list because of #1
*/
public class GosuFeatureInfoLookupItem extends LookupItem<BeanTree> {
private Icon getIcon(@NotNull IAttributedFeatureInfo info) {
Icon baseIcon = null;
if (info instanceof IPropertyInfo) {
if (info instanceof PackagePropertyInfo) {
baseIcon = PlatformIcons.PACKAGE_ICON;
} else if (info instanceof TypePropertyInfo) {
TypeInPackageType featureType = (TypeInPackageType) ((TypePropertyInfo) info).getFeatureType();
baseIcon = featureType.getIcon();
} else if (info instanceof IJavaFieldPropertyInfo || info instanceof IGosuVarPropertyInfo) {
baseIcon = GosuIcons.FIELD;
} else {
baseIcon = GosuIcons.PROPERTY;
}
} else if (info instanceof IMethodInfo) {
baseIcon = GosuIcons.METHOD;
}
final LayeredIcon temp = new LayeredIcon(2);
temp.setIcon(baseIcon, 0);
if (info.getOwnersType() instanceof IGosuEnhancement) {
temp.setIcon(GosuIcons.ENH, 1);
}
final RowIcon result = new RowIcon(2);
result.setIcon(temp, 0);
VisibilityIcons.setVisibilityIcon(getVisibility(info), result);
return result;
}
private int getVisibility(@NotNull IAttributedFeatureInfo fi) {
if (fi.isPublic()) {
return PsiUtil.ACCESS_LEVEL_PUBLIC;
}
if (fi.isProtected()) {
return PsiUtil.ACCESS_LEVEL_PROTECTED;
}
if (fi.isInternal()) {
return PsiUtil.ACCESS_LEVEL_PACKAGE_LOCAL;
}
return PsiUtil.ACCESS_LEVEL_PRIVATE;
}
@NotNull
private IAttributedFeatureInfo getFeatureInfo() {
return (IAttributedFeatureInfo) getObject().getBeanNode().getFeatureInfo();
}
private static String getSubstitution(IFeatureInfo info) {
if (info instanceof IPropertyInfo) {
return info.getName();
}
return info.getDisplayName();
}
public GosuFeatureInfoLookupItem(@NotNull BeanTree beanTree, PsiElement context) {
//noinspection deprecation
super(beanTree, getSubstitution(beanTree.getBeanNode().getFeatureInfo()));
setIcon(getIcon(getFeatureInfo()));
setTailType(TailType.NONE);
}
@Override
public void handleInsert(@NotNull InsertionContext context) {
final Document document = context.getDocument();
final IAttributedFeatureInfo info = getFeatureInfo();
if (info instanceof IMethodInfo) {
int parentStart = context.getStartOffset() + info.getDisplayName().length();
final StringBuilder sb = new StringBuilder("(");
boolean hasArguments = false;
if (info instanceof IHasParameterInfos) {
final IParameterInfo[] parameters = ((IHasParameterInfos) info).getParameters();
hasArguments = parameters.length > 0;
if (parameters.length == 1 && parameters[0].getFeatureType() instanceof IBlockType) {
final IBlockType blockType = (IBlockType) parameters[0].getFeatureType();
sb.append(" \\");
final String[] parameterNames = blockType.getParameterNames();
if (parameterNames.length > 0) {
sb.append(" ");
Joiner.on(", ").appendTo(sb, parameterNames);
sb.append(" ");
}
sb.append("-> ");
}
}
sb.append(")");
document.insertString(parentStart, sb);
final int caretPosition = parentStart + sb.length() - (hasArguments ? 1 : 0);
context.getEditor().getCaretModel().moveToOffset(caretPosition);
context.setAddCompletionChar(false);
}
}
@Override
public void renderElement(@NotNull LookupElementPresentation presentation) {
final BeanTree beanTree = getObject();
final BeanInfoNode node = beanTree.getBeanNode();
ITypeLoader typeLoader = beanTree.getWhosAsking().getTypeLoader();
final IModule module = typeLoader != null ? typeLoader.getModule() : node.getModule();
TypeSystem.pushModule(module);
try {
final IFeatureInfo info = node.getFeatureInfo();
presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this, presentation.isReal()));
presentation.setItemText(getSubstitution(info));
presentation.setStrikeout(JavaElementLookupRenderer.isToStrikeout(this));
boolean declaredOnRootType = beanTree.getParent().getBeanNode().getType() == info.getOwnersType();
presentation.setItemTextBold(declaredOnRootType || getAttribute(HIGHLIGHTED_ATTR) != null);
if (info instanceof IPropertyInfo) {
final PropertyInfoNode property = (PropertyInfoNode) node;
presentation.setTypeText(PropertyInfoNode.getTypeName(property.getType()));
} else if (info instanceof IMethodInfo) {
final MethodNode method = (MethodNode) node;
presentation.setTypeText(method.getReturnTypeName());
presentation.setTailText(method.getParameterDisplay());
}
} finally {
TypeSystem.popModule(module);
}
}
}