Converted to Kotlin and edited
This commit is contained in:
@@ -1,592 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.findUsages;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.base.Predicates;
|
|
||||||
import com.google.common.collect.Collections2;
|
|
||||||
import com.google.common.collect.Ordering;
|
|
||||||
import com.intellij.codeInsight.JavaTargetElementEvaluator;
|
|
||||||
import com.intellij.codeInsight.TargetElementUtilBase;
|
|
||||||
import com.intellij.find.FindManager;
|
|
||||||
import com.intellij.find.findUsages.*;
|
|
||||||
import com.intellij.find.impl.FindManagerImpl;
|
|
||||||
import com.intellij.lang.properties.psi.PropertiesFile;
|
|
||||||
import com.intellij.lang.properties.psi.Property;
|
|
||||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
|
||||||
import com.intellij.openapi.extensions.Extensions;
|
|
||||||
import com.intellij.openapi.project.Project;
|
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
|
||||||
import com.intellij.openapi.util.io.FileUtilRt;
|
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
|
||||||
import com.intellij.psi.*;
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
|
||||||
import com.intellij.testFramework.LightProjectDescriptor;
|
|
||||||
import com.intellij.usageView.UsageInfo;
|
|
||||||
import com.intellij.usages.TextChunk;
|
|
||||||
import com.intellij.usages.UsageGroup;
|
|
||||||
import com.intellij.usages.UsageInfo2UsageAdapter;
|
|
||||||
import com.intellij.usages.UsageViewPresentation;
|
|
||||||
import com.intellij.usages.impl.rules.UsageType;
|
|
||||||
import com.intellij.usages.impl.rules.UsageTypeProvider;
|
|
||||||
import com.intellij.usages.rules.UsageFilteringRule;
|
|
||||||
import com.intellij.usages.rules.UsageGroupingRule;
|
|
||||||
import com.intellij.util.ArrayUtil;
|
|
||||||
import com.intellij.util.CommonProcessors;
|
|
||||||
import kotlin.collections.CollectionsKt;
|
|
||||||
import kotlin.jvm.functions.Function1;
|
|
||||||
import org.gradle.util.CollectionUtils;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions;
|
|
||||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory;
|
|
||||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions;
|
|
||||||
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions;
|
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor;
|
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase;
|
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor;
|
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
|
||||||
import org.jetbrains.kotlin.idea.test.TestFixtureExtension;
|
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
|
|
||||||
import org.jetbrains.kotlin.psi.*;
|
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class AbstractFindUsagesTest extends KotlinLightCodeInsightFixtureTestCase {
|
|
||||||
|
|
||||||
public static final UsageViewPresentation USAGE_VIEW_PRESENTATION = new UsageViewPresentation();
|
|
||||||
|
|
||||||
protected enum OptionsParser {
|
|
||||||
CLASS {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
KotlinClassFindUsagesOptions options = new KotlinClassFindUsagesOptions(project);
|
|
||||||
options.isUsages = false;
|
|
||||||
options.isSearchForTextOccurrences = false;
|
|
||||||
options.setSearchConstructorUsages(false);
|
|
||||||
for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
|
||||||
if (parseCommonOptions(options, s)) continue;
|
|
||||||
|
|
||||||
if (s.equals("constructorUsages")) {
|
|
||||||
options.setSearchConstructorUsages(true);
|
|
||||||
}
|
|
||||||
else if (s.equals("derivedInterfaces")) {
|
|
||||||
options.isDerivedInterfaces = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("derivedClasses")) {
|
|
||||||
options.isDerivedClasses = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("functionUsages")) {
|
|
||||||
options.isMethodsUsages = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("propertyUsages")) {
|
|
||||||
options.isFieldsUsages = true;
|
|
||||||
}
|
|
||||||
else fail("Invalid option: " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FUNCTION {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
KotlinFunctionFindUsagesOptions options = new KotlinFunctionFindUsagesOptions(project);
|
|
||||||
options.isUsages = false;
|
|
||||||
for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
|
||||||
if (parseCommonOptions(options, s)) continue;
|
|
||||||
|
|
||||||
if (s.equals("overrides")) {
|
|
||||||
options.isOverridingMethods = true;
|
|
||||||
options.isImplementingMethods = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("overloadUsages")) {
|
|
||||||
options.isIncludeOverloadUsages = true;
|
|
||||||
options.isUsages = true;
|
|
||||||
}
|
|
||||||
else fail("Invalid option: " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PROPERTY {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
KotlinPropertyFindUsagesOptions options = new KotlinPropertyFindUsagesOptions(project);
|
|
||||||
options.isUsages = false;
|
|
||||||
for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
|
||||||
if (parseCommonOptions(options, s)) continue;
|
|
||||||
|
|
||||||
if (s.equals("overrides")) {
|
|
||||||
options.setSearchOverrides(true);
|
|
||||||
}
|
|
||||||
else if (s.equals("skipRead")) {
|
|
||||||
options.isReadAccess = false;
|
|
||||||
}
|
|
||||||
else if (s.equals("skipWrite")) {
|
|
||||||
options.isWriteAccess = false;
|
|
||||||
}
|
|
||||||
else fail("Invalid option: " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
JAVA_CLASS {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
KotlinClassFindUsagesOptions options = new KotlinClassFindUsagesOptions(project);
|
|
||||||
options.isUsages = false;
|
|
||||||
options.setSearchConstructorUsages(false);
|
|
||||||
for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
|
||||||
if (parseCommonOptions(options, s)) continue;
|
|
||||||
|
|
||||||
if (s.equals("derivedInterfaces")) {
|
|
||||||
options.isDerivedInterfaces = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("derivedClasses")) {
|
|
||||||
options.isDerivedClasses = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("implementingClasses")) {
|
|
||||||
options.isImplementingClasses = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("methodUsages")) {
|
|
||||||
options.isMethodsUsages = true;
|
|
||||||
}
|
|
||||||
else if (s.equals("fieldUsages")) {
|
|
||||||
options.isFieldsUsages = true;
|
|
||||||
}
|
|
||||||
else fail("Invalid option: " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
JAVA_METHOD {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
JavaMethodFindUsagesOptions options = new JavaMethodFindUsagesOptions(project);
|
|
||||||
options.isUsages = false;
|
|
||||||
for (String s : InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
|
||||||
if (parseCommonOptions(options, s)) continue;
|
|
||||||
|
|
||||||
if (s.equals("overrides")) {
|
|
||||||
options.isOverridingMethods = true;
|
|
||||||
options.isImplementingMethods = true;
|
|
||||||
}
|
|
||||||
else fail("Invalid option: " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
JAVA_FIELD {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
return new JavaVariableFindUsagesOptions(project);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
JAVA_PACKAGE {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
return new JavaPackageFindUsagesOptions(project);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DEFAULT {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FindUsagesOptions parse(@NotNull String text, @NotNull Project project) {
|
|
||||||
return new FindUsagesOptions(project);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected static boolean parseCommonOptions(JavaFindUsagesOptions options, String s) {
|
|
||||||
if (s.equals("usages")) {
|
|
||||||
options.isUsages = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.equals("skipImports")) {
|
|
||||||
options.isSkipImportStatements = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (s.equals("textOccurrences")) {
|
|
||||||
options.isSearchForTextOccurrences = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public abstract FindUsagesOptions parse(@NotNull String text, @NotNull Project project);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static OptionsParser getParserByPsiElementClass(@NotNull Class<? extends PsiElement> klass) {
|
|
||||||
if (klass == KtNamedFunction.class) {
|
|
||||||
return FUNCTION;
|
|
||||||
}
|
|
||||||
if (klass == KtProperty.class || klass == KtParameter.class) {
|
|
||||||
return PROPERTY;
|
|
||||||
}
|
|
||||||
if (klass == KtClass.class) {
|
|
||||||
return CLASS;
|
|
||||||
}
|
|
||||||
if (klass == PsiMethod.class) {
|
|
||||||
return JAVA_METHOD;
|
|
||||||
}
|
|
||||||
if (klass == PsiClass.class) {
|
|
||||||
return JAVA_CLASS;
|
|
||||||
}
|
|
||||||
if (klass == PsiField.class) {
|
|
||||||
return JAVA_FIELD;
|
|
||||||
}
|
|
||||||
if (klass == PsiPackage.class) {
|
|
||||||
return JAVA_PACKAGE;
|
|
||||||
}
|
|
||||||
if (klass == KtTypeParameter.class) {
|
|
||||||
return DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected LightProjectDescriptor getProjectDescriptor() {
|
|
||||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() {
|
|
||||||
super.setUp();
|
|
||||||
myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/findUsages");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void extraConfig(@SuppressWarnings("UnusedParameters") @NotNull String path) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <T extends PsiElement> void doTest(@NotNull String path) throws Exception {
|
|
||||||
File mainFile = new File(path);
|
|
||||||
final String mainFileName = mainFile.getName();
|
|
||||||
String mainFileText = FileUtil.loadFile(mainFile, true);
|
|
||||||
final String prefix = mainFileName.substring(0, mainFileName.indexOf('.') + 1);
|
|
||||||
|
|
||||||
boolean isPropertiesFile = FileUtilRt.getExtension(path).equals("properties");
|
|
||||||
|
|
||||||
Class<T> caretElementClass;
|
|
||||||
if (!isPropertiesFile) {
|
|
||||||
List<String> caretElementClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, "// PSI_ELEMENT: ");
|
|
||||||
assert caretElementClassNames.size() == 1;
|
|
||||||
//noinspection unchecked
|
|
||||||
caretElementClass = (Class<T>)Class.forName(caretElementClassNames.get(0));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//noinspection unchecked
|
|
||||||
caretElementClass = (Class<T>) (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "## FIND_FILE_USAGES")
|
|
||||||
? PropertiesFile.class
|
|
||||||
: Property.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(mainFileText, "// FIXTURE_CLASS: ");
|
|
||||||
for (String fixtureClass : fixtureClasses) {
|
|
||||||
TestFixtureExtension.Companion.loadFixture(fixtureClass, myFixture.getModule());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
extraConfig(path);
|
|
||||||
|
|
||||||
OptionsParser parser = OptionsParser.getParserByPsiElementClass(caretElementClass);
|
|
||||||
|
|
||||||
String rootPath = path.substring(0, path.lastIndexOf("/") + 1);
|
|
||||||
|
|
||||||
File rootDir = new File(rootPath);
|
|
||||||
File[] extraFiles = rootDir.listFiles(
|
|
||||||
new FilenameFilter() {
|
|
||||||
@Override
|
|
||||||
public boolean accept(@NotNull File dir, @NotNull String name) {
|
|
||||||
if (!name.startsWith(prefix) || name.equals(mainFileName)) return false;
|
|
||||||
|
|
||||||
String ext = FileUtilRt.getExtension(name);
|
|
||||||
return ext.equals("kt")
|
|
||||||
|| ext.equals("java")
|
|
||||||
|| ext.equals("xml")
|
|
||||||
|| ext.equals("properties")
|
|
||||||
|| (ext.equals("txt") && !name.endsWith(".results.txt"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
assert extraFiles != null;
|
|
||||||
for (File file : extraFiles) {
|
|
||||||
myFixture.configureByFile(rootPath + file.getName());
|
|
||||||
}
|
|
||||||
myFixture.configureByFile(path);
|
|
||||||
|
|
||||||
PsiElement caretElement =
|
|
||||||
InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// FIND_BY_REF")
|
|
||||||
? TargetElementUtilBase.findTargetElement(myFixture.getEditor(),
|
|
||||||
TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED |
|
|
||||||
JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR)
|
|
||||||
: myFixture.getElementAtCaret();
|
|
||||||
assertNotNull(caretElement);
|
|
||||||
assertInstanceOf(caretElement, caretElementClass);
|
|
||||||
|
|
||||||
PsiFile containingFile = caretElement.getContainingFile();
|
|
||||||
boolean isLibraryElement = containingFile != null && ProjectRootsUtil.isLibraryFile(getProject(), containingFile.getVirtualFile());
|
|
||||||
|
|
||||||
FindUsagesOptions options = parser != null ? parser.parse(mainFileText, getProject()) : null;
|
|
||||||
|
|
||||||
// Ensure that search by sources (if present) and decompiled declarations gives the same results
|
|
||||||
if (isLibraryElement) {
|
|
||||||
PsiElement originalElement = caretElement.getOriginalElement();
|
|
||||||
findUsagesAndCheckResults(mainFileText, prefix, rootPath, originalElement, options);
|
|
||||||
|
|
||||||
PsiElement navigationElement = caretElement.getNavigationElement();
|
|
||||||
if (navigationElement != originalElement) {
|
|
||||||
findUsagesAndCheckResults(mainFileText, prefix, rootPath, navigationElement, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
for (String fixtureClass : fixtureClasses) {
|
|
||||||
TestFixtureExtension.Companion.unloadFixture(fixtureClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends PsiElement> void findUsagesAndCheckResults(
|
|
||||||
@NotNull String mainFileText,
|
|
||||||
@NotNull String prefix,
|
|
||||||
@NotNull String rootPath,
|
|
||||||
@NotNull T caretElement,
|
|
||||||
@Nullable FindUsagesOptions options
|
|
||||||
) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
|
||||||
boolean highlightingMode = InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// HIGHLIGHTING");
|
|
||||||
|
|
||||||
Collection<UsageInfo> usageInfos;
|
|
||||||
String log = null;
|
|
||||||
ArrayList<String> logList = new ArrayList<String>();
|
|
||||||
try {
|
|
||||||
if (ExpressionsOfTypeProcessor.Companion.getMode() != ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN) {
|
|
||||||
ExpressionsOfTypeProcessor.Companion.setTestLog(logList);
|
|
||||||
}
|
|
||||||
|
|
||||||
usageInfos = findUsages(caretElement, options, highlightingMode);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
ExpressionsOfTypeProcessor.Companion.setTestLog(null);
|
|
||||||
if (logList.size() > 0) {
|
|
||||||
Collections.sort(logList);
|
|
||||||
log = StringUtil.join(logList, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<UsageFilteringRule> filteringRules = instantiateClasses(mainFileText, "// FILTERING_RULES: ");
|
|
||||||
final Collection<UsageGroupingRule> groupingRules = instantiateClasses(mainFileText, "// GROUPING_RULES: ");
|
|
||||||
|
|
||||||
Collection<UsageInfo2UsageAdapter> filteredUsages = getUsageAdapters(filteringRules, usageInfos);
|
|
||||||
|
|
||||||
List<String> usageFiles = CollectionsKt.distinct(
|
|
||||||
CollectionsKt.map(
|
|
||||||
filteredUsages,
|
|
||||||
new Function1<UsageInfo2UsageAdapter, String>() {
|
|
||||||
@Override
|
|
||||||
public String invoke(UsageInfo2UsageAdapter adapter) {
|
|
||||||
return adapter.getFile().getName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
final boolean appendFileName = usageFiles.size() > 1;
|
|
||||||
|
|
||||||
Function<UsageInfo2UsageAdapter, String> convertToString = new Function<UsageInfo2UsageAdapter, String>() {
|
|
||||||
@Override
|
|
||||||
public String apply(@Nullable final UsageInfo2UsageAdapter usageAdapter) {
|
|
||||||
assert usageAdapter != null;
|
|
||||||
|
|
||||||
String groupAsString = Joiner.on(", ").join(
|
|
||||||
CollectionsKt.map(
|
|
||||||
groupingRules,
|
|
||||||
new Function1<UsageGroupingRule, String>() {
|
|
||||||
@Override
|
|
||||||
public String invoke(UsageGroupingRule rule) {
|
|
||||||
UsageGroup group = rule.groupUsage(usageAdapter);
|
|
||||||
return group != null ? group.getText(null) : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if (!groupAsString.isEmpty()) {
|
|
||||||
groupAsString = "(" + groupAsString + ") ";
|
|
||||||
}
|
|
||||||
|
|
||||||
UsageType usageType = getUsageType(usageAdapter.getElement());
|
|
||||||
String usageTypeAsString = usageType == null ? "null" : usageType.toString(USAGE_VIEW_PRESENTATION);
|
|
||||||
|
|
||||||
List<TextChunk> usageChunks = new ArrayList<TextChunk>();
|
|
||||||
CollectionUtils.addAll(usageChunks, usageAdapter.getPresentation().getText());
|
|
||||||
|
|
||||||
// Add space after line number
|
|
||||||
usageChunks.add(1, new TextChunk(new TextAttributes(), " "));
|
|
||||||
|
|
||||||
return (appendFileName ? "[" + usageAdapter.getFile().getName() + "] " : "") +
|
|
||||||
usageTypeAsString + " " +
|
|
||||||
groupAsString +
|
|
||||||
Joiner.on("").join(usageChunks);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Collection<String> finalUsages = Ordering.natural().sortedCopy(Collections2.transform(filteredUsages, convertToString));
|
|
||||||
KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "results.txt"), StringUtil.join(finalUsages, "\n"));
|
|
||||||
|
|
||||||
if (log != null) {
|
|
||||||
KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "log"), log);
|
|
||||||
|
|
||||||
// if log is empty then compare results with plain search
|
|
||||||
try {
|
|
||||||
ExpressionsOfTypeProcessor.Companion.setMode(ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN);
|
|
||||||
|
|
||||||
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
ExpressionsOfTypeProcessor.Companion.setMode(ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Collection<UsageInfo> findUsages(
|
|
||||||
@NotNull PsiElement targetElement,
|
|
||||||
@Nullable FindUsagesOptions options,
|
|
||||||
boolean highlightingMode
|
|
||||||
) {
|
|
||||||
Project project = getProject();
|
|
||||||
|
|
||||||
FindUsagesHandler handler;
|
|
||||||
if (targetElement instanceof PsiMember) {
|
|
||||||
handler = new JavaFindUsagesHandler(targetElement, new JavaFindUsagesHandlerFactory(project));
|
|
||||||
}
|
|
||||||
else if (targetElement instanceof KtDeclaration) {
|
|
||||||
handler = new KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(targetElement);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
handler = ((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager().getFindUsagesHandler(targetElement, false);
|
|
||||||
}
|
|
||||||
assert handler != null : "Cannot find handler for: " + targetElement;
|
|
||||||
|
|
||||||
if (options == null) {
|
|
||||||
options = handler.getFindUsagesOptions(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.searchScope = GlobalSearchScope.allScope(project);
|
|
||||||
|
|
||||||
CommonProcessors.CollectProcessor<UsageInfo> processor = new CommonProcessors.CollectProcessor<UsageInfo>();
|
|
||||||
PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements());
|
|
||||||
|
|
||||||
for (PsiElement psiElement : psiElements) {
|
|
||||||
if (highlightingMode) {
|
|
||||||
for (PsiReference reference : handler.findReferencesToHighlight(psiElement, options.searchScope)) {
|
|
||||||
processor.process(new UsageInfo(reference));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
handler.processElementUsages(psiElement, processor, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return processor.getResults();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Collection<UsageInfo2UsageAdapter> getUsageAdapters(
|
|
||||||
final Collection<? extends UsageFilteringRule> filters,
|
|
||||||
Collection<UsageInfo> usageInfos
|
|
||||||
) {
|
|
||||||
return Collections2.filter(
|
|
||||||
Collections2.transform(usageInfos, new Function<UsageInfo, UsageInfo2UsageAdapter>() {
|
|
||||||
@Override
|
|
||||||
public UsageInfo2UsageAdapter apply(@Nullable UsageInfo usageInfo) {
|
|
||||||
assert (usageInfo != null);
|
|
||||||
|
|
||||||
UsageInfo2UsageAdapter usageAdapter = new UsageInfo2UsageAdapter(usageInfo);
|
|
||||||
for (UsageFilteringRule filter : filters) {
|
|
||||||
if (!filter.isVisible(usageAdapter)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return usageAdapter;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Predicates.notNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static UsageType getUsageType(@Nullable PsiElement element) {
|
|
||||||
if (element == null) return null;
|
|
||||||
|
|
||||||
if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) {
|
|
||||||
return UsageType.COMMENT_USAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
UsageTypeProvider[] providers = Extensions.getExtensions(UsageTypeProvider.EP_NAME);
|
|
||||||
for (UsageTypeProvider provider : providers) {
|
|
||||||
UsageType usageType = provider.getUsageType(element);
|
|
||||||
if (usageType != null) {
|
|
||||||
return usageType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return UsageType.UNCLASSIFIED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> Collection<T> instantiateClasses(String mainFileText, String directive)
|
|
||||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
|
||||||
List<String> filteringRuleClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, directive);
|
|
||||||
|
|
||||||
Collection<T> filters = new ArrayList<T>();
|
|
||||||
for (String filteringRuleClassName : filteringRuleClassNames) {
|
|
||||||
//noinspection unchecked
|
|
||||||
Class<T> klass = (Class<T>) Class.forName(filteringRuleClassName);
|
|
||||||
filters.add(klass.newInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
return filters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,462 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.findUsages
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.JavaTargetElementEvaluator
|
||||||
|
import com.intellij.codeInsight.TargetElementUtilBase
|
||||||
|
import com.intellij.find.FindManager
|
||||||
|
import com.intellij.find.findUsages.*
|
||||||
|
import com.intellij.find.impl.FindManagerImpl
|
||||||
|
import com.intellij.lang.properties.psi.PropertiesFile
|
||||||
|
import com.intellij.lang.properties.psi.Property
|
||||||
|
import com.intellij.openapi.editor.markup.TextAttributes
|
||||||
|
import com.intellij.openapi.extensions.Extensions
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.util.io.FileUtilRt
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
|
import com.intellij.usageView.UsageInfo
|
||||||
|
import com.intellij.usages.TextChunk
|
||||||
|
import com.intellij.usages.UsageInfo2UsageAdapter
|
||||||
|
import com.intellij.usages.UsageViewPresentation
|
||||||
|
import com.intellij.usages.impl.rules.UsageType
|
||||||
|
import com.intellij.usages.impl.rules.UsageTypeProvider
|
||||||
|
import com.intellij.usages.rules.UsageFilteringRule
|
||||||
|
import com.intellij.usages.rules.UsageGroupingRule
|
||||||
|
import com.intellij.util.CommonProcessors
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import org.jetbrains.kotlin.idea.test.TestFixtureExtension
|
||||||
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
|
|
||||||
|
protected enum class OptionsParser {
|
||||||
|
CLASS {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return KotlinClassFindUsagesOptions(project).apply {
|
||||||
|
isUsages = false
|
||||||
|
isSearchForTextOccurrences = false
|
||||||
|
searchConstructorUsages = false
|
||||||
|
for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
||||||
|
if (parseCommonOptions(this, s)) continue
|
||||||
|
|
||||||
|
when (s) {
|
||||||
|
"constructorUsages" -> searchConstructorUsages = true
|
||||||
|
"derivedInterfaces" -> isDerivedInterfaces = true
|
||||||
|
"derivedClasses" -> isDerivedClasses = true
|
||||||
|
"functionUsages" -> isMethodsUsages = true
|
||||||
|
"propertyUsages" -> isFieldsUsages = true
|
||||||
|
else -> fail("Invalid option: " + s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FUNCTION {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return KotlinFunctionFindUsagesOptions(project).apply {
|
||||||
|
isUsages = false
|
||||||
|
for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
||||||
|
if (parseCommonOptions(this, s)) continue
|
||||||
|
|
||||||
|
when (s) {
|
||||||
|
"overrides" -> {
|
||||||
|
isOverridingMethods = true
|
||||||
|
isImplementingMethods = true
|
||||||
|
}
|
||||||
|
"overloadUsages" -> {
|
||||||
|
isIncludeOverloadUsages = true
|
||||||
|
isUsages = true
|
||||||
|
}
|
||||||
|
else -> fail("Invalid option: " + s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PROPERTY {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return KotlinPropertyFindUsagesOptions(project).apply {
|
||||||
|
isUsages = false
|
||||||
|
for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
||||||
|
if (parseCommonOptions(this, s)) continue
|
||||||
|
|
||||||
|
when (s) {
|
||||||
|
"overrides" -> searchOverrides = true
|
||||||
|
"skipRead" -> isReadAccess = false
|
||||||
|
"skipWrite" -> isWriteAccess = false
|
||||||
|
else -> fail("Invalid option: " + s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
JAVA_CLASS {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return KotlinClassFindUsagesOptions(project).apply {
|
||||||
|
isUsages = false
|
||||||
|
searchConstructorUsages = false
|
||||||
|
for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
||||||
|
if (parseCommonOptions(this, s)) continue
|
||||||
|
|
||||||
|
when (s) {
|
||||||
|
"derivedInterfaces" -> isDerivedInterfaces = true
|
||||||
|
"derivedClasses" -> isDerivedClasses = true
|
||||||
|
"implementingClasses" -> isImplementingClasses = true
|
||||||
|
"methodUsages" -> isMethodsUsages = true
|
||||||
|
"fieldUsages" -> isFieldsUsages = true
|
||||||
|
else -> fail("Invalid option: " + s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
JAVA_METHOD {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return JavaMethodFindUsagesOptions(project).apply {
|
||||||
|
isUsages = false
|
||||||
|
for (s in InTextDirectivesUtils.findListWithPrefixes(text, "// OPTIONS: ")) {
|
||||||
|
if (parseCommonOptions(this, s)) continue
|
||||||
|
|
||||||
|
when (s) {
|
||||||
|
"overrides" -> {
|
||||||
|
isOverridingMethods = true
|
||||||
|
isImplementingMethods = true
|
||||||
|
}
|
||||||
|
else -> fail("Invalid option: " + s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
JAVA_FIELD {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return JavaVariableFindUsagesOptions(project)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
JAVA_PACKAGE {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return JavaPackageFindUsagesOptions(project)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DEFAULT {
|
||||||
|
override fun parse(text: String, project: Project): FindUsagesOptions {
|
||||||
|
return FindUsagesOptions(project)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
abstract fun parse(text: String, project: Project): FindUsagesOptions
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
protected fun parseCommonOptions(options: JavaFindUsagesOptions, s: String): Boolean {
|
||||||
|
when (s) {
|
||||||
|
"usages" -> {
|
||||||
|
options.isUsages = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
"skipImports" -> {
|
||||||
|
options.isSkipImportStatements = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
"textOccurrences" -> {
|
||||||
|
options.isSearchForTextOccurrences = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getParserByPsiElementClass(klass: Class<out PsiElement>): OptionsParser? {
|
||||||
|
return when (klass) {
|
||||||
|
KtNamedFunction::class.java -> FUNCTION
|
||||||
|
KtProperty::class.java, KtParameter::class.java -> PROPERTY
|
||||||
|
KtClass::class.java -> CLASS
|
||||||
|
PsiMethod::class.java -> JAVA_METHOD
|
||||||
|
PsiClass::class.java -> JAVA_CLASS
|
||||||
|
PsiField::class.java -> JAVA_FIELD
|
||||||
|
PsiPackage::class.java -> JAVA_PACKAGE
|
||||||
|
KtTypeParameter::class.java -> DEFAULT
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||||
|
|
||||||
|
public override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
myFixture.testDataPath = PluginTestCaseBase.getTestDataPathBase() + "/findUsages"
|
||||||
|
}
|
||||||
|
|
||||||
|
// used in Spring tests (outside main project!)
|
||||||
|
protected open fun extraConfig(path: String) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun <T : PsiElement> doTest(path: String) {
|
||||||
|
val mainFile = File(path)
|
||||||
|
val mainFileName = mainFile.name
|
||||||
|
val mainFileText = FileUtil.loadFile(mainFile, true)
|
||||||
|
val prefix = mainFileName.substringBefore(".") + "."
|
||||||
|
|
||||||
|
val isPropertiesFile = FileUtilRt.getExtension(path) == "properties"
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val caretElementClass = (if (!isPropertiesFile) {
|
||||||
|
val caretElementClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, "// PSI_ELEMENT: ")
|
||||||
|
Class.forName(caretElementClassNames.single())
|
||||||
|
}
|
||||||
|
else if (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "## FIND_FILE_USAGES")) {
|
||||||
|
PropertiesFile::class.java
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Property::class.java
|
||||||
|
}) as Class<T>
|
||||||
|
|
||||||
|
val fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(mainFileText, "// FIXTURE_CLASS: ")
|
||||||
|
for (fixtureClass in fixtureClasses) {
|
||||||
|
TestFixtureExtension.loadFixture(fixtureClass, myFixture.module)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
extraConfig(path)
|
||||||
|
|
||||||
|
val parser = OptionsParser.getParserByPsiElementClass(caretElementClass)
|
||||||
|
|
||||||
|
val rootPath = path.substringBeforeLast("/") + "/"
|
||||||
|
|
||||||
|
val rootDir = File(rootPath)
|
||||||
|
val extraFiles = rootDir.listFiles { dir, name ->
|
||||||
|
if (!name.startsWith(prefix) || name == mainFileName) return@listFiles false
|
||||||
|
|
||||||
|
val ext = FileUtilRt.getExtension(name)
|
||||||
|
ext == "kt"
|
||||||
|
|| ext == "java"
|
||||||
|
|| ext == "xml"
|
||||||
|
|| ext == "properties"
|
||||||
|
|| ext == "txt" && !name.endsWith(".results.txt")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (file in extraFiles) {
|
||||||
|
myFixture.configureByFile(rootPath + file.name)
|
||||||
|
}
|
||||||
|
myFixture.configureByFile(path)
|
||||||
|
|
||||||
|
val caretElement = if (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// FIND_BY_REF"))
|
||||||
|
TargetElementUtilBase.findTargetElement(myFixture.editor,
|
||||||
|
TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED or JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR)!!
|
||||||
|
else
|
||||||
|
myFixture.elementAtCaret
|
||||||
|
UsefulTestCase.assertInstanceOf(caretElement, caretElementClass)
|
||||||
|
|
||||||
|
val containingFile = caretElement.containingFile
|
||||||
|
val isLibraryElement = containingFile != null && ProjectRootsUtil.isLibraryFile(project, containingFile.virtualFile)
|
||||||
|
|
||||||
|
val options = parser?.parse(mainFileText, project)
|
||||||
|
|
||||||
|
// Ensure that search by sources (if present) and decompiled declarations gives the same results
|
||||||
|
if (isLibraryElement) {
|
||||||
|
val originalElement = caretElement.originalElement
|
||||||
|
findUsagesAndCheckResults(mainFileText, prefix, rootPath, originalElement, options)
|
||||||
|
|
||||||
|
val navigationElement = caretElement.navigationElement
|
||||||
|
if (navigationElement !== originalElement) {
|
||||||
|
findUsagesAndCheckResults(mainFileText, prefix, rootPath, navigationElement, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
findUsagesAndCheckResults<PsiElement>(mainFileText, prefix, rootPath, caretElement, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
fixtureClasses.forEach { TestFixtureExtension.unloadFixture(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : PsiElement> findUsagesAndCheckResults(
|
||||||
|
mainFileText: String,
|
||||||
|
prefix: String,
|
||||||
|
rootPath: String,
|
||||||
|
caretElement: T,
|
||||||
|
options: FindUsagesOptions?
|
||||||
|
) {
|
||||||
|
val highlightingMode = InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// HIGHLIGHTING")
|
||||||
|
|
||||||
|
var log: String? = null
|
||||||
|
val logList = ArrayList<String>()
|
||||||
|
val usageInfos = try {
|
||||||
|
if (ExpressionsOfTypeProcessor.mode !== ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN) {
|
||||||
|
ExpressionsOfTypeProcessor.testLog = logList
|
||||||
|
}
|
||||||
|
|
||||||
|
findUsages(caretElement, options, highlightingMode)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ExpressionsOfTypeProcessor.testLog = null
|
||||||
|
if (logList.size > 0) {
|
||||||
|
log = logList.sorted().joinToString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val filteringRules = instantiateClasses<UsageFilteringRule>(mainFileText, "// FILTERING_RULES: ")
|
||||||
|
val groupingRules = instantiateClasses<UsageGroupingRule>(mainFileText, "// GROUPING_RULES: ")
|
||||||
|
|
||||||
|
val filteredUsages = getUsageAdapters(filteringRules, usageInfos)
|
||||||
|
|
||||||
|
val usageFiles = filteredUsages.map { it.file.name }.distinct()
|
||||||
|
val appendFileName = usageFiles.size > 1
|
||||||
|
|
||||||
|
val convertToString: (UsageInfo2UsageAdapter) -> String = { usageAdapter ->
|
||||||
|
var groupAsString = groupingRules
|
||||||
|
.map { it.groupUsage(usageAdapter)?.getText(null) ?: "" }
|
||||||
|
.joinToString(", ")
|
||||||
|
if (!groupAsString.isEmpty()) {
|
||||||
|
groupAsString = "($groupAsString) "
|
||||||
|
}
|
||||||
|
|
||||||
|
val usageType = getUsageType(usageAdapter.element)
|
||||||
|
val usageTypeAsString = usageType?.toString(USAGE_VIEW_PRESENTATION) ?: "null"
|
||||||
|
|
||||||
|
val usageChunks = ArrayList<TextChunk>()
|
||||||
|
usageChunks.addAll(usageAdapter.presentation.text.asList())
|
||||||
|
usageChunks.add(1, TextChunk(TextAttributes(), " ")) // add space after line number
|
||||||
|
|
||||||
|
buildString {
|
||||||
|
if (appendFileName) {
|
||||||
|
append("[").append(usageAdapter.file.name).append("] ")
|
||||||
|
}
|
||||||
|
append(usageTypeAsString)
|
||||||
|
append(" ")
|
||||||
|
append(groupAsString)
|
||||||
|
append(usageChunks.joinToString(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val finalUsages = filteredUsages.map(convertToString).sorted()
|
||||||
|
KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "results.txt"), finalUsages.joinToString("\n"))
|
||||||
|
|
||||||
|
if (log != null) {
|
||||||
|
KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "log"), log)
|
||||||
|
|
||||||
|
// if log is empty then compare results with plain search
|
||||||
|
try {
|
||||||
|
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN
|
||||||
|
|
||||||
|
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun findUsages(
|
||||||
|
targetElement: PsiElement,
|
||||||
|
options: FindUsagesOptions?,
|
||||||
|
highlightingMode: Boolean
|
||||||
|
): Collection<UsageInfo> {
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
|
var options = options
|
||||||
|
val project = project
|
||||||
|
|
||||||
|
val handler: FindUsagesHandler = (if (targetElement is PsiMember) {
|
||||||
|
JavaFindUsagesHandler(targetElement, JavaFindUsagesHandlerFactory(project))
|
||||||
|
}
|
||||||
|
else if (targetElement is KtDeclaration) {
|
||||||
|
KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(targetElement)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(FindManager.getInstance(project) as FindManagerImpl).findUsagesManager.getFindUsagesHandler(targetElement, false)
|
||||||
|
}) ?: error("Cannot find handler for: $targetElement")
|
||||||
|
|
||||||
|
if (options == null) {
|
||||||
|
options = handler.getFindUsagesOptions(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
options.searchScope = GlobalSearchScope.allScope(project)
|
||||||
|
|
||||||
|
val processor = CommonProcessors.CollectProcessor<UsageInfo>()
|
||||||
|
for (psiElement in handler.primaryElements + handler.secondaryElements) {
|
||||||
|
if (highlightingMode) {
|
||||||
|
for (reference in handler.findReferencesToHighlight(psiElement, options.searchScope)) {
|
||||||
|
processor.process(UsageInfo(reference))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handler.processElementUsages(psiElement, processor, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processor.results
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val USAGE_VIEW_PRESENTATION = UsageViewPresentation()
|
||||||
|
|
||||||
|
private fun getUsageAdapters(
|
||||||
|
filters: Collection<UsageFilteringRule>,
|
||||||
|
usageInfos: Collection<UsageInfo>
|
||||||
|
): Collection<UsageInfo2UsageAdapter> {
|
||||||
|
return usageInfos
|
||||||
|
.map(::UsageInfo2UsageAdapter)
|
||||||
|
.filter { usageAdapter -> filters.all { it.isVisible(usageAdapter) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUsageType(element: PsiElement?): UsageType? {
|
||||||
|
if (element == null) return null
|
||||||
|
|
||||||
|
if (element.getNonStrictParentOfType<PsiComment>() != null) {
|
||||||
|
return UsageType.COMMENT_USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
val providers = Extensions.getExtensions(UsageTypeProvider.EP_NAME)
|
||||||
|
return providers
|
||||||
|
.mapNotNull { it.getUsageType(element) }
|
||||||
|
.firstOrNull()
|
||||||
|
?: UsageType.UNCLASSIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> instantiateClasses(mainFileText: String, directive: String): Collection<T> {
|
||||||
|
val filteringRuleClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, directive)
|
||||||
|
return filteringRuleClassNames
|
||||||
|
.map {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(Class.forName(it).newInstance() as T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user