-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpsi-utils.kt
175 lines (139 loc) · 5.86 KB
/
psi-utils.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package me.hydos.fukkitdevelopment
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.roots.impl.OrderEntryUtil
import com.intellij.psi.ElementManipulator
import com.intellij.psi.ElementManipulators
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementResolveResult
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiKeyword
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiModifier.ModifierConstant
import com.intellij.psi.PsiParameter
import com.intellij.psi.PsiParameterList
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiType
import com.intellij.psi.ResolveResult
import com.intellij.psi.filters.ElementFilter
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.TypeConversionUtil
import com.intellij.refactoring.changeSignature.ChangeSignatureUtil
import com.siyeh.ig.psiutils.ImportUtils
import java.util.stream.Stream
// Parent
fun PsiElement.findModule(): Module? = ModuleUtilCore.findModuleForPsiElement(this)
fun PsiElement.findContainingClass(): PsiClass? = findParent(resolveReferences = false)
fun PsiElement.findReferencedClass(): PsiClass? = findParent(resolveReferences = true)
fun PsiElement.findReferencedMember(): PsiMember? = findParent({ it is PsiClass }, resolveReferences = true)
fun PsiElement.findContainingMember(): PsiMember? = findParent({ it is PsiClass }, resolveReferences = false)
fun PsiElement.findContainingMethod(): PsiMethod? = findParent({ it is PsiClass }, resolveReferences = false)
private inline fun <reified T : PsiElement> PsiElement.findParent(resolveReferences: Boolean): T? {
return findParent({ false }, resolveReferences)
}
private inline fun <reified T : PsiElement> PsiElement.findParent(
stop: (PsiElement) -> Boolean,
resolveReferences: Boolean
): T? {
var el: PsiElement = this
while (true) {
if (resolveReferences && el is PsiReference) {
el = el.resolve() ?: return null
}
if (el is T) {
return el
}
if (el is PsiFile || el is PsiDirectory || stop(el)) {
return null
}
el = el.parent ?: return null
}
}
// Children
fun PsiClass.findFirstMember(): PsiMember? = findChild()
fun PsiElement.findNextMember(): PsiMember? = findSibling(true)
private inline fun <reified T : PsiElement> PsiElement.findChild(): T? {
return firstChild?.findSibling(strict = false)
}
private inline fun <reified T : PsiElement> PsiElement.findSibling(strict: Boolean): T? {
var sibling = if (strict) nextSibling ?: return null else this
while (true) {
if (sibling is T) {
return sibling
}
sibling = sibling.nextSibling ?: return null
}
}
fun PsiElement.findKeyword(name: String): PsiKeyword? {
forEachChild {
if (it is PsiKeyword && it.text == name) {
return it
}
}
return null
}
private inline fun PsiElement.forEachChild(func: (PsiElement) -> Unit) {
firstChild?.forEachSibling(func, strict = false)
}
private inline fun PsiElement.forEachSibling(func: (PsiElement) -> Unit, strict: Boolean) {
var sibling = if (strict) nextSibling ?: return else this
while (true) {
func(sibling)
sibling = sibling.nextSibling ?: return
}
}
inline fun PsiElement.findLastChild(condition: (PsiElement) -> Boolean): PsiElement? {
var child = firstChild ?: return null
var lastChild: PsiElement? = null
while (true) {
if (condition(child)) {
lastChild = child
}
child = child.nextSibling ?: return lastChild
}
}
fun <T : Any> Sequence<T>.filter(filter: ElementFilter?, context: PsiElement): Sequence<T> {
filter ?: return this
return filter { filter.isAcceptable(it, context) }
}
fun PsiParameterList.synchronize(newParams: List<PsiParameter>) {
ChangeSignatureUtil.synchronizeList(this, newParams, { it.parameters.asList() }, BooleanArray(newParams.size))
}
val PsiElement.constantValue: Any?
get() = JavaPsiFacade.getInstance(project).constantEvaluationHelper.computeConstantExpression(this)
val PsiElement.constantStringValue: String?
get() = constantValue as? String
private val ACCESS_MODIFIERS =
listOf(PsiModifier.PUBLIC, PsiModifier.PROTECTED, PsiModifier.PRIVATE, PsiModifier.PACKAGE_LOCAL)
fun isAccessModifier(@ModifierConstant modifier: String): Boolean {
return modifier in ACCESS_MODIFIERS
}
infix fun PsiElement.equivalentTo(other: PsiElement): Boolean {
return manager.areElementsEquivalent(this, other)
}
fun PsiType?.isErasureEquivalentTo(other: PsiType?): Boolean {
// TODO: Do more checks for generics instead
return TypeConversionUtil.erasure(this) == TypeConversionUtil.erasure(other)
}
val PsiMethod.nameAndParameterTypes: String
get() = "$name(${parameterList.parameters.joinToString(", ") { it.type.presentableText }})"
val <T : PsiElement> T.manipulator: ElementManipulator<T>?
get() = ElementManipulators.getManipulator(this)
inline fun <T> PsiElement.cached(crossinline compute: () -> T): T {
return CachedValuesManager.getCachedValue(this) { CachedValueProvider.Result.create(compute(), this) }
}
fun LookupElementBuilder.withImportInsertion(toImport: List<PsiClass>): LookupElementBuilder =
this.withInsertHandler { insertionContext, _ ->
toImport.forEach { ImportUtils.addImportIfNeeded(it, insertionContext.file) }
}