@Override protected void changedUpdateImpl(@NotNull DocumentEvent e) { // todo Denis Zhdanov DocumentEventImpl event = (DocumentEventImpl)e; final boolean shouldTranslateViaDiff = isValid() && PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, this); boolean wasTranslatedViaDiff = shouldTranslateViaDiff; if (shouldTranslateViaDiff) { wasTranslatedViaDiff = translatedViaDiff(e, event); } if (!wasTranslatedViaDiff) { super.changedUpdateImpl(e); if (isValid()) { myLine = getDocument().getLineNumber(getStartOffset()); int endLine = getDocument().getLineNumber(getEndOffset()); if (endLine != myLine) { setIntervalEnd(getDocument().getLineEndOffset(myLine)); } } } if (isValid() && getTargetArea() == HighlighterTargetArea.LINES_IN_RANGE) { setIntervalStart(DocumentUtil.getFirstNonSpaceCharOffset(getDocument(), myLine)); setIntervalEnd(getDocument().getLineEndOffset(myLine)); } }
private boolean translatedViaDiff(DocumentEvent e, DocumentEventImpl event) { try { myLine = event.translateLineViaDiff(myLine); } catch (FilesTooBigForDiffException ignored) { return false; } if (myLine < 0 || myLine >= getDocument().getLineCount()) { invalidate(e); } else { DocumentEx document = getDocument(); setIntervalStart(document.getLineStartOffset(myLine)); setIntervalEnd(document.getLineEndOffset(myLine)); } return true; }
@Override public void documentChanged(final DocumentEvent e) { isDocumentChanged = true; try { myIsInUpdate = false; doWithCaretMerging(new Runnable() { @Override public void run() { for (CaretImpl caret : myCarets) { caret.updateCaretPosition((DocumentEventImpl)e); } } }); } finally { isDocumentChanged = false; } }
@Nullable private static Pair<ProperTextRange, LinesCols> translateViaDiff(final DocumentEventImpl event, LinesCols linesCols) { try { int myStartLine = event.translateLineViaDiffStrict(linesCols.myStartLine); Document document = event.getDocument(); if (myStartLine < 0 || myStartLine >= document.getLineCount()) { return null; } int start = document.getLineStartOffset(myStartLine) + linesCols.myStartColumn; if (start >= document.getTextLength()) return null; int myEndLine = event.translateLineViaDiffStrict(linesCols.myEndLine); if (myEndLine < 0 || myEndLine >= document.getLineCount()) { return null; } int end = document.getLineStartOffset(myEndLine) + linesCols.myEndColumn; if (end > document.getTextLength() || end < start) return null; return Pair.create(new ProperTextRange(start, end), new LinesCols(myStartLine, linesCols.myStartColumn, myEndLine, linesCols.myEndColumn)); } catch (FilesTooBigForDiffException e) { return null; } }
@Nullable static Pair<ProperTextRange, LinesCols> applyChange(DocumentEvent event, Segment range, int intervalStart, int intervalEnd, boolean greedyLeft, boolean greedyRight, LinesCols linesCols) { final boolean shouldTranslateViaDiff = PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, range); Pair<ProperTextRange, LinesCols> translated = null; if (shouldTranslateViaDiff) { translated = translateViaDiff((DocumentEventImpl)event, linesCols); } if (translated == null) { ProperTextRange fallback = applyChange(event, intervalStart, intervalEnd, greedyLeft, greedyRight); if (fallback == null) return null; LinesCols lc = storeLinesAndCols(fallback, event.getDocument()); if (lc == null) return null; translated = Pair.create(fallback, lc); } if (translated.first.getEndOffset() > event.getDocument().getTextLength() || translated.second.myEndLine < translated.second.myStartLine || translated.second.myStartLine == translated.second.myEndLine && translated.second.myEndColumn < translated.second.myStartColumn || event.getDocument().getLineCount() < translated.second.myEndLine) { return null; } return translated; }
private void doCheckResults(final SegmentArrayWithData workingCopySegmentsForTesting, final DocumentEventImpl e, final SegmentArrayWithData data, final SegmentArrayWithData segments) { mySegments = workingCopySegmentsForTesting; processMultilineChange(e); mySegments = data; assert workingCopySegmentsForTesting.getSegmentCount() == segments.getSegmentCount(); for(int i =0; i < segments.getSegmentCount();++i) { assert workingCopySegmentsForTesting.getSegmentStart(i) == segments.getSegmentStart(i); assert workingCopySegmentsForTesting.getSegmentEnd(i) == segments.getSegmentEnd(i); assert workingCopySegmentsForTesting.getSegmentData(i) == segments.getSegmentData(i); } processMultilineChange(e); }
public void testGetUncommittedDocuments_documentChanged_DontProcessEvents() throws Exception { PsiFile file = getPsiManager().findFile(createFile()); final Document document = getPsiDocumentManager().getDocument(file); final TextBlock block = TextBlock.get(file); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { block.performAtomically(new Runnable() { @Override public void run() { getPsiDocumentManager().documentChanged(new DocumentEventImpl(document, 0, "", "", document.getModificationStamp(), false)); } }); } }); assertEquals(0, getPsiDocumentManager().getUncommittedDocuments().length); }
public void testCommitDocument_RemovesFromUncommittedList() throws Exception { PsiFile file = getPsiManager().findFile(createFile()); final Document document = getPsiDocumentManager().getDocument(file); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { getPsiDocumentManager().documentChanged(new DocumentEventImpl(document, 0, "", "", document.getModificationStamp(), false)); } }); getPsiDocumentManager().commitDocument(document); assertEquals(0, getPsiDocumentManager().getUncommittedDocuments().length); }
public void testCommitAllDocument_RemovesFromUncommittedList() throws Exception { PsiFile file = getPsiManager().findFile(createFile()); final Document document = getPsiDocumentManager().getDocument(file); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { getPsiDocumentManager().documentChanged(new DocumentEventImpl(document, 0, "", "", document.getModificationStamp(), false)); } }); getPsiDocumentManager().commitAllDocuments(); assertEquals(0, getPsiDocumentManager().getUncommittedDocuments().length); }
@Override protected void changedUpdateImpl(DocumentEvent e) { // todo Denis Zhdanov DocumentEventImpl event = (DocumentEventImpl)e; final boolean shouldTranslateViaDiff = PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, this); boolean wasTranslatedViaDiff = shouldTranslateViaDiff; if (shouldTranslateViaDiff) { wasTranslatedViaDiff = translatedViaDiff(e, event); } if (!wasTranslatedViaDiff) { super.changedUpdateImpl(e); if (isValid()) { setLine(getDocument().getLineNumber(getStartOffset())); int endLine = getDocument().getLineNumber(getEndOffset()); if (endLine != getLine()) { setIntervalEnd(getDocument().getLineEndOffset(getLine())); } } } if (isValid() && getTargetArea() == HighlighterTargetArea.LINES_IN_RANGE) { setIntervalStart(getDocument().getLineStartOffset(getLine())); setIntervalEnd(getDocument().getLineEndOffset(getLine())); } }
private boolean translatedViaDiff(DocumentEvent e, DocumentEventImpl event) { try { setLine(event.translateLineViaDiff(getLine())); } catch (FilesTooBigForDiffException e1) { return false; } if (getLine() < 0 || getLine() >= getDocument().getLineCount()) { invalidate(e); } else { DocumentEx document = getDocument(); setIntervalStart(document.getLineStartOffset(getLine())); setIntervalEnd(document.getLineEndOffset(getLine())); } return true; }
private void init(int size) { myArray = new CharArray(size, new char[0], 0) { @NotNull @Override protected DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) { return new DocumentEventImpl(myDocument, offset, oldString, newString, LocalTimeCounter.currentTime(), wholeTextReplaced); } @Override protected void afterChangedUpdate(@NotNull DocumentEvent event, long newModificationStamp) { } @Override protected void assertWriteAccess() { } @Override protected void assertReadAccess() { } }; }
/** * Answers if document region identified by the given range marker should be translated via diff algorithm on document change * identified by the given event. * * @param e event that describes document change * @param rangeMarker target range marker which update strategy should be selected * @return <code>true</code> if target document range referenced by the given range marker should be translated via * diff algorithm; <code>false</code> otherwise */ public static boolean shouldTranslateViaDiff(@NotNull DocumentEventImpl e, @NotNull RangeMarker rangeMarker) { if (e.isWholeTextReplaced()) { // Perform translation if the whole text is replaced. return true; } if (!rangeMarker.isValid()) { // Don't perform complex processing if current range marker is already invalid. return false; } if (e.getOffset() >= rangeMarker.getEndOffset() || e.getOffset() + e.getOldLength() <= rangeMarker.getStartOffset()) { // Don't perform complex processing if the change doesn't affect target range. return false; } // Perform complex processing only if significant document part is updated. return (Math.max(e.getNewLength(), e.getOldLength()) * 1.0) / e.getDocument().getTextLength() >= 0.8; }
@Override protected void changedUpdateImpl(DocumentEvent e) { DocumentEventImpl event = (DocumentEventImpl)e; final boolean shouldTranslateViaDiff = PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, this); boolean wasTranslated = shouldTranslateViaDiff; if (shouldTranslateViaDiff) { wasTranslated = translateViaDiff(event); } if (!wasTranslated) { super.changedUpdateImpl(e); if (isValid()) { storeLinesAndCols(e); } } if (intervalEnd() < intervalStart() || intervalEnd() > getDocument().getTextLength() || myEndLine < myStartLine || myStartLine == myEndLine && myEndColumn < myStartColumn || getDocument().getLineCount() < myEndLine) { invalidate(e); } }
@Override protected void changedUpdateImpl(@Nonnull DocumentEvent e) { // todo Denis Zhdanov DocumentEventImpl event = (DocumentEventImpl)e; final boolean shouldTranslateViaDiff = isValid() && PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, getStartOffset(), getEndOffset()); boolean wasTranslatedViaDiff = shouldTranslateViaDiff; if (shouldTranslateViaDiff) { wasTranslatedViaDiff = translatedViaDiff(e, event); } if (!wasTranslatedViaDiff) { super.changedUpdateImpl(e); if (isValid()) { myLine = getDocument().getLineNumber(getStartOffset()); int endLine = getDocument().getLineNumber(getEndOffset()); if (endLine != myLine) { setIntervalEnd(getDocument().getLineEndOffset(myLine)); } } } if (isValid() && getTargetArea() == HighlighterTargetArea.LINES_IN_RANGE) { setIntervalStart(DocumentUtil.getFirstNonSpaceCharOffset(getDocument(), myLine)); setIntervalEnd(getDocument().getLineEndOffset(myLine)); } }
@Nullable private static Pair<TextRange, LinesCols> applyChange(DocumentEvent event, Segment range, int intervalStart, int intervalEnd, boolean greedyLeft, boolean greedyRight, boolean stickingToRight, LinesCols linesCols) { final boolean shouldTranslateViaDiff = PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, range.getStartOffset(), range.getEndOffset()); Pair<TextRange, LinesCols> translated = null; if (shouldTranslateViaDiff) { translated = translateViaDiff((DocumentEventImpl)event, linesCols); } if (translated == null) { TextRange fallback = applyChange(event, intervalStart, intervalEnd, greedyLeft, greedyRight, stickingToRight); if (fallback == null) return null; LinesCols lc = storeLinesAndCols(event.getDocument(), fallback.getStartOffset(), fallback.getEndOffset()); if (lc == null) return null; translated = Pair.create(fallback, lc); } return translated; }
private void updateText(@Nonnull ImmutableCharSequence newText, int offset, @Nonnull CharSequence oldString, @Nonnull CharSequence newString, boolean wholeTextReplaced, long newModificationStamp, int initialStartOffset, int initialOldLength) { assertNotNestedModification(); myChangeInProgress = true; try { DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced, initialStartOffset, initialOldLength); beforeChangedUpdate(event); myTextString = null; ImmutableCharSequence prevText = myText; myText = newText; sequence.incrementAndGet(); // increment sequence before firing events so that modification sequence on commit will match this sequence now changedUpdate(event, newModificationStamp, prevText); } finally { myChangeInProgress = false; } }
@Nullable public ManualRangeMarker getUpdatedRange(@Nonnull DocumentEvent event, @Nonnull FrozenDocument documentBefore) { if (event instanceof RetargetRangeMarkers) { int start = ((RetargetRangeMarkers)event).getStartOffset(); if (myStart >= start && myEnd <= ((RetargetRangeMarkers)event).getEndOffset()) { int delta = ((RetargetRangeMarkers)event).getMoveDestinationOffset() - start; return new ManualRangeMarker(myStart + delta, myEnd + delta, myGreedyLeft, myGreedyRight, mySurviveOnExternalChange, null); } } if (mySurviveOnExternalChange && PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, myStart, myEnd)) { PersistentRangeMarker.LinesCols linesCols = myLinesCols != null ? myLinesCols : PersistentRangeMarker.storeLinesAndCols(documentBefore, myStart, myEnd); Pair<TextRange, PersistentRangeMarker.LinesCols> pair = linesCols == null ? null : PersistentRangeMarker.translateViaDiff((DocumentEventImpl)event, linesCols); if (pair != null) { return new ManualRangeMarker(pair.first.getStartOffset(), pair.first.getEndOffset(), myGreedyLeft, myGreedyRight, true, pair.second); } } TextRange range = RangeMarkerImpl.applyChange(event, myStart, myEnd, myGreedyLeft, myGreedyRight, false); return range == null ? null : new ManualRangeMarker(range.getStartOffset(), range.getEndOffset(), myGreedyLeft, myGreedyRight, mySurviveOnExternalChange, null); }
public void testGetUncommittedDocuments_documentNotRegistered() throws Exception { final Document document = new MockDocument(); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { getPsiDocumentManager().documentChanged(new DocumentEventImpl(document, 0, "", "", document.getModificationStamp(), false)); } }); assertEquals(0, getPsiDocumentManager().getUncommittedDocuments().length); }
@Override public void documentChanged(DocumentEvent e) { if (!myTaskFile.isTrackChanges()) { return; } if (myAnswerPlaceholders.isEmpty()) return; if (e instanceof DocumentEventImpl) { DocumentEventImpl event = (DocumentEventImpl)e; Document document = e.getDocument(); int offset = e.getOffset(); int change = event.getNewLength() - event.getOldLength(); for (AnswerPlaceholderWrapper answerPlaceholderWrapper : myAnswerPlaceholders) { int twStart = answerPlaceholderWrapper.getTwStart(); if (twStart > offset) { twStart += change; } int twEnd = answerPlaceholderWrapper.getTwEnd(); if (twEnd >= offset) { twEnd += change; } AnswerPlaceholder answerPlaceholder = answerPlaceholderWrapper.getAnswerPlaceholder(); int length = twEnd - twStart; answerPlaceholder.setOffset(twStart); if (myTrackLength) { if (answerPlaceholder.getUseLength()) { answerPlaceholder.setLength(length); } else { if (answerPlaceholder.isActive() && myTaskFile.isTrackLengths()) { answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(twStart, twStart + length))); } } } } } }
private void populate(int start, int end, String newText) { DocumentEventImpl event = new DocumentEventImpl( myDocument, start, buffer.substring(start, end), newText, ++ourCounter, false ); buffer.replace(start, end, newText); myCollector.beforeDocumentChange(event); myCollector.documentChanged(event); }
private void updateText(@NotNull ImmutableText newText, int offset, @Nullable CharSequence oldString, @Nullable CharSequence newString, boolean wholeTextReplaced, long newModificationStamp, int initialStartOffset, int initialOldLength) { assertNotNestedModification(); boolean enableRecursiveModifications = Registry.is("enable.recursive.document.changes"); // temporary property, to remove in IDEA 16 myChangeInProgress = true; try { DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced, initialStartOffset, initialOldLength); try { doBeforeChangedUpdate(event); } finally { if (enableRecursiveModifications) { myChangeInProgress = false; } } myTextString = null; ImmutableText prevText = myText; myText = newText; changedUpdate(event, newModificationStamp, prevText); } finally { if (!enableRecursiveModifications) { myChangeInProgress = false; } } }
@Override public void documentChanged(DocumentEvent e) { if (!myTaskFile.isTrackChanges()) { return; } if (myAnswerPlaceholders.isEmpty()) return; if (e instanceof DocumentEventImpl) { DocumentEventImpl event = (DocumentEventImpl)e; Document document = e.getDocument(); int offset = e.getOffset(); int change = event.getNewLength() - event.getOldLength(); for (AnswerPlaceholderWrapper answerPlaceholderWrapper : myAnswerPlaceholders) { int twStart = answerPlaceholderWrapper.getTwStart(); if (twStart > offset) { twStart += change; } int twEnd = answerPlaceholderWrapper.getTwEnd(); if (twEnd >= offset) { twEnd += change; } AnswerPlaceholder answerPlaceholder = answerPlaceholderWrapper.getAnswerPlaceholder(); int line = document.getLineNumber(twStart); int start = twStart - document.getLineStartOffset(line); int length = twEnd - twStart; answerPlaceholder.setLine(line); answerPlaceholder.setStart(start); if (usePossibleAnswerLength) { answerPlaceholder.setPossibleAnswer(document.getText(TextRange.create(twStart, twStart + length))); } else if (myTrackLength) { answerPlaceholder.setLength(length); } } } }
public void changedUpdate(DocumentEvent e1) { DocumentEventImpl e = (DocumentEventImpl) e1; if (e.isOnlyOneLineChanged() && mySegments.getSegmentCount() > 0) { processOneLineChange(e); } else { if (mySegments.getSegmentCount() == 0 || e.getStartOldIndex() >= mySegments.getSegmentCount() || e.getStartOldIndex() < 0) { initSegments(e.getDocument().getCharsSequence(), true); return; } final int optimizedLineShift = e.getOptimizedLineShift(); if (optimizedLineShift != -1) { processOptimizedMultilineInsert(e, optimizedLineShift); } else { final int optimizedOldLineShift = e.getOptimizedOldLineShift(); if (optimizedOldLineShift != -1) { processOptimizedMultilineDelete(e, optimizedOldLineShift); } else { processMultilineChange(e); } } } if (e.isWholeTextReplaced()) { clearModificationFlags(); } }
private void updateSegments(CharSequence newText, int oldStartLine, int oldEndLine, int offset1, DocumentEventImpl e) { int count = 0; LineTokenizer lineTokenizer = new LineTokenizer(newText); for (int index = oldStartLine; index <= oldEndLine; index++) { if (!lineTokenizer.atEnd()) { setSegmentAt(mySegments, index, lineTokenizer, offset1, true); lineTokenizer.advance(); } else { mySegments.remove(index, oldEndLine + 1); break; } count++; } if (!lineTokenizer.atEnd()) { SegmentArrayWithData insertSegments = new SegmentArrayWithData(); int i = 0; while (!lineTokenizer.atEnd()) { setSegmentAt(insertSegments, i, lineTokenizer, offset1, true); lineTokenizer.advance(); count++; i++; } mySegments.insert(insertSegments, oldEndLine + 1); } int shift = e.getNewLength() - e.getOldLength(); mySegments.shiftSegments(oldStartLine + count, shift); }
private void processOneLineChange(DocumentEventImpl e) { // Check, if the change on the end of text if (e.getOffset() >= mySegments.getSegmentEnd(mySegments.getSegmentCount() - 1)) { mySegments.changeSegmentLength(mySegments.getSegmentCount() - 1, e.getNewLength() - e.getOldLength()); setSegmentModified(mySegments, mySegments.getSegmentCount() - 1); } else { mySegments.changeSegmentLength(e.getStartOldIndex(), e.getNewLength() - e.getOldLength()); setSegmentModified(mySegments, e.getStartOldIndex()); } }
@NotNull private DocumentEvent doBeforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) { Application app = ApplicationManager.getApplication(); if (app != null) { FileDocumentManager manager = FileDocumentManager.getInstance(); if (manager != null) { VirtualFile file = manager.getFile(this); if (file != null && !file.isValid()) log("File of this document has been deleted."); } } getLineSet(); // initialize line set to track changed lines DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced); if (!ShutDownTracker.isShutdownHookRunning()) { DocumentListener[] listeners = getCachedListeners(); for (int i = listeners.length - 1; i >= 0; i--) { try { listeners[i].beforeDocumentChange(event); } catch (Throwable e) { log(e); } } } myEventsHandling = true; return event; }
public void testTextInserted_Once() throws Exception { myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "", "xxx", 1, false)); assertTrue(!myTextBlock.isEmpty()); assertEquals(10, myTextBlock.getStartOffset()); assertEquals(13, myTextBlock.getTextEndOffset()); assertEquals(10, myTextBlock.getPsiEndOffset()); }
public void testReset() throws Exception { assertTrue(myTextBlock.isEmpty()); myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "", "xxx", 1, false)); assertTrue(!myTextBlock.isEmpty()); myTextBlock.clear(); assertTrue(myTextBlock.isEmpty()); }
public void testTextInserted_SecondNonAdjFragmentsAfter() throws Exception { myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "", "xxx", 1, false)); myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 20, "", "xxx", 1, false)); assertTrue(!myTextBlock.isEmpty()); assertEquals(10, myTextBlock.getStartOffset()); assertEquals(23, myTextBlock.getTextEndOffset()); assertEquals(17, myTextBlock.getPsiEndOffset()); }
public void testTextInserted_SecondNonAdjFragmentsBefore() throws Exception { myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "", "xxx", 1, false)); myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 5, "", "xxx", 1, false)); assertTrue(!myTextBlock.isEmpty()); assertEquals(5, myTextBlock.getStartOffset()); assertEquals(16, myTextBlock.getTextEndOffset()); assertEquals(10, myTextBlock.getPsiEndOffset()); }
public void testTextInserted_SecondAdjFragmentsAfter() throws Exception { myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "", "xxx", 1, false)); myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 13, "", "xxx", 1, false)); assertTrue(!myTextBlock.isEmpty()); assertEquals(10, myTextBlock.getStartOffset()); assertEquals(16, myTextBlock.getTextEndOffset()); assertEquals(10, myTextBlock.getPsiEndOffset()); }
public void testTextInserted_SecondAdjFragmentsBefore() throws Exception { myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "", "xxx", 1, false)); myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "", "xxx", 1, false)); assertTrue(!myTextBlock.isEmpty()); assertEquals(10, myTextBlock.getStartOffset()); assertEquals(16, myTextBlock.getTextEndOffset()); assertEquals(10, myTextBlock.getPsiEndOffset()); }
public void testTextDeleted_Once() throws Exception { myTextBlock.documentChanged(new DocumentEventImpl(myDocument, 10, "xxx", "", 1, false)); assertTrue(!myTextBlock.isEmpty()); assertEquals(10, myTextBlock.getStartOffset()); assertEquals(10, myTextBlock.getTextEndOffset()); assertEquals(13, myTextBlock.getPsiEndOffset()); }