From cea7ba643ac7ff9d490c1e84304a1ccbc3c02353 Mon Sep 17 00:00:00 2001 From: Alexander Doroshko Date: Wed, 29 Nov 2023 22:03:33 +0100 Subject: [PATCH] Better fix for KTIJ-11594 File Nesting configured for Kotlin files makes files disappear from the Project View it doesn't introduce regression like KTIJ-27970 GitOrigin-RevId: 8c63baeb436df4bf60dd5d1459984d7e38a0d3cf --- java/java-impl/src/META-INF/JavaPlugin.xml | 2 +- .../impl/ClassesTreeStructureProvider.java | 27 ++++++++---- .../projectView/impl/nodes/ClassTreeNode.java | 30 +++++++------ .../nodes/FileNodeWithNestedFileNodes.java | 38 +++++++++++++++++ .../impl/nodes/NestingTreeNode.java | 27 +++++++----- .../projectView/KtClassOrObjectTreeNode.kt | 11 +++-- .../kotlin/idea/projectView/KtFileTreeNode.kt | 11 +++-- .../projectView/KtInternalFileTreeNode.kt | 11 +++-- .../idea/projectView/projectViewProviders.kt | 11 +++-- .../K2ProjectViewTestGenerated.java | 10 +++++ .../AbstractKotlinProjectViewTest.kt | 19 +++++++++ .../KotlinProjectViewTestGenerated.java | 10 +++++ .../NestedFilesInProjectViewTest.kt | 42 ------------------- .../projectView/FileNestingWithMembers.test | 27 ++++++++++++ .../projectView/FileNestingWithMembers.txt | 25 +++++++++++ .../FileNestingWithoutMembers.test | 25 +++++++++++ .../projectView/FileNestingWithoutMembers.txt | 19 +++++++++ .../common/resources/META-INF/kotlin-core.xml | 2 +- 18 files changed, 252 insertions(+), 95 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/FileNodeWithNestedFileNodes.java delete mode 100644 plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/NestedFilesInProjectViewTest.kt create mode 100644 plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.test create mode 100644 plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.txt create mode 100644 plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.test create mode 100644 plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.txt diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 488b53cb747a..8d1b9d9ab1ad 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -1012,7 +1012,7 @@ - + diff --git a/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java b/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java index 09ecd12a7eaf..09c22453e719 100644 --- a/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java +++ b/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java @@ -6,6 +6,7 @@ import com.intellij.ide.projectView.ProjectViewNode; import com.intellij.ide.projectView.SelectableTreeStructureProvider; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.nodes.ClassTreeNode; +import com.intellij.ide.projectView.impl.nodes.FileNodeWithNestedFileNodes; import com.intellij.ide.projectView.impl.nodes.PsiFileNode; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.application.ReadAction; @@ -27,6 +28,7 @@ import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; public class ClassesTreeStructureProvider implements SelectableTreeStructureProvider, DumbAware { @@ -68,10 +70,13 @@ public class ClassesTreeStructureProvider implements SelectableTreeStructureProv if (classOwner instanceof PsiCompiledElement || fileInRoots(file)) { PsiClass[] classes = ReadAction.compute(classOwner::getClasses); + Collection> nestedFileNodes = child instanceof FileNodeWithNestedFileNodes + ? ((FileNodeWithNestedFileNodes)child).getNestedFileNodes() + : Collections.emptyList(); if (classes.length == 1 && isClassForTreeNode(file, classes[0])) { - result.add(new ClassTreeNode(myProject, classes[0], settings1, child.getChildren())); + result.add(new ClassTreeNode(myProject, classes[0], settings1, nestedFileNodes)); } else { - result.add(new PsiClassOwnerTreeNode(classOwner, settings1, child.getChildren())); + result.add(new PsiClassOwnerTreeNode(classOwner, settings1, nestedFileNodes)); } continue; } @@ -143,28 +148,32 @@ public class ClassesTreeStructureProvider implements SelectableTreeStructureProv return element instanceof PsiClass && ClassUtil.isTopLevelClass((PsiClass)element); } - private static class PsiClassOwnerTreeNode extends PsiFileNode { - @NotNull - private final Collection> myMandatoryChildren; + private static class PsiClassOwnerTreeNode extends PsiFileNode implements FileNodeWithNestedFileNodes { + private final @NotNull Collection> myNestedFileNodes; PsiClassOwnerTreeNode(@NotNull PsiClassOwner classOwner, ViewSettings settings, - @NotNull Collection> mandatoryChildren) { + @NotNull Collection> nestedFileNodes) { super(classOwner.getProject(), classOwner, settings); - myMandatoryChildren = mandatoryChildren; + myNestedFileNodes = nestedFileNodes; + } + + @Override + public @NotNull Collection> getNestedFileNodes() { + return myNestedFileNodes; } @Override public Collection> getChildrenImpl() { - List> result = new ArrayList<>(myMandatoryChildren); + List> result = new ArrayList<>(myNestedFileNodes); PsiFile value = getValue(); if (value instanceof PsiClassOwner) { ViewSettings settings = getSettings(); PsiUnnamedClass unnamedClass = JavaUnnamedClassUtil.getUnnamedClassFor(value); if (unnamedClass != null) { - result.addAll(ClassTreeNode.computeChildren(unnamedClass, settings, myProject, true, ContainerUtil.emptyList())); + result.addAll(ClassTreeNode.computeChildren(unnamedClass, settings, myProject, true)); } else { for (PsiClass aClass : ((PsiClassOwner)value).getClasses()) { if (!(aClass instanceof SyntheticElement)) { diff --git a/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/ClassTreeNode.java b/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/ClassTreeNode.java index b65294466c13..a178b4170e53 100644 --- a/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/ClassTreeNode.java +++ b/java/java-impl/src/com/intellij/ide/projectView/impl/nodes/ClassTreeNode.java @@ -22,8 +22,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class ClassTreeNode extends BasePsiMemberNode { - private final Collection> myMandatoryChildren; +public class ClassTreeNode extends BasePsiMemberNode implements FileNodeWithNestedFileNodes { + private final Collection> myNestedFileNodes; private boolean isAlwaysExpand; public ClassTreeNode(Project project, @NotNull PsiClass value, ViewSettings viewSettings) { @@ -33,25 +33,29 @@ public class ClassTreeNode extends BasePsiMemberNode { public ClassTreeNode(Project project, @NotNull PsiClass value, ViewSettings viewSettings, - @NotNull Collection> mandatoryChildren) { + @NotNull Collection> nestedFileNodes) { super(project, value, viewSettings); - myMandatoryChildren = mandatoryChildren; + myNestedFileNodes = nestedFileNodes; + } + + @Override + public @NotNull Collection> getNestedFileNodes() { + return myNestedFileNodes; } @Override public Collection> getChildrenImpl() { - PsiClass parent = getValue(); - return computeChildren(parent, this.getSettings(), this.getProject(), this.isShowInnerClasses(), this.myMandatoryChildren); + ArrayList> result = new ArrayList<>(myNestedFileNodes); + result.addAll(computeChildren(getValue(), this.getSettings(), this.getProject(), this.isShowInnerClasses())); + return result; } - @NotNull @ApiStatus.Internal - public static List> computeChildren(PsiClass parent, - ViewSettings settings, - Project project, - boolean showInnerClasses, - Collection> mandatoryChildren) { - List> treeNodes = new ArrayList<>(mandatoryChildren); + public static @NotNull List> computeChildren(PsiClass parent, + ViewSettings settings, + Project project, + boolean showInnerClasses) { + List> treeNodes = new ArrayList<>(); if (parent != null) { try { boolean showMembers = settings.isShowMembers(); diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/FileNodeWithNestedFileNodes.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/FileNodeWithNestedFileNodes.java new file mode 100644 index 000000000000..fec252183667 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/FileNodeWithNestedFileNodes.java @@ -0,0 +1,38 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.projectView.impl.nodes; + +import com.intellij.ide.projectView.TreeStructureProvider; +import com.intellij.ide.projectView.impl.NestingTreeStructureProvider; +import com.intellij.ide.util.treeView.AbstractTreeNode; + +import java.util.Collection; + +/** + * A file node in the Project View, which may be a parent node for other file nodes, thanks to the + * file nesting feature. + *

