[UI, threading] IJPL-189188 Add @RequiresReadLock to some PsiCopyPasteManager methods

Sometimes they're called directly by AWT bypassing locks.
And these days locks aren't guaranteed on the EDT anyway.
So let's annotate and wrap into RA/WIRA as appropriate.
Mostly it's just a RA, but in the case of the paste action
it's quite possible that it'll want to write something as well,
so let's make it a WIRA there.

GitOrigin-RevId: f9d05b1ae4691f39d9f2740173e90ee5ea8b3768
This commit is contained in:
Sergei Tachenov
2025-11-10 20:00:27 +00:00
committed by intellij-monorepo-bot
parent ab8976405e
commit e25a7af880
5 changed files with 36 additions and 18 deletions
@@ -6,6 +6,7 @@ import com.intellij.codeInsight.navigation.BackgroundUpdaterTask;
import com.intellij.find.FindUtil;
import com.intellij.ide.PsiCopyPasteManager;
import com.intellij.ide.util.PsiElementListCellRenderer;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.progress.ProgressManager;
@@ -245,7 +246,7 @@ public final class PsiElementListNavigator {
for (int i = 0; i < selectedValues.length; i++) {
copy[i] = (PsiElement)selectedValues[i];
}
return PsiCopyPasteManager.newTransferable(copy);
return ReadAction.compute(() -> PsiCopyPasteManager.newTransferable(copy));
}
@Override
@@ -2,6 +2,8 @@
package com.intellij.ide;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.WriteIntentReadAction;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.DumbService;
@@ -15,6 +17,7 @@ import com.intellij.refactoring.copy.CopyHandler;
import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.MoveHandler;
import com.intellij.util.ObjectUtils;
import com.intellij.util.concurrency.annotations.RequiresReadLock;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -83,8 +86,10 @@ public class CopyPasteDelegator implements CopyPasteSupport {
@Override
public void performCopy(@NotNull DataContext dataContext) {
PsiElement[] elements = validate(getSelectedElements(dataContext));
PsiCopyPasteManager.getInstance().setElements(elements, true);
ReadAction.run(() -> {
PsiElement[] elements = validate(getSelectedElements(dataContext));
PsiCopyPasteManager.getInstance().setElements(elements, true);
});
updateView();
}
@@ -102,13 +107,15 @@ public class CopyPasteDelegator implements CopyPasteSupport {
@Override
public void performCut(@NotNull DataContext dataContext) {
PsiElement[] elements = validate(getSelectedElements(dataContext));
if (MoveHandler.adjustForMove(myProject, elements, null) == null) {
return;
}
// 'elements' passed instead of result of 'adjustForMove' because otherwise ProjectView would
// not recognize adjusted elements when graying them
PsiCopyPasteManager.getInstance().setElements(elements, false);
ReadAction.run(() -> {
PsiElement[] elements = validate(getSelectedElements(dataContext));
if (MoveHandler.adjustForMove(myProject, elements, null) == null) {
return;
}
// 'elements' passed instead of result of 'adjustForMove' because otherwise ProjectView would
// not recognize adjusted elements when graying them
PsiCopyPasteManager.getInstance().setElements(elements, false);
});
updateView();
}
@@ -125,18 +132,21 @@ public class CopyPasteDelegator implements CopyPasteSupport {
@Override
public void performPaste(@NotNull DataContext dataContext) {
if (!performDefaultPaste(dataContext)) {
for(PasteProvider provider: EP_NAME.getExtensionList()) {
if (provider.isPasteEnabled(dataContext)) {
provider.performPaste(dataContext);
break;
WriteIntentReadAction.run((Runnable)() -> {
if (!performDefaultPaste(dataContext)) {
for(PasteProvider provider: EP_NAME.getExtensionList()) {
if (provider.isPasteEnabled(dataContext)) {
provider.performPaste(dataContext);
break;
}
}
}
}
});
}
@VisibleForTesting
@ApiStatus.Internal
@RequiresReadLock
public boolean performDefaultPaste(@NotNull DataContext dataContext) {
final boolean[] isCopied = new boolean[1];
final PsiElement[] elements = PsiCopyPasteManager.getInstance().getElements(isCopied);
@@ -16,6 +16,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.concurrency.annotations.RequiresReadLock;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -83,6 +84,7 @@ public final class PsiCopyPasteManager {
return myRecentData.getElements();
}
@RequiresReadLock
public static @NotNull Transferable newTransferable(PsiElement @NotNull ... element) {
return new MyTransferable(element);
}
@@ -108,6 +110,7 @@ public final class PsiCopyPasteManager {
myCopyPasteManager.setContents(new StringSelection(""));
}
@RequiresReadLock
public void setElements(PsiElement @NotNull [] elements, boolean copied) {
myRecentData = new MyData(elements, copied);
myCopyPasteManager.setContents(new MyTransferable(myRecentData));
@@ -217,6 +220,7 @@ public final class PsiCopyPasteManager {
myDataProxy = data;
}
@RequiresReadLock
public MyTransferable(PsiElement @NotNull [] selectedValues) {
this(new PsiCopyPasteManager.MyData(selectedValues, true));
}
@@ -277,6 +281,7 @@ public final class PsiCopyPasteManager {
}
}
@RequiresReadLock
public static @Nullable List<File> asFileList(PsiElement[] elements) {
List<File> result = new ArrayList<>();
for (PsiElement element : elements) {
@@ -288,6 +293,7 @@ public final class PsiCopyPasteManager {
return result.isEmpty() ? null : result;
}
@RequiresReadLock
public static @Nullable VirtualFile asVirtualFile(@Nullable PsiElement element) {
PsiFileSystemItem psiFile = null;
if (element instanceof PsiFileSystemItem) {
@@ -20,6 +20,7 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.PlatformEditorBundle;
import com.intellij.openapi.fileEditor.PsiElementNavigatable;
@@ -281,7 +282,7 @@ public abstract class HierarchyBrowserBaseEx extends HierarchyBrowserBase implem
@Override
public List<File> asFileList() {
return PsiCopyPasteManager.asFileList(getPsiElements());
return ReadAction.compute(() -> PsiCopyPasteManager.asFileList(getPsiElements()));
}
});
}
@@ -971,7 +971,7 @@ public abstract class AbstractProjectViewPane implements UiCompatibleDataProvide
return new DnDDragStartBean(new TransferableWrapper() {
@Override
public List<File> asFileList() {
return PsiCopyPasteManager.asFileList(psiElements);
return ReadAction.compute(() -> PsiCopyPasteManager.asFileList(psiElements));
}
@Override