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
|
package org.jetbrains.kotlin.idea.core
|
||||||
|
|
||||||
import com.intellij.psi.JavaDirectoryService
|
import com.intellij.ide.util.DirectoryChooserUtil
|
||||||
import com.intellij.psi.PsiDirectory
|
import com.intellij.openapi.application.WriteAction
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.psi.PsiPackage
|
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.caches.PerModulePackageCacheService
|
||||||
import org.jetbrains.kotlin.idea.util.sourceRoot
|
import org.jetbrains.kotlin.idea.util.sourceRoot
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
fun PsiDirectory.getPackage(): PsiPackage? = JavaDirectoryService.getInstance()!!.getPackage(this)
|
fun PsiDirectory.getPackage(): PsiPackage? = JavaDirectoryService.getInstance()!!.getPackage(this)
|
||||||
|
|
||||||
@@ -34,3 +45,98 @@ fun KtFile.packageMatchesDirectory(): Boolean = packageFqName == getFqNameByDire
|
|||||||
|
|
||||||
fun KtFile.packageMatchesDirectoryOrImplicit() =
|
fun KtFile.packageMatchesDirectoryOrImplicit() =
|
||||||
packageFqName == getFqNameByDirectory() || packageFqName == parent?.getFqNameWithImplicitPrefix()
|
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.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.ide.util.EditorHelper
|
import com.intellij.ide.util.EditorHelper
|
||||||
import com.intellij.ide.util.PackageUtil
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
@@ -112,8 +111,8 @@ sealed class CreateActualFix<out D : KtNamedDeclaration>(
|
|||||||
val expectedDir = declaration.containingFile.containingDirectory
|
val expectedDir = declaration.containingFile.containingDirectory
|
||||||
val expectedPackage = JavaDirectoryService.getInstance().getPackage(expectedDir)
|
val expectedPackage = JavaDirectoryService.getInstance().getPackage(expectedDir)
|
||||||
|
|
||||||
val actualDirectory = PackageUtil.findOrCreateDirectoryForPackage(
|
val actualDirectory = findOrCreateDirectoryForPackage(
|
||||||
actualModule, expectedPackage?.qualifiedName ?: "", null, false
|
actualModule, expectedPackage?.qualifiedName ?: ""
|
||||||
) ?: return null
|
) ?: return null
|
||||||
return runWriteAction {
|
return runWriteAction {
|
||||||
val fileName = "$name.kt"
|
val fileName = "$name.kt"
|
||||||
|
|||||||
Reference in New Issue
Block a user