package org.jetbrains.plugins.clojure.psi.impl.defs; import com.intellij.lang.ASTNode; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; import com.intellij.psi.StubBasedPsiElement; import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.clojure.ClojureIcons; import org.jetbrains.plugins.clojure.lexer.ClojureTokenTypes; import org.jetbrains.plugins.clojure.psi.ClojurePsiElement; import org.jetbrains.plugins.clojure.psi.api.*; 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.list.ClListBaseImpl; import org.jetbrains.plugins.clojure.psi.resolve.ResolveUtil; import org.jetbrains.plugins.clojure.psi.stubs.api.ClDefStub; import org.jetbrains.plugins.clojure.psi.stubs.api.ClNsStub; import javax.swing.*; import java.util.ArrayList; /** * @author ilyas */ public class ClDefImpl extends ClListBaseImpl<ClDefStub> implements ClDef, StubBasedPsiElement<ClDefStub> { public ClDefImpl(ClDefStub stub, @NotNull IStubElementType nodeType) { super(stub, nodeType); } public ClDefImpl(ASTNode node) { super(node); } @Override public String toString() { return "ClDef"; } /** * @return Name of string symbol defined */ @Nullable public ClSymbol getNameSymbol() { final ClSymbol first = findChildByClass(ClSymbol.class); if (first == null) return null; return PsiTreeUtil.getNextSiblingOfType(first, ClSymbol.class); } public String getDefinedName() { ClSymbol sym = getNameSymbol(); if (sym != null) { String name = sym.getText(); assert name != null; return name; } return ""; } @Override @Nullable public String getName() { ClDefStub stub = getStub(); if (stub != null) { return stub.getName(); } return getDefinedName(); } @Override public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) { // Do not resolve identifier if (lastParent != null && lastParent.getParent() == this && lastParent instanceof ClSymbol) return true; //process parameters if (lastParent != null && lastParent.getParent() == this) { final ClVector paramVector = findChildByClass(ClVector.class); if (paramVector != null) { for (ClSymbol symbol : paramVector.getAllSymbols()) { if (!ResolveUtil.processElement(processor, symbol)) return false; } } // for recursive functions if (getNameSymbol() != null && lastParent != getNameSymbol() && !ResolveUtil.processElement(processor, getNameSymbol())) return false; else if (lastParent instanceof ClList) { // overloaded function (defn ([x] body)) ClList list = (ClList) lastParent; final PsiElement elem = list.getFirstNonLeafElement(); if (elem instanceof ClVector && !PsiTreeUtil.isAncestor(elem, place, false)) { final ClVector params = (ClVector) elem; if (params != null) { for (ClSymbol symbol : params.getAllSymbols()) { if (!ResolveUtil.processElement(processor, symbol)) return false; } } } } return true; } else { return ResolveUtil.processElement(processor, this); } } @Override public ItemPresentation getPresentation() { return new ItemPresentation() { public String getPresentableText() { return getPresentationText(); } @Nullable public String getLocationString() { String name = getContainingFile().getName(); //todo show namespace return "(in " + name + ")"; } @Nullable public Icon getIcon(boolean open) { return ClDefImpl.this.getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS); } @Nullable public TextAttributesKey getTextAttributesKey() { return null; } }; } public String getPresentationText() { final StringBuffer buffer = new StringBuffer(); final String name = getName(); if (name == null) return "<undefined>"; buffer.append(name).append(" "); buffer.append(getParameterString()); return buffer.toString(); } public String getDocString() { PsiElement element = getSecondNonLeafElement(); if (element == null) return null; element = element.getNextSibling(); while (element != null && isWrongElement(element)) { element = element.getNextSibling(); } // For doc String final String s = processString(element); if (s != null) return s; final ClMetadata meta = getMeta(); if (meta == null) return null; return processMetadata(meta); } private String processString(PsiElement element) { if (element instanceof ClLiteral && element.getFirstChild().getNode().getElementType() == ClojureTokenTypes.STRING_LITERAL) { final String rawText = element.getText(); final String str = StringUtil.trimStart(StringUtil.trimEnd(rawText, "\""), "\""); return str.replace("\n ", "\n").replace("\n","<br/>"); } return null; } private String processMetadata(@NotNull ClMetadata meta) { final StringBuffer buffer = new StringBuffer(); final ClojurePsiElement args = meta.getValue("arglists"); if (args != null) { if (args instanceof ClQuotedForm) { ClQuotedForm form = (ClQuotedForm) args; if (form.getQuotedElement() instanceof ClList) { ClList list = (ClList) form.getQuotedElement(); final ArrayList<String> chunks = new ArrayList<String>(); if (list != null) { for (PsiElement element : list.getChildren()) { if (element instanceof ClVector) { chunks.add(element.getText()); } } } buffer.append("Arguments:\n"); for (String chunk : chunks) { buffer.append("<b>").append(chunk.trim()).append("</b>").append("\n"); } buffer.append("<br/>"); } } } final ClojurePsiElement value = meta.getValue("doc"); if (value != null) { buffer.append(processString(value)); } return buffer.toString(); } @Override public Icon getIcon(int flags) { return ClojureIcons.FUNCTION; } public PsiElement setName(@NotNull @NonNls String name) throws IncorrectOperationException { final ClSymbol sym = getNameSymbol(); if (sym != null) sym.setName(name); return this; } @Override public int getTextOffset() { ClDefStub stub = getStub(); if (stub != null) { return stub.getTextOffset(); } final ClSymbol symbol = getNameSymbol(); if (symbol != null) { return symbol.getTextRange().getStartOffset(); } return super.getTextOffset(); } public String getParameterString() { final ClVector params = findChildByClass(ClVector.class); return params == null ? "" : params.getText(); } public ClMetadata getMeta() { for (PsiElement element : getChildren()) { if (element instanceof ClMetadata) { return (ClMetadata) element; } } return null; } public String getMethodInfo() { final ClSymbol sym = getNameSymbol(); if (sym == null) return ""; PsiElement next = sym.getNextSibling(); while (next instanceof LeafPsiElement) next = next.getNextSibling(); return next == null ? "" : next.getText(); } }