mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
1541 lines
50 KiB
Java
1541 lines
50 KiB
Java
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
|
package com.intellij.openapi.command.undo;
|
|
|
|
import com.intellij.codeInsight.CodeInsightUtilCore;
|
|
import com.intellij.codeInsight.actions.ReformatCodeProcessor;
|
|
import com.intellij.codeInsight.intention.IntentionAction;
|
|
import com.intellij.codeInsight.intention.QuickFixFactory;
|
|
import com.intellij.configurationStore.StoreUtil;
|
|
import com.intellij.mock.Mock;
|
|
import com.intellij.openapi.application.ApplicationManager;
|
|
import com.intellij.openapi.application.WriteAction;
|
|
import com.intellij.openapi.command.CommandProcessor;
|
|
import com.intellij.openapi.command.UndoConfirmationPolicy;
|
|
import com.intellij.openapi.command.WriteCommandAction;
|
|
import com.intellij.openapi.editor.*;
|
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|
import com.intellij.openapi.fileEditor.FileEditor;
|
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
|
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
|
import com.intellij.openapi.fileEditor.impl.CurrentEditorProvider;
|
|
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.ui.TestDialog;
|
|
import com.intellij.openapi.ui.TestDialogManager;
|
|
import com.intellij.openapi.util.EmptyRunnable;
|
|
import com.intellij.openapi.util.Pair;
|
|
import com.intellij.openapi.util.io.FileUtil;
|
|
import com.intellij.openapi.util.registry.Registry;
|
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
|
import com.intellij.openapi.vfs.VfsUtilCore;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.refactoring.rename.RenameProcessor;
|
|
import com.intellij.testFramework.PlatformTestUtil;
|
|
import com.intellij.testFramework.VfsTestUtil;
|
|
import kotlin.text.Charsets;
|
|
import org.jetbrains.annotations.NonNls;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.lang.ref.Reference;
|
|
import java.util.Collections;
|
|
|
|
public class GlobalUndoTest extends UndoTestCase implements TestDialog {
|
|
private TestDialog myOldTestDialogValue;
|
|
|
|
private PsiClass myClass;
|
|
private VirtualFile myDir;
|
|
private static final String myDirName = "dir1";
|
|
private VirtualFile myDir1;
|
|
private VirtualFile myDir2;
|
|
private VirtualFile myDirToMove;
|
|
private VirtualFile myDirToRename;
|
|
private static final String DIR_TO_RENAME_NAME = "dirToRename.txt";
|
|
private static final String NEW_NAME = "NewName";
|
|
private boolean myConfirmationWasRequested;
|
|
private static final String DIR_NAME = "dir.txt";
|
|
private PsiJavaFile myContainingFile;
|
|
|
|
@Override
|
|
protected void setUp() throws Exception {
|
|
super.setUp();
|
|
myOldTestDialogValue = TestDialogManager.setTestDialog(this);
|
|
}
|
|
|
|
@Override
|
|
protected void tearDown() throws Exception {
|
|
try {
|
|
TestDialogManager.setTestDialog(myOldTestDialogValue);
|
|
myContainingFile = null;
|
|
myClass = null;
|
|
}
|
|
catch (Throwable e) {
|
|
addSuppressedException(e);
|
|
}
|
|
finally {
|
|
super.tearDown();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int show(@NotNull String message) {
|
|
myConfirmationWasRequested = true;
|
|
return 0;
|
|
}
|
|
|
|
public void testCreateClassIsUndoableAction() {
|
|
createClass("Class");
|
|
globalUndo();
|
|
}
|
|
|
|
public void testUndoDeleteClass() {
|
|
String className = "TestClass";
|
|
createClass(className);
|
|
|
|
String contentBefore = getDocumentText(findFile(className, myRoot));
|
|
deleteClass();
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
globalUndo();
|
|
assertFileExists(className, contentBefore);
|
|
|
|
globalUndo();
|
|
assertFileDoesNotExist(className, myRoot);
|
|
|
|
globalRedo();
|
|
assertFileExists(className, contentBefore);
|
|
|
|
globalRedo();
|
|
assertFileDoesNotExist(className, myRoot);
|
|
}
|
|
}
|
|
|
|
public void testUndoCreateClassTwice() {
|
|
createClass("TestClass1");
|
|
createClass("TestClass2");
|
|
|
|
Editor editor = openEditor("TestClass2.java");
|
|
|
|
undo(editor);
|
|
|
|
globalUndo();
|
|
|
|
StoreUtil.saveDocumentsAndProjectsAndApp(false);
|
|
|
|
checkAllFilesDeleted();
|
|
}
|
|
|
|
public void testUndoFileCopy() throws Exception {
|
|
VirtualFile file = createFile("a.txt", "").getVirtualFile();
|
|
|
|
VirtualFile dir = file.getParent();
|
|
VirtualFile copy = WriteCommandAction.writeCommandAction(myProject).compute(() -> file.copy(this, dir, "b.txt"));
|
|
|
|
globalUndo();
|
|
|
|
assertTrue(file.isValid());
|
|
assertFalse(copy.isValid());
|
|
}
|
|
|
|
public void testUndoRenameClass() {
|
|
String firstClassName = "Class1";
|
|
String secondClassName = "Class223467234678234678236478263478";
|
|
|
|
createClass(firstClassName);
|
|
renameClassTo(secondClassName);
|
|
|
|
assertFileExists(myRoot, secondClassName);
|
|
assertFileDoesNotExist(firstClassName, myRoot);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
globalUndo();
|
|
assertFileExists(myRoot, firstClassName);
|
|
assertFileDoesNotExist(secondClassName, myRoot);
|
|
|
|
globalRedo();
|
|
assertFileExists(myRoot, secondClassName);
|
|
assertFileDoesNotExist(firstClassName, myRoot);
|
|
}
|
|
}
|
|
|
|
public void testUndoRenameWithCaseChangeClass() {
|
|
final VirtualFile f = createChildData(myRoot, "Foo.txt");
|
|
|
|
executeCommand(() -> rename(f, "FOO.txt"));
|
|
assertEquals("FOO.txt", f.getName());
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
globalUndo();
|
|
assertEquals("Foo.txt", f.getName());
|
|
|
|
globalRedo();
|
|
assertEquals("FOO.txt", f.getName());
|
|
}
|
|
}
|
|
|
|
private void renameClassTo(final String newClassName) {
|
|
executeCommand("Rename Class", () -> new RenameProcessor(myProject, myClass, newClassName, true, true).run());
|
|
}
|
|
|
|
public void testUndoAfterEmptyReformat() {
|
|
createClass("foo");
|
|
final PsiFile file = myContainingFile;
|
|
final Editor editor = openEditor("foo.java");
|
|
new ReformatCodeProcessor(myProject, file, null, false).run();
|
|
undo(editor);
|
|
assertFileDoesNotExist("foo", myRoot);
|
|
}
|
|
|
|
public void testUndoMoveFile() {
|
|
VirtualFile dir1 = createChildDirectory(myRoot, myDirName);
|
|
VirtualFile dir2 = createChildDirectory(myRoot, "dir2");
|
|
|
|
String className = "Class1";
|
|
createClass(className, dir1);
|
|
assertFileExists(dir1, className);
|
|
assertFileDoesNotExist(className, dir2);
|
|
|
|
moveClassTo(dir2);
|
|
assertFileExists(dir2, className);
|
|
assertFileDoesNotExist(className, dir1);
|
|
|
|
globalUndo();
|
|
assertFileExists(dir1, className);
|
|
assertFileDoesNotExist(className, dir2);
|
|
|
|
globalRedo();
|
|
assertFileExists(dir2, className);
|
|
assertFileDoesNotExist(className, dir1);
|
|
}
|
|
|
|
public void testRedoCreateAndMove() {
|
|
final VirtualFile dir = createChildDirectory(myRoot, "dir");
|
|
|
|
final VirtualFile[] f = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
f[0] = createChildData(myRoot, "foo.txt");
|
|
setDocumentText(f[0], "foo");
|
|
});
|
|
executeCommand(() -> {
|
|
move(f[0], dir);
|
|
setDocumentText(f[0], "foobar");
|
|
});
|
|
|
|
globalUndo();
|
|
globalUndo();
|
|
|
|
assertNull(myRoot.findChild("foo.txt"));
|
|
|
|
globalRedo();
|
|
f[0] = myRoot.findChild("foo.txt");
|
|
assertNotNull(f[0]);
|
|
assertEquals("foo", getDocumentText(f[0]));
|
|
|
|
globalRedo();
|
|
assertEquals(dir, f[0].getParent());
|
|
assertEquals("foobar", getDocumentText(f[0]));
|
|
}
|
|
|
|
public void testRestoringUndoStackForDocumentWhenItIsRecreatedWithSameName() {
|
|
final VirtualFile[] f = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
f[0] = createChildData(myRoot, "foo.txt");
|
|
setDocumentText(f[0], "foo");
|
|
});
|
|
|
|
executeCommand(() -> setDocumentText(f[0], "foofoo"));
|
|
|
|
undo(getEditor(f[0]));
|
|
undo(getEditor(f[0]));
|
|
assertNull(myRoot.findChild("foo.txt"));
|
|
|
|
globalRedo();
|
|
f[0] = myRoot.findChild("foo.txt");
|
|
assertNotNull(f[0]);
|
|
assertEquals("foo", getDocumentText(f[0]));
|
|
|
|
redo(getEditor(f[0]));
|
|
|
|
assertEquals("foofoo", getDocumentText(f[0]));
|
|
}
|
|
|
|
public void testDoNotConfuseRecreatedFilesWithDeleted() {
|
|
final VirtualFile[] f = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
f[0] = createChildData(myRoot, "foo.txt");
|
|
setDocumentText(f[0], "foo");
|
|
});
|
|
|
|
executeCommand(() -> setDocumentText(f[0], "foofoo"));
|
|
|
|
executeCommand(() -> delete(f[0]));
|
|
|
|
executeCommand(() -> f[0] = createChildData(myRoot, "foo.txt"));
|
|
assertGlobalRedoNotAvailable();
|
|
assertRedoNotAvailable(getEditor(f[0]));
|
|
|
|
undo(getEditor(f[0])); // should undo creation, not editing of previous document
|
|
assertNull(myRoot.findChild("foo.txt"));
|
|
|
|
globalUndo();
|
|
f[0] = myRoot.findChild("foo.txt");
|
|
assertNotNull(f[0]);
|
|
assertEquals("foofoo", getDocumentText(f[0]));
|
|
|
|
undo(getEditor(f[0]));
|
|
assertEquals("foo", getDocumentText(f[0]));
|
|
}
|
|
|
|
public void testDeletionOfNoneJavaFiles() {
|
|
VirtualFile f = createChildData(myRoot, "f.xxx");
|
|
|
|
deleteInCommand(f);
|
|
assertGlobalUndoIsAvailable();
|
|
|
|
globalUndo();
|
|
assertNotNull(myRoot.findChild("f.xxx"));
|
|
}
|
|
|
|
public void testUndoDeleteDir() {
|
|
createDirectory(myDirName);
|
|
checkDirExists();
|
|
|
|
deleteDirectory();
|
|
checkDirDoesNotExist();
|
|
|
|
globalUndo();
|
|
checkDirExists();
|
|
|
|
globalRedo();
|
|
checkDirDoesNotExist();
|
|
}
|
|
|
|
public void testUndoRenameDirectoryWithFile() {
|
|
final VirtualFile[] dir = new VirtualFile[1];
|
|
final VirtualFile[] file = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
dir[0] = createChildDirectory(myRoot, "dir");
|
|
file[0] = createChildData(dir[0], "file.txt");
|
|
});
|
|
|
|
setDocumentText(file[0], "document");
|
|
|
|
executeCommand(() -> {
|
|
rename(dir[0], "dir2");
|
|
setDocumentText(file[0], "document2");
|
|
});
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
globalUndo();
|
|
assertNull(myRoot.findChild("dir2"));
|
|
dir[0] = myRoot.findChild("dir");
|
|
assertNotNull(dir);
|
|
file[0] = dir[0].findChild("file.txt");
|
|
assertNotNull(file[0]);
|
|
assertEquals("document", getDocumentText(file[0]));
|
|
|
|
globalRedo();
|
|
assertNull(myRoot.findChild("dir"));
|
|
dir[0] = myRoot.findChild("dir2");
|
|
assertNotNull(dir);
|
|
file[0] = dir[0].findChild("file.txt");
|
|
assertNotNull(file[0]);
|
|
assertEquals("document2", getDocumentText(file[0]));
|
|
}
|
|
}
|
|
|
|
public void testUndoDeleteDirectoryWithFile() {
|
|
final VirtualFile[] dir = new VirtualFile[1];
|
|
final VirtualFile[] file = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
dir[0] = createChildDirectory(myRoot, "dir");
|
|
file[0] = createChildData(dir[0], "file.txt");
|
|
});
|
|
|
|
setDocumentText(file[0], "document");
|
|
|
|
executeCommand(() -> {
|
|
delete(file[0]);
|
|
delete(dir[0]);
|
|
});
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
globalUndo();
|
|
dir[0] = myRoot.findChild("dir");
|
|
assertNotNull(dir);
|
|
file[0] = dir[0].findChild("file.txt");
|
|
assertNotNull(file[0]);
|
|
assertEquals("document", getDocumentText(file[0]));
|
|
|
|
globalRedo();
|
|
assertNull(myRoot.findChild("dir"));
|
|
}
|
|
}
|
|
|
|
public void testUndoDeleteDirectoryWithFileWithDisabledUndoMustNotRecreateFiles() {
|
|
final VirtualFile[] dir = new VirtualFile[1];
|
|
final VirtualFile[] file = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
dir[0] = createChildDirectory(myRoot, "dir");
|
|
file[0] = createChildData(dir[0], "file.txt");
|
|
});
|
|
|
|
setDocumentText(file[0], "document");
|
|
|
|
executeCommand(() -> {
|
|
UndoUtil.disableUndoFor(file[0]);
|
|
delete(file[0]);
|
|
delete(dir[0]);
|
|
});
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
globalUndo();
|
|
dir[0] = myRoot.findChild("dir");
|
|
assertNotNull(dir);
|
|
file[0] = dir[0].findChild("file.txt");
|
|
assertNull(file[0]);
|
|
|
|
globalRedo();
|
|
assertNull(myRoot.findChild("dir"));
|
|
}
|
|
}
|
|
|
|
public void testUndoDirectoryMovingToExistingDirectory() throws IOException {
|
|
createTestProjectStructureAndMoveDirectory();
|
|
|
|
File ioDir = createDirOnTheDirToMovePlaceWithTheSameName();
|
|
|
|
checkSuccessfulMoveUndo(ioDir);
|
|
}
|
|
|
|
public void testUndoDirectoryMovingToExistingFile() throws IOException {
|
|
createTestProjectStructureAndMoveDirectory();
|
|
|
|
File ioDir = createFileOnTheDirToMovePlaceWithTheSameName();
|
|
|
|
checkSuccessfulMoveUndo(ioDir);
|
|
}
|
|
|
|
private void createTestProjectStructureAndMoveDirectory() throws IOException {
|
|
WriteCommandAction.writeCommandAction(getProject()).run(() -> {
|
|
createDirectory("parent");
|
|
myDir1 = myDir.createChildDirectory(this, "dir1");
|
|
myDir2 = myDir.createChildDirectory(this, "dir2");
|
|
myDirToMove = myDir1.createChildDirectory(this, DIR_NAME);
|
|
myDirToMove.createChildData(this, "file.txt");
|
|
});
|
|
|
|
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> move(myDirToMove, myDir2), "Moving", null);
|
|
}
|
|
|
|
private File createFileOnTheDirToMovePlaceWithTheSameName() throws IOException {
|
|
File ioDir = new File(VfsUtilCore.virtualToIoFile(myDir1), DIR_NAME);
|
|
ioDir.createNewFile();
|
|
|
|
refreshFileSystem();
|
|
return ioDir;
|
|
}
|
|
|
|
private File createDirOnTheDirToMovePlaceWithTheSameName() {
|
|
File ioDir = new File(VfsUtilCore.virtualToIoFile(myDir1), DIR_NAME);
|
|
ioDir.mkdir();
|
|
|
|
refreshFileSystem();
|
|
return ioDir;
|
|
}
|
|
|
|
private void checkSuccessfulMoveUndo(File ioDir) {
|
|
globalUndo();
|
|
|
|
assertTrue(new File(ioDir, "file.txt").exists());
|
|
assertNull(myDir2.findChild(DIR_NAME));
|
|
}
|
|
|
|
public void testUndoDirectoryRenamingToExistingDirectory() {
|
|
createTestProjectStructureAndRenameDirectory();
|
|
|
|
File ioDir = createDirOnTheDirToRenamePlaceWithTheSameName();
|
|
|
|
checkSuccessfulRenameUndo(ioDir);
|
|
}
|
|
|
|
public void testUndoDirectoryRenamingToExistingFile() throws IOException {
|
|
createTestProjectStructureAndRenameDirectory();
|
|
|
|
File ioDir = createFileOnTheDirToRenamePlaceWithTheSameName();
|
|
|
|
checkSuccessfulRenameUndo(ioDir);
|
|
}
|
|
|
|
// ignored because some problems with weak references in filedocumentmanager
|
|
public void testCanUndoDocumentAfterExternalChange() throws Exception {
|
|
final VirtualFile f = createChildData(myRoot, "f.txt");
|
|
|
|
executeCommand(() -> setDocumentText(f, "doc"));
|
|
|
|
Document doc = FileDocumentManager.getInstance().getDocument(f); // prevent weak refs from being collected
|
|
FileDocumentManager.getInstance().saveAllDocuments(); // prevent 'file has been changed dialog'
|
|
|
|
File ioFile = new File(f.getPath());
|
|
FileUtil.writeToFile(ioFile, "external".getBytes(Charsets.UTF_8));
|
|
ioFile.setLastModified(f.getTimeStamp() + 2000);
|
|
LocalFileSystem.getInstance().refresh(false);
|
|
|
|
assertEquals("external", getDocumentText(f));
|
|
|
|
Editor editor = getEditor(f);
|
|
|
|
assertGlobalUndoNotAvailable();
|
|
assertUndoIsAvailable(editor);
|
|
|
|
undo(editor);
|
|
assertEquals("doc", getDocumentText(f));
|
|
undo(editor);
|
|
assertEquals("", getDocumentText(f));
|
|
|
|
redo(editor);
|
|
assertEquals("doc", getDocumentText(f));
|
|
redo(editor);
|
|
assertEquals("external", getDocumentText(f));
|
|
Reference.reachabilityFence(doc);
|
|
}
|
|
|
|
public void testCanUndoAfterFileWasDeletedAndWhenCreatedExternally() throws IOException {
|
|
VirtualFile f = createChildData(myRoot, "f.txt");
|
|
|
|
final VirtualFile finalF = f;
|
|
executeCommand(() -> setDocumentText(finalF, "doc"));
|
|
|
|
executeCommand(() -> delete(finalF));
|
|
|
|
File ioFile = new File(f.getPath());
|
|
FileUtil.writeToFile(ioFile, "external".getBytes(Charsets.UTF_8));
|
|
LocalFileSystem.getInstance().refresh(false);
|
|
|
|
f = myRoot.findChild("f.txt");
|
|
assertNotNull(f);
|
|
|
|
assertEquals("external", getDocumentText(f));
|
|
|
|
Editor editor = getEditor(f);
|
|
|
|
assertGlobalUndoIsAvailable();
|
|
assertUndoIsAvailable(editor);
|
|
|
|
undo(editor);
|
|
assertEquals("doc", getDocumentText(f));
|
|
undo(editor);
|
|
assertEquals("", getDocumentText(f));
|
|
|
|
redo(editor);
|
|
assertEquals("doc", getDocumentText(f));
|
|
|
|
// todo
|
|
//redo(editor);
|
|
//assertEquals("external", getDocumentText(f));
|
|
}
|
|
|
|
public void testDoNotRecordDocumentChangesWhenFileChangedExternallyAndNoChangesRecordedYet() throws IOException {
|
|
VirtualFile f = createChildData(myRoot, "f.txt");
|
|
|
|
Document d = FileDocumentManager.getInstance().getDocument(f); // make sure the document is cached.
|
|
|
|
File ioFile = new File(f.getPath());
|
|
FileUtil.writeToFile(ioFile, "external".getBytes(Charsets.UTF_8));
|
|
ioFile.setLastModified(f.getTimeStamp() + 2000);
|
|
LocalFileSystem.getInstance().refresh(false);
|
|
|
|
assertEquals("external", getDocumentText(f));
|
|
|
|
assertGlobalUndoNotAvailable();
|
|
assertUndoNotAvailable(getEditor(f));
|
|
Reference.reachabilityFence(d);
|
|
}
|
|
|
|
public void testGlobalUndoIsAvailableWhenFileChangedExternallyWithForceFlag() {
|
|
String fileName = "f.txt";
|
|
String projectFileName = "proj.txt";
|
|
VirtualFile f = createChildData(myRoot, projectFileName);
|
|
|
|
executeCommand(() -> {
|
|
createChildData(myRoot, fileName);
|
|
|
|
// Rider case, modify externally and refresh some file in a command
|
|
File ioFile = new File(f.getPath());
|
|
FileUtil.writeToFile(ioFile, "content".getBytes(Charsets.UTF_8));
|
|
ioFile.setLastModified(f.getTimeStamp() + 2000);
|
|
|
|
UndoUtil.setForceUndoFlag(f, true);
|
|
f.refresh(false, true);
|
|
});
|
|
|
|
assertGlobalUndoIsAvailable();
|
|
|
|
assertNotNull(myRoot.findChild(fileName));
|
|
globalUndo();
|
|
assertNull(myRoot.findChild(fileName));
|
|
}
|
|
|
|
public void testRecordDocumentChangesWhenFileChangedExternallyAndAlreadyHasChanges() throws IOException {
|
|
final VirtualFile f = createChildData(myRoot, "f.txt");
|
|
|
|
executeCommand(() -> setDocumentText(f, "doc"));
|
|
|
|
assertGlobalUndoNotAvailable();
|
|
assertUndoIsAvailable(getEditor(f));
|
|
|
|
undo(getEditor(f)); // clear undo stack, but preserve the change in redo stack
|
|
|
|
File ioFile = new File(f.getPath());
|
|
FileUtil.writeToFile(ioFile, "external".getBytes(Charsets.UTF_8));
|
|
ioFile.setLastModified(f.getTimeStamp() + 2000);
|
|
LocalFileSystem.getInstance().refresh(false);
|
|
|
|
assertEquals("external", getDocumentText(f));
|
|
|
|
assertGlobalUndoNotAvailable();
|
|
assertUndoIsAvailable(getEditor(f));
|
|
}
|
|
|
|
private Pair<Editor, Editor> prepareTestUndoFallback(){
|
|
createClass("Bar");
|
|
createClass("Foo");
|
|
|
|
Editor barEditor = openEditor("Bar.java");
|
|
|
|
// 1: extends Foo
|
|
WriteAction.runAndWait(() -> executeCommand(() -> barEditor.getDocument().insertString(17, "extends Foo")));
|
|
|
|
// 2: change local stack in second file
|
|
WriteAction.runAndWait(() -> executeCommand(() -> barEditor.getDocument().insertString(30, "public void Test(){}")));
|
|
|
|
// 3: rename Foo class
|
|
renameClassTo("FooRenamed");
|
|
Editor fooEditor = openEditor("FooRenamed.java");
|
|
|
|
// 4: change local stack in FooRenamed.java
|
|
WriteAction.runAndWait(() -> executeCommand(() -> fooEditor.getDocument().insertString(25, "public void Test(){}")));
|
|
|
|
return new Pair<>(barEditor, fooEditor);
|
|
}
|
|
|
|
public void testUndoFallbackToLocalStack() {
|
|
Registry.get("ide.undo.fallback").setValue(true, getTestRootDisposable());
|
|
Pair<Editor, Editor> pair = prepareTestUndoFallback();
|
|
var barEditor = pair.first;
|
|
var fooEditor = pair.second;
|
|
|
|
undo(barEditor);
|
|
|
|
assertFalse("Bar.java doesn't remove rename from Foo.java", barEditor.getDocument().getText().contains("FooRenamed"));
|
|
assertTrue("Foo.java doesn't contains last rename result", fooEditor.getDocument().getText().contains("FooRenamed"));
|
|
|
|
}
|
|
public void testUndoFallbackToLocalStackAndRedo() {
|
|
Registry.get("ide.undo.fallback").setValue(true, getTestRootDisposable());
|
|
Pair<Editor, Editor> pair = prepareTestUndoFallback();
|
|
var barEditor = pair.first;
|
|
var fooEditor = pair.second;
|
|
|
|
undo(barEditor);
|
|
|
|
// call redo to gather splitted command together
|
|
redo(barEditor);
|
|
|
|
assertTrue(barEditor.getDocument().getText().contains("FooRenamed"));
|
|
|
|
// 7: undo rename Foo -> FooRenamed to check that changes also applied to Bar.java
|
|
undo(fooEditor);
|
|
undo(fooEditor);
|
|
|
|
assertFalse("FooRenamed rename undo doesn't applied to Bar.java", barEditor.getDocument().getText().contains("FooRenamed"));
|
|
}
|
|
|
|
public void testUndoRedoNotAvailableAfterFileWasDeletedExternally() {
|
|
final VirtualFile f1 = createChildData(myRoot, "f1.txt");
|
|
final VirtualFile f2 = createChildData(myRoot, "f2.txt");
|
|
|
|
executeCommand(() -> {
|
|
rename(f1, "ff1.txt");
|
|
setDocumentText(f1, "ff1");
|
|
});
|
|
executeCommand(() -> {
|
|
rename(f2, "ff2.txt");
|
|
setDocumentText(f2, "ff2");
|
|
});
|
|
|
|
// <---- test point
|
|
|
|
executeCommand(() -> {
|
|
rename(f2, "fff2.txt");
|
|
setDocumentText(f2, "fff2");
|
|
});
|
|
executeCommand(() -> {
|
|
rename(f1, "fff1.txt");
|
|
setDocumentText(f1, "fff1");
|
|
});
|
|
|
|
globalUndo();
|
|
assertGlobalUndoIsAvailable();
|
|
assertGlobalRedoIsAvailable();
|
|
|
|
globalUndo();
|
|
assertGlobalUndoIsAvailable();
|
|
assertGlobalRedoIsAvailable();
|
|
|
|
delete(f1); // commands for f1 should be invalidated here
|
|
|
|
assertGlobalUndoIsAvailable();
|
|
assertGlobalRedoIsAvailable();
|
|
|
|
globalRedo();
|
|
|
|
assertGlobalUndoIsAvailable();
|
|
assertGlobalRedoNotAvailable();
|
|
|
|
globalUndo();
|
|
globalUndo(); // back to the 'test point'
|
|
|
|
assertGlobalUndoNotAvailable();
|
|
assertGlobalRedoIsAvailable();
|
|
}
|
|
|
|
public void testCanUndoDocumentsAfterSave() {
|
|
FileDocumentManager dm = FileDocumentManager.getInstance();
|
|
EditorFactory ef = EditorFactory.getInstance();
|
|
|
|
VirtualFile f = createChildData(myRoot, "f.java");
|
|
|
|
Document d = dm.getDocument(f);
|
|
Editor e = ef.createEditor(d);
|
|
|
|
assertEquals("", d.getText());
|
|
typeInText(e, "12345");
|
|
assertEquals("12345", d.getText());
|
|
|
|
dm.saveDocument(d);
|
|
undo(e);
|
|
ef.releaseEditor(e);
|
|
|
|
assertEquals("", d.getText());
|
|
}
|
|
|
|
public void testClearingRedoStackClearsGlobalCommandsCorrectly() {
|
|
final VirtualFile[] f1 = new VirtualFile[1];
|
|
final VirtualFile[] f2 = new VirtualFile[1];
|
|
final VirtualFile[] f3 = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
f1[0] = createChildData(myRoot, "f1.java");
|
|
f2[0] = createChildData(myRoot, "f2.java");
|
|
f3[0] = createChildData(myRoot, "f3.java");
|
|
});
|
|
|
|
for (VirtualFile each : FileEditorManager.getInstance(myProject).getOpenFiles()) {
|
|
FileEditorManager.getInstance(myProject).closeFile(each);
|
|
}
|
|
|
|
executeCommand(() -> {
|
|
setDocumentText(f1[0], "f1");
|
|
setDocumentText(f2[0], "f2");
|
|
});
|
|
|
|
executeCommand(() -> setDocumentText(f3[0], "f3"));
|
|
|
|
undo(getEditor(f3[0]));
|
|
undo(null);
|
|
assertEquals("", getDocumentText(f1[0]));
|
|
assertEquals("", getDocumentText(f3[0]));
|
|
|
|
assertRedoIsAvailable(null);
|
|
assertRedoIsAvailable(getEditor(f1[0]));
|
|
assertRedoIsAvailable(getEditor(f2[0]));
|
|
assertRedoIsAvailable(getEditor(f3[0]));
|
|
|
|
for (VirtualFile each : FileEditorManager.getInstance(myProject).getOpenFiles()) {
|
|
FileEditorManager.getInstance(myProject).closeFile(each);
|
|
}
|
|
executeCommand(() -> setDocumentText(f1[0], "ff1"));
|
|
|
|
assertRedoNotAvailable(null);
|
|
assertRedoNotAvailable(getEditor(f1[0]));
|
|
assertRedoNotAvailable(getEditor(f2[0]));
|
|
assertRedoIsAvailable(getEditor(f3[0]));
|
|
}
|
|
|
|
public void testRegisteringOpenDocumentWhenAnotherFileIsAffected() {
|
|
final VirtualFile[] f1 = new VirtualFile[1];
|
|
final VirtualFile[] f2 = new VirtualFile[1];
|
|
final VirtualFile[] f3 = new VirtualFile[1];
|
|
f1[0] = createChildData(myRoot, "f1.java");
|
|
f2[0] = createChildData(myRoot, "f2.java");
|
|
f3[0] = createChildData(myRoot, "f3.java");
|
|
|
|
executeCommand(() -> setDocumentText(f1[0], "f1"));
|
|
assertUndoNotAvailable(null);
|
|
assertUndoIsAvailable(getEditor(f1[0]));
|
|
assertUndoNotAvailable(getEditor(f2[0]));
|
|
assertUndoNotAvailable(getEditor(f3[0]));
|
|
|
|
myEditor = openEditor(f2[0]);
|
|
|
|
executeCommand(() -> setDocumentText(f1[0], "f1"));
|
|
assertUndoIsAvailable(null);
|
|
assertUndoIsAvailable(getEditor(f1[0]));
|
|
assertUndoIsAvailable(getEditor(f2[0]));
|
|
assertUndoNotAvailable(getEditor(f3[0]));
|
|
}
|
|
|
|
public void testRegisteringOpenDocumentForReadOnlyFileDoesNotBreakUndoChain() throws Exception {
|
|
final VirtualFile f1 = createChildData(myRoot, "f1.java");
|
|
final VirtualFile f2 = createChildData(myRoot, "f2.java");
|
|
final VirtualFile f3 = createChildData(myRoot, "f3.java");
|
|
|
|
WriteAction.runAndWait(() -> f2.setWritable(false));
|
|
|
|
executeCommand(() -> setDocumentText(f1, "initial"));
|
|
assertUndoNotAvailable(null);
|
|
assertUndoIsAvailable(getEditor(f1));
|
|
assertUndoNotAvailable(getEditor(f2));
|
|
assertUndoNotAvailable(getEditor(f3));
|
|
|
|
myEditor = openEditor(f2);
|
|
|
|
executeCommand(() -> setDocumentText(f1, "new content"));
|
|
assertUndoIsAvailable(null);
|
|
assertUndoIsAvailable(getEditor(f1));
|
|
assertUndoIsAvailable(getEditor(f2));
|
|
assertUndoNotAvailable(getEditor(f3));
|
|
|
|
undo(getEditor(f2));
|
|
assertEquals("initial", getDocumentText(f1));
|
|
}
|
|
|
|
public void testDoNotRegisterOpenDocumentWhenAnotherFileReloaded() throws Exception {
|
|
final VirtualFile f1 = createChildData(myRoot, "f1.java");
|
|
final VirtualFile f2 = createChildData(myRoot, "f2.java");
|
|
|
|
executeCommand(() -> setDocumentText(f1, "f1"));
|
|
executeCommand(() -> {
|
|
setDocumentText(f2, "f2");
|
|
FileDocumentManager.getInstance().saveDocument(getDocument(f2));
|
|
});
|
|
|
|
assertUndoNotAvailable(null);
|
|
assertUndoIsAvailable(getEditor(f1));
|
|
assertUndoIsAvailable(getEditor(f2));
|
|
|
|
myEditor = openEditor(f1);
|
|
|
|
File file = new File(f2.getPath());
|
|
FileUtil.writeToFile(file, "reloaded");
|
|
file.setLastModified(file.lastModified() + 10000);
|
|
LocalFileSystem.getInstance().refreshIoFiles(Collections.singletonList(file));
|
|
|
|
assertUndoNotAvailable(null);
|
|
assertUndoIsAvailable(getEditor(f1));
|
|
assertUndoIsAvailable(getEditor(f2));
|
|
|
|
// undo open document first
|
|
undo(getEditor(f1));
|
|
assertEquals("", getDocumentText(f1));
|
|
|
|
undo(getEditor(f2));
|
|
assertEquals("f2", getDocumentText(f2));
|
|
}
|
|
|
|
public void testClearingRedoStackClearsGlobalCommandsCorrectlyIntersectingStacks() {
|
|
final VirtualFile[] f1 = new VirtualFile[1];
|
|
final VirtualFile[] f2 = new VirtualFile[1];
|
|
final VirtualFile[] f3 = new VirtualFile[1];
|
|
final VirtualFile[] f4 = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
f1[0] = createChildData(myRoot, "f1.java");
|
|
f2[0] = createChildData(myRoot, "f2.java");
|
|
f3[0] = createChildData(myRoot, "f3.java");
|
|
f4[0] = createChildData(myRoot, "f4.java");
|
|
});
|
|
|
|
executeCommand(() -> {
|
|
setDocumentText(f1[0], "f1");
|
|
setDocumentText(f2[0], "f2");
|
|
});
|
|
|
|
executeCommand(() -> {
|
|
setDocumentText(f3[0], "f3");
|
|
setDocumentText(f4[0], "f4");
|
|
});
|
|
|
|
undo(null);
|
|
undo(null);
|
|
assertEquals("", getDocumentText(f1[0]));
|
|
assertEquals("", getDocumentText(f3[0]));
|
|
|
|
assertRedoIsAvailable(null);
|
|
assertRedoIsAvailable(getEditor(f1[0]));
|
|
assertRedoIsAvailable(getEditor(f2[0]));
|
|
assertRedoIsAvailable(getEditor(f3[0]));
|
|
assertRedoIsAvailable(getEditor(f4[0]));
|
|
|
|
executeCommand(() -> setDocumentText(f1[0], "ff1"));
|
|
|
|
assertRedoNotAvailable(null);
|
|
assertRedoNotAvailable(getEditor(f1[0]));
|
|
assertRedoNotAvailable(getEditor(f2[0]));
|
|
assertRedoNotAvailable(getEditor(f3[0]));
|
|
assertRedoNotAvailable(getEditor(f4[0]));
|
|
}
|
|
|
|
public void testClearingRedoStackClearsStackNotTooMuch() {
|
|
final VirtualFile[] f1 = new VirtualFile[1];
|
|
final VirtualFile[] f2 = new VirtualFile[1];
|
|
executeCommand(() -> {
|
|
f1[0] = createChildData(myRoot, "f1.java");
|
|
f2[0] = createChildData(myRoot, "f2.java");
|
|
});
|
|
|
|
executeCommand(() -> setDocumentText(f1[0], "f1"));
|
|
|
|
executeCommand(() -> {
|
|
setDocumentText(f1[0], "ff1");
|
|
setDocumentText(f2[0], "ff2");
|
|
});
|
|
|
|
undo(getEditor(f1[0]));
|
|
undo(getEditor(f1[0]));
|
|
assertEquals("", getDocumentText(f1[0]));
|
|
assertEquals("", getDocumentText(f2[0]));
|
|
|
|
assertRedoIsAvailable(null);
|
|
assertRedoIsAvailable(getEditor(f1[0]));
|
|
assertRedoIsAvailable(getEditor(f2[0]));
|
|
|
|
executeCommand(() -> setDocumentText(f2[0], "fff2"));
|
|
|
|
assertRedoNotAvailable(null);
|
|
assertRedoIsAvailable(getEditor(f1[0]));
|
|
assertRedoNotAvailable(getEditor(f2[0]));
|
|
|
|
redo(getEditor(f1[0]));
|
|
assertEquals("f1", getDocumentText(f1[0]));
|
|
}
|
|
|
|
public void testAddingAffectedDocumentOrFile() {
|
|
final VirtualFile f1 = createChildData(myRoot, "f1.java");
|
|
final VirtualFile f2 = createChildData(myRoot, "f2.java");
|
|
final VirtualFile f3 = createChildData(myRoot, "f3.java");
|
|
|
|
assertUndoNotAvailable(getEditor(f1));
|
|
assertUndoNotAvailable(getEditor(f2));
|
|
assertUndoNotAvailable(getEditor(f3));
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> {
|
|
setDocumentText(f1, "foo");
|
|
CommandProcessor.getInstance().addAffectedDocuments(myProject, FileDocumentManager.getInstance().getDocument(f2));
|
|
CommandProcessor.getInstance().addAffectedFiles(myProject, f3);
|
|
}, null, null);
|
|
|
|
assertUndoIsAvailable(getEditor(f1));
|
|
assertUndoIsAvailable(getEditor(f2));
|
|
assertUndoIsAvailable(getEditor(f3));
|
|
|
|
myManager.flushCurrentCommandMerger();
|
|
|
|
assertUndoIsAvailable(getEditor(f1));
|
|
assertUndoIsAvailable(getEditor(f2));
|
|
assertUndoIsAvailable(getEditor(f3));
|
|
}
|
|
|
|
public void testAddingAffectedDocumentWhenNoOtherChangesDoesntChangeUndoRedoStacks() {
|
|
final VirtualFile f1 = createChildData(myRoot, "f1.java");
|
|
final VirtualFile f2 = createChildData(myRoot, "f2.java");
|
|
final VirtualFile f3 = createChildData(myRoot, "f3.java");
|
|
|
|
assertUndoNotAvailable(getEditor(f1));
|
|
assertUndoNotAvailable(getEditor(f2));
|
|
assertUndoNotAvailable(getEditor(f3));
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> {
|
|
CommandProcessor.getInstance().addAffectedDocuments(myProject, FileDocumentManager.getInstance().getDocument(f2));
|
|
CommandProcessor.getInstance().addAffectedFiles(myProject, f3);
|
|
}, null, null);
|
|
|
|
assertUndoNotAvailable(getEditor(f1));
|
|
assertUndoNotAvailable(getEditor(f2));
|
|
assertUndoNotAvailable(getEditor(f3));
|
|
|
|
myManager.flushCurrentCommandMerger();
|
|
|
|
assertUndoNotAvailable(getEditor(f1));
|
|
assertUndoNotAvailable(getEditor(f2));
|
|
assertUndoNotAvailable(getEditor(f3));
|
|
}
|
|
|
|
public void testFixRIDER10340() {
|
|
createClass("TestClass1");
|
|
Editor editor = openEditor("TestClass1.java");
|
|
|
|
WriteAction.runAndWait(() -> executeCommand(() -> editor.getDocument().insertString(27, "public class Aaa {}")));
|
|
|
|
PsiDocumentManager instance = PsiDocumentManager.getInstance(myProject);
|
|
instance.commitAllDocuments();
|
|
PsiFile file = instance.getPsiFile(editor.getDocument());
|
|
PsiClass aaaClass = (PsiClass)file.getViewProvider().findElementAt(41).getParent();
|
|
|
|
IntentionAction fix = QuickFixFactory.getInstance().createMoveClassToSeparateFileFix(aaaClass);
|
|
WriteAction.runAndWait(() -> executeCommand(() -> fix.invoke(myProject, editor, file)));
|
|
instance.commitAllDocuments();
|
|
|
|
VirtualFile aaaFile = file.getVirtualFile().getParent().findChild("Aaa.java");
|
|
deleteInCommand(aaaFile);
|
|
|
|
undo(editor);
|
|
|
|
undo(editor);
|
|
|
|
assertEquals("public class TestClass1 {\n}public class Aaa {}\n", editor.getDocument().getText());
|
|
}
|
|
|
|
public void testPerformance() {
|
|
WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
VirtualFile dir1 = myRoot.createChildDirectory(this, "dir1");
|
|
VirtualFile dir2 = myRoot.createChildDirectory(this, "dir2");
|
|
dir1.createChildData(this, "f.java");
|
|
dir1.move(this, dir2);
|
|
}
|
|
catch (IOException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
});
|
|
|
|
PlatformTestUtil.assertTiming("", 2000, 1, () -> {
|
|
redoUndo();
|
|
redoUndo();
|
|
redoUndo();
|
|
});
|
|
}
|
|
|
|
private void redoUndo() {
|
|
globalUndo();
|
|
globalRedo();
|
|
}
|
|
|
|
public void testRedoIsNotAvailableAfterFileChanges() {
|
|
final VirtualFile f = createChildData(myRoot, "f.java");
|
|
|
|
renameInCommand(f, "ff.java");
|
|
|
|
globalUndo();
|
|
assertGlobalRedoIsAvailable();
|
|
|
|
renameInCommand(f, "fff.java");
|
|
|
|
assertGlobalRedoNotAvailable();
|
|
}
|
|
|
|
public void testUndoRedoFileWithChangedDocument() throws Exception {
|
|
VirtualFile file = getTempDir().createVirtualDir();
|
|
assertNotNull(file);
|
|
final String[] path = new String[1];
|
|
executeCommand(() -> {
|
|
PsiFile f = createFile(myModule, file, "foo.txt", "initial");
|
|
Document doc = PsiDocumentManager.getInstance(myProject).getDocument(f);
|
|
setDocumentText(doc, "document");
|
|
path[0] = f.getVirtualFile().getPath();
|
|
});
|
|
|
|
globalUndo();
|
|
globalRedo();
|
|
|
|
VirtualFile f = LocalFileSystem.getInstance().findFileByPath(path[0]);
|
|
assertNotNull(f);
|
|
assertEquals("initial", VfsUtilCore.loadText(f));
|
|
Document doc = FileDocumentManager.getInstance().getDocument(f);
|
|
assertEquals("document", doc.getText());
|
|
}
|
|
|
|
public void testUndoRedoFileWithChangedDocumentWithSeveralAffectedFiles() throws Exception {
|
|
final String[] path = new String[2];
|
|
executeCommand(() -> {
|
|
VirtualFile f = createChildData(myRoot, "foo1.txt");
|
|
setBinaryContent(f, "initial1".getBytes(Charsets.UTF_8));
|
|
Document doc = FileDocumentManager.getInstance().getDocument(f);
|
|
setDocumentText(doc, "document1");
|
|
path[0] = f.getPath();
|
|
|
|
f = createChildData(myRoot, "foo2.txt");
|
|
setBinaryContent(f, "initial2".getBytes(Charsets.UTF_8));
|
|
doc = FileDocumentManager.getInstance().getDocument(f);
|
|
setDocumentText(doc, "document2");
|
|
path[1] = f.getPath();
|
|
});
|
|
|
|
globalUndo();
|
|
globalRedo();
|
|
|
|
VirtualFile f = LocalFileSystem.getInstance().findFileByPath(path[0]);
|
|
assertNotNull(f);
|
|
assertEquals("initial1", VfsUtilCore.loadText(f));
|
|
Document doc = FileDocumentManager.getInstance().getDocument(f);
|
|
assertEquals("document1", doc.getText());
|
|
|
|
f = LocalFileSystem.getInstance().findFileByPath(path[1]);
|
|
assertNotNull(f);
|
|
assertEquals("initial2", VfsUtilCore.loadText(f));
|
|
doc = FileDocumentManager.getInstance().getDocument(f);
|
|
assertEquals("document2", doc.getText());
|
|
}
|
|
|
|
private static void setDocumentText(final Document doc, final String document2) {
|
|
ApplicationManager.getApplication().runWriteAction(() -> doc.setText(document2));
|
|
}
|
|
|
|
public void testUndoRedoFileMoveAndDeleteWithChangedDocument() throws Exception {
|
|
final VirtualFile[] dir = new VirtualFile[1];
|
|
final VirtualFile[] f = new VirtualFile[1];
|
|
|
|
executeCommand(() -> {
|
|
dir[0] = createChildDirectory(myRoot, "dir");
|
|
f[0] = createChildData(myRoot, "foo.txt");
|
|
setBinaryContent(f[0], "initial".getBytes(Charsets.UTF_8));
|
|
setDocumentText(f[0], "document");
|
|
});
|
|
executeCommand(() -> {
|
|
move(f[0], dir[0]);
|
|
setDocumentText(f[0], "moved");
|
|
});
|
|
executeCommand(() -> delete(dir[0]));
|
|
|
|
globalUndo();
|
|
|
|
f[0] = LocalFileSystem.getInstance().findFileByPath(f[0].getPath());
|
|
dir[0] = LocalFileSystem.getInstance().findFileByPath(dir[0].getPath());
|
|
assertNotNull(f[0]);
|
|
assertNotNull(dir[0]);
|
|
assertEquals(dir[0], f[0].getParent());
|
|
assertEquals("moved", VfsUtilCore.loadText(f[0]));
|
|
Document doc = FileDocumentManager.getInstance().getDocument(f[0]);
|
|
assertEquals("moved", doc.getText());
|
|
|
|
globalUndo();
|
|
|
|
assertEquals(myRoot, f[0].getParent());
|
|
assertEquals("moved", VfsUtilCore.loadText(f[0]));
|
|
doc = FileDocumentManager.getInstance().getDocument(f[0]);
|
|
assertEquals("document", doc.getText());
|
|
|
|
globalUndo();
|
|
globalRedo();
|
|
|
|
f[0] = LocalFileSystem.getInstance().findFileByPath(f[0].getPath());
|
|
dir[0] = LocalFileSystem.getInstance().findFileByPath(dir[0].getPath());
|
|
|
|
assertEquals(myRoot, f[0].getParent());
|
|
assertEquals("initial", VfsUtilCore.loadText(f[0]));
|
|
doc = FileDocumentManager.getInstance().getDocument(f[0]);
|
|
assertEquals("document", doc.getText());
|
|
|
|
globalRedo();
|
|
|
|
assertEquals(dir[0], f[0].getParent());
|
|
assertEquals("initial", VfsUtilCore.loadText(f[0]));
|
|
doc = FileDocumentManager.getInstance().getDocument(f[0]);
|
|
assertEquals("moved", doc.getText());
|
|
}
|
|
|
|
private void renameInCommand(final VirtualFile f, final String name) {
|
|
executeCommand(() -> rename(f, name));
|
|
}
|
|
|
|
private void checkSuccessfulRenameUndo(File ioDir) {
|
|
try {
|
|
globalUndo();
|
|
}
|
|
catch (Exception ex) {
|
|
fail("Unexpected exception: " + ex.getLocalizedMessage());
|
|
}
|
|
|
|
assertTrue(new File(ioDir, "file.txt").exists());
|
|
assertNull(myDir.findChild(NEW_NAME));
|
|
}
|
|
|
|
private File createDirOnTheDirToRenamePlaceWithTheSameName() {
|
|
File result = new File(VfsUtilCore.virtualToIoFile(myDirToRename.getParent()), DIR_TO_RENAME_NAME);
|
|
result.mkdir();
|
|
refreshFileSystem();
|
|
return result;
|
|
}
|
|
|
|
private File createFileOnTheDirToRenamePlaceWithTheSameName() throws IOException {
|
|
File result = new File(VfsUtilCore.virtualToIoFile(myDirToRename.getParent()), DIR_TO_RENAME_NAME);
|
|
result.createNewFile();
|
|
refreshFileSystem();
|
|
return result;
|
|
}
|
|
|
|
private void createTestProjectStructureAndRenameDirectory() {
|
|
createDirectory("parent");
|
|
myDirToRename = createChildDirectory(myDir, DIR_TO_RENAME_NAME);
|
|
createChildData(myDirToRename, "file.txt");
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> rename(myDirToRename, NEW_NAME), "Renaming", null);
|
|
}
|
|
|
|
private static void refreshFileSystem() {
|
|
VfsTestUtil.syncRefresh();
|
|
}
|
|
|
|
private void deleteInCommand(final VirtualFile f) {
|
|
executeCommand("Delete file", () -> delete(f));
|
|
}
|
|
|
|
private void checkDirDoesNotExist() {
|
|
assertNull(myRoot.findChild(myDirName));
|
|
}
|
|
|
|
private void checkDirExists() {
|
|
VirtualFile file = myRoot.findChild(myDirName);
|
|
assertNotNull(file);
|
|
assertTrue(file.isValid());
|
|
}
|
|
|
|
private void createDirectory(final String name) {
|
|
executeCommand("Create Directory", () -> myDir = createChildDirectory(myRoot, name));
|
|
}
|
|
|
|
private void deleteDirectory() {
|
|
Command command = () -> delete(myDir);
|
|
executeCommand("Delete Directory", command);
|
|
}
|
|
|
|
private void moveClassTo(final VirtualFile dirTo) {
|
|
Command command = () -> {
|
|
VirtualFile file = myClass.getContainingFile().getVirtualFile();
|
|
move(file, dirTo);
|
|
};
|
|
executeCommand("Move class to a new dir", command);
|
|
}
|
|
|
|
public void testSCR5784() throws Exception {
|
|
myFile = createFile("Test.java", "abcd efgh ijk");
|
|
myEditor = createEditor(myFile.getVirtualFile());
|
|
|
|
myManager.setOverriddenEditorProvider(new CurrentEditorProvider() {
|
|
@Override
|
|
public @Nullable FileEditor getCurrentEditor(@Nullable Project project) {
|
|
return TextEditorProvider.getInstance().getTextEditor(myEditor);
|
|
}
|
|
});
|
|
|
|
final SelectionModel selectionModel = myEditor.getSelectionModel();
|
|
final CaretModel caretModel = myEditor.getCaretModel();
|
|
final Document document = myEditor.getDocument();
|
|
|
|
selectionModel.setSelection(0, 4);
|
|
caretModel.moveToOffset(4);
|
|
|
|
String text0 = document.getText();
|
|
int caret0 = caretModel.getOffset();
|
|
int selStart0 = selectionModel.getSelectionStart();
|
|
int selEnd0 = selectionModel.getSelectionEnd();
|
|
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> ApplicationManager.getApplication().runWriteAction(() -> {
|
|
selectionModel.removeSelection();
|
|
document.insertString(document.getTextLength(), "abcd");
|
|
selectionModel.setSelection(document.getTextLength() - "abcd".length(), document.getTextLength());
|
|
}), "Command 1", "DndGroup");
|
|
|
|
CommandProcessor.getInstance()
|
|
.executeCommand(myProject, () -> ApplicationManager.getApplication().runWriteAction(() -> document.deleteString(0, "abcd".length())),
|
|
"Command 2", "DndGroup");
|
|
|
|
CommandProcessor.getInstance().executeCommand(myProject, EmptyRunnable.getInstance(), "Command 3", null);
|
|
|
|
String text1 = document.getText();
|
|
int caret1 = caretModel.getOffset();
|
|
int selStart1 = selectionModel.getSelectionStart();
|
|
int selEnd1 = selectionModel.getSelectionEnd();
|
|
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> ApplicationManager.getApplication().runWriteAction(() -> {
|
|
selectionModel.removeSelection();
|
|
document.insertString(4, "abcd");
|
|
selectionModel.setSelection(4, 4 + "abcd".length());
|
|
}), "Command 4", "DndGroup");
|
|
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> ApplicationManager.getApplication()
|
|
.runWriteAction(() -> document.deleteString(document.getTextLength() - "abcd".length(), document.getTextLength())), "Command 5",
|
|
"DndGroup");
|
|
|
|
undo(myEditor);
|
|
|
|
assertEquals(text1, document.getText());
|
|
assertEquals(caret1, caretModel.getOffset());
|
|
assertEquals(selStart1, selectionModel.getSelectionStart());
|
|
assertEquals(selEnd1, selectionModel.getSelectionEnd());
|
|
|
|
undo(myEditor);
|
|
|
|
assertEquals(text0, document.getText());
|
|
assertEquals(caret0, caretModel.getOffset());
|
|
assertEquals(selStart0, selectionModel.getSelectionStart());
|
|
assertEquals(selEnd0, selectionModel.getSelectionEnd());
|
|
}
|
|
|
|
public void testEditorWithSeveralDocumentsUndo() {
|
|
final VirtualFile file1 = createChildData(myRoot, "file1.txt");
|
|
final VirtualFile file2 = createChildData(myRoot, "file2.txt");
|
|
|
|
final Document document1 = FileDocumentManager.getInstance().getDocument(file1);
|
|
final Document document2 = FileDocumentManager.getInstance().getDocument(file2);
|
|
|
|
Mock.MyFileEditor fileEditor = new Mock.MyFileEditor(document1, document2) {
|
|
@Override
|
|
public @NotNull VirtualFile getFile() {
|
|
return file1;
|
|
}
|
|
};
|
|
|
|
UndoManager undoManager = UndoManager.getInstance(myProject);
|
|
|
|
CommandProcessor.getInstance()
|
|
.executeCommand(myProject, () -> ApplicationManager.getApplication().runWriteAction(() -> document2.replaceString(0, 0, "text2")),
|
|
"test_command", null, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION);
|
|
|
|
assertTrue(undoManager.isUndoAvailable(fileEditor));
|
|
|
|
undoManager.undo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("", document1.getText());
|
|
assertEquals("", document2.getText());
|
|
|
|
undoManager.redo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("", document1.getText());
|
|
assertEquals("text2", document2.getText());
|
|
|
|
changeText(document1, "");
|
|
changeText(document2, "");
|
|
|
|
changeText(document1, "text1");
|
|
changeText(document2, "text2");
|
|
assertTrue(undoManager.isUndoAvailable(fileEditor));
|
|
|
|
undoManager.undo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("text1", document1.getText());
|
|
assertEquals("", document2.getText());
|
|
|
|
undoManager.undo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("", document1.getText());
|
|
assertEquals("", document2.getText());
|
|
|
|
undoManager.redo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("text1", document1.getText());
|
|
assertEquals("", document2.getText());
|
|
|
|
undoManager.redo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("text1", document1.getText());
|
|
assertEquals("text2", document2.getText());
|
|
|
|
//check documents changes backward
|
|
|
|
changeText(document1, "");
|
|
changeText(document2, "");
|
|
|
|
changeText(document2, "text2");
|
|
changeText(document1, "text1");
|
|
assertTrue(undoManager.isUndoAvailable(fileEditor));
|
|
|
|
undoManager.undo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("text2", document2.getText());
|
|
assertEquals("", document1.getText());
|
|
|
|
undoManager.undo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("", document1.getText());
|
|
assertEquals("", document2.getText());
|
|
|
|
undoManager.redo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("", document1.getText());
|
|
assertEquals("text2", document2.getText());
|
|
|
|
undoManager.redo(fileEditor);
|
|
assertFalse(myConfirmationWasRequested);
|
|
assertEquals("text1", document1.getText());
|
|
assertEquals("text2", document2.getText());
|
|
}
|
|
|
|
public void testMergingSeveralDocumentCommands() {
|
|
final VirtualFile[] files = new VirtualFile[3];
|
|
files[0] = createChildData(myRoot, "f1.txt");
|
|
files[1] = createChildData(myRoot, "f2.txt");
|
|
files[2] = createChildData(myRoot, "f3.txt");
|
|
|
|
executeCommand("command", "ID", () -> setDocumentText(files[0], "text1"));
|
|
|
|
executeCommand("command", "ID", () -> setDocumentText(files[1], "text2"));
|
|
|
|
executeCommand("command", "ID", () -> setDocumentText(files[2], "text3"));
|
|
|
|
//assertGlobalUndoIsAvailable();
|
|
|
|
assertUndoIsAvailable(getEditor(files[0]));
|
|
assertUndoIsAvailable(getEditor(files[1]));
|
|
assertUndoIsAvailable(getEditor(files[2]));
|
|
|
|
//globalUndo();
|
|
undo(getEditor(files[0]));
|
|
|
|
assertEquals("", getDocumentText(files[0]));
|
|
assertEquals("", getDocumentText(files[1]));
|
|
assertEquals("", getDocumentText(files[2]));
|
|
|
|
//assertGlobalUndoNotAvailable();
|
|
assertUndoNotAvailable(getEditor(files[0]));
|
|
assertUndoNotAvailable(getEditor(files[1]));
|
|
assertUndoNotAvailable(getEditor(files[2]));
|
|
|
|
//assertGlobalRedoIsAvailable();
|
|
assertRedoIsAvailable(getEditor(files[0]));
|
|
assertRedoIsAvailable(getEditor(files[1]));
|
|
assertRedoIsAvailable(getEditor(files[2]));
|
|
|
|
//globalRedo();
|
|
redo(getEditor(files[0]));
|
|
|
|
assertEquals("text1", getDocumentText(files[0]));
|
|
assertEquals("text2", getDocumentText(files[1]));
|
|
assertEquals("text3", getDocumentText(files[2]));
|
|
}
|
|
|
|
public void testUndoConfirmationPolicy() {
|
|
doTest(UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION, false);
|
|
doTest(UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION, UndoConfirmationPolicy.REQUEST_CONFIRMATION, true);
|
|
doTest(UndoConfirmationPolicy.REQUEST_CONFIRMATION, UndoConfirmationPolicy.REQUEST_CONFIRMATION, true);
|
|
doTest(UndoConfirmationPolicy.REQUEST_CONFIRMATION, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION, true);
|
|
|
|
doTest(UndoConfirmationPolicy.DEFAULT, UndoConfirmationPolicy.DEFAULT, true);
|
|
doTest(UndoConfirmationPolicy.REQUEST_CONFIRMATION, UndoConfirmationPolicy.DEFAULT, true);
|
|
doTest(UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION, UndoConfirmationPolicy.DEFAULT, false);
|
|
doTest(UndoConfirmationPolicy.DEFAULT, UndoConfirmationPolicy.REQUEST_CONFIRMATION, true);
|
|
doTest(UndoConfirmationPolicy.DEFAULT, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION, false);
|
|
}
|
|
|
|
private void doTest(UndoConfirmationPolicy policy1, UndoConfirmationPolicy policy2, boolean expected) {
|
|
final VirtualFile file1 = createChildData(myRoot, "file1.txt");
|
|
final VirtualFile file2 = createChildData(myRoot, "file2.txt");
|
|
|
|
final Document document1 = FileDocumentManager.getInstance().getDocument(file1);
|
|
final Document document2 = FileDocumentManager.getInstance().getDocument(file2);
|
|
|
|
String groupId = "ID";
|
|
|
|
changeText(document1, "text1", policy1, groupId);
|
|
changeText(document2, "text2", policy2, groupId);
|
|
|
|
UndoManager undoManager = UndoManager.getInstance(myProject);
|
|
|
|
Mock.MyFileEditor fileEditor = new Mock.MyFileEditor(document1, document2) {
|
|
@Override
|
|
public @NotNull VirtualFile getFile() {
|
|
return file1;
|
|
}
|
|
};
|
|
|
|
assertTrue(undoManager.isUndoAvailable(fileEditor));
|
|
|
|
undoManager.undo(fileEditor);
|
|
|
|
assertEquals(expected, myConfirmationWasRequested);
|
|
|
|
assertEquals("", document1.getText());
|
|
assertEquals("", document2.getText());
|
|
|
|
delete(file1);
|
|
delete(file2);
|
|
|
|
myConfirmationWasRequested = false;
|
|
}
|
|
|
|
private void changeText(final Document document1, final String text) {
|
|
changeText(document1, text, UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION, null);
|
|
}
|
|
|
|
private void changeText(final Document document1, final String text, UndoConfirmationPolicy policy, String groupId) {
|
|
CommandProcessor.getInstance().executeCommand(myProject, () -> ApplicationManager.getApplication()
|
|
.runWriteAction(() -> document1.replaceString(0, document1.getTextLength(), text)), "test_command", groupId, policy);
|
|
}
|
|
|
|
private void assertFileExists(String fileName, String content) {
|
|
assertFileExists(myRoot, fileName, content);
|
|
}
|
|
|
|
private static void assertFileExists(VirtualFile dir, String fileName, String content) {
|
|
assertFileExists(dir, fileName);
|
|
assertEquals(content, getDocumentText(findFile(fileName, dir)));
|
|
}
|
|
|
|
private static void assertFileDoesNotExist(String fileName, VirtualFile dir) {
|
|
assertNull(findFile(fileName, dir));
|
|
}
|
|
|
|
private static void assertFileExists(VirtualFile dir, String fileName) {
|
|
StoreUtil.saveDocumentsAndProjectsAndApp(false);
|
|
assertNotNull(findFile(fileName, dir));
|
|
}
|
|
|
|
protected static VirtualFile findFile(String className, VirtualFile dir) {
|
|
return dir.findChild(className + ".java");
|
|
}
|
|
|
|
private static void setDocumentText(final VirtualFile f, final String text) {
|
|
ApplicationManager.getApplication().runWriteAction(() -> FileDocumentManager.getInstance().getDocument(f).setText(text));
|
|
}
|
|
|
|
private static String getDocumentText(VirtualFile f) {
|
|
return FileDocumentManager.getInstance().getDocument(f).getText();
|
|
}
|
|
|
|
private void checkAllFilesDeleted() {
|
|
assertEquals(0, myRoot.getChildren().length);
|
|
}
|
|
|
|
private Editor openEditor(String fileName) {
|
|
return openEditor(myRoot.findChild(fileName));
|
|
}
|
|
|
|
private Editor openEditor(VirtualFile file) {
|
|
assertNotNull(file);
|
|
return FileEditorManager.getInstance(myProject).openTextEditor(new OpenFileDescriptor(myProject, file), false);
|
|
}
|
|
|
|
protected void deleteClass() {
|
|
executeCommand("Delete Class", () -> ApplicationManager.getApplication().runWriteAction(() -> myClass.delete()));
|
|
}
|
|
|
|
protected void createClass(@NonNls final String name) {
|
|
createClass(name, myRoot);
|
|
}
|
|
|
|
protected PsiJavaFile createClass(final String name, final VirtualFile dir) {
|
|
executeCommand("Create Class" + name, () -> {
|
|
ApplicationManager.getApplication().runWriteAction(() -> {
|
|
myClass = JavaDirectoryService.getInstance().createClass(myPsiManager.findDirectory(dir), name);
|
|
myClass = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myClass);
|
|
});
|
|
|
|
myContainingFile = (PsiJavaFile)myClass.getContainingFile();
|
|
});
|
|
return myContainingFile;
|
|
}
|
|
}
|