IDEA-369073 JPA in Structure view: slow operations on navigating to DB

(cherry picked from commit 3d8474ac84d0fc9818ea617b16f4dab92adf0c9c)

IJ-CR-159980

GitOrigin-RevId: 553f165cf3fc1a14b5f4cecda51e4a97d4badc2d
This commit is contained in:
Anton Kozub
2025-03-24 14:53:45 +01:00
committed by intellij-monorepo-bot
parent 13b5dd97c8
commit 4f984e6aea
8 changed files with 99 additions and 28 deletions

View File

@@ -53,6 +53,12 @@
- a:isFileOpen(com.intellij.openapi.vfs.VirtualFile):Z
- a:isImplicitSaveAllowed(com.intellij.openapi.editor.Document):Z
- a:saveAs(com.intellij.ide.lightEdit.LightEditorInfo,com.intellij.openapi.vfs.VirtualFile):com.intellij.ide.lightEdit.LightEditorInfo
*f:com.intellij.ide.structureView.StructureViewClickEvent
- <init>(com.intellij.ide.structureView.StructureViewTreeElement,I):V
- f:getElement():com.intellij.ide.structureView.StructureViewTreeElement
- f:getFragmentIndex():I
*:com.intellij.ide.structureView.StructureViewModel$ClickHandler
- a:handleClick(com.intellij.ide.structureView.StructureViewClickEvent):java.util.concurrent.CompletableFuture
com.intellij.ide.structureView.StructureViewModel$ExpandInfoProvider
- *:getMinimumAutoExpandDepth():I
*:com.intellij.ide.ui.ToolbarSettings

View File

@@ -0,0 +1,10 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.structureView
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Experimental
class StructureViewClickEvent(
val element: StructureViewTreeElement,
val fragmentIndex: Int
)

View File

@@ -9,6 +9,8 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
/**
* Defines the model for the data displayed in the standard structure view or file structure
* popup component. The model of the standard structure view is represented as a tree of elements.
@@ -105,9 +107,26 @@ public interface StructureViewModel extends TreeModel, Disposable {
}
}
/**
* @deprecated use ClickHandler
*/
@Deprecated(forRemoval = true, since = "2025.2")
interface ActionHandler {
boolean handleClick(StructureViewTreeElement element, int fragmentIndex);
}
@ApiStatus.Experimental
@FunctionalInterface
interface ClickHandler {
/**
* @return - true if the event was handled
*/
@NotNull
CompletableFuture<Boolean> handleClick(@NotNull StructureViewClickEvent event);
}
}

View File

