mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[threading] IJPL-184084: Do not require read lock for interacting with editor models
GitOrigin-RevId: e90351dd339f4436a66debf6c84060a3b568fbb2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
009d4c4ded
commit
d91afee6e2
@@ -172,6 +172,11 @@ f:com.intellij.openapi.editor.impl.EditorImpl
|
||||
- java.awt.event.FocusListener
|
||||
- *:addHighlightingPredicate(com.intellij.openapi.util.Key,com.intellij.openapi.editor.impl.EditorHighlightingPredicate):com.intellij.openapi.editor.impl.EditorHighlightingPredicate
|
||||
- *:removeHighlightingPredicate(com.intellij.openapi.util.Key):com.intellij.openapi.editor.impl.EditorHighlightingPredicate
|
||||
*f:com.intellij.openapi.editor.impl.EditorThreading
|
||||
- sf:INSTANCE:com.intellij.openapi.editor.impl.EditorThreading
|
||||
- sf:assertInteractionAllowed():V
|
||||
- sf:compute(com.intellij.openapi.util.ThrowableComputable):java.lang.Object
|
||||
- sf:run(java.lang.Runnable):V
|
||||
*f:com.intellij.openapi.editor.impl.event.EditorGutterHoverEvent
|
||||
- java.util.EventObject
|
||||
- <init>(com.intellij.openapi.editor.ex.EditorGutterComponentEx,com.intellij.openapi.editor.markup.GutterIconRenderer):V
|
||||
|
||||
@@ -658,7 +658,7 @@ public final class CaretImpl extends UserDataHolderBase implements Caret, Dumpab
|
||||
@Override
|
||||
public int getOffset() {
|
||||
assertNotUpdating();
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
while (true) {
|
||||
PositionMarker marker = myPositionMarker;
|
||||
if (marker == null) return 0; // caret was disposed
|
||||
@@ -910,7 +910,7 @@ public final class CaretImpl extends UserDataHolderBase implements Caret, Dumpab
|
||||
|
||||
@Override
|
||||
public int getSelectionStart() {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
if (hasSelection()) {
|
||||
RangeMarker marker = mySelectionMarker;
|
||||
if (marker != null) {
|
||||
@@ -963,7 +963,7 @@ public final class CaretImpl extends UserDataHolderBase implements Caret, Dumpab
|
||||
|
||||
@Override
|
||||
public int getSelectionEnd() {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
if (hasSelection()) {
|
||||
RangeMarker marker = mySelectionMarker;
|
||||
if (marker != null) {
|
||||
@@ -1016,7 +1016,7 @@ public final class CaretImpl extends UserDataHolderBase implements Caret, Dumpab
|
||||
|
||||
@Override
|
||||
public boolean hasSelection() {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
SelectionMarker marker = mySelectionMarker;
|
||||
return hasSelection(marker);
|
||||
}
|
||||
@@ -1028,7 +1028,7 @@ public final class CaretImpl extends UserDataHolderBase implements Caret, Dumpab
|
||||
|
||||
@Override
|
||||
public @NotNull TextRange getSelectionRange() {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
SelectionMarker marker = mySelectionMarker;
|
||||
if (hasSelection(marker)) {
|
||||
return marker.getTextRange();
|
||||
@@ -1207,7 +1207,7 @@ public final class CaretImpl extends UserDataHolderBase implements Caret, Dumpab
|
||||
|
||||
@Override
|
||||
public int getLeadSelectionOffset() {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
int caretOffset = getOffset();
|
||||
if (hasSelection()) {
|
||||
RangeMarker marker = mySelectionMarker;
|
||||
@@ -1335,7 +1335,7 @@ public final class CaretImpl extends UserDataHolderBase implements Caret, Dumpab
|
||||
}
|
||||
|
||||
boolean hasVirtualSelection() {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
SelectionMarker marker = mySelectionMarker;
|
||||
return marker != null && marker.isValid() && isVirtualSelectionEnabled() && marker.hasVirtualSelection();
|
||||
}
|
||||
|
||||
+10
-11
@@ -317,7 +317,7 @@ public final class EditorComponentImpl extends JTextComponent implements Scrolla
|
||||
@DirtyUI
|
||||
@Override
|
||||
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
if (orientation == SwingConstants.VERTICAL) {
|
||||
return editor.getLineHeight();
|
||||
}
|
||||
@@ -445,7 +445,7 @@ public final class EditorComponentImpl extends JTextComponent implements Scrolla
|
||||
final @NotNull var primaryCaret = caretModel.getPrimaryCaret();
|
||||
|
||||
myLastKnownPrimaryCaret = new WeakReference<>(primaryCaret);
|
||||
ReadAction.run(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
myPrimaryCaretLastKnownDot = primaryCaret.getOffset();
|
||||
myPrimaryCaretLastKnownMark = primaryCaret.getLeadSelectionOffset();
|
||||
});
|
||||
@@ -670,13 +670,13 @@ public final class EditorComponentImpl extends JTextComponent implements Scrolla
|
||||
|
||||
@Override
|
||||
public int getCaretPosition() {
|
||||
return ReadAction.compute(() -> editor.getCaretModel().getOffset());
|
||||
return EditorThreading.compute(() -> editor.getCaretModel().getOffset());
|
||||
}
|
||||
|
||||
@DirtyUI
|
||||
@Override
|
||||
public void updateUI() {
|
||||
ReadAction.run(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
// Don't use the default TextUI, BaseTextUI, which does a lot of unnecessary
|
||||
// work. We do however need to provide a TextUI implementation since some
|
||||
// screen reader support code will invoke it
|
||||
@@ -804,8 +804,7 @@ public final class EditorComponentImpl extends JTextComponent implements Scrolla
|
||||
|
||||
@Override
|
||||
public String getText(final int offset, final int length) {
|
||||
return ReadAction
|
||||
.compute(() -> editor.getDocument().getText(new TextRange(offset, offset + length)));
|
||||
return EditorThreading.compute(() -> editor.getDocument().getText(new TextRange(offset, offset + length)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1293,7 +1292,7 @@ public final class EditorComponentImpl extends JTextComponent implements Scrolla
|
||||
|
||||
@Override
|
||||
public int getCaretPosition() {
|
||||
return ReadAction.compute(() -> editor.getCaretModel().getOffset());
|
||||
return EditorThreading.compute(() -> editor.getCaretModel().getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1330,17 +1329,17 @@ public final class EditorComponentImpl extends JTextComponent implements Scrolla
|
||||
|
||||
@Override
|
||||
public int getSelectionStart() {
|
||||
return ReadAction.compute(() -> editor.getSelectionModel().getSelectionStart());
|
||||
return EditorThreading.compute(() -> editor.getSelectionModel().getSelectionStart());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectionEnd() {
|
||||
return ReadAction.compute(() -> editor.getSelectionModel().getSelectionEnd());
|
||||
return EditorThreading.compute(() -> editor.getSelectionModel().getSelectionEnd());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getSelectedText() {
|
||||
return ReadAction.compute(() -> editor.getSelectionModel().getSelectedText());
|
||||
return EditorThreading.compute(() -> editor.getSelectionModel().getSelectedText());
|
||||
}
|
||||
|
||||
// ---- Implements AccessibleEditableText ----
|
||||
@@ -1392,7 +1391,7 @@ public final class EditorComponentImpl extends JTextComponent implements Scrolla
|
||||
|
||||
@Override
|
||||
public void selectText(int startIndex, int endIndex) {
|
||||
ReadAction.run(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
editor.getSelectionModel().setSelection(startIndex, endIndex);
|
||||
editor.getCaretModel().moveToOffset(endIndex);
|
||||
});
|
||||
|
||||
+1
-1
@@ -383,7 +383,7 @@ final class EditorGutterComponentImpl extends EditorGutterComponentEx
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g_) {
|
||||
ReadAction.run(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
Rectangle clip = g_.getClipBounds();
|
||||
if (clip == null || clip.isEmpty()) {
|
||||
return;
|
||||
|
||||
@@ -1605,22 +1605,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public @NotNull VisualPosition offsetToVisualPosition(int offset, boolean leanForward, boolean beforeSoftWrap) {
|
||||
return ReadAction.compute(() -> myView.offsetToVisualPosition(offset, leanForward, beforeSoftWrap));
|
||||
return EditorThreading.compute(() -> myView.offsetToVisualPosition(offset, leanForward, beforeSoftWrap));
|
||||
}
|
||||
|
||||
public int offsetToVisualColumnInFoldRegion(@NotNull FoldRegion region, int offset, boolean leanTowardsLargerOffsets) {
|
||||
assertIsDispatchThread();
|
||||
return ReadAction.compute(() -> myView.offsetToVisualColumnInFoldRegion(region, offset, leanTowardsLargerOffsets));
|
||||
return EditorThreading.compute(() -> myView.offsetToVisualColumnInFoldRegion(region, offset, leanTowardsLargerOffsets));
|
||||
}
|
||||
|
||||
public int visualColumnToOffsetInFoldRegion(@NotNull FoldRegion region, int visualColumn, boolean leansRight) {
|
||||
assertIsDispatchThread();
|
||||
return ReadAction.compute(() -> myView.visualColumnToOffsetInFoldRegion(region, visualColumn, leansRight));
|
||||
return EditorThreading.compute(() -> myView.visualColumnToOffsetInFoldRegion(region, visualColumn, leansRight));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
return ReadAction.compute(() -> myView.offsetToLogicalPosition(offset));
|
||||
return EditorThreading.compute(() -> myView.offsetToLogicalPosition(offset));
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@@ -1667,7 +1667,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public @NotNull Point logicalPositionToXY(@NotNull LogicalPosition pos) {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
VisualPosition visible = logicalToVisualPosition(pos);
|
||||
return visualPositionToXY(visible);
|
||||
});
|
||||
@@ -1675,7 +1675,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public @NotNull Point visualPositionToXY(@NotNull VisualPosition visible) {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
Point2D point2D = myView.visualPositionToXY(visible);
|
||||
return new Point((int)point2D.getX(), (int)point2D.getY());
|
||||
});
|
||||
@@ -1683,7 +1683,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public @NotNull Point2D visualPositionToPoint2D(@NotNull VisualPosition visible) {
|
||||
return ReadAction.compute(() -> myView.visualPositionToXY(visible));
|
||||
return EditorThreading.compute(() -> myView.visualPositionToXY(visible));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1701,12 +1701,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
public int findNearestDirectionBoundary(int offset, boolean lookForward) {
|
||||
return ReadAction.compute(() -> myView.findNearestDirectionBoundary(offset, lookForward));
|
||||
return EditorThreading.compute(() -> myView.findNearestDirectionBoundary(offset, lookForward));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int visualLineToY(int line) {
|
||||
return ReadAction.compute(() -> myView.visualLineToY(line));
|
||||
return EditorThreading.compute(() -> myView.visualLineToY(line));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1733,7 +1733,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return;
|
||||
}
|
||||
assertIsDispatchThread();
|
||||
ReadAction.run(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
int minEndOffset = Math.min(endOffset, getEditorModel().getDocument().getTextLength());
|
||||
|
||||
if (invalidateTextLayout) {
|
||||
@@ -2200,7 +2200,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
if (myUpdateCursor && !myPurePaintingMode) {
|
||||
setCursorPosition();
|
||||
myUpdateCursor = false;
|
||||
@@ -2266,7 +2266,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public boolean hasHeaderComponent() {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
JComponent header = getHeaderComponent();
|
||||
return header != null && header != getPermanentHeaderComponent();
|
||||
});
|
||||
@@ -2274,7 +2274,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public @Nullable JComponent getPermanentHeaderComponent() {
|
||||
return ReadAction.compute(() -> getUserData(PERMANENT_HEADER));
|
||||
return EditorThreading.compute(() -> getUserData(PERMANENT_HEADER));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2307,7 +2307,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public @NotNull Color getBackgroundColor() {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
Color forcedBackground = myState.getMyForcedBackground();
|
||||
return forcedBackground == null ? getBackgroundIgnoreForced() : forcedBackground;
|
||||
});
|
||||
@@ -2376,7 +2376,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public int getMaxWidthInRange(int startOffset, int endOffset) {
|
||||
return ReadAction.compute(() -> myView.getMaxWidthInRange(startOffset, endOffset));
|
||||
return EditorThreading.compute(() -> myView.getMaxWidthInRange(startOffset, endOffset));
|
||||
}
|
||||
|
||||
public boolean isPaintSelection() {
|
||||
@@ -2414,7 +2414,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public int getLineHeight() {
|
||||
return ReadAction.compute(() -> myView.getLineHeight());
|
||||
return EditorThreading.compute(() -> myView.getLineHeight());
|
||||
}
|
||||
|
||||
public int getDescent() {
|
||||
@@ -2452,7 +2452,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
public @NotNull Dimension getPreferredSize() {
|
||||
return ReadAction.compute(() -> isReleased ? new Dimension()
|
||||
return EditorThreading.compute(() -> isReleased ? new Dimension()
|
||||
: Registry.is("idea.true.smooth.scrolling.dynamic.scrollbars")
|
||||
? new Dimension(getPreferredWidthOfVisibleLines(), myView.getPreferredHeight())
|
||||
: myView.getPreferredSize());
|
||||
@@ -2508,7 +2508,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public @NotNull Dimension getContentSize() {
|
||||
return ReadAction.compute(() -> isReleased ? new Dimension() : myView.getPreferredSize());
|
||||
return EditorThreading.compute(() -> isReleased ? new Dimension() : myView.getPreferredSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2528,12 +2528,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public Insets getInsets() {
|
||||
return ReadAction.compute(() -> myScrollPane.getInsets());
|
||||
return EditorThreading.compute(() -> myScrollPane.getInsets());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int logicalPositionToOffset(@NotNull LogicalPosition pos) {
|
||||
return ReadAction.compute(() -> myView.logicalPositionToOffset(pos));
|
||||
return EditorThreading.compute(() -> myView.logicalPositionToOffset(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2544,17 +2544,17 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
* minus the number of folded lines.
|
||||
*/
|
||||
public int getVisibleLineCount() {
|
||||
return ReadAction.compute(() -> myView.getVisibleLineCount());
|
||||
return EditorThreading.compute(() -> myView.getVisibleLineCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logicalPos) {
|
||||
return ReadAction.compute(() -> myView.logicalToVisualPosition(logicalPos, false));
|
||||
return EditorThreading.compute(() -> myView.logicalToVisualPosition(logicalPos, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visiblePos) {
|
||||
return ReadAction.compute(() -> myView.visualToLogicalPosition(visiblePos));
|
||||
return EditorThreading.compute(() -> myView.visualToLogicalPosition(visiblePos));
|
||||
}
|
||||
|
||||
private int offsetToLogicalLine(int offset) {
|
||||
@@ -2678,7 +2678,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public EditorMouseEventArea getMouseEventArea(@NotNull MouseEvent e) {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
if (myGutterComponent != e.getSource()) return EditorMouseEventArea.EDITING_AREA;
|
||||
|
||||
int x = myGutterComponent.convertX(e.getX());
|
||||
@@ -3136,7 +3136,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public boolean setCaretVisible(boolean b) {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
boolean old = myCaretCursor.isActive();
|
||||
if (b) {
|
||||
myCaretCursor.activate();
|
||||
@@ -3150,7 +3150,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
|
||||
@Override
|
||||
public boolean setCaretEnabled(boolean enabled) {
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
boolean old = myCaretCursor.isEnabled();
|
||||
myCaretCursor.setEnabled(enabled);
|
||||
return old;
|
||||
@@ -3185,7 +3185,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
@Override
|
||||
public void setEmbeddedIntoDialogWrapper(boolean b) {
|
||||
assertIsDispatchThread();
|
||||
ReadAction.run(() -> myState.setEmbeddedIntoDialogWrapper(b));
|
||||
EditorThreading.run(() -> myState.setEmbeddedIntoDialogWrapper(b));
|
||||
}
|
||||
|
||||
private void isEmbeddedIntoDialogWrapperChanged(ObservableStateListener.PropertyChangeEvent event) {
|
||||
@@ -3561,7 +3561,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
private @NotNull MyEditable getViewer() {
|
||||
MyEditable editable = myEditable;
|
||||
if (editable == null) {
|
||||
myEditable = editable = ReadAction.compute(() -> new MyEditable());
|
||||
myEditable = editable = EditorThreading.compute(() -> new MyEditable());
|
||||
}
|
||||
return editable;
|
||||
}
|
||||
@@ -3694,7 +3694,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
public void setColorsScheme(final @NotNull EditorColorsScheme scheme) {
|
||||
assertIsDispatchThread();
|
||||
final EditorImpl finalEditor = this;
|
||||
ReadAction.run(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
final EditorColorsManager colorsManager = ApplicationManager.getApplication().getServiceIfCreated(EditorColorsManager.class);
|
||||
if (colorsManager == null) {
|
||||
LOG.info("Skipping attempt to set color scheme without EditorColorsManager");
|
||||
@@ -3721,7 +3721,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
@Override
|
||||
public void setVerticalScrollbarOrientation(int type) {
|
||||
assertIsDispatchThread();
|
||||
ReadAction.run(() -> myState.setVerticalScrollBarOrientation(type));
|
||||
EditorThreading.run(() -> myState.setVerticalScrollBarOrientation(type));
|
||||
}
|
||||
|
||||
private void verticalScrollBarOrientationChanged(ObservableStateListener.PropertyChangeEvent event) {
|
||||
@@ -4035,7 +4035,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
|
||||
}
|
||||
|
||||
private static <T> T execute(@NotNull ThrowableComputable<T, RuntimeException> computable) {
|
||||
return UIUtil.invokeAndWaitIfNeeded(() -> ReadAction.compute(computable));
|
||||
return UIUtil.invokeAndWaitIfNeeded(() -> EditorThreading.compute(computable));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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.openapi.editor.impl
|
||||
|
||||
import com.intellij.openapi.util.ThrowableComputable
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.util.application
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import com.intellij.util.ui.EDT
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* Historically, Editor models required read lock for reading them.
|
||||
* We are planning to relax the contracts and allow interacting with Editor from EDT without any locks.
|
||||
* Hence, editor models should use this class to ensure consistency.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
object EditorThreading {
|
||||
|
||||
/**
|
||||
* Checks that interaction with the editor is allowed in the current context
|
||||
*/
|
||||
@JvmStatic
|
||||
fun assertInteractionAllowed() {
|
||||
if (Registry.`is`("editor.allow.raw.access.on.edt")) {
|
||||
if (!EDT.isCurrentThreadEdt() && !application.isReadAccessAllowed) {
|
||||
throw IllegalStateException("Access to Editor models (caret, selection, etc.) is allowed either from EDT, or under read action. Current thread: ${Thread.currentThread()}")
|
||||
}
|
||||
}
|
||||
else {
|
||||
ThreadingAssertions.assertReadAccess()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the context for editor accessing operation [action].
|
||||
* [action] is always executed in place.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T, E : Throwable> compute(action: ThrowableComputable<T, E>): T {
|
||||
return if (Registry.`is`("editor.allow.raw.access.on.edt")) {
|
||||
if (EDT.isCurrentThreadEdt()) {
|
||||
action.compute()
|
||||
}
|
||||
else {
|
||||
application.runReadAction(action)
|
||||
}
|
||||
}
|
||||
else {
|
||||
application.runReadAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the context for editor accessing operation [action].
|
||||
* [action] is always executed in place.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T, E : Throwable> run(action: Runnable) {
|
||||
return if (Registry.`is`("editor.allow.raw.access.on.edt")) {
|
||||
if (EDT.isCurrentThreadEdt()) {
|
||||
action.run()
|
||||
}
|
||||
else {
|
||||
application.runReadAction(action)
|
||||
}
|
||||
}
|
||||
else {
|
||||
application.runReadAction(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ package com.intellij.openapi.editor.impl.view;
|
||||
import com.intellij.diagnostic.Dumpable;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
@@ -19,7 +18,6 @@ import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.concurrency.ThreadingAssertions;
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt;
|
||||
import org.intellij.lang.annotations.JdkConstants;
|
||||
import org.jetbrains.annotations.*;
|
||||
@@ -126,12 +124,12 @@ public final class EditorView implements TextDrawingCallback, Disposable, Dumpab
|
||||
}
|
||||
|
||||
public @NotNull LogicalPosition offsetToLogicalPosition(int offset) {
|
||||
assertReadAccess();
|
||||
assertEditorAccessible();
|
||||
return myMapper.offsetToLogicalPosition(offset);
|
||||
}
|
||||
|
||||
public int logicalPositionToOffset(@NotNull LogicalPosition pos) {
|
||||
assertReadAccess();
|
||||
assertEditorAccessible();
|
||||
return myMapper.logicalPositionToOffset(pos);
|
||||
}
|
||||
|
||||
@@ -232,7 +230,7 @@ public final class EditorView implements TextDrawingCallback, Disposable, Dumpab
|
||||
@RequiresEdt
|
||||
public @NotNull Dimension getPreferredSize() {
|
||||
assert !myEditor.isPurePaintingMode();
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
getSoftWrapModel().prepareToMapping();
|
||||
return mySizeManager.getPreferredSize();
|
||||
});
|
||||
@@ -250,7 +248,7 @@ public final class EditorView implements TextDrawingCallback, Disposable, Dumpab
|
||||
@RequiresEdt
|
||||
public int getPreferredWidth(int beginLine, int endLine) {
|
||||
assert !myEditor.isPurePaintingMode();
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
getSoftWrapModel().prepareToMapping();
|
||||
return mySizeManager.getPreferredWidth(beginLine, endLine);
|
||||
});
|
||||
@@ -259,7 +257,7 @@ public final class EditorView implements TextDrawingCallback, Disposable, Dumpab
|
||||
@RequiresEdt
|
||||
public int getPreferredHeight() {
|
||||
assert !myEditor.isPurePaintingMode();
|
||||
return ReadAction.compute(() -> {
|
||||
return EditorThreading.compute(() -> {
|
||||
getSoftWrapModel().prepareToMapping();
|
||||
return mySizeManager.getPreferredHeight();
|
||||
});
|
||||
@@ -694,7 +692,7 @@ public final class EditorView implements TextDrawingCallback, Disposable, Dumpab
|
||||
}
|
||||
|
||||
private void invalidateFoldRegionLayouts() {
|
||||
ReadAction.run(() -> {
|
||||
EditorThreading.run(() -> {
|
||||
for (FoldRegion region : getFoldingModel().getAllFoldRegions()) {
|
||||
invalidateFoldRegionLayout(region);
|
||||
}
|
||||
@@ -802,9 +800,9 @@ public final class EditorView implements TextDrawingCallback, Disposable, Dumpab
|
||||
}
|
||||
}
|
||||
|
||||
private void assertReadAccess() {
|
||||
private void assertEditorAccessible() {
|
||||
if (!myEditorModel.isAd()) {
|
||||
ThreadingAssertions.assertReadAccess();
|
||||
EditorThreading.assertInteractionAllowed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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.openapi.editor
|
||||
|
||||
import com.intellij.openapi.application.UiDispatcherKind
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.application.ui
|
||||
import com.intellij.openapi.editor.impl.EditorThreading
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.junit5.RegistryKey
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import org.junit.jupiter.api.DynamicTest
|
||||
import org.junit.jupiter.api.TestFactory
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
|
||||
@TestApplication
|
||||
class EditorThreadingTest {
|
||||
|
||||
private companion object {
|
||||
val project = projectFixture()
|
||||
val module = project.moduleFixture()
|
||||
val sourceRoot = module.sourceRootFixture()
|
||||
val file = sourceRoot.psiFileFixture("file.txt", "abcde")
|
||||
}
|
||||
|
||||
private val editor = file.editorFixture()
|
||||
|
||||
|
||||
val actions = listOf(
|
||||
{ editor.get().caretModel.offset } to "caret",
|
||||
{ EditorThreading.assertInteractionAllowed() } to "raw threading",
|
||||
{ editor.get().selectionModel.selectedText } to "selection"
|
||||
)
|
||||
|
||||
fun runTest(processor: (() -> Unit) -> Unit): List<DynamicTest> {
|
||||
return actions.map {
|
||||
DynamicTest.dynamicTest(it.second) {
|
||||
processor { it.first() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun withRawAccess(kind: Boolean, action: () -> Unit) {
|
||||
val registryValue = Registry.get("editor.allow.raw.access.on.edt")
|
||||
val current = registryValue.asBoolean()
|
||||
registryValue.setValue(kind)
|
||||
try {
|
||||
action
|
||||
}
|
||||
finally {
|
||||
registryValue.setValue(current)
|
||||
}
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `access to editor is not allowed on BGT`() = runTest { action ->
|
||||
withRawAccess(false) {
|
||||
timeoutRunBlocking(context = Dispatchers.Default) {
|
||||
assertThrows<Exception> {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `access to editor is not allowed on raw EDT`() = runTest { action ->
|
||||
withRawAccess(true) {
|
||||
timeoutRunBlocking(context = Dispatchers.ui(UiDispatcherKind.RELAX)) {
|
||||
assertThrows<Exception> {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `access to editor is allowed under read lock`() = runTest { action ->
|
||||
withRawAccess(false) {
|
||||
timeoutRunBlocking(context = Dispatchers.ui(UiDispatcherKind.RELAX)) {
|
||||
readAction {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `access to editor is allowed on raw EDT with flag`() = runTest { action ->
|
||||
withRawAccess(true) {
|
||||
timeoutRunBlocking(context = Dispatchers.ui(UiDispatcherKind.RELAX)) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
@RegistryKey("editor.allow.raw.access.on.edt", "true")
|
||||
fun `access to editor is allowed under read lock with flag`() = runTest { action ->
|
||||
withRawAccess(true) {
|
||||
timeoutRunBlocking(context = Dispatchers.Default) {
|
||||
readAction {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,6 +420,9 @@ editor.mouseSelectionStateResetDeadZone=4
|
||||
editor.distraction.free.mode=false
|
||||
editor.paint.empty.text=true
|
||||
|
||||
editor.allow.raw.access.on.edt=false
|
||||
editor.allow.raw.access.on.edt.description=Enables the possibility to interact with the Editor models (Caret, Selection, etc.) without read lock on EDT
|
||||
|
||||
editor.focus.mode.color.light=c0c0c0
|
||||
editor.focus.mode.color.light.description=Light color for focus mode
|
||||
editor.focus.mode.color.dark=404040
|
||||
|
||||
Reference in New Issue
Block a user