package org.jetbrains.plugins.clojure.psi.impl.list; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.clojure.psi.api.ClList; import org.jetbrains.plugins.clojure.psi.api.ClVector; import org.jetbrains.plugins.clojure.psi.api.defs.ClDef; import org.jetbrains.plugins.clojure.psi.api.symbols.ClSymbol; import org.jetbrains.plugins.clojure.psi.impl.ImportOwner; import org.jetbrains.plugins.clojure.psi.impl.ns.ClSyntheticNamespace; import org.jetbrains.plugins.clojure.psi.impl.ns.NamespaceUtil; import org.jetbrains.plugins.clojure.psi.impl.symbols.ClSymbolImpl; import org.jetbrains.plugins.clojure.psi.resolve.ResolveUtil; import org.jetbrains.plugins.clojure.psi.util.ClojureKeywords; import java.util.Arrays; import java.util.Set; /** * @author ilyas */ public class ListDeclarations { public static final String LET = "let"; public static final String WITH_OPEN = "with-open"; public static final String WITH_LOCAL_VARS = "with-local-vars"; public static final String WHEN_LET = "when-let"; public static final String WHEN_FIRST = "when-let"; public static final String FOR = "for"; public static final String IF_LET = "if-let"; public static final String LOOP = "loop"; public static final String DOSEQ = "doseq"; public static final String DECLARE = "declare"; public static final String FN = "fn"; public static final String NS = "ns"; public static final String DEFN = "defn"; public static final String DEFN_ = "defn-"; public static final String IMPORT = "import"; private static final String MEMFN = "memfn"; public static final String USE = "use"; public static final String REFER = "refer"; public static final String REQUIRE = "require"; private static final String DOT = "."; private static final Set<String> LOCAL_BINDINGS = new HashSet<String>(Arrays.asList( LET, WITH_OPEN, WITH_LOCAL_VARS, WHEN_LET, WHEN_FIRST, FOR, IF_LET, LOOP, FN, DOSEQ )); public static boolean get(PsiScopeProcessor processor, ResolveState state, PsiElement lastParent, PsiElement place, ClList list, @Nullable String headText) { if (headText == null) return true; if (headText.equals(FN)) return processFnDeclaration(processor, list, place, lastParent); if (headText.equals(IMPORT)) return ImportOwner.processImports(processor, place, list, headText); if (headText.equals(USE)) return ImportOwner.processUses(processor, place, list, headText); if (headText.equals(REFER)) return ImportOwner.processRefer(processor, place, list, headText); if (headText.equals(REQUIRE)) return ImportOwner.processRequires(processor, place, list, headText); if (headText.equals(MEMFN)) return processMemFnDeclaration(processor, list, place); if (headText.equals(DOT)) return processDotDeclaration(processor, list, place, lastParent); if (headText.equals(LOOP)) return processLoopDeclaration(processor, list, place, lastParent); if (headText.equals(DOSEQ)) return processDoseqDeclaration(processor, list, place, lastParent); if (headText.equals(DECLARE)) return processDeclareDeclaration(processor, list, place, lastParent); if (headText.equals(LET) && !processLetContents(processor, list, place)) return false; if (LOCAL_BINDINGS.contains(headText)) return processBindings(processor, list, place); final PsiElement parent = list.getParent(); if (parent != null && parent instanceof ClList) { return getWithParentContext(processor, list, ((ClList) parent), state, lastParent, place); } return true; } private static boolean getWithParentContext(PsiScopeProcessor processor, ClList list, ClList parent, ResolveState state, PsiElement lastParent, PsiElement place) { final String parentHead = parent.getHeadText(); final PsiElement first = list.getFirstNonLeafElement(); if (processUseParent(processor, place, parentHead, first, state, lastParent)) return true; return true; } /* Handle (:use ...) parent, e.g. (ns my-namespace (:use (clojure <caret> data inspector))) */ private static boolean processUseParent(PsiScopeProcessor processor, PsiElement place, String parentHead, PsiElement first, ResolveState state, PsiElement lastParent) { if ((USE.equals(parentHead) || ClojureKeywords.USE.equals(parentHead)) && first instanceof ClSymbol) { final ClSymbol symbol = (ClSymbol) first; final ClSyntheticNamespace namespace = NamespaceUtil.getNamespace(symbol.getNameString(), place.getProject()); return namespace == null? true : namespace.processDeclarations(processor, state, lastParent, place); } return true; } private static boolean processDeclareDeclaration(PsiScopeProcessor processor, ClList list, PsiElement place, PsiElement lastParent) { final ClVector paramVector = list.findFirstChildByClass(ClVector.class); if (paramVector != null) { for (ClSymbol symbol : paramVector.getOddSymbols()) { if (!ResolveUtil.processElement(processor, symbol)) return false; } } return true; } private static boolean processLoopDeclaration(PsiScopeProcessor processor, ClList list, PsiElement place, PsiElement lastParent) { if (lastParent != null && lastParent.getParent() == list) { final ClVector paramVector = list.findFirstChildByClass(ClVector.class); if (paramVector != null) { for (ClSymbol symbol : paramVector.getOddSymbols()) { if (!ResolveUtil.processElement(processor, symbol)) return false; } } return true; } return true; } private static boolean processDoseqDeclaration(PsiScopeProcessor processor, ClList list, PsiElement place, PsiElement lastParent) { if (lastParent != null && lastParent.getParent() == list) { final ClVector paramVector = list.findFirstChildByClass(ClVector.class); if (paramVector != null) { for (ClSymbol symbol : paramVector.getOddSymbols()) { if (!ResolveUtil.processElement(processor, symbol)) return false; } } return true; } return true; } private static boolean processDotDeclaration(PsiScopeProcessor processor, ClList list, PsiElement place, PsiElement lastParent) { final PsiElement parent = place.getParent(); if (parent == null || list == parent) return true; final PsiElement second = list.getSecondNonLeafElement(); if (second instanceof ClSymbol && place != second) { ClSymbol symbol = (ClSymbol) second; for (ResolveResult result : symbol.multiResolve(false)) { final PsiElement element = result.getElement(); if (element instanceof PsiNamedElement && !ResolveUtil.processElement(processor, (PsiNamedElement) element)) { return false; } } if (lastParent == null || lastParent == list) { return true; } if (parent.getParent() == list) { if (place instanceof ClSymbol && ((ClSymbol) place).getQualifierSymbol() == null) { ClSymbol symbol2 = (ClSymbol) place; ResolveResult[] results = ClSymbolImpl.MyResolver.resolveJavaMethodReference(symbol2, list, true); for (ResolveResult result : results) { final PsiElement element = result.getElement(); if (element instanceof PsiNamedElement && !ResolveUtil.processElement(processor, (PsiNamedElement) element)) { return false; } } } } } return true; } private static boolean processMemFnDeclaration(PsiScopeProcessor processor, ClList list, PsiElement place) { if (place instanceof ClSymbol && place.getParent() == list && ((ClSymbol) place).getQualifierSymbol() == null) { ClSymbol symbol = (ClSymbol) place; ResolveResult[] results = ClSymbolImpl.MyResolver.resolveJavaMethodReference(symbol, list.getParent(), true); for (ResolveResult result : results) { final PsiElement element = result.getElement(); if (element instanceof PsiNamedElement && !ResolveUtil.processElement(processor, (PsiNamedElement) element)) { return false; } } } return true; } private static boolean processBindings(PsiScopeProcessor processor, ClList list, PsiElement place) { if (PsiTreeUtil.findCommonParent(place, list) == list) { final ClVector paramVector = list.findFirstChildByClass(ClVector.class); if (paramVector != null) { for (ClSymbol symbol : paramVector.getOddSymbols()) { if (!ResolveUtil.processElement(processor, symbol)) return false; } } return true; } return true; } private static boolean processLetContents(PsiScopeProcessor processor, ClList list, PsiElement place) { if (PsiTreeUtil.findCommonParent(place, list) != list) { final ClDef def = list.findFirstChildByClass(ClDef.class); if (def != null) { return ResolveUtil.processElement(processor, def); } return true; } return true; } private static boolean processFnDeclaration(PsiScopeProcessor processor, ClList list, PsiElement place, PsiElement lastParent) { final PsiElement second = list.getSecondNonLeafElement(); if (lastParent == second) return true; if ((second instanceof ClSymbol) && !ResolveUtil.processElement(processor, ((ClSymbol) second))) return false; if (PsiTreeUtil.findCommonParent(place, list) == list) { ClVector paramVector = list.findFirstChildByClass(ClVector.class); if (paramVector == null && lastParent instanceof ClList) { paramVector = ((ClList) lastParent).findFirstChildByClass(ClVector.class); } if (paramVector != null) { for (ClSymbol symbol : paramVector.getAllSymbols()) { if (!ResolveUtil.processElement(processor, symbol)) return false; } } return true; } return true; } public static boolean isLocal(PsiElement element) { if (element instanceof ClSymbol) { ClSymbol symbol = (ClSymbol) element; final PsiElement parent = symbol.getParent(); if (parent instanceof ClList) { ClList list = (ClList) parent; if (FN.equals(list.getHeadText())) return true; } else if (parent instanceof ClVector) { final PsiElement par = parent.getParent(); if (par instanceof ClDef && ((ClDef) par).getSecondNonLeafElement() == element) return true; if (par instanceof ClList) { final String ht = ((ClList) par).getHeadText(); if (LOCAL_BINDINGS.contains(ht)) return true; } } } return false; } }