package org.jetbrains.plugins.clojure.psi.impl.ns; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.stubs.StubIndex; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.clojure.psi.api.ClojureFile; import org.jetbrains.plugins.clojure.psi.api.defs.ClDef; import org.jetbrains.plugins.clojure.psi.api.ns.ClNs; import org.jetbrains.plugins.clojure.psi.resolve.ResolveUtil; import org.jetbrains.plugins.clojure.psi.stubs.index.ClojureNsNameIndex; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; /** * @author ilyas */ public class NamespaceUtil { public static final String[] DEFAULT_NSES = new String[]{"clojure.core", // "clojure.inspector", // "clojure.main", // "clojure.parallel", // "clojure.set", // "clojure.zip", // "clojure.xml" }; public static PsiNamedElement[] getDeclaredElements(@NotNull String nsFqn, @NotNull Project project) { final Collection<ClNs> nses = StubIndex.getInstance().get(ClojureNsNameIndex.KEY, nsFqn, project, GlobalSearchScope.allScope(project)); ArrayList<PsiNamedElement> result = new ArrayList<PsiNamedElement>(); for (ClNs ns : nses) { if (nsFqn.equals(ns.getName())) { final PsiFile file = ns.getContainingFile(); if (file instanceof ClojureFile) { for (ClDef elem : ((ClojureFile) file).getFileDefinitions()) { if (StringUtil.isNotEmpty(elem.getName()) && ns.getTextOffset() < elem.getTextOffset()) { result.add(elem); } } } } } return result.toArray(PsiNamedElement.EMPTY_ARRAY); } public static PsiNamedElement[] getDefaultDefinitions(@NotNull Project project) { final ArrayList<PsiNamedElement> res = new ArrayList<PsiNamedElement>(); for (String ns : DEFAULT_NSES) { res.addAll(Arrays.asList(getDeclaredElements(ns, project))); } return res.toArray(PsiNamedElement.EMPTY_ARRAY); } public static ClSyntheticNamespace[] getTopLevelNamespaces(@NotNull Project project) { ArrayList<ClSyntheticNamespace> result = new ArrayList<ClSyntheticNamespace>(); for (String fqn : StubIndex.getInstance().getAllKeys(ClojureNsNameIndex.KEY, project)) { if (!fqn.contains(".")) { result.add(getNamespace(fqn, project)); } } return result.toArray(new ClSyntheticNamespace[result.size()]); } @Nullable public static ClSyntheticNamespace getNamespace(@NotNull String fqn, @NotNull final Project project) { final Collection<ClNs> nsWithPrefix = StubIndex.getInstance().get(ClojureNsNameIndex.KEY, fqn, project, GlobalSearchScope.allScope(project)); if (!nsWithPrefix.isEmpty()) { final ClNs ns = nsWithPrefix.iterator().next(); final String nsName = ns.getName(); assert nsName != null; final String synthName = nsName.equals(fqn) ? nsName : fqn; final String refName = StringUtil.getShortName(synthName); ClNs navigationElement = null; for (ClNs clNs : nsWithPrefix) { if (fqn.equals(clNs.getName())) { navigationElement = clNs; } } return new MyClSyntheticNamespace(project, refName, synthName, navigationElement); } return null; } private static class MyClSyntheticNamespace extends ClSyntheticNamespace { private final Project project; private final ClNs navigationElement; public MyClSyntheticNamespace(Project project, String refName, String synthName, ClNs navigationElement) { super(PsiManager.getInstance(project), refName, synthName, navigationElement); this.project = project; this.navigationElement = navigationElement; } @NotNull @Override public PsiElement getNavigationElement() { return navigationElement != null ? navigationElement : super.getNavigationElement(); } @Override public boolean canNavigateToSource() { return navigationElement != null; } @Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) { final HashSet<String> innerNamespaces = new HashSet<String>(); // Add inner namespaces final String outerName = getQualifiedName(); for (String fqn : StubIndex.getInstance().getAllKeys(ClojureNsNameIndex.KEY, project)) { if (fqn.startsWith(outerName) && !fqn.equals(outerName) && !StringUtil.trimStart(fqn, outerName + ".").contains(".")) { final ClSyntheticNamespace inner = getNamespace(fqn, project); innerNamespaces.add(fqn); if (!ResolveUtil.processElement(processor, inner)) { return false; } } } // Add declared elements for (PsiNamedElement element : getDeclaredElements(getQualifiedName(), getProject())) { if (!ResolveUtil.processElement(processor, element)) { return false; } } final String qualifiedName = getQualifiedName(); final PsiPackage aPackage = JavaPsiFacade.getInstance(getProject()).findPackage(qualifiedName); if (aPackage != null) { for (PsiClass clazz : aPackage.getClasses(place.getResolveScope())) { if (!ResolveUtil.processElement(processor, clazz)) return false; } for (PsiPackage pack : aPackage.getSubPackages(place.getResolveScope())) { if (!innerNamespaces.contains(pack.getQualifiedName()) && !ResolveUtil.processElement(processor, getNamespaceElement(pack))) { return false; } } } return true; } } public static ClSyntheticNamespace getNamespaceElement(PsiPackage pack) { return new MyClSyntheticNamespace(pack.getProject(), pack.getName(), pack.getQualifiedName(), null); } }