@@ -79,6 +79,7 @@ import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -1003,14 +1004,16 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
@Override
public void processMouseEvent(MouseEvent event) {
if (event.getID() == MouseEvent.MOUSE_PRESSED) requestFocus();
if (myTreeModel instanceof StructureViewModel.ActionHandler actionHandler) {
boolean handled = processCustomEventHandler(actionHandler, event);
if (handled) {
event.consume();
return;
}
if (myTreeModel instanceof StructureViewModel.ActionHandler || myTreeModel instanceof StructureViewModel.ClickHandler) {
processCustomEventHandler(myTreeModel, event)
.whenComplete((Boolean handled, Throwable t) -> {
if (handled != null && handled)
event.consume();
else
super.processMouseEvent(event);
});
}
super.processMouseEvent(event);
else super.processMouseEvent(event);
}
@Override
@@ -1048,24 +1051,32 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
return super.getFileColorForPath(path);
}
private boolean processCustomEventHandler(StructureViewModel.ActionHandler actionHandler, MouseEvent event) {
if (event.getClickCount() != 1 || event.getID() != MouseEvent.MOUSE_PRESSED) return false;
private CompletableFuture<Boolean> processCustomEventHandler(
StructureViewModel handler, MouseEvent event
) {
if (event.getClickCount() != 1 || event.getID() != MouseEvent.MOUSE_PRESSED) return CompletableFuture.completedFuture(false);
TreePath path = getPathForLocation(event.getX(), event.getY());
if (path == null) return false;
if (path == null) return CompletableFuture.completedFuture(false);
Object lastPathComponent = path.getLastPathComponent();
StructureViewTreeElement treeElement = getStructureTreeElement(lastPathComponent);
if (treeElement == null) return false;
if (treeElement == null) return CompletableFuture.completedFuture(false);
Rectangle pathBounds = getPathBounds(path);
if (pathBounds == null) return false;
if (pathBounds == null) return CompletableFuture.completedFuture(false);
int dx = event.getX() - (int) pathBounds.getX();
if (dx < 0 || dx > pathBounds.width) return false;
if (dx < 0 || dx > pathBounds.width) return CompletableFuture.completedFuture(false);
Component component = this.cellRenderer.getTreeCellRendererComponent(this, lastPathComponent, false, false, true, getRowForPath(path), false);
if (!(component instanceof SimpleColoredComponent simpleColoredComponent)) return false;
if (!(component instanceof SimpleColoredComponent simpleColoredComponent)) return CompletableFuture.completedFuture(false);
int fragmentIndex = simpleColoredComponent.findFragmentAt(dx);
if (fragmentIndex >= 0) return actionHandler.handleClick(treeElement, fragmentIndex);
return false;
if (fragmentIndex < 0)
return CompletableFuture.completedFuture(false);
else if (handler instanceof StructureViewModel.ActionHandler actionHandler)
return CompletableFuture.completedFuture(actionHandler.handleClick(treeElement, fragmentIndex));
else if (handler instanceof StructureViewModel.ClickHandler actionHandler)
return actionHandler.handleClick(new StructureViewClickEvent(treeElement, fragmentIndex));
else
return CompletableFuture.completedFuture(false);
}
@Override

View File

@@ -19,7 +19,7 @@
- *sf:Companion:com.intellij.ide.structureView.logical.model.LogicalModelPresentationProvider$Companion
- <init>():V
- getColoredText(java.lang.Object):java.util.List
- handleClick(java.lang.Object,I):Z
- handleClick(java.lang.Object,I):java.util.concurrent.CompletableFuture
- isAutoExpand(java.lang.Object):Z
*f:com.intellij.ide.structureView.logical.model.LogicalModelPresentationProvider$Companion
- f:getForObject(java.lang.Object):com.intellij.ide.structureView.logical.model.LogicalModelPresentationProvider

View File

@@ -8,6 +8,9 @@ import com.intellij.ide.structureView.logical.impl.LogicalStructureViewTreeEleme
import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.ReadAction
import com.intellij.util.concurrency.AppExecutorUtil
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@@ -42,12 +45,24 @@ object StructureViewEventsCollector: CounterUsagesCollector() {
fun logBuildStructure(viewDescriptor: StructureViewDescriptor) {
val tab = viewDescriptor.title?.let { StructureViewTab.ofTitle(it) } ?: return
BUILD_STRUCTURE.log(tab, getModelClass(viewDescriptor))
ReadAction
.nonBlocking<Class<*>?> {
getModelClass(viewDescriptor)
}
.finishOnUiThread(ModalityState.any()) {
BUILD_STRUCTURE.log(tab, it)
}.submit(AppExecutorUtil.getAppScheduledExecutorService())
}
fun logTabSelected(viewDescriptor: StructureViewDescriptor) {
val tab = viewDescriptor.title?.let { StructureViewTab.ofTitle(it) } ?: return
TAB_SELECTED.log(tab, getModelClass(viewDescriptor))
ReadAction
.nonBlocking<Class<*>?> {
getModelClass(viewDescriptor)
}
.finishOnUiThread(ModalityState.any()) {
TAB_SELECTED.log(tab, it)
}.submit(AppExecutorUtil.getAppScheduledExecutorService())
}
fun logNavigate(modelClass: Class<*>) {

View File

@@ -3,6 +3,7 @@ package com.intellij.ide.structureView.logical.impl
import com.intellij.ide.TypePresentationService
import com.intellij.ide.projectView.PresentationData
import com.intellij.ide.structureView.StructureViewClickEvent
import com.intellij.ide.structureView.StructureViewEventsCollector
import com.intellij.ide.structureView.StructureViewModel
import com.intellij.ide.structureView.StructureViewModelBase
@@ -12,9 +13,9 @@ import com.intellij.ide.structureView.logical.ContainerElementsProvider
import com.intellij.ide.structureView.logical.ExternalElementsProvider
import com.intellij.ide.structureView.logical.LogicalStructureTreeElementProvider
import com.intellij.ide.structureView.logical.PropertyElementProvider
import com.intellij.ide.structureView.logical.model.ExtendedLogicalObject
import com.intellij.ide.structureView.logical.model.LogicalContainerPresentationProvider
import com.intellij.ide.structureView.logical.model.LogicalModelPresentationProvider
import com.intellij.ide.structureView.logical.model.ExtendedLogicalObject
import com.intellij.ide.structureView.logical.model.LogicalContainer
import com.intellij.ide.structureView.logical.model.LogicalStructureAssembledModel
import com.intellij.ide.structureView.logical.model.ProvidedLogicalContainer
@@ -26,13 +27,14 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.PsiTarget
import com.intellij.ui.SimpleTextAttributes
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap
import javax.swing.Icon
@ApiStatus.Internal
class LogicalStructureViewModel private constructor(psiFile: PsiFile, editor: Editor?, assembledModel: LogicalStructureAssembledModel<*>, elementBuilder: ElementsBuilder)
: StructureViewModelBase(psiFile, editor, elementBuilder.createViewTreeElement(assembledModel)),
StructureViewModel.ElementInfoProvider, StructureViewModel.ExpandInfoProvider, StructureViewModel.ActionHandler {
StructureViewModel.ElementInfoProvider, StructureViewModel.ExpandInfoProvider, StructureViewModel.ClickHandler {
constructor(psiFile: PsiFile, editor: Editor?, assembledModel: LogicalStructureAssembledModel<*>):
this(psiFile, editor, assembledModel, ElementsBuilder())
@@ -53,13 +55,18 @@ class LogicalStructureViewModel private constructor(psiFile: PsiFile, editor: Ed
override fun isSmartExpand(): Boolean = false
override fun handleClick(element: StructureViewTreeElement, fragmentIndex: Int): Boolean {
val model = getModel(element) ?: return false
val handled = LogicalModelPresentationProvider.getForObject(model)?.handleClick(model, fragmentIndex) ?: false
if (handled) {
StructureViewEventsCollector.logCustomClickHandled(model::class.java)
override fun handleClick(event: StructureViewClickEvent): CompletableFuture<Boolean> {
val model = getModel(event.element)
val presentation = model?.let { LogicalModelPresentationProvider.getForObject(it) }
if (model == null || presentation == null) {
return CompletableFuture.completedFuture(false)
}
return presentation.handleClick(model, event.fragmentIndex).thenApply { handled ->
if (handled) {
StructureViewEventsCollector.logCustomClickHandled(model::class.java)
}
handled
}
return handled
}
private fun getModel(element: StructureViewTreeElement): Any? {

View File

@@ -6,6 +6,7 @@ import com.intellij.ide.util.treeView.PresentableNodeDescriptor
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.util.ClassExtension
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.CompletableFuture
/**
* Adds additional features for common PresentationProvider
@@ -30,6 +31,8 @@ abstract class LogicalModelPresentationProvider<T>: PresentationProvider<T>() {
open fun getColoredText(t: T): List<PresentableNodeDescriptor.ColoredFragment> = emptyList()
open fun handleClick(t: T, fragmentIndex: Int): Boolean = false
open fun handleClick(t: T, fragmentIndex: Int): CompletableFuture<Boolean> {
return CompletableFuture.completedFuture(false)
}
}