| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.openapi.editor.impl; |
| |
| import com.intellij.openapi.application.Result; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.command.impl.UndoManagerImpl; |
| import com.intellij.openapi.command.undo.UndoManager; |
| import com.intellij.openapi.editor.*; |
| import com.intellij.openapi.editor.event.DocumentAdapter; |
| import com.intellij.openapi.editor.event.DocumentEvent; |
| import com.intellij.openapi.editor.ex.DocumentEx; |
| import com.intellij.openapi.editor.ex.MarkupModelEx; |
| import com.intellij.openapi.editor.ex.RangeHighlighterEx; |
| import com.intellij.openapi.editor.ex.RangeMarkerEx; |
| import com.intellij.openapi.editor.markup.HighlighterTargetArea; |
| import com.intellij.openapi.editor.markup.MarkupModel; |
| import com.intellij.openapi.editor.markup.RangeHighlighter; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.ThrowableComputable; |
| import com.intellij.openapi.util.Trinity; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.impl.PsiDocumentManagerImpl; |
| import com.intellij.psi.impl.PsiToDocumentSynchronizer; |
| import com.intellij.testFramework.LeakHunter; |
| import com.intellij.testFramework.LightPlatformTestCase; |
| import com.intellij.testFramework.PlatformTestUtil; |
| import com.intellij.testFramework.Timings; |
| import com.intellij.util.CommonProcessors; |
| import com.intellij.util.ThrowableRunnable; |
| import com.intellij.util.containers.WeakList; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.*; |
| |
| /** |
| * @author mike |
| */ |
| public class RangeMarkerTest extends LightPlatformTestCase { |
| private PsiDocumentManagerImpl documentManager; |
| private PsiToDocumentSynchronizer synchronizer; |
| private Document document; |
| private PsiFile psiFile; |
| |
| @Override |
| protected void runTest() throws Throwable { |
| if (getTestName(false).contains("NoVerify")) { |
| super.runTest(); |
| return; |
| } |
| boolean oldVerify = RedBlackTree.VERIFY; |
| RedBlackTree.VERIFY = !isPerformanceTest(); |
| final Throwable[] ex = {null}; |
| try { |
| if (getTestName(false).contains("NoCommand")) { |
| super.runTest(); |
| return; |
| } |
| WriteCommandAction.runWriteCommandAction(getProject(), new ThrowableComputable<Void, Throwable>() { |
| @Override |
| public Void compute() throws Throwable { |
| RangeMarkerTest.super.runTest(); |
| return null; |
| } |
| }); |
| } |
| finally { |
| RedBlackTree.VERIFY = oldVerify; |
| } |
| |
| if (ex[0] != null) throw ex[0]; |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(getProject()); |
| synchronizer = documentManager.getSynchronizer(); |
| } |
| |
| public void testCreation() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(5, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testDeleteBeforeStart() throws Exception { |
| RangeMarker marker = createMarker("01[234]56789"); |
| |
| marker.getDocument().deleteString(0, 1); |
| |
| assertEquals(1, marker.getStartOffset()); |
| assertEquals(4, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testInsertIntoRange() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().insertString(4, "xxx"); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(8, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testInsertIntoPoint() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 2); |
| |
| marker.getDocument().insertString(2, "xxx"); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(2, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testDeletePoint() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 2); |
| |
| marker.getDocument().deleteString(1, 3); |
| |
| assertFalse(marker.isValid()); |
| } |
| |
| public void testDeleteRangeInside() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 1, 7); |
| |
| marker.getDocument().deleteString(2, 5); |
| |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testReplaceRangeToSingleChar() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 1, 7); |
| |
| marker.getDocument().replaceString(2, 5, " "); |
| |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testReplaceWholeRange() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 1, 7); |
| marker.getDocument().replaceString(1, 7, "abc"); |
| assertValidMarker(marker, 1, 4); |
| } |
| |
| public void testUpdateInvalid() throws Exception { |
| RangeMarker marker = createMarker("01[]23456789"); |
| |
| marker.getDocument().deleteString(1, 3); |
| assertFalse(marker.isValid()); |
| |
| marker.getDocument().insertString(2, "xxx"); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(2, marker.getEndOffset()); |
| assertFalse(marker.isValid()); |
| } |
| |
| public void testInsertAfterEnd() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().insertString(6, "xxx"); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(5, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| |
| public void testDeleteEndPart() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().deleteString(4, 6); |
| |
| assertValidMarker(marker, 2, 4); |
| } |
| |
| public void testDeleteStartPart() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().deleteString(0, 4); |
| |
| assertValidMarker(marker, 0, 1); |
| } |
| |
| public void testReplaceStartPartInvalid() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().replaceString(0, 4, "xxxx"); |
| |
| assertValidMarker(marker, 4, 5); |
| } |
| |
| public void testDeleteFirstChar() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 0, 5); |
| |
| marker.getDocument().deleteString(0, 1); |
| |
| assertValidMarker(marker, 0, 4); |
| } |
| |
| public void testInsertBeforeStart() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().insertString(0, "xxx"); |
| |
| assertEquals(5, marker.getStartOffset()); |
| assertEquals(8, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testInsertIntoStart() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().insertString(2, "xxx"); |
| |
| assertValidMarker(marker, 5, 8); |
| } |
| |
| public void testInsertIntoStartExpandToLeft() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.setGreedyToLeft(true); |
| |
| marker.getDocument().insertString(2, "xxx"); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(8, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testInsertIntoEnd() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().insertString(5, "xxx"); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(5, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testInsertIntoEndExpandRight() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.setGreedyToRight(true); |
| |
| marker.getDocument().insertString(5, "xxx"); |
| |
| assertEquals(2, marker.getStartOffset()); |
| assertEquals(8, marker.getEndOffset()); |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testNoNegative() throws Exception { |
| RangeMarker marker = createMarker("package safd;\n\n[import javax.swing.JPanel;]\nimport java.util.ArrayList;\n\nclass T{}"); |
| |
| marker.getDocument() |
| .replaceString(15, 15 + "import javax.swing.JPanel;\nimport java.util.ArrayList;".length(), "import java.util.ArrayList;"); |
| |
| assertEquals(15, marker.getStartOffset()); |
| } |
| |
| public void testReplaceRightIncludingFirstChar() throws Exception { |
| String s = "12345\n \n12345"; |
| RangeMarker marker = createMarker(s, 6, 8); |
| |
| marker.getDocument().replaceString(0, s.length(), s.replaceAll(" ", "")); |
| |
| assertValidMarker(marker, 6, 7); |
| } |
| |
| public void testDeleteRightPart() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().deleteString(4, 6); |
| |
| assertValidMarker(marker, 2, 4); |
| } |
| |
| public void testDeleteRightPart2() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().deleteString(4, 5); |
| |
| assertValidMarker(marker, 2, 4); |
| } |
| |
| public void testReplaceRightPartInvalid() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().replaceString(4, 6, "xxx"); |
| |
| assertValidMarker(marker, 2, 4); |
| } |
| |
| public void testDeleteWholeRange() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| marker.getDocument().deleteString(1, 6); |
| assertFalse(marker.isValid()); |
| } |
| |
| public void testDeleteExactRange() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().deleteString(2, 5); |
| assertValidMarker(marker, 2, 2); |
| } |
| |
| public void testDeleteJustBeforeStart() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().deleteString(0, 2); |
| assertValidMarker(marker, 0, 3); |
| } |
| |
| public void testDeleteRightAfterEnd() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 2); |
| |
| marker.getDocument().deleteString(2, 5); |
| assertValidMarker(marker, 2, 2); |
| } |
| |
| public void testReplacementWithOldTextOverlap() throws Exception { |
| RangeMarker marker = createMarker("0123456789", 2, 5); |
| |
| marker.getDocument().replaceString(0, 10, "0123456789"); |
| assertValidMarker(marker, 2, 5); |
| } |
| |
| // Psi -> Document synchronization |
| |
| public void testPsi2Doc1() throws Exception { |
| StringBuilder buffer = new StringBuilder("0123456789"); |
| RangeMarker marker = createMarker(buffer.toString(), 2, 5); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| |
| synchronizer.insertString(document, 3, "a"); |
| buffer.insert(3, "a"); |
| |
| synchronizer.commitTransaction(this.document); |
| |
| assertEquals(buffer.toString(), document.getText()); |
| |
| assertValidMarker(marker, 2, 6); |
| } |
| |
| public void testDocSynchronizerPrefersLineBoundaryChanges() throws Exception { |
| String text = "import java.awt.List;\n" + |
| "[import java.util.ArrayList;\n]" + |
| "import java.util.HashMap;\n" + |
| "import java.util.Map;"; |
| RangeMarker marker = createMarker(text); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| |
| String newText = StringUtil.replaceSubstring(document.getText(), TextRange.create(marker), ""); |
| synchronizer.replaceString(document, 0, document.getTextLength(), newText); |
| |
| final List<DocumentEvent> events = new ArrayList<DocumentEvent>(); |
| document.addDocumentListener(new DocumentAdapter() { |
| @Override |
| public void documentChanged(DocumentEvent e) { |
| events.add(e); |
| } |
| }); |
| synchronizer.commitTransaction(document); |
| |
| assertEquals(newText, document.getText()); |
| DocumentEvent event = assertOneElement(events); |
| assertEquals("DocumentEventImpl[myOffset=22, myOldLength=28, myNewLength=0, myOldString='import java.util.ArrayList;\n', myNewString=''].", event.toString()); |
| } |
| |
| public void testPsi2DocReplaceAfterAdd() throws Exception { |
| StringBuilder buffer = new StringBuilder("0123456789"); |
| RangeMarker marker = createMarker(buffer.toString(), 2, 5); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| |
| synchronizer.insertString(document, 1, "a"); |
| buffer.insert(1, "a"); |
| |
| synchronizer.replaceString(document, 3, 4, "a"); |
| buffer.replace(3, 4, "a"); |
| |
| synchronizer.commitTransaction(document); |
| |
| assertEquals(buffer.toString(), document.getText()); |
| |
| assertValidMarker(marker, 3, 6); |
| } |
| |
| public void testPsi2DocMergeReplaceAfterAdd() throws Exception { |
| StringBuilder buffer = new StringBuilder("0123456789"); |
| RangeMarker marker = createMarker(buffer.toString(), 2, 5); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| |
| synchronizer.insertString(document, 1, "a"); |
| buffer.insert(1, "a"); |
| |
| synchronizer.replaceString(document, 3, 4, "a"); |
| buffer.replace(3, 4, "a"); |
| |
| synchronizer.replaceString(document, 3, 5, "bb"); |
| buffer.replace(3, 5, "bb"); |
| final PsiToDocumentSynchronizer.DocumentChangeTransaction transaction = synchronizer.getTransaction(document); |
| final Set<Pair<PsiToDocumentSynchronizer.MutableTextRange, StringBuffer>> affectedFragments = transaction.getAffectedFragments(); |
| assertEquals(affectedFragments.size(), 2); |
| |
| synchronizer.commitTransaction(document); |
| |
| assertEquals(buffer.toString(), document.getText()); |
| |
| assertValidMarker(marker, 3, 6); |
| } |
| |
| public void testPsi2DocMergeReplaceWithMultipleAdditions() throws Exception { |
| StringBuilder buffer = new StringBuilder("0123456789"); |
| RangeMarker marker = createMarker(buffer.toString(), 2, 5); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| |
| synchronizer.replaceString(document, 0, 10, "0"); |
| buffer.replace(0, 10, "0"); |
| |
| for (int i = 1; i < 10; i++) { |
| synchronizer.insertString(document, i, "" + i); |
| buffer.insert(i, "" + i); |
| } |
| final PsiToDocumentSynchronizer.DocumentChangeTransaction transaction = synchronizer.getTransaction(document); |
| final Set<Pair<PsiToDocumentSynchronizer.MutableTextRange, StringBuffer>> affectedFragments = transaction.getAffectedFragments(); |
| assertEquals(1, affectedFragments.size()); |
| |
| synchronizer.commitTransaction(document); |
| |
| |
| assertEquals(buffer.toString(), document.getText()); |
| |
| assertValidMarker(marker, 2, 5); |
| } |
| |
| public void testPsi2DocMergeMultipleAdditionsWithReplace() throws Exception { |
| StringBuilder buffer = new StringBuilder("0123456789"); |
| RangeMarker marker = createMarker(buffer.toString(), 2, 5); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| final PsiToDocumentSynchronizer.DocumentChangeTransaction transaction = synchronizer.getTransaction(document); |
| final Set<Pair<PsiToDocumentSynchronizer.MutableTextRange, StringBuffer>> affectedFragments = transaction.getAffectedFragments(); |
| |
| |
| for (int i = 0; i < 10; i++) { |
| synchronizer.insertString(document, i, "" + i); |
| buffer.insert(i, "" + i); |
| } |
| |
| assertEquals(1, affectedFragments.size()); |
| synchronizer.replaceString(document, 0, 20, "0123456789"); |
| buffer.replace(0, 20, "0123456789"); |
| |
| assertEquals(1, affectedFragments.size()); |
| |
| synchronizer.commitTransaction(document); |
| |
| assertEquals(buffer.toString(), document.getText()); |
| |
| assertValidMarker(marker, 2, 5); |
| } |
| |
| public void testPsi2DocSurround() throws Exception { |
| StringBuilder buffer = new StringBuilder("0123456789"); |
| RangeMarker marker = createMarker(buffer.toString(), 2, 5); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| |
| synchronizer.replaceString(document, 3, 5, "3a4"); |
| buffer.replace(3, 5, "3a4"); |
| |
| synchronizer.insertString(document, 3, "b"); |
| buffer.insert(3, "b"); |
| |
| synchronizer.insertString(document, 7, "d"); |
| buffer.insert(7, "d"); |
| |
| final PsiToDocumentSynchronizer.DocumentChangeTransaction transaction = synchronizer.getTransaction(document); |
| final Set<Pair<PsiToDocumentSynchronizer.MutableTextRange, StringBuffer>> affectedFragments = transaction.getAffectedFragments(); |
| assertEquals(3, affectedFragments.size()); |
| |
| synchronizer.commitTransaction(document); |
| |
| assertEquals(buffer.toString(), document.getText()); |
| |
| assertValidMarker(marker, 2, 7); |
| } |
| |
| public void testPsi2DocForwardRangesChanges() throws Exception { |
| StringBuilder buffer = new StringBuilder("0123456789"); |
| RangeMarker marker = createMarker(buffer.toString(), 2, 5); |
| synchronizer.startTransaction(getProject(), document, psiFile); |
| |
| synchronizer.replaceString(document, 4, 5, "3a4"); |
| buffer.replace(4, 5, "3a4"); |
| |
| synchronizer.insertString(document, 7, "b"); |
| buffer.insert(7, "b"); |
| |
| synchronizer.insertString(document, 1, "b"); |
| buffer.insert(1, "b"); |
| |
| synchronizer.commitTransaction(document); |
| |
| assertEquals(buffer.toString(), document.getText()); |
| |
| assertValidMarker(marker, 3, 8); |
| } |
| |
| private static void assertValidMarker(@NotNull RangeMarker marker, int start, int end) { |
| assertTrue(marker.isValid()); |
| assertEquals(start, marker.getStartOffset()); |
| assertEquals(end, marker.getEndOffset()); |
| } |
| |
| public void testNested() { |
| RangeMarker marker1 = createMarker("0[12345678]9"); |
| Document document = marker1.getDocument(); |
| RangeMarker marker2 = document.createRangeMarker(2, 5); |
| RangeMarker marker3 = document.createRangeMarker(3, 4); |
| document.insertString(0, "x"); |
| |
| assertEquals(2, marker1.getStartOffset()); |
| assertEquals(3, marker2.getStartOffset()); |
| assertEquals(4, marker3.getStartOffset()); |
| } |
| |
| public void testNestedAfter() { |
| RangeMarker marker1 = createMarker("0[12345678]90123"); |
| Document document = marker1.getDocument(); |
| RangeMarker marker2 = document.createRangeMarker(2, 5); |
| RangeMarker marker3 = document.createRangeMarker(3, 4); |
| document.insertString(10, "x"); |
| |
| assertEquals(1, marker1.getStartOffset()); |
| assertEquals(2, marker2.getStartOffset()); |
| assertEquals(3, marker3.getStartOffset()); |
| } |
| |
| public void testNested3() { |
| RangeMarker marker1 = createMarker("01[23]4567890123"); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| RangeMarker marker2 = document.createRangeMarker(9, 11); |
| RangeMarker marker3 = document.createRangeMarker(1, 12); |
| marker3.dispose(); |
| |
| document.deleteString(marker1.getEndOffset(), marker2.getStartOffset()); |
| } |
| |
| public void testBranched() { |
| RangeMarker marker1 = createMarker("01234567890123456", 0, 1); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| RangeMarker marker2 = document.createRangeMarker(2, 3); |
| RangeMarker marker3 = document.createRangeMarker(4, 5); |
| RangeMarker marker4 = document.createRangeMarker(6, 7); |
| RangeMarker marker5 = document.createRangeMarker(8, 9); |
| RangeMarker marker6 = document.createRangeMarker(10, 11); |
| RangeMarker marker7 = document.createRangeMarker(12, 13); |
| RangeMarker marker8 = document.createRangeMarker(14, 15); |
| document.deleteString(1, 2); |
| } |
| |
| public void testDevourMarkerWithDeletion() { |
| RangeMarker marker1 = createMarker("012345[67890123456]7"); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| document.deleteString(1, document.getTextLength()); |
| } |
| |
| public void testLL() { |
| RangeMarker marker1 = createMarker("012345678901234567", 5,6); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| document.createRangeMarker(4, 5); |
| document.createRangeMarker(6, 7); |
| document.createRangeMarker(0, 4); |
| document.deleteString(1, 2); |
| |
| document.createRangeMarker(0, 7); |
| document.createRangeMarker(0, 7); |
| } |
| |
| public void testSwap() { |
| RangeMarkerEx marker1 = createMarker("012345678901234567", 5,6); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| document.createRangeMarker(3, 5); |
| document.createRangeMarker(6, 7); |
| document.createRangeMarker(4, 4); |
| marker1.dispose(); |
| } |
| |
| public void testX() { |
| RangeMarkerEx marker1 = createMarker(StringUtil.repeatSymbol(' ', 10), 3,6); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| document.createRangeMarker(2, 3); |
| document.createRangeMarker(3, 8); |
| document.createRangeMarker(7, 9); |
| RangeMarkerEx r1 = (RangeMarkerEx)document.createRangeMarker(6, 8); |
| |
| r1.dispose(); |
| marker1.dispose(); |
| } |
| |
| private static List<RangeMarker> add(DocumentEx document, int... offsets) { |
| List<RangeMarker> result = new ArrayList<RangeMarker>(); |
| for (int i=0; i<offsets.length; i+=2) { |
| int start = offsets[i]; |
| int end = offsets[i+1]; |
| RangeMarker m = document.createRangeMarker(start, end); |
| result.add(m); |
| } |
| return result; |
| } |
| private static void delete(List<RangeMarker> mm, int... indexes) { |
| for (int index : indexes) { |
| RangeMarker m = mm.get(index); |
| m.dispose(); |
| } |
| } |
| public void testX2() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 2,9, 0,0, 7,7 |
| ); |
| delete(mm, 0); |
| } |
| public void testX3() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 1,9, 8,8, 8,8, 0,5, 4,5 |
| ); |
| delete(mm, 0); |
| } |
| |
| public void _testRandomAddRemove() { |
| int N = 100; |
| |
| for (int ti=0; ;ti++) { |
| if (ti%10000 ==0) System.out.println(ti); |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', N)); |
| |
| Random gen = new Random(); |
| List<Pair<RangeMarker, TextRange>> adds = new ArrayList<Pair<RangeMarker, TextRange>>(); |
| List<Pair<RangeMarker, TextRange>> dels = new ArrayList<Pair<RangeMarker, TextRange>>(); |
| |
| |
| try { |
| for (int i = 0; i < 30; i++) { |
| int x = gen.nextInt(N); |
| int y = x + gen.nextInt(N - x); |
| if (gen.nextBoolean()) { |
| x = 0; |
| y = document.getTextLength(); |
| } |
| RangeMarkerEx r = (RangeMarkerEx)document.createRangeMarker(x, y); |
| adds.add(Pair.create((RangeMarker)r, TextRange.create(r))); |
| } |
| List<Pair<RangeMarker, TextRange>> candidates = new ArrayList<Pair<RangeMarker, TextRange>>(adds); |
| while (!candidates.isEmpty()) { |
| int size = candidates.size(); |
| int x = gen.nextInt(size); |
| Pair<RangeMarker, TextRange> c = candidates.remove(x); |
| RangeMarkerEx r = (RangeMarkerEx)c.first; |
| assertEquals(size-1, candidates.size()); |
| dels.add(c); |
| r.dispose(); |
| } |
| } |
| catch (AssertionError e) { |
| String s= "adds: "; |
| for (Pair<RangeMarker, TextRange> c : adds) { |
| TextRange t = c.second; |
| s += t.getStartOffset() + "," + t.getEndOffset() + ", "; |
| } |
| s += "\ndels: "; |
| |
| for (Pair<RangeMarker, TextRange> c : dels) { |
| int index = adds.indexOf(c); |
| assertSame(c, adds.get(index)); |
| s += index + ", "; |
| } |
| System.err.println(s); |
| throw e; |
| } |
| } |
| |
| } |
| |
| private static void edit(DocumentEx document, int... offsets) { |
| for (int i = 0; i < offsets.length; i+=3) { |
| int offset = offsets[i]; |
| int oldlength = offsets[i+1]; |
| int newlength = offsets[i+2]; |
| |
| document.replaceString(offset, offset + oldlength, StringUtil.repeatSymbol(' ', newlength)); |
| } |
| } |
| |
| public void testE1() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 3,5, 0,1, 9,9 |
| ); |
| edit(document, 3,6,0); |
| delete(mm, 0); |
| } |
| |
| public void testE2() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 0,3, 6,9, 8,8 |
| ); |
| edit(document, 0,3,0); |
| delete(mm, 0); |
| } |
| |
| public void testE3() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 4,5, 6,8, 3,4, 4,9, 2,9 |
| ); |
| edit(document, 4,6,0); |
| delete(mm, 0); |
| } |
| |
| public void testE4() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 3,5, 5,6, 4,8, 6,9, 8,9 |
| ); |
| edit(document, 6,0,0, 3,0,2); |
| delete(mm, 1,0); |
| } |
| public void testE5() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 9,9, 4,4, 1,7, 7,7, 4,7 |
| ); |
| edit(document, 1,5,0); |
| delete(mm, 3); |
| } |
| |
| public void testE6() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 4,8, 4,4, 4,9, 0,2, 6,8 |
| ); |
| edit(document, 3,2,0); |
| } |
| |
| public void testE7() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 6,7, 0,3, 3,6, 5,9, 2,9 |
| ); |
| edit(document, 5,2,0); |
| } |
| |
| public void testE8() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 5,5, 8,8, 1,3, 3,9 |
| ); |
| edit(document, 4,3,0); |
| } |
| |
| public void testE9() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 4,5, 9,9, 1,2, 0,3 |
| ); |
| edit(document, 0,3,0); |
| } |
| |
| public void testE10() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 9,9, 6,8, 8,8, 5,9 |
| ); |
| edit(document, 2,6,0, 2,0,4); |
| } |
| |
| public void testE11() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 9,9, 7,7, 1,6, 3,7 |
| ); |
| //edit(document, 0,0,0); |
| delete(mm, 1); |
| } |
| public void testE12() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = |
| add(document, 3,3, 8,8, 5,5, 5,6 |
| ); |
| edit(document, 2,0,2); |
| delete(mm, 2); |
| } |
| |
| public void testE13() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10)); |
| List<RangeMarker> mm = add(document, 5,9, 9,9, 7,7, 6,8); |
| edit(document, 2,1,0); |
| delete(mm, 0, 2); |
| } |
| |
| public void testE14() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100)); |
| List<RangeMarker> mm = add(document, 6,11, 2,13, 17,17, 13,19, 2,3, 9,10, 10,11, 14,14, 1,3, 4,12, 14,15, 3,10, 14,14, 4,4, 4,8, 6,14, 8,16, 2,12, 11,19, 10,13 |
| ); |
| edit(document, 19,0,0, 7,3,0, 16,0,3); |
| } |
| |
| public void testE15() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100)); |
| List<RangeMarker> mm = add(document, 90,93, 0,9, 44,79, 4,48, 44,99, 53,64, 59,82, 12,99, 81,86, 8,40, 24,55, 32,50, 74,79, 14,94, 7,14 |
| ); |
| edit(document, 34,0,4, 99,0,3); |
| } |
| |
| public void testE16() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100)); |
| List<RangeMarker> mm = add(document, 29,63, 47,52, 72,86, 19,86, 13,55, 18,57, 92,95, 83,99, 41,80, 53,85, 10,30, 28,44, 23,32, 70,95, 14,28 |
| ); |
| edit(document, 67,5,0, 1,0,4); |
| delete(mm, 11); |
| } |
| public void testE17() { |
| DocumentEx document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100)); |
| |
| List<RangeMarker> mm = add(document, 15,85, 79,88, 90,94, 43,67, 54,89, 81,98, 1,34, 58,93, 22,23, 44,45, 63,84, 45,76, 58,87, 40,59, 5,81, 95,95, 12,61, 52,65, 80,95, 6,16, 7,67, 59,63, 91,96, 99,99, 50,96, 72,78, 78,78, 85,85, 5,51, 90,91 |
| ); |
| edit(document, 20,26,0, 15,0,4, 64,4,0); |
| } |
| |
| public void testRandomEdit_NoCommand() { |
| final int N = 100; |
| |
| final Random gen = new Random(); |
| int N_TRIES = Timings.adjustAccordingToMySpeed(7000, false); |
| System.out.println("N_TRIES = " + N_TRIES); |
| DocumentEx document = null; |
| for (int tryn=0; tryn < N_TRIES;tryn++) { |
| ((UndoManagerImpl)UndoManager.getInstance(getProject())).flushCurrentCommandMerger(); |
| ((UndoManagerImpl)UndoManager.getGlobalInstance()).flushCurrentCommandMerger(); |
| if (document != null) { |
| ((UndoManagerImpl)UndoManager.getInstance(getProject())).clearUndoRedoQueueInTests(document); |
| ((UndoManagerImpl)UndoManager.getGlobalInstance()).clearUndoRedoQueueInTests(document); |
| } |
| |
| if (tryn % 10000 == 0) { |
| System.out.println(tryn); |
| } |
| document = (DocumentEx)EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', N)); |
| |
| final DocumentEx finalDocument = document; |
| new WriteCommandAction(getProject()) { |
| @Override |
| protected void run(Result result) throws Exception { |
| List<Pair<RangeMarker, TextRange>> adds = new ArrayList<Pair<RangeMarker, TextRange>>(); |
| List<Pair<RangeMarker, TextRange>> dels = new ArrayList<Pair<RangeMarker, TextRange>>(); |
| List<Trinity<Integer, Integer, Integer>> edits = new ArrayList<Trinity<Integer, Integer, Integer>>(); |
| |
| try { |
| for (int i = 0; i < 30; i++) { |
| int x = gen.nextInt(N); |
| int y = x + gen.nextInt(N - x); |
| RangeMarkerEx r = (RangeMarkerEx)finalDocument.createRangeMarker(x, y); |
| adds.add(Pair.create((RangeMarker)r, TextRange.create(r))); |
| } |
| |
| for (int i = 0; i < 10; i++) { |
| int offset = gen.nextInt(finalDocument.getTextLength()); |
| if (gen.nextBoolean()) { |
| int length = gen.nextInt(5); |
| edits.add(Trinity.create(offset, 0, length)); |
| finalDocument.insertString(offset, StringUtil.repeatSymbol(' ', length)); |
| } |
| else { |
| int length = gen.nextInt(finalDocument.getTextLength() - offset); |
| edits.add(Trinity.create(offset, length, 0)); |
| finalDocument.deleteString(offset, offset + length); |
| } |
| } |
| List<Pair<RangeMarker, TextRange>> candidates = new ArrayList<Pair<RangeMarker, TextRange>>(adds); |
| while (!candidates.isEmpty()) { |
| int size = candidates.size(); |
| int x = gen.nextInt(size); |
| Pair<RangeMarker, TextRange> c = candidates.remove(x); |
| RangeMarkerEx r = (RangeMarkerEx)c.first; |
| assertEquals(size - 1, candidates.size()); |
| dels.add(c); |
| r.dispose(); |
| } |
| } |
| catch (AssertionError e) { |
| String s = "adds: "; |
| for (Pair<RangeMarker, TextRange> c : adds) { |
| TextRange t = c.second; |
| s += t.getStartOffset() + "," + t.getEndOffset() + ", "; |
| } |
| |
| s += "\nedits: "; |
| for (Trinity<Integer, Integer, Integer> edit : edits) { |
| s += edit.first + "," + edit.second + "," + edit.third + ", "; |
| } |
| s += "\ndels: "; |
| |
| for (Pair<RangeMarker, TextRange> c : dels) { |
| int index = adds.indexOf(c); |
| assertSame(c, adds.get(index)); |
| s += index + ", "; |
| } |
| System.err.println(s); |
| throw e; |
| } |
| } |
| }.execute(); |
| } |
| } |
| |
| private RangeMarkerEx createMarker(String text, final int start, final int end) { |
| psiFile = createFile("x.txt", text); |
| return createMarker(psiFile, start, end); |
| } |
| |
| private RangeMarkerEx createMarker(PsiFile psiFile, final int start, final int end) { |
| document = documentManager.getDocument(psiFile); |
| return (RangeMarkerEx)document.createRangeMarker(start, end); |
| } |
| |
| private RangeMarkerEx createMarker(@NonNls String string) { |
| int start = string.indexOf('['); |
| assertTrue(start != -1); |
| string = string.replace("[", ""); |
| int end = string.indexOf(']'); |
| assertTrue(end != -1); |
| string = string.replace("]", ""); |
| return createMarker(string, start, end); |
| } |
| |
| public void testRangeMarkersAreWeakReferenced_NoVerify() throws Exception { |
| final Document document = EditorFactory.getInstance().createDocument("[xxxxxxxxxxxxxx]"); |
| for (int i = 0; i < 10; i++) { |
| document.createRangeMarker(0, document.getTextLength()); |
| } |
| |
| LeakHunter.checkLeak(document, RangeMarker.class); |
| } |
| |
| public void testRangeMarkersAreLazyCreated() throws Exception { |
| final Document document = EditorFactory.getInstance().createDocument("[xxxxxxxxxxxxxx]"); |
| RangeMarker m1 = document.createRangeMarker(2, 4); |
| RangeMarker m2 = document.createRangeMarker(2, 4); |
| |
| assertEquals(2, ((DocumentImpl)document).getRangeMarkersSize()); |
| assertEquals(1, ((DocumentImpl)document).getRangeMarkersNodeSize()); |
| |
| RangeMarker m3 = document.createRangeMarker(2, 5); |
| assertEquals(2, ((DocumentImpl)document).getRangeMarkersNodeSize()); |
| document.deleteString(4,5); |
| assertTrue(m1.isValid()); |
| assertTrue(m2.isValid()); |
| assertTrue(m3.isValid()); |
| assertEquals(1, ((DocumentImpl)document).getRangeMarkersNodeSize()); |
| |
| m1.setGreedyToLeft(true); |
| assertTrue(m1.isValid()); |
| assertEquals(3, ((DocumentImpl)document).getRangeMarkersSize()); |
| assertEquals(2, ((DocumentImpl)document).getRangeMarkersNodeSize()); |
| |
| m3.dispose(); |
| assertTrue(m1.isValid()); |
| assertTrue(m2.isValid()); |
| assertFalse(m3.isValid()); |
| assertEquals(2, ((DocumentImpl)document).getRangeMarkersSize()); |
| assertEquals(2, ((DocumentImpl)document).getRangeMarkersNodeSize()); |
| } |
| public void testRangeHighlightersRecreateBug() throws Exception { |
| Document document = EditorFactory.getInstance().createDocument("[xxxxxxxxxxxxxx]"); |
| |
| MarkupModel markupModel = DocumentMarkupModel.forDocument(document, ourProject, true); |
| for (int i=0; i<2; i++) { |
| RangeMarker m = markupModel.addRangeHighlighter(1, 6, 0, null, HighlighterTargetArea.EXACT_RANGE); |
| RangeMarker m2 = markupModel.addRangeHighlighter(2, 7, 0, null, HighlighterTargetArea.EXACT_RANGE); |
| RangeMarker m3 = markupModel.addRangeHighlighter(1, 6, 0, null, HighlighterTargetArea.EXACT_RANGE); |
| markupModel.removeAllHighlighters(); |
| } |
| } |
| public void testValidationBug() throws Exception { |
| Document document = EditorFactory.getInstance().createDocument("[xxxxxxxxxxxxxx]"); |
| final Editor editor = EditorFactory.getInstance().createEditor(document); |
| |
| try { |
| final FoldRegion[] fold = new FoldRegion[1]; |
| editor.getFoldingModel().runBatchFoldingOperation(new Runnable() { |
| @Override |
| public void run() { |
| fold[0] = editor.getFoldingModel().addFoldRegion(0, 2, ""); |
| } |
| }); |
| RangeMarker marker = document.createRangeMarker(0, 2); |
| document.deleteString(1,2); |
| |
| assertTrue(marker.isValid()); |
| //assertFalse(fold[0].isValid()); |
| } |
| finally { |
| EditorFactory.getInstance().releaseEditor(editor); |
| } |
| } |
| public void testPersistent() throws Exception { |
| String text = "xxx\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; |
| Document document = EditorFactory.getInstance().createDocument(text); |
| int startOffset = text.indexOf('z'); |
| int endOffset = text.lastIndexOf('z'); |
| RangeMarker marker = document.createRangeMarker(startOffset, endOffset, true); |
| |
| document.replaceString(startOffset+1, endOffset-1, "ccc"); |
| |
| assertTrue(marker.isValid()); |
| } |
| |
| public void testMoveTextRetargetsMarkers() throws Exception { |
| RangeMarkerEx marker1 = createMarker("01234567890", 1, 3); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| RangeMarker marker2 = document.createRangeMarker(2, 4); |
| |
| document.moveText(0, 5, 8); |
| assertEquals("56701234890", document.getText()); |
| |
| assertValidMarker(marker1, 4, 6); |
| assertValidMarker(marker2, 5, 7); |
| } |
| |
| public void testMoveTextToTheBeginningRetargetsMarkers() throws Exception { |
| RangeMarkerEx marker1 = createMarker("01234567890", 5, 5); |
| DocumentEx document = (DocumentEx)marker1.getDocument(); |
| RangeMarker marker2 = document.createRangeMarker(5, 7); |
| |
| document.moveText(4, 7, 1); |
| assertEquals("04561237890", document.getText()); |
| |
| assertValidMarker(marker1, 2, 2); |
| assertValidMarker(marker2, 2, 4); |
| } |
| |
| public void testRangeHighlighterDisposeVsRemoveAllConflict() throws Exception { |
| Document document = EditorFactory.getInstance().createDocument("[xxxxxxxxxxxxxx]"); |
| |
| MarkupModel markupModel = DocumentMarkupModel.forDocument(document, ourProject, true); |
| RangeMarker m = markupModel.addRangeHighlighter(1, 6, 0, null, HighlighterTargetArea.EXACT_RANGE); |
| assertTrue(m.isValid()); |
| markupModel.removeAllHighlighters(); |
| assertFalse(m.isValid()); |
| assertEmpty(markupModel.getAllHighlighters()); |
| m.dispose(); |
| assertFalse(m.isValid()); |
| } |
| |
| public void testRangeHighlighterLinesInRangeForLongLinePerformance() throws Exception { |
| final int N = 50000; |
| Document document = EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol('x', 2 * N)); |
| |
| final MarkupModelEx markupModel = (MarkupModelEx)DocumentMarkupModel.forDocument(document, ourProject, true); |
| for (int i=0; i<N-1;i++) { |
| markupModel.addRangeHighlighter(2*i, 2*i+1, 0, null, HighlighterTargetArea.EXACT_RANGE); |
| } |
| markupModel.addRangeHighlighter(N / 2, N / 2 + 1, 0, null, HighlighterTargetArea.LINES_IN_RANGE); |
| |
| PlatformTestUtil.startPerformanceTest("slow highlighters lookup", (int)(N*Math.log(N)/1000), new ThrowableRunnable() { |
| @Override |
| public void run() { |
| List<RangeHighlighterEx> list = new ArrayList<RangeHighlighterEx>(); |
| CommonProcessors.CollectProcessor<RangeHighlighterEx> coll = new CommonProcessors.CollectProcessor<RangeHighlighterEx>(list); |
| for (int i=0; i<N-1;i++) { |
| list.clear(); |
| markupModel.processRangeHighlightersOverlappingWith(2*i, 2*i+1, coll); |
| assertEquals(2, list.size()); // 1 line plus one exact range marker |
| } |
| } |
| }).assertTiming(); |
| } |
| |
| public void testRangeHighlighterIteratorOrder() throws Exception { |
| Document document = EditorFactory.getInstance().createDocument("1234567890"); |
| |
| final MarkupModelEx markupModel = (MarkupModelEx)DocumentMarkupModel.forDocument(document, ourProject, true); |
| RangeHighlighter exact = markupModel.addRangeHighlighter(3, 6, 0, null, HighlighterTargetArea.EXACT_RANGE); |
| RangeHighlighter line = markupModel.addRangeHighlighter(4, 5, 0, null, HighlighterTargetArea.LINES_IN_RANGE); |
| List<RangeHighlighter> list = new ArrayList<RangeHighlighter>(); |
| markupModel.processRangeHighlightersOverlappingWith(2, 9, new CommonProcessors.CollectProcessor<RangeHighlighter>(list)); |
| assertEquals(Arrays.asList(line, exact), list); |
| } |
| |
| public void testLazyRangeMarkers() { |
| psiFile = createFile("x.txt", "xxx"); |
| |
| LazyRangeMarkerFactoryImpl factory = (LazyRangeMarkerFactoryImpl)LazyRangeMarkerFactory.getInstance(getProject()); |
| VirtualFile virtualFile = psiFile.getVirtualFile(); |
| LazyRangeMarkerFactoryImpl.LazyMarker marker = (LazyRangeMarkerFactoryImpl.LazyMarker)factory.createRangeMarker(virtualFile, 0); |
| WeakList<LazyRangeMarkerFactoryImpl.LazyMarker> markers = LazyRangeMarkerFactoryImpl.getMarkers(virtualFile); |
| assertSame(marker, assertOneElement(markers)); |
| |
| assertFalse(marker.isDelegated()); |
| assertTrue(marker.isValid()); |
| assertEquals(0, marker.getStartOffset()); |
| assertFalse(marker.isDelegated()); |
| |
| marker.dispose(); |
| assertFalse(marker.isValid()); |
| assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile)); |
| |
| |
| marker = (LazyRangeMarkerFactoryImpl.LazyMarker)factory.createRangeMarker(virtualFile, 0); |
| assertFalse(marker.isDelegated()); |
| assertTrue(marker.isValid()); |
| assertEquals(0, marker.getStartOffset()); |
| assertFalse(marker.isDelegated()); |
| |
| Document document = marker.getDocument(); |
| document.insertString(2, "yyy"); |
| assertTrue(marker.isDelegated()); |
| assertTrue(marker.isValid()); |
| assertEquals(0, marker.getStartOffset()); |
| |
| assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile)); |
| marker.dispose(); |
| assertEmpty(LazyRangeMarkerFactoryImpl.getMarkers(virtualFile)); |
| } |
| } |