mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
EA-326396 diff: do not start a new command inside document listener
UndoManager fails if command is started with Document bulk mode enabled. This might have happened if "Original" document is "forUseInNonAWTThread == true". For example: for "Compare with Clipboard" action on ConsoleViewImpl and later called "Clear All" action. Copy constraints flags from the original document to the fragment document, and do start our own command. GitOrigin-RevId: d6b317f024178670a2243c34899dbf34d7d5fb90
This commit is contained in:
committed by
intellij-monorepo-bot
parent
14b4b6fe01
commit
f108439628
@@ -5,6 +5,8 @@ import com.intellij.diff.contents.DiffContentBase;
|
||||
import com.intellij.diff.contents.DocumentContent;
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx;
|
||||
import com.intellij.diff.util.LineCol;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
@@ -14,6 +16,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -142,18 +145,38 @@ public final class DocumentFragmentContent extends DiffContentBase implements Do
|
||||
|
||||
@Override
|
||||
public void startListen() {
|
||||
if (myRangeMarker.isValid()) {
|
||||
myDocument2.setReadOnly(false);
|
||||
CharSequence nexText = myDocument1.getCharsSequence().subSequence(myRangeMarker.getStartOffset(), myRangeMarker.getEndOffset());
|
||||
replaceString(myDocument2, 0, myDocument2.getTextLength(), nexText);
|
||||
myDocument2.setReadOnly(!myDocument1.isWritable());
|
||||
}
|
||||
else {
|
||||
myDocument2.setReadOnly(false);
|
||||
replaceString(myDocument2, 0, myDocument2.getTextLength(), DiffBundle.message("synchronize.document.and.its.fragment.range.error"));
|
||||
myDocument2.setReadOnly(true);
|
||||
}
|
||||
// no need to set myDuringModification - listeners are not added yet
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(() -> {
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
if (myRangeMarker.isValid()) {
|
||||
myDocument2.setReadOnly(false);
|
||||
CharSequence nexText = myRangeMarker.getTextRange().subSequence(myDocument1.getCharsSequence());
|
||||
myDocument2.setText(nexText);
|
||||
myDocument2.setReadOnly(!myDocument1.isWritable());
|
||||
}
|
||||
else {
|
||||
myDocument2.setReadOnly(false);
|
||||
myDocument2.setText(DiffBundle.message("synchronize.document.and.its.fragment.range.error"));
|
||||
myDocument2.setReadOnly(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
super.startListen();
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
private void replaceString(@NotNull Document document,
|
||||
int startOffset,
|
||||
int endOffset,
|
||||
@NotNull CharSequence newText) {
|
||||
try {
|
||||
myDuringModification = true;
|
||||
document.replaceString(startOffset, endOffset, newText);
|
||||
}
|
||||
finally {
|
||||
myDuringModification = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.diff.actions;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.diff.DiffBundle;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.editor.impl.EditorFactoryImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt;
|
||||
@@ -20,7 +18,7 @@ import java.beans.PropertyChangeListener;
|
||||
public abstract class DocumentsSynchronizer {
|
||||
@NotNull protected final Document myDocument1;
|
||||
@NotNull protected final Document myDocument2;
|
||||
@Nullable private final Project myProject;
|
||||
@Nullable protected final Project myProject;
|
||||
|
||||
protected boolean myDuringModification = false;
|
||||
|
||||
@@ -64,25 +62,6 @@ public abstract class DocumentsSynchronizer {
|
||||
|
||||
protected abstract void onDocumentChanged2(@NotNull DocumentEvent event);
|
||||
|
||||
@RequiresEdt
|
||||
protected void replaceString(@NotNull final Document document,
|
||||
final int startOffset,
|
||||
final int endOffset,
|
||||
@NotNull final CharSequence newText) {
|
||||
try {
|
||||
myDuringModification = true;
|
||||
CommandProcessor.getInstance().executeCommand(
|
||||
myProject,
|
||||
() -> ApplicationManager.getApplication().runWriteAction(() -> document.replaceString(startOffset, endOffset, newText)),
|
||||
DiffBundle.message("synchronize.document.and.its.fragment"),
|
||||
document
|
||||
);
|
||||
}
|
||||
finally {
|
||||
myDuringModification = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void startListen() {
|
||||
myDocument1.addDocumentListener(myListener1);
|
||||
myDocument2.addDocumentListener(myListener2);
|
||||
@@ -97,7 +76,9 @@ public abstract class DocumentsSynchronizer {
|
||||
|
||||
public static @NotNull Document createFakeDocument(@NotNull Document original) {
|
||||
EditorFactoryImpl editorFactory = (EditorFactoryImpl)EditorFactory.getInstance();
|
||||
Document document = editorFactory.createDocument("", true, false);
|
||||
boolean acceptsSlashR = original instanceof DocumentImpl && ((DocumentImpl)original).acceptsSlashR();
|
||||
boolean writeThreadOnly = original instanceof DocumentImpl && ((DocumentImpl)original).isWriteThreadOnly();
|
||||
Document document = editorFactory.createDocument("", acceptsSlashR, !writeThreadOnly);
|
||||
document.putUserData(UndoManager.ORIGINAL_DOCUMENT, original);
|
||||
return document;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,14 @@ import com.intellij.diff.actions.DocumentsSynchronizer
|
||||
import com.intellij.diff.contents.DiffContentBase
|
||||
import com.intellij.diff.contents.DocumentContent
|
||||
import com.intellij.diff.util.DiffUserDataKeysEx
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.command.undo.UndoManager
|
||||
import com.intellij.openapi.diff.DiffBundle
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.impl.EditorFactoryImpl
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.fileTypes.FileTypes
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -16,6 +22,7 @@ import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import com.intellij.refactoring.suggested.startOffset
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import java.util.function.IntUnaryOperator
|
||||
|
||||
class TestDiffContent(
|
||||
@@ -28,12 +35,14 @@ class TestDiffContent(
|
||||
|
||||
override fun getContentType(): FileType = FileTypes.PLAIN_TEXT
|
||||
|
||||
private val fakeDocument = DocumentsSynchronizer.createFakeDocument(original.document)
|
||||
private val fakeDocument = (EditorFactory.getInstance() as EditorFactoryImpl).createDocument("", true, false).apply {
|
||||
putUserData(UndoManager.ORIGINAL_DOCUMENT, original.document)
|
||||
}
|
||||
private val synchronizer: DocumentsSynchronizer = object : DocumentsSynchronizer(project, original.document, fakeDocument) {
|
||||
override fun onDocumentChanged1(event: DocumentEvent) {
|
||||
PsiDocumentManager.getInstance(project).performForCommittedDocument(document1, Runnable {
|
||||
val element = elemPtr.element ?: return@Runnable
|
||||
replaceString(myDocument2, 0, myDocument2.textLength, ElementManipulators.getValueText(element))
|
||||
replaceString(myDocument2, ElementManipulators.getValueText(element))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,9 +59,30 @@ class TestDiffContent(
|
||||
}
|
||||
|
||||
override fun startListen() {
|
||||
replaceString(myDocument2, 0, myDocument2.textLength, text)
|
||||
replaceString(myDocument2, text)
|
||||
super.startListen()
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
private fun replaceString(document: Document, newText: CharSequence) {
|
||||
try {
|
||||
myDuringModification = true
|
||||
CommandProcessor.getInstance().executeCommand(
|
||||
myProject,
|
||||
{
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
document.replaceString(0, document.textLength, newText)
|
||||
}
|
||||
},
|
||||
DiffBundle.message("synchronize.document.and.its.fragment"),
|
||||
document
|
||||
)
|
||||
}
|
||||
finally {
|
||||
myDuringModification = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private var assignments = 0
|
||||
|
||||
Reference in New Issue
Block a user