@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 void contentsChanged(VirtualFileEvent event) { if (event.isFromSave()) return; final VirtualFile file = event.getFile(); final Document document = getCachedDocument(file); if (document == null) { myMultiCaster.fileWithNoDocumentChanged(file); return; } if (isBinaryWithDecompiler(file)) { myMultiCaster.fileWithNoDocumentChanged( file); // This will generate PSI event at FileManagerImpl } long documentStamp = document.getModificationStamp(); long oldFileStamp = event.getOldModificationStamp(); if (documentStamp != oldFileStamp) { LOG.info("reload from disk?"); LOG.info(" documentStamp:" + documentStamp); LOG.info(" oldFileStamp:" + oldFileStamp); if (file.isValid() && askReloadFromDisk(file, document)) { reloadFromDisk(document); } } else { reloadFromDisk(document); } }
public boolean isErrorAnalyzingFinished(PsiFile file) { if (myDisposed) return false; Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file); return document != null && document.getModificationStamp() == file.getModificationStamp() && myFileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL) == null; }
@Override public boolean isFileModified(@NotNull VirtualFile file) { final Document doc = getCachedDocument(file); return doc != null && isDocumentUnsaved(doc) && doc.getModificationStamp() != file.getModificationStamp(); }
public boolean isAllAnalysisFinished(@NotNull PsiFile file) { if (myDisposed) return false; Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file); return document != null && document.getModificationStamp() == file.getModificationStamp() && myFileStatusMap.allDirtyScopesAreNull(document); }
protected TextEditorHighlightingPass( @NotNull final Project project, @Nullable final Document document, boolean runIntentionPassAfter) { myDocument = document; myProject = project; myRunIntentionPassAfter = runIntentionPassAfter; myInitialStamp = document == null ? 0 : document.getModificationStamp(); }
private boolean isValid() { if (isDumbMode() && !DumbService.isDumbAware(this)) { return false; } if (myDocument != null && myDocument.getModificationStamp() != myInitialStamp) return false; if (myProject != null && myDocument != null) { PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument); if (file == null || !file.isValid()) return false; } return true; }
@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); }
protected void reportStubAstMismatch(String message, StubTree stubTree, Document cachedDocument) { rebuildStub(); clearStub("stub-psi mismatch"); scheduleDropCachesWithInvalidStubPsi(); String msg = message; msg += "\n file=" + this; msg += ", modStamp=" + getModificationStamp(); msg += "\n stub debugInfo=" + stubTree.getDebugInfo(); msg += "\n document before=" + cachedDocument; ObjectStubTree latestIndexedStub = StubTreeLoader.getInstance().readFromVFile(getProject(), getVirtualFile()); msg += "\nlatestIndexedStub=" + latestIndexedStub; if (latestIndexedStub != null) { msg += "\n same size=" + (stubTree.getPlainList().size() == latestIndexedStub.getPlainList().size()); msg += "\n debugInfo=" + latestIndexedStub.getDebugInfo(); } FileViewProvider viewProvider = getViewProvider(); msg += "\n viewProvider=" + viewProvider; msg += "\n viewProvider stamp: " + viewProvider.getModificationStamp(); VirtualFile file = viewProvider.getVirtualFile(); msg += "; file stamp: " + file.getModificationStamp(); msg += "; file modCount: " + file.getModificationCount(); Document document = FileDocumentManager.getInstance().getCachedDocument(file); if (document != null) { msg += "\n doc saved: " + !FileDocumentManager.getInstance().isDocumentUnsaved(document); msg += "; doc stamp: " + document.getModificationStamp(); msg += "; doc size: " + document.getTextLength(); msg += "; committed: " + PsiDocumentManager.getInstance(getProject()).isCommitted(document); } throw new AssertionError(msg + "\n------------\n"); }
private void doSaveDocumentInWriteAction(@NotNull Document document, @NotNull VirtualFile file) throws IOException { if (!file.isValid()) { removeFromUnsaved(document); return; } if (!file.equals(getFile(document))) { registerDocument(document, file); } if (!isSaveNeeded(document, file)) { if (document instanceof DocumentEx) { ((DocumentEx) document).setModificationStamp(file.getModificationStamp()); } removeFromUnsaved(document); updateModifiedProperty(file); return; } myMultiCaster.beforeDocumentSaving(document); LOG.assertTrue(file.isValid()); String text = document.getText(); String lineSeparator = getLineSeparator(document, file); if (!lineSeparator.equals("\n")) { text = StringUtil.convertLineSeparators(text, lineSeparator); } Project project = ProjectLocator.getInstance().guessProjectForFile(file); LoadTextUtil.write(project, file, this, text, document.getModificationStamp()); myUnsavedDocuments.remove(document); LOG.assertTrue(!myUnsavedDocuments.contains(document)); myTrailingSpacesStripper.clearLineModificationFlags(document); }
@Override public long getLastCommittedStamp(@NotNull Document document) { Pair<CharSequence, Long> pair = myLastCommittedTexts.get(document); return pair != null ? pair.second : document.getModificationStamp(); }
@Override public long getModificationStamp() { Document document = myDocument == null ? null : myDocument.get(); if (document != null) return document.getModificationStamp(); return myVirtualFile.getModificationStamp(); }
@Nullable("returns runnable to execute under write action in AWT to finish the commit") private Processor<Document> doCommit( @NotNull final Document document, @NotNull final PsiFile file, @NotNull ProgressIndicator indicator, final boolean synchronously, @NotNull PsiDocumentManager documentManager) { ((PsiDocumentManagerImpl) documentManager).clearTreeHardRef(document); final TextBlock textBlock = TextBlock.get(file); if (textBlock.isEmpty()) return null; final long startPsiModificationTimeStamp = file.getModificationStamp(); final long startDocModificationTimeStamp = document.getModificationStamp(); final FileElement myTreeElementBeingReparsedSoItWontBeCollected = ((PsiFileImpl) file).calcTreeElement(); if (textBlock.isEmpty()) return null; // if tree was just loaded above textBlock will be cleared by contentsLoaded final CharSequence chars = document.getCharsSequence(); final Boolean data = document.getUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY); if (data != null) { document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, data); } final String oldPsiText = ApplicationManagerEx.getApplicationEx().isInternal() && !ApplicationManagerEx.getApplicationEx().isUnitTestMode() ? myTreeElementBeingReparsedSoItWontBeCollected.getText() : null; int startOffset; int endOffset; int lengthShift; if (file.getViewProvider().supportsIncrementalReparse(file.getLanguage())) { startOffset = textBlock.getStartOffset(); int psiEndOffset = textBlock.getPsiEndOffset(); endOffset = psiEndOffset; lengthShift = textBlock.getTextEndOffset() - psiEndOffset; } else { startOffset = 0; endOffset = document.getTextLength(); lengthShift = document.getTextLength() - myTreeElementBeingReparsedSoItWontBeCollected.getTextLength(); } assertBeforeCommit( document, file, textBlock, chars, oldPsiText, myTreeElementBeingReparsedSoItWontBeCollected); BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange(file, startOffset, endOffset, lengthShift, chars, indicator); return new Processor<Document>() { @Override public boolean process(Document document) { ApplicationManager.getApplication().assertWriteAccessAllowed(); log( "Finishing", document, synchronously, document.getModificationStamp(), startDocModificationTimeStamp); // if (file.getModificationStamp() != startPsiModificationTimeStamp) return; // optimistic // locking failed if (document.getModificationStamp() != startDocModificationTimeStamp) { return false; // optimistic locking failed } try { textBlock.performAtomically( new Runnable() { @Override public void run() { CodeStyleManager.getInstance(file.getProject()) .performActionWithFormatterDisabled( new Runnable() { @Override public void run() { synchronized (PsiLock.LOCK) { doActualPsiChange(file, diffLog); } } }); } }); assertAfterCommit( document, file, oldPsiText, myTreeElementBeingReparsedSoItWontBeCollected); } finally { textBlock.clear(); SmartPointerManagerImpl.synchronizePointers(file); } return true; } }; }
QuickEditHandler( Project project, @NotNull PsiFile injectedFile, final PsiFile origFile, Editor editor, QuickEditAction action) { myProject = project; myEditor = editor; myAction = action; myOrigDocument = editor.getDocument(); Place shreds = InjectedLanguageUtil.getShreds(injectedFile); FileType fileType = injectedFile.getFileType(); Language language = injectedFile.getLanguage(); PsiLanguageInjectionHost.Shred firstShred = ContainerUtil.getFirstItem(shreds); PsiFileFactory factory = PsiFileFactory.getInstance(project); String text = InjectedLanguageManager.getInstance(project).getUnescapedText(injectedFile); String newFileName = StringUtil.notNullize(language.getDisplayName(), "Injected") + " Fragment " + "(" + origFile.getName() + ":" + firstShred.getHost().getTextRange().getStartOffset() + ")" + "." + fileType.getDefaultExtension(); // preserve \r\n as it is done in MultiHostRegistrarImpl myNewFile = factory.createFileFromText(newFileName, language, text, true, false); myNewVirtualFile = ObjectUtils.assertNotNull((LightVirtualFile) myNewFile.getVirtualFile()); myNewVirtualFile.setOriginalFile(origFile.getVirtualFile()); assert myNewFile != null : "PSI file is null"; assert myNewFile.getTextLength() == myNewVirtualFile.getContent().length() : "PSI / Virtual file text mismatch"; myNewVirtualFile.setOriginalFile(origFile.getVirtualFile()); // suppress possible errors as in injected mode myNewFile.putUserData( InjectedLanguageUtil.FRANKENSTEIN_INJECTION, injectedFile.getUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION)); myNewFile.putUserData(FileContextUtil.INJECTED_IN_ELEMENT, shreds.getHostPointer()); myNewDocument = PsiDocumentManager.getInstance(project).getDocument(myNewFile); assert myNewDocument != null; EditorActionManager.getInstance() .setReadonlyFragmentModificationHandler(myNewDocument, new MyQuietHandler()); myOrigCreationStamp = myOrigDocument.getModificationStamp(); // store creation stamp for UNDO tracking myOrigDocument.addDocumentListener(this, this); myNewDocument.addDocumentListener(this, this); EditorFactory editorFactory = ObjectUtils.assertNotNull(EditorFactory.getInstance()); // not FileEditorManager listener because of RegExp checker and alike editorFactory.addEditorFactoryListener( new EditorFactoryAdapter() { int useCount; @Override public void editorCreated(@NotNull EditorFactoryEvent event) { if (event.getEditor().getDocument() != myNewDocument) return; useCount++; } @Override public void editorReleased(@NotNull EditorFactoryEvent event) { if (event.getEditor().getDocument() != myNewDocument) return; if (--useCount > 0) return; if (Boolean.TRUE.equals( myNewVirtualFile.getUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN))) return; Disposer.dispose(QuickEditHandler.this); } }, this); if ("JAVA".equals(firstShred.getHost().getLanguage().getID())) { PsiLanguageInjectionHost.Shred lastShred = ContainerUtil.getLastItem(shreds); myAltFullRange = myOrigDocument.createRangeMarker( firstShred.getHostRangeMarker().getStartOffset(), lastShred.getHostRangeMarker().getEndOffset()); myAltFullRange.setGreedyToLeft(true); myAltFullRange.setGreedyToRight(true); initGuardedBlocks(shreds); myInjectedFile = null; } else { initMarkers(shreds); myAltFullRange = null; myInjectedFile = injectedFile; } }
public void execute( final Change change, final FilePath filePath, final SLRUMap<Pair<Long, String>, List<BeforeAfter<TextRange>>> cache, final LineStatusTrackerManagerI lstManager) { try { myDocument = null; myOldDocument = documentFromRevision(change.getBeforeRevision()); final String convertedPath = FilePathsHelper.convertPath(filePath); if (filePath.getVirtualFile() != null) { myDocument = FileStatus.DELETED.equals(change.getFileStatus()) ? new DocumentImpl("") : FileDocumentManager.getInstance().getDocument(filePath.getVirtualFile()); if (myDocument != null) { final List<BeforeAfter<TextRange>> cached = cache.get(new Pair<Long, String>(myDocument.getModificationStamp(), convertedPath)); if (cached != null) { myRanges = cached; return; } } } if (myDocument == null) { myDocument = documentFromRevision(change.getAfterRevision()); final List<BeforeAfter<TextRange>> cached = cache.get(new Pair<Long, String>(-1L, convertedPath)); if (cached != null) { myRanges = cached; return; } } ComparisonPolicy comparisonPolicy = DiffManagerImpl.getInstanceEx().getComparisonPolicy(); if (comparisonPolicy == null) { comparisonPolicy = ComparisonPolicy.DEFAULT; } final TextCompareProcessor processor = new TextCompareProcessor(comparisonPolicy); final List<LineFragment> lineFragments = processor.process(myOldDocument.getText(), myDocument.getText()); myRanges = new ArrayList<BeforeAfter<TextRange>>(lineFragments.size()); for (LineFragment lineFragment : lineFragments) { if (!lineFragment.isEqual()) { final TextRange oldRange = lineFragment.getRange(FragmentSide.SIDE1); final TextRange newRange = lineFragment.getRange(FragmentSide.SIDE2); int beforeBegin = myOldDocument.getLineNumber(oldRange.getStartOffset()); int beforeEnd = myOldDocument.getLineNumber( correctRangeEnd(oldRange.getEndOffset(), myOldDocument)); int afterBegin = myDocument.getLineNumber(newRange.getStartOffset()); int afterEnd = myDocument.getLineNumber(correctRangeEnd(newRange.getEndOffset(), myDocument)); if (oldRange.isEmpty()) { beforeEnd = beforeBegin - 1; } if (newRange.isEmpty()) { afterEnd = afterBegin - 1; } myRanges.add( new BeforeAfter<TextRange>( new UnfairTextRange(beforeBegin, beforeEnd), new UnfairTextRange(afterBegin, afterEnd))); } } cache.put( new Pair<Long, String>(myDocument.getModificationStamp(), convertedPath), new ArrayList<BeforeAfter<TextRange>>(myRanges)); } catch (VcsException e) { myException = e; } catch (FilesTooBigForDiffException e) { myException = new VcsException(e); } }
@Override public Collection<LineExtensionInfo> getLineExtensions( @NotNull Project project, @NotNull VirtualFile file, int lineNumber) { if (!Registry.is("ide.debugger.inline")) { return null; } final Map<Pair<VirtualFile, Integer>, Set<XValueNodeImpl>> map = project.getUserData(XVariablesView.DEBUG_VARIABLES); final Map<VirtualFile, Long> timestamps = project.getUserData(XVariablesView.DEBUG_VARIABLES_TIMESTAMPS); final Document doc = FileDocumentManager.getInstance().getDocument(file); if (map == null || timestamps == null || doc == null) { return null; } Map<Variable, VariableValue> oldValues = project.getUserData(CACHE); if (oldValues == null) { oldValues = new HashMap<Variable, VariableValue>(); project.putUserData(CACHE, oldValues); } final Long timestamp = timestamps.get(file); if (timestamp == null || timestamp < doc.getModificationStamp()) { return null; } Set<XValueNodeImpl> values = map.get(Pair.create(file, lineNumber)); if (values != null && !values.isEmpty()) { ArrayList<LineExtensionInfo> result = new ArrayList<LineExtensionInfo>(); for (XValueNodeImpl value : values) { SimpleColoredText text = new SimpleColoredText(); XValueTextRendererImpl renderer = new XValueTextRendererImpl(text); final XValuePresentation presentation = value.getValuePresentation(); if (presentation == null) continue; try { if (presentation instanceof XValueCompactPresentation) { ((XValueCompactPresentation) presentation).renderValue(renderer, value); } else { presentation.renderValue(renderer); } } catch (Exception e) { continue; } final Color color = getForeground(); final String name = value.getName(); result.add(new LineExtensionInfo(" " + name + ": ", color, null, null, Font.PLAIN)); Variable var = new Variable(name, lineNumber); VariableValue variableValue = oldValues.get(var); if (variableValue == null) { variableValue = new VariableValue(text.toString(), null, value.hashCode()); oldValues.put(var, variableValue); } if (variableValue.valueNodeHashCode != value.hashCode()) { variableValue.old = variableValue.actual; variableValue.actual = text.toString(); variableValue.valueNodeHashCode = value.hashCode(); } if (!variableValue.isChanged()) { for (String s : text.getTexts()) { result.add(new LineExtensionInfo(s, color, null, null, Font.PLAIN)); } } else { variableValue.produceChangedParts(result); } } return result; } return null; }
/** * Inserts the <code>rBracesCount</code> of '}' at the <code>rBracesInsertOffset</code> position * and formats the code block. * * @param file target PSI file * @param editor target editor * @param caretOffset target caret offset * @param rBracesInsertOffset target position to insert * @param rBracesCount count of '}' to insert * @return true for success */ protected boolean insertRBraces( @NotNull PsiFile file, @NotNull Editor editor, int caretOffset, int rBracesInsertOffset, int rBracesCount) { final Document document = editor.getDocument(); document.insertString(rBracesInsertOffset, "\n" + StringUtil.repeatSymbol('}', rBracesCount)); // We need to adjust indents of the text that will be moved, hence, need to insert preliminary // line feed. // Example: // if (test1()) { // } else {<caret> if (test2()) { // foo(); // } // We insert here '\n}' after 'foo();' and have the following: // if (test1()) { // } else { if (test2()) { // foo(); // } // } // That is formatted incorrectly because line feed between 'else' and 'if' is not inserted yet // (whole 'if' block is indent anchor // to 'if' code block('{}')). So, we insert temporary line feed between 'if' and 'else', correct // indent and remove that temporary // line feed. document.insertString(caretOffset, "\n"); Project project = file.getProject(); long stamp = document.getModificationStamp(); boolean closingBraceIndentAdjusted; try { PsiDocumentManager.getInstance(project).commitDocument(document); CodeStyleManager.getInstance(project) .adjustLineIndent(file, new TextRange(caretOffset, rBracesInsertOffset + 2)); } catch (IncorrectOperationException e) { LOG.error(e); } finally { closingBraceIndentAdjusted = stamp != document.getModificationStamp(); document.deleteString(caretOffset, caretOffset + 1); } // There is a possible case that formatter was unable to adjust line indent for the closing // brace (that is the case for plain text // document for example). Hence, we're trying to do the manually. if (!closingBraceIndentAdjusted) { int line = document.getLineNumber(rBracesInsertOffset); StringBuilder buffer = new StringBuilder(); int start = document.getLineStartOffset(line); int end = document.getLineEndOffset(line); final CharSequence text = document.getCharsSequence(); for (int i = start; i < end; i++) { char c = text.charAt(i); if (c != ' ' && c != '\t') { break; } else { buffer.append(c); } } if (buffer.length() > 0) { document.insertString(rBracesInsertOffset + 1, buffer); } } return true; }
@SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") @Override public Result beforeCharTyped( final char charTyped, Project project, final Editor editor, PsiFile file, FileType fileType) { assert !inside; inside = true; try { final LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(editor); if (lookup == null) { return Result.CONTINUE; } if (charTyped == ' ' && ChooseItemReplaceAction.hasTemplatePrefix(lookup, TemplateSettings.SPACE_CHAR)) { return Result.CONTINUE; } final CharFilter.Result result = getLookupAction(charTyped, lookup); if (lookup.isLookupDisposed()) { return Result.CONTINUE; } if (!lookup.performGuardedChange( new Runnable() { @Override public void run() { EditorModificationUtil.deleteSelectedText(editor); } })) { return Result.STOP; } if (result == CharFilter.Result.ADD_TO_PREFIX) { Document document = editor.getDocument(); long modificationStamp = document.getModificationStamp(); if (!lookup.performGuardedChange( new Runnable() { @Override public void run() { EditorModificationUtil.typeInStringAtCaretHonorBlockSelection( editor, String.valueOf(charTyped), true); } })) { return Result.STOP; } lookup.appendPrefix(charTyped); if (lookup.isStartCompletionWhenNothingMatches() && lookup.getItems().isEmpty()) { final CompletionProgressIndicator completion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); if (completion != null) { completion.scheduleRestart(); } else { AutoPopupController.getInstance(editor.getProject()).scheduleAutoPopup(editor, null); } } AutoHardWrapHandler.getInstance() .wrapLineIfNecessary( editor, DataManager.getInstance().getDataContext(editor.getContentComponent()), modificationStamp); final CompletionProgressIndicator completion = CompletionServiceImpl.getCompletionService().getCurrentCompletion(); if (completion != null) { completion.prefixUpdated(); } return Result.STOP; } if (result == CharFilter.Result.SELECT_ITEM_AND_FINISH_LOOKUP && lookup.isFocused()) { LookupElement item = lookup.getCurrentItem(); if (item != null) { if (completeTillTypedCharOccurrence(charTyped, lookup, item)) { return Result.STOP; } inside = false; ((CommandProcessorEx) CommandProcessor.getInstance()).enterModal(); try { finishLookup(charTyped, lookup); } finally { ((CommandProcessorEx) CommandProcessor.getInstance()).leaveModal(); } return Result.STOP; } } lookup.hide(); TypedHandler.autoPopupCompletion(editor, charTyped, project); return Result.CONTINUE; } finally { inside = false; } }