package de.neitzel.fxdesigner.intellij; import com.intellij.testFramework.fixtures.BasePlatformTestCase; import de.neitzel.fxdesigner.contracts.*; import java.nio.file.*; import java.util.*; public final class IntellijDocumentsTest extends BasePlatformTestCase { public void testNativeDocumentTransactionUndoAndIndependentHandles() throws Exception { Path file = Files.createTempFile("designer-document-", ".fxml"); Files.writeString(file, "abcdef"); com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess.allowRootAccess( getTestRootDisposable(), file.toRealPath().toString()); try (var docs = new IntellijDocuments(getProject())) { var a = docs.open(file.toUri()).toCompletableFuture().get(); var b = docs.open(file.toUri()).toCompletableFuture().get(); var initial = a.snapshot().toCompletableFuture().get(); var changed = a.apply( new EditBatch( initial.revision(), "Component properties", List.of( new EditBatch.Replacement(0, 1, "A"), new EditBatch.Replacement(5, 6, "F")))) .toCompletableFuture() .get(); assertEquals("AbcdeF", b.snapshot().toCompletableFuture().get().text()); a.close(); var undone = b.undo().toCompletableFuture().get(); assertEquals("abcdef", undone.text()); assertTrue(undone.revision() > changed.revision()); try { b.apply(new EditBatch(initial.revision(), "stale", List.of())).toCompletableFuture().get(); fail("Stale edit accepted"); } catch (java.util.concurrent.ExecutionException expected) { assertInstanceOf(expected.getCause(), ConcurrentModificationException.class); } var redone = b.redo().toCompletableFuture().get(); assertEquals("AbcdeF", redone.text()); b.save(redone.revision()).toCompletableFuture().get(); assertEquals("AbcdeF", Files.readString(file)); b.close(); } finally { Files.deleteIfExists(file); } } public void testExternalChangesDoNotLoseDraft() throws Exception { Path file = Files.createTempFile("designer-conflict-", ".fxml"); Files.writeString(file, "old"); com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess.allowRootAccess( getTestRootDisposable(), file.toRealPath().toString()); try (var docs = new IntellijDocuments(getProject())) { var d = docs.open(file.toUri()).toCompletableFuture().get(); var changed = d.apply(new EditBatch(0, "edit", List.of(new EditBatch.Replacement(0, 3, "draft")))) .toCompletableFuture() .get(); Files.writeString(file, "external"); try { d.save(changed.revision()).toCompletableFuture().get(); fail("Conflict accepted"); } catch (java.util.concurrent.ExecutionException expected) { assertInstanceOf(expected.getCause(), ConcurrentModificationException.class); } assertEquals("draft", d.snapshot().toCompletableFuture().get().text()); assertEquals("external", Files.readString(file)); d.close(); } finally { Files.deleteIfExists(file); } } public void testKeepsNativeDocumentOpenedThroughAlias() throws Exception { Path directory = Files.createTempDirectory("designer-alias-"); Path real = directory.resolve("real"); Files.createDirectory(real); Path alias = directory.resolve("alias"); Files.createSymbolicLink(alias, real); Path file = real.resolve("View.fxml"); Files.writeString(file, "before"); com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess.allowRootAccess( getTestRootDisposable(), directory.toRealPath().toString(), directory.toString()); var virtual = com.intellij.openapi.vfs.LocalFileSystem.getInstance() .refreshAndFindFileByNioFile(alias.resolve("View.fxml")); var nativeDocument = com.intellij.openapi.fileEditor.FileDocumentManager.getInstance().getDocument(virtual); try (var docs = new IntellijDocuments(getProject()); var handle = docs.open(alias.resolve("View.fxml").toUri()).toCompletableFuture().get()) { var before = handle.snapshot().toCompletableFuture().get(); var after = handle .apply( new EditBatch( before.revision(), "Alias edit", List.of(new EditBatch.Replacement(0, before.text().length(), "after")))) .toCompletableFuture() .get(); assertEquals("after", nativeDocument.getText()); assertEquals(file.toRealPath().toUri(), after.document()); handle.save(after.revision()).toCompletableFuture().get(); } finally { Files.deleteIfExists(alias); Files.deleteIfExists(file); Files.deleteIfExists(real); Files.deleteIfExists(directory); } } public void testCleanExternalReloadAndRangeCaretPreservation() throws Exception { Path file = Files.createTempFile("designer-reload-", ".fxml"); Files.writeString(file, "prefix value suffix"); com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess.allowRootAccess( getTestRootDisposable(), file.toRealPath().toString()); try (var docs = new IntellijDocuments(getProject()); var handle = docs.open(file.toUri()).toCompletableFuture().get()) { var initial = handle.snapshot().toCompletableFuture().get(); Files.writeString(file, "prefix external suffix"); var current = handle.snapshot().toCompletableFuture().get(); assertEquals("prefix external suffix", current.text()); assertFalse(current.dirty()); assertTrue(initial.revision() != current.revision()); var vf = com.intellij.openapi.vfs.LocalFileSystem.getInstance().refreshAndFindFileByNioFile(file); var document = com.intellij.openapi.fileEditor.FileDocumentManager.getInstance().getDocument(vf); var editor = com.intellij.openapi.editor.EditorFactory.getInstance() .createEditor(document, getProject()); try { editor.getCaretModel().moveToOffset(current.text().length()); editor .getSelectionModel() .setSelection(current.text().indexOf("suffix"), current.text().length()); var updated = handle .apply( new EditBatch( current.revision(), "Range edit", List.of(new EditBatch.Replacement(7, 15, "X")))) .toCompletableFuture() .get(); assertEquals("prefix X suffix", updated.text()); assertEquals("suffix", editor.getSelectionModel().getSelectedText()); assertEquals(updated.text().length(), editor.getCaretModel().getOffset()); com.intellij.openapi.fileEditor.FileDocumentManager.getInstance().saveDocument(document); var saved = handle.snapshot().toCompletableFuture().get(); assertEquals(updated.revision(), saved.revision()); assertFalse(saved.dirty()); handle.save(updated.revision()).toCompletableFuture().get(); } finally { com.intellij.openapi.editor.EditorFactory.getInstance().releaseEditor(editor); } } finally { Files.deleteIfExists(file); } } }