selection after methods chains completion and checking parameters in context

This commit is contained in:
Dmitry Batkovich
2013-10-24 12:47:33 +04:00
parent 0fb461e070
commit 80106fc16c
53 changed files with 1582 additions and 1943 deletions
@@ -9,7 +9,7 @@ import com.intellij.codeInsight.completion.methodChains.search.MethodChainsSearc
import com.intellij.codeInsight.completion.methodChains.search.MethodsChain;
import com.intellij.codeInsight.completion.methodChains.search.MethodsChainLookupRangingHelper;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexFeature;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
@@ -35,13 +35,13 @@ import static com.intellij.patterns.PsiJavaPatterns.or;
public class MethodsChainsCompletionContributor extends CompletionContributor {
public static final int INVOCATIONS_THRESHOLD = 3;
private final static int MAX_SEARCH_RESULT_SIZE = 20;
private final static int MAX_SEARCH_RESULT_SIZE = 5;
private final static int MAX_CHAIN_SIZE = 4;
private final static int FILTER_RATIO = 10;
@Override
public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) {
if (parameters.getInvocationCount() >= INVOCATIONS_THRESHOLD && CompilerOutputIndexer.getInstance(parameters.getPosition().getProject()).isEnabled()) {
if (parameters.getInvocationCount() >= INVOCATIONS_THRESHOLD && CompilerOutputIndexFeature.METHOD_CHAINS_COMPLETION.isEnabled()) {
super.fillCompletionVariants(parameters, result);
if (ApplicationManager.getApplication().isUnitTestMode()) {
result.stopHere();
@@ -73,8 +73,8 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
}
contextRelevantTypes.remove(targetClassQName);
final List<LookupElement> foundedElements = searchForLookups(targetClassQName, contextRelevantTypes, completionContext);
result.addAllElements(foundedElements);
final List<LookupElement> foundElements = searchForLookups(targetClassQName, contextRelevantTypes, completionContext);
result.addAllElements(foundElements);
}
});
}
@@ -100,7 +100,8 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
completionContext, searchService)) {
boolean insert = true;
for (final MethodsChain baseChain : searchResult) {
if (baseChain.weakContains(chain)) {
final MethodsChain.CompareResult r = MethodsChain.compare(baseChain, chain, completionContext);
if (r != MethodsChain.CompareResult.NOT_EQUAL) {
insert = false;
break;
}
@@ -116,8 +117,19 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
});
}
}
return MethodsChainLookupRangingHelper.chainsToWeightableLookupElements(filterTailAndGetSumLastMethodOccurrence(searchResult),
completionContext);
final List<MethodsChain> chains = searchResult.size() > MAX_CHAIN_SIZE ? chooseHead(searchResult) : searchResult;
return MethodsChainLookupRangingHelper
.chainsToWeightableLookupElements(filterTailAndGetSumLastMethodOccurrence(chains), completionContext);
}
private static List<MethodsChain> chooseHead(final List<MethodsChain> elements) {
Collections.sort(elements, new Comparator<MethodsChain>() {
@Override
public int compare(final MethodsChain o1, final MethodsChain o2) {
return o2.getChainWeight() - o1.getChainWeight();
}
});
return elements.subList(0, MAX_CHAIN_SIZE);
}
@SuppressWarnings("unchecked")
@@ -188,8 +200,7 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
final MethodChainsSearchService searchService) {
return ChainsSearcher.search(searchService, targetQName, contextVarsQNames, maxResultSize, maxChainSize,
createNotDeprecatedMethodsResolver(JavaPsiFacade.getInstance(context.getProject()),
context.getResolveScope()),
context.getExcludedQNames(), context.getContextMethodName());
context.getResolveScope()), context.getExcludedQNames(), context);
}
private static FactoryMap<MethodIncompleteSignature, PsiMethod[]> createNotDeprecatedMethodsResolver(final JavaPsiFacade javaPsiFacade,
@@ -1,10 +1,12 @@
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
@@ -12,59 +14,87 @@ import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class ChainCompletionNewVariableLookupElement extends LookupItem<PsiClass> {
public class ChainCompletionNewVariableLookupElement extends LookupElementDecorator<LookupElement> {
private final PsiClass psiClass;
private final String newVarName;
private final PsiClass myPsiClass;
private final String myNewVarName;
public ChainCompletionNewVariableLookupElement(final PsiClass psiClass, final String newVarName) {
super(psiClass, newVarName);
this.newVarName = newVarName;
this.psiClass = psiClass;
public ChainCompletionNewVariableLookupElement(final PsiClass psiClass, final String newVarName, final LookupElement calledMethods) {
super(calledMethods);
myNewVarName = newVarName;
myPsiClass = psiClass;
}
public static ChainCompletionNewVariableLookupElement create(final PsiClass psiClass) {
public static ChainCompletionNewVariableLookupElement create(final PsiClass psiClass, final LookupElement calledMethods) {
final Project project = psiClass.getProject();
final SuggestedNameInfo suggestedNameInfo = JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, JavaPsiFacade .getElementFactory( project).createType(psiClass));
return new ChainCompletionNewVariableLookupElement(psiClass, chooseLongest(suggestedNameInfo.names));
final String newVarName = chooseLongestName(JavaCodeStyleManager.getInstance(project).
suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, JavaPsiFacade.getElementFactory(project).createType(psiClass)));
return new ChainCompletionNewVariableLookupElement(psiClass, newVarName, calledMethods);
}
@Override
public void handleInsert(final InsertionContext context) {
final PsiFile file = context.getFile();
((PsiJavaFile) file).importClass(psiClass);
final PsiStatement statement = PsiTreeUtil.getParentOfType(file.findElementAt(context.getEditor().getCaretModel().getOffset()), PsiStatement.class);
((PsiJavaFile)file).importClass(myPsiClass);
final PsiElement caretElement = file.findElementAt(context.getEditor().getCaretModel().getOffset());
if (caretElement == null) {
throw new NullPointerException();
}
final PsiStatement statement = (PsiStatement) caretElement.getPrevSibling();
final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(statement, PsiCodeBlock.class);
assert codeBlock != null;
if (codeBlock == null) {
throw new NullPointerException();
}
final Project project = context.getProject();
final Ref<PsiElement> insertedStatementRef = Ref.create();
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
new WriteCommandAction.Simple(project, file) {
@Override
protected void run() throws Throwable {
codeBlock.addBefore(
JavaPsiFacade.getElementFactory(
project).
createStatementFromText(String.format("%s %s = null;", psiClass.getName(), newVarName), null), statement);
final PsiStatement statementFromText = elementFactory.createStatementFromText(String.format("%s %s = null;", myPsiClass.getName(), myNewVarName), null);
insertedStatementRef.set(codeBlock.addBefore(statementFromText, statement));
}
}.execute();
final PsiLiteralExpression nullKeyword = findNull(insertedStatementRef.get());
context.commitDocument();
PsiDocumentManager.getInstance(context.getProject()).doPostponedOperationsAndUnblockDocument(context.getDocument());
getDelegate().handleInsert(context);
final int offset = nullKeyword.getTextOffset();
final int endOffset = offset + nullKeyword.getTextLength();
context.getEditor().getSelectionModel().setSelection(offset, endOffset);
context.getEditor().getCaretModel().moveToOffset(offset);
}
@NotNull
@Override
public String getLookupString() {
return newVarName;
return myNewVarName + "." + getDelegate().getLookupString();
}
@Override
public void renderElement(final LookupElementPresentation presentation) {
super.renderElement(presentation);
presentation.setItemText(newVarName);
presentation.setItemText(myNewVarName + "." + presentation.getItemText());
}
private static String chooseLongest(final String[] names) {
private static PsiLiteralExpression findNull(final PsiElement psiElement) {
final Collection<PsiLiteralExpression> literalExpressions = PsiTreeUtil.findChildrenOfType(psiElement, PsiLiteralExpression.class);
for (final PsiLiteralExpression literalExpression : literalExpressions) {
if (PsiKeyword.NULL.equals(literalExpression.getText())) {
return literalExpression;
}
}
throw new IllegalArgumentException();
}
private static String chooseLongestName(final SuggestedNameInfo suggestedNameInfo) {
final String[] names = suggestedNameInfo.names;
String longestWord = names[0];
int maxLength = longestWord.length();
for (int i = 1; i < names.length; i++) {
@@ -1,36 +0,0 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class CachedNotDeprecatedMethodsResolver {
private final Map<MethodIncompleteSignature, PsiMethod[]> myResolveLocalCache = new HashMap<MethodIncompleteSignature, PsiMethod[]>();
private final JavaPsiFacade myJavaPsiFacade;
private final GlobalSearchScope myScope;
public CachedNotDeprecatedMethodsResolver(final Project project, final GlobalSearchScope scope) {
myScope = scope;
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
}
@NotNull
public PsiMethod[] resolveNotDeprecated(@NotNull final MethodIncompleteSignature methodInvocation) {
final PsiMethod[] cached = myResolveLocalCache.get(methodInvocation);
if (cached != null) {
return cached;
}
final PsiMethod[] methods = methodInvocation.resolveNotDeprecated(myJavaPsiFacade, myScope);
myResolveLocalCache.put(methodInvocation, methods);
return methods;
}
}
@@ -1,12 +1,15 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.completion.methodChains.Constants;
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiMethod;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FactoryMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -15,17 +18,6 @@ import java.util.*;
*/
public class ChainsSearcher {
public static List<MethodsChain> search(final MethodChainsSearchService searchService,
final String targetQName,
final Set<String> contextQNames,
final int maxResultSize,
final int pathMaximalLength,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final String contextMethodName) {
return search(searchService, targetQName, contextQNames, maxResultSize, pathMaximalLength, resolver,
Collections.<String>singleton(targetQName), contextMethodName);
}
public static List<MethodsChain> search(final MethodChainsSearchService searchService,
final String targetQName,
final Set<String> contextQNames,
@@ -33,83 +25,53 @@ public class ChainsSearcher {
final int pathMaximalLength,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final Set<String> excludedParamsTypesQNames,
final String contextMethodName) {
final ChainCompletionContext context) {
final SearchInitializer initializer = createInitializer(targetQName, resolver, searchService, excludedParamsTypesQNames);
final ArrayList<MethodsChain> methodsChains = new ArrayList<MethodsChain>(maxResultSize);
final MethodsChain firstBestMethodsChain =
search(searchService, initializer, contextQNames, Collections.<String>emptySet(), pathMaximalLength, resolver, targetQName,
excludedParamsTypesQNames, contextMethodName);
if (firstBestMethodsChain != null) {
methodsChains.add(firstBestMethodsChain);
Set<Set<String>> excludedCombinations = MethodsChain.edgeCombinations(Collections.<Set<String>>emptySet(), firstBestMethodsChain);
while (methodsChains.size() <= maxResultSize) {
final Set<Set<String>> localExcludedCombinations = excludedCombinations;
boolean allLocalsIsNull = true;
final int beforeStepChainsCount = methodsChains.size();
for (final Set<String> excludedEdges : localExcludedCombinations) {
final MethodsChain local =
search(searchService, initializer, contextQNames, excludedEdges, pathMaximalLength, resolver, targetQName,
excludedParamsTypesQNames, contextMethodName);
if (local != null) {
allLocalsIsNull = false;
}
else {
continue;
}
boolean add = true;
for (int i = 0; i < methodsChains.size(); i++) {
final MethodsChain chain = methodsChains.get(i);
final MethodsChain.CompareResult compareResult = MethodsChain.compare(local, chain);
if (compareResult == MethodsChain.CompareResult.EQUAL || compareResult == MethodsChain.CompareResult.RIGHT_CONTAINS_LEFT) {
add = false;
break;
}
else if (compareResult == MethodsChain.CompareResult.LEFT_CONTAINS_RIGHT) {
methodsChains.set(i, local);
add = false;
break;
}
}
if (add) {
methodsChains.add(local);
if (methodsChains.size() >= maxResultSize) {
return methodsChains;
}
excludedCombinations = MethodsChain.edgeCombinations(excludedCombinations, local);
}
}
if (allLocalsIsNull || beforeStepChainsCount == methodsChains.size()) {
return methodsChains;
}
}
}
return methodsChains;
return search(searchService, initializer, contextQNames, pathMaximalLength, maxResultSize, resolver, targetQName,
excludedParamsTypesQNames, context);
}
private static SearchInitializer createInitializer(final String targetQName,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> context,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final MethodChainsSearchService searchService,
final Set<String> excludedParamsTypesQNames) {
return new SearchInitializer(searchService.getMethods(targetQName), context, targetQName, excludedParamsTypesQNames);
return new SearchInitializer(searchService.getMethods(targetQName), resolver, targetQName, excludedParamsTypesQNames);
}
@Nullable
private static MethodsChain search(final MethodChainsSearchService searchService,
final SearchInitializer initializer,
final Set<String> toSet,
final Set<String> excludedEdgeNames,
final int pathMaximalLength,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final String targetQName,
final Set<String> excludedParamsTypesQNames,
final String contextMethodName) {
@NotNull
private static List<MethodsChain> search(final MethodChainsSearchService searchService,
final SearchInitializer initializer,
final Set<String> toSet,
final int pathMaximalLength,
final int maxResultSize,
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
final String targetQName,
final Set<String> excludedParamsTypesQNames,
final ChainCompletionContext context) {
final Set<String> allExcludedNames = MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName);
ProgressManager.checkCanceled();
final SearchInitializer.InitResult initResult = initializer.init(excludedEdgeNames, toSet, searchService, contextMethodName);
final SearchInitializer.InitResult initResult = initializer.init(Collections.<String>emptySet());
final Map<MethodIncompleteSignature, MethodsChain> knownDistance = initResult.getChains();
final PriorityQueue<WeightAware<MethodIncompleteSignature>> q =
new PriorityQueue<WeightAware<MethodIncompleteSignature>>(initResult.getVertexes());
MethodsChain result = initResult.getCurrentBestTargetChain();
final List<WeightAware<MethodIncompleteSignature>> allInitialVertexes = initResult.getVertexes();
final LinkedList<WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>> q =
new LinkedList<WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>>(
ContainerUtil.map(allInitialVertexes, new Function<WeightAware<MethodIncompleteSignature>, WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>>() {
@Override
public WeightAware<Pair<MethodIncompleteSignature, MethodsChain>> fun(
final WeightAware<MethodIncompleteSignature> methodIncompleteSignatureWeightAware) {
return new WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>(
new Pair<MethodIncompleteSignature, MethodsChain>(
methodIncompleteSignatureWeightAware
.getUnderlying(),
new MethodsChain(resolver.get(
methodIncompleteSignatureWeightAware.getUnderlying()),
methodIncompleteSignatureWeightAware.getWeight(),
methodIncompleteSignatureWeightAware.getUnderlying().getOwner())),
methodIncompleteSignatureWeightAware.getWeight());
}
}));
int maxWeight = 0;
for (final MethodsChain methodsChain : knownDistance.values()) {
@@ -118,89 +80,139 @@ public class ChainsSearcher {
}
}
final WeightAware<MethodIncompleteSignature> maxVertex = q.peek();
final int maxDistance;
if (maxVertex != null) {
maxDistance = maxVertex.getWeight();
}
else {
return null;
}
final ResultHolder result = new ResultHolder(context);
while (!q.isEmpty()) {
final WeightAware<MethodIncompleteSignature> currentVertex = q.poll();
ProgressManager.checkCanceled();
final WeightAware<Pair<MethodIncompleteSignature, MethodsChain>> currentVertex = q.poll();
final int currentVertexDistance = currentVertex.getWeight();
if (currentVertexDistance * Constants.CHAIN_SEARCH_MAGIC_RATIO < maxDistance) {
return result;
}
final MethodIncompleteSignature currentVertexUnderlying = currentVertex.getUnderlying();
final MethodsChain currentVertexMethodsChain = knownDistance.get(currentVertexUnderlying);
final Pair<MethodIncompleteSignature, MethodsChain> currentVertexUnderlying = currentVertex.getUnderlying();
final MethodsChain currentVertexMethodsChain = knownDistance.get(currentVertexUnderlying.getFirst());
if (currentVertexDistance != currentVertexMethodsChain.getChainWeight()) {
continue;
}
final SortedSet<UsageIndexValue> bigrams = searchService.getBigram(currentVertexUnderlying);
int bigramsSumWeight = 0;
int maxUpdatedWeight = 0;
if (currentVertex.getUnderlying().getFirst().isStatic() || toSet.contains(currentVertex.getUnderlying().getFirst().getOwner())) {
result.add(currentVertex.getUnderlying().getSecond());
continue;
}
final SortedSet<UsageIndexValue> bigrams = searchService.getBigram(currentVertexUnderlying.getFirst());
final MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>> currentSignatures =
new MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>>(maxResultSize);
for (final UsageIndexValue indexValue : bigrams) {
final MethodIncompleteSignature vertex = indexValue.getMethodIncompleteSignature();
final int occurrences = indexValue.getOccurrences();
bigramsSumWeight += occurrences;
final boolean canBeResult = vertex.isStatic() || toSet.contains(vertex.getOwner());
if (!vertex.getOwner().equals(targetQName) || canBeResult) {
if (!vertex.getOwner().equals(targetQName)) {
final int vertexDistance = Math.min(currentVertexDistance, occurrences);
final MethodsChain knownVertexMethodsChain = knownDistance.get(vertex);
if ((knownVertexMethodsChain == null || knownVertexMethodsChain.getChainWeight() < vertexDistance) &&
(result == null || result.getChainWeight() < vertexDistance)) {
if (occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO >= currentVertexMethodsChain.getChainWeight()) {
if ((knownVertexMethodsChain == null || knownVertexMethodsChain.getChainWeight() < vertexDistance)) {
if (currentSignatures.isEmpty() || currentSignatures.last().getWeight() < vertexDistance) {
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
final PsiMethod[] psiMethods = resolver.get(methodInvocation);
if (psiMethods.length != 0 && MethodChainsSearchUtil.checkParametersForTypesQNames(psiMethods, allExcludedNames)) {
final MethodsChain newBestMethodsChain = currentVertexMethodsChain.addEdge(psiMethods);
if (canBeResult) {
result = newBestMethodsChain;
}
else if (newBestMethodsChain.size() < pathMaximalLength - 1) {
maxUpdatedWeight = Math.max(maxUpdatedWeight, newBestMethodsChain.getChainWeight());
q.add(new WeightAware<MethodIncompleteSignature>(indexValue.getMethodIncompleteSignature(),
newBestMethodsChain.getChainWeight()));
final MethodsChain newBestMethodsChain =
currentVertexMethodsChain.addEdge(psiMethods, indexValue.getMethodIncompleteSignature().getOwner(), vertexDistance);
if (newBestMethodsChain.size() <= pathMaximalLength - 1) {
currentSignatures
.add(new WeightAware<MethodIncompleteSignature>(indexValue.getMethodIncompleteSignature(), vertexDistance));
}
knownDistance.put(vertex, newBestMethodsChain);
}
}
else if (!allExcludedNames.contains(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName()) &&
searchService.isSingleton(currentVertexMethodsChain.getFirstQualifierClass(), contextMethodName) &&
(searchService.isRelevantMethodForNotOverriden(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName()) ||
searchService.isRelevantMethodForField(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName()))) {
result = currentVertexMethodsChain;
}
}
}
}
//if ((result == null || maxUpdatedWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO2 <= bigramsSumWeight)
// && bigramsSumWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO >= currentVertexMethodsChain.getChainWeight()) {
// return currentVertexMethodsChain;
//}
boolean updated = false;
if (!currentSignatures.isEmpty()) {
boolean isBreak = false;
for (final WeightAware<MethodIncompleteSignature> sign : currentSignatures) {
final PsiMethod[] resolved = resolver.get(sign.getUnderlying());
if (!isBreak) {
if (sign.getWeight() * maxResultSize > currentVertex.getWeight()) {
final boolean stopChain = sign.getUnderlying().isStatic() || toSet.contains(sign.getUnderlying().getOwner());
if (stopChain) {
updated = true;
result.add(currentVertex.getUnderlying().getSecond().addEdge(resolved, sign.getUnderlying().getOwner(), sign.getWeight()));
continue;
}
else {
updated = true;
final MethodsChain methodsChain =
currentVertexUnderlying.second.addEdge(resolved, sign.getUnderlying().getOwner(), sign.getWeight());
q.add(new WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>(
new Pair<MethodIncompleteSignature, MethodsChain>(sign.getUnderlying(), methodsChain), sign.getWeight()));
continue;
}
}
}
final MethodsChain methodsChain =
currentVertexUnderlying.second.addEdge(resolved, sign.getUnderlying().getOwner(), sign.getWeight());
if (ParametersMatcher.matchParameters(methodsChain, context).noUnmatched()) {
updated = true;
q.addFirst(new WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>(
new Pair<MethodIncompleteSignature, MethodsChain>(sign.getUnderlying(), methodsChain), sign.getWeight()));
}
isBreak = true;
}
}
if (!updated &&
(currentVertex.getUnderlying().getFirst().isStatic() ||
!targetQName.equals(currentVertex.getUnderlying().getFirst().getOwner()))) {
result.add(currentVertex.getUnderlying().getSecond());
}
if (result.size() > maxResultSize) {
return result.getResult();
}
}
return result.getResult();
}
if ((currentVertexMethodsChain.isStaticChain() ||
!allExcludedNames.contains(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName())) &&
bigramsSumWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO <= currentVertexDistance &&
(result == null || result.getChainWeight() < currentVertexDistance) &&
(currentVertexMethodsChain.isStaticChain() ||
searchService.isSingleton(currentVertexMethodsChain.getFirstQualifierClass(), contextMethodName) &&
(searchService.isRelevantMethodForNotOverriden(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName()) ||
searchService.isRelevantMethodForField(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
currentVertexMethodsChain.getOneOfFirst().getName())))) {
result = currentVertexMethodsChain;
private static class ResultHolder {
private final List<MethodsChain> myResult;
private final ChainCompletionContext myContext;
private ResultHolder(final ChainCompletionContext context) {
myContext = context;
myResult = new ArrayList<MethodsChain>();
}
public void add(final MethodsChain newChain) {
if (myResult.isEmpty()) {
myResult.add(newChain);
return;
}
boolean doAdd = true;
final Stack<Integer> indexesToRemove = new Stack<Integer>();
for (int i = 0; i < myResult.size(); i++) {
final MethodsChain chain = myResult.get(i);
//
final MethodsChain.CompareResult r = MethodsChain.compare(chain, newChain, myContext);
switch (r) {
case LEFT_CONTAINS_RIGHT:
indexesToRemove.add(i);
break;
case RIGHT_CONTAINS_LEFT:
case EQUAL:
doAdd = false;
break;
case NOT_EQUAL:
break;
}
}
while (!indexesToRemove.empty()) {
myResult.remove((int)indexesToRemove.pop());
}
if (doAdd) {
myResult.add(newChain);
}
}
if (result != null && result.getChainWeight() * Constants.CHAIN_SEARCH_MAGIC_RATIO >= maxWeight) {
return result;
public List<MethodsChain> getResult() {
return myResult;
}
public int size() {
return myResult.size();
}
return null;
}
}
@@ -0,0 +1,209 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class MaxSizeTreeSet<E> implements NavigableSet<E> {
@NotNull
private final NavigableSet<E> myUnderlying;
private final int myMaxSize;
public MaxSizeTreeSet(final int maxSize) {
myMaxSize = maxSize;
if (myMaxSize < 1) {
throw new IllegalArgumentException();
}
myUnderlying = new TreeSet<E>();
}
public E lower(final E e) {
return myUnderlying.lower(e);
}
public E floor(final E e) {
return myUnderlying.floor(e);
}
public E ceiling(final E e) {
return myUnderlying.ceiling(e);
}
public E higher(final E e) {
return myUnderlying.higher(e);
}
@Override
public E pollFirst() {
return myUnderlying.pollFirst();
}
@Override
public E pollLast() {
return myUnderlying.pollLast();
}
@NotNull
@Override
public Iterator<E> iterator() {
return myUnderlying.iterator();
}
@NotNull
@Override
public NavigableSet<E> descendingSet() {
return myUnderlying.descendingSet();
}
@NotNull
@Override
public Iterator<E> descendingIterator() {
return myUnderlying.descendingIterator();
}
@NotNull
public NavigableSet<E> subSet(final E fromElement, final boolean fromInclusive, final E toElement, final boolean toInclusive) {
return myUnderlying.subSet(fromElement, fromInclusive, toElement, toInclusive);
}
@NotNull
public NavigableSet<E> headSet(final E toElement, final boolean inclusive) {
return myUnderlying.headSet(toElement, inclusive);
}
@NotNull
public NavigableSet<E> tailSet(final E fromElement, final boolean inclusive) {
return myUnderlying.tailSet(fromElement, inclusive);
}
@NotNull
public SortedSet<E> subSet(final E fromElement, final E toElement) {
return myUnderlying.subSet(fromElement, toElement);
}
@NotNull
public SortedSet<E> headSet(final E toElement) {
return myUnderlying.headSet(toElement);
}
@NotNull
public SortedSet<E> tailSet(final E fromElement) {
return myUnderlying.tailSet(fromElement);
}
@Nullable
@Override
public Comparator<? super E> comparator() {
return myUnderlying.comparator();
}
@Override
public E first() {
return myUnderlying.first();
}
@Override
public E last() {
return myUnderlying.last();
}
@Override
public int size() {
return myUnderlying.size();
}
@Override
public boolean isEmpty() {
return myUnderlying.isEmpty();
}
@Override
public boolean contains(final Object o) {
return myUnderlying.contains(o);
}
@NotNull
@Override
public Object[] toArray() {
return myUnderlying.toArray();
}
@NotNull
@Override
public <T> T[] toArray(final T[] a) {
return myUnderlying.toArray(a);
}
public boolean add(final E e) {
if (myUnderlying.size() == myMaxSize) {
//noinspection ConstantConditions
final Comparator<? super E> comparator = comparator();
if ((comparator == null ? ((Comparable)e).compareTo(last()) : comparator.compare(e, last())) < 0) {
final boolean isAdded = myUnderlying.add(e);
if (isAdded) {
pollLast();
return true;
}
}
return false;
}
return myUnderlying.add(e);
}
@Override
public boolean remove(final Object o) {
return myUnderlying.remove(o);
}
@Override
public boolean containsAll(final Collection<?> c) {
return myUnderlying.containsAll(c);
}
public boolean addAll(final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(final Collection<?> c) {
return myUnderlying.retainAll(c);
}
@Override
public boolean removeAll(final Collection<?> c) {
return myUnderlying.removeAll(c);
}
@Override
public void clear() {
myUnderlying.clear();
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof MaxSizeTreeSet)) return false;
final MaxSizeTreeSet that = (MaxSizeTreeSet)o;
if (!myUnderlying.equals(that.myUnderlying)) return false;
return true;
}
@Override
public int hashCode() {
return myUnderlying.hashCode();
}
@Override
public String toString() {
return myUnderlying.toString();
}
}
@@ -4,14 +4,9 @@ import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.MethodsUsageIndex;
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
import com.intellij.compilerOutputIndex.impl.bigram.BigramMethodsUsageIndex;
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodNameAndQualifier;
import com.intellij.codeInsight.completion.methodChains.search.service.OverridenMethodsService;
import com.intellij.codeInsight.completion.methodChains.search.service.SingletonService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -26,20 +21,12 @@ public class MethodChainsSearchService {
private final MethodsUsageIndex myMethodsUsageIndex;
private final BigramMethodsUsageIndex myBigramMethodsUsageIndex;
private final SingletonService mySingletonService;
private final OverridenMethodsService myOverridenMethodsService;
private final Project myProject;
private final Map<String, Boolean> mySingletonLocalCache;
public MethodChainsSearchService(final Project project) {
myOverridenMethodsService = new OverridenMethodsService(project);
myMethodsUsageIndex = MethodsUsageIndex.getInstance(project);
myBigramMethodsUsageIndex = BigramMethodsUsageIndex.getInstance(project);
mySingletonService = new SingletonService(project);
myProject = project;
mySingletonLocalCache = new HashMap<String, Boolean>();
mySingletonLocalCache.put(null, false);
}
public Project getProject() {
@@ -66,28 +53,7 @@ public class MethodChainsSearchService {
return EMPTY_SORTED_SET;
}
public boolean isSingleton(@NotNull final PsiClass psiClass, final String contextMethodName) {
return isSingleton(psiClass.getQualifiedName(), contextMethodName);
}
public boolean isSingleton(@Nullable final String typeQName, final String methodName) {
Boolean isSingleton = mySingletonLocalCache.get(typeQName);
if (isSingleton == null) {
isSingleton = mySingletonService.isSingleton(typeQName, methodName);
mySingletonLocalCache.put(typeQName, isSingleton);
}
return isSingleton;
}
public boolean isRelevantMethodForField(@NotNull final String className, @NotNull final String methodName) {
final Pair<Integer, Integer> occurrences =
myOverridenMethodsService.getMethodUsageInFieldContext(new MethodNameAndQualifier(methodName, className));
return occurrences.getFirst() > occurrences.getSecond();
}
public boolean isRelevantMethodForNotOverriden(@NotNull final String className, @NotNull final String methodName) {
final Pair<Integer, Integer> occurrences =
myOverridenMethodsService.getMethodsUsageInOverridenContext(new MethodNameAndQualifier(methodName, className));
return occurrences.getFirst() < occurrences.getSecond();
public PsiManager getPsiManager() {
return PsiManager.getInstance(getProject());
}
}
@@ -1,7 +1,9 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
@@ -15,27 +17,26 @@ import static com.intellij.util.containers.ContainerUtil.reverse;
public class MethodsChain {
private final List<PsiMethod[]> myRevertedPath;
private final int myWeight;
//
// chain qualifier class could be different with method.getContainingClass()
private final String myQualifierClassName;
public MethodsChain(final PsiMethod[] methods, final int weight) {
this(ContainerUtil.<PsiMethod[]>newArrayList(methods), weight);
public MethodsChain(final PsiMethod[] methods, final int weight, final String qualifierClassName) {
this(ContainerUtil.<PsiMethod[]>newArrayList(methods), weight, qualifierClassName);
}
public MethodsChain(final List<PsiMethod[]> revertedPath, final int weight) {
public MethodsChain(final List<PsiMethod[]> revertedPath, final int weight, final String qualifierClassName) {
myRevertedPath = revertedPath;
myWeight = weight;
myQualifierClassName = qualifierClassName;
}
public int size() {
return myRevertedPath.size();
}
public boolean isStaticChain() {
return myRevertedPath.get(myRevertedPath.size() - 1)[0].hasModifierProperty(PsiModifier.STATIC);
}
@Nullable
public PsiClass getFirstQualifierClass() {
return myRevertedPath.isEmpty() ? null : myRevertedPath.get(myRevertedPath.size() - 1)[0].getContainingClass();
public String getQualifierClassName() {
return myQualifierClassName;
}
@Nullable
@@ -51,43 +52,11 @@ public class MethodsChain {
return myWeight;
}
public MethodsChain addEdge(final PsiMethod[] psiMethods) {
public MethodsChain addEdge(final PsiMethod[] psiMethods, final String newQualifierClassName, final int newWeight) {
final List<PsiMethod[]> newRevertedPath = new ArrayList<PsiMethod[]>(myRevertedPath.size() + 1);
newRevertedPath.addAll(myRevertedPath);
newRevertedPath.add(psiMethods);
return new MethodsChain(newRevertedPath, myWeight);
}
/**
* checking only method names
*/
public boolean weakContains(final MethodsChain otherChain) {
if (otherChain.myRevertedPath.isEmpty()) {
return true;
}
if (myRevertedPath.isEmpty()) {
return false;
}
final Iterator<PsiMethod[]> otherChainIterator = otherChain.myRevertedPath.iterator();
String otherChainCurrentName = otherChainIterator.next()[0].getName();
boolean checkingStarted = false;
for (final PsiMethod[] methods : myRevertedPath) {
final String thisCurrentName = methods[0].getName();
if (!checkingStarted && thisCurrentName.equals(otherChainCurrentName)) {
checkingStarted = true;
}
if (checkingStarted) {
if (otherChainIterator.hasNext()) {
otherChainCurrentName = otherChainIterator.next()[0].getName();
if (!otherChainCurrentName.equals(thisCurrentName)) {
return false;
}
} else {
return false;
}
}
}
return !otherChainIterator.hasNext();
return new MethodsChain(newRevertedPath, newWeight, newQualifierClassName);
}
@Override
@@ -95,34 +64,8 @@ public class MethodsChain {
return StringUtil.join(myRevertedPath, "<-");
}
public static Set<Set<String>> edgeCombinations(final Set<Set<String>> oldCombinations,
final MethodsChain methodsChain) {
if (oldCombinations.isEmpty()) {
final Set<Set<String>> result = new HashSet<Set<String>>(methodsChain.myRevertedPath.size());
for (final PsiMethod[] e : methodsChain.myRevertedPath) {
final Set<String> set = new HashSet<String>();
set.add(e[0].getName());
result.add(set);
}
return result;
} else {
final Set<Set<String>> newTail = new HashSet<Set<String>>(oldCombinations.size() * methodsChain.size());
for (final PsiMethod[] e : methodsChain.myRevertedPath) {
final String methodName = e[0].getName();
for (final Set<String> tailSet : oldCombinations) {
final Set<String> newSet = new HashSet<String>(tailSet);
newSet.add(methodName);
if (!oldCombinations.contains(newSet)) {
newTail.add(newSet);
}
}
}
return newTail;
}
}
@SuppressWarnings("ConstantConditions")
public static CompareResult compare(final MethodsChain left, final MethodsChain right) {
public static CompareResult compare(final MethodsChain left, final MethodsChain right, final ChainCompletionContext context) {
if (left.size() == 0) {
return CompareResult.RIGHT_CONTAINS_LEFT;
}
@@ -132,13 +75,14 @@ public class MethodsChain {
final Iterator<PsiMethod[]> leftIterator = left.myRevertedPath.iterator();
final Iterator<PsiMethod[]> rightIterator = right.myRevertedPath.iterator();
final PsiManager psiManager = PsiManager.getInstance(left.getFirstQualifierClass().getProject());
while (leftIterator.hasNext() && rightIterator.hasNext()) {
final PsiMethod thisNext = leftIterator.next()[0];
final PsiMethod thatNext = rightIterator.next()[0];
if (thisNext == null || thatNext == null) {
throw new NullPointerException();
}
if (((thisNext.isConstructor() != thatNext.isConstructor()))
|| !thisNext.getName().equals(thatNext.getName())
|| !psiManager.areElementsEquivalent(thisNext.getContainingClass(), thatNext.getContainingClass())) {
|| !thisNext.getName().equals(thatNext.getName())) {
return CompareResult.NOT_EQUAL;
}
}
@@ -148,7 +92,23 @@ public class MethodsChain {
if (!leftIterator.hasNext() && rightIterator.hasNext()) {
return CompareResult.RIGHT_CONTAINS_LEFT;
}
return CompareResult.EQUAL;
final PsiClass leftQualifier = JavaPsiFacade.getInstance(context.getProject()).findClass(left.getQualifierClassName(), context.getResolveScope());
final PsiClass rightQualifier = JavaPsiFacade.getInstance(context.getProject()).findClass(left.getQualifierClassName(), context.getResolveScope());
return hasBaseClass(leftQualifier, rightQualifier, PsiManager.getInstance(context.getProject())) ? CompareResult.EQUAL : CompareResult.NOT_EQUAL;
}
private static boolean hasBaseClass(final PsiClass left, final PsiClass right, final PsiManager psiManager) {
//todo so slow
final Set<PsiClass> leftSupers = InheritanceUtil.getSuperClasses(left);
final Set<PsiClass> rightSupers = InheritanceUtil.getSuperClasses(right);
for (final PsiClass leftSuper : leftSupers) {
if (!CommonClassNames.JAVA_LANG_OBJECT.equals(leftSuper.getQualifiedName()) && rightSupers.contains(leftSuper)) {
return true;
}
}
return false;
}
public enum CompareResult {
@@ -56,7 +56,7 @@ public class MethodsChainLookupRangingHelper {
Boolean isFirstMethodStatic = null;
Boolean hasCallingVariableInContext = null;
LookupElement chainLookupElement = null;
PsiClass newVariableClass = null;
final NullableNotNullManager nullableNotNullManager = NullableNotNullManager.getInstance(context.getProject());
for (final PsiMethod[] psiMethods : chain.getPath()) {
@@ -68,33 +68,51 @@ public class MethodsChainLookupRangingHelper {
if (isFirstMethodStatic == null) {
isFirstMethodStatic = psiMethods[0].hasModifierProperty(PsiModifier.STATIC);
}
final MethodProcResult procResult =
processMethod(method, context, lastMethodWeight, chainLookupElement == null, nullableNotNullManager);
final PsiClass qualifierClass;
final boolean isHead = chainLookupElement == null;
if (isHead) {
final String qualifierClassName = chain.getQualifierClassName();
qualifierClass = JavaPsiFacade.getInstance(context.getProject()).
findClass(qualifierClassName, context.getResolveScope());
}
else {
qualifierClass = null;
}
final MethodProcResult procResult = processMethod(method, qualifierClass, context, lastMethodWeight, isHead, nullableNotNullManager);
if (procResult == null) {
return null;
}
if (hasCallingVariableInContext == null) {
hasCallingVariableInContext = procResult.hasCallingVariableInContext();
}
if (isHead && procResult.isIntroduceNewVariable()) {
newVariableClass = qualifierClass;
}
unreachableParametersCount += procResult.getUnreachableParametersCount();
notMatchedStringVars += procResult.getNotMatchedStringVars();
chainLookupElement = chainLookupElement == null
? procResult.getLookupElement()
: new JavaChainLookupElement(chainLookupElement, procResult.getLookupElement());
chainLookupElement = isHead ? procResult.getLookupElement() : new JavaChainLookupElement(chainLookupElement, procResult.getLookupElement());
}
final ChainRelevance relevance = new ChainRelevance(chainSize,
lastMethodWeight,
unreachableParametersCount,
notMatchedStringVars,
hasCallingVariableInContext,
isFirstMethodStatic);
if (newVariableClass != null) {
chainLookupElement = ChainCompletionNewVariableLookupElement.create(newVariableClass, chainLookupElement);
}
final ChainRelevance relevance =
new ChainRelevance(chainSize,
lastMethodWeight,
unreachableParametersCount,
notMatchedStringVars,
hasCallingVariableInContext,
isFirstMethodStatic);
return new WeightableChainLookupElement(chainLookupElement, relevance);
}
@Nullable
private static MethodProcResult processMethod(@NotNull final PsiMethod method,
@Nullable final PsiClass qualifierClass,
final ChainCompletionContext context,
final int weight,
final boolean isHeadMethod,
@@ -102,6 +120,7 @@ public class MethodsChainLookupRangingHelper {
int unreachableParametersCount = 0;
int notMatchedStringVars = 0;
boolean hasCallingVariableInContext = false;
boolean introduceNewVariable = false;
final PsiParameterList parameterList = method.getParameterList();
final TIntObjectHashMap<SubLookupElement> parametersMap = new TIntObjectHashMap<SubLookupElement>(parameterList.getParametersCount());
final PsiParameter[] parameters = parameterList.getParameters();
@@ -161,8 +180,8 @@ public class MethodsChainLookupRangingHelper {
return null;
}
else {
final PsiClass containingClass = method.getContainingClass();
final String classQName = containingClass.getQualifiedName();
@SuppressWarnings("ConstantConditions")
final String classQName = qualifierClass.getQualifiedName();
if (classQName == null) return null;
final Object e = ContainerUtil.getFirstItem(context.getContextRefElements(classQName), null);
if (e != null) {
@@ -182,16 +201,18 @@ public class MethodsChainLookupRangingHelper {
}
lookupElement = new JavaChainLookupElement(firstChainElement, createLookupElement(method, parametersMap));
}
else lookupElement = context.getContainingClassQNames().contains(classQName)
? createLookupElement(method, parametersMap)
: new JavaChainLookupElement(ChainCompletionNewVariableLookupElement.create(containingClass),
createLookupElement(method, parametersMap));
else {
lookupElement = createLookupElement(method, parametersMap);
if (!context.getContainingClassQNames().contains(classQName)) {
introduceNewVariable = true;
}
}
}
}
else {
lookupElement = createLookupElement(method, parametersMap);
}
return new MethodProcResult(lookupElement, unreachableParametersCount, notMatchedStringVars, hasCallingVariableInContext);
return new MethodProcResult(lookupElement, unreachableParametersCount, notMatchedStringVars, hasCallingVariableInContext, introduceNewVariable);
}
private static class MethodProcResult {
@@ -199,15 +220,22 @@ public class MethodsChainLookupRangingHelper {
private final int myUnreachableParametersCount;
private final int myNotMatchedStringVars;
private final boolean myHasCallingVariableInContext;
private final boolean myIntroduceNewVariable;
private MethodProcResult(final LookupElement methodLookup,
final int unreachableParametersCount,
final int notMatchedStringVars,
final boolean hasCallingVariableInContext) {
final boolean hasCallingVariableInContext,
final boolean introduceNewVariable) {
myMethodLookup = methodLookup;
myUnreachableParametersCount = unreachableParametersCount;
myNotMatchedStringVars = notMatchedStringVars;
myHasCallingVariableInContext = hasCallingVariableInContext;
myIntroduceNewVariable = introduceNewVariable;
}
private boolean isIntroduceNewVariable() {
return myIntroduceNewVariable;
}
private boolean hasCallingVariableInContext() {
@@ -0,0 +1,82 @@
package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import java.util.NavigableSet;
import java.util.TreeSet;
/**
* @author Dmitry Batkovich
*/
public class ParametersMatcher {
public static MatchResult matchParameters(final MethodsChain chain, final ChainCompletionContext context) {
MatchResult overallResult = EMPTY;
for (final PsiMethod[] methods : chain.getPath()) {
final NavigableSet<MatchResult> matchResults = new TreeSet<MatchResult>();
for (final PsiMethod method : methods) {
matchResults.add(matchParameters(method, context));
}
final MatchResult best = matchResults.first();
overallResult = overallResult.add(best);
}
return overallResult;
}
public static MatchResult matchParameters(final PsiMethod method, final ChainCompletionContext context) {
int matched = 0;
int unMatched = 0;
for (final PsiParameter parameter : method.getParameterList().getParameters()) {
final PsiType type = parameter.getType();
if (context.contains(type.getCanonicalText()) || type instanceof PsiPrimitiveType) {
matched++;
}
else {
unMatched++;
}
}
return new MatchResult(matched, unMatched);
}
private static final MatchResult EMPTY = new MatchResult(0, 0);
public static class MatchResult implements Comparable<MatchResult> {
private final int myMatched;
private final int myUnMatched;
private MatchResult(final int matched, final int unMatched) {
myMatched = matched;
myUnMatched = unMatched;
}
public int getMatched() {
return myMatched;
}
public int getUnMatched() {
return myUnMatched;
}
public MatchResult add(final MatchResult other) {
return new MatchResult(getMatched() + other.getMatched(), getUnMatched() + other.getUnMatched());
}
public boolean noUnmatched() {
return myUnMatched == 0;
}
@Override
public int compareTo(@NotNull final MatchResult other) {
final int sub = getUnMatched() - other.getUnMatched();
if (sub != 0) {
return sub;
}
return getMatched() - other.getMatched();
}
}
}
@@ -3,11 +3,8 @@ package com.intellij.codeInsight.completion.methodChains.search;
import com.intellij.codeInsight.completion.methodChains.Constants;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.util.containers.FactoryMap;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -15,9 +12,8 @@ import java.util.*;
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class SearchInitializer {
private final List<WeightAware<MethodIncompleteSignature>> myVertexes;
private final List<WeightAware<MethodIncompleteSignature>> myVertices;
private final LinkedHashMap<MethodIncompleteSignature, MethodsChain> myChains;
private final Map<MethodIncompleteSignature, Integer> myOccurrencesMap;
private final FactoryMap<MethodIncompleteSignature, PsiMethod[]> myResolver;
public SearchInitializer(final SortedSet<UsageIndexValue> indexValues,
@@ -26,9 +22,8 @@ public class SearchInitializer {
final Set<String> excludedParamsTypesQNames) {
myResolver = resolver;
final int size = indexValues.size();
myVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size);
myVertices = new ArrayList<WeightAware<MethodIncompleteSignature>>(size);
myChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size);
myOccurrencesMap = new HashMap<MethodIncompleteSignature, Integer>(size);
add(indexValues, MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName));
}
@@ -39,7 +34,8 @@ public class SearchInitializer {
final int occurrences = indexValue.getOccurrences();
if (bestOccurrences == -1) {
bestOccurrences = occurrences;
} else if (bestOccurrences > occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO) {
}
else if (bestOccurrences > occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO) {
return;
}
}
@@ -51,65 +47,40 @@ public class SearchInitializer {
final PsiMethod[] psiMethods = myResolver.get(methodInvocation);
if (psiMethods.length != 0 && MethodChainsSearchUtil.checkParametersForTypesQNames(psiMethods, excludedParamsTypesQNames)) {
final int occurrences = indexValue.getOccurrences();
final MethodsChain methodsChain = new MethodsChain(psiMethods, occurrences);
final MethodsChain methodsChain = new MethodsChain(psiMethods, occurrences, indexValue.getMethodIncompleteSignature().getOwner());
myChains.put(methodInvocation, methodsChain);
myVertexes.add(new WeightAware<MethodIncompleteSignature>(methodInvocation, occurrences));
myOccurrencesMap.put(methodInvocation, occurrences);
myVertices.add(new WeightAware<MethodIncompleteSignature>(methodInvocation, occurrences));
return true;
}
return false;
}
public InitResult init(final Set<String> excludedEdgeNames,
final Set<String> contextQNames,
final MethodChainsSearchService searchService,
final String contextMethodName) {
final int size = myVertexes.size();
int bestOccurrences = 0;
MethodsChain bestTargetMethodChain = null;
public InitResult init(final Set<String> excludedEdgeNames) {
final int size = myVertices.size();
final List<WeightAware<MethodIncompleteSignature>> initedVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size);
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> initedChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size);
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> initedChains =
new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size);
final Iterator<Map.Entry<MethodIncompleteSignature, MethodsChain>> chainsIterator = myChains.entrySet().iterator();
for (final WeightAware<MethodIncompleteSignature> vertex : myVertexes) {
for (final WeightAware<MethodIncompleteSignature> vertex : myVertices) {
final Map.Entry<MethodIncompleteSignature, MethodsChain> chainEntry = chainsIterator.next();
final MethodIncompleteSignature method = vertex.getUnderlying();
if (!excludedEdgeNames.contains(method.getName())) {
initedVertexes.add(vertex);
final MethodsChain methodsChain = chainEntry.getValue();
initedChains.put(chainEntry.getKey(), methodsChain);
if (contextQNames.contains(method.getOwner())) {
final Integer occurrences = myOccurrencesMap.get(method);
if (occurrences > bestOccurrences) {
final PsiMethod oneOfFirst = methodsChain.getOneOfFirst();
if (oneOfFirst != null && oneOfFirst.hasModifierProperty(PsiModifier.STATIC)) {
bestTargetMethodChain = methodsChain;
bestOccurrences = occurrences;
continue;
}
final PsiClass firstQualifierClass = methodsChain.getFirstQualifierClass();
if (firstQualifierClass != null && (searchService.isSingleton(firstQualifierClass, contextMethodName)
|| contextQNames.contains(firstQualifierClass.getQualifiedName()))) {
bestTargetMethodChain = methodsChain;
bestOccurrences = occurrences;
}
}
}
}
}
return new InitResult(initedVertexes, initedChains, bestTargetMethodChain);
return new InitResult(initedVertexes, initedChains);
}
public static class InitResult {
private final List<WeightAware<MethodIncompleteSignature>> myVertexes;
private final LinkedHashMap<MethodIncompleteSignature, MethodsChain> myChains;
private final MethodsChain myCurrentBestTargetChain;
private InitResult(final List<WeightAware<MethodIncompleteSignature>> vertexes,
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> chains,
final @Nullable MethodsChain currentBestTargetChain) {
this.myVertexes = vertexes;
this.myChains = chains;
this.myCurrentBestTargetChain = currentBestTargetChain;
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> chains) {
myVertexes = vertexes;
myChains = chains;
}
public List<WeightAware<MethodIncompleteSignature>> getVertexes() {
@@ -119,10 +90,5 @@ public class SearchInitializer {
public LinkedHashMap<MethodIncompleteSignature, MethodsChain> getChains() {
return myChains;
}
@Nullable
public MethodsChain getCurrentBestTargetChain() {
return myCurrentBestTargetChain;
}
}
}
@@ -24,6 +24,10 @@ public class WeightAware<V> implements Comparable<WeightAware<V>> {
@Override
public int compareTo(@NotNull final WeightAware<V> that) {
return -getWeight() + that.getWeight();
final int sub = -getWeight() + that.getWeight();
if (sub != 0) {
return sub;
}
return myUnderlying.hashCode() - that.myUnderlying.hashCode();
}
}
@@ -1,65 +0,0 @@
package com.intellij.codeInsight.completion.methodChains.search.service;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.callingLocation.CallingLocation;
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodCallingLocationIndex;
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodNameAndQualifier;
import com.intellij.compilerOutputIndex.impl.callingLocation.VariableType;
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickInheritanceIndex;
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickMethodsIndex;
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickOverrideUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
/**
* @author Dmitry Batkovich
*/
public class OverridenMethodsService {
private final QuickMethodsIndex myQuickMethodsIndex;
private final QuickInheritanceIndex myQuickInheritanceIndex;
private final MethodCallingLocationIndex myMethodCallingLocationIndex;
public OverridenMethodsService(final Project project) {
myQuickInheritanceIndex = QuickInheritanceIndex.getInstance(project);
myQuickMethodsIndex = QuickMethodsIndex.getInstance(project);
myMethodCallingLocationIndex = MethodCallingLocationIndex.getInstance(project);
}
public boolean isMethodOverriden(final String classQName, final String methodName) {
return QuickOverrideUtil.isMethodOverriden(classQName, methodName, myQuickInheritanceIndex, myQuickMethodsIndex);
}
public Pair<Integer, Integer> getMethodsUsageInOverridenContext(final MethodNameAndQualifier method) {
final Multiset<MethodIncompleteSignature> locationsAsParam = myMethodCallingLocationIndex.getLocationsAsParam(method);
int overridenOccurrences = 0;
int nonOverridenOccurrences = 0;
for (final Multiset.Entry<MethodIncompleteSignature> e : locationsAsParam.entrySet()) {
final MethodIncompleteSignature sign = e.getElement();
final boolean methodOverriden = isMethodOverriden(sign.getOwner(), sign.getName());
if (methodOverriden) {
overridenOccurrences++;
}
else {
nonOverridenOccurrences++;
}
}
return Pair.create(overridenOccurrences, nonOverridenOccurrences);
}
public Pair<Integer, Integer> getMethodUsageInFieldContext(final MethodNameAndQualifier method) {
int asField = 0;
int notField = 0;
for (final CallingLocation callingLocation : myMethodCallingLocationIndex.getAllLocations(method)) {
if (callingLocation.getVariableType().equals(VariableType.FIELD)) {
asField++;
}
else {
notField++;
}
}
return Pair.create(asField, notField);
}
}
@@ -1,89 +0,0 @@
package com.intellij.codeInsight.completion.methodChains.search.service;
import com.intellij.codeInsight.completion.methodChains.Constants;
import com.intellij.compilerOutputIndex.impl.singleton.MethodShortSignatureWithWeight;
import com.intellij.compilerOutputIndex.impl.singleton.ParamsInMethodOccurrencesIndex;
import com.intellij.compilerOutputIndex.impl.singleton.TwinVariablesIndex;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.CommonClassNames;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class SingletonService {
private final TwinVariablesIndex myTwinVariablesIndex;
private final ParamsInMethodOccurrencesIndex myParamsInMethodOccurrencesIndex;
private final GlobalSearchScope myAllScope;
private final JavaPsiFacade myJavaPsiFacade;
private final Map<String, Boolean> myLocalCache;
public SingletonService(final Project project) {
myTwinVariablesIndex = TwinVariablesIndex.getInstance(project);
myParamsInMethodOccurrencesIndex = ParamsInMethodOccurrencesIndex.getInstance(project);
myAllScope = GlobalSearchScope.allScope(project);
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
myLocalCache = new HashMap<String, Boolean>();
myLocalCache.put(null, false);
}
public boolean isSingleton(@Nullable final String typeQName, final @NotNull String contextMethodName) {
final Boolean isSingletonCached = myLocalCache.get(typeQName);
if (isSingletonCached == null) {
assert typeQName != null;
final PsiClass aClass = myJavaPsiFacade.findClass(typeQName, myAllScope);
if (aClass == null) {
myLocalCache.put(typeQName, false);
return false;
}
for (final PsiClass psiClass : aClass.getInterfaces()) {
final String qualifiedName = psiClass.getQualifiedName();
if (CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName) || !isSingleton(qualifiedName, contextMethodName)) {
myLocalCache.put(typeQName, false);
return false;
}
}
final boolean isSingleton = hasTwinsFeature(typeQName) && isSuitableTypeFor(typeQName, contextMethodName);
myLocalCache.put(typeQName, isSingleton);
return isSingleton;
}
return isSingletonCached;
}
public boolean hasTwinsFeature(final String typeQName) {
final List<Integer> twinInfo = myTwinVariablesIndex.getTwinInfo(typeQName);
if (twinInfo.isEmpty()) {
return false;
}
int ones = 0;
for (final int i : twinInfo) {
if (i == 1) {
ones++;
}
}
return (twinInfo.size() - ones) * Constants.SINGLETON_MAGIC_RATIO < twinInfo.size();
}
private boolean isSuitableTypeFor(final String typeName, final String methodName) {
final Pair<List<MethodShortSignatureWithWeight>, Integer> parameterOccurrences =
myParamsInMethodOccurrencesIndex.getParameterOccurrences(typeName);
if (parameterOccurrences.getSecond() == 0) {
return true;
}
final List<MethodShortSignatureWithWeight> contextMethods = parameterOccurrences.getFirst();
final MethodShortSignatureWithWeight last = contextMethods.get(contextMethods.size() - 1);
return last.getMethodShortSignature().getName().equals(methodName) || last.getWeight() * Constants.SINGLETON_MAGIC_RATIO2 <= parameterOccurrences.getSecond();
}
}
@@ -9,14 +9,14 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.IOUtil;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.tree.ClassNode;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.intellij.util.indexing.IndexInfrastructure.*;
@@ -30,115 +30,114 @@ public abstract class CompilerOutputBaseIndex<K, V> {
private final static Logger LOG = Logger.getInstance(CompilerOutputBaseIndex.class);
private final KeyDescriptor<K> myKeyDescriptor;
private final DataExternalizer<V> myValueExternalizer;
protected volatile MapReduceIndex<K, V, ClassNode> myIndex;
protected volatile MapReduceIndex<K, V, ClassReader> myIndex;
protected final Project myProject;
private volatile Project myProject;
protected volatile AtomicBoolean myInitialized = new AtomicBoolean(false);
public CompilerOutputBaseIndex(final KeyDescriptor<K> keyDescriptor, final DataExternalizer<V> valueExternalizer) {
public CompilerOutputBaseIndex(final KeyDescriptor<K> keyDescriptor, final DataExternalizer<V> valueExternalizer, final Project project) {
myProject = project;
myKeyDescriptor = keyDescriptor;
myValueExternalizer = valueExternalizer;
}
public final boolean init(final Project project) {
myProject = project;
final MapReduceIndex<K, V, ClassReader> index;
final Ref<Boolean> rewriteIndex = new Ref<Boolean>(false);
try {
final ID<K, V> indexId = getIndexId();
if (!IndexInfrastructure.getIndexRootDir(indexId).exists()) {
rewriteIndex.set(true);
}
final File storageFile = getStorageFile(indexId);
MapIndexStorage<K, V> indexStorage = null;
for(int i = 0; i < 2; ++i) {
try {
indexStorage = new MapIndexStorage<K, V>(storageFile, myKeyDescriptor, myValueExternalizer, 1024);
} catch (IOException ex) {
if (i == 1) throw ex;
IOUtil.deleteAllFilesStartingWith(storageFile);
public final boolean initIfNeed() {
if (myInitialized.compareAndSet(false, true)) {
final MapReduceIndex<K, V, ClassNode> index;
final Ref<Boolean> rewriteIndex = new Ref<Boolean>(false);
try {
final ID<K, V> indexId = getIndexId();
if (!IndexInfrastructure.getIndexRootDir(indexId).exists()) {
rewriteIndex.set(true);
}
}
assert indexStorage != null;
index = new MapReduceIndex<K, V, ClassReader>(indexId, getIndexer(), indexStorage);
final MapIndexStorage<K, V> finalIndexStorage = indexStorage;
index.setInputIdToDataKeysIndex(new Factory<PersistentHashMap<Integer, Collection<K>>>() {
@Override
public PersistentHashMap<Integer, Collection<K>> create() {
Exception failCause = null;
for (int attempts = 0; attempts < 2; attempts++) {
try {
return FileBasedIndexImpl.createIdToDataKeysIndex(indexId, myKeyDescriptor, new MemoryIndexStorage<K, V>(finalIndexStorage));
final File storageFile = IndexInfrastructure.getStorageFile(indexId);
final MapIndexStorage<K, V> indexStorage = new MapIndexStorage<K, V>(storageFile, myKeyDescriptor, myValueExternalizer, 1024);
index = new MapReduceIndex<K, V, ClassNode>(indexId, getIndexer(), indexStorage);
index.setInputIdToDataKeysIndex(new Factory<PersistentHashMap<Integer, Collection<K>>>() {
@Override
public PersistentHashMap<Integer, Collection<K>> create() {
Exception failCause = null;
for (int attempts = 0; attempts < 2; attempts++) {
try {
return FileBasedIndexImpl.createIdToDataKeysIndex(indexId, myKeyDescriptor, new MemoryIndexStorage<K, V>(indexStorage));
}
catch (IOException e) {
failCause = e;
FileUtil.delete(IndexInfrastructure.getInputIndexStorageFile(getIndexId()));
rewriteIndex.set(true);
}
}
catch (IOException e) {
failCause = e;
FileUtil.delete(getInputIndexStorageFile(getIndexId()));
rewriteIndex.set(true);
throw new RuntimeException("couldn't create index", failCause);
}
});
final File versionFile = getVersionFile(indexId);
if (versionFile.exists()) {
if (versionDiffers(versionFile, getVersion())) {
rewriteVersion(versionFile, getVersion());
rewriteIndex.set(true);
try {
LOG.info("clearing index for updating index version");
index.clear();
}
catch (StorageException e) {
LOG.error("couldn't clear index for reinitializing", e);
throw new RuntimeException(e);
}
}
throw new RuntimeException("couldn't create index", failCause);
}
});
final File versionFile = getVersionFile(indexId);
if (versionFile.exists()) {
if (versionDiffers(versionFile, getVersion())) {
else if (versionFile.createNewFile()) {
rewriteVersion(versionFile, getVersion());
rewriteIndex.set(true);
try {
LOG.info("clearing index for updating index version");
index.clear();
}
catch (StorageException e) {
LOG.error("couldn't clear index for reinitializing");
throw new RuntimeException(e);
}
}
else {
LOG.error(String.format("problems while access to index version file to index %s ", indexId));
}
}
else if (versionFile.createNewFile()) {
rewriteVersion(versionFile, getVersion());
rewriteIndex.set(true);
}
else {
LOG.error(String.format("problems while access to index version file to index %s ", indexId));
catch (IOException e) {
LOG.error("couldn't initialize index", e);
throw new RuntimeException(e);
}
myIndex = index;
return rewriteIndex.get();
}
catch (IOException e) {
LOG.error("couldn't initialize index", e);
throw new RuntimeException(e);
else {
return false;
}
myIndex = index;
return rewriteIndex.get();
}
protected abstract ID<K, V> getIndexId();
protected abstract int getVersion();
protected abstract DataIndexer<K, V, ClassReader> getIndexer();
protected abstract DataIndexer<K, V, ClassNode> getIndexer();
public final void projectClosed() {
if (myIndex != null) {
try {
myIndex.flush();
public final void closeIfInitialized() {
if (myInitialized.get()) {
if (myIndex != null) {
try {
myIndex.flush();
}
catch (StorageException ignored) {
}
myIndex.dispose();
}
catch (StorageException ignored) {
}
myIndex.dispose();
}
}
public void update(final int id, final ClassReader classReader) {
Boolean result = myIndex.update(id, classReader).compute();
public final void update(final int id, final ClassNode inputData) {
final Boolean result = myIndex.update(id, inputData).compute();
if (result == Boolean.FALSE) throw new RuntimeException();
}
public void clear() {
try {
myIndex.clear();
}
catch (StorageException e) {
throw new RuntimeException(e);
public final void clearIfInitialized() {
if (myInitialized.get()) {
try {
myIndex.clear();
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2000-2013 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 com.intellij.compilerOutputIndex.api.indexer;
import com.intellij.compilerOutputIndex.impl.MethodsUsageIndex;
import com.intellij.compilerOutputIndex.impl.bigram.BigramMethodsUsageIndex;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryValue;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
/**
* @author Dmitry Batkovich
*/
@SuppressWarnings("unchecked")
public enum CompilerOutputIndexFeature {
METHOD_CHAINS_COMPLETION("completion.enable.relevant.method.chain.suggestions", ContainerUtil
.<Class<? extends CompilerOutputBaseIndex>>newArrayList(BigramMethodsUsageIndex.class, MethodsUsageIndex.class));
@NotNull
private final String myKey;
@NotNull
private final Collection<Class<? extends CompilerOutputBaseIndex>> myRequiredIndexes;
CompilerOutputIndexFeature(@NotNull final String key,
@NotNull final Collection<Class<? extends CompilerOutputBaseIndex>> requiredIndexes) {
myKey = key;
myRequiredIndexes = requiredIndexes;
}
CompilerOutputIndexFeature(@NotNull final String key, @NotNull final Class<? extends CompilerOutputBaseIndex> requiredIndex) {
this(key, Collections.<Class<? extends CompilerOutputBaseIndex>>singleton(requiredIndex));
}
public RegistryValue getRegistryValue() {
return Registry.get(myKey);
}
public boolean isEnabled() {
return Registry.is(myKey);
}
public void enable() {
getRegistryValue().setValue(true);
}
public void disable() {
getRegistryValue().setValue(false);
}
@NotNull
public Collection<Class<? extends CompilerOutputBaseIndex>> getRequiredIndexes() {
return myRequiredIndexes;
}
}
@@ -2,34 +2,41 @@ package com.intellij.compilerOutputIndex.api.indexer;
import com.intellij.compilerOutputIndex.api.fs.CompilerOutputFilesUtil;
import com.intellij.compilerOutputIndex.api.fs.FileVisitorService;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompilationStatusAdapter;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileTask;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.registry.RegistryValue;
import com.intellij.openapi.util.registry.RegistryValueListener;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ConcurrentHashSet;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.IndexInfrastructure;
import com.intellij.util.io.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.PersistentEnumeratorDelegate;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.ClassNode;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -40,18 +47,17 @@ import java.util.concurrent.locks.ReentrantLock;
public class CompilerOutputIndexer extends AbstractProjectComponent {
private final static Logger LOG = Logger.getInstance(CompilerOutputIndexer.class);
public final static String REGISTRY_KEY = "completion.enable.relevant.method.chain.suggestions";
public final static String TITLE = "Compiler output indexer in progress...";
private volatile CompilerOutputBaseIndex[] myIndexes;
private volatile Map<String, CompilerOutputBaseIndex> myIndexTypeQNameToIndex;
private final Map<String, CompilerOutputBaseIndex> myIndexTypeQNameToIndex = new HashMap<String, CompilerOutputBaseIndex>();
private volatile PersistentHashMap<String, Long> myFileTimestampsIndex;
private volatile PersistentEnumeratorDelegate<String> myFileEnumerator;
private volatile boolean myInitialized = false;
private final Lock myLock = new ReentrantLock();
private final AtomicBoolean myInProgress = new AtomicBoolean(false);
private volatile boolean myEnabled = false;
@SuppressWarnings("SetReplaceableByEnumSet")
private final Set<CompilerOutputIndexFeature> myCurrentEnabledFeatures = new ConcurrentHashSet<CompilerOutputIndexFeature>();
private final AtomicBoolean myInitialized = new AtomicBoolean(false);
public static CompilerOutputIndexer getInstance(final Project project) {
return project.getComponent(CompilerOutputIndexer.class);
@@ -61,63 +67,112 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
super(project);
}
public boolean isEnabled() {
return myEnabled;
}
private ID<String, Long> getFileTimestampsIndexId() {
return CompilerOutputIndexUtil.generateIndexId("ProjectCompilerOutputClassFilesTimestamps", myProject);
}
@Override
public final void projectOpened() {
Registry.get(REGISTRY_KEY).addListener(new RegistryValueListener.Adapter() {
@Override
public void afterValueChanged(final RegistryValue value) {
final boolean asBoolean = value.asBoolean();
myEnabled = asBoolean;
if (asBoolean) {
doEnable();
for (final CompilerOutputIndexFeature feature : CompilerOutputIndexFeature.values()) {
final RegistryValue registryValue = feature.getRegistryValue();
registryValue.addListener(new RegistryValueListener.Adapter() {
@Override
public void afterValueChanged(final RegistryValue rawValue) {
final Collection<Class<? extends CompilerOutputBaseIndex>> requiredIndexes = feature.getRequiredIndexes();
if (rawValue.asBoolean()) {
if (myCurrentEnabledFeatures.add(feature)) {
if (myCurrentEnabledFeatures.size() == 1) {
doEnable();
}
addIndexes(requiredIndexes);
}
}
else {
removeIndexes(requiredIndexes);
myCurrentEnabledFeatures.remove(feature);
}
}
}, myProject);
if (registryValue.asBoolean()) {
if (myCurrentEnabledFeatures.add(feature)) {
if (myCurrentEnabledFeatures.size() == 1) {
doEnable();
}
addIndexes(feature.getRequiredIndexes());
}
}
}, myProject);
}
}
myEnabled = Registry.is(REGISTRY_KEY);
if (myEnabled) {
doEnable();
private CompilerOutputBaseIndex[] getAllIndexes() {
return Extensions.getExtensions(CompilerOutputBaseIndex.EXTENSION_POINT_NAME, myProject);
}
private void addIndexes(final Collection<Class<? extends CompilerOutputBaseIndex>> indexes) {
final Collection<CompilerOutputBaseIndex> indexesToReindex = new ArrayList<CompilerOutputBaseIndex>();
for (final Class<? extends CompilerOutputBaseIndex> indexClass : indexes) {
final String canonicalName = indexClass.getCanonicalName();
if (!myIndexTypeQNameToIndex.containsKey(canonicalName)) {
final CompilerOutputBaseIndex index = Extensions.findExtension(CompilerOutputBaseIndex.EXTENSION_POINT_NAME, myProject, indexClass);
myIndexTypeQNameToIndex.put(canonicalName, index);
if (index.initIfNeed()) {
indexesToReindex.add(index);
}
}
}
if (!indexesToReindex.isEmpty()) {
if (myInProgress.compareAndSet(false, true)) {
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, TITLE) {
@Override
public void onCancel() {
myInProgress.set(false);
}
@Override
public void onSuccess() {
myInProgress.set(false);
}
@Override
public void run(@NotNull final ProgressIndicator indicator) {
reindex(new FileVisitorService.ProjectClassFiles(CompilerOutputIndexer.this.myProject), indexesToReindex, true, indicator);
}
});
}
}
}
private void removeIndexes(final Collection<Class<? extends CompilerOutputBaseIndex>> indexes) {
final Set<Class<? extends CompilerOutputBaseIndex>> toRemove = ContainerUtil.newHashSet(indexes);
for (final CompilerOutputIndexFeature feature : CompilerOutputIndexFeature.values()) {
if (feature.getRegistryValue().asBoolean()) {
for (final Class<? extends CompilerOutputBaseIndex> aClass : feature.getRequiredIndexes()) {
toRemove.remove(aClass);
}
}
}
for (final Class aClass : toRemove) {
myIndexTypeQNameToIndex.remove(aClass.getCanonicalName());
}
}
private void doEnable() {
if (!myInitialized) {
myIndexes = Extensions.getExtensions(CompilerOutputBaseIndex.EXTENSION_POINT_NAME, myProject);
myIndexTypeQNameToIndex = new HashMap<String, CompilerOutputBaseIndex>();
boolean needReindex = false;
for (final CompilerOutputBaseIndex index : myIndexes) {
if (index.init(myProject)) {
needReindex = true;
}
myIndexTypeQNameToIndex.put(index.getClass().getCanonicalName(), index);
if (myInitialized.compareAndSet(false, true)) {
initTimestampIndex();
try {
myFileEnumerator = new PersistentEnumeratorDelegate<String>(
IndexInfrastructure.getStorageFile(CompilerOutputIndexUtil.generateIndexId("compilerOutputIndexFileId.enum", myProject)),
new EnumeratorStringDescriptor(), 2048);
}
initTimestampIndex(needReindex);
File storageFile =
IndexInfrastructure.getStorageFile(CompilerOutputIndexUtil.generateIndexId("compilerOutputIndexFileId.enum", myProject));
for(int i = 0; i < 2; ++i) {
try {
myFileEnumerator = new PersistentEnumeratorDelegate<String>(
storageFile,
new EnumeratorStringDescriptor(), 2048);
} catch (IOException e) {
if (i == 1) throw new RuntimeException(e);
IOUtil.deleteAllFilesStartingWith(storageFile);
}
catch (IOException e) {
throw new RuntimeException(e);
}
CompilerManager.getInstance(myProject).addCompilationStatusListener(new CompilationStatusAdapter() {
@Override
public void fileGenerated(final String outputRoot, final String relativePath) {
if (StringUtil.endsWith(relativePath, CompilerOutputFilesUtil.CLASS_FILES_SUFFIX) && myEnabled) {
if (StringUtil.endsWith(relativePath, CompilerOutputFilesUtil.CLASS_FILES_SUFFIX) && !myCurrentEnabledFeatures.isEmpty()) {
try {
doIndexing(new File(outputRoot, relativePath), null);
doIndexing(new File(outputRoot, relativePath), myIndexTypeQNameToIndex.values(), false, null);
}
catch (ProcessCanceledException e0) {
throw e0;
@@ -128,17 +183,10 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
}
}
}, myProject);
if (needReindex) {
reindexAllProjectInBackground();
}
myInitialized = true;
}
}
private void initTimestampIndex(final boolean needReindex) {
if (needReindex) {
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
}
private void initTimestampIndex() {
for (int attempts = 0; attempts < 2; attempts++) {
try {
myFileTimestampsIndex = new PersistentHashMap<String, Long>(IndexInfrastructure.getStorageFile(getFileTimestampsIndexId()),
@@ -164,8 +212,14 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
throw new RuntimeException("Timestamps index not initialized");
}
public void reindex(final FileVisitorService visitorService, final @NotNull ProgressIndicator indicator) {
reindex(visitorService, myIndexTypeQNameToIndex.values(), false, indicator);
}
public void reindex(final FileVisitorService visitorService, @NotNull final ProgressIndicator indicator) {
private void reindex(final FileVisitorService visitorService,
final @NotNull Collection<CompilerOutputBaseIndex> indexes,
final boolean force,
final @NotNull ProgressIndicator indicator) {
myLock.lock();
try {
indicator.setText(TITLE);
@@ -173,7 +227,7 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
@Override
public void consume(final File file) {
try {
doIndexing(file, indicator);
doIndexing(file, indexes, force, indicator);
}
catch (ProcessCanceledException e0) {
throw e0;
@@ -189,35 +243,11 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
}
}
public void reindexAllProjectInBackground() {
if (myInProgress.compareAndSet(false, true)) {
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, TITLE) {
@Override
public void onCancel() {
myIndexTypeQNameToIndex.clear();
myInProgress.set(false);
}
@Override
public void onSuccess() {
myInProgress.set(false);
}
@Override
public void run(@NotNull final ProgressIndicator indicator) {
reindexAllProject(indicator);
}
});
}
}
public void reindexAllProject(@NotNull final ProgressIndicator indicator) {
reindex(new FileVisitorService.ProjectClassFiles(myProject), indicator);
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
private void doIndexing(@NotNull final File file, @Nullable final ProgressIndicator indicator) {
private void doIndexing(@NotNull final File file,
@NotNull final Collection<CompilerOutputBaseIndex> indexes,
final boolean force,
@Nullable final ProgressIndicator indicator) {
final String filePath;
try {
filePath = file.getCanonicalPath();
@@ -226,16 +256,17 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
LOG.error(e);
return;
}
final Long timestamp = getTimestamp(filePath);
final Long timestamp;
ProgressManager.checkCanceled();
final long currentTimeStamp = file.lastModified();
if (timestamp == null || timestamp != currentTimeStamp) {
if (force || (timestamp = getTimestamp(filePath)) == null || timestamp != currentTimeStamp) {
putTimestamp(filePath, currentTimeStamp);
final ClassReader reader;
final ClassNode inputData = new ClassNode(Opcodes.ASM4);
InputStream is = null;
try {
is = new FileInputStream(file);
reader = new ClassReader(is);
final ClassReader reader = new ClassReader(is);
reader.accept(inputData, ClassReader.EXPAND_FRAMES);
}
catch (IOException e) {
removeTimestamp(filePath);
@@ -255,8 +286,8 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
indicator.setText2(filePath);
}
final int id = myFileEnumerator.enumerate(filePath);
for (final CompilerOutputBaseIndex index : myIndexes) {
index.update(id, reader);
for (final CompilerOutputBaseIndex index : indexes) {
index.update(id, inputData);
}
}
catch (RuntimeException e) {
@@ -275,9 +306,9 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
catch (IOException e) {
throw new RuntimeException(e);
}
initTimestampIndex(true);
for (final CompilerOutputBaseIndex index : myIndexes) {
index.clear();
initTimestampIndex();
for (final CompilerOutputBaseIndex index : getAllIndexes()) {
index.clearIfInitialized();
}
}
@@ -313,9 +344,9 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
@Override
public void projectClosed() {
if (myInitialized) {
for (final CompilerOutputBaseIndex index : myIndexes) {
index.projectClosed();
if (myInitialized.get()) {
for (final CompilerOutputBaseIndex index : getAllIndexes()) {
index.closeIfInitialized();
}
try {
myFileTimestampsIndex.close();
@@ -329,12 +360,15 @@ public class CompilerOutputIndexer extends AbstractProjectComponent {
@TestOnly
public void removeIndexes() {
for (final CompilerOutputBaseIndex index : myIndexes) {
for (final CompilerOutputBaseIndex index : getAllIndexes()) {
FileUtil.delete(IndexInfrastructure.getIndexRootDir(index.getIndexId()));
}
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
}
/**
* try to find index with corresponding class only in currently enabled indexes
*/
@SuppressWarnings("unchecked")
public <T extends CompilerOutputBaseIndex> T getIndex(final Class<T> tClass) {
final CompilerOutputBaseIndex index = myIndexTypeQNameToIndex.get(tClass.getCanonicalName());
@@ -6,6 +6,7 @@ import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.ClassNode;
import java.util.ArrayList;
import java.util.List;
@@ -16,13 +17,13 @@ import java.util.List;
public class ClassFileData {
private final List<MethodData> myMethodDatas;
public ClassFileData(final ClassReader classReader) {
this(classReader, true);
public ClassFileData(final ClassNode classNode) {
this(classNode, true);
}
public ClassFileData(final ClassReader classReader, final boolean checkForPrimitiveReturn) {
public ClassFileData(final ClassNode classNode, final boolean checkForPrimitiveReturn) {
myMethodDatas = new ArrayList<MethodData>();
classReader.accept(new ClassVisitor(Opcodes.ASM4) {
classNode.accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public MethodVisitor visitMethod(final int access,
final String name,
@@ -33,7 +34,7 @@ public class ClassFileData {
myMethodDatas.add(methodDataAccumulator.getMethodData());
return methodDataAccumulator;
}
}, Opcodes.ASM4);
});
}
public List<MethodData> getMethodDatas() {
@@ -113,4 +114,4 @@ public class ClassFileData {
return myDesc;
}
}
}
}
@@ -3,6 +3,7 @@ package com.intellij.compilerOutputIndex.impl;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.KeyDescriptor;
@@ -14,8 +15,8 @@ import java.util.TreeSet;
*/
public abstract class CompilerOutputBaseGramsIndex<K> extends CompilerOutputBaseIndex<K, Multiset<MethodIncompleteSignature>> {
protected CompilerOutputBaseGramsIndex(final KeyDescriptor<K> keyDescriptor) {
super(keyDescriptor, new GuavaHashMultiSetExternalizer<MethodIncompleteSignature>(MethodIncompleteSignature.createKeyDescriptor()));
protected CompilerOutputBaseGramsIndex(final KeyDescriptor<K> keyDescriptor, final Project project) {
super(keyDescriptor, new GuavaHashMultiSetExternalizer<MethodIncompleteSignature>(MethodIncompleteSignature.createKeyDescriptor()), project);
}
public TreeSet<UsageIndexValue> getValues(final K key) {
@@ -12,6 +12,7 @@ import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.ClassNode;
import java.util.HashMap;
import java.util.Map;
@@ -26,16 +27,16 @@ public class MethodsUsageIndex extends CompilerOutputBaseGramsIndex<String> {
return CompilerOutputIndexer.getInstance(project).getIndex(MethodsUsageIndex.class);
}
public MethodsUsageIndex() {
super(new EnumeratorStringDescriptor());
public MethodsUsageIndex(final Project project) {
super(new EnumeratorStringDescriptor(), project);
}
@Override
protected DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassReader> getIndexer() {
return new DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassReader>() {
protected DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassNode> getIndexer() {
return new DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassNode>() {
@NotNull
@Override
public Map<String, Multiset<MethodIncompleteSignature>> map(final ClassReader inputData) {
public Map<String, Multiset<MethodIncompleteSignature>> map(final ClassNode inputData) {
final Map<String, Multiset<MethodIncompleteSignature>> map = new HashMap<String, Multiset<MethodIncompleteSignature>>();
for (final ClassFileData.MethodData data : new ClassFileData(inputData).getMethodDatas()) {
for (final ClassFileData.MethodInsnSignature ms : data.getMethodInsnSignatures()) {
@@ -59,18 +60,9 @@ public class MethodsUsageIndex extends CompilerOutputBaseGramsIndex<String> {
};
}
public void clear() {
try {
myIndex.clear();
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected ID<String, Multiset<MethodIncompleteSignature>> getIndexId() {
return generateIndexId(MethodsUsageIndex.class);
return generateIndexId("MethodsUsage");
}
@Override
@@ -88,4 +80,4 @@ public class MethodsUsageIndex extends CompilerOutputBaseGramsIndex<String> {
}
occurrences.add(mi);
}
}
}
@@ -15,6 +15,7 @@ import com.intellij.util.indexing.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.ClassNode;
import java.util.*;
@@ -26,13 +27,13 @@ public class BigramMethodsUsageIndex extends CompilerOutputBaseGramsIndex<Method
return CompilerOutputIndexer.getInstance(project).getIndex(BigramMethodsUsageIndex.class);
}
public BigramMethodsUsageIndex() {
super(MethodIncompleteSignature.createKeyDescriptor());
public BigramMethodsUsageIndex( final Project project) {
super(MethodIncompleteSignature.createKeyDescriptor(), project);
}
@Override
protected ID<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> getIndexId() {
return generateIndexId(BigramMethodsUsageIndex.class);
return generateIndexId("BigramMethodsUsage");
}
@Override
@@ -41,14 +42,14 @@ public class BigramMethodsUsageIndex extends CompilerOutputBaseGramsIndex<Method
}
@Override
protected DataIndexer<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>, ClassReader> getIndexer() {
protected DataIndexer<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>,ClassNode> getIndexer() {
//
// not fair way, but works fast
//
return new DataIndexer<MethodIncompleteSignature,Multiset<MethodIncompleteSignature>,ClassReader>() {
return new DataIndexer<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>, ClassNode>() {
@NotNull
@Override
public Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map(final ClassReader inputData) {
public Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map(final ClassNode inputData) {
final Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map =
new HashMap<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>>();
for (final ClassFileData.MethodData data : new ClassFileData(inputData).getMethodDatas()) {
@@ -1,71 +0,0 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class CallingLocation {
@NotNull
private final MethodIncompleteSignature myMethodIncompleteSignature;
@NotNull
private final VariableType myVariableType;
public CallingLocation(@NotNull final MethodIncompleteSignature methodIncompleteSignature, @NotNull final VariableType variableType) {
myMethodIncompleteSignature = methodIncompleteSignature;
myVariableType = variableType;
}
@NotNull
public MethodIncompleteSignature getMethodIncompleteSignature() {
return myMethodIncompleteSignature;
}
@NotNull
public VariableType getVariableType() {
return myVariableType;
}
public static DataExternalizer<CallingLocation> createDataExternalizer() {
final KeyDescriptor<MethodIncompleteSignature> methodIncompleteSignatureKeyDescriptor = MethodIncompleteSignature.createKeyDescriptor();
return new DataExternalizer<CallingLocation>() {
@Override
public void save(final DataOutput out, final CallingLocation value) throws IOException {
methodIncompleteSignatureKeyDescriptor.save(out, value.getMethodIncompleteSignature());
VariableType.KEY_DESCRIPTOR.save(out, value.getVariableType());
}
@Override
public CallingLocation read(final DataInput in) throws IOException {
return new CallingLocation(methodIncompleteSignatureKeyDescriptor.read(in), VariableType.KEY_DESCRIPTOR.read(in));
}
};
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final CallingLocation that = (CallingLocation)o;
if (!myMethodIncompleteSignature.equals(that.myMethodIncompleteSignature)) return false;
if (myVariableType != that.myVariableType) return false;
return true;
}
@Override
public int hashCode() {
int result = myMethodIncompleteSignature.hashCode();
result = 31 * result + myVariableType.hashCode();
return result;
}
}
@@ -1,157 +0,0 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import org.jetbrains.asm4.commons.AnalyzerAdapter;
import org.jetbrains.asm4.commons.JSRInlinerAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class MethodCallingLocationExtractor {
private MethodCallingLocationExtractor() {
}
public static Map<MethodNameAndQualifier, List<CallingLocation>> extract(final ClassReader classReader) {
final MyClassVisitor classVisitor = new MyClassVisitor();
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
return classVisitor.getExtractedMethodsCallings();
}
private static class MyClassVisitor extends ClassVisitor {
public MyClassVisitor() {
super(Opcodes.ASM4);
}
private final Map<MethodNameAndQualifier, List<CallingLocation>> myExtractedMethodsCallings =
new HashMap<MethodNameAndQualifier, List<CallingLocation>>();
private String myClassName;
private String myRawClassName;
private Map<MethodNameAndQualifier, List<CallingLocation>> getExtractedMethodsCallings() {
return myExtractedMethodsCallings;
}
@Override
public void visit(final int version,
final int access,
final String className,
final String signature,
final String superName,
final String[] interfaces) {
myRawClassName = className;
myClassName = AsmUtil.getQualifiedClassName(className);
}
@Nullable
@Override
public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
return null;
}
@Nullable
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if (name.charAt(0) == '<') {
return null;
}
final boolean isStaticMethod = AsmUtil.isStaticMethodDeclaration(access);
if (isStaticMethod) {
return null;
}
@SuppressWarnings("UnnecessaryLocalVariable") final String methodName = name;
final String[] methodParams = AsmUtil.getParamsTypes(desc);
final MethodIncompleteSignature currentMethodSignature =
new MethodIncompleteSignature(myClassName, AsmUtil.getReturnType(desc), methodName, isStaticMethod);
return new JSRInlinerAdapter(new AnalyzerAdapter(Opcodes.ASM4, myRawClassName, access, name, desc, null) {
private final Map<Integer, Variable> myFieldsAndParamsPositionInStack = new HashMap<Integer, Variable>();
@Override
public void visitInsn(final int opcode) {
super.visitInsn(opcode);
}
@Override
public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
boolean onThis = false;
if (stack != null && opcode == Opcodes.GETFIELD && !ChainCompletionStringUtil.isPrimitiveOrArray(AsmUtil.getReturnType(desc))) {
final Object objectRef = stack.get(stack.size() - 1);
if (objectRef instanceof String && objectRef.equals(myRawClassName)) {
onThis = true;
}
}
super.visitFieldInsn(opcode, owner, name, desc);
if (onThis) {
final int index = stack.size() - 1;
final Object marker = stack.get(index);
myFieldsAndParamsPositionInStack.put(index, new Variable(marker, VariableType.FIELD));
}
}
@Override
public void visitVarInsn(final int opcode, final int varIndex) {
super.visitVarInsn(opcode, varIndex);
if (stack != null && opcode == Opcodes.ALOAD &&
varIndex > 0 &&
varIndex <= methodParams.length &&
!ChainCompletionStringUtil.isPrimitiveOrArray(methodParams[varIndex - 1])) {
final int stackPos = stack.size() - 1;
myFieldsAndParamsPositionInStack.put(stackPos, new Variable(stack.get(stackPos), VariableType.METHOD_PARAMETER));
}
}
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
if (stack != null && opcode != Opcodes.INVOKESTATIC && !methodName.startsWith("<")) {
final int index = stack.size() - 1 - AsmUtil.getParamsTypes(desc).length;
final Object stackValue = stack.get(index);
final Variable variable = myFieldsAndParamsPositionInStack.get(index);
if (variable != null && variable.getMarker() == stackValue /*equality by reference is not mistake*/) {
final CallingLocation callingLocation = new CallingLocation(currentMethodSignature, variable.getVariableType());
final MethodNameAndQualifier invokedMethod = new MethodNameAndQualifier(name, AsmUtil.getQualifiedClassName(owner));
List<CallingLocation> callingLocations = myExtractedMethodsCallings.get(invokedMethod);
if (callingLocations == null) {
callingLocations = new ArrayList<CallingLocation>();
myExtractedMethodsCallings.put(invokedMethod, callingLocations);
}
callingLocations.add(callingLocation);
}
}
super.visitMethodInsn(opcode, owner, name, desc);
}
}, access, name, desc, signature, exceptions);
}
}
private static class Variable {
private final Object myMarker;
private final VariableType myVariableType;
private Variable(final Object marker, final VariableType variableType) {
myMarker = marker;
myVariableType = variableType;
}
private Object getMarker() {
return myMarker;
}
private VariableType getVariableType() {
return myVariableType;
}
}
}
@@ -1,94 +0,0 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.descriptor.ArrayListDataExternalizer;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class MethodCallingLocationIndex extends CompilerOutputBaseIndex<MethodNameAndQualifier, List<CallingLocation>> {
public static MethodCallingLocationIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(MethodCallingLocationIndex.class);
}
public MethodCallingLocationIndex() {
super(MethodNameAndQualifier.createKeyDescriptor(),
new ArrayListDataExternalizer<CallingLocation>(CallingLocation.createDataExternalizer()));
}
@Override
protected ID<MethodNameAndQualifier, List<CallingLocation>> getIndexId() {
return generateIndexId(MethodCallingLocationIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
public List<CallingLocation> getAllLocations(final MethodNameAndQualifier methodNameAndQualifier) {
try {
final List<CallingLocation> result = new ArrayList<CallingLocation>();
myIndex.getData(methodNameAndQualifier).forEach(new ValueContainer.ContainerAction<List<CallingLocation>>() {
@Override
public boolean perform(final int id, final List<CallingLocation> values) {
result.addAll(values);
return true;
}
});
return result;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
public Multiset<MethodIncompleteSignature> getLocationsAsParam(final MethodNameAndQualifier methodNameAndQualifier) {
final Multiset<MethodIncompleteSignature> result = HashMultiset.create();
try {
myIndex.getData(methodNameAndQualifier).forEach(new ValueContainer.ContainerAction<List<CallingLocation>>() {
@Override
public boolean perform(final int id, final List<CallingLocation> values) {
for (final CallingLocation value : values) {
if (value.getVariableType().equals(VariableType.METHOD_PARAMETER)) {
result.add(value.getMethodIncompleteSignature());
}
}
return true;
}
});
}
catch (StorageException e) {
throw new RuntimeException(e);
}
return result;
}
@Override
protected DataIndexer<MethodNameAndQualifier, List<CallingLocation>, ClassReader> getIndexer() {
return new DataIndexer<MethodNameAndQualifier, List<CallingLocation>, ClassReader>() {
@NotNull
@Override
public Map<MethodNameAndQualifier, List<CallingLocation>> map(final ClassReader inputData) {
return MethodCallingLocationExtractor.extract(inputData);
}
};
}
}
@@ -1,81 +0,0 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Dmitry Batkovich
*/
public class MethodNameAndQualifier {
@NotNull
private final String myMethodName;
@NotNull
private final String myQualifierName;
public MethodNameAndQualifier(@NotNull final String methodName, @NotNull final String qualifierName) {
myMethodName = methodName;
myQualifierName = qualifierName;
}
@NotNull
public String getMethodName() {
return myMethodName;
}
@NotNull
public String getQualifierName() {
return myQualifierName;
}
public static KeyDescriptor<MethodNameAndQualifier> createKeyDescriptor() {
final DataExternalizer<String> stringDataExternalizer = new EnumeratorStringDescriptor();
return new KeyDescriptor<MethodNameAndQualifier>() {
@Override
public void save(final DataOutput out, final MethodNameAndQualifier value) throws IOException {
stringDataExternalizer.save(out, value.myMethodName);
stringDataExternalizer.save(out, value.myQualifierName);
}
@Override
public MethodNameAndQualifier read(final DataInput in) throws IOException {
return new MethodNameAndQualifier(stringDataExternalizer.read(in), stringDataExternalizer.read(in));
}
@Override
public int getHashCode(final MethodNameAndQualifier value) {
return value.hashCode();
}
@Override
public boolean isEqual(final MethodNameAndQualifier val1, final MethodNameAndQualifier val2) {
return val1.equals(val2);
}
};
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MethodNameAndQualifier that = (MethodNameAndQualifier)o;
if (!myMethodName.equals(that.myMethodName)) return false;
if (!myQualifierName.equals(that.myQualifierName)) return false;
return true;
}
@Override
public int hashCode() {
int result = myMethodName.hashCode();
result = 31 * result + myQualifierName.hashCode();
return result;
}
}
@@ -1,15 +0,0 @@
package com.intellij.compilerOutputIndex.impl.callingLocation;
import com.intellij.util.io.EnumDataDescriptor;
import com.intellij.util.io.KeyDescriptor;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public enum VariableType {
FIELD,
METHOD_PARAMETER,
OTHER;
public static final KeyDescriptor<VariableType> KEY_DESCRIPTOR = new EnumDataDescriptor<VariableType>(VariableType.class);
}
@@ -1,96 +0,0 @@
package com.intellij.compilerOutputIndex.impl.quickInheritance;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.CommonClassNames;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.compilerOutputIndex.api.descriptor.HashSetDataExternalizer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.Type;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class QuickInheritanceIndex extends CompilerOutputBaseIndex<String, Set<String>> {
public static QuickInheritanceIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(QuickInheritanceIndex.class);
}
public QuickInheritanceIndex() {
super(new EnumeratorStringDescriptor(), new HashSetDataExternalizer<String>(new EnumeratorStringDescriptor()));
}
@Override
protected ID<String, Set<String>> getIndexId() {
return generateIndexId(QuickInheritanceIndex.class);
}
protected Set<String> getSupers(final String classQName) {
try {
final ValueContainer<Set<String>> valueContainer = myIndex.getData(classQName);
final Ref<Set<String>> setRef = Ref.create();
valueContainer.forEach(new ValueContainer.ContainerAction<Set<String>>() {
@Override
public boolean perform(final int id, final Set<String> value) {
setRef.set(value);
return false;
}
});
final Set<String> supers = setRef.get();
if (supers == null) {
return Collections.emptySet();
}
return supers;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected int getVersion() {
return 0;
}
@Override
protected DataIndexer<String, Set<String>, ClassReader> getIndexer() {
return new DataIndexer<String, Set<String>, ClassReader>() {
@NotNull
@Override
public Map<String, Set<String>> map(final ClassReader inputData) {
final Map<String, Set<String>> map = new HashMap<String, Set<String>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public void visit(final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
final String className = Type.getObjectType(name).getClassName();
if (className != null) {
final HashSet<String> value = ContainerUtil.newHashSet(AsmUtil.getQualifiedClassNames(interfaces, superName));
value.remove(CommonClassNames.JAVA_LANG_OBJECT);
map.put(className, value);
}
}
}, Opcodes.ASM4);
return map;
}
};
}
}
@@ -1,103 +0,0 @@
package com.intellij.compilerOutputIndex.impl.quickInheritance;
import com.intellij.compilerOutputIndex.api.descriptor.HashSetDataExternalizer;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class QuickMethodsIndex extends CompilerOutputBaseIndex<String, Set<String>> {
public static QuickMethodsIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(QuickMethodsIndex.class);
}
public QuickMethodsIndex() {
super(new EnumeratorStringDescriptor(), new HashSetDataExternalizer<String>(new EnumeratorStringDescriptor()));
}
@Override
protected ID<String, Set<String>> getIndexId() {
return generateIndexId(QuickMethodsIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
protected Set<String> getMethodsNames(final String classQName) {
final Ref<Set<String>> methodsRef = Ref.create();
try {
myIndex.getData(classQName).forEach(new ValueContainer.ContainerAction<Set<String>>() {
@Override
public boolean perform(final int id, final Set<String> value) {
methodsRef.set(value);
return true;
}
});
final Set<String> methods = methodsRef.get();
return methods == null ? Collections.<String>emptySet() : methods;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected DataIndexer<String, Set<String>, ClassReader> getIndexer() {
return new DataIndexer<String, Set<String>, ClassReader>() {
@NotNull
@Override
public Map<String, Set<String>> map(final ClassReader inputData) {
final Map<String, Set<String>> map = new HashMap<String, Set<String>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
private String myClassName;
private final HashSet<String> myMethodNames = new HashSet<String>();
@Override
public void visit(final int i, final int i2, final String name, final String s2, final String s3, final String[] strings) {
myClassName = AsmUtil.getQualifiedClassName(name);
}
@Override
public void visitEnd() {
map.put(myClassName, myMethodNames);
}
@Nullable
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String sign,
final String[] exceptions) {
if ((access & Opcodes.ACC_STATIC) == 0) {
myMethodNames.add(name);
}
return null;
}
}, Opcodes.ASM4);
return map;
}
};
}
}
@@ -1,23 +0,0 @@
package com.intellij.compilerOutputIndex.impl.quickInheritance;
/**
* @author Dmitry Batkovich
*/
public final class QuickOverrideUtil {
private QuickOverrideUtil() {}
public static boolean isMethodOverriden(final String classQName, final String methodName,
final QuickInheritanceIndex quickInheritanceIndex,
final QuickMethodsIndex quickMethodsIndex) {
for (final String aSuper : quickInheritanceIndex.getSupers(classQName)) {
if (quickMethodsIndex.getMethodsNames(aSuper).contains(methodName)) {
return true;
}
if (isMethodOverriden(aSuper, methodName, quickInheritanceIndex, quickMethodsIndex)) {
return true;
}
}
return false;
}
}
@@ -1,71 +0,0 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* @author Dmitry Batkovich
*/
public class MethodShortSignature {
@NotNull
private final String myName;
@NotNull
private final String mySignature; //in raw asm type
public MethodShortSignature(final @NotNull String name, final @NotNull String signature) {
myName = name;
mySignature = signature;
}
@NotNull
public String getName() {
return myName;
}
@NotNull
public String getSignature() {
return mySignature;
}
public static DataExternalizer<MethodShortSignature> createDataExternalizer() {
final EnumeratorStringDescriptor stringDescriptor = new EnumeratorStringDescriptor();
return new DataExternalizer<MethodShortSignature>() {
@Override
public void save(final DataOutput out, final MethodShortSignature value) throws IOException {
stringDescriptor.save(out, value.getName());
stringDescriptor.save(out, value.getSignature());
}
@Override
public MethodShortSignature read(final DataInput in) throws IOException {
return new MethodShortSignature(stringDescriptor.read(in), stringDescriptor.read(in));
}
};
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MethodShortSignature that = (MethodShortSignature) o;
if (!myName.equals(that.myName)) return false;
if (!mySignature.equals(that.mySignature)) return false;
return true;
}
@Override
public int hashCode() {
int result = myName.hashCode();
result = 31 * result + mySignature.hashCode();
return result;
}
}
@@ -1,53 +0,0 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
/**
* @author Dmitry Batkovich
*/
public class MethodShortSignatureWithWeight {
private final MethodShortSignature myMethodShortSignature;
private final int myWeight;
public MethodShortSignatureWithWeight(final MethodShortSignature methodShortSignature, final int weight) {
myMethodShortSignature = methodShortSignature;
myWeight = weight;
}
public MethodShortSignature getMethodShortSignature() {
return myMethodShortSignature;
}
public int getWeight() {
return myWeight;
}
public static Comparator<MethodShortSignatureWithWeight> COMPARATOR = new Comparator<MethodShortSignatureWithWeight>() {
@Override
public int compare(final MethodShortSignatureWithWeight o1, final MethodShortSignatureWithWeight o2) {
return o1.getWeight() - o2.getWeight();
}
} ;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MethodShortSignatureWithWeight that = (MethodShortSignatureWithWeight) o;
if (myWeight != that.myWeight) return false;
if (!myMethodShortSignature.equals(that.myMethodShortSignature)) return false;
return true;
}
@Override
public int hashCode() {
int result = myMethodShortSignature.hashCode();
result = 31 * result + myWeight;
return result;
}
}
@@ -1,116 +0,0 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.impl.GuavaHashMultiSetExternalizer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class ParamsInMethodOccurrencesIndex extends CompilerOutputBaseIndex<String, Multiset<MethodShortSignature>> {
public static ParamsInMethodOccurrencesIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(ParamsInMethodOccurrencesIndex.class);
}
public ParamsInMethodOccurrencesIndex() {
super(new EnumeratorStringDescriptor(), new GuavaHashMultiSetExternalizer<MethodShortSignature>(MethodShortSignature.createDataExternalizer()));
}
@Override
protected ID<String, Multiset<MethodShortSignature>> getIndexId() {
return generateIndexId(ParamsInMethodOccurrencesIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
public Pair<List<MethodShortSignatureWithWeight>, Integer> getParameterOccurrences(final String parameterTypeName) {
try {
final Multiset<MethodShortSignature> resultAsMultiset = HashMultiset.create();
final ValueContainer<Multiset<MethodShortSignature>> valueContainer = myIndex.getData(parameterTypeName);
valueContainer.forEach(new ValueContainer.ContainerAction<Multiset<MethodShortSignature>>() {
@Override
public boolean perform(final int id, final Multiset<MethodShortSignature> localMap) {
for (final Multiset.Entry<MethodShortSignature> e : localMap.entrySet()) {
resultAsMultiset.add(e.getElement(), e.getCount());
}
return true;
}
});
final List<MethodShortSignatureWithWeight> result = new ArrayList<MethodShortSignatureWithWeight>(resultAsMultiset.elementSet().size());
int sumWeight = 0;
for (final Multiset.Entry<MethodShortSignature> e : resultAsMultiset.entrySet()) {
sumWeight += e.getCount();
result.add(new MethodShortSignatureWithWeight(e.getElement(), e.getCount()));
}
Collections.sort(result, MethodShortSignatureWithWeight.COMPARATOR);
return Pair.create(result, sumWeight);
} catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected DataIndexer<String, Multiset<MethodShortSignature>, ClassReader> getIndexer() {
return new DataIndexer<String, Multiset<MethodShortSignature>, ClassReader>() {
@NotNull
@Override
public Map<String, Multiset<MethodShortSignature>> map(final ClassReader inputData) {
final Map<String, Multiset<MethodShortSignature>> result = new HashMap<String, Multiset<MethodShortSignature>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
@Nullable
@Override
public MethodVisitor visitMethod(final int i, final String name, final String desc, final String signature, final String[] exception) {
if (CompilerOutputIndexUtil.isSetterOrConstructorMethodName(name)) {
return null;
}
final String[] parameters = AsmUtil.getParamsTypes(desc);
final MethodShortSignature thisMethodShortSignature = new MethodShortSignature(name, desc);
for (final String parameter : parameters) {
Multiset<MethodShortSignature> methods = result.get(parameter);
if (methods == null) {
methods = HashMultiset.create();
result.put(parameter, methods);
}
methods.add(thisMethodShortSignature);
}
return new MethodVisitor(Opcodes.ASM4) {
@Override
public void visitLocalVariable(final String s, final String desc, final String signature, final Label label, final Label label2, final int i) {
final String varType = AsmUtil.getQualifiedClassName(desc);
Multiset<MethodShortSignature> methods = result.get(varType);
if (methods == null) {
methods = HashMultiset.create();
result.put(varType, methods);
}
methods.add(thisMethodShortSignature);
}
};
}
}, Opcodes.ASM4);
return result;
}
};
}
}
@@ -1,137 +0,0 @@
package com.intellij.compilerOutputIndex.impl.singleton;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.compilerOutputIndex.api.descriptor.ArrayListDataExternalizer;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.EnumeratorIntegerDescriptor;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
public class TwinVariablesIndex extends CompilerOutputBaseIndex<String, List<Integer>> {
public static TwinVariablesIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(TwinVariablesIndex.class);
}
public TwinVariablesIndex() {
super(new EnumeratorStringDescriptor(), new ArrayListDataExternalizer<Integer>(EnumeratorIntegerDescriptor.INSTANCE));
}
@Override
protected ID<String, List<Integer>> getIndexId() {
return generateIndexId(TwinVariablesIndex.class);
}
@Override
protected int getVersion() {
return 0;
}
@NotNull
public List<Integer> getTwinInfo(final String typeQName) {
try {
final ValueContainer<List<Integer>> valueContainer = myIndex.getData(typeQName);
final List<Integer> result = new ArrayList<Integer>(valueContainer.size());
valueContainer.forEach(new ValueContainer.ContainerAction<List<Integer>>() {
@Override
public boolean perform(final int id, final List<Integer> value) {
result.addAll(value);
return true;
}
});
return result;
}
catch (StorageException e) {
throw new RuntimeException(e);
}
}
@Override
protected DataIndexer<String, List<Integer>, ClassReader> getIndexer() {
return new DataIndexer<String, List<Integer>, ClassReader>() {
@NotNull
@Override
public Map<String, List<Integer>> map(final ClassReader inputData) {
final Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
@Nullable
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
if (CompilerOutputIndexUtil.isSetterOrConstructorMethodName(name)) {
return null;
}
final Multiset<String> myTypesOccurrences = HashMultiset.create();
final String[] paramsTypes = AsmUtil.getParamsTypes(desc);
Collections.addAll(myTypesOccurrences, paramsTypes);
return new MethodVisitor(Opcodes.ASM4) {
private final Set<String> myLocalVarNames = new HashSet<String>();
@SuppressWarnings("unchecked")
@Override
public void visitEnd() {
for (final Multiset.Entry<String> e: myTypesOccurrences.entrySet()) {
final String key = e.getElement();
if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(key)) {
List<Integer> values = map.get(key);
if (values == null) {
values = new ArrayList<Integer>();
map.put(key, values);
}
values.add(e.getCount());
}
}
}
private final Set<String> myUsedReadFieldsIndex = new HashSet<String>();
@Override
public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
final String fieldTypeQName = AsmUtil.getReturnType(desc);
if ((opcode == Opcodes.GETSTATIC || opcode == Opcodes.GETFIELD)) {
if (myUsedReadFieldsIndex.add(owner + name)) {
myTypesOccurrences.add(fieldTypeQName);
}
}
}
@Override
public void visitLocalVariable(final String name,
final String desc,
final String signature,
final Label start,
final Label end,
final int index) {
if (index > paramsTypes.length && myLocalVarNames.add(name)) {
final String type = AsmUtil.getReturnType(desc);
myTypesOccurrences.add(type);
}
}
};
}
}, Opcodes.ASM4);
return map;
}
};
}
}
@@ -0,0 +1,64 @@
import java.jang.String;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
class Some {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some1 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some2 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper() ;
h.getTag();
}
}
class Some3 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper() ;
h.getTag();
}
}
class Some4 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some5 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper() ;
h.getTag();
}
}
interface PsiElement {
}
class MethodJavaDocHelper {
public PsiElement getTag() {
return null;
}
}
public class TestCompletion {
public void method() {
PsiElement e = <caret>
}
}
@@ -0,0 +1,57 @@
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class TestIndex {
}
class Some {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some1 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some2 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some3 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some4 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
class Some5 {
public void m() {
MethodJavaDocHelper h = new MethodJavaDocHelper();
h.getTag();
}
}
interface PsiElement {
}
class MethodJavaDocHelper {
public PsiElement getTag() {
return null;
}
}
@@ -0,0 +1,15 @@
interface PsiMember {
PsiClass getContainingClass();
}
interface PsiMethod extends PsiMember {
}
interface PsiClass {}
public class TestCompletion {
public void method() {
PsiMethod psiMethod = <caret><selection>null</selection>;
PsiClass c = psiMethod.getContainingClass()
}
}
@@ -0,0 +1,14 @@
interface PsiMember {
PsiClass getContainingClass();
}
interface PsiMethod extends PsiMember {
}
interface PsiClass {}
public class TestCompletion {
public void method() {
PsiClass c = <caret>
}
}
@@ -0,0 +1,22 @@
public class TestIndex {
public void statMethod(PsiMethod m) {
m.getContainingClass();
m.getContainingClass();
m.getContainingClass();
m.getContainingClass();
m.getContainingClass();
m.getContainingClass();
m.getContainingClass();
m.getContainingClass();
}
}
interface PsiMember {
PsiClass getContainingClass();
}
interface PsiMethod extends PsiMember {
}
interface PsiClass {}
@@ -0,0 +1,48 @@
import java.jang.String;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
class CP1 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
class CP2 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
class CP3 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
class CP4 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
interface CompletionProvider {
void addCompletions(CompletionParameters p);
}
interface CompletionParameters {
PsiElement getPosition();
}
interface PsiElement {
}
public class TestCompletion {
public void method() {
PsiElement e = <caret>
}
}
@@ -0,0 +1,41 @@
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class TestIndex {
}
class CP1 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
class CP2 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
class CP3 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
class CP4 implements CompletionProvider {
public void addCompletions(CompletionParameters p) {
p.getPosition();
}
}
interface CompletionProvider {
void addCompletions(CompletionParameters p);
}
interface CompletionParameters {
PsiElement getPosition();
}
interface PsiElement {
}
@@ -0,0 +1,81 @@
interface PsiElement {
}
interface PsiFile {
PsiElement findElementAt(int index);
}
interface VirtualFile {
}
class PsiManager {
PsiFile findFile(VirtualFile vf) {
return null;
}
public static PsiManager getInstance() {
return null;
}
}
interface UnknownParameter {
}
class SomeManager {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager getInstance() {
return null;
}
}
class SomeManager1 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager1 getInstance() {
return null;
}
}
class SomeManager2 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager2 getInstance() {
return null;
}
}
class SomeManager3 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager3 getInstance() {
return null;
}
}
class SomeManager4 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager4 getInstance() {
return null;
}
}
public class TestCompletion {
public void method(VirtualFile f) {
PsiElement element = <caret>
}
}
@@ -0,0 +1,106 @@
public class TestIndex {
public void m1(VirtualFile f) {
PsiManager.getInstance().findFile(f).findElementAt(0);
PsiManager.getInstance().findFile(f).findElementAt(0);
PsiManager.getInstance().findFile(f).findElementAt(0);
PsiManager.getInstance().findFile(f).findElementAt(0);
}
public void m2(UnknownParameter p) {
SomeManager1.getInstance().findFile(p).findElementAt(0);
SomeManager1.getInstance().findFile(p).findElementAt(0);
SomeManager1.getInstance().findFile(p).findElementAt(0);
SomeManager1.getInstance().findFile(p).findElementAt(0);
SomeManager2.getInstance().findFile(p).findElementAt(0);
SomeManager2.getInstance().findFile(p).findElementAt(0);
SomeManager2.getInstance().findFile(p).findElementAt(0);
SomeManager2.getInstance().findFile(p).findElementAt(0);
SomeManager3.getInstance().findFile(p).findElementAt(0);
SomeManager3.getInstance().findFile(p).findElementAt(0);
SomeManager3.getInstance().findFile(p).findElementAt(0);
SomeManager3.getInstance().findFile(p).findElementAt(0);
SomeManager4.getInstance().findFile(p).findElementAt(0);
SomeManager4.getInstance().findFile(p).findElementAt(0);
SomeManager4.getInstance().findFile(p).findElementAt(0);
SomeManager4.getInstance().findFile(p).findElementAt(0);
}
}
interface PsiElement {
}
interface PsiFile {
PsiElement findElementAt(int index);
}
interface VirtualFile {
}
class PsiManager {
PsiFile findFile(VirtualFile vf) {
return null;
}
public static PsiManager getInstance() {
return null;
}
}
interface UnknownParameter {
}
class SomeManager {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager getInstance() {
return null;
}
}
class SomeManager1 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager1 getInstance() {
return null;
}
}
class SomeManager2 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager2 getInstance() {
return null;
}
}
class SomeManager3 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager3 getInstance() {
return null;
}
}
class SomeManager4 {
PsiFile findFile(UnknownParameter parameter) {
return null;
}
public static SomeManager4 getInstance() {
return null;
}
}
@@ -0,0 +1,17 @@
interface PsiManager {
Project getProject();
}
interface PsiElement {
Project getProject();
}
interface Project {}
public class TestCompletion {
public void method(PsiElement e, PsiManager m) {
Project p = <caret>
}
}
@@ -0,0 +1,25 @@
public class TestIndex {
public void statMethod(PsiManager m, PsiElement e) {
m.getProject();
m.getProject();
m.getProject();
m.getProject();
m.getProject();
e.getProject();
e.getProject();
e.getProject();
e.getProject();
e.getProject();
}
}
interface PsiManager {
Project getProject();
}
interface PsiElement {
Project getProject();
}
interface Project {}
@@ -0,0 +1,32 @@
public class TestCompletion {
void m() {
PsiFile f = <caret>
}
}
interface PsiFile {
}
interface PsiElement {
PsiFile getContainingFile();
}
class SomeUtil {
//representate some not frequently used methods
public static PsiFile get1() {
return null;
}
public static PsiFile get2() {
return null;
}
public static PsiFile get3() {
return null;
}
public static PsiFile get4() {
return null;
}
public static PsiFile get5() {
return null;
}
}
@@ -0,0 +1,56 @@
public class TestIndex {
void m(PsiElement e) {
//10
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
e.getContainingFile();
}
void m2() {
SomeUtil.get1();
SomeUtil.get2();
SomeUtil.get3();
SomeUtil.get4();
SomeUtil.get5();
}
}
interface PsiFile {
}
interface PsiElement {
PsiFile getContainingFile();
}
class SomeUtil {
//representate some not frequently used methods
public static PsiFile get1() {
return null;
}
public static PsiFile get2() {
return null;
}
public static PsiFile get3() {
return null;
}
public static PsiFile get4() {
return null;
}
public static PsiFile get5() {
return null;
}
}
@@ -1,53 +0,0 @@
import java.jang.String;
class B {
C getC() {
return null;
}
static B b = new B();
static B getB1() {
return b;
}
static B getB2() {
return b;
}
static B getB3() {
return b;
}
static B getB4() {
return b;
}
static B getB5() {
return b;
}
static B getB6() {
return b;
}
static B getB7() {
return b;
}
static B getB8() {
return b;
}
static B getB9() {
return b;
}
static B getB10() {
return b;
}
static B getB11() {
return b;
}
}
class C {}
public class TestCompletion {
public void method() {
C c = <caret>
}
}
@@ -1,104 +0,0 @@
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class TestIndex {
public void statMethod1() {
B.getB1().getC();
B.getB1().getC();
B.getB1().getC();
}
public void statMethod2() {
B.getB2().getC();
B.getB2().getC();
B.getB2().getC();
}
public void statMethod3() {
B.getB3().getC();
B.getB3().getC();
B.getB3().getC();
}
public void statMethod4() {
B.getB4().getC();
B.getB4().getC();
B.getB4().getC();
}
public void statMethod5() {
B.getB5().getC();
B.getB5().getC();
B.getB5().getC();
}
public void statMethod6() {
B.getB6().getC();
B.getB6().getC();
B.getB6().getC();
}
public void statMethod7() {
B.getB7().getC();
B.getB7().getC();
B.getB7().getC();
}
public void statMethod8() {
B.getB8().getC();
B.getB8().getC();
B.getB8().getC();
}
public void statMethod9() {
B.getB9().getC();
B.getB9().getC();
B.getB9().getC();
}
public void statMethod10() {
B.getB10().getC();
B.getB10().getC();
B.getB10().getC();
}
public void statMethod11() {
B.getB11().getC();
B.getB11().getC();
B.getB11().getC();
}
}
class B {
C getC() {
return null;
}
static B b = new B();
static B getB1() {
return b;
}
static B getB2() {
return b;
}
static B getB3() {
return b;
}
static B getB4() {
return b;
}
static B getB5() {
return b;
}
static B getB6() {
return b;
}
static B getB7() {
return b;
}
static B getB8() {
return b;
}
static B getB9() {
return b;
}
static B getB10() {
return b;
}
static B getB11() {
return b;
}
}
class C {}
@@ -8,7 +8,7 @@ interface Project {}
public class TestCompletion {
void m() {
PsiManager psiManager = null;
PsiManager psiManager = <selection><caret>null</selection>;
Project p = psiManager.getProject()
}
}
@@ -10,7 +10,7 @@ interface Project {}
public class TestCompletion {
void m() {
String asd = "123";
PsiManager psiManager = null;
PsiManager psiManager = <selection><caret>null</selection>;
Project p = psiManager.getProject(asd, zxc)
}
}
@@ -0,0 +1,14 @@
interface PsiElement {
PsiElement findElementAt(int offset);
}
interface PsiFile extends PsiElement {
}
public class TestCompletion(){
void m(){
PsiElement e = <caret>
}
}
@@ -0,0 +1,26 @@
public class TestIndex {
PsiFile f;
void m() {
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
f.findElementAt(0);
}
}
interface PsiElement {
PsiElement findElementAt(int offset);
}
interface PsiFile extends PsiElement {
}
@@ -7,10 +7,10 @@ import com.intellij.codeInsight.completion.methodChains.completion.lookup.Weight
import com.intellij.codeInsight.completion.methodChains.search.ChainRelevance;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.compilerOutputIndex.api.fs.FileVisitorService;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexFeature;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.mock.MockProgressIndicator;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.SmartList;
import java.io.File;
@@ -28,14 +28,14 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
@Override
protected void setUp() throws Exception {
Registry.get(CompilerOutputIndexer.REGISTRY_KEY).setValue(true);
super.setUp();
CompilerOutputIndexFeature.METHOD_CHAINS_COMPLETION.enable();
}
@Override
protected void tearDown() throws Exception {
CompilerOutputIndexFeature.METHOD_CHAINS_COMPLETION.disable();
super.tearDown();
Registry.get(CompilerOutputIndexer.REGISTRY_KEY).setValue(false);
}
protected String getTestDataPath() {
@@ -57,7 +57,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
public void testStaticMethodAndMethod() {
final List<WeightableChainLookupElement> elements = doCompletion();
assertEquals(elements.size(), 2);
assertEquals(String.valueOf(elements), elements.size(), 2);
assertAdvisorLookupElementEquals("findClass", 0, 3, 1, 1, elements.get(1));
assertAdvisorLookupElementEquals("m.getContainingClass", 0, 5, 1, 0, elements.get(0));
}
@@ -74,6 +74,10 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
assertAdvisorLookupElementEquals("m.getProject", 0, 8, 1, 0, assertOneElement(doCompletion()));
}
public void testMethodsWithParametersInContext() {
assertAdvisorLookupElementEquals("getInstance().findFile().findElementAt", 0, 4, 3, 0, assertOneElement(doCompletion()));
}
public void testMethodReturnsSubclassOfTargetClassNotShowed2() {
assertEmpty(doCompletion());
}
@@ -100,9 +104,16 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
assertAdvisorLookupElementEquals("getInstance().findFile().findElementAt", 2, 8, 3, 0, assertOneElement(collection));
}
public void _testReduceChain4() {
final List<WeightableChainLookupElement> collection = doCompletion();
assertAdvisorLookupElementEquals("b.getC", 0, 33, 1, 0, assertOneElement(collection));
public void testMethodWithNoQualifiedVariableInContext() {
assertOneElement(doCompletion());
}
public void testMethodIsNotRelevantForField() {
assertOneElement(doCompletion());
}
public void testNotRelevantMethodsFilteredInResult() {
assertOneElement(doCompletion());
}
public void testGetterInContext() {
@@ -125,17 +136,25 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
assertOneElement(doCompletion());
}
public void testMethodsWithSameNameWithoutSameParent() {
assertSize(2, doCompletion());
}
public void testResultQualifierNotSameWithTarget() {
assertEmpty(doCompletion());
}
public void testResultRelevance() {
final List<WeightableChainLookupElement> advisorWeightableChainLookupElements = doCompletion();
assertEquals("e.getContainingClass", advisorWeightableChainLookupElements.get(0).getLookupString());
assertEquals("getInstance().findClass", advisorWeightableChainLookupElements.get(1).getLookupString());
final List<WeightableChainLookupElement> weightableChainLookupElements = doCompletion();
assertEquals("e.getContainingClass", weightableChainLookupElements.get(0).getLookupString());
assertEquals("getInstance().findClass", weightableChainLookupElements.get(1).getLookupString());
}
public void testResultRelevance3() {
final List<WeightableChainLookupElement> advisorWeightableChainLookupElements = doCompletion();
assertSize(2, advisorWeightableChainLookupElements);
assertEquals("e.getProject1", advisorWeightableChainLookupElements.get(0).getLookupString());
assertEquals("psiManager.getProject", advisorWeightableChainLookupElements.get(1).getLookupString());
final List<WeightableChainLookupElement> weightableChainLookupElements = doCompletion();
assertSize(2, weightableChainLookupElements);
assertEquals("e.getProject1", weightableChainLookupElements.get(0).getLookupString());
assertEquals("psiManager.getProject", weightableChainLookupElements.get(1).getLookupString());
}
public void testRenderingVariableInContextAndNotInContext() {
@@ -150,6 +169,10 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
doTestRendering();
}
public void testMethodQualifierClass() {
doTestRendering();
}
public void assertAdvisorLookupElementEquals(final String lookupText,
final int unreachableParametersCount,
final int lastMethodWeight,
-5
View File
@@ -1453,11 +1453,6 @@
<java.compilerOutputIndex implementation="com.intellij.compilerOutputIndex.impl.MethodsUsageIndex"/>
<java.compilerOutputIndex implementation="com.intellij.compilerOutputIndex.impl.bigram.BigramMethodsUsageIndex"/>
<java.compilerOutputIndex implementation="com.intellij.compilerOutputIndex.impl.singleton.TwinVariablesIndex"/>
<java.compilerOutputIndex implementation="com.intellij.compilerOutputIndex.impl.singleton.ParamsInMethodOccurrencesIndex"/>
<java.compilerOutputIndex implementation="com.intellij.compilerOutputIndex.impl.quickInheritance.QuickMethodsIndex"/>
<java.compilerOutputIndex implementation="com.intellij.compilerOutputIndex.impl.quickInheritance.QuickInheritanceIndex"/>
<java.compilerOutputIndex implementation="com.intellij.compilerOutputIndex.impl.callingLocation.MethodCallingLocationIndex"/>
<completion.contributor language="JAVA" id="methodsChainsCompletionContributor" order="first"
implementationClass="com.intellij.codeInsight.completion.methodChains.completion.MethodsChainsCompletionContributor"/>
<weigher order="first" key="completion" id="methodsChains"