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
This commit is contained in:
Alexander Doroshko
2023-11-29 22:32:14 +00:00
committed by intellij-monorepo-bot
parent 7b96d01ca8
commit cea7ba643a
18 changed files with 252 additions and 95 deletions
+1 -1
View File
@@ -1012,7 +1012,7 @@
<codeInsight.surroundWithRangeAdjuster implementation="com.intellij.codeInsight.generation.surroundWith.XmlSurroundWithRangeAdjuster"/>
<liveTemplateSubstitutor implementation="com.intellij.codeInsight.template.impl.JavaTemplateSubstitutor"/>
<defaultLiveTemplates file="liveTemplates/Java"/>
<treeStructureProvider implementation="com.intellij.ide.projectView.impl.ClassesTreeStructureProvider" order="last, after NestingTreeStructureProvider"/>
<treeStructureProvider id="ClassesTreeStructureProvider" implementation="com.intellij.ide.projectView.impl.ClassesTreeStructureProvider" order="last, after NestingTreeStructureProvider"/>
<importFilteringRule implementation="com.intellij.usages.impl.rules.ImportFilteringRule"/>
<usageContextPanelProvider implementation="com.intellij.usageView.UsageContextDataflowFromPanel$Provider"/>
<usageContextPanelProvider implementation="com.intellij.usageView.UsageContextDataflowToPanel$Provider"/>
@@ -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<? extends AbstractTreeNode<?>> 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<? extends AbstractTreeNode<?>> myMandatoryChildren;
private static class PsiClassOwnerTreeNode extends PsiFileNode implements FileNodeWithNestedFileNodes {
private final @NotNull Collection<? extends AbstractTreeNode<?>> myNestedFileNodes;
PsiClassOwnerTreeNode(@NotNull PsiClassOwner classOwner,
ViewSettings settings,
@NotNull Collection<? extends AbstractTreeNode<?>> mandatoryChildren) {
@NotNull Collection<? extends AbstractTreeNode<?>> nestedFileNodes) {
super(classOwner.getProject(), classOwner, settings);
myMandatoryChildren = mandatoryChildren;
myNestedFileNodes = nestedFileNodes;
}
@Override
public @NotNull Collection<? extends AbstractTreeNode<?>> getNestedFileNodes() {
return myNestedFileNodes;
}
@Override
public Collection<AbstractTreeNode<?>> getChildrenImpl() {
List<AbstractTreeNode<?>> result = new ArrayList<>(myMandatoryChildren);
List<AbstractTreeNode<?>> 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)) {
@@ -22,8 +22,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ClassTreeNode extends BasePsiMemberNode<PsiClass> {
private final Collection<? extends AbstractTreeNode<?>> myMandatoryChildren;
public class ClassTreeNode extends BasePsiMemberNode<PsiClass> implements FileNodeWithNestedFileNodes {
private final Collection<? extends AbstractTreeNode<?>> myNestedFileNodes;
private boolean isAlwaysExpand;
public ClassTreeNode(Project project, @NotNull PsiClass value, ViewSettings viewSettings) {
@@ -33,25 +33,29 @@ public class ClassTreeNode extends BasePsiMemberNode<PsiClass> {
public ClassTreeNode(Project project,
@NotNull PsiClass value,
ViewSettings viewSettings,
@NotNull Collection<? extends AbstractTreeNode<?>> mandatoryChildren) {
@NotNull Collection<? extends AbstractTreeNode<?>> nestedFileNodes) {
super(project, value, viewSettings);
myMandatoryChildren = mandatoryChildren;
myNestedFileNodes = nestedFileNodes;
}
@Override
public @NotNull Collection<? extends AbstractTreeNode<?>> getNestedFileNodes() {
return myNestedFileNodes;
}
@Override
public Collection<AbstractTreeNode<?>> getChildrenImpl() {
PsiClass parent = getValue();
return computeChildren(parent, this.getSettings(), this.getProject(), this.isShowInnerClasses(), this.myMandatoryChildren);
ArrayList<AbstractTreeNode<?>> result = new ArrayList<>(myNestedFileNodes);
result.addAll(computeChildren(getValue(), this.getSettings(), this.getProject(), this.isShowInnerClasses()));
return result;
}
@NotNull
@ApiStatus.Internal
public static List<AbstractTreeNode<?>> computeChildren(PsiClass parent,
ViewSettings settings,
Project project,
boolean showInnerClasses,
Collection<? extends AbstractTreeNode<?>> mandatoryChildren) {
List<AbstractTreeNode<?>> treeNodes = new ArrayList<>(mandatoryChildren);
public static @NotNull List<AbstractTreeNode<?>> computeChildren(PsiClass parent,
ViewSettings settings,
Project project,
boolean showInnerClasses) {
List<AbstractTreeNode<?>> treeNodes = new ArrayList<>();
if (parent != null) {
try {
boolean showMembers = settings.isShowMembers();
@@ -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
* <a href="https://www.jetbrains.com/help/idea/file-nesting-dialog.html">file nesting</a> feature.
* <p>
* This class is used in the following way:
* <ul>
* <li>{@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}</li>
* <li>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:
* <ul>
* <li>{@link com.intellij.ide.projectView.impl.ClassesTreeStructureProvider} handles Java, Groovy, Kotlin files</li>
* <li>{@link org.jetbrains.kotlin.idea.projectView.KotlinExpandNodeProjectViewProvider} handles Kotlin files once again</li>
* <li>{@link com.goide.tree.GoTreeStructureProvider} for Go</li>
* <li>{@link org.jetbrains.plugins.scala.projectView.ScalaTreeStructureProvider} for Scala</li>
* </ul>
* </li>
* <li>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</li>
* <li>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</li>
* </ul>
*/
public interface FileNodeWithNestedFileNodes {
Collection<? extends AbstractTreeNode<?>> getNestedFileNodes();
}
@@ -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<? extends PsiFileNode> myChildNodes;
public final class NestingTreeNode extends PsiFileNode implements FileNodeWithNestedFileNodes {
private final @NotNull Collection<? extends PsiFileNode> myNestedFileNodes;
public NestingTreeNode(@NotNull final PsiFileNode originalNode, @NotNull final Collection<? extends PsiFileNode> childNodes) {
super(originalNode.getProject(), originalNode.getValue(), originalNode.getSettings());
myChildNodes = childNodes;
public NestingTreeNode(@NotNull PsiFileNode originalNode, @NotNull Collection<? extends PsiFileNode> 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<AbstractTreeNode<?>> getChildrenImpl() {
final ArrayList<AbstractTreeNode<?>> result = new ArrayList<>(myChildNodes.size());
for (PsiFileNode node : myChildNodes) {
public @NotNull Collection<AbstractTreeNode<?>> getNestedFileNodes() {
ArrayList<AbstractTreeNode<?>> 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<AbstractTreeNode<?>> superChildren = super.getChildrenImpl();
@Override
public Collection<AbstractTreeNode<?>> getChildrenImpl() {
ArrayList<AbstractTreeNode<?>> result = new ArrayList<>(getNestedFileNodes());
Collection<AbstractTreeNode<?>> 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;
@@ -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<AbstractTreeNode<*>>
) : AbstractPsiBasedNode<KtClassOrObject>(project, ktClassOrObject, viewSettings) {
private val nestedFileNodes: Collection<AbstractTreeNode<*>>
) : AbstractPsiBasedNode<KtClassOrObject>(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<AbstractTreeNode<*>> = nestedFileNodes
override fun getChildrenImpl(): Collection<AbstractTreeNode<*>> =
if (value != null && settings.isShowMembers) {
mandatoryChildren + value.getStructureDeclarations().toNodes(settings)
nestedFileNodes + value.getStructureDeclarations().toNodes(settings)
} else {
mandatoryChildren
nestedFileNodes
}
override fun updateImpl(data: PresentationData) {
@@ -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<AbstractTreeNode<*>>
) : PsiFileNode(project, ktFile, viewSettings) {
private val nestedFileNodes: Collection<AbstractTreeNode<*>>
) : PsiFileNode(project, ktFile, viewSettings), FileNodeWithNestedFileNodes {
override fun getNestedFileNodes(): Collection<AbstractTreeNode<*>> = nestedFileNodes
override fun getChildrenImpl(): Collection<AbstractTreeNode<*>> =
if (settings.isShowMembers) {
mandatoryChildren + ktFile.toDeclarationsNodes(settings)
nestedFileNodes + ktFile.toDeclarationsNodes(settings)
} else {
mandatoryChildren
nestedFileNodes
}
}
@@ -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<AbstractTreeNode<*>>
) : AbstractPsiBasedNode<KtLightClass>(project, lightClass, viewSettings) {
private val nestedFileNodes: Collection<AbstractTreeNode<*>>
) : AbstractPsiBasedNode<KtLightClass>(project, lightClass, viewSettings), FileNodeWithNestedFileNodes {
private val navigatablePsiElement: SmartPsiElementPointer<KtElement>? 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<AbstractTreeNode<*>> = nestedFileNodes
override fun getChildrenImpl(): Collection<AbstractTreeNode<*>> {
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 {
@@ -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)
}
@@ -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");
@@ -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 ||
@@ -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");
@@ -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()
)
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -178,7 +178,7 @@
implementationClass="org.jetbrains.kotlin.psi.psiUtil.KtStringTemplateExpressionManipulator"/>
<treeStructureProvider implementation="org.jetbrains.kotlin.idea.projectView.KotlinSelectInProjectViewProvider"/>
<treeStructureProvider implementation="org.jetbrains.kotlin.idea.projectView.KotlinExpandNodeProjectViewProvider" order="last, after NestingTreeStructureProvider"/>
<treeStructureProvider implementation="org.jetbrains.kotlin.idea.projectView.KotlinExpandNodeProjectViewProvider" order="last, after ClassesTreeStructureProvider"/>
<qualifiedNameProvider implementation="org.jetbrains.kotlin.idea.actions.KotlinQualifiedNameProvider"/>