fix first call in chain detection : IDEA-192777

This commit is contained in:
Roman.Ivanov
2018-06-01 10:27:52 +07:00
parent 260b590077
commit a71e9d7f12
2 changed files with 141 additions and 5 deletions
@@ -23,11 +23,8 @@ class MethodChainHintsPass(
override fun collectElementHints(element: PsiElement, collector: (offset: Int, hint: String) -> Unit) {
val call = element as? PsiMethodCallExpression ?: return
val qualifier = call.methodExpression.qualifierExpression
if (qualifier != null && qualifier is PsiMethodCallExpression) {
val callSibling = qualifier.nextSibling
if (callSibling is PsiWhiteSpace && callSibling.textContains('\n')) return // Not first call
}
if (!isFirstCall(call)) return
val next = call.nextSibling
if (!(next is PsiWhiteSpace && next.textContains('\n'))) return
val chain = collectChain(call)
@@ -50,6 +47,29 @@ class MethodChainHintsPass(
}
}
private fun isFirstCall(call: PsiMethodCallExpression): Boolean {
val document = myEditor.document
val callLine = document.getLineNumber(call.argumentList.textOffset)
val callForQualifier = ExpressionUtils.getCallForQualifier(call)
if (callForQualifier == null ||
document.getLineNumber(callForQualifier.argumentList.textOffset) == callLine) return false
val firstQualifierCall = call.methodExpression.qualifier as? PsiMethodCallExpression
if (firstQualifierCall != null) {
if (document.getLineNumber(firstQualifierCall.argumentList.textOffset) != callLine) return false
var currentQualifierCall: PsiMethodCallExpression = firstQualifierCall
while (true) {
val qualifier = currentQualifierCall.methodExpression.qualifier
if (qualifier == null) return false
if (qualifier !is PsiMethodCallExpression) return true
if (document.getLineNumber(qualifier.argumentList.textOffset) != callLine) return false
currentQualifierCall = qualifier
}
}
return true
}
private fun collectChain(call: PsiMethodCallExpression): List<PsiMethodCallExpression> {
val chain = mutableListOf(call)
var current = call
@@ -0,0 +1,116 @@
/*
* Copyright 2000-2018 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 com.intellij.java.codeInsight.daemon.inlays
import com.intellij.codeInsight.daemon.impl.HintRenderer
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.intellij.lang.annotations.Language
class MethodChainHintsTest: LightCodeInsightFixtureTestCase() {
fun check(@Language("Java") text: String) {
myFixture.configureByText("A.java", text)
myFixture.testInlays({ (it.renderer as HintRenderer).text }, { it.renderer is HintRenderer })
}
fun `test plain builder`() {
check("""
public class Chains {
static class A {
B b() {return null;}
C c() {return null;}
}
static class B {
A a() {return null;}
C c() {return null;}
}
static class C {
B b() {return null;}
A a() {return null;}
}
public static void main(String[] args) {
new A()
.b()<hint text="B"/>
.c()<hint text="C"/>
.a()<hint text="A"/>
.c();
}
}""")
}
fun `test duplicated builder`() {
// IDEA-192777
check("""public class Chains {
static class A {
B b() {return null;}
C c() {return null;}
}
static class B {
A a() {return null;}
C c() {return null;}
}
static class C {
B b() {return null;}
A a() {return null;}
}
public static void main(String[] args) {
new A()
.b()<hint text="B"/>
.c().b()<hint text="B"/>
.a()<hint text="A"/>
.c()<hint text="C"/>
.b()<hint text="B"/>
.c();
}
}
""")
}
fun `test duplicated call with reference qualifier`() {
check("""public class Chains {
static class A {
B b() {return null;}
C c() {return null;}
}
static class B {
A a() {return null;}
C c() {return null;}
}
static class C {
B b() {return null;}
A a() {return null;}
}
public static void main(String[] args) {
A a = new A();
a.b().c()<hint text="C"/>
.a()<hint text="A"/>
.b()<hint text="B"/>
.a()<hint text="A"/>
.c()<hint text="C"/>
.b();
}
}
""")
}
}