/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.lang.psi; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.TokenType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.SmartList; /** * @author: Fedor.Korotkov */ public class UsefulPsiTreeUtil { public static ASTNode[] findChildrenRange(ASTNode[] elements, int startOffset, int endOffset) { int i = findChildIndex(elements, startOffset); int j = findChildIndex(elements, endOffset); i = i == -1 ? 0 : i; j = j == -1 ? elements.length : j; // trim while(0 < j && j < elements.length && elements[j].getElementType() == TokenType.WHITE_SPACE) { --j; } int to = j; if(j < elements.length && elements[j].getElementType() != CSharpTokens.SEMICOLON) { // try eat until ';' while(j + 1 < elements.length && (elements[j + 1].getElementType() == CSharpTokens.SEMICOLON || elements[j + 1].getElementType() == TokenType.WHITE_SPACE)) { ++j; if(elements[j].getElementType() == CSharpTokens.SEMICOLON) { to = j; } } } to = Math.min(elements.length, to + 1); if(to < i) { return ASTNode.EMPTY_ARRAY; } return Arrays.copyOfRange(elements, i, to); } private static int findChildIndex(ASTNode[] children, int offset) { for(int i = 0, length = children.length; i < length; i++) { ASTNode child = children[i]; if(child.getTextRange().contains(offset)) { return i; } } return -1; } public static boolean isWhitespaceOrComment(PsiElement element) { return element instanceof PsiWhiteSpace || element instanceof PsiComment; } @Nullable public static List<PsiElement> getPathToParentOfType(@Nullable PsiElement element, @NotNull Class<? extends PsiElement> aClass) { if(element == null) { return null; } final List<PsiElement> result = new ArrayList<PsiElement>(); while(element != null) { result.add(element); if(aClass.isInstance(element)) { return result; } if(element instanceof PsiFile) { return null; } element = element.getParent(); } return null; } @Nullable public static PsiElement getNextSiblingSkippingWhiteSpacesAndComments(@NotNull PsiElement sibling) { return getSiblingSkippingCondition(sibling, new Function<PsiElement, PsiElement>() { @Nullable @Override public PsiElement fun(PsiElement element) { return element.getNextSibling(); } }, new Condition<PsiElement>() { @Override public boolean value(PsiElement element) { return isWhitespaceOrComment(element); } }, true ); } @Nullable public static PsiElement getPrevSiblingSkipWhiteSpacesAndComments(@Nullable PsiElement sibling, boolean strictly) { return getPrevSiblingSkippingCondition(sibling, new Condition<PsiElement>() { @Override public boolean value(PsiElement element) { return isWhitespaceOrComment(element); } }, strictly); } @Nullable public static ASTNode getPrevSiblingSkipWhiteSpacesAndComments(@Nullable ASTNode sibling) { if(sibling == null) { return null; } ASTNode result = sibling.getTreePrev(); while(result != null && isWhitespaceOrComment(result.getPsi())) { result = result.getTreePrev(); } return result; } @Nullable public static PsiElement getPrevSiblingSkipWhiteSpaces(@Nullable PsiElement sibling, boolean strictly) { return getPrevSiblingSkippingCondition(sibling, new Condition<PsiElement>() { @Override public boolean value(PsiElement element) { return element instanceof PsiWhiteSpace; } }, strictly); } @Nullable public static PsiElement getPrevSiblingSkippingCondition(@Nullable PsiElement sibling, Condition<PsiElement> condition, boolean strictly) { return getSiblingSkippingCondition(sibling, new Function<PsiElement, PsiElement>() { @Nullable @Override public PsiElement fun(PsiElement element) { return element.getPrevSibling(); } }, condition, strictly); } @Nullable public static PsiElement getSiblingSkippingCondition(@Nullable PsiElement sibling, Function<PsiElement, PsiElement> nextSibling, Condition<PsiElement> condition, boolean strictly) { if(sibling == null) { return null; } if(sibling instanceof PsiFile) { return sibling; } PsiElement result = strictly ? nextSibling.fun(sibling) : sibling; while(result != null && !(result instanceof PsiFile) && condition.value(result)) { result = nextSibling.fun(result); } return result; } @Nullable public static <T extends PsiElement> T[] getChildrenOfType(@Nullable PsiElement element, @NotNull Class<T> aClass, @Nullable PsiElement lastParent) { if(element == null) { return null; } List<T> result = null; for(PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { if(lastParent == child) { break; } if(aClass.isInstance(child)) { if(result == null) { result = new SmartList<T>(); } //noinspection unchecked result.add((T) child); } } return result == null ? null : ArrayUtil.toObjectArray(result, aClass); } public static boolean isAncestor(@NotNull PsiElement element, List<PsiElement> children, boolean strict) { for(PsiElement child : children) { if(child != null && !PsiTreeUtil.isAncestor(element, child, strict)) { return false; } } return true; } }