Provide incremental analysis of a file when it is applicable

#KT32868 Fixed
This commit is contained in:
Vladimir Dolzhenko
2019-07-27 16:18:53 +02:00
parent c7bd6d8ede
commit 4b2834c4a8
68 changed files with 730 additions and 318 deletions
@@ -107,7 +107,7 @@ class CompletionBindingContextProvider(project: Project) {
val inStatement = position.findStatementInBlock()
val block = inStatement?.parent as KtBlockExpression?
val prevStatement = inStatement?.siblings(forward = false, withItself = false)?.firstIsInstanceOrNull<KtExpression>()
val modificationScope = inStatement?.let { KotlinCodeBlockModificationListener.getInsideCodeBlockModificationScope(it) }
val modificationScope = inStatement?.let { KotlinCodeBlockModificationListener.getInsideCodeBlockModificationScope(it)?.element }
val psiElementsBeforeAndAfter = modificationScope?.let { collectPsiElementsBeforeAndAfter(modificationScope, inStatement) }
@@ -19,13 +19,16 @@ import org.jetbrains.kotlin.utils.addToStdlib.indexOfOrNull
import java.io.File
abstract class AbstractCompletionHandlerTest(private val defaultCompletionType: CompletionType) : CompletionHandlerTestBase() {
private val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:"
private val LOOKUP_STRING_PREFIX = "ELEMENT:"
private val ELEMENT_TEXT_PREFIX = "ELEMENT_TEXT:"
private val TAIL_TEXT_PREFIX = "TAIL_TEXT:"
private val COMPLETION_CHAR_PREFIX = "CHAR:"
private val COMPLETION_CHARS_PREFIX = "CHARS:"
private val CODE_STYLE_SETTING_PREFIX = "CODE_STYLE_SETTING:"
companion object {
val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:"
val LOOKUP_STRING_PREFIX = "ELEMENT:"
val ELEMENT_TEXT_PREFIX = "ELEMENT_TEXT:"
val TAIL_TEXT_PREFIX = "TAIL_TEXT:"
val COMPLETION_CHAR_PREFIX = "CHAR:"
val COMPLETION_CHARS_PREFIX = "CHARS:"
val CODE_STYLE_SETTING_PREFIX = "CODE_STYLE_SETTING:"
}
protected open fun doTest(testPath: String) {
setUpFixture(fileName())
@@ -42,11 +45,7 @@ abstract class AbstractCompletionHandlerTest(private val defaultCompletionType:
val lookupString = InTextDirectivesUtils.findStringWithPrefixes(fileText, LOOKUP_STRING_PREFIX)
val itemText = InTextDirectivesUtils.findStringWithPrefixes(fileText, ELEMENT_TEXT_PREFIX)
val tailText = InTextDirectivesUtils.findStringWithPrefixes(fileText, TAIL_TEXT_PREFIX)
val completionChars = completionChars(
char = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_CHAR_PREFIX),
chars = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_CHARS_PREFIX)
)
val completionChars = completionChars(fileText)
val completionType = ExpectedCompletionUtils.getCompletionType(fileText) ?: defaultCompletionType
@@ -58,8 +57,7 @@ abstract class AbstractCompletionHandlerTest(private val defaultCompletionType:
val settingValue = line.substring(index + 1).trim()
val (field, settings) = try {
kotlinStyleSettings::class.java.getField(settingName) to kotlinStyleSettings
}
catch (e: NoSuchFieldException) {
} catch (e: NoSuchFieldException) {
commonStyleSettings::class.java.getField(settingName) to commonStyleSettings
}
when (field.type.name) {
@@ -70,6 +68,7 @@ abstract class AbstractCompletionHandlerTest(private val defaultCompletionType:
}
doTestWithTextLoaded(
myFixture,
completionType,
invocationCount,
lookupString,
@@ -31,7 +31,16 @@ class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
private fun doTest(time: Int, lookupString: String?, itemText: String?, tailText: String?, completionChar: Char) {
fixture.configureByFile(fileName())
doTestWithTextLoaded(CompletionType.BASIC, time, lookupString, itemText, tailText, completionChar.toString(), fileName() + ".after")
doTestWithTextLoaded(
myFixture,
CompletionType.BASIC,
time,
lookupString,
itemText,
tailText,
completionChar.toString(),
fileName() + ".after"
)
}
fun testClassCompletionImport() = doTest(2, "SortedSet", null, '\n')
@@ -11,114 +11,133 @@ import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.codeInsight.lookup.LookupEvent
import com.intellij.codeInsight.lookup.LookupManager
import com.intellij.codeInsight.lookup.impl.LookupImpl
import com.intellij.openapi.project.Project
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import org.jetbrains.kotlin.idea.completion.test.ExpectedCompletionUtils
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.test.InTextDirectivesUtils
abstract class CompletionHandlerTestBase : KotlinLightCodeInsightFixtureTestCase() {
protected val fixture: JavaCodeInsightTestFixture
get() = myFixture
protected fun doTestWithTextLoaded(
completionType: CompletionType,
time: Int,
lookupString: String?,
itemText: String?,
tailText: String?,
completionChars: String,
afterFilePath: String
) {
for (idx in 0 until completionChars.length - 1) {
fixture.type(completionChars[idx])
companion object {
fun doTestWithTextLoaded(
fixture: JavaCodeInsightTestFixture,
completionType: CompletionType,
time: Int,
lookupString: String?,
itemText: String?,
tailText: String?,
completionChars: String,
afterFilePath: String,
actions: List<String>? = emptyList(),
afterTypingBlock: () -> Unit = {}
) {
for (idx in 0 until completionChars.length - 1) {
fixture.type(completionChars[idx])
afterTypingBlock()
}
if (actions != null && actions.isNotEmpty()) {
for (action in actions) {
fixture.performEditorAction(action)
}
}
fixture.complete(completionType, time)
if (lookupString != null || itemText != null || tailText != null) {
val item = getExistentLookupElement(fixture.project, lookupString, itemText, tailText)
if (item != null) {
selectItem(fixture, item, completionChars.last())
}
}
afterTypingBlock()
fixture.checkResultByFile(afterFilePath)
}
fixture.complete(completionType, time)
if (lookupString != null || itemText != null || tailText != null) {
val item = getExistentLookupElement(lookupString, itemText, tailText)
if (item != null) {
selectItem(item, completionChars.last())
fun completionChars(text: String): String {
val char: String? = InTextDirectivesUtils.findStringWithPrefixes(text, AbstractCompletionHandlerTest.COMPLETION_CHAR_PREFIX)
val chars: String? = InTextDirectivesUtils.findStringWithPrefixes(text, AbstractCompletionHandlerTest.COMPLETION_CHARS_PREFIX)
return when (char) {
null -> when (chars) {
null -> "\n"
else -> chars.replace("\\n", "\n").replace("\\t", "\t")
}
"\\n" -> "\n"
"\\t" -> "\t"
else -> char.single().toString() ?: error("Incorrect completion char: \"$char\"")
}
}
fixture.checkResultByFile(afterFilePath)
}
protected fun completionChars(char: String?, chars: String?): String =
when (char) {
null -> when (chars) {
null -> "\n"
else -> chars.replace("\\n", "\n").replace("\\t", "\t")
fun getExistentLookupElement(project: Project, lookupString: String?, itemText: String?, tailText: String?): LookupElement? {
val lookup = LookupManager.getInstance(project)?.activeLookup as LookupImpl? ?: return null
val items = lookup.items
if (lookupString == "*") {
assert(itemText == null)
assert(tailText == null)
return items.firstOrNull()
}
"\\n" -> "\n"
"\\t" -> "\t"
else -> char.single().toString() ?: error("Incorrect completion char: \"$char\"")
}
protected fun getExistentLookupElement(lookupString: String?, itemText: String?, tailText: String?): LookupElement? {
val lookup = LookupManager.getInstance(project)?.activeLookup as LookupImpl? ?: return null
val items = lookup.items
var foundElement : LookupElement? = null
val presentation = LookupElementPresentation()
for (lookupElement in items) {
val lookupOk = if (lookupString != null) lookupElement.lookupString == lookupString else true
if (lookupString == "*") {
assert(itemText == null)
assert(tailText == null)
return items.firstOrNull()
}
if (lookupOk) {
lookupElement.renderElement(presentation)
var foundElement : LookupElement? = null
val presentation = LookupElementPresentation()
for (lookupElement in items) {
val lookupOk = if (lookupString != null) lookupElement.lookupString == lookupString else true
if (lookupOk) {
lookupElement.renderElement(presentation)
val textOk = if (itemText != null) {
val itemItemText = presentation.itemText
itemItemText != null && itemItemText == itemText
}
else {
true
}
if (textOk) {
val tailOk = if (tailText != null) {
val itemTailText = presentation.tailText
itemTailText != null && itemTailText == tailText
val textOk = if (itemText != null) {
val itemItemText = presentation.itemText
itemItemText != null && itemItemText == itemText
}
else {
true
}
if (tailOk) {
if (foundElement != null) {
val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(arrayOf(foundElement, lookupElement)))
fail("Several elements satisfy to completion restrictions:\n$dump")
if (textOk) {
val tailOk = if (tailText != null) {
val itemTailText = presentation.tailText
itemTailText != null && itemTailText == tailText
}
else {
true
}
foundElement = lookupElement
if (tailOk) {
if (foundElement != null) {
val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(arrayOf(foundElement, lookupElement)))
fail("Several elements satisfy to completion restrictions:\n$dump")
}
foundElement = lookupElement
}
}
}
}
if (foundElement == null) {
val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(items.toTypedArray()))
error("No element satisfy completion restrictions in:\n$dump")
}
return foundElement
}
if (foundElement == null) {
val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(items.toTypedArray()))
error("No element satisfy completion restrictions in:\n$dump")
}
return foundElement
}
protected fun selectItem(item: LookupElement?, completionChar: Char) {
val lookup = (fixture.lookup as LookupImpl)
if (lookup.currentItem != item) { // do not touch selection if not changed - important for char filter tests
lookup.currentItem = item
}
lookup.focusDegree = LookupImpl.FocusDegree.FOCUSED
if (LookupEvent.isSpecialCompletionChar(completionChar)) {
lookup.finishLookup(completionChar)
}
else {
fixture.type(completionChar)
fun selectItem(fixture: JavaCodeInsightTestFixture, item: LookupElement?, completionChar: Char) {
val lookup = (fixture.lookup as LookupImpl)
if (lookup.currentItem != item) { // do not touch selection if not changed - important for char filter tests
lookup.currentItem = item
}
lookup.focusDegree = LookupImpl.FocusDegree.FOCUSED
if (LookupEvent.isSpecialCompletionChar(completionChar)) {
lookup.finishLookup(completionChar)
}
else {
fixture.type(completionChar)
}
}
}
}