From e6b79debb2e3282aecf0e741197a41d6fabb5974 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Mon, 27 Feb 2012 15:22:13 +0400 Subject: [PATCH] KT-1151 Code completion for not imported extension functions - completion for extension functions when function doesn't have parameters --- .../jetbrains/jet/lang/psi/JetPsiUtil.java | 31 +++- .../ControlStructureTypingVisitor.java | 2 +- .../expressions/ExpressionTypingUtils.java | 83 ++++++++-- .../caches/JavaToJetCompletionResolver.java | 72 --------- .../caches/JetFromJavaDescriptorHelper.java | 148 ++++++++++++++++++ .../jet/plugin/caches/JetShortNamesCache.java | 73 +++++---- .../completion/JetCompletionContributor.java | 80 +++++++++- .../completion/JetPackagesContributor.java | 3 + .../handlers/JetFunctionInsertHandler.java | 12 +- .../stubindex/StubIndexServiceImpl.java | 12 +- 10 files changed, 388 insertions(+), 128 deletions(-) delete mode 100644 idea/src/org/jetbrains/jet/plugin/caches/JavaToJetCompletionResolver.java create mode 100644 idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java index aec915b4eff..bd0e8075768 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -17,13 +17,13 @@ package org.jetbrains.jet.lang.psi; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; import com.intellij.psi.impl.CheckUtil; import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.util.QualifiedNamesUtil; import java.util.Collection; import java.util.HashSet; @@ -37,6 +37,9 @@ public class JetPsiUtil { public static final String NO_NAME_PROVIDED = ""; + private JetPsiUtil() { + } + @Nullable public static JetExpression deparenthesize(@NotNull JetExpression expression) { if (expression instanceof JetBinaryExpressionWithTypeRHS) { @@ -157,6 +160,32 @@ public class JetPsiUtil { return jetClass.getName(); } + public static String getFQName(JetNamedFunction jetNamedFunction) { + + String functionName = jetNamedFunction.getName(); + if (functionName == null) { + return functionName; + } + + @SuppressWarnings("unchecked") + PsiElement qualifiedElement = PsiTreeUtil.getParentOfType( + jetNamedFunction, + JetFile.class, JetClassOrObject.class, JetNamedFunction.class); + + String firstPart = ""; + if (qualifiedElement instanceof JetFile) { + firstPart = getFQName((JetFile) qualifiedElement); + } + else if (qualifiedElement instanceof JetClassOrObject) { + firstPart = getFQName((JetClassOrObject) qualifiedElement); + } + else if (qualifiedElement instanceof JetNamedFunction) { + firstPart = getFQName((JetNamedFunction) qualifiedElement); + } + + return QualifiedNamesUtil.combine(firstPart, functionName); + } + @Nullable @JetElement.IfNotParsed public static String getImportPath(JetImportDirective importDirective) { final JetExpression importedReference = importDirective.getImportedReference(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java index 1cd5fe7bc1f..0d51a11f4d0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java @@ -347,7 +347,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { return null; } - private static OverloadResolutionResults resolveFakeCall(ExpressionReceiver receiver, ExpressionTypingContext context, String name) { + public static OverloadResolutionResults resolveFakeCall(ExpressionReceiver receiver, ExpressionTypingContext context, String name) { JetReferenceExpression fake = JetPsiFactory.createSimpleName(context.getProject(), "fake"); BindingTrace fakeTrace = new BindingTraceContext(); Call call = CallMaker.makeCall(fake, receiver, null, fake, Collections.emptyList()); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java index 9e2ef4b0e3e..6e2bb13efe6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java @@ -25,24 +25,21 @@ import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; -import org.jetbrains.jet.lang.psi.JetExpression; -import org.jetbrains.jet.lang.psi.JetPattern; -import org.jetbrains.jet.lang.psi.JetPsiFactory; -import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; -import org.jetbrains.jet.lang.resolve.BindingContextUtils; -import org.jetbrains.jet.lang.resolve.BindingTrace; -import org.jetbrains.jet.lang.resolve.BindingTraceContext; -import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; -import org.jetbrains.jet.lang.resolve.scopes.JetScope; -import org.jetbrains.jet.lang.resolve.scopes.WritableScope; -import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; +import org.jetbrains.jet.lang.resolve.scopes.*; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeUtils; +import org.jetbrains.jet.util.QualifiedNamesUtil; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -53,7 +50,10 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.*; * @author abreslav */ public class ExpressionTypingUtils { - + + private ExpressionTypingUtils() { + } + @Nullable protected static ExpressionReceiver getExpressionReceiver(@NotNull JetExpression expression, @Nullable JetType type) { if (type == null) return null; @@ -165,4 +165,63 @@ public class ExpressionTypingUtils { ); return ControlStructureTypingVisitor.checkIterableConvention(expressionReceiver, context) != null; } + + /** + * Check that function with the given qualified name can be resolved in given scope for given receiver + * + * @param functionFQN + * @param project + * @param scope + * @return + */ + public static ArrayList canCallBeResolved( + @NotNull String functionFQN, + @NotNull Project project, + @NotNull JetExpression receiverExpression, + @NotNull JetType receiverType, + @NotNull JetScope scope) { + + WritableScopeWithImports writableScopeWithImports = new WritableScopeImpl( + scope, scope.getContainingDeclaration(), RedeclarationHandler.DO_NOTHING); + + JetImportDirective importDirective = JetPsiFactory.createImportDirective(project, functionFQN); + + ExpressionReceiver expressionReceiver = new ExpressionReceiver(receiverExpression, receiverType); + + ImportsResolver.ImportResolver importResolver = new ImportsResolver.ImportResolver(new BindingTraceContext(), false); + importResolver.processImportReference( + importDirective, scope, + new Importer.StandardImporter(writableScopeWithImports, false)); + + ExpressionTypingContext context = ExpressionTypingContext.newContext( + project, + JetSemanticServices.createSemanticServices(project), + new HashMap(), + new HashMap>(), + new LabelResolver(), + new BindingTraceContext(), + writableScopeWithImports, + DataFlowInfo.EMPTY, + TypeUtils.NO_EXPECTED_TYPE, + TypeUtils.NO_EXPECTED_TYPE, + false + ); + + writableScopeWithImports.changeLockLevel(WritableScope.LockLevel.READING); + + OverloadResolutionResults resolutionResult = + ControlStructureTypingVisitor.resolveFakeCall(expressionReceiver, context, QualifiedNamesUtil.fqnToShortName(functionFQN)); + + if (!resolutionResult.isSuccess()) { + return new ArrayList(); + } + + ArrayList resolvedDescriptors = new ArrayList(); + + for (ResolvedCall resolvedCall : resolutionResult.getResultingCalls()) { + resolvedDescriptors.add(resolvedCall.getCandidateDescriptor()); + } + + return resolvedDescriptors; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JavaToJetCompletionResolver.java b/idea/src/org/jetbrains/jet/plugin/caches/JavaToJetCompletionResolver.java deleted file mode 100644 index de587c0abbb..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/caches/JavaToJetCompletionResolver.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2010-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.jet.plugin.caches; - -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiModifier; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.PsiShortNamesCache; -import com.intellij.xml.impl.schema.TypeDescriptor; -import org.jetbrains.jet.lang.resolve.java.JvmAbi; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * Get jet declarations from java that could be used in completion. Unlike the real jet resolver this helper is allowed - * to return partially unresolved descriptors in exchange of execution speed. - * - * @author Nikolay Krasko - */ -class JetFromJavaDescriptorHelper { - - private JetFromJavaDescriptorHelper() { - } - - /** - * Get java equivalents for jet top level classes. - */ - static PsiClass[] getClassesForJetNamespaces(Project project, GlobalSearchScope scope) { - return PsiShortNamesCache.getInstance(project).getClassesByName(JvmAbi.PACKAGE_CLASS, scope); - } - - /** - * Get names that could have jet descriptor equivalents. It could be inaccurate and return more results than necessary. - */ - static Collection getPossiblePackageDeclarationsNames(Project project, GlobalSearchScope scope) { - final ArrayList result = new ArrayList(); - - for (PsiClass jetNamespaceClass : getClassesForJetNamespaces(project, scope)) { - for (PsiMethod psiMethod : jetNamespaceClass.getMethods()) { - if (psiMethod.getModifierList().hasModifierProperty(PsiModifier.STATIC)) { - result.add(psiMethod.getName()); - } - } - } - - return result; - } - - static Collection getTopExtensionFunctionNames(TypeDescriptor typeDescriptor, Project project, - GlobalSearchScope scope) { - - return getPossiblePackageDeclarationsNames(project, scope); - } - -} diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java b/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java new file mode 100644 index 00000000000..4b67c06c968 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java @@ -0,0 +1,148 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.plugin.caches; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; +import com.intellij.psi.impl.java.stubs.index.JavaMethodNameIndex; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.PsiShortNamesCache; +import com.intellij.psi.util.PsiTreeUtil; +import jet.runtime.typeinfo.JetValueParameter; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +/** + * Get jet declarations from java that could be used in completion. Unlike the real jet resolver this helper is allowed + * to return partially unresolved descriptors in exchange of execution speed. + * + * @author Nikolay Krasko + */ +class JetFromJavaDescriptorHelper { + + private JetFromJavaDescriptorHelper() { + } + + /** + * Get java equivalents for jet top level classes. + */ + static PsiClass[] getClassesForJetNamespaces(Project project, GlobalSearchScope scope) { + return PsiShortNamesCache.getInstance(project).getClassesByName(JvmAbi.PACKAGE_CLASS, scope); + } + + /** + * Get names that could have jet descriptor equivalents. It could be inaccurate and return more results than necessary. + */ + static Collection getPossiblePackageDeclarationsNames(Project project, GlobalSearchScope scope) { + final ArrayList result = new ArrayList(); + + for (PsiClass jetNamespaceClass : getClassesForJetNamespaces(project, scope)) { + for (PsiMethod psiMethod : jetNamespaceClass.getMethods()) { + if (psiMethod.getModifierList().hasModifierProperty(PsiModifier.STATIC)) { + result.add(psiMethod.getName()); + } + } + } + + return result; + } + + static Collection getTopExtensionFunctionNames(Project project, GlobalSearchScope scope) { + + // Extension function should have an parameter of type JetValueParameter with explicit receiver parameter. + + HashSet extensionNames = new HashSet(); + + Collection valueParametersAnnotations = JavaAnnotationIndex.getInstance().get( + JetValueParameter.class.getSimpleName(), project, scope); + + for (PsiAnnotation parameterAnnotation : valueParametersAnnotations) { + String qualifiedName = parameterAnnotation.getQualifiedName(); + + if (qualifiedName == null || !qualifiedName.equals(JetValueParameter.class.getCanonicalName())) { + continue; + } + + if (!getAnnotationAttribute(parameterAnnotation, "receiver", false)) { + continue; + } + + PsiMethod psiMethod = PsiTreeUtil.getParentOfType(parameterAnnotation, PsiMethod.class); + if (psiMethod != null) { + extensionNames.add(psiMethod.getName()); + } + } + + return extensionNames; + } + + static Collection getTopExtensionFunctionByName(String name, Project project, GlobalSearchScope scope) { + + HashSet selectedMethods = new HashSet(); + + Collection psiMethods = JavaMethodNameIndex.getInstance().get(name, project, scope); + for (PsiMethod psiMethod : psiMethods) { + if (psiMethod == null) { + continue; + } + + // Check this is top level function + PsiClass containingClass = psiMethod.getContainingClass(); + if (containingClass == null || !JvmAbi.PACKAGE_CLASS.equals(containingClass.getName())) { + continue; + } + + // Should be parameter with JetValueParameter.receiver == true + for (PsiParameter parameter : psiMethod.getParameterList().getParameters()) { + PsiModifierList modifierList = parameter.getModifierList(); + if (modifierList != null) { + for (PsiAnnotation psiAnnotation : modifierList.getAnnotations()) { + if (!JetValueParameter.class.getCanonicalName().equals(psiAnnotation.getQualifiedName())) { + continue; + } + + if (getAnnotationAttribute(psiAnnotation, "receiver", false)) { + selectedMethods.add(psiMethod); + } + } + } + } + } + + return selectedMethods; + } + + private static boolean getAnnotationAttribute(PsiAnnotation annotation, String attributeName, boolean defaultValue) { + // Check that parameter is receiver + PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue(attributeName); + if (!(attributeValue instanceof PsiLiteralExpression)) { + return defaultValue; + } + + // Every extension function will have parameter marked with attribute where receiver == true + Object value = ((PsiLiteralExpression) attributeValue).getValue(); + if (value == null) { + return defaultValue; + } + + return (Boolean) value; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java index 1a1ce427549..27485e94733 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetShortNamesCache.java @@ -20,20 +20,20 @@ import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiField; import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.asJava.JavaElementFinder; import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; -import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.psi.JetNamedFunction; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; import org.jetbrains.jet.plugin.stubindex.JetExtensionFunctionNameIndex; import org.jetbrains.jet.plugin.stubindex.JetFullClassNameIndex; @@ -69,7 +69,7 @@ public class JetShortNamesCache extends PsiShortNamesCache { @Override public String[] getAllClassNames() { final Collection classNames = JetShortClassNameIndex.getInstance().getAllKeys(project); - return classNames.toArray(new String[classNames.size()]); + return ArrayUtil.toStringArray(classNames); } /** @@ -104,10 +104,10 @@ public class JetShortNamesCache extends PsiShortNamesCache { // TODO: Implement it. Is it called somewhere? } - public Collection getALlJetClassFQNames() { - final BindingContext context = getResolutionContext(GlobalSearchScope.allScope(project)); - return context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR); - } +// public Collection getALlJetClassFQNames() { +// final BindingContext context = getResolutionContext(GlobalSearchScope.allScope(project)); +// return context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR); +// } @NotNull public Collection getFQNamesByName(@NotNull final String name, @NotNull GlobalSearchScope scope) { @@ -138,7 +138,7 @@ public class JetShortNamesCache extends PsiShortNamesCache { public Collection getTopLevelFunctionDescriptorsByName(final @NotNull String name, final @NotNull GlobalSearchScope scope) { - // TODO: Add jet function in jar-dependencies (those functions are missing in BindingContext and stubs) + final Collection jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope); @@ -176,32 +176,48 @@ public class JetShortNamesCache extends PsiShortNamesCache { final Set extensionFunctionNames = new HashSet(); extensionFunctionNames.addAll(JetExtensionFunctionNameIndex.getInstance().getAllKeys(project)); - extensionFunctionNames.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionNames(null, project, scope)); + extensionFunctionNames.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionNames(project, scope)); return extensionFunctionNames; } - public Collection getAllJetExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) { - // TODO: Add jet extension functions in jar-dependencies (those functions are missing in BindingContext and stubs) + public Collection getJetExtensionFunctionsByName(final @NotNull String name, @NotNull GlobalSearchScope scope) { + HashSet functions = new HashSet(); + functions.addAll(JetExtensionFunctionNameIndex.getInstance().get(name, project, scope)); + functions.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionByName(name, project, scope)); - final Collection jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope); + return functions; + } - final BindingContext context = getResolutionContext(scope); +// public Collection getAllJetExtensionFunctionsByName(@NotNull String name, @NotNull GlobalSearchScope scope) { +// // TODO: Add jet extension functions in jar-dependencies (those functions are missing in BindingContext and stubs) +// +// final Collection jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope); +// +// final BindingContext context = getResolutionContext(scope); +// +// final HashSet result = new HashSet(); +// +// for (JetNamedFunction jetNamedFunction : jetNamedFunctions) { +// final NamedFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction); +// if (functionDescriptor != null) { +// if (functionDescriptor.getContainingDeclaration() instanceof NamespaceDescriptor) { +// if (functionDescriptor.getExpectedThisObject() != ReceiverDescriptor.NO_RECEIVER) { +// result.add(functionDescriptor); +// } +// } +// } +// } +// +// return result; +// } - final HashSet result = new HashSet(); + public Collection getJetFunctionsByName(@NonNls @NotNull String name, @NotNull GlobalSearchScope scope) { + return JetShortFunctionNameIndex.getInstance().get(name, project, scope); + } - for (JetNamedFunction jetNamedFunction : jetNamedFunctions) { - final SimpleFunctionDescriptor functionDescriptor = context.get(BindingContext.FUNCTION, jetNamedFunction); - if (functionDescriptor != null) { - if (functionDescriptor.getContainingDeclaration() instanceof NamespaceDescriptor) { - if (functionDescriptor.getExpectedThisObject() != ReceiverDescriptor.NO_RECEIVER) { - result.add(functionDescriptor); - } - } - } - } - - return result; + public Collection getAllJetFunctionsNames() { + return JetShortFunctionNameIndex.getInstance().getAllKeys(project); } @NotNull @@ -219,11 +235,12 @@ public class JetShortNamesCache extends PsiShortNamesCache { @NotNull @Override public String[] getAllMethodNames() { - return new String[0]; + return ArrayUtil.EMPTY_STRING_ARRAY; } @Override public void getAllMethodNames(@NotNull HashSet set) { + set.addAll(JetShortFunctionNameIndex.getInstance().getAllKeys(project)); } @NotNull @@ -235,7 +252,7 @@ public class JetShortNamesCache extends PsiShortNamesCache { @NotNull @Override public String[] getAllFieldNames() { - return new String[0]; + return ArrayUtil.EMPTY_STRING_ARRAY; } @Override diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java index a7b90559619..bc9e963014c 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetCompletionContributor.java @@ -21,7 +21,9 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.project.Project; import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; @@ -30,18 +32,21 @@ import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; -import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.psi.JetQualifiedExpression; -import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; -import org.jetbrains.jet.lang.psi.JetUserType; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.plugin.caches.JetCacheManager; import org.jetbrains.jet.plugin.caches.JetShortNamesCache; +import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; import org.jetbrains.jet.plugin.references.JetSimpleNameReference; +import org.jetbrains.jet.util.QualifiedNamesUtil; import java.util.Collection; import java.util.HashSet; +import java.util.List; /** * @author Nikolay Krasko @@ -74,6 +79,7 @@ public class JetCompletionContributor extends CompletionContributor { if (shouldRunTopLevelCompletion(parameters)) { addClasses(parameters, result, positions); addJetTopLevelFunctions(result, position, positions); + addJetExtensionFunctions(jetReference.getExpression(), result, position); } result.stopHere(); @@ -82,6 +88,67 @@ public class JetCompletionContributor extends CompletionContributor { }); } + private static void addJetExtensionFunctions(JetSimpleNameExpression expression, CompletionResultSet result, PsiElement position) { + + BindingContext context = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) position.getContainingFile()); + JetExpression receiverExpression = expression.getReceiverExpression(); + + + if (receiverExpression != null) { + final JetType expressionType = context.get(BindingContext.EXPRESSION_TYPE, receiverExpression); + final JetScope scope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression); + + if (expressionType != null && scope != null) { + JetShortNamesCache namesCache = JetCacheManager.getInstance(position.getProject()).getNamesCache(); + Collection extensionFunctionsNames = namesCache.getAllJetExtensionFunctionsNames( + GlobalSearchScope.allScope(position.getProject())); + + HashSet functionFQNs = new HashSet(); + + // Collect all possible extension function qualified names + for (String name : extensionFunctionsNames) { + if (result.getPrefixMatcher().prefixMatches(name)) { + Collection extensionFunctions = + namesCache.getJetExtensionFunctionsByName(name, GlobalSearchScope.allScope(position.getProject())); + + for (PsiElement extensionFunction : extensionFunctions) { + if (extensionFunction instanceof JetNamedFunction) { + functionFQNs.add(JetPsiUtil.getFQName((JetNamedFunction) extensionFunction)); + } else if (extensionFunction instanceof PsiMethod) { + final PsiMethod function = (PsiMethod) extensionFunction; + final PsiClass containingClass = function.getContainingClass(); + + if (containingClass != null) { + final String classFQN = containingClass.getQualifiedName(); + + if (classFQN != null) { + final String classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN); + functionFQNs.add(QualifiedNamesUtil.combine(classParentFQN, function.getName())); + } + } + } + } + } + } + + for (String functionFQN : functionFQNs) { + // System.out.println(functionFQN); + + List functionDescriptors = ExpressionTypingUtils.canCallBeResolved( + functionFQN, position.getProject(), receiverExpression, expressionType, scope); + + // System.out.println(!functionDescriptors.isEmpty()); + + if (!functionDescriptors.isEmpty()) { + for (FunctionDescriptor functionDescriptor : functionDescriptors) { + result.addElement(DescriptorLookupConverter.createLookupElement(context, functionDescriptor)); + } + } + } + } + } + } + private static void addReferenceVariant( @NotNull CompletionResultSet result, @NotNull Object variant, @@ -210,4 +277,9 @@ public class JetCompletionContributor extends CompletionContributor { return null; } + + @Override + public void beforeCompletion(@NotNull CompletionInitializationContext context) { + super.beforeCompletion(context); //To change body of overridden methods use File | Settings | File Templates. + } } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java index 75f3c44fbd1..b62ddcd5fef 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetPackagesContributor.java @@ -29,6 +29,9 @@ import org.jetbrains.jet.lang.psi.JetNamespaceHeader; import org.jetbrains.jet.plugin.references.JetPackageReference; /** + * Performs completion in package directive. Should suggest only packages and avoid showing fake package produced by + * DUMMY_IDENTIFIER. + * * @author Nikolay Krasko */ public class JetPackagesContributor extends CompletionContributor { diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java index 40612db4ebd..9e46c7c00d4 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java @@ -115,11 +115,6 @@ public class JetFunctionInsertHandler implements InsertHandler { return; } - // No auto import for qualified expressions - if (PsiTreeUtil.getParentOfType(element, JetQualifiedExpression.class) != null) { - return; - } - if (context.getFile() instanceof JetFile && item.getObject() instanceof JetLookupObject) { final DeclarationDescriptor descriptor = ((JetLookupObject) item.getObject()).getDescriptor(); if (descriptor instanceof SimpleFunctionDescriptor) { @@ -128,6 +123,13 @@ public class JetFunctionInsertHandler implements InsertHandler { SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor; final String fqn = DescriptorUtils.getFQName(functionDescriptor); + // Don't insert import for qualified expression if don't try to insert extension function + if (PsiTreeUtil.getParentOfType(element, JetQualifiedExpression.class) != null && + !functionDescriptor.getReceiverParameter().exists()) { + + return; + } + if (DescriptorUtils.isTopLevelFunction(functionDescriptor)) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override diff --git a/idea/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java b/idea/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java index 97572a1652e..923efa85391 100644 --- a/idea/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java +++ b/idea/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java @@ -43,14 +43,16 @@ public class StubIndexServiceImpl implements StubIndexService { public void indexFunction(PsiJetFunctionStub stub, IndexSink sink) { String name = stub.getName(); if (name != null) { - if (!stub.isExtension()) { - if (stub.isTopLevel()) { + if (stub.isTopLevel()) { + // Collection only top level functions as only they are expected in completion without explicit import + if (!stub.isExtension()) { sink.occurrence(JetIndexKeys.TOP_LEVEL_FUNCTION_SHORT_NAME_KEY, name); + // sink.occurrence(JetIndexKeys.TOP_LEVEL_FUNCTION_FQNAME_KEY, name); + } else { + sink.occurrence(JetIndexKeys.EXTENSION_FUNCTION_SHORT_NAME_KEY, name); + // sink.occurrence(JetIndexKeys.EXTENSION_FUNCTION_FQNAME_KEY, name); } } - else { - sink.occurrence(JetIndexKeys.EXTENSION_FUNCTION_SHORT_NAME_KEY, name); - } } } }