global method completion to insert qualified calls by default, import static available via Left key option

This commit is contained in:
peter
2010-08-29 09:40:12 +01:00
parent 60ee721d2e
commit 549415de14
17 changed files with 388 additions and 205 deletions
@@ -84,14 +84,14 @@ public class AllClassesGetter {
LOG.error(endOffset + " became invalid: " + context.getOffsetMap() + "; inserting " + qname);
}
final RangeMarker toDelete = DefaultInsertHandler.insertSpace(endOffset, document);
final RangeMarker toDelete = JavaCompletionUtil.insertSpace(endOffset, document);
psiDocumentManager.commitAllDocuments();
PsiReference psiReference = file.findReferenceAt(endOffset - 1);
boolean insertFqn=checkReference!=ClassNameInsertHandlerResult.REFERENCE_CORRECTED;
if (checkReference == ClassNameInsertHandlerResult.CHECK_FOR_CORRECT_REFERENCE && psiReference != null) {
final PsiManager psiManager = file.getManager();
if (psiManager.areElementsEquivalent(psiClass, DefaultInsertHandler.resolveReference(psiReference))) {
if (psiManager.areElementsEquivalent(psiClass, JavaCompletionUtil.resolveReference(psiReference))) {
insertFqn = false;
}
else if (psiClass.isValid()) {
@@ -101,7 +101,7 @@ public class AllClassesGetter {
final PsiElement psiElement = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newUnderlying);
if (psiElement != null) {
for (final PsiReference reference : psiElement.getReferences()) {
if (psiManager.areElementsEquivalent(psiClass, DefaultInsertHandler.resolveReference(reference))) {
if (psiManager.areElementsEquivalent(psiClass, JavaCompletionUtil.resolveReference(reference))) {
insertFqn = false;
endOffset = reference.getRangeInElement().getEndOffset() + reference.getElement().getTextRange().getStartOffset();
break;
@@ -16,21 +16,17 @@
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.TailTypes;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
@@ -40,7 +36,6 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class DefaultInsertHandler extends TemplateInsertHandler implements Cloneable {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.DefaultInsertHandler");
@@ -399,94 +394,9 @@ public class DefaultInsertHandler extends TemplateInsertHandler implements Clone
}
private static int addImportForClass(PsiFile file, int startOffset, int endOffset, PsiClass aClass) throws IncorrectOperationException {
if (!aClass.isValid()) {
return startOffset;
}
SmartPsiElementPointer<PsiClass> pointer = SmartPointerManager.getInstance(file.getProject()).createSmartPsiElementPointer(aClass);
LOG.assertTrue(CommandProcessor.getInstance().getCurrentCommand() != null);
LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().getCurrentWriteAction(null) != null);
final PsiManager manager = file.getManager();
final Document document = FileDocumentManager.getInstance().getDocument(file.getViewProvider().getVirtualFile());
final PsiReference reference = file.findReferenceAt(startOffset);
if (reference != null) {
final PsiElement resolved = reference.resolve();
if (resolved instanceof PsiClass) {
if (((PsiClass)resolved).getQualifiedName() == null || manager.areElementsEquivalent(aClass, resolved)) {
return startOffset;
}
}
}
String name = aClass.getName();
document.replaceString(startOffset, endOffset, name);
//PsiDocumentManager.getInstance(manager.getProject()).commitAllDocuments();
final RangeMarker toDelete = insertSpace(endOffset, document);
PsiDocumentManager.getInstance(manager.getProject()).commitAllDocuments();
int newStartOffset = startOffset;
PsiElement element = file.findElementAt(startOffset);
if (element instanceof PsiIdentifier) {
PsiElement parent = element.getParent();
if (parent instanceof PsiJavaCodeReferenceElement && !((PsiJavaCodeReferenceElement)parent).isQualified() && !(parent.getParent() instanceof PsiPackageStatement)) {
PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)parent;
if (!aClass.getManager().areElementsEquivalent(aClass, resolveReference(ref))) {
final PsiElement pointerElement = pointer.getElement();
if (pointerElement instanceof PsiClass) {
PsiElement newElement;
if (!(ref instanceof PsiImportStaticReferenceElement)) {
newElement = ref.bindToElement(pointerElement);
}
else {
newElement = ((PsiImportStaticReferenceElement)ref).bindToTargetClass((PsiClass)pointerElement);
}
RangeMarker marker = document.createRangeMarker(newElement.getTextRange());
CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newElement);
newStartOffset = marker.getStartOffset();
}
}
}
}
if (toDelete.isValid()) {
document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
}
return newStartOffset;
return JavaCompletionUtil.insertClassReference(aClass, file, startOffset, endOffset);
}
public static RangeMarker insertSpace(final int endOffset, final Document document) {
final CharSequence chars = document.getCharsSequence();
final int length = chars.length();
final RangeMarker toDelete;
if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))){
document.insertString(endOffset, " ");
toDelete = document.createRangeMarker(endOffset, endOffset + 1);
} else if (endOffset >= length) {
toDelete = document.createRangeMarker(length, length);
}
else {
toDelete = document.createRangeMarker(endOffset, endOffset);
}
toDelete.setGreedyToLeft(true);
toDelete.setGreedyToRight(true);
return toDelete;
}
@Nullable
static PsiElement resolveReference(final PsiReference psiReference) {
if (psiReference instanceof PsiPolyVariantReference) {
final ResolveResult[] results = ((PsiPolyVariantReference)psiReference).multiResolve(true);
if (results.length == 1) return results[0].getElement();
}
return psiReference.resolve();
}
public static class InsertHandlerState{
int tailOffset;
@@ -62,8 +62,9 @@ public class ExcludeFromCompletionLookupActionProvider implements LookupActionPr
}
@Override
public void performLookupAction() {
public Result performLookupAction() {
AddImportAction.excludeFromImport(myProject, myToExclude);
return Result.HIDE_LOOKUP;
}
}
}
@@ -0,0 +1,31 @@
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.lookup.*;
import com.intellij.util.Consumer;
import com.intellij.util.Icons;
import com.intellij.util.ui.EmptyIcon;
import javax.swing.*;
/**
* @author peter
*/
public class ImportStaticLookupActionProvider implements LookupActionProvider {
@Override
public void fillActions(final LookupElement element, Lookup lookup, Consumer<LookupElementAction> consumer) {
if (!(element instanceof JavaGlobalMemberLookupElement)) {
return;
}
final JavaGlobalMemberLookupElement item = (JavaGlobalMemberLookupElement)element;
final Icon checkIcon = Icons.CHECK_ICON;
final Icon icon = item.isShouldImport() ? checkIcon : new EmptyIcon(checkIcon.getIconWidth(), checkIcon.getIconHeight());
consumer.consume(new LookupElementAction(icon, "Import statically") {
@Override
public Result performLookupAction() {
item.setShouldImport(!item.isShouldImport());
return Result.REFRESH_ITEM;
}
});
}
}
@@ -16,6 +16,7 @@
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypeInfoImpl;
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
@@ -25,8 +26,12 @@ import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.codeInsight.guess.GuessManager;
import com.intellij.codeInsight.lookup.*;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
@@ -1062,18 +1067,113 @@ public class JavaCompletionUtil {
@Nullable
static ElementFilter recursionFilter(PsiElement element) {
if (com.intellij.patterns.PsiJavaPatterns.psiElement().afterLeaf(PsiKeyword.RETURN).inside(PsiReturnStatement.class).accepts(element)) {
if (PsiJavaPatterns.psiElement().afterLeaf(PsiKeyword.RETURN).inside(PsiReturnStatement.class).accepts(element)) {
return new ExcludeDeclaredFilter(ElementClassFilter.METHOD);
}
if (com.intellij.patterns.PsiJavaPatterns.psiElement().inside(
if (PsiJavaPatterns.psiElement().inside(
PsiJavaPatterns.or(
PsiJavaPatterns.psiElement(PsiAssignmentExpression.class),
PsiJavaPatterns.psiElement(PsiVariable.class))).
andNot(com.intellij.patterns.PsiJavaPatterns.psiElement().afterLeaf(".")).accepts(element)) {
andNot(PsiJavaPatterns.psiElement().afterLeaf(".")).accepts(element)) {
return new AndFilter(new ExcludeSillyAssignment(),
new ExcludeDeclaredFilter(new ClassFilter(PsiVariable.class)));
}
return null;
}
public static int insertClassReference(@NotNull PsiClass psiClass, @NotNull PsiFile file, int offset) {
return insertClassReference(psiClass, file, offset, offset);
}
public static int insertClassReference(PsiClass psiClass, PsiFile file, int startOffset, int endOffset) {
PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
if (!psiClass.isValid()) {
return startOffset;
}
SmartPsiElementPointer<PsiClass> pointer = SmartPointerManager.getInstance(file.getProject()).createSmartPsiElementPointer(psiClass);
LOG.assertTrue(CommandProcessor.getInstance().getCurrentCommand() != null);
LOG.assertTrue(
ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().getCurrentWriteAction(null) != null);
final PsiManager manager = file.getManager();
final Document document = FileDocumentManager.getInstance().getDocument(file.getViewProvider().getVirtualFile());
final PsiReference reference = file.findReferenceAt(startOffset);
if (reference != null) {
final PsiElement resolved = reference.resolve();
if (resolved instanceof PsiClass) {
if (((PsiClass)resolved).getQualifiedName() == null || manager.areElementsEquivalent(psiClass, resolved)) {
return startOffset;
}
}
}
String name = psiClass.getName();
document.replaceString(startOffset, endOffset, name);
final RangeMarker toDelete = insertSpace(startOffset + name.length(), document);
PsiDocumentManager.getInstance(manager.getProject()).commitAllDocuments();
int newStartOffset = startOffset;
PsiElement element = file.findElementAt(startOffset);
if (element instanceof PsiIdentifier) {
PsiElement parent = element.getParent();
if (parent instanceof PsiJavaCodeReferenceElement && !((PsiJavaCodeReferenceElement)parent).isQualified() && !(parent.getParent() instanceof PsiPackageStatement)) {
PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)parent;
if (!psiClass.getManager().areElementsEquivalent(psiClass, resolveReference(ref))) {
final PsiElement pointerElement = pointer.getElement();
if (pointerElement instanceof PsiClass) {
PsiElement newElement;
if (!(ref instanceof PsiImportStaticReferenceElement)) {
newElement = ref.bindToElement(pointerElement);
}
else {
newElement = ((PsiImportStaticReferenceElement)ref).bindToTargetClass((PsiClass)pointerElement);
}
RangeMarker marker = document.createRangeMarker(newElement.getTextRange());
CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newElement);
newStartOffset = marker.getStartOffset();
}
}
}
}
if (toDelete.isValid()) {
document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
}
return newStartOffset;
}
@Nullable
static PsiElement resolveReference(final PsiReference psiReference) {
if (psiReference instanceof PsiPolyVariantReference) {
final ResolveResult[] results = ((PsiPolyVariantReference)psiReference).multiResolve(true);
if (results.length == 1) return results[0].getElement();
}
return psiReference.resolve();
}
public static RangeMarker insertSpace(final int endOffset, final Document document) {
final CharSequence chars = document.getCharsSequence();
final int length = chars.length();
final RangeMarker toDelete;
if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))){
document.insertString(endOffset, " ");
toDelete = document.createRangeMarker(endOffset, endOffset + 1);
} else if (endOffset >= length) {
toDelete = document.createRangeMarker(length, length);
}
else {
toDelete = document.createRangeMarker(endOffset, endOffset);
}
toDelete.setGreedyToLeft(true);
toDelete.setGreedyToRight(true);
return toDelete;
}
}
@@ -0,0 +1,91 @@
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.lookup.DefaultLookupItemRenderer;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiFormatUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author peter
*/
public class JavaGlobalMemberLookupElement extends LookupElement {
private final PsiMethod myMethod;
private final PsiClass myContainingClass;
private final InsertHandler<JavaGlobalMemberLookupElement> myQualifiedInsertion;
private final InsertHandler<JavaGlobalMemberLookupElement> myImportInsertion;
private boolean myShouldImport = false;
public JavaGlobalMemberLookupElement(PsiMethod method,
PsiClass containingClass,
InsertHandler<JavaGlobalMemberLookupElement> qualifiedInsertion,
InsertHandler<JavaGlobalMemberLookupElement> importInsertion) {
myMethod = method;
myContainingClass = containingClass;
myQualifiedInsertion = qualifiedInsertion;
myImportInsertion = importInsertion;
}
@NotNull
@Override
public PsiMethod getObject() {
return myMethod;
}
@NotNull
public PsiClass getContainingClass() {
return myContainingClass;
}
@NotNull
@Override
public String getLookupString() {
return myMethod.getName();
}
@Override
public void renderElement(LookupElementPresentation presentation) {
final String className = myContainingClass.getName();
presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this, presentation.isReal()));
final String methodName = myMethod.getName();
if (Boolean.FALSE.equals(myShouldImport) && StringUtil.isNotEmpty(className)) {
presentation.setItemText(className + "." + methodName);
} else {
presentation.setItemText(methodName);
}
final String params = PsiFormatUtil.formatMethod(myMethod, PsiSubstitutor.EMPTY,
PsiFormatUtil.SHOW_PARAMETERS,
PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE);
if (Boolean.TRUE.equals(myShouldImport) && StringUtil.isNotEmpty(className)) {
presentation.setTailText(params + " in " + className);
} else {
presentation.setTailText(params);
}
final PsiType type = myMethod.getReturnType();
if (type != null) {
presentation.setTypeText(type.getPresentableText());
}
}
public boolean isShouldImport() {
return myShouldImport;
}
public void setShouldImport(boolean shouldImport) {
myShouldImport = shouldImport;
}
@Override
public void handleInsert(InsertionContext context) {
(myShouldImport ? myImportInsertion : myQualifiedInsertion).handleInsert(context, this);
}
}
@@ -2,14 +2,12 @@ package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.completion.simple.PsiMethodInsertHandler;
import com.intellij.codeInsight.daemon.impl.quickfix.StaticImportMethodFix;
import com.intellij.codeInsight.lookup.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
/**
@@ -17,40 +15,25 @@ import com.intellij.psi.util.PsiTreeUtil;
*/
public class JavaGlobalMemberNameCompletionContributor extends CompletionContributor {
private static final LookupElementRenderer<LookupElement> STATIC_METHOD_RENDERER = new LookupElementRenderer<LookupElement>() {
private static final InsertHandler<JavaGlobalMemberLookupElement> STATIC_METHOD_INSERT_HANDLER = new InsertHandler<JavaGlobalMemberLookupElement>() {
@Override
public void renderElement(LookupElement element, LookupElementPresentation presentation) {
PsiMethod method = (PsiMethod)element.getObject();
final PsiClass containingClass = method.getContainingClass();
presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(element, presentation.isReal()));
presentation.setItemText(method.getName());
final String params = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY,
PsiFormatUtil.SHOW_PARAMETERS,
PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE);
if (containingClass != null) {
presentation.setTailText(params + " in " + containingClass.getName());
} else {
presentation.setTailText(params);
}
final PsiType type = method.getReturnType();
if (type != null) {
presentation.setTypeText(type.getPresentableText());
public void handleInsert(InsertionContext context, JavaGlobalMemberLookupElement item) {
PsiMethodInsertHandler.INSTANCE.handleInsert(context, item);
final PsiClass containingClass = item.getContainingClass();
PsiDocumentManager.getInstance(containingClass.getProject()).commitDocument(context.getDocument());
final PsiReferenceExpression ref = PsiTreeUtil
.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiReferenceExpression.class, false);
if (ref != null) {
ref.bindToElementViaStaticImport(containingClass);
}
}
};
private static final InsertHandler<LookupElement> STATIC_METHOD_INSERT_HANDLER = new InsertHandler<LookupElement>() {
private static final InsertHandler<JavaGlobalMemberLookupElement> QUALIFIED_METHOD_INSERT_HANDLER = new InsertHandler<JavaGlobalMemberLookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
public void handleInsert(InsertionContext context, JavaGlobalMemberLookupElement item) {
PsiMethodInsertHandler.INSTANCE.handleInsert(context, item);
final PsiClass containingClass = ((PsiMethod)item.getObject()).getContainingClass();
if (containingClass != null) {
PsiDocumentManager.getInstance(containingClass.getProject()).commitDocument(context.getDocument());
final PsiReferenceExpression ref = PsiTreeUtil
.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiReferenceExpression.class, false);
if (ref != null) {
ref.bindToElementViaStaticImport(containingClass);
}
}
context.getDocument().insertString(context.getStartOffset(), ".");
JavaCompletionUtil.insertClassReference(item.getContainingClass(), context.getFile(), context.getStartOffset());
}
};
@@ -76,12 +59,13 @@ public class JavaGlobalMemberNameCompletionContributor extends CompletionContrib
return;
}
processStaticMethods(result, position, STATIC_METHOD_INSERT_HANDLER);
processStaticMethods(result, position, QUALIFIED_METHOD_INSERT_HANDLER, STATIC_METHOD_INSERT_HANDLER);
}
public static void processStaticMethods(final CompletionResultSet result,
final PsiElement position,
final InsertHandler<LookupElement> insertHandler) {
final InsertHandler<JavaGlobalMemberLookupElement> qualifiedInsert,
final InsertHandler<JavaGlobalMemberLookupElement> importInsert) {
PrefixMatcher matcher = result.getPrefixMatcher();
final Project project = position.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
@@ -106,9 +90,7 @@ public class JavaGlobalMemberNameCompletionContributor extends CompletionContrib
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null) {
if (!JavaCompletionUtil.isInExcludedPackage(containingClass) && !StaticImportMethodFix.isExcluded(method)) {
result.addElement(LookupElementDecorator.withInsertHandler(
LookupElementDecorator.withRenderer(LookupElementBuilder.create(method), STATIC_METHOD_RENDERER),
insertHandler));
result.addElement(new JavaGlobalMemberLookupElement(method, containingClass, qualifiedInsert, importInsert));
}
}
@@ -24,6 +24,20 @@ public class Foo {
class Bar {{ abcmethod()<caret> }}"""
}
public void testQualifiedMethodName() throws Exception {
myFixture.addClass("""
package foo;
public class Foo {
public static int abcmethod() {}
}
""")
doTest "class Bar {{ abcm<caret> }}", false, """import foo.Foo;
class Bar {{ Foo.abcmethod()<caret> }}"""
}
@Override protected void tearDown() {
CodeInsightSettings.instance.EXCLUDED_PACKAGES = ArrayUtil.EMPTY_STRING_ARRAY
super.tearDown()
@@ -64,9 +78,14 @@ class Bar {{ abcmethod1()<caret> }}"""
}
private void doTest(String input, String output) {
doTest input, true, output
}
private void doTest(String input, boolean importStatic, String output) {
myFixture.configureByText("a.java", input)
assertOneElement myFixture.complete(CompletionType.CLASS_NAME)
def item = assertOneElement(myFixture.complete(CompletionType.CLASS_NAME))
item.'as'(JavaGlobalMemberLookupElement).shouldImport = importStatic
myFixture.type('\n')
myFixture.checkResult output
}
@@ -172,7 +172,7 @@ public class LookupElementBuilder extends LookupElement {
myAllLookupStrings, myCaseSensitive);
}
public LookupElement setAutoCompletionPolicy(AutoCompletionPolicy policy) {
public LookupElement withAutoCompletionPolicy(AutoCompletionPolicy policy) {
return policy.applyPolicy(this);
}
@@ -41,5 +41,10 @@ public abstract class LookupElementAction {
return myText;
}
public abstract void performLookupAction();
public abstract Result performLookupAction();
public static enum Result {
HIDE_LOOKUP,
REFRESH_ITEM
}
}
@@ -31,6 +31,7 @@ import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.popup.PopupIcons;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
@@ -279,14 +280,18 @@ public class LookupCellRenderer implements ListCellRenderer {
}
private Icon getIcon(Icon icon){
return augmentIcon(icon, myEmptyIcon);
}
public static Icon augmentIcon(@Nullable Icon icon, @NotNull Icon standard) {
if (icon == null) {
return myEmptyIcon;
return standard;
}
if (icon.getIconHeight() < myEmptyIcon.getIconHeight() || icon.getIconWidth() < myEmptyIcon.getIconWidth()) {
if (icon.getIconHeight() < standard.getIconHeight() || icon.getIconWidth() < standard.getIconWidth()) {
final LayeredIcon layeredIcon = new LayeredIcon(2);
layeredIcon.setIcon(icon, 0, 0, (myEmptyIcon.getIconHeight() - icon.getIconHeight()) / 2);
layeredIcon.setIcon(myEmptyIcon, 1);
layeredIcon.setIcon(icon, 0, 0, (standard.getIconHeight() - icon.getIconHeight()) / 2);
layeredIcon.setIcon(standard, 1);
return layeredIcon;
}
@@ -188,14 +188,8 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
}
public void addItem(LookupElement item) {
final CollectConsumer<LookupElementAction> consumer = new CollectConsumer<LookupElementAction>();
for (LookupActionProvider provider : LookupActionProvider.EP_NAME.getExtensions()) {
provider.fillActions(item, this, consumer);
}
myItemActions.put(item, consumer.getResult());
int maxWidth = myCellRenderer.updateMaximumWidth(item);
myLookupWidth = Math.max(maxWidth, myLookupWidth);
updateItemActions(item);
updateLookupWidth(item);
synchronized (myItems) {
myItems.add(item);
@@ -203,6 +197,19 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
}
}
public void updateLookupWidth(LookupElement item) {
int maxWidth = myCellRenderer.updateMaximumWidth(item);
myLookupWidth = Math.max(maxWidth, myLookupWidth);
}
public void updateItemActions(LookupElement item) {
final CollectConsumer<LookupElementAction> consumer = new CollectConsumer<LookupElementAction>();
for (LookupActionProvider provider : LookupActionProvider.EP_NAME.getExtensions()) {
provider.fillActions(item, this, consumer);
}
myItemActions.put(item, consumer.getResult());
}
public Collection<LookupElementAction> getActionsFor(LookupElement element) {
final Collection<LookupElementAction> collection = myItemActions.get(element);
return collection == null ? Collections.<LookupElementAction>emptyList() : collection;
@@ -580,14 +587,11 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
final int i = myList.locationToIndex(point);
if (i >= 0) {
final LookupElement selected = (LookupElement)myList.getModel().getElementAt(i);
if (selected != null) {
final Collection<LookupElementAction> actions = getActionsFor(selected);
if (!actions.isEmpty() &&
e.getClickCount() == 1 &&
point.x >= myList.getCellBounds(i, i).width - PopupIcons.EMPTY_ICON.getIconWidth()) {
ShowLookupActionsHandler.showItemActions(LookupImpl.this, actions);
return;
}
if (selected != null &&
e.getClickCount() == 1 &&
point.x >= myList.getCellBounds(i, i).width - PopupIcons.EMPTY_ICON.getIconWidth() &&
ShowLookupActionsHandler.showItemActions(LookupImpl.this, selected)) {
return;
}
}
@@ -839,8 +843,9 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
myInitialSelection = myEditor.getDocument().createRangeMarker(new TextRange(selStart, selEnd));
}
@Nullable
public PsiFile getPsiFile() {
return PsiDocumentManager.getInstance(myEditor.getProject()).getPsiFile(myEditor.getDocument());
return PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument());
}
public boolean isCompletion() {
@@ -25,6 +25,7 @@ import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.popup.ClosableByLeftArrow;
import com.intellij.util.ui.EmptyIcon;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -43,43 +44,64 @@ public class ShowLookupActionsHandler extends LookupActionHandler {
return;
}
final Collection<LookupElementAction> actions = lookup.getActionsFor(element);
if (actions.isEmpty()) {
if (!showItemActions(lookup, element)) {
lookup.getEditor().getCaretModel().moveCaretRelatively(1, 0, false, false, true);
return;
}
showItemActions(lookup, actions);
}
public static void showItemActions(LookupImpl lookup, Collection<LookupElementAction> actions) {
final BaseListPopupStep<LookupElementAction> step = new LookupActionsStep(actions, lookup);
public static boolean showItemActions(LookupImpl lookup, LookupElement element) {
final Collection<LookupElementAction> actions = lookup.getActionsFor(element);
if (actions.isEmpty()) {
return false;
}
final BaseListPopupStep<LookupElementAction> step = new LookupActionsStep(actions, lookup, element);
final Rectangle bounds = lookup.getCurrentItemBounds();
final ListPopup popup = JBPopupFactory.getInstance().createListPopup(step);
final JComponent component = lookup.getComponent();
popup.show(new RelativePoint(component, new Point(bounds.x + bounds.width,
bounds.y)));
return true;
}
private static class LookupActionsStep extends BaseListPopupStep<LookupElementAction> implements ClosableByLeftArrow {
private final LookupImpl myLookup;
private final LookupElement myLookupElement;
private final Icon myEmptyIcon;
public LookupActionsStep(Collection<LookupElementAction> actions, LookupImpl lookup) {
public LookupActionsStep(Collection<LookupElementAction> actions, LookupImpl lookup, LookupElement lookupElement) {
super(null, new ArrayList<LookupElementAction>(actions));
myLookup = lookup;
myLookupElement = lookupElement;
int w = 0, h = 0;
for (LookupElementAction action : actions) {
final Icon icon = action.getIcon();
if (icon != null) {
w = Math.max(w, icon.getIconWidth());
h = Math.max(h, icon.getIconHeight());
}
}
myEmptyIcon = new EmptyIcon(w, h);
}
@Override
public PopupStep onChosen(LookupElementAction selectedValue, boolean finalChoice) {
selectedValue.performLookupAction();
myLookup.hide();
final LookupElementAction.Result result = selectedValue.performLookupAction();
if (result == LookupElementAction.Result.HIDE_LOOKUP) {
myLookup.hide();
} else {
myLookup.updateItemActions(myLookupElement);
myLookup.updateLookupWidth(myLookupElement);
myLookup.refreshUi();
}
return FINAL_CHOICE;
}
@Override
public Icon getIconFor(LookupElementAction aValue) {
return aValue.getIcon();
return LookupCellRenderer.augmentIcon(aValue.getIcon(), myEmptyIcon);
}
@NotNull
@@ -44,7 +44,6 @@ import org.jetbrains.plugins.groovy.lang.completion.handlers.NamedArgumentInsert
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
@@ -97,22 +96,34 @@ public class GroovyCompletionContributor extends CompletionContributor {
psiElement().withParent(psiElement(GrReferenceExpression.class).withParent(psiElement(GrArgumentList.class).withParent(GrCall.class)));
private static final String[] THIS_SUPER = {"this", "super"};
private static final InsertHandler<LookupElement> STATIC_IMPORT_INSERT_HANDLER = new InsertHandler<LookupElement>() {
private static final InsertHandler<JavaGlobalMemberLookupElement> STATIC_IMPORT_INSERT_HANDLER = new InsertHandler<JavaGlobalMemberLookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
public void handleInsert(InsertionContext context, JavaGlobalMemberLookupElement item) {
new GroovyInsertHandler().handleInsert(context, item);
final PsiClass containingClass = ((PsiMethod)item.getObject()).getContainingClass();
if (containingClass != null) {
PsiDocumentManager.getInstance(containingClass.getProject()).commitDocument(context.getDocument());
final GrReferenceExpression ref = PsiTreeUtil
.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), GrReferenceExpression.class, false);
if (ref != null) {
ref.bindToElementViaStaticImport(containingClass);
}
final PsiClass containingClass = item.getContainingClass();
PsiDocumentManager.getInstance(containingClass.getProject()).commitDocument(context.getDocument());
final GrReferenceExpression ref = PsiTreeUtil
.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), GrReferenceExpression.class, false);
if (ref != null) {
ref.bindToElementViaStaticImport(containingClass);
}
}
};
private static final InsertHandler<JavaGlobalMemberLookupElement> QUALIFIED_METHOD_INSERT_HANDLER = new InsertHandler<JavaGlobalMemberLookupElement>() {
@Override
public void handleInsert(InsertionContext context, JavaGlobalMemberLookupElement item) {
new GroovyInsertHandler().handleInsert(context, item);
final PsiClass containingClass = item.getContainingClass();
context.getDocument().insertString(context.getStartOffset(), containingClass.getName() + ".");
PsiDocumentManager.getInstance(containingClass.getProject()).commitDocument(context.getDocument());
final GrReferenceExpression ref = PsiTreeUtil
.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), GrReferenceExpression.class, false);
if (ref != null) {
ref.bindToElement(containingClass);
}
}
};
public static boolean isReferenceInNewExpression(PsiElement reference) {
if (!(reference instanceof GrCodeReferenceElement)) return false;
@@ -349,7 +360,7 @@ public class GroovyCompletionContributor extends CompletionContributor {
final String s = result.getPrefixMatcher().getPrefix();
if (StringUtil.isEmpty(s) || !Character.isLowerCase(s.charAt(0))) return;
JavaGlobalMemberNameCompletionContributor.processStaticMethods(result, position, STATIC_IMPORT_INSERT_HANDLER);
JavaGlobalMemberNameCompletionContributor.processStaticMethods(result, position, QUALIFIED_METHOD_INSERT_HANDLER, STATIC_IMPORT_INSERT_HANDLER);
}
});
}
@@ -491,22 +502,6 @@ public class GroovyCompletionContributor extends CompletionContributor {
while (!iterator.atEnd() && GroovyTokenTypes.WHITE_SPACES_OR_COMMENTS.contains(iterator.getTokenType())) {
iterator.advance();
}
// if (iterator.atEnd()) return true;
// return iterator.getTokenType() == GroovyTokenTypes.mASSIGN;
return true;
}
private static boolean shouldRunClassNameCompletion(CompletionResultSet result, PsiFile file, PsiElement position) {
if (!(file instanceof GroovyFileBase)) {
return false;
}
if (!(position.getParent() instanceof GrCodeReferenceElement)) return false;
if (((GrCodeReferenceElement)position.getParent()).getQualifier() != null) return false;
final String s = result.getPrefixMatcher().getPrefix();
if (StringUtil.isEmpty(s) || !Character.isUpperCase(s.charAt(0))) return false;
return true;
}
@@ -19,11 +19,9 @@ package org.jetbrains.plugins.groovy.lang.completion;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.AllClassesGetter;
import com.intellij.codeInsight.completion.DefaultInsertHandler;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
@@ -397,7 +395,7 @@ public class GroovyCompletionUtil {
String name = aClass.getName();
document.replaceString(startOffset, endOffset, name);
final RangeMarker toDelete = DefaultInsertHandler.insertSpace(endOffset, document);
final RangeMarker toDelete = JavaCompletionUtil.insertSpace(endOffset, document);
PsiDocumentManager.getInstance(manager.getProject()).commitAllDocuments();
@@ -24,7 +24,8 @@ import com.intellij.codeInsight.lookup.impl.TestLookupManager;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.util.TestUtils;
import org.jetbrains.plugins.groovy.util.TestUtils
import com.intellij.codeInsight.completion.JavaGlobalMemberLookupElement;
/**
* @author Maxim.Medvedev
@@ -107,16 +108,32 @@ public class GroovyClassNameCompletionTest extends LightCodeInsightFixtureTestCa
myFixture.checkResult "a.FooBarGooDoo<caret>"
}
public void testStaticMethod() throws Exception {
public void testImportedStaticMethod() throws Exception {
myFixture.addFileToProject("b.groovy", """
class Foo {
static def abcmethod1(int a) {}
static def abcmethod2(int a) {}
}""")
myFixture.configureByText("a.groovy", "abcme<caret>")
def item = myFixture.complete(CompletionType.CLASS_NAME)[0]
((JavaGlobalMemberLookupElement) item).shouldImport = true
myFixture.type('\n')
myFixture.checkResult """import static Foo.abcmethod1
abcmethod1(<caret>)"""
}
public void testQualifiedStaticMethod() throws Exception {
myFixture.addFileToProject("foo/b.groovy", """package foo
class Foo {
static def abcmethod(int a) {}
}""")
myFixture.configureByText("a.groovy", "abcme<caret>")
myFixture.complete(CompletionType.CLASS_NAME)
myFixture.checkResult """import static Foo.abcmethod
myFixture.checkResult """import foo.Foo
abcmethod(<caret>)"""
Foo.abcmethod(<caret>)"""
}
+3 -1
View File
@@ -223,7 +223,9 @@
<fileTypeFactory implementation="com.intellij.openapi.fileTypes.impl.DefaultFileTypeFactory"/>
<lookup.actionProvider implementation="com.intellij.codeInsight.completion.ExcludeFromCompletionLookupActionProvider"/>
<lookup.actionProvider implementation="com.intellij.codeInsight.completion.ImportStaticLookupActionProvider" id="importStatic"/>
<lookup.actionProvider implementation="com.intellij.codeInsight.completion.ExcludeFromCompletionLookupActionProvider"
id="excludeFromCompletion" order="last"/>
<completion.contributor language="any" implementationClass="com.intellij.codeInsight.completion.BasicToClassNameDelegator" id="basic2ClassName"
order="first, before javaMemberName, after xml"/>