diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 497b168f5bc0..5eacc7b52126 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -782,6 +782,10 @@ + + diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/logical/ClassOwnerLogicalStructureElementsProvider.kt b/java/java-structure-view/src/com/intellij/ide/structureView/logical/ClassOwnerLogicalStructureElementsProvider.kt new file mode 100644 index 000000000000..857e945245e7 --- /dev/null +++ b/java/java-structure-view/src/com/intellij/ide/structureView/logical/ClassOwnerLogicalStructureElementsProvider.kt @@ -0,0 +1,13 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.structureView.logical + +import com.intellij.ide.logical.LogicalStructureElementsProvider +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassOwner + +class ClassOwnerLogicalStructureElementsProvider: LogicalStructureElementsProvider { + override fun getElements(parent: PsiClassOwner): List { + return parent.classes.toList() + } + override fun forLogicalModelClass(): Class = PsiClassOwner::class.java +} diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/logical/JavaPsiClassLogicalStructureTreeElementProvider.kt b/java/java-structure-view/src/com/intellij/ide/structureView/logical/JavaPsiClassLogicalStructureTreeElementProvider.kt new file mode 100644 index 000000000000..0c0e223f6d24 --- /dev/null +++ b/java/java-structure-view/src/com/intellij/ide/structureView/logical/JavaPsiClassLogicalStructureTreeElementProvider.kt @@ -0,0 +1,17 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.structureView.logical + +import com.intellij.ide.logical.LogicalStructureTreeElementProvider +import com.intellij.ide.structureView.StructureViewTreeElement +import com.intellij.ide.structureView.impl.java.JavaClassTreeElement +import com.intellij.psi.PsiClass + +class JavaPsiClassLogicalStructureTreeElementProvider: LogicalStructureTreeElementProvider { + + override fun getModelClass(): Class = PsiClass::class.java + + override fun getTreeElement(model: PsiClass): StructureViewTreeElement { + return JavaClassTreeElement(model, false) + } + +} \ No newline at end of file diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/logical/PsiClassLogicalElementProvider.kt b/java/java-structure-view/src/com/intellij/ide/structureView/logical/PsiClassLogicalElementProvider.kt new file mode 100644 index 000000000000..d3c5da334560 --- /dev/null +++ b/java/java-structure-view/src/com/intellij/ide/structureView/logical/PsiClassLogicalElementProvider.kt @@ -0,0 +1,11 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.structureView.logical + +import com.intellij.ide.logical.ConvertElementsProvider +import com.intellij.psi.PsiClass + +abstract class PsiClassLogicalElementProvider : ConvertElementsProvider() { + override fun forLogicalModelClass(): Class { + return PsiClass::class.java + } +} \ No newline at end of file diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index c3f56c9bb7e8..01da5dbe1895 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -120,6 +120,9 @@ + + + diff --git a/platform/structure-view-impl/src/com/intellij/ide/highlighter/LanguageFileTypeStructureViewBuilderProvider.java b/platform/structure-view-impl/src/com/intellij/ide/highlighter/LanguageFileTypeStructureViewBuilderProvider.java index 500094fe7ddc..01f03245d0dc 100644 --- a/platform/structure-view-impl/src/com/intellij/ide/highlighter/LanguageFileTypeStructureViewBuilderProvider.java +++ b/platform/structure-view-impl/src/com/intellij/ide/highlighter/LanguageFileTypeStructureViewBuilderProvider.java @@ -19,6 +19,7 @@ */ package com.intellij.ide.highlighter; +import com.intellij.ide.logical.impl.LogicalStructureViewService; import com.intellij.ide.structureView.StructureViewBuilder; import com.intellij.ide.structureView.StructureViewBuilderProvider; import com.intellij.lang.LanguageStructureViewBuilder; @@ -43,6 +44,12 @@ public class LanguageFileTypeStructureViewBuilderProvider implements StructureVi final PsiFile psiFile = PsiManager.getInstance(project).findFile(file); if (psiFile == null) return null; + //boolean isLogicalViewAvailable = true; // TODO StructureTW: add logic + //if (isLogicalViewAvailable) { + StructureViewBuilder logicalViewBuilder = LogicalStructureViewService.Companion.getInstance(project).getLogicalStructureBuilder(psiFile); + if (logicalViewBuilder != null) return logicalViewBuilder; + //} + final PsiStructureViewFactory factory = LanguageStructureViewBuilder.getInstance().forLanguage(psiFile.getLanguage()); return factory == null ? null : factory.getStructureViewBuilder(psiFile); } diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/ConvertElementsProvider.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/ConvertElementsProvider.kt new file mode 100644 index 000000000000..35633393c6af --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/ConvertElementsProvider.kt @@ -0,0 +1,21 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical + +abstract class ConvertElementsProvider : LogicalStructureElementsProvider { + abstract fun convert(p: P): C? + + override fun getElements(p: P): List { + val t = convert(p) + return if (t != null) listOf(t) else emptyList() + } + + companion object { + fun

