IDEA-354490 Customise Structure tool window for Spring elements

GitOrigin-RevId: b0ba9385fc2f8421b3f334aec25431d1a8e985c7
This commit is contained in:
Anton Kozub
2024-08-26 12:56:58 +00:00
committed by intellij-monorepo-bot
parent 90f2e26354
commit f8acffcc57
17 changed files with 388 additions and 0 deletions
@@ -782,6 +782,10 @@
<codeInsight.linkHandler prefix="#javaClass/" handlerClass="com.intellij.codeInsight.daemon.impl.JavaClassTooltipLinkHandler"/>
<lang.psiStructureViewFactory language="JAVA" implementationClass="com.intellij.lang.java.JavaStructureViewBuilderFactory"/>
<lang.logicalStructureElementsProvider implementation="com.intellij.ide.structureView.logical.ClassOwnerLogicalStructureElementsProvider"/>
<lang.logicalStructureTreeElementProvider
id="javaPsiClassLogicalStructureTreeElementProvider"
implementation="com.intellij.ide.structureView.logical.JavaPsiClassLogicalStructureTreeElementProvider"/>
<lang.smartEnterProcessor language="JSPX" implementationClass="com.intellij.codeInsight.completion.XmlSmartEnterProcessor"/>
@@ -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<PsiClassOwner, PsiClass> {
override fun getElements(parent: PsiClassOwner): List<PsiClass> {
return parent.classes.toList()
}
override fun forLogicalModelClass(): Class<PsiClassOwner> = PsiClassOwner::class.java
}
@@ -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<PsiClass> {
override fun getModelClass(): Class<PsiClass> = PsiClass::class.java
override fun getTreeElement(model: PsiClass): StructureViewTreeElement {
return JavaClassTreeElement(model, false)
}
}
@@ -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<T> : ConvertElementsProvider<PsiClass, T>() {
override fun forLogicalModelClass(): Class<PsiClass> {
return PsiClass::class.java
}
}
@@ -120,6 +120,9 @@
<extensionPoint name="lang.structureViewExtension" interface="com.intellij.ide.structureView.StructureViewExtension" dynamic="true"/>
<extensionPoint name="lang.logicalStructureElementsProvider" interface="com.intellij.ide.logical.LogicalStructureElementsProvider" dynamic="true"/>
<extensionPoint name="lang.logicalStructureTreeElementProvider" interface="com.intellij.ide.logical.LogicalStructureTreeElementProvider" dynamic="true"/>
<extensionPoint name="lang.surroundDescriptor" beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
<with attribute="implementationClass" implements="com.intellij.lang.surroundWith.SurroundDescriptor"/>
</extensionPoint>
@@ -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);
}
@@ -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<P, C> : LogicalStructureElementsProvider<P, C> {
abstract fun convert(p: P): C?
override fun getElements(p: P): List<C> {
val t = convert(p)
return if (t != null) listOf(t) else emptyList()
}
companion object {
fun <P> convert(p: P): Sequence<*> {
val convertProviders = LogicalStructureElementsProvider.getProviders(p).filterIsInstance<ConvertElementsProvider<P, Any>>()
if (convertProviders.count() == 0) return sequenceOf(p)
val converted = convertProviders.map { it.convert(p) }.filterNotNull()
return if(converted.count() == 0) sequenceOf(p) else converted
}
}
}
@@ -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<P, C> {
fun getElements(parent: P): List<C>
companion object {
private const val EP_FQN = "com.intellij.lang.logicalStructureElementsProvider"
val EP_NAME = ExtensionPointName.create<LogicalStructureElementsProvider<*, *>>(EP_FQN)
fun <P> getProviders(p: P): Sequence<LogicalStructureElementsProvider<P, Any>> {
return EP_NAME.extensionList.asSequence()
.filter { it.forLogicalModelClass().isInstance(p) } as Sequence<LogicalStructureElementsProvider<P, Any>>
}
}
fun forLogicalModelClass(): Class<P>
}
interface ContainerElementsProvider<P, C> : LogicalStructureElementsProvider<P, C> {
val containerName: String?
}
@@ -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<T> {
companion object {
private const val EP_FQN = "com.intellij.lang.logicalStructureTreeElementProvider"
val EP_NAME = ExtensionPointName.create<LogicalStructureTreeElementProvider<*>>(EP_FQN)
fun <T> getTreeElement(model: T): StructureViewTreeElement? {
val provider = EP_NAME.extensionList
.firstOrNull { it.getModelClass().isInstance(model) } as? LogicalStructureTreeElementProvider<T>
return provider?.getTreeElement(model)
}
}
fun getModelClass(): Class<T>
fun getTreeElement(model: T): StructureViewTreeElement
}
@@ -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<TreeElement>): Collection<Group> {
val structureElement = parent.value as? LogicalStructureViewTreeElement<*> ?: return emptyList()
val result = mutableListOf<Group>()
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<TreeElement> {
return pair.second.mapNotNull { childLogical ->
children.firstOrNull { it is LogicalStructureViewTreeElement<*> && it.getLogicalAssembledModel().model == childLogical.model }
}
}
})
}
return result
}
}
@@ -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<Grouper> {
return arrayOf(LogicalGrouper())
}
}
@@ -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 <T> createViewTreeElement(assembledModel: LogicalStructureAssembledModel<T>): 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<T>: StructureViewTreeElement {
fun getLogicalAssembledModel(): LogicalStructureAssembledModel<T>
}
private class PsiElementStructureElement<T>(
private val assembledModel: LogicalStructureAssembledModel<T>,
psiElement: PsiElement
): PsiTreeElementBase<PsiElement>(psiElement), LogicalStructureViewTreeElement<T> {
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<StructureViewTreeElement> {
return assembledModel.getChildren().map { createViewTreeElement(it) }
}
override fun getLogicalAssembledModel() = assembledModel
}
private class OtherStructureElement<T>(
private val assembledModel: LogicalStructureAssembledModel<T>,
): StructureViewTreeElement, LogicalStructureViewTreeElement<T> {
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<TreeElement> {
return assembledModel.getChildren().map { createViewTreeElement(it) }.toTypedArray()
}
}
@@ -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<T>(
val project: Project,
val model: T
) {
fun getChildren(): List<LogicalStructureAssembledModel<*>> {
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<Pair<Any, List<LogicalStructureAssembledModel<*>>>> {
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()
}
}
@@ -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
}
@@ -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<T: PsiElement>: LogicalStructureModel {
fun getPsiElement(): T?
}
@@ -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<KtLightClass> {
override fun getModelClass(): Class<KtLightClass> = KtLightClass::class.java
override fun getTreeElement(model: KtLightClass): StructureViewTreeElement {
return KotlinStructureViewElement(model.kotlinOrigin ?: model, false)
}
}
@@ -155,6 +155,9 @@
order="first"
key="KNM"
factoryClass="org.jetbrains.kotlin.idea.structureView.KtDecompiledFileStructureViewBuilderProvider"/>
<lang.logicalStructureTreeElementProvider
implementation="org.jetbrains.kotlin.idea.structureView.KotlinClassLogicalStructureTreeElementProvider"
order="before javaPsiClassLogicalStructureTreeElementProvider"/>
</extensions>
<!-- Line markers -->