Fixed freezes with add unambiguous imports on the fly
We still had backup way to recalculate things for the case when fix is not available. So if error is not importable, or we have second error, which will be outdated after adding first import, we could get freezes. 2019.2 and 2019.1 have different APIs, so it's implemented in different way. #KT-30863 Fixed
This commit is contained in:
+2
-6
@@ -261,7 +261,8 @@ internal abstract class OrdinaryImportFixBase<T : KtExpression>(expression: T, f
|
||||
}
|
||||
}
|
||||
|
||||
internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFixBase<KtSimpleNameExpression>(expression, MyFactory) {
|
||||
// This is required to be abstract to reduce bunch file size
|
||||
internal abstract class AbstractImportFix(expression: KtSimpleNameExpression, factory: Factory) : OrdinaryImportFixBase<KtSimpleNameExpression>(expression, factory) {
|
||||
override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.detect(it) }
|
||||
|
||||
private fun importNamesForMembers(): Collection<Name> {
|
||||
@@ -390,11 +391,6 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix
|
||||
indicesHelper
|
||||
)
|
||||
}
|
||||
|
||||
companion object MyFactory : Factory() {
|
||||
override fun createImportAction(diagnostic: Diagnostic) =
|
||||
(diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportFix)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ImportConstructorReferenceFix(expression: KtSimpleNameExpression) :
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
internal class ImportFix(expression: KtSimpleNameExpression): AbstractImportFix(expression, MyFactory) {
|
||||
override fun fixSilently(editor: Editor): Boolean {
|
||||
if (!CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY) return false
|
||||
if (isOutdated()) return false
|
||||
val element = element ?: return false
|
||||
val addImportAction = createAction(element.project, editor, element)
|
||||
if (addImportAction.isUnambiguous()) {
|
||||
addImportAction.execute()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object MyFactory : Factory() {
|
||||
override fun createImportAction(diagnostic: Diagnostic) =
|
||||
(diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportFix)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
|
||||
internal class ImportFix(expression: KtSimpleNameExpression): AbstractImportFix(expression) {
|
||||
companion object MyFactory : Factory() {
|
||||
override fun createImportAction(diagnostic: Diagnostic) =
|
||||
(diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportFix)
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.daemon.ReferenceImporter
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonListeners
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.editor.Editor
|
||||
@@ -30,7 +29,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.codeInsight.KotlinCodeInsightWorkspaceSettings
|
||||
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
@@ -39,77 +37,19 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class KotlinReferenceImporter : ReferenceImporter {
|
||||
override fun autoImportReferenceAtCursor(editor: Editor, file: PsiFile)
|
||||
= autoImportReferenceAtCursor(editor, file, allowCaretNearRef = false)
|
||||
|
||||
override fun autoImportReferenceAt(editor: Editor, file: PsiFile, offset: Int): Boolean {
|
||||
override fun autoImportReferenceAtCursor(editor: Editor, file: PsiFile): Boolean {
|
||||
if (file !is KtFile) return false
|
||||
|
||||
val nameExpression = file.findElementAt(offset)?.parent as? KtSimpleNameExpression ?: return false
|
||||
|
||||
if (!KotlinCodeInsightWorkspaceSettings.getInstance(file.project).addUnambiguousImportsOnTheFly) return false
|
||||
|
||||
val importFix: ImportFixBase<out KtExpression>? = findImportFixAt(editor, file, offset)
|
||||
if (importFix != null && !importFix.isOutdated()) {
|
||||
val addImportAction = importFix.createAction(file.project, editor, nameExpression)
|
||||
if (addImportAction.isUnambiguous()) {
|
||||
addImportAction.execute()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return nameExpression.autoImport(editor, file)
|
||||
}
|
||||
|
||||
private fun findImportFixAt(
|
||||
editor: Editor,
|
||||
file: KtFile,
|
||||
offset: Int
|
||||
): ImportFixBase<out KtExpression>? {
|
||||
var importFix: ImportFixBase<out KtExpression>? = null
|
||||
DaemonCodeAnalyzerEx.processHighlights(editor.document, file.project, null, offset, offset) { info ->
|
||||
importFix = info.quickFixActionRanges?.asSequence()
|
||||
?.map { it.first.action }?.filterIsInstance<ImportFixBase<*>>()?.firstOrNull()
|
||||
importFix == null
|
||||
}
|
||||
return importFix
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
// TODO: use in table cell
|
||||
fun autoImportReferenceAtCursor(editor: Editor, file: PsiFile, allowCaretNearRef: Boolean): Boolean {
|
||||
if (file !is KtFile) return false
|
||||
|
||||
val caretOffset = editor.caretModel.offset
|
||||
val document = editor.document
|
||||
val lineNumber = document.getLineNumber(caretOffset)
|
||||
val startOffset = document.getLineStartOffset(lineNumber)
|
||||
val endOffset = document.getLineEndOffset(lineNumber)
|
||||
|
||||
val elements = file.elementsInRange(TextRange(startOffset, endOffset))
|
||||
.flatMap { it.collectDescendantsOfType<KtSimpleNameExpression>() }
|
||||
for (element in elements) {
|
||||
if (!allowCaretNearRef && element.endOffset == caretOffset) continue
|
||||
|
||||
if (element.autoImport(editor, file)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun hasUnresolvedImportWhichCanImport(file: KtFile, name: String): Boolean {
|
||||
fun hasUnresolvedImportWhichCanImport(name: String): Boolean {
|
||||
return file.importDirectives.any {
|
||||
it.targetDescriptors().isEmpty() && (it.isAllUnder || it.importPath?.importedName?.asString() == name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtSimpleNameExpression.autoImport(editor: Editor, file: KtFile): Boolean {
|
||||
fun KtSimpleNameExpression.autoImport(): Boolean {
|
||||
if (!KotlinCodeInsightWorkspaceSettings.getInstance(project).addUnambiguousImportsOnTheFly) return false
|
||||
if (!DaemonListeners.canChangeFileSilently(file)) return false
|
||||
if (hasUnresolvedImportWhichCanImport(file, getReferencedName())) return false
|
||||
if (hasUnresolvedImportWhichCanImport(getReferencedName())) return false
|
||||
|
||||
val bindingContext = analyze(BodyResolveMode.PARTIAL)
|
||||
if (mainReference.resolveToDescriptors(bindingContext).isNotEmpty()) return false
|
||||
@@ -127,5 +67,15 @@ class KotlinReferenceImporter : ReferenceImporter {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
val caretOffset = editor.caretModel.offset
|
||||
val document = editor.document
|
||||
val lineNumber = document.getLineNumber(caretOffset)
|
||||
val startOffset = document.getLineStartOffset(lineNumber)
|
||||
val endOffset = document.getLineEndOffset(lineNumber)
|
||||
|
||||
return file.elementsInRange(TextRange(startOffset, endOffset))
|
||||
.flatMap { it.collectDescendantsOfType<KtSimpleNameExpression>() }
|
||||
.any { it.endOffset != caretOffset && it.autoImport() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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 org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings
|
||||
import com.intellij.codeInsight.daemon.ReferenceImporter
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonListeners
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.actions.createSingleImportAction
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class KotlinReferenceImporter : ReferenceImporter {
|
||||
override fun autoImportReferenceAtCursor(editor: Editor, file: PsiFile)
|
||||
= autoImportReferenceAtCursor(editor, file, allowCaretNearRef = false)
|
||||
|
||||
override fun autoImportReferenceAt(editor: Editor, file: PsiFile, offset: Int): Boolean {
|
||||
if (file !is KtFile) return false
|
||||
|
||||
val nameExpression = file.findElementAt(offset)?.parent as? KtSimpleNameExpression ?: return false
|
||||
|
||||
if (!CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY) return false
|
||||
|
||||
val importFix: ImportFixBase<out KtExpression>? = findImportFixAt(editor, file, offset)
|
||||
if (importFix != null && !importFix.isOutdated()) {
|
||||
val addImportAction = importFix.createAction(file.project, editor, nameExpression)
|
||||
if (addImportAction.isUnambiguous()) {
|
||||
addImportAction.execute()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findImportFixAt(
|
||||
editor: Editor,
|
||||
file: KtFile,
|
||||
offset: Int
|
||||
): ImportFixBase<out KtExpression>? {
|
||||
var importFix: ImportFixBase<out KtExpression>? = null
|
||||
DaemonCodeAnalyzerEx.processHighlights(editor.document, file.project, null, offset, offset) { info ->
|
||||
importFix = info.quickFixActionRanges?.asSequence()
|
||||
?.map { it.first.action }?.filterIsInstance<ImportFixBase<*>>()?.firstOrNull()
|
||||
importFix == null
|
||||
}
|
||||
return importFix
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
// TODO: use in table cell
|
||||
fun autoImportReferenceAtCursor(editor: Editor, file: PsiFile, allowCaretNearRef: Boolean): Boolean {
|
||||
if (file !is KtFile) return false
|
||||
|
||||
val caretOffset = editor.caretModel.offset
|
||||
val document = editor.document
|
||||
val lineNumber = document.getLineNumber(caretOffset)
|
||||
val startOffset = document.getLineStartOffset(lineNumber)
|
||||
val endOffset = document.getLineEndOffset(lineNumber)
|
||||
|
||||
val elements = file.elementsInRange(TextRange(startOffset, endOffset))
|
||||
.flatMap { it.collectDescendantsOfType<KtSimpleNameExpression>() }
|
||||
for (element in elements) {
|
||||
if (!allowCaretNearRef && element.endOffset == caretOffset) continue
|
||||
|
||||
if (element.autoImport(editor, file)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun hasUnresolvedImportWhichCanImport(file: KtFile, name: String): Boolean {
|
||||
return file.importDirectives.any {
|
||||
it.targetDescriptors().isEmpty() && (it.isAllUnder || it.importPath?.importedName?.asString() == name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtSimpleNameExpression.autoImport(editor: Editor, file: KtFile): Boolean {
|
||||
if (!CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY) return false
|
||||
if (!DaemonListeners.canChangeFileSilently(file)) return false
|
||||
if (hasUnresolvedImportWhichCanImport(file, getReferencedName())) return false
|
||||
|
||||
val bindingContext = analyze(BodyResolveMode.PARTIAL)
|
||||
if (mainReference.resolveToDescriptors(bindingContext).isNotEmpty()) return false
|
||||
|
||||
val suggestions = ImportFix(this).collectSuggestions()
|
||||
if (suggestions.size != 1) return false
|
||||
val descriptors = file.resolveImportReference(suggestions.single())
|
||||
|
||||
// we do not auto-import nested classes because this will probably add qualification into the text and this will confuse the user
|
||||
if (descriptors.any { it is ClassDescriptor && it.containingDeclaration is ClassDescriptor }) return false
|
||||
|
||||
var result = false
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
result = createSingleImportAction(project, editor, this, suggestions).execute()
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user