KT-8179 Settings / "Add unambiguous imports on the fly" could work in Kotlin files too
#KT-8179 Fixed
This commit is contained in:
@@ -1187,6 +1187,8 @@
|
||||
level="WARNING"
|
||||
/>
|
||||
|
||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||
|
||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||
|
||||
<filetype.stubBuilder filetype="KJSM" implementationClass="com.intellij.psi.impl.compiled.ClassFileStubBuilder"/>
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope
|
||||
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper
|
||||
@@ -96,59 +97,6 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction<Jet
|
||||
|
||||
private fun createAction(project: Project, editor: Editor) = KotlinAddImportAction(project, editor, element, suggestions)
|
||||
|
||||
private fun computeSuggestions(element: JetSimpleNameExpression): Collection<DeclarationDescriptor> {
|
||||
if (!element.isValid()) return listOf()
|
||||
|
||||
val file = element.getContainingFile() as? JetFile ?: return listOf()
|
||||
|
||||
var referenceName = element.getReferencedName()
|
||||
if (element.getIdentifier() == null) {
|
||||
val conventionName = JetPsiUtil.getConventionName(element)
|
||||
if (conventionName != null) {
|
||||
referenceName = conventionName.asString()
|
||||
}
|
||||
}
|
||||
if (referenceName.isEmpty()) return listOf()
|
||||
|
||||
val resolutionFacade = element.getResolutionFacade()
|
||||
|
||||
val searchScope = getResolveScope(file)
|
||||
|
||||
val bindingContext = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
|
||||
|
||||
val diagnostics = bindingContext.getDiagnostics().forElement(element)
|
||||
if (!diagnostics.any { it.getFactory() in ERRORS }) return listOf()
|
||||
|
||||
val resolutionScope = element.getResolutionScope(bindingContext, resolutionFacade)
|
||||
val containingDescriptor = resolutionScope.ownerDescriptor
|
||||
|
||||
fun isVisible(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (descriptor is DeclarationDescriptorWithVisibility) {
|
||||
return descriptor.isVisible(containingDescriptor, bindingContext, element)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
val result = ArrayList<DeclarationDescriptor>()
|
||||
|
||||
val indicesHelper = KotlinIndicesHelper(resolutionFacade, searchScope, ::isVisible, true)
|
||||
|
||||
if (!element.isImportDirectiveExpression() && !JetPsiUtil.isSelectorInQualified(element)) {
|
||||
if (ProjectStructureUtil.isJsKotlinModule(file)) {
|
||||
result.addAll(indicesHelper.getKotlinClasses({ it == referenceName }, { true }))
|
||||
}
|
||||
else {
|
||||
result.addAll(indicesHelper.getJvmClassesByName(referenceName))
|
||||
}
|
||||
result.addAll(indicesHelper.getTopLevelCallablesByName(referenceName))
|
||||
}
|
||||
|
||||
result.addAll(indicesHelper.getCallableTopLevelExtensions({ it == referenceName }, element, bindingContext))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val ERRORS = setOf(Errors.UNRESOLVED_REFERENCE, Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER)
|
||||
|
||||
@@ -168,5 +116,56 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction<Jet
|
||||
= true
|
||||
}
|
||||
}
|
||||
|
||||
public fun computeSuggestions(element: JetSimpleNameExpression): Collection<DeclarationDescriptor> {
|
||||
if (!element.isValid()) return listOf()
|
||||
|
||||
val file = element.getContainingFile() as? JetFile ?: return listOf()
|
||||
|
||||
var referenceName = element.getReferencedName()
|
||||
if (element.getIdentifier() == null) {
|
||||
val conventionName = JetPsiUtil.getConventionName(element)
|
||||
if (conventionName != null) {
|
||||
referenceName = conventionName.asString()
|
||||
}
|
||||
}
|
||||
if (referenceName.isEmpty()) return listOf()
|
||||
|
||||
val searchScope = getResolveScope(file)
|
||||
|
||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||
|
||||
val diagnostics = bindingContext.getDiagnostics().forElement(element)
|
||||
if (!diagnostics.any { it.getFactory() in ERRORS }) return listOf()
|
||||
|
||||
val resolutionScope = element.getResolutionScope(bindingContext, file.getResolutionFacade())
|
||||
val containingDescriptor = resolutionScope.ownerDescriptor
|
||||
|
||||
fun isVisible(descriptor: DeclarationDescriptor): Boolean {
|
||||
if (descriptor is DeclarationDescriptorWithVisibility) {
|
||||
return descriptor.isVisible(containingDescriptor, bindingContext, element)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
val result = ArrayList<DeclarationDescriptor>()
|
||||
|
||||
val indicesHelper = KotlinIndicesHelper(element.getResolutionFacade(), searchScope, ::isVisible, true)
|
||||
|
||||
if (!element.isImportDirectiveExpression() && !JetPsiUtil.isSelectorInQualified(element)) {
|
||||
if (ProjectStructureUtil.isJsKotlinModule(file)) {
|
||||
result.addAll(indicesHelper.getKotlinClasses({ it == referenceName }, { true }))
|
||||
}
|
||||
else {
|
||||
result.addAll(indicesHelper.getJvmClassesByName(referenceName))
|
||||
}
|
||||
result.addAll(indicesHelper.getTopLevelCallablesByName(referenceName))
|
||||
}
|
||||
|
||||
result.addAll(indicesHelper.getCallableTopLevelExtensions({ it == referenceName }, element, bindingContext))
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.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.idea.actions.KotlinAddImportAction
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.targetDescriptors
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
||||
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
|
||||
|
||||
public 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 JetFile) return false
|
||||
|
||||
val nameExpression = file.findElementAt(offset)?.parent as? JetSimpleNameExpression ?: return false
|
||||
|
||||
return nameExpression.autoImport(editor, file)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
// TODO: use in table cell
|
||||
// TODO: DependencyValidationManager
|
||||
// TODO: filter out non-top level
|
||||
public fun autoImportReferenceAtCursor(editor: Editor, file: PsiFile, allowCaretNearRef: Boolean): Boolean {
|
||||
if (file !is JetFile) 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<JetSimpleNameExpression>() }
|
||||
for (element in elements) {
|
||||
if (!allowCaretNearRef && element.endOffset == caretOffset) continue
|
||||
|
||||
if (element.autoImport(editor, file)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun hasUnresolvedImportWhichCanImport(file: JetFile, name: String): Boolean {
|
||||
return file.importDirectives.any {
|
||||
it.targetDescriptors().isEmpty() && (it.isAllUnder || it.importPath?.importedName?.asString() == name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun JetSimpleNameExpression.autoImport(editor: Editor, file: JetFile): 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 = AutoImportFix.computeSuggestions(this)
|
||||
|
||||
if (suggestions.distinctBy { it.importableFqName!! }.size() != 1) return false
|
||||
|
||||
var result = false
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
result = KotlinAddImportAction(project, editor, this, suggestions).execute()
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user