convert(p: P): Sequence<*> { + val convertProviders = LogicalStructureElementsProvider.getProviders(p).filterIsInstance>() + if (convertProviders.count() == 0) return sequenceOf(p) + + val converted = convertProviders.map { it.convert(p) }.filterNotNull() + return if(converted.count() == 0) sequenceOf(p) else converted + } + } +} \ No newline at end of file diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/LogicalStructureElementsProvider.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/LogicalStructureElementsProvider.kt new file mode 100644 index 000000000000..2e7ad74bf31a --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/LogicalStructureElementsProvider.kt @@ -0,0 +1,25 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical + +import com.intellij.openapi.extensions.ExtensionPointName + +interface LogicalStructureElementsProvider { + + fun getElements(parent: P): List + + companion object { + private const val EP_FQN = "com.intellij.lang.logicalStructureElementsProvider" + + val EP_NAME = ExtensionPointName.create>(EP_FQN) + + fun

getProviders(p: P): Sequence> { + return EP_NAME.extensionList.asSequence() + .filter { it.forLogicalModelClass().isInstance(p) } as Sequence> + } + } + fun forLogicalModelClass(): Class

+} + +interface ContainerElementsProvider : LogicalStructureElementsProvider { + val containerName: String? +} \ No newline at end of file diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/LogicalStructureTreeElementProvider.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/LogicalStructureTreeElementProvider.kt new file mode 100644 index 000000000000..5612e23845a0 --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/LogicalStructureTreeElementProvider.kt @@ -0,0 +1,25 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical + +import com.intellij.ide.structureView.StructureViewTreeElement +import com.intellij.openapi.extensions.ExtensionPointName + +interface LogicalStructureTreeElementProvider { + + companion object { + private const val EP_FQN = "com.intellij.lang.logicalStructureTreeElementProvider" + + val EP_NAME = ExtensionPointName.create>(EP_FQN) + + fun getTreeElement(model: T): StructureViewTreeElement? { + val provider = EP_NAME.extensionList + .firstOrNull { it.getModelClass().isInstance(model) } as? LogicalStructureTreeElementProvider + return provider?.getTreeElement(model) + } + } + + fun getModelClass(): Class + + fun getTreeElement(model: T): StructureViewTreeElement + +} diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalGrouper.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalGrouper.kt new file mode 100644 index 000000000000..14b5fca8d6d9 --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalGrouper.kt @@ -0,0 +1,54 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical.impl + +import com.intellij.ide.TypePresentationService +import com.intellij.ide.projectView.PresentationData +import com.intellij.ide.util.treeView.AbstractTreeNode +import com.intellij.ide.util.treeView.smartTree.ActionPresentation +import com.intellij.ide.util.treeView.smartTree.Group +import com.intellij.ide.util.treeView.smartTree.Grouper +import com.intellij.ide.util.treeView.smartTree.TreeElement +import com.intellij.navigation.ItemPresentation +import javax.swing.Icon + +class LogicalGrouper: Grouper { + + private val typePresentationService = TypePresentationService.getService() + + override fun getPresentation(): ActionPresentation { + return object : ActionPresentation { + override fun getText(): String = "Logical" + + override fun getDescription(): String = "" + + override fun getIcon(): Icon? = null + } + } + + override fun getName(): String = "Logical" + + override fun group(parent: AbstractTreeNode<*>, children: MutableCollection): Collection { + val structureElement = parent.value as? LogicalStructureViewTreeElement<*> ?: return emptyList() + val result = mutableListOf() + for (pair in structureElement.getLogicalAssembledModel().getChildrenGrouped()) { + result.add(object: Group { + override fun getPresentation(): ItemPresentation { + val groupingObject = pair.first + return PresentationData( + typePresentationService.getObjectName(groupingObject), + typePresentationService.getTypeName(groupingObject), + typePresentationService.getIcon(groupingObject), + null + ) + } + + override fun getChildren(): Collection { + return pair.second.mapNotNull { childLogical -> + children.firstOrNull { it is LogicalStructureViewTreeElement<*> && it.getLogicalAssembledModel().model == childLogical.model } + } + } + }) + } + return result + } +} \ No newline at end of file diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalStructureViewService.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalStructureViewService.kt new file mode 100644 index 000000000000..0c5e224853f0 --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalStructureViewService.kt @@ -0,0 +1,51 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical.impl + +import com.intellij.ide.logical.model.LogicalStructureAssembledModel +import com.intellij.ide.structureView.* +import com.intellij.ide.util.treeView.smartTree.Grouper +import com.intellij.openapi.components.Service +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile + +@Service(Service.Level.PROJECT) +class LogicalStructureViewService( + private val project: Project +) { + + companion object { + fun getInstance(project: Project): LogicalStructureViewService = project.getService(LogicalStructureViewService::class.java) + } + + fun getLogicalStructureBuilder(psiFile: PsiFile): StructureViewBuilder? { + val assembledModel = LogicalStructureAssembledModel(project, psiFile) + if (assembledModel.getChildren().isEmpty()) return null + return object: TreeBasedStructureViewBuilder() { + override fun createStructureViewModel(editor: Editor?): StructureViewModel { + return LogicalStructureViewModel(psiFile, editor, assembledModel) + } + + override fun isRootNodeShown(): Boolean { + return false + } + } + } + +} + +private class LogicalStructureViewModel(psiFile: PsiFile, editor: Editor?, assembledModel: LogicalStructureAssembledModel<*>) + : StructureViewModelBase(psiFile, editor, createViewTreeElement(assembledModel)), StructureViewModel.ElementInfoProvider { + + override fun isAlwaysShowsPlus(element: StructureViewTreeElement?): Boolean { + return false + } + + override fun isAlwaysLeaf(element: StructureViewTreeElement?): Boolean { + return false + } + + override fun getGroupers(): Array { + return arrayOf(LogicalGrouper()) + } +} \ No newline at end of file diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalStructureViewTreeElements.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalStructureViewTreeElements.kt new file mode 100644 index 000000000000..ae50f6282fc3 --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/impl/LogicalStructureViewTreeElements.kt @@ -0,0 +1,84 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical.impl + +import com.intellij.ide.TypePresentationService +import com.intellij.ide.logical.LogicalStructureTreeElementProvider +import com.intellij.ide.logical.model.LogicalStructureAssembledModel +import com.intellij.ide.logical.model.LogicalStructureModel +import com.intellij.ide.logical.model.PsiRepresentingLogicalStructureModel +import com.intellij.ide.projectView.PresentationData +import com.intellij.ide.structureView.StructureViewTreeElement +import com.intellij.ide.structureView.impl.common.PsiTreeElementBase +import com.intellij.ide.util.treeView.smartTree.TreeElement +import com.intellij.navigation.ItemPresentation +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiTarget +import javax.swing.Icon + +fun createViewTreeElement(assembledModel: LogicalStructureAssembledModel): StructureViewTreeElement { + val model = assembledModel.model + val explicitElement = LogicalStructureTreeElementProvider.getTreeElement(model) + if (explicitElement != null) return explicitElement + val psiElement: PsiElement? = when { + model is PsiElement -> model + model is PsiTarget && model.isValid() -> model.navigationElement + else -> null + } + if (psiElement != null) { + return PsiElementStructureElement(assembledModel, psiElement) + } + return OtherStructureElement(assembledModel) +} + + +interface LogicalStructureViewTreeElement: StructureViewTreeElement { + + fun getLogicalAssembledModel(): LogicalStructureAssembledModel + +} + +private class PsiElementStructureElement( + private val assembledModel: LogicalStructureAssembledModel, + psiElement: PsiElement +): PsiTreeElementBase(psiElement), LogicalStructureViewTreeElement { + + private val typePresentationService = TypePresentationService.getService() + + override fun getPresentableText(): String? = typePresentationService.getObjectName(assembledModel.model!!) + + override fun getLocationString(): String? = typePresentationService.getTypeName(assembledModel.model!!) + + override fun getIcon(open: Boolean): Icon? = typePresentationService.getIcon(assembledModel.model!!) + + override fun getChildrenBase(): Collection { + return assembledModel.getChildren().map { createViewTreeElement(it) } + } + + override fun getLogicalAssembledModel() = assembledModel + +} + +private class OtherStructureElement( + private val assembledModel: LogicalStructureAssembledModel, +): StructureViewTreeElement, LogicalStructureViewTreeElement { + + private val typePresentationService = TypePresentationService.getService() + + override fun getValue(): Any = assembledModel.model ?: "" + + override fun getLogicalAssembledModel() = assembledModel + + override fun getPresentation(): ItemPresentation { + return object: ItemPresentation { + override fun getPresentableText(): String? = typePresentationService.getObjectName(assembledModel.model!!) + + override fun getLocationString(): String? = typePresentationService.getTypeName(assembledModel.model!!) + + override fun getIcon(unused: Boolean): Icon? = typePresentationService.getIcon(assembledModel.model!!) + } + } + + override fun getChildren(): Array { + return assembledModel.getChildren().map { createViewTreeElement(it) }.toTypedArray() + } +} \ No newline at end of file diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/model/LogicalStructureAssembledModel.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/model/LogicalStructureAssembledModel.kt new file mode 100644 index 000000000000..3fbef637d02e --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/model/LogicalStructureAssembledModel.kt @@ -0,0 +1,36 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical.model + +import com.intellij.ide.logical.ContainerElementsProvider +import com.intellij.ide.logical.ConvertElementsProvider +import com.intellij.ide.logical.LogicalStructureElementsProvider +import com.intellij.openapi.project.Project + +class LogicalStructureAssembledModel( + val project: Project, + val model: T +) { + + fun getChildren(): List> { + return LogicalStructureElementsProvider.getProviders(model!!) + .flatMap { it.getElements(model) } + .flatMap { ConvertElementsProvider.convert(it) } + .map { LogicalStructureAssembledModel(project, it) } + .toList() + } + + /** + * The grouping element in each pair - Any - can be any object, for which a PresentationProvider is registered + */ + fun getChildrenGrouped(): List>>> { + return LogicalStructureElementsProvider.getProviders(model!!) + .mapNotNull { provider -> + if (provider !is ContainerElementsProvider) return@mapNotNull null + val children = provider.getElements(model) + .map { LogicalStructureAssembledModel(project, it) } + Pair(provider, children) + } + .toList() + } + +} \ No newline at end of file diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/model/LogicalStructureModel.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/model/LogicalStructureModel.kt new file mode 100644 index 000000000000..21a960b140a8 --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/model/LogicalStructureModel.kt @@ -0,0 +1,8 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical.model + +interface LogicalStructureModel { + + fun getName(): String + +} \ No newline at end of file diff --git a/platform/structure-view-impl/src/com/intellij/ide/logical/model/PsiRepresentingLogicalStructureModel.kt b/platform/structure-view-impl/src/com/intellij/ide/logical/model/PsiRepresentingLogicalStructureModel.kt new file mode 100644 index 000000000000..cae49707490c --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/logical/model/PsiRepresentingLogicalStructureModel.kt @@ -0,0 +1,10 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ide.logical.model + +import com.intellij.psi.PsiElement + +interface PsiRepresentingLogicalStructureModel: LogicalStructureModel { + + fun getPsiElement(): T? + +} \ No newline at end of file diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/structureView/KotlinClassLogicalStructureTreeElementProvider.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/structureView/KotlinClassLogicalStructureTreeElementProvider.kt new file mode 100644 index 000000000000..2e18610deb38 --- /dev/null +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/structureView/KotlinClassLogicalStructureTreeElementProvider.kt @@ -0,0 +1,16 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.structureView + +import com.intellij.ide.logical.LogicalStructureTreeElementProvider +import com.intellij.ide.structureView.StructureViewTreeElement +import org.jetbrains.kotlin.asJava.classes.KtLightClass + +class KotlinClassLogicalStructureTreeElementProvider: LogicalStructureTreeElementProvider { + + override fun getModelClass(): Class = KtLightClass::class.java + + override fun getTreeElement(model: KtLightClass): StructureViewTreeElement { + return KotlinStructureViewElement(model.kotlinOrigin ?: model, false) + } + +} \ No newline at end of file diff --git a/plugins/kotlin/plugin/k1/resources/META-INF/kotlin-core-fe10.xml b/plugins/kotlin/plugin/k1/resources/META-INF/kotlin-core-fe10.xml index 8069744ab091..2736a52c0c39 100644 --- a/plugins/kotlin/plugin/k1/resources/META-INF/kotlin-core-fe10.xml +++ b/plugins/kotlin/plugin/k1/resources/META-INF/kotlin-core-fe10.xml @@ -155,6 +155,9 @@ order="first" key="KNM" factoryClass="org.jetbrains.kotlin.idea.structureView.KtDecompiledFileStructureViewBuilderProvider"/> +