Skip to content

Instantly share code, notes, and snippets.

@dkandalov
Last active December 12, 2015 01:48
Show Gist options
  • Save dkandalov/4694040 to your computer and use it in GitHub Desktop.
Save dkandalov/4694040 to your computer and use it in GitHub Desktop.
Infer Type Parameter
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiReferenceParameterList
import com.intellij.psi.PsiStatement
import com.intellij.psi.PsiTypeElement
import com.intellij.psi.PsiVariable
import com.intellij.psi.PsiMethod
import static intellijeval.PluginUtil.*
def <T> T findParent(PsiElement element, Closure<T> matches) {
if (element == null) null
else if (matches(element)) element
else findParent(element.parent, matches)
}
def <T> T findChild(PsiElement element, Closure<T> matches) {
if (element == null) null
else if (matches(element)) element
else element.children.findResult{ findChild(it, matches) }
}
final class Matcher {
final String varName
final Closure closure
final context
Matcher(String varName = null, context = null, Closure closure = {true}) {
this.varName = varName
this.closure = closure
this.context = context
}
def matchesThis(object) {
def result = closure(object)
if (result) {
if (varName != null)
context.put(varName, object)
context
} else {
object?.children.findResult{child -> matchesThis(child)}
}
}
}
class ObjMatch {
final context = [:]
def match(object, Closure<Matcher> createMatcher) {
createMatcher.delegate = this
createMatcher().matchesThis(object)
context
}
def m(String varName = null, Closure matchesThis = {true}) {
matchesThis.delegate = this
new Matcher(varName, context, matchesThis)
}
def and(String varName = null, Matcher... matchers) {
new Matcher(varName, context, {object -> matchers.every{
it.matchesThis(object)
}})
}
}
def classHasTypeParameters(PsiTypeElement typeElement) {
def type = typeElement.type
if (type instanceof PsiClassType) {
type.resolve().hasTypeParameters()
} else {
false
}
}
registerAction("Infer Type Parameter", "ctrl alt I", "ToolsMenu") { event ->
def editor = currentEditorIn(event.project)
def psiFile = currentPsiFileIn(event.project)
def element = psiFile.findElementAt(editor.caretModel.offset)
def statement = findParent(element){ it instanceof PsiStatement }
if (statement == null) return
def context = new ObjMatch().match(statement) { and(
m("var", {it instanceof PsiVariable && match(it, {
m("varTypeParam", {it instanceof PsiReferenceParameterList &&
it.parent.parent instanceof PsiTypeElement && classHasTypeParameters(it.parent.parent) &&
it.typeArguments.size() == 0})
})}),
m("methodCall", {it instanceof PsiMethodCallExpression && match(it?.methodExpression?.advancedResolve(true).element, {
m("methodDef", {it instanceof PsiMethod && match(it, {
m("methTypeParam", {it instanceof PsiReferenceParameterList && it.typeArguments.size() > 0})
})})
})})
)}
show(context?.entrySet().join("\n") + "_")
return
/*
def var = findChild(statement) { it instanceof PsiVariable }
if (var == null) return
def typeParameters = findChild(var){ it instanceof PsiReferenceParameterList }.typeArguments.toList()
if (typeParameters.size() > 0) return
def methodCall = findChild(statement){ it instanceof PsiMethodCallExpression }
if (methodCall == null) return
def methodDefinition = methodCall?.getMethodExpression()?.advancedResolve(true).element
if (methodDefinition == null) return
def methodTypeParameters = findChild(methodDefinition.returnTypeElement){ it instanceof PsiReferenceParameterList }.typeArguments.toList()
if (methodTypeParameters.size() == 0) return
*/
runDocumentWriteAction(event.project) {
show("replacing")
findChild(var){ it instanceof PsiReferenceParameterList }.replace(
findChild(methodDefinition.returnTypeElement){ it instanceof PsiReferenceParameterList }
)
show("replaced")
}
// show(methodTypeParameters)
// show(typeParameters)
}
show("registered")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment