/* * Copyright 2010 Jon S Akhtar (Sylvanaar) * * 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 com.sylvanaar.idea.Lua.lang.psi.util; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; import com.intellij.util.IncorrectOperationException; import com.sylvanaar.idea.Lua.lang.luadoc.psi.api.LuaDocPsiElement; import com.sylvanaar.idea.Lua.lang.psi.LuaPsiElement; import com.sylvanaar.idea.Lua.lang.psi.LuaPsiFile; import com.sylvanaar.idea.Lua.lang.psi.LuaReferenceElement; import com.sylvanaar.idea.Lua.lang.psi.expressions.LuaIdentifierList; import com.sylvanaar.idea.Lua.lang.psi.statements.LuaBlock; import com.sylvanaar.idea.Lua.lang.psi.symbols.LuaSymbol; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; /** * User: jansorg * Date: 04.08.2009 * Time: 21:45:47 */ public class LuaPsiUtils { /** * Returns the depth in the tree this element has. * * @param element The element to lookup * @return The depth, 0 if it's at the top level */ public static int nestingLevel(PsiElement element) { int depth = 0; PsiElement current = element.getContext(); while (current != null) { depth++; current = current.getContext(); } return depth; } /** * Returns the depth in blocks this element has in the tree. * * @param element The element to lookup * @return The depth measured in blocks, 0 if it's at the top level */ public static int blockNestingLevel(PsiElement element) { int depth = 0; PsiElement current = findEnclosingBlock(element); while (current != null) { depth++; current = findEnclosingBlock(current); } return depth; } /** * Returns the element after the given element. It may either be the next sibling or the * next logical element after the given element (i.e. the element after the parent context). * If it's the last element of the file null is retunred. * * @param element The element to check * @return Next element or null */ @Nullable public static PsiElement elementAfter(PsiElement element) { ASTNode node = element.getNode(); ASTNode next = node != null ? node.getTreeNext() : null; return next != null ? next.getPsi() : null; /*if (element == null) return null; PsiElement next = element.getNextSibling(); if (next != null) { return next; } //check parent return elementAfter(element.getContext());*/ } /** * Returns the next logical block which contains this element. * * @param element The element to check * @return The containing block or null */ public static PsiElement findEnclosingBlock(PsiElement element) { while (element != null && element.getContext() != null) { element = element.getContext(); if (isValidContainer(element)) { return element; } } return null; } private static boolean isValidContainer(PsiElement element) { return element instanceof LuaBlock || element instanceof LuaPsiFile; } public static boolean processChildDeclarationsS(PsiElement parentContainer, PsiScopeProcessor processor, ResolveState resolveState, PsiElement parent, PsiElement place) { PsiElement child = parentContainer.getFirstChild(); while (child != null) { if (!child.processDeclarations(processor, resolveState, parent, place)) { return false; } child = child.getNextSibling(); } return true; } public static boolean processChildDeclarations(PsiElement element, PsiScopeProcessor processor, ResolveState substitutor, PsiElement lastParent, PsiElement place) { PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling(); while (run != null) { if (!run.processDeclarations(processor, substitutor, null, place)) return false; run = run.getPrevSibling(); } return true; } public static int getElementLineNumber(PsiElement element) { FileViewProvider fileViewProvider = element.getContainingFile().getViewProvider(); if (fileViewProvider.getDocument() != null) { return fileViewProvider.getDocument().getLineNumber(element.getTextOffset()) + 1; } return 0; } public static int getElementEndLineNumber(PsiElement element) { FileViewProvider fileViewProvider = element.getContainingFile().getViewProvider(); if (fileViewProvider.getDocument() != null) { return fileViewProvider.getDocument().getLineNumber(element.getTextOffset() + element.getTextLength()) + 1; } return 0; } @Nullable public static LuaPsiElement getCoveringPsiElement(@NotNull final PsiElement psiElement) { PsiElement current = psiElement; while (current != null) { if (current instanceof LuaPsiElement) { return (LuaPsiElement) current; } current = current.getParent(); } return null; } public static TextRange createRange(PsiElement node) { return TextRange.from(node.getTextOffset(), node.getTextLength()); } public static IElementType nodeType(PsiElement element) { ASTNode node = element.getNode(); if (node == null) { return null; } return node.getElementType(); } public static PsiElement findNextSibling(PsiElement start, IElementType ignoreType) { PsiElement current = start.getNextSibling(); while (current != null) { if (ignoreType != nodeType(current)) { return current; } current = current.getNextSibling(); } return null; } public static PsiElement findPreviousSibling(PsiElement start, IElementType ignoreType) { PsiElement current = start.getPrevSibling(); while (current != null) { if (ignoreType != nodeType(current)) { return current; } current = current.getPrevSibling(); } return null; } /** * Replaces the priginal element with the replacement. * * @param original The original element which should be replaced. * @param replacement The new element * @return The replaces element. Depending on the context of the original element it either the original element * or the replacement element. * @throws com.intellij.util.IncorrectOperationException * cant do it */ public static PsiElement replaceElement(PsiElement original, PsiElement replacement) throws IncorrectOperationException { try { try { return original.replace(replacement); } catch (IncorrectOperationException e) { //failed, try another way } catch (UnsupportedOperationException e) { //failed, try another way } PsiElement parent = original.getParent(); if (parent != null) { PsiElement inserted = parent.addBefore(replacement, original); original.delete(); return inserted; } else { //last try, not optimal original.getNode().replaceAllChildrenToChildrenOf(replacement.getNode()); return original; } } finally { } } public static boolean isLValue(LuaPsiElement element) { if (element instanceof LuaReferenceElement) if (((LuaReferenceElement) element).getElement().getParent().getParent() instanceof LuaIdentifierList) return true; if (element instanceof LuaSymbol) if (element.getParent().getParent() instanceof LuaIdentifierList) return true; return false; } @NotNull public static LuaDocPsiElement[] toPsiElementArray(@NotNull Collection<? extends LuaDocPsiElement> collection) { if (collection.isEmpty()) return LuaDocPsiElement.EMPTY_ARRAY; return collection.toArray(new LuaDocPsiElement[collection.size()]); } @NotNull public static LuaPsiElement[] toPsiElementArray(@NotNull Collection<? extends LuaPsiElement> collection) { if (collection.isEmpty()) return LuaPsiElement.EMPTY_ARRAY; return collection.toArray(new LuaPsiElement[collection.size()]); } @NotNull public static PsiElement[] toPsiElementArray(@NotNull Collection<? extends PsiElement> collection) { if (collection.isEmpty()) return PsiElement.EMPTY_ARRAY; return collection.toArray(new PsiElement[collection.size()]); } }