Moved language independent methods from jetPsiUtil.kt to psiUtils.kt + reordered and gropped them

This commit is contained in:
Valentin Kipyatkov
2015-05-28 22:32:53 +03:00
parent 65383cf68b
commit 0e8a43f746
3 changed files with 283 additions and 250 deletions
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.psi.psiUtil
import com.intellij.psi.PsiElement
public data class PsiChildRange(public val first: PsiElement?, public val last: PsiElement?) : Sequence<PsiElement> {
init {
if (first == null) {
assert(last == null)
}
else {
assert(first.getParent() == last!!.getParent())
}
}
public val isEmpty: Boolean
get() = first == null
override fun iterator(): Iterator<PsiElement> {
val sequence = if (first == null) {
emptySequence<PsiElement>()
}
else {
val afterLast = last!!.getNextSibling()
first.siblings().takeWhile { it != afterLast }
}
return sequence.iterator()
}
companion object {
public val EMPTY: PsiChildRange = PsiChildRange(null, null)
public fun singleElement(element: PsiElement): PsiChildRange = PsiChildRange(element, element)
}
}
@@ -47,71 +47,6 @@ public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? {
}
}
public fun PsiElement.getParentOfTypesAndPredicate<T: PsiElement>(
strict : Boolean = false, vararg parentClasses : Class<T>, predicate: (T) -> Boolean
) : T? {
var element = if (strict) getParent() else this
while (element != null) {
@suppress("UNCHECKED_CAST")
when {
(parentClasses.isEmpty() || parentClasses.any {parentClass -> parentClass.isInstance(element)}) && predicate(element!! as T) ->
return element as T
element is PsiFile ->
return null
else ->
element = element!!.getParent()
}
}
return null
}
public fun PsiElement.getNonStrictParentOfType<T: PsiElement>(parentClass : Class<T>) : T? {
return PsiTreeUtil.getParentOfType(this, parentClass, false)
}
inline public fun PsiElement.getParentOfType<reified T: PsiElement>(strict: Boolean): T? {
return PsiTreeUtil.getParentOfType(this, javaClass<T>(), strict)
}
inline public fun PsiElement.getStrictParentOfType<reified T: PsiElement>(): T? {
return PsiTreeUtil.getParentOfType(this, javaClass<T>(), true)
}
inline public fun PsiElement.getNonStrictParentOfType<reified T: PsiElement>(): T? {
return PsiTreeUtil.getParentOfType(this, javaClass<T>(), false)
}
inline public fun PsiElement.getChildOfType<reified T: PsiElement>(): T? {
return PsiTreeUtil.getChildOfType(this, javaClass<T>())
}
inline public fun PsiElement.getChildrenOfType<reified T: PsiElement>(): Array<T> {
return PsiTreeUtil.getChildrenOfType(this, javaClass<T>()) ?: arrayOf()
}
public fun PsiElement.getNextSiblingIgnoringWhitespaceAndComments(): PsiElement? {
var current = this
do {
current = current.getNextSibling()
if (current == null) return null
}
while (current is PsiComment || current is PsiWhiteSpace)
return current
}
public fun PsiElement?.isAncestor(element: PsiElement, strict: Boolean = false): Boolean {
return PsiTreeUtil.isAncestor(this, element, strict)
}
public fun <T: PsiElement> T.getIfChildIsInBranch(element: PsiElement, branch: T.() -> PsiElement?): T? {
return if (branch().isAncestor(element)) this else null
}
inline public fun PsiElement.getParentOfTypeAndBranch<reified T: PsiElement>(strict: Boolean = false, noinline branch: T.() -> PsiElement?) : T? {
return getParentOfType<T>(strict)?.getIfChildIsInBranch(this, branch)
}
public fun JetClassOrObject.effectiveDeclarations(): List<JetDeclaration> =
when(this) {
is JetClass ->
@@ -207,8 +142,6 @@ public fun <T: JetClassOrObject> StubBasedPsiElementBase<out KotlinClassOrObject
return result
}
public fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUtil.isInScope(this, element)
public fun JetClass.isInheritable(): Boolean {
return isInterface() || hasModifier(JetTokens.OPEN_KEYWORD) || hasModifier(JetTokens.ABSTRACT_KEYWORD)
}
@@ -342,13 +275,6 @@ public fun PsiDirectory.getPackage(): PsiPackage? = JavaDirectoryService.getInst
public fun JetModifierListOwner.isPrivate(): Boolean = hasModifier(JetTokens.PRIVATE_KEYWORD)
public fun PsiElement.isInsideOf(elements: Iterable<PsiElement>): Boolean = elements.any { it.isAncestor(this) }
public tailRecursive fun PsiElement.getOutermostParentContainedIn(container: PsiElement): PsiElement? {
val parent = getParent()
return if (parent == container) this else parent?.getOutermostParentContainedIn(container)
}
public fun JetSimpleNameExpression.getReceiverExpression(): JetExpression? {
val parent = getParent()
when {
@@ -407,55 +333,11 @@ public fun JetExpression.isFunctionLiteralOutsideParentheses(): Boolean {
}
}
public fun PsiElement.siblings(forward: Boolean = true, withItself: Boolean = true): Sequence<PsiElement> {
val stepFun = if (forward) { e: PsiElement -> e.getNextSibling() } else { e: PsiElement -> e.getPrevSibling() }
val sequence = sequence(this, stepFun)
return if (withItself) sequence else sequence.drop(1)
}
public fun ASTNode.siblings(forward: Boolean = true, withItself: Boolean = true): Sequence<ASTNode> {
val stepFun = if (forward) { node: ASTNode -> node.getTreeNext() } else { e: ASTNode -> e.getTreeNext() }
val sequence = sequence(this, stepFun)
return if (withItself) sequence else sequence.drop(1)
}
public fun PsiElement.parents(withItself: Boolean = true): Sequence<PsiElement> {
val sequence = sequence(this) { if (it is PsiFile) null else it.getParent() }
return if (withItself) sequence else sequence.drop(1)
}
public fun ASTNode.parents(withItself: Boolean = true): Sequence<ASTNode> {
val sequence = sequence(this) { it.getTreeParent() }
return if (withItself) sequence else sequence.drop(1)
}
public fun JetExpression.getAssignmentByLHS(): JetBinaryExpression? {
val parent = getParent() as? JetBinaryExpression ?: return null
return if (JetPsiUtil.isAssignment(parent) && parent.getLeft() == this) parent else null
}
public fun PsiElement.prevLeaf(skipEmptyElements: Boolean = false): PsiElement?
= PsiTreeUtil.prevLeaf(this, skipEmptyElements)
public fun PsiElement.nextLeaf(skipEmptyElements: Boolean = false): PsiElement?
= PsiTreeUtil.nextLeaf(this, skipEmptyElements)
public fun PsiElement.prevLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
var leaf = prevLeaf()
while (leaf != null && !filter(leaf)) {
leaf = leaf.prevLeaf()
}
return leaf
}
public fun PsiElement.nextLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
var leaf = nextLeaf()
while (leaf != null && !filter(leaf)) {
leaf = leaf.nextLeaf()
}
return leaf
}
public fun JetExpression.isDotReceiver(): Boolean =
(getParent() as? JetDotQualifiedExpression)?.getReceiverExpression() == this
@@ -481,42 +363,11 @@ public fun JetStringTemplateExpression.getContentRange(): TextRange {
public fun JetStringTemplateExpression.isSingleQuoted(): Boolean
= getNode().getFirstChildNode().getTextLength() == 1
public fun PsiFile.elementsInRange(range: TextRange): List<PsiElement> {
var offset = range.getStartOffset()
val result = ArrayList<PsiElement>()
while (offset < range.getEndOffset()) {
val currentRange = TextRange(offset, range.getEndOffset())
val leaf = findFirstLeafWhollyInRange(this, currentRange) ?: break
val element = leaf
.parents(withItself = true)
.first {
val parent = it.getParent()
it is PsiFile || parent.getTextRange() !in currentRange
}
result.add(element)
offset = element.endOffset
}
return result
}
private fun findFirstLeafWhollyInRange(file: PsiFile, range: TextRange): PsiElement? {
var element = file.findElementAt(range.getStartOffset()) ?: return null
var elementRange = element.getTextRange()
if (elementRange.getStartOffset() < range.getStartOffset()) {
element = element.nextLeaf(skipEmptyElements = true) ?: return null
elementRange = element.getTextRange()
}
assert(elementRange.getStartOffset() >= range.getStartOffset())
return if (elementRange.getEndOffset() <= range.getEndOffset()) element else null
}
fun JetNamedDeclaration.getValueParameters(): List<JetParameter> {
public fun JetNamedDeclaration.getValueParameters(): List<JetParameter> {
return getValueParameterList()?.getParameters() ?: Collections.emptyList()
}
fun JetNamedDeclaration.getValueParameterList(): JetParameterList? {
public fun JetNamedDeclaration.getValueParameterList(): JetParameterList? {
return when (this) {
is JetCallableDeclaration -> getValueParameterList()
is JetClass -> getPrimaryConstructorParameterList()
@@ -524,26 +375,6 @@ fun JetNamedDeclaration.getValueParameterList(): JetParameterList? {
}
}
public fun PsiElement.getElementTextWithContext(): String {
if (this is PsiFile) {
return getContainingFile().getText()
}
// Find parent for element among file children
val topLevelElement = PsiTreeUtil.findFirstParent(this, { it.getParent() is PsiFile }) ?:
throw AssertionError("For non-file element we should always be able to find parent in file children")
val startContextOffset = topLevelElement.startOffset
val elementContextOffset = getTextRange().getStartOffset()
val inFileParentOffset = elementContextOffset - startContextOffset
return StringBuilder(topLevelElement.getText())
.insert(inFileParentOffset, "<caret>")
.insert(0, "File name: ${getContainingFile().getName()}\n")
.toString()
}
// Calls `block` on each descendant of T type
// Note, that calls happen in order of DFS-exit, so deeper nodes are applied earlier
public inline fun <reified T : JetElement> forEachDescendantOfTypeVisitor(noinline block: (T) -> Unit): JetVisitorVoid {
@@ -561,46 +392,6 @@ public inline fun <reified T : JetElement, R> flatMapDescendantsOfTypeVisitor(ac
return forEachDescendantOfTypeVisitor<T> { accumulator.addAll(map(it)) }
}
public inline fun <reified T : PsiElement> PsiElement.forEachDescendantOfType(noinline action: (T) -> Unit) {
this.accept(object : PsiRecursiveElementVisitor(){
override fun visitElement(element: PsiElement) {
super.visitElement(element)
if (element is T) {
action(element)
}
}
})
}
public inline fun <reified T : PsiElement> PsiElement.anyDescendantOfType(noinline predicate: (T) -> Boolean = { true }): Boolean {
return findDescendantOfType(predicate) != null
}
public inline fun <reified T : PsiElement> PsiElement.findDescendantOfType(noinline predicate: (T) -> Boolean = { true }): T? {
var result: T? = null
this.accept(object : PsiRecursiveElementVisitor(){
override fun visitElement(element: PsiElement) {
if (result != null) return
if (element is T && predicate(element)) {
result = element
return
}
super.visitElement(element)
}
})
return result
}
public inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List<T> {
val result = ArrayList<T>()
forEachDescendantOfType<T> {
if (predicate(it)) {
result.add(it)
}
}
return result
}
public fun PsiFile.getFqNameByDirectory(): FqName {
val qualifiedNameByDirectory = getParent()?.getPackage()?.getQualifiedName()
return qualifiedNameByDirectory?.let { FqName(it) } ?: FqName.ROOT
@@ -16,10 +16,182 @@
package org.jetbrains.kotlin.psi.psiUtil
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.*
import com.intellij.psi.search.PsiSearchScopeUtil
import com.intellij.psi.search.SearchScope
import com.intellij.psi.util.PsiTreeUtil
import java.util.ArrayList
//TODO: move here more functions from jetPsiUtil.kt
// NOTE: in this file we collect only LANGUAGE INDEPENDENT methods working with PSI and not modifying it
// ----------- Walking children/siblings/parents -------------------------------------------------------------------------------------------
public val PsiElement.allChildren: PsiChildRange
get() {
val first = getFirstChild()
return if (first != null) PsiChildRange(first, getLastChild()) else PsiChildRange.EMPTY
}
public fun PsiElement.siblings(forward: Boolean = true, withItself: Boolean = true): Sequence<PsiElement> {
val stepFun = if (forward) { e: PsiElement -> e.getNextSibling() } else { e: PsiElement -> e.getPrevSibling() }
val sequence = sequence(this, stepFun)
return if (withItself) sequence else sequence.drop(1)
}
public fun ASTNode.siblings(forward: Boolean = true, withItself: Boolean = true): Sequence<ASTNode> {
val stepFun = if (forward) { node: ASTNode -> node.getTreeNext() } else { e: ASTNode -> e.getTreeNext() }
val sequence = sequence(this, stepFun)
return if (withItself) sequence else sequence.drop(1)
}
public fun PsiElement.parents(withItself: Boolean = true): Sequence<PsiElement> {
val sequence = sequence(this) { if (it is PsiFile) null else it.getParent() }
return if (withItself) sequence else sequence.drop(1)
}
public fun ASTNode.parents(withItself: Boolean = true): Sequence<ASTNode> {
val sequence = sequence(this) { it.getTreeParent() }
return if (withItself) sequence else sequence.drop(1)
}
public fun PsiElement.prevLeaf(skipEmptyElements: Boolean = false): PsiElement?
= PsiTreeUtil.prevLeaf(this, skipEmptyElements)
public fun PsiElement.nextLeaf(skipEmptyElements: Boolean = false): PsiElement?
= PsiTreeUtil.nextLeaf(this, skipEmptyElements)
public fun PsiElement.prevLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
var leaf = prevLeaf()
while (leaf != null && !filter(leaf)) {
leaf = leaf.prevLeaf()
}
return leaf
}
public fun PsiElement.nextLeaf(filter: (PsiElement) -> Boolean): PsiElement? {
var leaf = nextLeaf()
while (leaf != null && !filter(leaf)) {
leaf = leaf.nextLeaf()
}
return leaf
}
public fun PsiElement.getParentOfTypesAndPredicate<T: PsiElement>(
strict : Boolean = false, vararg parentClasses : Class<T>, predicate: (T) -> Boolean
) : T? {
var element = if (strict) getParent() else this
while (element != null) {
@suppress("UNCHECKED_CAST")
when {
(parentClasses.isEmpty() || parentClasses.any {parentClass -> parentClass.isInstance(element)}) && predicate(element!! as T) ->
return element as T
element is PsiFile ->
return null
else ->
element = element!!.getParent()
}
}
return null
}
public fun PsiElement.getNonStrictParentOfType<T: PsiElement>(parentClass : Class<T>) : T? {
return PsiTreeUtil.getParentOfType(this, parentClass, false)
}
inline public fun PsiElement.getParentOfType<reified T: PsiElement>(strict: Boolean): T? {
return PsiTreeUtil.getParentOfType(this, javaClass<T>(), strict)
}
inline public fun PsiElement.getStrictParentOfType<reified T: PsiElement>(): T? {
return PsiTreeUtil.getParentOfType(this, javaClass<T>(), true)
}
inline public fun PsiElement.getNonStrictParentOfType<reified T: PsiElement>(): T? {
return PsiTreeUtil.getParentOfType(this, javaClass<T>(), false)
}
inline public fun PsiElement.getChildOfType<reified T: PsiElement>(): T? {
return PsiTreeUtil.getChildOfType(this, javaClass<T>())
}
inline public fun PsiElement.getChildrenOfType<reified T: PsiElement>(): Array<T> {
return PsiTreeUtil.getChildrenOfType(this, javaClass<T>()) ?: arrayOf()
}
public fun PsiElement.getNextSiblingIgnoringWhitespaceAndComments(): PsiElement? {
var current = this
do {
current = current.getNextSibling()
if (current == null) return null
}
while (current is PsiComment || current is PsiWhiteSpace)
return current
}
public fun PsiElement?.isAncestor(element: PsiElement, strict: Boolean = false): Boolean {
return PsiTreeUtil.isAncestor(this, element, strict)
}
public fun <T: PsiElement> T.getIfChildIsInBranch(element: PsiElement, branch: T.() -> PsiElement?): T? {
return if (branch().isAncestor(element)) this else null
}
public inline fun PsiElement.getParentOfTypeAndBranch<reified T: PsiElement>(strict: Boolean = false, noinline branch: T.() -> PsiElement?) : T? {
return getParentOfType<T>(strict)?.getIfChildIsInBranch(this, branch)
}
public tailRecursive fun PsiElement.getOutermostParentContainedIn(container: PsiElement): PsiElement? {
val parent = getParent()
return if (parent == container) this else parent?.getOutermostParentContainedIn(container)
}
public fun PsiElement.isInsideOf(elements: Iterable<PsiElement>): Boolean = elements.any { it.isAncestor(this) }
// -------------------- Recursive tree visiting --------------------------------------------------------------------------------------------
public inline fun <reified T : PsiElement> PsiElement.forEachDescendantOfType(noinline action: (T) -> Unit) {
this.accept(object : PsiRecursiveElementVisitor(){
override fun visitElement(element: PsiElement) {
super.visitElement(element)
if (element is T) {
action(element)
}
}
})
}
public inline fun <reified T : PsiElement> PsiElement.anyDescendantOfType(noinline predicate: (T) -> Boolean = { true }): Boolean {
return findDescendantOfType(predicate) != null
}
public inline fun <reified T : PsiElement> PsiElement.findDescendantOfType(noinline predicate: (T) -> Boolean = { true }): T? {
var result: T? = null
this.accept(object : PsiRecursiveElementVisitor(){
override fun visitElement(element: PsiElement) {
if (result != null) return
if (element is T && predicate(element)) {
result = element
return
}
super.visitElement(element)
}
})
return result
}
public inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List<T> {
val result = ArrayList<T>()
forEachDescendantOfType<T> {
if (predicate(it)) {
result.add(it)
}
}
return result
}
// ----------- Working with offsets, ranges and texts ----------------------------------------------------------------------------------------------
public val PsiElement.startOffset: Int
get() = getTextRange().getStartOffset()
@@ -37,43 +209,6 @@ public fun PsiElement.getStartOffsetIn(ancestor: PsiElement): Int {
return offset
}
public data class PsiChildRange(public val first: PsiElement?, public val last: PsiElement?) : Sequence<PsiElement> {
init {
if (first == null) {
assert(last == null)
}
else {
assert(first.getParent() == last!!.getParent())
}
}
public val isEmpty: Boolean
get() = first == null
override fun iterator(): Iterator<PsiElement> {
val sequence = if (first == null) {
emptySequence<PsiElement>()
}
else {
val afterLast = last!!.getNextSibling()
first.siblings().takeWhile { it != afterLast }
}
return sequence.iterator()
}
companion object {
public val EMPTY: PsiChildRange = PsiChildRange(null, null)
public fun singleElement(element: PsiElement): PsiChildRange = PsiChildRange(element, element)
}
}
public val PsiElement.allChildren: PsiChildRange
get() {
val first = getFirstChild()
return if (first != null) PsiChildRange(first, getLastChild()) else PsiChildRange.EMPTY
}
public val PsiChildRange.textRange: TextRange?
get() {
if (isEmpty) return null
@@ -85,3 +220,60 @@ public fun PsiChildRange.getText(): String {
return this.map { it.getText() }.joinToString("")
}
public fun PsiFile.elementsInRange(range: TextRange): List<PsiElement> {
var offset = range.getStartOffset()
val result = ArrayList<PsiElement>()
while (offset < range.getEndOffset()) {
val currentRange = TextRange(offset, range.getEndOffset())
val leaf = findFirstLeafWhollyInRange(this, currentRange) ?: break
val element = leaf
.parents(withItself = true)
.first {
val parent = it.getParent()
it is PsiFile || parent.getTextRange() !in currentRange
}
result.add(element)
offset = element.endOffset
}
return result
}
private fun findFirstLeafWhollyInRange(file: PsiFile, range: TextRange): PsiElement? {
var element = file.findElementAt(range.getStartOffset()) ?: return null
var elementRange = element.getTextRange()
if (elementRange.getStartOffset() < range.getStartOffset()) {
element = element.nextLeaf(skipEmptyElements = true) ?: return null
elementRange = element.getTextRange()
}
assert(elementRange.getStartOffset() >= range.getStartOffset())
return if (elementRange.getEndOffset() <= range.getEndOffset()) element else null
}
// ---------------------------------- Debug/logging ----------------------------------------------------------------------------------------
public fun PsiElement.getElementTextWithContext(): String {
if (this is PsiFile) {
return getContainingFile().getText()
}
// Find parent for element among file children
val topLevelElement = PsiTreeUtil.findFirstParent(this, { it.getParent() is PsiFile }) ?:
throw AssertionError("For non-file element we should always be able to find parent in file children")
val startContextOffset = topLevelElement.startOffset
val elementContextOffset = getTextRange().getStartOffset()
val inFileParentOffset = elementContextOffset - startContextOffset
return StringBuilder(topLevelElement.getText())
.insert(inFileParentOffset, "<caret>")
.insert(0, "File name: ${getContainingFile().getName()}\n")
.toString()
}
// -----------------------------------------------------------------------------------------------------------------------------------------
public fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUtil.isInScope(this, element)