KT-12447 Don't use CompletionProgressIndicator in Kotlin plugin

#KT-12447 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-06-16 20:35:40 +03:00
parent 4e16d67321
commit 962a8f9bf4
5 changed files with 61 additions and 92 deletions
@@ -23,8 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.template.TemplateManager import com.intellij.codeInsight.template.TemplateManager
import com.intellij.patterns.PatternCondition import com.intellij.patterns.PatternCondition
import com.intellij.patterns.StandardPatterns import com.intellij.patterns.StandardPatterns
import com.intellij.psi.JavaPsiFacade import com.intellij.psi.*
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
@@ -119,11 +118,28 @@ class BasicCompletionSession(
lookupElement.putUserData(LookupCancelWatcher.AUTO_POPUP_AT, position.startOffset) lookupElement.putUserData(LookupCancelWatcher.AUTO_POPUP_AT, position.startOffset)
lookupElement lookupElement
} }
if (isInFunctionLiteralStart(position)) {
collector.addLookupElementPostProcessor { lookupElement ->
lookupElement.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit)
lookupElement
}
}
} }
completionKind.doComplete() completionKind.doComplete()
} }
private fun isInFunctionLiteralStart(position: PsiElement): Boolean {
var prev = position.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
if (prev?.node?.elementType == KtTokens.LPAR) {
prev = prev?.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
}
if (prev?.node?.elementType != KtTokens.LBRACE) return false
val functionLiteral = prev!!.parent as? KtFunctionLiteral ?: return false
return functionLiteral.lBrace == prev
}
override fun createSorter(): CompletionSorter { override fun createSorter(): CompletionSorter {
var sorter = super.createSorter() var sorter = super.createSorter()
@@ -16,12 +16,9 @@
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionProgressIndicator
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.completion.PrefixMatcher import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.lookup.* import com.intellij.codeInsight.lookup.*
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.patterns.ElementPattern import com.intellij.patterns.ElementPattern
import com.intellij.patterns.StandardPatterns import com.intellij.patterns.StandardPatterns
@@ -107,17 +104,6 @@ fun LookupElement.keepOldArgumentListOnTab(): LookupElement {
return this return this
} }
fun rethrowWithCancelIndicator(exception: ProcessCanceledException): ProcessCanceledException {
val indicator = CompletionService.getCompletionService().currentCompletion as CompletionProgressIndicator
// Force cancel to avoid deadlock in CompletionThreading.delegateWeighing()
if (!indicator.isCanceled) {
indicator.cancel()
}
return exception
}
fun PrefixMatcher.asNameFilter(): (Name) -> Boolean { fun PrefixMatcher.asNameFilter(): (Name) -> Boolean {
return { name -> !name.isSpecial && prefixMatches(name.identifier) } return { name -> !name.isSpecial && prefixMatches(name.identifier) }
} }
@@ -16,19 +16,11 @@
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionProgressIndicator
import com.intellij.codeInsight.completion.CompletionService import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.lookup.CharFilter import com.intellij.codeInsight.lookup.CharFilter
import com.intellij.codeInsight.lookup.CharFilter.Result
import com.intellij.codeInsight.lookup.Lookup import com.intellij.codeInsight.lookup.Lookup
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
class KotlinCompletionCharFilter() : CharFilter() { class KotlinCompletionCharFilter() : CharFilter() {
companion object { companion object {
@@ -43,10 +35,7 @@ class KotlinCompletionCharFilter() : CharFilter() {
override fun acceptChar(c : Char, prefixLength : Int, lookup : Lookup) : Result? { override fun acceptChar(c : Char, prefixLength : Int, lookup : Lookup) : Result? {
if (lookup.psiFile !is KtFile) return null if (lookup.psiFile !is KtFile) return null
if (!lookup.isCompletion) return null if (!lookup.isCompletion) return null
// it does not work in tests, so we use other way val isAutopopup = CompletionService.getCompletionService().currentCompletion!!.isAutopopupCompletion
// val isAutopopup = CompletionService.getCompletionService().getCurrentCompletion().isAutopopupCompletion()
val completionParameters = (CompletionService.getCompletionService().currentCompletion as CompletionProgressIndicator).parameters
val isAutopopup = completionParameters.invocationCount == 0
if (Character.isJavaIdentifierPart(c) || c == '@') { if (Character.isJavaIdentifierPart(c) || c == '@') {
return CharFilter.Result.ADD_TO_PREFIX return CharFilter.Result.ADD_TO_PREFIX
@@ -55,8 +44,7 @@ class KotlinCompletionCharFilter() : CharFilter() {
val currentItem = lookup.currentItem val currentItem = lookup.currentItem
// do not accept items by special chars in some special positions such as in the very beginning of function literal where name of the first parameter can be // do not accept items by special chars in some special positions such as in the very beginning of function literal where name of the first parameter can be
if (isAutopopup && !lookup.isSelectionTouched if (isAutopopup && !lookup.isSelectionTouched && currentItem?.getUserData(SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING) != null) {
&& (currentItem?.getUserData(SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING) != null || isInFunctionLiteralStart(completionParameters.position))) {
return Result.HIDE_LOOKUP return Result.HIDE_LOOKUP
} }
@@ -94,14 +82,4 @@ class KotlinCompletionCharFilter() : CharFilter() {
else -> CharFilter.Result.HIDE_LOOKUP else -> CharFilter.Result.HIDE_LOOKUP
} }
} }
private fun isInFunctionLiteralStart(position: PsiElement): Boolean {
var prev = position.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
if (prev?.node?.elementType == KtTokens.LPAR) {
prev = prev?.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment }
}
if (prev?.node?.elementType != KtTokens.LBRACE) return false
val functionLiteral = prev!!.parent as? KtFunctionLiteral ?: return false
return functionLiteral.lBrace == prev
}
} }
@@ -284,44 +284,39 @@ class KotlinCompletionContributor : CompletionContributor() {
} }
} }
try { result.restartCompletionWhenNothingMatches()
result.restartCompletionWhenNothingMatches()
val configuration = CompletionSessionConfiguration(parameters) val configuration = CompletionSessionConfiguration(parameters)
if (parameters.completionType == CompletionType.BASIC) { if (parameters.completionType == CompletionType.BASIC) {
val session = BasicCompletionSession(configuration, parameters, toFromOriginalFileMapper, result) val session = BasicCompletionSession(configuration, parameters, toFromOriginalFileMapper, result)
addPostProcessor(session) addPostProcessor(session)
if (parameters.isAutoPopup && session.shouldDisableAutoPopup()) { if (parameters.isAutoPopup && session.shouldDisableAutoPopup()) {
result.stopHere() result.stopHere()
return return
}
val somethingAdded = session.complete()
if (!somethingAdded && parameters.invocationCount < 2) {
// Rerun completion if nothing was found
val newConfiguration = CompletionSessionConfiguration(
useBetterPrefixMatcherForNonImportedClasses = false,
completeNonAccessibleDeclarations = false,
filterOutJavaGettersAndSetters = false,
completeJavaClassesNotToBeUsed = false,
completeStaticMembers = parameters.invocationCount > 0
)
val newSession = BasicCompletionSession(newConfiguration, parameters, toFromOriginalFileMapper, result)
addPostProcessor(newSession)
newSession.complete()
}
} }
else {
val session = SmartCompletionSession(configuration, parameters, toFromOriginalFileMapper, result) val somethingAdded = session.complete()
addPostProcessor(session) if (!somethingAdded && parameters.invocationCount < 2) {
session.complete() // Rerun completion if nothing was found
val newConfiguration = CompletionSessionConfiguration(
useBetterPrefixMatcherForNonImportedClasses = false,
completeNonAccessibleDeclarations = false,
filterOutJavaGettersAndSetters = false,
completeJavaClassesNotToBeUsed = false,
completeStaticMembers = parameters.invocationCount > 0
)
val newSession = BasicCompletionSession(newConfiguration, parameters, toFromOriginalFileMapper, result)
addPostProcessor(newSession)
newSession.complete()
} }
} }
catch (e: ProcessCanceledException) { else {
throw rethrowWithCancelIndicator(e) val session = SmartCompletionSession(configuration, parameters, toFromOriginalFileMapper, result)
addPostProcessor(session)
session.complete()
} }
} }
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.completion.PlainPrefixMatcher import com.intellij.codeInsight.completion.PlainPrefixMatcher
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.patterns.PlatformPatterns import com.intellij.patterns.PlatformPatterns
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.CallType
@@ -44,29 +43,24 @@ object PackageDirectiveCompletion {
val expression = file.findElementAt(parameters.offset)?.parent as? KtSimpleNameExpression ?: return false val expression = file.findElementAt(parameters.offset)?.parent as? KtSimpleNameExpression ?: return false
try { val prefixLength = parameters.offset - expression.textOffset
val prefixLength = parameters.offset - expression.textOffset val prefix = expression.text!!
val prefix = expression.text!! val prefixMatcher = PlainPrefixMatcher(prefix.substring(0, prefixLength))
val prefixMatcher = PlainPrefixMatcher(prefix.substring(0, prefixLength)) val result = result.withPrefixMatcher(prefixMatcher)
val result = result.withPrefixMatcher(prefixMatcher)
val resolutionFacade = expression.getResolutionFacade() val resolutionFacade = expression.getResolutionFacade()
val packageMemberScope = resolutionFacade.moduleDescriptor.getPackage(file.packageFqName.parent()).memberScope val packageMemberScope = resolutionFacade.moduleDescriptor.getPackage(file.packageFqName.parent()).memberScope
val variants = packageMemberScope.getContributedDescriptors(DescriptorKindFilter.PACKAGES, prefixMatcher.asNameFilter()) val variants = packageMemberScope.getContributedDescriptors(DescriptorKindFilter.PACKAGES, prefixMatcher.asNameFilter())
val lookupElementFactory = BasicLookupElementFactory(resolutionFacade.project, InsertHandlerProvider(callType = CallType.PACKAGE_DIRECTIVE, expectedInfosCalculator = { emptyList() })) val lookupElementFactory = BasicLookupElementFactory(resolutionFacade.project, InsertHandlerProvider(callType = CallType.PACKAGE_DIRECTIVE, expectedInfosCalculator = { emptyList() }))
for (variant in variants) { for (variant in variants) {
val lookupElement = lookupElementFactory.createLookupElement(variant) val lookupElement = lookupElementFactory.createLookupElement(variant)
if (!lookupElement.lookupString.contains(DUMMY_IDENTIFIER)) { if (!lookupElement.lookupString.contains(DUMMY_IDENTIFIER)) {
result.addElement(lookupElement) result.addElement(lookupElement)
}
} }
}
return true return true
}
catch (e: ProcessCanceledException) {
throw rethrowWithCancelIndicator(e)
}
} }
} }