Kotlinize findOrCreateDirectoryForPackage in create actual fix
Before this commit, without existing directory we tried to create it only inside Java source roots, which is incorrect for JS and other non-JVM modules. Source roots themselves still aren't created if they don't exist. This fixes KT-19586 partially
This commit is contained in:
@@ -5,14 +5,25 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.core
|
||||
|
||||
import com.intellij.psi.JavaDirectoryService
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiPackage
|
||||
import com.intellij.ide.util.DirectoryChooserUtil
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtil
|
||||
import com.intellij.openapi.roots.ModulePackageIndex
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.Query
|
||||
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootProperties
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
|
||||
import org.jetbrains.kotlin.config.KotlinSourceRootType
|
||||
import org.jetbrains.kotlin.idea.caches.PerModulePackageCacheService
|
||||
import org.jetbrains.kotlin.idea.util.sourceRoot
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.File
|
||||
|
||||
fun PsiDirectory.getPackage(): PsiPackage? = JavaDirectoryService.getInstance()!!.getPackage(this)
|
||||
|
||||
@@ -34,3 +45,98 @@ fun KtFile.packageMatchesDirectory(): Boolean = packageFqName == getFqNameByDire
|
||||
|
||||
fun KtFile.packageMatchesDirectoryOrImplicit() =
|
||||
packageFqName == getFqNameByDirectory() || packageFqName == parent?.getFqNameWithImplicitPrefix()
|
||||
|
||||
private fun getWritableModuleDirectory(vFiles: Query<VirtualFile>, module: Module, manager: PsiManager): PsiDirectory? {
|
||||
for (vFile in vFiles) {
|
||||
if (ModuleUtil.findModuleForFile(vFile, module.project) !== module) continue
|
||||
val directory = manager.findDirectory(vFile)
|
||||
if (directory != null && directory.isValid && directory.isWritable) {
|
||||
return directory
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findLongestExistingPackage(module: Module, packageName: String): PsiPackage? {
|
||||
val manager = PsiManager.getInstance(module.project)
|
||||
|
||||
var nameToMatch = packageName
|
||||
while (true) {
|
||||
val vFiles = ModulePackageIndex.getInstance(module).getDirsByPackageName(nameToMatch, false)
|
||||
val directory = getWritableModuleDirectory(vFiles, module, manager)
|
||||
if (directory != null) return directory.getPackage()
|
||||
|
||||
val lastDotIndex = nameToMatch.lastIndexOf('.')
|
||||
if (lastDotIndex < 0) {
|
||||
return null
|
||||
}
|
||||
nameToMatch = nameToMatch.substring(0, lastDotIndex)
|
||||
}
|
||||
}
|
||||
|
||||
private val kotlinSourceRootTypes: Set<JpsModuleSourceRootType<JavaSourceRootProperties>> =
|
||||
setOf(KotlinSourceRootType.Source, KotlinSourceRootType.TestSource) + JavaModuleSourceRootTypes.SOURCES
|
||||
|
||||
private fun Module.getSourceRoots(): List<VirtualFile> =
|
||||
ModuleRootManager.getInstance(this).getSourceRoots(kotlinSourceRootTypes)
|
||||
|
||||
private fun getPackageDirectoriesInModule(rootPackage: PsiPackage, module: Module): Array<PsiDirectory> =
|
||||
rootPackage.getDirectories(GlobalSearchScope.moduleScope(module))
|
||||
|
||||
// This is Kotlin version of PackageUtil.findOrCreateDirectoryForPackage
|
||||
fun findOrCreateDirectoryForPackage(module: Module, packageName: String): PsiDirectory? {
|
||||
val project = module.project
|
||||
var existingDirectoryByPackage: PsiDirectory? = null
|
||||
var restOfName = packageName
|
||||
if (!packageName.isEmpty()) {
|
||||
val rootPackage = findLongestExistingPackage(module, packageName)
|
||||
if (rootPackage != null) {
|
||||
val beginIndex = rootPackage.qualifiedName.length + 1
|
||||
val subPackageName = if (beginIndex < packageName.length) packageName.substring(beginIndex) else ""
|
||||
var postfixToShow = subPackageName.replace('.', File.separatorChar)
|
||||
if (subPackageName.isNotEmpty()) {
|
||||
postfixToShow = File.separatorChar + postfixToShow
|
||||
}
|
||||
val moduleDirectories = getPackageDirectoriesInModule(rootPackage, module)
|
||||
existingDirectoryByPackage =
|
||||
DirectoryChooserUtil.selectDirectory(project, moduleDirectories, null, postfixToShow) ?: return null
|
||||
restOfName = subPackageName
|
||||
}
|
||||
}
|
||||
|
||||
val existingDirectory = existingDirectoryByPackage ?: run {
|
||||
val sourceRoots = module.getSourceRoots()
|
||||
if (sourceRoots.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
val directoryList = mutableListOf<PsiDirectory>()
|
||||
for (sourceRoot in sourceRoots) {
|
||||
val directory = PsiManager.getInstance(project).findDirectory(sourceRoot) ?: continue
|
||||
directoryList += directory
|
||||
}
|
||||
val sourceDirectories = directoryList.toTypedArray()
|
||||
DirectoryChooserUtil.selectDirectory(
|
||||
project, sourceDirectories, null,
|
||||
File.separatorChar + packageName.replace('.', File.separatorChar)
|
||||
) ?: return null
|
||||
}
|
||||
|
||||
fun getLeftPart(packageName: String): String {
|
||||
val index = packageName.indexOf('.')
|
||||
return if (index > -1) packageName.substring(0, index) else packageName
|
||||
}
|
||||
|
||||
fun cutLeftPart(packageName: String): String {
|
||||
val index = packageName.indexOf('.')
|
||||
return if (index > -1) packageName.substring(index + 1) else ""
|
||||
}
|
||||
|
||||
var psiDirectory = existingDirectory
|
||||
while (restOfName.isNotEmpty()) {
|
||||
val name = getLeftPart(restOfName)
|
||||
val foundExistingDirectory = psiDirectory.findSubdirectory(name)
|
||||
psiDirectory = foundExistingDirectory ?: WriteAction.compute<PsiDirectory, Exception> { psiDirectory.createSubdirectory(name) }
|
||||
restOfName = cutLeftPart(restOfName)
|
||||
}
|
||||
return psiDirectory
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.quickfix.expectactual
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.ide.util.EditorHelper
|
||||
import com.intellij.ide.util.PackageUtil
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -112,8 +111,8 @@ sealed class CreateActualFix<out D : KtNamedDeclaration>(
|
||||
val expectedDir = declaration.containingFile.containingDirectory
|
||||
val expectedPackage = JavaDirectoryService.getInstance().getPackage(expectedDir)
|
||||
|
||||
val actualDirectory = PackageUtil.findOrCreateDirectoryForPackage(
|
||||
actualModule, expectedPackage?.qualifiedName ?: "", null, false
|
||||
val actualDirectory = findOrCreateDirectoryForPackage(
|
||||
actualModule, expectedPackage?.qualifiedName ?: ""
|
||||
) ?: return null
|
||||
return runWriteAction {
|
||||
val fileName = "$name.kt"
|
||||
|
||||
Reference in New Issue
Block a user