@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); } } } }
@Override public void beforeDocumentChange(DocumentEvent event) { final Document document = event.getDocument(); final FileViewProvider viewProvider = getCachedViewProvider(document); if (viewProvider == null) return; if (!isRelevant(viewProvider)) return; VirtualFile virtualFile = viewProvider.getVirtualFile(); if (virtualFile.getFileType().isBinary()) return; final List<PsiFile> files = viewProvider.getAllFiles(); PsiFile psiCause = null; for (PsiFile file : files) { mySmartPointerManager.fastenBelts(file, event.getOffset(), null); if (TextBlock.get(file).isLocked()) { psiCause = file; } } if (psiCause == null) { beforeDocumentChangeOnUnlockedDocument(viewProvider); } ((SingleRootFileViewProvider) viewProvider).beforeDocumentChanged(psiCause); }
@Override public void documentChanged(final DocumentEvent e) { myApplication.assertIsDispatchThread(); synchronized (myLock) { if (myReleased) return; if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return; assert myDocument == e.getDocument(); int afterChangedLines; if (e.getNewLength() == 0) { afterChangedLines = 1; } else { int line1 = myLine1; int line2 = myDocument.getLineNumber(e.getOffset() + e.getNewLength()); afterChangedLines = line2 - line1 + 1; } int linesShift = afterChangedLines - myBeforeChangedLines; int line1 = myLine1; int line2 = line1 + myBeforeChangedLines; int[] fixed = fixRanges(e, line1, line2); line1 = fixed[0]; line2 = fixed[1]; doUpdateRanges(line1, line2, linesShift, myBeforeTotalLines); } }
@Override public void documentChanged(DocumentEvent e) { if (!myTaskFile.isTrackChanges()) { return; } if (e instanceof DocumentEventImpl) { final DocumentEventImpl event = (DocumentEventImpl) e; final Document document = e.getDocument(); int offset = e.getOffset(); int change = event.getNewLength() - event.getOldLength(); for (TaskWindowWrapper taskWindowWrapper : myTaskWindows) { int twStart = taskWindowWrapper.getTwStart(); if (twStart > offset) { twStart += change; } int twEnd = taskWindowWrapper.getTwEnd(); if (twEnd >= offset) { twEnd += change; } final AnswerPlaceholder answerPlaceholder = taskWindowWrapper.getAnswerPlaceholder(); int line = document.getLineNumber(twStart); int start = twStart - document.getLineStartOffset(line); int length = twEnd - twStart; answerPlaceholder.setLine(line); answerPlaceholder.setStart(start); answerPlaceholder.setLength(length); } } }
@Override public void documentChanged(DocumentEvent e) { UndoManager undoManager = UndoManager.getInstance(myProject); boolean undoOrRedo = undoManager.isUndoInProgress() || undoManager.isRedoInProgress(); if (undoOrRedo) { // allow undo/redo up until 'creation stamp' back in time // and check it after action is completed if (e.getDocument() == myOrigDocument) { //noinspection SSBasedInspection SwingUtilities.invokeLater( () -> { if (myOrigCreationStamp > myOrigDocument.getModificationStamp()) { closeEditor(); } }); } } else if (e.getDocument() == myNewDocument) { commitToOriginal(e); if (!isValid()) { ApplicationManager.getApplication() .invokeLater(() -> closeEditor(), myProject.getDisposed()); } } else if (e.getDocument() == myOrigDocument) { if (myCommittingToOriginal || myAltFullRange != null && myAltFullRange.isValid()) return; ApplicationManager.getApplication().invokeLater(() -> closeEditor(), myProject.getDisposed()); } }
@Override public final void documentChanged(@NotNull DocumentEvent e) { int oldStart = intervalStart(); int oldEnd = intervalEnd(); int docLength = myDocument.getTextLength(); if (!isValid()) { LOG.error( "Invalid range marker " + (isGreedyToLeft() ? "[" : "(") + oldStart + ", " + oldEnd + (isGreedyToRight() ? "]" : ")") + ". Event = " + e + ". Doc length=" + docLength + "; " + getClass()); return; } if (intervalStart() > intervalEnd() || intervalStart() < 0 || intervalEnd() > docLength - e.getNewLength() + e.getOldLength()) { LOG.error( "RangeMarker" + (isGreedyToLeft() ? "[" : "(") + oldStart + ", " + oldEnd + (isGreedyToRight() ? "]" : ")") + " is invalid before update. Event = " + e + ". Doc length=" + docLength + "; " + getClass()); invalidate(e); return; } changedUpdateImpl(e); if (isValid() && (intervalStart() > intervalEnd() || intervalStart() < 0 || intervalEnd() > docLength)) { LOG.error( "Update failed. Event = " + e + ". " + "old doc length=" + docLength + "; real doc length = " + myDocument.getTextLength() + "; " + getClass() + "." + " After update: '" + this + "'"); invalidate(e); } }
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()); }
protected void onCopyChanged(DocumentEvent event, Document original) { final int originalOffset = event.getOffset() + myRangeMarker.getStartOffset(); LOG.assertTrue(originalOffset >= 0); if (!original.isWritable()) return; final String newText = subText(event.getDocument(), event.getOffset(), event.getNewLength()); final int originalEnd = originalOffset + event.getOldLength(); replaceString(original, originalOffset, originalEnd, newText); }
private boolean needToShiftWhiteSpaces(final DocumentEvent e) { if (!CharArrayUtil.containsOnlyWhiteSpaces(e.getNewFragment()) || CharArrayUtil.containLineBreaks(e.getNewFragment())) return e.getOldLength() > 0; if (e.getOffset() == 0) return false; final char charBefore = myEditor.getDocument().getCharsSequence().charAt(e.getOffset() - 1); // final char charAfter = myEditor.getDocument().getCharsSequence().charAt(e.getOffset() + // e.getNewLength()); return Character.isWhitespace(charBefore) /* || !Character.isWhitespace(charAfter)*/; }
public void documentChanged(DocumentEvent e) { finishUpdate(); DocumentEventImpl event = (DocumentEventImpl) e; final Document document = myEditor.getDocument(); boolean performSoftWrapAdjustment = e.getNewLength() > 0 // We want to put caret just after the last added symbol // There is a possible case that the user removes text just before the soft wrap. We // want to keep caret // on a visual line with soft wrap start then. || myEditor.getSoftWrapModel().getSoftWrap(e.getOffset()) != null; if (event.isWholeTextReplaced()) { int newLength = document.getTextLength(); if (myOffset == newLength - e.getNewLength() + e.getOldLength() || newLength == 0) { moveToOffset(newLength, performSoftWrapAdjustment); } else { final int line; try { line = event.translateLineViaDiff(myLogicalCaret.line); moveToLogicalPosition( new LogicalPosition(line, myLogicalCaret.column), performSoftWrapAdjustment); } catch (FilesTooBigForDiffException e1) { LOG.info(e1); moveToOffset(0); } } } else { if (document instanceof DocumentEx && ((DocumentEx) document).isInBulkUpdate()) return; int startOffset = e.getOffset(); int oldEndOffset = startOffset + e.getOldLength(); int newOffset = myOffset; if (myOffset > oldEndOffset || myOffset == oldEndOffset && needToShiftWhiteSpaces(e)) { newOffset += e.getNewLength() - e.getOldLength(); } else if (myOffset >= startOffset && myOffset <= oldEndOffset) { newOffset = Math.min(newOffset, startOffset + e.getNewLength()); } newOffset = Math.min(newOffset, document.getTextLength()); // if (newOffset != myOffset) { moveToOffset(newOffset, performSoftWrapAdjustment); // } // else { // moveToVisualPosition(oldPosition); // } } myVisualLineStart = myEditor.logicalPositionToOffset( myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0))); myVisualLineEnd = myEditor.logicalPositionToOffset( myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line + 1, 0))); }
@Override public void documentChanged(DocumentEvent event) { final Document document = event.getDocument(); final FileViewProvider viewProvider = getCachedViewProvider(document); if (viewProvider == null) return; if (!isRelevant(viewProvider)) return; ApplicationManager.getApplication().assertWriteAccessAllowed(); final List<PsiFile> files = viewProvider.getAllFiles(); boolean commitNecessary = true; for (PsiFile file : files) { mySmartPointerManager.unfastenBelts(file, event.getOffset()); final TextBlock textBlock = TextBlock.get(file); if (textBlock.isLocked()) { commitNecessary = false; continue; } textBlock.documentChanged(event); 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()); // 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) { myUncommittedDocuments.add(document); if (forceCommit) { commitDocument(document); } else if (!((DocumentEx) document).isInBulkUpdate()) { myDocumentCommitProcessor.commitAsynchronously(myProject, document, event); } } }
public void documentChanged(DocumentEvent event) { if (((DocumentEx) event.getDocument()).isInBulkUpdate()) { myFoldTree.clear(); } else { updateCachedOffsets(); } }
@Nullable static ProperTextRange applyChange( @NotNull DocumentEvent e, int intervalStart, int intervalEnd, boolean isGreedyToLeft, boolean isGreedyToRight) { if (intervalStart == intervalEnd) { return processIfOnePoint(e, intervalStart, intervalEnd, isGreedyToRight); } final int offset = e.getOffset(); final int oldLength = e.getOldLength(); final int newLength = e.getNewLength(); // changes after the end. if (intervalEnd < offset || !isGreedyToRight && intervalEnd == offset) { return new ProperTextRange(intervalStart, intervalEnd); } // changes before start if (intervalStart > offset + oldLength || !isGreedyToLeft && intervalStart == offset + oldLength) { return new ProperTextRange( intervalStart + newLength - oldLength, intervalEnd + newLength - oldLength); } // Changes inside marker's area. Expand/collapse. if (intervalStart <= offset && intervalEnd >= offset + oldLength) { return new ProperTextRange(intervalStart, intervalEnd + newLength - oldLength); } // At this point we either have (myStart xor myEnd inside changed area) or whole area changed. // Replacing prefix or suffix... if (intervalStart >= offset && intervalStart <= offset + oldLength && intervalEnd > offset + oldLength) { return new ProperTextRange(offset + newLength, intervalEnd + newLength - oldLength); } if (intervalEnd >= offset && intervalEnd <= offset + oldLength && intervalStart < offset) { return new ProperTextRange(intervalStart, offset); } return null; }
private void altCommitToOriginal(@NotNull DocumentEvent e) { final PsiFile origPsiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myOrigDocument); String newText = myNewDocument.getText(); // prepare guarded blocks LinkedHashMap<String, String> replacementMap = new LinkedHashMap<String, String>(); int count = 0; for (RangeMarker o : ContainerUtil.reverse(((DocumentEx) myNewDocument).getGuardedBlocks())) { String replacement = o.getUserData(REPLACEMENT_KEY); String tempText = "REPLACE" + (count++) + Long.toHexString(StringHash.calc(replacement)); newText = newText.substring(0, o.getStartOffset()) + tempText + newText.substring(o.getEndOffset()); replacementMap.put(tempText, replacement); } // run preformat processors final int hostStartOffset = myAltFullRange.getStartOffset(); myEditor.getCaretModel().moveToOffset(hostStartOffset); for (CopyPastePreProcessor preProcessor : Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) { newText = preProcessor.preprocessOnPaste(myProject, origPsiFile, myEditor, newText, null); } myOrigDocument.replaceString(hostStartOffset, myAltFullRange.getEndOffset(), newText); // replace temp strings for guarded blocks for (String tempText : replacementMap.keySet()) { int idx = CharArrayUtil.indexOf( myOrigDocument.getCharsSequence(), tempText, hostStartOffset, myAltFullRange.getEndOffset()); myOrigDocument.replaceString(idx, idx + tempText.length(), replacementMap.get(tempText)); } // JAVA: fix occasional char literal concatenation fixDocumentQuotes(myOrigDocument, hostStartOffset - 1); fixDocumentQuotes(myOrigDocument, myAltFullRange.getEndOffset()); // reformat PsiDocumentManager.getInstance(myProject).commitDocument(myOrigDocument); Runnable task = () -> { try { CodeStyleManager.getInstance(myProject) .reformatRange(origPsiFile, hostStartOffset, myAltFullRange.getEndOffset(), true); } catch (IncorrectOperationException e1) { // LOG.error(e); } }; DocumentUtil.executeInBulk(myOrigDocument, true, task); PsiElement newInjected = InjectedLanguageManager.getInstance(myProject) .findInjectedElementAt(origPsiFile, hostStartOffset); DocumentWindow documentWindow = newInjected == null ? null : InjectedLanguageUtil.getDocumentWindow(newInjected); if (documentWindow != null) { myEditor.getCaretModel().moveToOffset(documentWindow.injectedToHost(e.getOffset())); myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); } }
@Override public void documentChanged(DocumentEvent event) { try { if (!((DocumentEx) event.getDocument()).isInBulkUpdate()) { updateCachedOffsets(); } } finally { myDocumentChangeProcessed = true; } }
protected void onOriginalChanged(DocumentEvent event, Document copy) { if (!myRangeMarker.isValid()) { fireContentInvalid(); return; } replaceString( copy, 0, copy.getTextLength(), subText(event.getDocument(), myRangeMarker.getStartOffset(), getLength())); }
public void documentChanged(@NotNull DocumentEvent event) { if (!VimPlugin.isEnabled()) { return; } Project[] projs = ProjectManager.getInstance().getOpenProjects(); for (Project proj : projs) { Editor[] editors = EditorFactory.getInstance().getEditors(event.getDocument(), proj); for (Editor editor : editors) { Collection hls = EditorData.getLastHighlights(editor); if (hls == null) { continue; } int soff = event.getOffset(); int eoff = soff + event.getNewLength(); if (logger.isDebugEnabled()) { logger.debug("hls=" + hls); logger.debug("event=" + event); } Iterator iter = hls.iterator(); while (iter.hasNext()) { RangeHighlighter rh = (RangeHighlighter) iter.next(); if (!rh.isValid() || (eoff >= rh.getStartOffset() && soff <= rh.getEndOffset())) { iter.remove(); editor.getMarkupModel().removeHighlighter(rh); } } int sl = editor.offsetToLogicalPosition(soff).line; int el = editor.offsetToLogicalPosition(eoff).line; VimPlugin.getSearch().highlightSearchLines(editor, false, sl, el); hls = EditorData.getLastHighlights(editor); if (logger.isDebugEnabled()) { logger.debug("sl=" + sl + ", el=" + el); logger.debug("hls=" + hls); } } } }
@Nullable private static ProperTextRange processIfOnePoint( @NotNull DocumentEvent e, int intervalStart, int intervalEnd, boolean greedyRight) { int offset = e.getOffset(); int oldLength = e.getOldLength(); int oldEnd = offset + oldLength; if (offset < intervalStart && intervalStart < oldEnd) { return null; } if (offset == intervalStart && oldLength == 0 && greedyRight) { return new ProperTextRange(intervalStart, intervalEnd + e.getNewLength()); } if (intervalStart > oldEnd || intervalStart == oldEnd && oldLength > 0) { return new ProperTextRange( intervalStart + e.getNewLength() - oldLength, intervalEnd + e.getNewLength() - oldLength); } return new ProperTextRange(intervalStart, intervalEnd); }
// remembering old end before document change because of problems // with fragments containing "\n" @Override public void beforeDocumentChange(DocumentEvent e) { if (!myTaskFile.isTrackChanges()) { return; } myTaskFile.setHighlightErrors(true); final Document document = e.getDocument(); myTaskWindows.clear(); for (AnswerPlaceholder answerPlaceholder : myTaskFile.getAnswerPlaceholders()) { int twStart = answerPlaceholder.getRealStartOffset(document); int twEnd = twStart + answerPlaceholder.getLength(); myTaskWindows.add(new TaskWindowWrapper(answerPlaceholder, twStart, twEnd)); } }
@Override public void documentChanged(final DocumentEvent e) { final Document document = e.getDocument(); Collection<XLineBreakpointImpl> breakpoints = myBreakpoints.getKeysByValue(document); if (breakpoints != null && !breakpoints.isEmpty()) { myBreakpointsUpdateQueue.queue( new Update(document) { @Override public void run() { updateBreakpoints(document); } }); } }
@Override public void beforeDocumentChange(DocumentEvent e) { myApplication.assertIsDispatchThread(); synchronized (myLock) { if (myReleased) return; if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return; assert myDocument == e.getDocument(); try { myLine1 = myDocument.getLineNumber(e.getOffset()); if (e.getOldLength() == 0) { myBeforeChangedLines = 1; } else { int line1 = myLine1; int line2 = myDocument.getLineNumber(e.getOffset() + e.getOldLength()); myBeforeChangedLines = line2 - line1 + 1; } myBeforeTotalLines = getLineCount(myDocument); } catch (ProcessCanceledException ignore) { } } }
@Override public void beforeDocumentChange(DocumentEvent event) { if (project.isDisposed()) { return; } if (!isListening) { System.out.println("Ignoring change."); return; } VirtualFile file = FileDocumentManager.getInstance().getFile(event.getDocument()); // Make sure file exists if (file == null) { return; } // Make sure file is in the project if (!ProjectFileIndex.SERVICE.getInstance(project).isInSource(file)) { return; } String dummyIdentifier = "IntellijIdeaRulezzz"; if (!event.getNewFragment().toString().contains(dummyIdentifier)) { if (!event.isWholeTextReplaced()) { // Get path relative to project root (e.g. src/Sample.java) Path basePath = Paths.get(project.getBasePath()); Path absoluteFilePath = Paths.get(file.getPath()); String relativeFilePath = basePath.relativize(absoluteFilePath).toString(); String documentText = event.getDocument().getText(); String oldFragment = event.getOldFragment().toString(); String newFragment = event.getNewFragment().toString(); int offset = event.getOffset(); IOPatcher patcher = new IOPatcher(); LinkedList<Patch> patches = patcher.makePatchAsList(documentText, oldFragment, newFragment, offset); UserEdit edit = new UserEdit(0, relativeFilePath, patches); for (EditorEvent editorEvent : events) { editorEvent.sendChange(edit); } } } }
@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); }
// remembering old end before document change because of problems // with fragments containing "\n" @Override public void beforeDocumentChange(DocumentEvent e) { if (!myTaskFile.isTrackChanges()) { return; } myTaskFile.setHighlightErrors(true); Document document = e.getDocument(); myAnswerPlaceholders.clear(); for (AnswerPlaceholder answerPlaceholder : myTaskFile.getAnswerPlaceholders()) { int twStart = answerPlaceholder.getRealStartOffset(document); int length = usePossibleAnswerLength ? answerPlaceholder.getPossibleAnswerLength() : answerPlaceholder.getLength(); int twEnd = twStart + length; myAnswerPlaceholders.add(new AnswerPlaceholderWrapper(answerPlaceholder, twStart, twEnd)); } }
@NotNull private int[] fixRanges(@NotNull DocumentEvent e, int line1, int line2) { CharSequence document = myDocument.getCharsSequence(); int offset = e.getOffset(); if (e.getOldLength() == 0 && e.getNewLength() != 0) { if (StringUtil.endsWithChar(e.getNewFragment(), '\n') && isNewline(offset - 1, document)) { return new int[] {line1, line2 - 1}; } if (StringUtil.startsWithChar(e.getNewFragment(), '\n') && isNewline(offset + e.getNewLength(), document)) { return new int[] {line1 + 1, line2}; } } if (e.getOldLength() != 0 && e.getNewLength() == 0) { if (StringUtil.endsWithChar(e.getOldFragment(), '\n') && isNewline(offset - 1, document)) { return new int[] {line1, line2 - 1}; } if (StringUtil.startsWithChar(e.getOldFragment(), '\n') && isNewline(offset + e.getNewLength(), document)) { return new int[] {line1 + 1, line2}; } } return new int[] {line1, line2}; }
@Override public void beforeDocumentChange(DocumentEvent event) { if (myDocument.isInBulkUpdate()) return; myDocumentChangeStartOffset = event.getOffset(); myDocumentChangeEndOffset = event.getOffset() + event.getNewLength(); }
@Override public void documentChanged(DocumentEvent event) { invalidateRange(event.getOffset(), event.getOffset() + event.getNewLength()); }
@Override public synchronized void documentChanged(DocumentEvent e) { final Document document = e.getDocument(); if (document instanceof DocumentEx && ((DocumentEx) document).isInBulkUpdate()) { mySegments.removeAll(); return; } if (mySegments.getSegmentCount() == 0) { setText(document.getCharsSequence()); return; } CharSequence text = document.getCharsSequence(); int oldStartOffset = e.getOffset(); final int segmentIndex; try { segmentIndex = mySegments.findSegmentIndex(oldStartOffset) - 2; } catch (IndexOutOfBoundsException ex) { throw new IndexOutOfBoundsException(ex.getMessage() + " Lexer: " + myLexer); } final int oldStartIndex = Math.max(0, segmentIndex); int startIndex = oldStartIndex; int data; do { data = mySegments.getSegmentData(startIndex); if (isInitialState(data) || startIndex == 0) break; startIndex--; } while (true); int startOffset = mySegments.getSegmentStart(startIndex); int newEndOffset = e.getOffset() + e.getNewLength(); myLexer.start(text, startOffset, text.length(), myInitialState); int lastTokenStart = -1; int lastLexerState = -1; while (myLexer.getTokenType() != null) { if (startIndex >= oldStartIndex) break; int tokenStart = myLexer.getTokenStart(); int lexerState = myLexer.getState(); if (tokenStart == lastTokenStart && lexerState == lastLexerState) { throw new IllegalStateException( "Error while updating lexer: " + e + " document text: " + document.getText()); } int tokenEnd = myLexer.getTokenEnd(); data = packData(myLexer.getTokenType(), lexerState); if (mySegments.getSegmentStart(startIndex) != tokenStart || mySegments.getSegmentEnd(startIndex) != tokenEnd || mySegments.getSegmentData(startIndex) != data) { break; } startIndex++; myLexer.advance(); lastTokenStart = tokenStart; lastLexerState = lexerState; } startOffset = mySegments.getSegmentStart(startIndex); int repaintEnd = -1; int insertSegmentCount = 0; int oldEndIndex = -1; SegmentArrayWithData insertSegments = new SegmentArrayWithData(); while (myLexer.getTokenType() != null) { int tokenStart = myLexer.getTokenStart(); int lexerState = myLexer.getState(); if (tokenStart == lastTokenStart && lexerState == lastLexerState) { throw new IllegalStateException( "Error while updating lexer: " + e + " document text: " + document.getText()); } lastTokenStart = tokenStart; lastLexerState = lexerState; int tokenEnd = myLexer.getTokenEnd(); data = packData(myLexer.getTokenType(), lexerState); if (tokenStart >= newEndOffset && lexerState == myInitialState) { int shiftedTokenStart = tokenStart - e.getNewLength() + e.getOldLength(); int index = mySegments.findSegmentIndex(shiftedTokenStart); if (mySegments.getSegmentStart(index) == shiftedTokenStart && mySegments.getSegmentData(index) == data) { repaintEnd = tokenStart; oldEndIndex = index; break; } } insertSegments.setElementAt(insertSegmentCount, tokenStart, tokenEnd, data); insertSegmentCount++; myLexer.advance(); } final int shift = e.getNewLength() - e.getOldLength(); if (repaintEnd > 0) { while (insertSegmentCount > 0 && oldEndIndex > startIndex) { if (!segmentsEqual( mySegments, oldEndIndex - 1, insertSegments, insertSegmentCount - 1, shift)) { break; } insertSegmentCount--; oldEndIndex--; repaintEnd = insertSegments.getSegmentStart(insertSegmentCount); insertSegments.remove(insertSegmentCount, insertSegmentCount + 1); } } if (repaintEnd == -1) { repaintEnd = text.length(); } if (oldEndIndex < 0) { oldEndIndex = mySegments.getSegmentCount(); } mySegments.shiftSegments(oldEndIndex, shift); mySegments.replace(startIndex, oldEndIndex, insertSegments); if (insertSegmentCount == 0 || oldEndIndex == startIndex + 1 && insertSegmentCount == 1 && data == mySegments.getSegmentData(startIndex)) { return; } myEditor.repaint(startOffset, repaintEnd); }
@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); } }