Create Class From Usage: Allow user to choose a target directory when creating class in the specific package
This commit is contained in:
@@ -59,6 +59,6 @@ public class JetSourceFilterScope extends DelegatingGlobalSearchScope {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProjectRootsUtil.isInSources(project, file, includeLibraries, /* withLibraryClassesRoots */ true, index);
|
return ProjectRootsUtil.isInSources(project, file, /* includeTestSources */ false, includeLibraries, /* withLibraryClassesRoots */ true, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ import org.jetbrains.jet.plugin.stubindex.JetSourceFilterScope
|
|||||||
import org.jetbrains.jet.plugin.configuration.JetModuleTypeManager
|
import org.jetbrains.jet.plugin.configuration.JetModuleTypeManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiDirectory
|
||||||
|
|
||||||
public object ProjectRootsUtil {
|
public object ProjectRootsUtil {
|
||||||
platformStatic
|
platformStatic
|
||||||
public fun isInSources(project: Project, file: VirtualFile,
|
public fun isInSources(project: Project, file: VirtualFile,
|
||||||
includeLibrarySources: Boolean, withLibraryClassesRoots: Boolean,
|
includeTestSources: Boolean, includeLibrarySources: Boolean, withLibraryClassesRoots: Boolean,
|
||||||
fileIndex: ProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)): 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)
|
return !JetModuleTypeManager.getInstance()!!.isKtFileInGradleProjectInWrongFolder(file, project)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,16 +41,20 @@ public object ProjectRootsUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
platformStatic
|
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 ->
|
return runReadAction { (): Boolean ->
|
||||||
val containingFile = element.getContainingFile()
|
val virtualFile = when(element) {
|
||||||
if (containingFile == null) return@runReadAction false
|
is PsiDirectory -> element.getVirtualFile()
|
||||||
|
else -> element.getContainingFile()?.getVirtualFile()
|
||||||
val virtualFile = containingFile.getVirtualFile()
|
} ?: return@runReadAction false
|
||||||
if (virtualFile == null) return@runReadAction false
|
|
||||||
|
|
||||||
val project = element.getProject()
|
val project = element.getProject()
|
||||||
return@runReadAction isInSources(project, virtualFile, includeLibrarySources, withLibraryClassesRoots)
|
return@runReadAction isInSources(project, virtualFile, includeTestSources, includeLibrarySources, withLibraryClassesRoots)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-20
@@ -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.ParameterInfo
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.ConstructorInfo
|
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.ConstructorInfo
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallableBuilderConfiguration
|
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallableBuilderConfiguration
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression
|
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.createBuilder
|
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.createBuilder
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallablePlacement
|
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallablePlacement
|
||||||
import org.jetbrains.jet.plugin.refactoring.getOrCreateKotlinFile
|
import org.jetbrains.jet.plugin.refactoring.getOrCreateKotlinFile
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassKind.*
|
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) {
|
enum class ClassKind(val keyword: String, val description: String) {
|
||||||
PLAIN_CLASS: ClassKind("class", "class")
|
PLAIN_CLASS: ClassKind("class", "class")
|
||||||
@@ -66,26 +70,44 @@ public class CreateClassFromUsageFix(
|
|||||||
JetBundle.message("create.0.from.usage", "${classInfo.kind.description} '${classInfo.name}'")
|
JetBundle.message("create.0.from.usage", "${classInfo.kind.description} '${classInfo.name}'")
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor, file: JetFile) {
|
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<PsiDirectory, String>())
|
||||||
|
}
|
||||||
|
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) {
|
with (classInfo) {
|
||||||
val targetParent = when (targetParent) {
|
val targetParent =
|
||||||
is JetElement -> targetParent
|
when (targetParent) {
|
||||||
is PsiDirectory -> {
|
is JetElement -> targetParent
|
||||||
val fileName = "$name.${JetFileType.INSTANCE.getDefaultExtension()}"
|
is PsiPackage -> createFileByPackage(targetParent)
|
||||||
val targetFile = getOrCreateKotlinFile(fileName, targetParent)
|
else -> throw AssertionError("Unexpected element: " + targetParent.getText())
|
||||||
if (targetFile == null) {
|
} as? JetElement ?: return
|
||||||
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 constructorInfo = ConstructorInfo(classInfo, expectedTypeInfo)
|
val constructorInfo = ConstructorInfo(classInfo, expectedTypeInfo)
|
||||||
val builder = CallableBuilderConfiguration(
|
val builder = CallableBuilderConfiguration(
|
||||||
|
|||||||
+24
-22
@@ -8,7 +8,6 @@ import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor
|
|||||||
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
|
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
|
||||||
import com.intellij.openapi.module.ModuleUtilCore
|
import com.intellij.openapi.module.ModuleUtilCore
|
||||||
import com.intellij.psi.JavaPsiFacade
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.PsiDirectory
|
|
||||||
import org.jetbrains.jet.lang.psi.JetElement
|
import org.jetbrains.jet.lang.psi.JetElement
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression
|
import org.jetbrains.jet.lang.psi.JetExpression
|
||||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.guessTypes
|
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.plugin.quickfix.createFromUsage.callableBuilder.noSubstitutions
|
||||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
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())
|
private fun String.checkClassName(): Boolean = isNotEmpty() && Character.isUpperCase(first())
|
||||||
|
|
||||||
@@ -34,31 +36,20 @@ private fun getTargetParentByQualifier(
|
|||||||
isQualified: Boolean,
|
isQualified: Boolean,
|
||||||
qualifierDescriptor: DeclarationDescriptor?): PsiElement? {
|
qualifierDescriptor: DeclarationDescriptor?): PsiElement? {
|
||||||
val project = file.getProject()
|
val project = file.getProject()
|
||||||
|
|
||||||
val targetParent = when {
|
val targetParent = when {
|
||||||
!isQualified -> file
|
!isQualified ->
|
||||||
|
file
|
||||||
qualifierDescriptor is ClassDescriptor -> {
|
qualifierDescriptor is ClassDescriptor ->
|
||||||
DescriptorToDeclarationUtil.getDeclaration(project, qualifierDescriptor)
|
DescriptorToDeclarationUtil.getDeclaration(project, qualifierDescriptor)
|
||||||
}
|
qualifierDescriptor is PackageViewDescriptor ->
|
||||||
|
if (qualifierDescriptor.getFqName() != file.getPackageFqName()) {
|
||||||
qualifierDescriptor is PackageViewDescriptor -> {
|
JavaPsiFacade.getInstance(project).findPackage(qualifierDescriptor.getFqName().asString())
|
||||||
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 }
|
|
||||||
}
|
}
|
||||||
else file
|
else file : PsiElement
|
||||||
}
|
else ->
|
||||||
|
null
|
||||||
else -> null
|
|
||||||
} ?: return null
|
} ?: return null
|
||||||
return if (targetParent.isWritable()
|
return if (targetParent.canRefactor()) return targetParent else null
|
||||||
&& ProjectRootsUtil.isInProjectOrLibSource(targetParent)
|
|
||||||
&& (targetParent is PsiDirectory || targetParent is JetElement)) return targetParent else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTargetParentByCall(call: Call, file: JetFile): PsiElement? {
|
private fun getTargetParentByCall(call: Call, file: JetFile): PsiElement? {
|
||||||
@@ -97,4 +88,15 @@ private fun JetExpression.getInheritableTypeInfo(
|
|||||||
else -> canHaveSubtypes
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user