diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/stubindex/JetSourceFilterScope.java b/idea/idea-analysis/src/org/jetbrains/jet/plugin/stubindex/JetSourceFilterScope.java index 1682d54dbc4..ee48b5fb045 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/stubindex/JetSourceFilterScope.java +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/stubindex/JetSourceFilterScope.java @@ -59,6 +59,6 @@ public class JetSourceFilterScope extends DelegatingGlobalSearchScope { return false; } - return ProjectRootsUtil.isInSources(project, file, includeLibraries, /* withLibraryClassesRoots */ true, index); + return ProjectRootsUtil.isInSources(project, file, /* includeTestSources */ false, includeLibraries, /* withLibraryClassesRoots */ true, index); } } diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt index 1b165e04831..6267e127afd 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/util/ProjectRootsUtil.kt @@ -24,13 +24,14 @@ import org.jetbrains.jet.plugin.stubindex.JetSourceFilterScope import org.jetbrains.jet.plugin.configuration.JetModuleTypeManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiDirectory public object ProjectRootsUtil { platformStatic public fun isInSources(project: Project, file: VirtualFile, - includeLibrarySources: Boolean, withLibraryClassesRoots: Boolean, + includeTestSources: Boolean, includeLibrarySources: Boolean, withLibraryClassesRoots: Boolean, fileIndex: ProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)): Boolean { - if (fileIndex.isInSourceContent(file)) { + if (fileIndex.isInSourceContent(file) || (includeTestSources && fileIndex.isInTestSourceContent(file))) { return !JetModuleTypeManager.getInstance()!!.isKtFileInGradleProjectInWrongFolder(file, project) } @@ -40,16 +41,20 @@ public object ProjectRootsUtil { } platformStatic - public fun isInSource(element: PsiElement, includeLibrarySources: Boolean, withLibraryClassesRoots: Boolean = false): Boolean { + public fun isInSource( + element: PsiElement, + includeLibrarySources: Boolean, + includeTestSources: Boolean = false, + withLibraryClassesRoots: Boolean = false + ): Boolean { return runReadAction { (): Boolean -> - val containingFile = element.getContainingFile() - if (containingFile == null) return@runReadAction false - - val virtualFile = containingFile.getVirtualFile() - if (virtualFile == null) return@runReadAction false + val virtualFile = when(element) { + is PsiDirectory -> element.getVirtualFile() + else -> element.getContainingFile()?.getVirtualFile() + } ?: return@runReadAction false val project = element.getProject() - return@runReadAction isInSources(project, virtualFile, includeLibrarySources, withLibraryClassesRoots) + return@runReadAction isInSources(project, virtualFile, includeTestSources, includeLibrarySources, withLibraryClassesRoots) } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt index 4f31eaffdf2..206221a3a2d 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/CreateClassFromUsageFix.kt @@ -30,13 +30,17 @@ import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.TypeInf import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.ParameterInfo import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.ConstructorInfo import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallableBuilderConfiguration -import org.jetbrains.jet.lang.psi.JetExpression import java.util.Collections import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.createBuilder import com.intellij.openapi.command.CommandProcessor import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallablePlacement import org.jetbrains.jet.plugin.refactoring.getOrCreateKotlinFile import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassKind.* +import com.intellij.psi.PsiPackage +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.ide.util.DirectoryChooserUtil +import java.util.HashMap +import com.intellij.openapi.application.ApplicationManager enum class ClassKind(val keyword: String, val description: String) { PLAIN_CLASS: ClassKind("class", "class") @@ -66,26 +70,44 @@ public class CreateClassFromUsageFix( JetBundle.message("create.0.from.usage", "${classInfo.kind.description} '${classInfo.name}'") override fun invoke(project: Project, editor: Editor, file: JetFile) { + fun createFileByPackage(psiPackage: PsiPackage): JetFile? { + val directories = psiPackage.getDirectories().filter { it.canRefactor() } + assert (directories.isNotEmpty(), "Package '${psiPackage.getQualifiedName() ?: ""}' must be refactorable") + + val currentModule = ModuleUtilCore.findModuleForPsiElement(file) + val preferredDirectory = + directories.firstOrNull { ModuleUtilCore.findModuleForPsiElement(it) == currentModule } + ?: directories.firstOrNull() + + val targetDirectory = if (directories.size > 1 && !ApplicationManager.getApplication().isUnitTestMode()) { + DirectoryChooserUtil.chooseDirectory(directories.copyToArray(), preferredDirectory, project, HashMap()) + } + else { + preferredDirectory + } ?: return null + + val fileName = "${classInfo.name}.${JetFileType.INSTANCE.getDefaultExtension()}" + val targetFile = getOrCreateKotlinFile(fileName, targetDirectory) + if (targetFile == null) { + val filePath = "${targetDirectory.getVirtualFile().getPath()}/$fileName" + CodeInsightUtils.showErrorHint( + targetDirectory.getProject(), + editor, + "File $filePath already exists but does not correspond to Kotlin file", + "Create file", + null + ) + } + return targetFile + } + with (classInfo) { - val targetParent = when (targetParent) { - is JetElement -> targetParent - is PsiDirectory -> { - val fileName = "$name.${JetFileType.INSTANCE.getDefaultExtension()}" - val targetFile = getOrCreateKotlinFile(fileName, targetParent) - if (targetFile == null) { - val filePath = "${targetParent.getVirtualFile().getPath()}/$fileName" - CodeInsightUtils.showErrorHint( - targetParent.getProject(), - editor, - "File $filePath already exists but does not correspond to Kotlin file", - "Create file", - null - ) - } - targetFile - } - else -> throw AssertionError("Unexpected element: " + targetParent.getText()) - } as? JetElement ?: return + val targetParent = + when (targetParent) { + is JetElement -> targetParent + is PsiPackage -> createFileByPackage(targetParent) + else -> throw AssertionError("Unexpected element: " + targetParent.getText()) + } as? JetElement ?: return val constructorInfo = ConstructorInfo(classInfo, expectedTypeInfo) val builder = CallableBuilderConfiguration( diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/createClassUtils.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/createClassUtils.kt index 8efed47bfd4..deae306e39b 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/createClassUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createClass/createClassUtils.kt @@ -8,7 +8,6 @@ import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil import com.intellij.openapi.module.ModuleUtilCore import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiDirectory import org.jetbrains.jet.lang.psi.JetElement import org.jetbrains.jet.lang.psi.JetExpression import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.guessTypes @@ -26,6 +25,9 @@ import org.jetbrains.jet.plugin.util.ProjectRootsUtil import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.noSubstitutions import org.jetbrains.jet.lang.resolve.DescriptorUtils import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils +import com.intellij.psi.PsiPackage +import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.canRefactor +import com.intellij.psi.PsiDirectory private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first()) @@ -34,31 +36,20 @@ private fun getTargetParentByQualifier( isQualified: Boolean, qualifierDescriptor: DeclarationDescriptor?): PsiElement? { val project = file.getProject() - val targetParent = when { - !isQualified -> file - - qualifierDescriptor is ClassDescriptor -> { + !isQualified -> + file + qualifierDescriptor is ClassDescriptor -> DescriptorToDeclarationUtil.getDeclaration(project, qualifierDescriptor) - } - - qualifierDescriptor is PackageViewDescriptor -> { - val currentModule = ModuleUtilCore.findModuleForPsiElement(file) - val targetFqName = qualifierDescriptor.getFqName() - if (targetFqName != file.getPackageFqName()) { - JavaPsiFacade.getInstance(project) - .findPackage(targetFqName.asString()) - ?.getDirectories() - ?.firstOrNull { ModuleUtilCore.findModuleForPsiElement(it) == currentModule } + qualifierDescriptor is PackageViewDescriptor -> + if (qualifierDescriptor.getFqName() != file.getPackageFqName()) { + JavaPsiFacade.getInstance(project).findPackage(qualifierDescriptor.getFqName().asString()) } - else file - } - - else -> null + else file : PsiElement + else -> + null } ?: return null - return if (targetParent.isWritable() - && ProjectRootsUtil.isInProjectOrLibSource(targetParent) - && (targetParent is PsiDirectory || targetParent is JetElement)) return targetParent else null + return if (targetParent.canRefactor()) return targetParent else null } private fun getTargetParentByCall(call: Call, file: JetFile): PsiElement? { @@ -97,4 +88,15 @@ private fun JetExpression.getInheritableTypeInfo( else -> canHaveSubtypes } } +} + +private fun PsiElement.canRefactor(): Boolean { + return when (this) { + is PsiPackage -> + getDirectories().any { it.canRefactor() } + is JetElement, is PsiDirectory -> + isWritable() && ProjectRootsUtil.isInSource(element = this, includeLibrarySources = false, includeTestSources = true) + else -> + false + } } \ No newline at end of file