From 3a364bbe419a0622bb76a3ec16e4d771e1dba67f Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 26 Aug 2010 19:23:28 +0100 Subject: [PATCH] java global member name completion initial (IDEA-54855) --- ...GlobalMemberNameCompletionContributor.java | 113 ++++++++++++++++++ .../simple/PsiMethodInsertHandler.java | 16 +-- java/java-tests/java-tests.iml | 1 + .../GlobalMemberNameCompletionTest.groovy | 29 +++++ .../completion/InsertionContext.java | 8 +- .../fixtures/CodeInsightTestFixture.java | 2 + .../impl/CodeInsightTestFixtureImpl.java | 7 ++ resources/src/META-INF/IdeaPlugin.xml | 5 +- 8 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/completion/GlobalMemberNameCompletionTest.groovy 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..4930dcf47973 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGlobalMemberNameCompletionContributor.java @@ -0,0 +1,113 @@ +package com.intellij.codeInsight.completion; + +import com.intellij.codeInsight.completion.simple.PsiMethodInsertHandler; +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.openapi.util.Iconable; +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(method.getIcon(Iconable.ICON_FLAG_VISIBILITY)); //todo don't calculate if not a real presentation + 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; + } + + 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) { + result.addElement(LookupElementDecorator.withInsertHandler( + LookupElementDecorator.withRenderer(LookupElementBuilder.create(method), STATIC_METHOD_RENDERER), + STATIC_METHOD_INSERT_HANDLER)); + } + } + } + }); + + } + } + } + } +} 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-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..26f55ee19067 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/GlobalMemberNameCompletionTest.groovy @@ -0,0 +1,29 @@ +package com.intellij.codeInsight.completion; + +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; + +/** + * @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() {} +} +""") + + myFixture.configureByText("a.java", "class Bar {{ abcm }}") + + myFixture.complete(CompletionType.CLASS_NAME) + myFixture.type('\n') + myFixture.checkResult """import static foo.Foo.abcmethod; + +class Bar {{ abcmethod() }}""" + } + +} 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/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/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index bab0c5b4e992..2e70419025bc 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"/> +