package com.jetbrains.lang.dart.ide.index;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.CommonProcessors;
import com.intellij.util.indexing.*;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import com.jetbrains.lang.dart.psi.*;
import com.jetbrains.lang.dart.util.DartResolveUtil;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class DartSymbolIndex extends ScalarIndexExtension<String> {
public static final ID<String, Void> DART_SYMBOL_INDEX = ID.create("DartSymbolIndex");
private DataIndexer<String, Void, FileContent> myDataIndexer = new MyDataIndexer();
@NotNull
@Override
public ID<String, Void> getName() {
return DART_SYMBOL_INDEX;
}
@Override
public int getVersion() {
return DartIndexUtil.INDEX_VERSION;
}
@NotNull
@Override
public DataIndexer<String, Void, FileContent> getIndexer() {
return myDataIndexer;
}
@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return EnumeratorStringDescriptor.INSTANCE;
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return DartInputFilter.INSTANCE;
}
@Override
public boolean dependsOnFileContent() {
return true;
}
public static String[] getAllSymbols(@NotNull final GlobalSearchScope scope) {
final CommonProcessors.CollectProcessor<String> processor = new CommonProcessors.CollectProcessor<>();
FileBasedIndex.getInstance().processAllKeys(DART_SYMBOL_INDEX, processor, scope, null);
return ArrayUtil.toStringArray(processor.getResults());
}
public static List<DartComponentName> getItemsByName(@NotNull final String name,
@NotNull final Project project,
@NotNull final GlobalSearchScope searchScope) {
final Collection<VirtualFile> files =
FileBasedIndex.getInstance().getContainingFiles(DART_SYMBOL_INDEX, name, searchScope);
final Set<DartComponentName> result = new THashSet<>();
for (VirtualFile vFile : files) {
final PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile);
for (PsiElement root : DartResolveUtil.findDartRoots(psiFile)) {
processComponents(root, component -> {
if (name.equals(component.getName())) {
result.add(component.getComponentName());
}
return true;
});
}
}
return new ArrayList<>(result);
}
private static class MyDataIndexer implements DataIndexer<String, Void, FileContent> {
@Override
@NotNull
public Map<String, Void> map(@NotNull final FileContent inputData) {
List<String> symbols = DartIndexUtil.indexFile(inputData).getSymbols();
final Map<String, Void> result = new THashMap<>();
for (String symbol : symbols) {
result.put(symbol, null);
}
return result;
}
}
private static void processComponents(PsiElement context, PsiElementProcessor<DartComponent> processor) {
// top-level components
final DartComponent[] components = PsiTreeUtil.getChildrenOfType(context, DartComponent.class);
if (components != null) {
for (DartComponent component : components) {
if (!processComponent(processor, component)) {
return;
}
}
}
// top-level variables
final DartVarDeclarationList[] varLists = PsiTreeUtil.getChildrenOfType(context, DartVarDeclarationList.class);
if (varLists != null) {
for (DartVarDeclarationList varList : varLists) {
if (!processComponent(processor, varList.getVarAccessDeclaration())) {
return;
}
for (DartVarDeclarationListPart part : varList.getVarDeclarationListPartList()) {
if (!processComponent(processor, part)) {
return;
}
}
}
}
}
private static boolean processComponent(PsiElementProcessor<DartComponent> processor, DartComponent component) {
final String componentName = component.getName();
if (componentName == null) {
return true;
}
if (!processor.execute(component)) {
return false;
}
if (component instanceof DartClass) {
for (DartComponent subComponent : DartResolveUtil.getNamedSubComponents((DartClass)component)) {
if (subComponent.isConstructor() && componentName.equals(subComponent.getName())) {
continue;
}
if (!processor.execute(subComponent)) {
return false;
}
}
}
return true;
}
}