KT-1151 Code completion for not imported extension functions - completion for extension functions when function doesn't have parameters
This commit is contained in:
@@ -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 = "<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();
|
||||
|
||||
+1
-1
@@ -347,7 +347,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static OverloadResolutionResults<FunctionDescriptor> resolveFakeCall(ExpressionReceiver receiver, ExpressionTypingContext context, String name) {
|
||||
public static OverloadResolutionResults<FunctionDescriptor> 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.<ValueArgument>emptyList());
|
||||
|
||||
+71
-12
@@ -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<FunctionDescriptor> 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<JetPattern, DataFlowInfo>(),
|
||||
new HashMap<JetPattern, List<VariableDescriptor>>(),
|
||||
new LabelResolver(),
|
||||
new BindingTraceContext(),
|
||||
writableScopeWithImports,
|
||||
DataFlowInfo.EMPTY,
|
||||
TypeUtils.NO_EXPECTED_TYPE,
|
||||
TypeUtils.NO_EXPECTED_TYPE,
|
||||
false
|
||||
);
|
||||
|
||||
writableScopeWithImports.changeLockLevel(WritableScope.LockLevel.READING);
|
||||
|
||||
OverloadResolutionResults<FunctionDescriptor> resolutionResult =
|
||||
ControlStructureTypingVisitor.resolveFakeCall(expressionReceiver, context, QualifiedNamesUtil.fqnToShortName(functionFQN));
|
||||
|
||||
if (!resolutionResult.isSuccess()) {
|
||||
return new ArrayList<FunctionDescriptor>();
|
||||
}
|
||||
|
||||
ArrayList<FunctionDescriptor> resolvedDescriptors = new ArrayList<FunctionDescriptor>();
|
||||
|
||||
for (ResolvedCall<? extends FunctionDescriptor> resolvedCall : resolutionResult.getResultingCalls()) {
|
||||
resolvedDescriptors.add(resolvedCall.getCandidateDescriptor());
|
||||
}
|
||||
|
||||
return resolvedDescriptors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> getPossiblePackageDeclarationsNames(Project project, GlobalSearchScope scope) {
|
||||
final ArrayList<String> result = new ArrayList<String>();
|
||||
|
||||
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<String> getTopExtensionFunctionNames(TypeDescriptor typeDescriptor, Project project,
|
||||
GlobalSearchScope scope) {
|
||||
|
||||
return getPossiblePackageDeclarationsNames(project, scope);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> getPossiblePackageDeclarationsNames(Project project, GlobalSearchScope scope) {
|
||||
final ArrayList<String> result = new ArrayList<String>();
|
||||
|
||||
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<String> getTopExtensionFunctionNames(Project project, GlobalSearchScope scope) {
|
||||
|
||||
// Extension function should have an parameter of type JetValueParameter with explicit receiver parameter.
|
||||
|
||||
HashSet<String> extensionNames = new HashSet<String>();
|
||||
|
||||
Collection<PsiAnnotation> 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<PsiMethod> getTopExtensionFunctionByName(String name, Project project, GlobalSearchScope scope) {
|
||||
|
||||
HashSet<PsiMethod> selectedMethods = new HashSet<PsiMethod>();
|
||||
|
||||
Collection<PsiMethod> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<String> getALlJetClassFQNames() {
|
||||
final BindingContext context = getResolutionContext(GlobalSearchScope.allScope(project));
|
||||
return context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR);
|
||||
}
|
||||
// public Collection<String> getALlJetClassFQNames() {
|
||||
// final BindingContext context = getResolutionContext(GlobalSearchScope.allScope(project));
|
||||
// return context.getKeys(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR);
|
||||
// }
|
||||
|
||||
@NotNull
|
||||
public Collection<String> getFQNamesByName(@NotNull final String name, @NotNull GlobalSearchScope scope) {
|
||||
@@ -138,7 +138,7 @@ public class JetShortNamesCache extends PsiShortNamesCache {
|
||||
public Collection<SimpleFunctionDescriptor> 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<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope);
|
||||
|
||||
@@ -176,32 +176,48 @@ public class JetShortNamesCache extends PsiShortNamesCache {
|
||||
final Set<String> extensionFunctionNames = new HashSet<String>();
|
||||
|
||||
extensionFunctionNames.addAll(JetExtensionFunctionNameIndex.getInstance().getAllKeys(project));
|
||||
extensionFunctionNames.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionNames(null, project, scope));
|
||||
extensionFunctionNames.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionNames(project, scope));
|
||||
|
||||
return extensionFunctionNames;
|
||||
}
|
||||
|
||||
public Collection<SimpleFunctionDescriptor> 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<PsiElement> getJetExtensionFunctionsByName(final @NotNull String name, @NotNull GlobalSearchScope scope) {
|
||||
HashSet<PsiElement> functions = new HashSet<PsiElement>();
|
||||
functions.addAll(JetExtensionFunctionNameIndex.getInstance().get(name, project, scope));
|
||||
functions.addAll(JetFromJavaDescriptorHelper.getTopExtensionFunctionByName(name, project, scope));
|
||||
|
||||
final Collection<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope);
|
||||
return functions;
|
||||
}
|
||||
|
||||
final BindingContext context = getResolutionContext(scope);
|
||||
// public Collection<NamedFunctionDescriptor> 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<JetNamedFunction> jetNamedFunctions = JetShortFunctionNameIndex.getInstance().get(name, project, scope);
|
||||
//
|
||||
// final BindingContext context = getResolutionContext(scope);
|
||||
//
|
||||
// final HashSet<NamedFunctionDescriptor> result = new HashSet<NamedFunctionDescriptor>();
|
||||
//
|
||||
// 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<SimpleFunctionDescriptor> result = new HashSet<SimpleFunctionDescriptor>();
|
||||
public Collection<JetNamedFunction> 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<String> 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<String> 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
|
||||
|
||||
@@ -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<String> extensionFunctionsNames = namesCache.getAllJetExtensionFunctionsNames(
|
||||
GlobalSearchScope.allScope(position.getProject()));
|
||||
|
||||
HashSet<String> functionFQNs = new HashSet<String>();
|
||||
|
||||
// Collect all possible extension function qualified names
|
||||
for (String name : extensionFunctionsNames) {
|
||||
if (result.getPrefixMatcher().prefixMatches(name)) {
|
||||
Collection<PsiElement> 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<FunctionDescriptor> 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.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -115,11 +115,6 @@ public class JetFunctionInsertHandler implements InsertHandler<LookupElement> {
|
||||
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<LookupElement> {
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user