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 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()); }
@Nullable public static Document getDocumentToBeUsedFor(final PsiFile file) { final Project project = file.getProject(); final Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document == null) return null; if (PsiDocumentManager.getInstance(project).isUncommited(document)) return null; PsiToDocumentSynchronizer synchronizer = ((PsiDocumentManagerImpl) PsiDocumentManager.getInstance(project)).getSynchronizer(); if (synchronizer.isDocumentAffectedByTransactions(document)) return null; return document; }
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 boolean finishCommit( @NotNull final Document document, @NotNull final List<Processor<Document>> finishProcessors, final boolean synchronously, @NotNull final Object reason) { assert !myProject.isDisposed() : "Already disposed"; final boolean[] ok = {true}; ApplicationManager.getApplication() .runWriteAction( new CommitToPsiFileAction(document, myProject) { @Override public void run() { ok[0] = finishCommitInWriteAction(document, finishProcessors, synchronously); } }); if (ok[0]) { // otherwise changes maybe not synced to the document yet, and injectors will crash if (!mySynchronizer.isDocumentAffectedByTransactions(document)) { final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(myProject); if (injectedLanguageManager != null) injectedLanguageManager.startRunInjectors(document, synchronously); } // run after commit actions outside write action runAfterCommitActions(document); if (DebugUtil.DO_EXPENSIVE_CHECKS) { checkAllElementsValid(document, reason); } } return ok[0]; }
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); }
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); }
@Override public void beforeDocumentChange(@NotNull DocumentEvent event) { if (myStopTrackingDocuments) return; final Document document = event.getDocument(); if (!(document instanceof DocumentWindow) && !myLastCommittedTexts.containsKey(document)) { myLastCommittedTexts.put( document, Pair.create(document.getImmutableCharSequence(), document.getModificationStamp())); } VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); boolean isRelevant = virtualFile != null && isRelevant(virtualFile); final FileViewProvider viewProvider = getCachedViewProvider(document); boolean inMyProject = viewProvider != null && viewProvider.getManager() == myPsiManager; if (!isRelevant || !inMyProject) { return; } final List<PsiFile> files = viewProvider.getAllFiles(); PsiFile psiCause = null; for (PsiFile file : files) { if (file == null) { throw new AssertionError( "View provider " + viewProvider + " (" + viewProvider.getClass() + ") returned null in its files array: " + files + " for file " + viewProvider.getVirtualFile()); } if (mySynchronizer.isInsideAtomicChange(file)) { psiCause = file; } } if (psiCause == null) { beforeDocumentChangeOnUnlockedDocument(viewProvider); } ((SingleRootFileViewProvider) viewProvider).beforeDocumentChanged(psiCause); }
@TestOnly public void clearUncommittedDocuments() { myUncommittedDocuments.clear(); mySynchronizer.cleanupForNextTest(); }
@Override public void documentChanged(DocumentEvent event) { if (myStopTrackingDocuments) return; final Document document = event.getDocument(); VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); boolean isRelevant = virtualFile != null && isRelevant(virtualFile); final FileViewProvider viewProvider = getCachedViewProvider(document); if (viewProvider == null) { handleCommitWithoutPsi(document); return; } boolean inMyProject = viewProvider.getManager() == myPsiManager; if (!isRelevant || !inMyProject) { myLastCommittedTexts.remove(document); return; } ApplicationManager.getApplication().assertWriteAccessAllowed(); final List<PsiFile> files = viewProvider.getAllFiles(); boolean commitNecessary = true; for (PsiFile file : files) { if (mySynchronizer.isInsideAtomicChange(file)) { commitNecessary = false; continue; } assert file instanceof PsiFileImpl || "mock.file".equals(file.getName()) && ApplicationManager.getApplication().isUnitTestMode() : event + "; file=" + file + "; allFiles=" + files + "; viewProvider=" + viewProvider; } boolean forceCommit = ApplicationManager.getApplication().hasWriteAction(ExternalChangeAction.class) && (SystemProperties.getBooleanProperty("idea.force.commit.on.external.change", false) || ApplicationManager.getApplication().isHeadlessEnvironment() && !ApplicationManager.getApplication().isUnitTestMode()); // Consider that it's worth to perform complete re-parse instead of merge if the whole document // text is replaced and // current document lines number is roughly above 5000. This makes sense in situations when // external change is performed // for the huge file (that causes the whole document to be reloaded and 'merge' way takes a // while to complete). if (event.isWholeTextReplaced() && document.getTextLength() > 100000) { document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); } if (commitNecessary) { assert !(document instanceof DocumentWindow); myUncommittedDocuments.add(document); myDocumentCommitProcessor.log( "added uncommitted doc", null, false, myProject, document, ((DocumentEx) document).isInBulkUpdate()); if (forceCommit) { commitDocument(document); } else if (!((DocumentEx) document).isInBulkUpdate() && myPerformBackgroundCommit) { myDocumentCommitProcessor.commitAsynchronously(myProject, document, event); } } else { myLastCommittedTexts.remove(document); } }