From 2dd32d9d081823cf1a94262d5eff83ebb81a6d55 Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Thu, 5 Oct 2023 22:46:56 +0200 Subject: [PATCH] Revert "[java-execution] IDEA-327658 Freeze due to non-cancelable RA in ShowAffectedTestsAction.findMethods." This reverts commit 79b7ec3902cd374dd8b2e16196154df15752d36d. GitOrigin-RevId: daa7dd27676e582bcfcfd45987cc25e4a28a6fcb --- .../messages/JavaCompilerBundle.properties | 2 +- .../AffectedTestsInChangeListPainter.java | 83 ++++++++++++++++++- .../actions/ShowAffectedTestsAction.java | 13 ++- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/java/compiler/openapi/resources/messages/JavaCompilerBundle.properties b/java/compiler/openapi/resources/messages/JavaCompilerBundle.properties index e1736c1b3bf7..946aab484395 100644 --- a/java/compiler/openapi/resources/messages/JavaCompilerBundle.properties +++ b/java/compiler/openapi/resources/messages/JavaCompilerBundle.properties @@ -280,7 +280,7 @@ error.no.scratch.file.associated.with.configuration=No scratch file associated w error.associated.scratch.file.not.found=Associated scratch file not found java.scratch=Java Scratch configuration.for.java.scratch.files=Configuration for Java scratch files -test.discovery.show.affected.tests=Find affected test... +test.discovery.show.affected.tests=Show affected tests test.discovery.parametrized=Parametrized test.discovery.unused.test.data.tab.title=Unused Test Data test.discovery.tests.tab.title=Tests for {0} diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/AffectedTestsInChangeListPainter.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/AffectedTestsInChangeListPainter.java index c72f3e3b0e2e..4bf77c6b1ffc 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/AffectedTestsInChangeListPainter.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/AffectedTestsInChangeListPainter.java @@ -5,23 +5,64 @@ import com.intellij.execution.testDiscovery.actions.ShowAffectedTestsAction; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.compiler.JavaCompilerBundle; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.registry.Registry; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ChangeListDecorator; -import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.psi.PsiMethod; import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.SimpleTextAttributes; +import com.intellij.util.Alarm; +import com.intellij.util.io.PowerStatus; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.ui.EdtInvocationManager; import com.intellij.util.ui.NamedColorUtil; import org.jetbrains.annotations.NotNull; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + import static com.intellij.ui.SimpleTextAttributes.STYLE_UNDERLINE; final class AffectedTestsInChangeListPainter implements ChangeListDecorator { private final Project myProject; + private final Alarm myAlarm; + private final AtomicReference> myChangeListsToShow = new AtomicReference<>(Collections.emptySet()); public AffectedTestsInChangeListPainter(@NotNull Project project) { myProject = project; + ChangeListListener changeListListener = new ChangeListAdapter() { + @Override + public void changeListsChanged() { + scheduleUpdate(); + } + + @Override + public void changeListUpdateDone() { + scheduleUpdate(); + } + + @Override + public void defaultListChanged(ChangeList oldDefaultList, ChangeList newDefaultList, boolean automatic) { + scheduleUpdate(); + } + }; + myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, project); + MessageBusConnection connection = myProject.getMessageBus().connect(); + connection.subscribe(ChangeListListener.TOPIC, changeListListener); + DumbService.getInstance(myProject).runWhenSmart(() -> scheduleUpdate()); + } + + private void scheduleRefresh() { + if (!myProject.isDisposed()) { + ChangesViewManager.getInstance(myProject).scheduleRefresh(); + } + } + + private static int updateDelay() { + return PowerStatus.getPowerStatus() == PowerStatus.AC ? 50 : 300; } @Override @@ -33,12 +74,46 @@ final class AffectedTestsInChangeListPainter implements ChangeListDecorator { if (!Registry.is("show.affected.tests.in.changelists")) return; if (!ShowAffectedTestsAction.isEnabled(myProject)) return; if (changeList.getChanges().isEmpty()) return; + if (!myChangeListsToShow.get().contains(changeList.getId())) return; renderer.append(", ", SimpleTextAttributes.GRAYED_ATTRIBUTES); - renderer.append(JavaCompilerBundle.message("test.discovery.show.affected.tests"), new SimpleTextAttributes(STYLE_UNDERLINE, NamedColorUtil.getInactiveTextColor()), (Runnable)() -> { + renderer.append(JavaCompilerBundle.message("test.discovery.show.affected.tests"), new SimpleTextAttributes(STYLE_UNDERLINE, + NamedColorUtil.getInactiveTextColor()), (Runnable)() -> { DataContext dataContext = DataManager.getInstance().getDataContext(renderer.getTree()); Change[] changes = changeList.getChanges().toArray(Change.EMPTY_CHANGE_ARRAY); ShowAffectedTestsAction.showDiscoveredTestsByChanges(myProject, changes, changeList.getName(), dataContext); }); } + + private void scheduleUpdate() { + if (!Registry.is("show.affected.tests.in.changelists")) return; + if (!ShowAffectedTestsAction.isEnabled(myProject)) return; + myAlarm.cancelAllRequests(); + if (!myAlarm.isDisposed()) { + myAlarm.addRequest(() -> update(), updateDelay()); + } + } + + private void update() { + myChangeListsToShow.set( + ChangeListManager.getInstance(myProject).getChangeLists().stream() + .filter(list -> !list.getChanges().isEmpty()) + .map(list -> { + Collection changes = list.getChanges(); + + PsiMethod[] methods = ShowAffectedTestsAction.findMethods(myProject, changes.toArray(Change.EMPTY_CHANGE_ARRAY)); + List paths = ShowAffectedTestsAction.getRelativeAffectedPaths(myProject, changes); + if (methods.length == 0 && paths.isEmpty()) return null; + + Ref ref = Ref.create(); + ShowAffectedTestsAction.processMethods(myProject, methods, paths, (clazz, method, parameter) -> { + ref.set(list.getId()); + return false; + }); + return ref.get(); + }).filter(Objects::nonNull).collect(Collectors.toSet()) + ); + + EdtInvocationManager.getInstance().invokeLater(this::scheduleRefresh); + } } \ No newline at end of file diff --git a/java/execution/impl/src/com/intellij/execution/testDiscovery/actions/ShowAffectedTestsAction.java b/java/execution/impl/src/com/intellij/execution/testDiscovery/actions/ShowAffectedTestsAction.java index 85b9e0dfa0b5..6b820adc86ae 100644 --- a/java/execution/impl/src/com/intellij/execution/testDiscovery/actions/ShowAffectedTestsAction.java +++ b/java/execution/impl/src/com/intellij/execution/testDiscovery/actions/ShowAffectedTestsAction.java @@ -1,9 +1,11 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.execution.testDiscovery.actions; -import com.intellij.CommonBundle; import com.intellij.codeInsight.actions.VcsFacadeImpl; -import com.intellij.execution.*; +import com.intellij.execution.ExecutionException; +import com.intellij.execution.Executor; +import com.intellij.execution.JavaTestConfigurationWithDiscoverySupport; +import com.intellij.execution.Location; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.RunConfigurationProducer; import com.intellij.execution.executors.DefaultRunExecutor; @@ -204,16 +206,11 @@ public class ShowAffectedTestsAction extends AnAction { @NotNull DataContext dataContext) { DiscoveredTestsTree tree = showTree(project, dataContext, title, ActionPlaces.UNKNOWN); FeatureUsageTracker.getInstance().triggerFeatureUsed("test.discovery.selected.changes"); - tree.getEmptyText().setText(CommonBundle.message("tree.node.loading")); ApplicationManager.getApplication().executeOnPooledThread(() -> { PsiMethod[] methods = findMethods(project, changes); List filePaths = getRelativeAffectedPaths(project, Arrays.asList(changes)); - processMethodsAsync(project, methods, filePaths, createTreeProcessor(tree), () -> - { - tree.setPaintBusy(false); - tree.getEmptyText().setText(ExecutionBundle.message("no.tests.captured.for.0", title)); - }); + processMethodsAsync(project, methods, filePaths, createTreeProcessor(tree), () -> tree.setPaintBusy(false)); }); }