+ * This class is used in the following way: + *

    + *
  • {@link NestingTreeStructureProvider} may replace {@link PsiFileNode} with {@link NestingTreeNode} if the corresponding file must + * show up as a parent for some other files according to the configured nesting rules. + * {@link NestingTreeNode} implements {@link FileNodeWithNestedFileNodes}
  • + *
  • Other {@link TreeStructureProvider} implementations may want to replace some file nodes with technology-specific nodes + * in order to have a special presentation and optionally to show the file structure ('Show members' feature of the Project View). + * Examples: + *
      + *
    • {@link com.intellij.ide.projectView.impl.ClassesTreeStructureProvider} handles Java, Groovy, Kotlin files
    • + *
    • {@link org.jetbrains.kotlin.idea.projectView.KotlinExpandNodeProjectViewProvider} handles Kotlin files once again
    • + *
    • {@link com.goide.tree.GoTreeStructureProvider} for Go
    • + *
    • {@link org.jetbrains.plugins.scala.projectView.ScalaTreeStructureProvider} for Scala
    • + *
    + *
  • + *
  • Not to lose the nested files, such {@link TreeStructureProvider} implementations check if the node, + * which they are going to replace, is an instance of {@link FileNodeWithNestedFileNodes}. + * If so, they make sure that the children of the replacement node include {@link #getNestedFileNodes()} of the original node
  • + *
  • For the replacement node, it makes sense to implement {@link FileNodeWithNestedFileNodes} as well, + * in case some other {@link TreeStructureProvider} decides to replace it once again
  • + *
+ */ +public interface FileNodeWithNestedFileNodes { + Collection> getNestedFileNodes(); +} diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/NestingTreeNode.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/NestingTreeNode.java index d6c954f0d5e1..32cdedd98ba8 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/NestingTreeNode.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/nodes/NestingTreeNode.java @@ -8,13 +8,14 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; +import java.util.Objects; -public final class NestingTreeNode extends PsiFileNode { - @NotNull private final Collection myChildNodes; +public final class NestingTreeNode extends PsiFileNode implements FileNodeWithNestedFileNodes { + private final @NotNull Collection myNestedFileNodes; - public NestingTreeNode(@NotNull final PsiFileNode originalNode, @NotNull final Collection childNodes) { - super(originalNode.getProject(), originalNode.getValue(), originalNode.getSettings()); - myChildNodes = childNodes; + public NestingTreeNode(@NotNull PsiFileNode originalNode, @NotNull Collection nestedFileNodes) { + super(originalNode.getProject(), Objects.requireNonNull(originalNode.getValue()), originalNode.getSettings()); + myNestedFileNodes = nestedFileNodes; } @Override @@ -28,20 +29,24 @@ public final class NestingTreeNode extends PsiFileNode { } @Override - public Collection> getChildrenImpl() { - final ArrayList> result = new ArrayList<>(myChildNodes.size()); - for (PsiFileNode node : myChildNodes) { + public @NotNull Collection> getNestedFileNodes() { + ArrayList> result = new ArrayList<>(myNestedFileNodes.size()); + for (PsiFileNode node : myNestedFileNodes) { PsiFile value = node.getValue(); if (value != null) { result.add(new PsiFileNode(node.getProject(), value, node.getSettings())); } } + return result; + } - final Collection> superChildren = super.getChildrenImpl(); + @Override + public Collection> getChildrenImpl() { + ArrayList> result = new ArrayList<>(getNestedFileNodes()); + Collection> superChildren = super.getChildrenImpl(); if (superChildren != null) { result.addAll(superChildren); } - return result; } @@ -49,7 +54,7 @@ public final class NestingTreeNode extends PsiFileNode { public boolean contains(@NotNull final VirtualFile file) { if (super.contains(file)) return true; - for (PsiFileNode node : myChildNodes) { + for (PsiFileNode node : myNestedFileNodes) { final PsiFile psiFile = node.getValue(); if (psiFile != null && file.equals(psiFile.getVirtualFile())) { return true; diff --git a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtClassOrObjectTreeNode.kt b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtClassOrObjectTreeNode.kt index 0e895f7082e0..f27a5b94e094 100644 --- a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtClassOrObjectTreeNode.kt +++ b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtClassOrObjectTreeNode.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.idea.projectView import com.intellij.ide.projectView.PresentationData import com.intellij.ide.projectView.ViewSettings import com.intellij.ide.projectView.impl.nodes.AbstractPsiBasedNode +import com.intellij.ide.projectView.impl.nodes.FileNodeWithNestedFileNodes import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile @@ -16,8 +17,8 @@ class KtClassOrObjectTreeNode( project: Project?, ktClassOrObject: KtClassOrObject, viewSettings: ViewSettings, - private val mandatoryChildren: Collection> -) : AbstractPsiBasedNode(project, ktClassOrObject, viewSettings) { + private val nestedFileNodes: Collection> +) : AbstractPsiBasedNode(project, ktClassOrObject, viewSettings), FileNodeWithNestedFileNodes { // this constructor is kept for plugin API compatibility constructor( @@ -28,11 +29,13 @@ class KtClassOrObjectTreeNode( override fun extractPsiFromValue(): PsiElement? = value + override fun getNestedFileNodes(): Collection> = nestedFileNodes + override fun getChildrenImpl(): Collection> = if (value != null && settings.isShowMembers) { - mandatoryChildren + value.getStructureDeclarations().toNodes(settings) + nestedFileNodes + value.getStructureDeclarations().toNodes(settings) } else { - mandatoryChildren + nestedFileNodes } override fun updateImpl(data: PresentationData) { diff --git a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtFileTreeNode.kt b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtFileTreeNode.kt index 506a76c42d64..5d97bf51eba2 100644 --- a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtFileTreeNode.kt +++ b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtFileTreeNode.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlin.idea.projectView import com.intellij.ide.projectView.ViewSettings import com.intellij.ide.projectView.impl.nodes.AbstractPsiBasedNode +import com.intellij.ide.projectView.impl.nodes.FileNodeWithNestedFileNodes import com.intellij.ide.projectView.impl.nodes.PsiFileNode import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.project.Project @@ -14,14 +15,16 @@ class KtFileTreeNode( project: Project?, val ktFile: KtFile, viewSettings: ViewSettings, - private val mandatoryChildren: Collection> -) : PsiFileNode(project, ktFile, viewSettings) { + private val nestedFileNodes: Collection> +) : PsiFileNode(project, ktFile, viewSettings), FileNodeWithNestedFileNodes { + + override fun getNestedFileNodes(): Collection> = nestedFileNodes override fun getChildrenImpl(): Collection> = if (settings.isShowMembers) { - mandatoryChildren + ktFile.toDeclarationsNodes(settings) + nestedFileNodes + ktFile.toDeclarationsNodes(settings) } else { - mandatoryChildren + nestedFileNodes } } diff --git a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtInternalFileTreeNode.kt b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtInternalFileTreeNode.kt index 7f05dd7444d6..a1f561d6f6b4 100644 --- a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtInternalFileTreeNode.kt +++ b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/KtInternalFileTreeNode.kt @@ -4,6 +4,7 @@ package org.jetbrains.kotlin.idea.projectView import com.intellij.ide.projectView.PresentationData import com.intellij.ide.projectView.ViewSettings import com.intellij.ide.projectView.impl.nodes.AbstractPsiBasedNode +import com.intellij.ide.projectView.impl.nodes.FileNodeWithNestedFileNodes import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project @@ -30,8 +31,8 @@ class KtInternalFileTreeNode( project: Project?, lightClass: KtLightClass, viewSettings: ViewSettings, - private val mandatoryChildren: Collection> -) : AbstractPsiBasedNode(project, lightClass, viewSettings) { + private val nestedFileNodes: Collection> +) : AbstractPsiBasedNode(project, lightClass, viewSettings), FileNodeWithNestedFileNodes { private val navigatablePsiElement: SmartPsiElementPointer? by lazy { val ktClsFile = value?.navigationElement as? KtClsFile @@ -75,11 +76,13 @@ class KtInternalFileTreeNode( override fun extractPsiFromValue(): PsiElement? = navigatablePsiElement?.element ?: value + override fun getNestedFileNodes(): Collection> = nestedFileNodes + override fun getChildrenImpl(): Collection> { - if (!settings.isShowMembers) return mandatoryChildren + if (!settings.isShowMembers) return nestedFileNodes val members = (extractPsiFromValue() as? KtFile)?.toDeclarationsNodes(settings) - return if (members.isNullOrEmpty()) mandatoryChildren else mandatoryChildren + members + return if (members.isNullOrEmpty()) nestedFileNodes else nestedFileNodes + members } override fun canRepresent(element: Any?): Boolean { diff --git a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/projectViewProviders.kt b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/projectViewProviders.kt index 2eabd427c6f9..61be0ef38396 100644 --- a/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/projectViewProviders.kt +++ b/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/projectView/projectViewProviders.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.idea.projectView import com.intellij.ide.projectView.SelectableTreeStructureProvider import com.intellij.ide.projectView.TreeStructureProvider import com.intellij.ide.projectView.ViewSettings +import com.intellij.ide.projectView.impl.nodes.FileNodeWithNestedFileNodes import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project @@ -12,7 +13,6 @@ import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement import org.jetbrains.annotations.ApiStatus -import org.jetbrains.kotlin.asJava.classes.KtExtensibleLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.idea.KotlinIconProvider @@ -35,19 +35,18 @@ class KotlinExpandNodeProjectViewProvider : TreeStructureProvider, DumbAware { for (child in children) { val value = child.value val ktFile = value?.asKtFile() + val nestedFileNodes = (child as? FileNodeWithNestedFileNodes)?.nestedFileNodes ?: emptyList() - // TODO: drop filter as far as get KTIJ-11594 fixed - val mandatoryChildren = child.children.filter { it.value !is KtExtensibleLightClass } if (ktFile != null) { val mainClass = KotlinIconProvider.getSingleClass(ktFile) if (mainClass != null) { - result.add(KtClassOrObjectTreeNode(ktFile.project, mainClass, settings, mandatoryChildren)) + result.add(KtClassOrObjectTreeNode(ktFile.project, mainClass, settings, nestedFileNodes)) } else { - result.add(KtFileTreeNode(ktFile.project, ktFile, settings, mandatoryChildren)) + result.add(KtFileTreeNode(ktFile.project, ktFile, settings, nestedFileNodes)) } } else { if (value is KtLightClass) { - result.add(KtInternalFileTreeNode(value.project, value, settings, mandatoryChildren)) + result.add(KtInternalFileTreeNode(value.project, value, settings, nestedFileNodes)) } else { result.add(child) } diff --git a/plugins/kotlin/fir/test/org/jetbrains/kotlin/idea/projectView/K2ProjectViewTestGenerated.java b/plugins/kotlin/fir/test/org/jetbrains/kotlin/idea/projectView/K2ProjectViewTestGenerated.java index 8be30447df20..3270103083a7 100644 --- a/plugins/kotlin/fir/test/org/jetbrains/kotlin/idea/projectView/K2ProjectViewTestGenerated.java +++ b/plugins/kotlin/fir/test/org/jetbrains/kotlin/idea/projectView/K2ProjectViewTestGenerated.java @@ -73,6 +73,16 @@ public class K2ProjectViewTestGenerated extends AbstractK2ProjectViewTest { runTest("../idea/tests/testData/projectView/FacadeInternalWithNameClashAndSources2.test"); } + @TestMetadata("FileNestingWithMembers.test") + public void testFileNestingWithMembers() throws Exception { + runTest("../idea/tests/testData/projectView/FileNestingWithMembers.test"); + } + + @TestMetadata("FileNestingWithoutMembers.test") + public void testFileNestingWithoutMembers() throws Exception { + runTest("../idea/tests/testData/projectView/FileNestingWithoutMembers.test"); + } + @TestMetadata("MultifileFacadeInternalClass.test") public void testMultifileFacadeInternalClass() throws Exception { runTest("../idea/tests/testData/projectView/MultifileFacadeInternalClass.test"); diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/AbstractKotlinProjectViewTest.kt b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/AbstractKotlinProjectViewTest.kt index a48070ca1c09..7972f0cc5228 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/AbstractKotlinProjectViewTest.kt +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/AbstractKotlinProjectViewTest.kt @@ -1,7 +1,10 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.projectView +import com.intellij.ide.projectView.impl.ProjectViewFileNestingService +import com.intellij.ide.projectView.impl.ProjectViewFileNestingService.NestingRule import com.intellij.ide.projectView.impl.nodes.AbstractPsiBasedNode +import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.JarFileSystem import com.intellij.openapi.vfs.VirtualFile import com.intellij.projectView.TestProjectTreeStructure @@ -25,6 +28,9 @@ abstract class AbstractKotlinProjectViewTest : KotlinMultiFileHeavyProjectTestCa override fun setUp() { super.setUp() treeStructure = TestProjectTreeStructure(project, testRootDisposable) + + val initialNestingRules = ProjectViewFileNestingService.getInstance().rules + Disposer.register(testRootDisposable) { ProjectViewFileNestingService.getInstance().rules = initialNestingRules } } override fun doMultiFileTest(testDataPath: String, globalDirectives: Directives) { @@ -34,6 +40,15 @@ abstract class AbstractKotlinProjectViewTest : KotlinMultiFileHeavyProjectTestCa } treeStructure.isShowMembers = globalDirectives.getBooleanValue("SHOW_MEMBERS") + globalDirectives.listValues("NESTING_RULE")?.forEach { rule -> + rule.split("->").let { + val parentSuffix = it[0] + val childSuffix = it[1] + val nestingService = ProjectViewFileNestingService.getInstance() + nestingService.setRules(nestingService.rules + NestingRule(parentSuffix, childSuffix)) + } + } + FilenameIndex.processFilesByName(path.name, true, GlobalSearchScope.allScope(project), processor) val resultFile = processor.foundValue ?: error("$path file is not found") @@ -44,6 +59,10 @@ abstract class AbstractKotlinProjectViewTest : KotlinMultiFileHeavyProjectTestCa val tree = pane.tree PlatformTestUtil.waitWhileBusy(tree) + globalDirectives.listValues("EXPAND_ROW")?.forEach { + PlatformTestUtil.expand(tree, it.toInt()) + } + val node = TreeUtil.findNode(tree.model.root as DefaultMutableTreeNode) { val userObject = it.userObject userObject is PsiElement && userObject.containingFile?.virtualFile == resultFile || diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/KotlinProjectViewTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/KotlinProjectViewTestGenerated.java index 78a2f0897bfd..60ac67174fdc 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/KotlinProjectViewTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/KotlinProjectViewTestGenerated.java @@ -73,6 +73,16 @@ public class KotlinProjectViewTestGenerated extends AbstractKotlinProjectViewTes runTest("testData/projectView/FacadeInternalWithNameClashAndSources2.test"); } + @TestMetadata("FileNestingWithMembers.test") + public void testFileNestingWithMembers() throws Exception { + runTest("testData/projectView/FileNestingWithMembers.test"); + } + + @TestMetadata("FileNestingWithoutMembers.test") + public void testFileNestingWithoutMembers() throws Exception { + runTest("testData/projectView/FileNestingWithoutMembers.test"); + } + @TestMetadata("MultifileFacadeInternalClass.test") public void testMultifileFacadeInternalClass() throws Exception { runTest("testData/projectView/MultifileFacadeInternalClass.test"); diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/NestedFilesInProjectViewTest.kt b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/NestedFilesInProjectViewTest.kt deleted file mode 100644 index 59e3c47ce2a5..000000000000 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/projectView/NestedFilesInProjectViewTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package org.jetbrains.kotlin.idea.projectView - -import com.intellij.ide.projectView.impl.ProjectViewFileNestingService.NestingRule -import com.intellij.projectView.NestedFilesInProjectViewTest -import com.intellij.testFramework.fixtures.BasePlatformTestCase - -// TODO fix KTIJ-11594 without introducing KTIJ-27970 -abstract class NestedFilesInProjectViewTest : BasePlatformTestCase() { - private fun doTest(showMembers: Boolean, expectedTree: String) { - myFixture.addFileToProject("Foo.kt", "class Foo {val foo = 0}") - myFixture.addFileToProject("Bar.kt", "class Bar1 {val bar1 = 0}\nclass Bar2 {val bar2 = 0}") - myFixture.addFileToProject("Foo.txt", "") - myFixture.addFileToProject("Bar.txt", "") - NestedFilesInProjectViewTest.doTest(myFixture, listOf(NestingRule(".kt", ".txt")), showMembers, expectedTree) - } - - fun testWithoutMembers() = doTest( - false, - """ - | -Bar.kt - | Bar.txt - | -Foo - | Foo.txt - |""".trimMargin() - ) - - fun testWithMembers() = doTest( - true, - """ - | -Bar.kt - | Bar.txt - | -Bar1 - | bar1 - | -Bar2 - | bar2 - | -Foo - | Foo.txt - | foo - |""".trimMargin() - ) -} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.test b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.test new file mode 100644 index 000000000000..88a8f5bcbbf4 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.test @@ -0,0 +1,27 @@ +// PATH: Foo.kt +// SHOW_MEMBERS: TRUE + +// NESTING_RULE: .kt->.txt +// NESTING_RULE: .kt->Impl.kt + +// EXPAND_ROW: 1 +// EXPAND_ROW: 5 +// EXPAND_ROW: 4 + +// FILE: Foo.kt +class Foo {val foo = 0} + +// FILE: FooImpl.kt +class FooImpl {val fooImpl = 0} + +// FILE: Foo.txt + +// FILE: Bar.kt +class Bar1 {val bar1 = 0} +class Bar2 {val bar2 = 0} + +// FILE: BarImpl.kt +class BarImpl1 {val barImpl1 = 0} +class BarImpl2 {val barImpl2 = 0} + +// FILE: Bar.txt diff --git a/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.txt b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.txt new file mode 100644 index 000000000000..4cf96304099d --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithMembers.txt @@ -0,0 +1,25 @@ +Node: Foo +User object class: KtClassOrObjectTreeNode + +Value: CLASS +Value file: src/Foo.kt + +Navigation item #0: CLASS +Navigation item file #0: src/Foo.kt + + +-Project + -PsiDirectory: src + -Bar.kt + Bar.txt + +Bar1 + -Bar2 + bar2 + -BarImpl.kt + +BarImpl1 + +BarImpl2 + -[Foo] + Foo.txt + +FooImpl + foo + +External Libraries diff --git a/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.test b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.test new file mode 100644 index 000000000000..5c214974b607 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.test @@ -0,0 +1,25 @@ +// PATH: Foo.kt +// SHOW_MEMBERS: FALSE + +// NESTING_RULE: .kt->.txt +// NESTING_RULE: .kt->Impl.kt + +// EXPAND_ROW: 1 + +// FILE: Foo.kt +class Foo {val foo = 0} + +// FILE: FooImpl.kt +class FooImpl {val fooImpl = 0} + +// FILE: Foo.txt + +// FILE: Bar.kt +class Bar1 {val bar1 = 0} +class Bar2 {val bar2 = 0} + +// FILE: BarImpl.kt +class BarImpl1 {val barImpl1 = 0} +class BarImpl2 {val barImpl2 = 0} + +// FILE: Bar.txt diff --git a/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.txt b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.txt new file mode 100644 index 000000000000..226644d2f3ca --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/projectView/FileNestingWithoutMembers.txt @@ -0,0 +1,19 @@ +Node: Foo +User object class: KtClassOrObjectTreeNode + +Value: CLASS +Value file: src/Foo.kt + +Navigation item #0: CLASS +Navigation item file #0: src/Foo.kt + + +-Project + -PsiDirectory: src + -Bar.kt + Bar.txt + BarImpl.kt + -[Foo] + Foo.txt + FooImpl + +External Libraries diff --git a/plugins/kotlin/plugin/common/resources/META-INF/kotlin-core.xml b/plugins/kotlin/plugin/common/resources/META-INF/kotlin-core.xml index df5d86e4a9b6..cbdf3323bc5d 100644 --- a/plugins/kotlin/plugin/common/resources/META-INF/kotlin-core.xml +++ b/plugins/kotlin/plugin/common/resources/META-INF/kotlin-core.xml @@ -178,7 +178,7 @@ implementationClass="org.jetbrains.kotlin.psi.psiUtil.KtStringTemplateExpressionManipulator"/> - +