diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ExcludeFromCompletionLookupActionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/ExcludeFromCompletionLookupActionProvider.java index 6d48746bb20c..c4d92cf8f258 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ExcludeFromCompletionLookupActionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ExcludeFromCompletionLookupActionProvider.java @@ -16,15 +16,17 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.daemon.impl.actions.AddImportAction; +import com.intellij.codeInsight.daemon.impl.quickfix.StaticImportMethodFix; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupActionProvider; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiType; -import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiMethod; import com.intellij.util.Consumer; +import org.jetbrains.annotations.Nullable; /** * @author peter @@ -32,20 +34,20 @@ import com.intellij.util.Consumer; public class ExcludeFromCompletionLookupActionProvider implements LookupActionProvider { public void fillActions(LookupElement element, Lookup lookup, Consumer consumer) { final Object o = element.getObject(); - PsiClass clazz = null; - if (o instanceof PsiType) { - clazz = PsiUtil.resolveClassInType((PsiType)o); - } else if (o instanceof PsiClass) { - clazz = (PsiClass)o; + if (o instanceof PsiClass) { + PsiClass clazz = (PsiClass)o; + addExcludes(consumer, clazz, clazz.getQualifiedName()); + } else if (o instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)o; + addExcludes(consumer, method, StaticImportMethodFix.getMethodQualifiedName(method)); } - if (clazz != null && clazz.isValid()) { - final String qname = clazz.getQualifiedName(); - if (qname != null) { - final Project project = clazz.getProject(); - for (final String s : AddImportAction.getAllExcludableStrings(qname)) { - consumer.consume(new ExcludeFromCompletionAction(project, s)); - } - } + } + + private static void addExcludes(Consumer consumer, PsiMember element, @Nullable String qname) { + if (qname == null) return; + final Project project = element.getProject(); + for (final String s : AddImportAction.getAllExcludableStrings(qname)) { + consumer.consume(new ExcludeFromCompletionAction(project, s)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java new file mode 100644 index 000000000000..ab0b2c164fb0 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java @@ -0,0 +1,123 @@ +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; + +/** + * @author peter + */ +public class JavaGlobalMemberNameCompletionContributor extends CompletionContributor { + + private static final LookupElementRenderer STATIC_METHOD_RENDERER = new LookupElementRenderer() { + @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()); + } + } + }; + private static final InsertHandler STATIC_METHOD_INSERT_HANDLER = new InsertHandler() { + @Override + public void handleInsert(InsertionContext context, LookupElement 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); + } + } + } + }; + + @Override + public void fillCompletionVariants(CompletionParameters parameters, final CompletionResultSet result) { + if (parameters.getCompletionType() != CompletionType.CLASS_NAME) { + return; + } + + final PrefixMatcher matcher = result.getPrefixMatcher(); + final String prefix = matcher.getPrefix(); + if (prefix.length() == 0 || !Character.isLowerCase(prefix.charAt(0))) { + return; + } + + final PsiElement position = parameters.getPosition(); + final PsiElement parent = position.getParent(); + if (!(parent instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)parent; + if (referenceExpression.isQualified()) { + return; + } + + processStaticMethods(result, position, STATIC_METHOD_INSERT_HANDLER); + } + + public static void processStaticMethods(final CompletionResultSet result, + final PsiElement position, + final InsertHandler insertHandler) { + PrefixMatcher matcher = result.getPrefixMatcher(); + final Project project = position.getProject(); + final GlobalSearchScope scope = GlobalSearchScope.allScope(project); + final PsiShortNamesCache namesCache = JavaPsiFacade.getInstance(project).getShortNamesCache(); + final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper(); + final String[] methodNames = ApplicationManager.getApplication().runReadAction(new Computable() { + public String[] compute() { + return namesCache.getAllMethodNames(); + } + }); + for (final String methodName : methodNames) { + if (matcher.prefixMatches(methodName)) { + final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction(new Computable() { + public PsiMethod[] compute() { + return namesCache.getMethodsByName(methodName, scope); + } + }); + for (final PsiMethod method : methods) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + if (method.hasModifierProperty(PsiModifier.STATIC) && resolveHelper.isAccessible(method, position, null)) { + 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)); + } + + } + } + } + }); + + } + } + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/simple/PsiMethodInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/simple/PsiMethodInsertHandler.java index 5826964f43a9..fb6cad1fd4fa 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/simple/PsiMethodInsertHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/simple/PsiMethodInsertHandler.java @@ -39,11 +39,11 @@ import org.jetbrains.annotations.Nullable; /** * @author peter */ -public class PsiMethodInsertHandler implements InsertHandler> { +public class PsiMethodInsertHandler implements InsertHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.simple.PsiMethodInsertHandler"); public static final PsiMethodInsertHandler INSTANCE = new PsiMethodInsertHandler(); - public static void insertParentheses(final InsertionContext context, final LookupItem item, boolean overloadsMatter, boolean hasParams) { + public static void insertParentheses(final InsertionContext context, final LookupElement item, boolean overloadsMatter, boolean hasParams) { final Editor editor = context.getEditor(); final TailType tailType = getTailType(item, context); final PsiFile file = context.getFile(); @@ -73,12 +73,12 @@ public class PsiMethodInsertHandler implements InsertHandler item) { + public void handleInsert(final InsertionContext context, final LookupElement item) { final Editor editor = context.getEditor(); final Document document = editor.getDocument(); final PsiFile file = context.getFile(); final int offset = editor.getCaretModel().getOffset(); - final PsiMethod method = item.getObject(); + final PsiMethod method = (PsiMethod)item.getObject(); final LookupElement[] allItems = context.getElements(); final boolean overloadsMatter = allItems.length == 1 && item.getUserData(LookupItem.FORCE_SHOW_SIGNATURE_ATTR) == null; @@ -115,9 +115,9 @@ public class PsiMethodInsertHandler implements InsertHandler item, final Document document, final int offset, PsiFile file) { - final PsiMethod method = item.getObject(); + private static void insertExplicitTypeParams(final LookupElement item, final Document document, final int offset, PsiFile file) { + final PsiMethod method = (PsiMethod)item.getObject(); if (!SmartCompletionDecorator.hasUnboundTypeParams(method)) { return; } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java index 5cc6eb917471..cc5d211ae100 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java @@ -42,6 +42,7 @@ import com.intellij.ui.popup.list.PopupListElementRenderer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -138,8 +139,9 @@ public class StaticImportMethodFix implements IntentionAction { return result; } - private static boolean isExcluded(PsiMethod method) { - String name = getQName(method); + public static boolean isExcluded(PsiMethod method) { + String name = getMethodQualifiedName(method); + if (name == null) return false; CodeInsightSettings cis = CodeInsightSettings.getInstance(); for (String excluded : cis.EXCLUDED_PACKAGES) { if (name.equals(excluded) || name.startsWith(excluded + ".")) { @@ -199,7 +201,7 @@ public class StaticImportMethodFix implements IntentionAction { return FINAL_CHOICE; } - String qname = getQName(selectedValue); + String qname = getMethodQualifiedName(selectedValue); if (qname == null) return FINAL_CHOICE; List excludableStrings = AddImportAction.getAllExcludableStrings(qname); return new BaseListPopupStep(null, excludableStrings) { @@ -264,12 +266,13 @@ public class StaticImportMethodFix implements IntentionAction { popup.showInBestPositionFor(editor); } - private static String getQName(PsiMethod selectedValue) { - PsiClass containingClass = selectedValue.getContainingClass(); + @Nullable + public static String getMethodQualifiedName(PsiMethod method) { + PsiClass containingClass = method.getContainingClass(); if (containingClass == null) return null; String className = containingClass.getQualifiedName(); if (className == null) return null; - return className + "." + selectedValue.getName(); + return className + "." + method.getName(); } public boolean startInWriteAction() { diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index 643d97843f30..c2ad4c94f0a0 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -18,6 +18,7 @@ + diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/GlobalMemberNameCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/GlobalMemberNameCompletionTest.groovy new file mode 100644 index 000000000000..9fae06d94d27 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/GlobalMemberNameCompletionTest.groovy @@ -0,0 +1,74 @@ +package com.intellij.codeInsight.completion; + +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import com.intellij.codeInsight.CodeInsightSettings +import com.intellij.util.ArrayUtil; + +/** + * @author peter + */ +public class GlobalMemberNameCompletionTest extends LightCodeInsightFixtureTestCase { + + public void testMethodName() throws Exception { + myFixture.addClass(""" +package foo; + +public class Foo { + public static int abcmethod() {} + static void methodThatsNotVisible() {} +} +""") + + doTest "class Bar {{ abcm }}", """import static foo.Foo.abcmethod; + +class Bar {{ abcmethod() }}""" + } + + @Override protected void tearDown() { + CodeInsightSettings.instance.EXCLUDED_PACKAGES = ArrayUtil.EMPTY_STRING_ARRAY + super.tearDown() + } + + public void testExcludeClassFromCompletion() throws Exception { + myFixture.addClass("""package foo; + public class Foo { + public static int abcmethod() {} + } + """) + myFixture.addClass("""package foo; + public class Excl { + public static int abcmethod2() {} + } + """) + + CodeInsightSettings.instance.EXCLUDED_PACKAGES = ["foo.Excl"] as String[] + + doTest "class Bar {{ abcm }}", """import static foo.Foo.abcmethod; + +class Bar {{ abcmethod() }}""" + } + + public void testExcludeMethodFromCompletion() throws Exception { + myFixture.addClass("""package foo; + public class Foo { + public static int abcmethod1() {} + public static int abcmethodExcluded() {} + } + """) + + CodeInsightSettings.instance.EXCLUDED_PACKAGES = ["foo.Foo.abcmethodExcluded"] as String[] + + doTest "class Bar {{ abcm }}", """import static foo.Foo.abcmethod1; + +class Bar {{ abcmethod1() }}""" + } + + private void doTest(String input, String output) { + myFixture.configureByText("a.java", input) + + assertOneElement myFixture.complete(CompletionType.CLASS_NAME) + myFixture.type('\n') + myFixture.checkResult output + } + +} diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/InsertionContext.java b/platform/lang-api/src/com/intellij/codeInsight/completion/InsertionContext.java index 1017e5ac6482..21a9b72bf873 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/InsertionContext.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/InsertionContext.java @@ -16,11 +16,12 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author peter @@ -65,6 +66,11 @@ public class InsertionContext { return myEditor; } + @NotNull + public Document getDocument() { + return getEditor().getDocument(); + } + public OffsetMap getOffsetMap() { return myOffsetMap; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java index bdb0b8adb28b..02e7ef4b71b2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/DefaultLookupItemRenderer.java @@ -44,7 +44,7 @@ public class DefaultLookupItemRenderer extends LookupElementRenderer } @Nullable - public static Icon getRawIcon(final LookupItem item, boolean real) { + public static Icon getRawIcon(final LookupElement item, boolean real) { final Icon icon = _getRawIcon(item, real); if (icon != null && icon.getIconHeight() > SAMPLE_ICON.getIconHeight()) { return new SizedIcon(icon, icon.getIconWidth(), SAMPLE_ICON.getIconHeight()); @@ -53,9 +53,11 @@ public class DefaultLookupItemRenderer extends LookupElementRenderer } @Nullable - private static Icon _getRawIcon(LookupItem item, boolean real) { - Icon icon = (Icon)item.getAttribute(LookupItem.ICON_ATTR); - if (icon != null) return icon; + private static Icon _getRawIcon(LookupElement item, boolean real) { + if (item instanceof LookupItem) { + Icon icon = (Icon)((LookupItem)item).getAttribute(LookupItem.ICON_ATTR); + if (icon != null) return icon; + } Object o = item.getObject(); diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java index 4b7d2b81ee06..19a1c8bd8345 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java @@ -305,6 +305,8 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture { void type(final char c); + void type(final String s); + void performEditorAction(String actionId); /** diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index 7b0b083f207e..768df215da7f 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -602,6 +602,13 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig }.execute(); } + @Override + public void type(String s) { + for (int i = 0; i < s.length(); i++) { + type(s.charAt(i)); + } + } + public void performEditorAction(final String actionId) { assertInitialized(); final DataContext dataContext = DataManager.getInstance().getDataContext(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java index 91bcad8e84c7..a22e14032890 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionContributor.java @@ -45,7 +45,6 @@ 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.GroovyPsiElement; 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; @@ -98,6 +97,22 @@ 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 STATIC_IMPORT_INSERT_HANDLER = new InsertHandler() { + @Override + public void handleInsert(InsertionContext context, LookupElement 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); + } + } + + } + }; public static boolean isReferenceInNewExpression(PsiElement reference) { if (!(reference instanceof GrCodeReferenceElement)) return false; @@ -303,7 +318,7 @@ public class GroovyCompletionContributor extends CompletionContributor { } }); - extend(CompletionType.BASIC, psiElement().withParent(GrReferenceElement.class), new CompletionProvider() { + extend(CompletionType.BASIC, psiElement().withParent(GrReferenceElement.class), new CompletionProvider(false) { @Override protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @@ -323,6 +338,20 @@ public class GroovyCompletionContributor extends CompletionContributor { } }); + extend(CompletionType.CLASS_NAME, psiElement().withParent(GrReferenceElement.class), new CompletionProvider(false) { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull final CompletionResultSet result) { + final PsiElement position = parameters.getPosition(); + if (((GrReferenceElement)position.getParent()).getQualifier() != null) return; + + final String s = result.getPrefixMatcher().getPrefix(); + if (StringUtil.isEmpty(s) || !Character.isLowerCase(s.charAt(0))) return; + + JavaGlobalMemberNameCompletionContributor.processStaticMethods(result, position, STATIC_IMPORT_INSERT_HANDLER); + } + }); } private static void addPropertiesForClass(CompletionResultSet result, diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.groovy index 71b585c28c10..918921dd044c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.groovy @@ -107,4 +107,17 @@ public class GroovyClassNameCompletionTest extends LightCodeInsightFixtureTestCa myFixture.checkResult "a.FooBarGooDoo" } + public void testStaticMethod() throws Exception { + myFixture.addFileToProject("b.groovy", """ +class Foo { + static def abcmethod(int a) {} +}""") + myFixture.configureByText("a.groovy", "abcme") + myFixture.complete(CompletionType.CLASS_NAME) + myFixture.checkResult """import static Foo.abcmethod + +abcmethod()""" + + } + } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index d63674b8be86..aee42c0de350 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -229,7 +229,8 @@ order="first, before javaMemberName, after xml"/> - + @@ -237,6 +238,8 @@ order="before javaLegacy"/> +