> 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 extends AbstractTreeNode>> 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 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> 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"/>
-
+