static void performAtomically(@NotNull PsiFile file, @NotNull Runnable runnable) { assert !isInsideAtomicChange(file); file.putUserData(PSI_DOCUMENT_ATOMIC_ACTION, Boolean.TRUE); try { runnable.run(); } finally { file.putUserData(PSI_DOCUMENT_ATOMIC_ACTION, null); } }
public static void installCompletion( Document document, Project project, @Nullable Consumer<Task> consumer, boolean autoPopup) { PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); if (psiFile != null) { //noinspection unchecked psiFile.putUserData( KEY, consumer == null ? (Consumer<Task>) Consumer.EMPTY_CONSUMER : consumer); psiFile.putUserData(AUTO_POPUP_KEY, autoPopup); } }
private static PsiFile createFileCopy(PsiFile file, long caret, long selEnd) { final VirtualFile virtualFile = file.getVirtualFile(); boolean mayCacheCopy = file.isPhysical() && // we don't want to cache code fragment copies even if they appear to be physical virtualFile != null && virtualFile.isInLocalFileSystem(); long combinedOffsets = caret + (selEnd << 32); if (mayCacheCopy) { final Trinity<PsiFile, Document, Long> cached = SoftReference.dereference(file.getUserData(FILE_COPY_KEY)); if (cached != null && cached.first.getClass().equals(file.getClass()) && isCopyUpToDate(cached.second, cached.first)) { final PsiFile copy = cached.first; if (copy.getViewProvider().getModificationStamp() > file.getViewProvider().getModificationStamp() && cached.third.longValue() != combinedOffsets) { // the copy PSI might have some caches that are not cleared on its modification because // there are no events in the copy // so, clear all the caches // hopefully it's a rare situation that the user invokes completion in different parts of // the file // without modifying anything physical in between ((PsiModificationTrackerImpl) file.getManager().getModificationTracker()).incCounter(); } final Document document = cached.second; assert document != null; file.putUserData( FILE_COPY_KEY, new SoftReference<Trinity<PsiFile, Document, Long>>( Trinity.create(copy, document, combinedOffsets))); Document originalDocument = file.getViewProvider().getDocument(); assert originalDocument != null; assert originalDocument.getTextLength() == file.getTextLength() : originalDocument; document.setText(originalDocument.getImmutableCharSequence()); return copy; } } final PsiFile copy = (PsiFile) file.copy(); if (mayCacheCopy) { final Document document = copy.getViewProvider().getDocument(); assert document != null; file.putUserData( FILE_COPY_KEY, new SoftReference<Trinity<PsiFile, Document, Long>>( Trinity.create(copy, document, combinedOffsets))); } return copy; }
private static void assertAfterCommit( Document document, final PsiFile file, String oldPsiText, FileElement myTreeElementBeingReparsedSoItWontBeCollected) { if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { final String documentText = document.getText(); if (ApplicationManagerEx.getApplicationEx().isInternal()) { String fileText = file.getText(); LOG.error( "commitDocument left PSI inconsistent; file len=" + myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() + "; doc len=" + document.getTextLength() + "; doc.getText() == file.getText(): " + Comparing.equal(fileText, documentText) + ";\n file psi text=" + fileText + ";\n doc text=" + documentText + ";\n old psi file text=" + oldPsiText); } else { LOG.error("commitDocument left PSI inconsistent: " + file); } file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); try { BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange( file, 0, documentText.length(), 0, documentText, new ProgressIndicatorBase()); CodeStyleManager.getInstance(file.getProject()) .performActionWithFormatterDisabled( new Runnable() { @Override public void run() { synchronized (PsiLock.LOCK) { doActualPsiChange(file, diffLog); } } }); if (myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() != document.getTextLength()) { LOG.error("PSI is broken beyond repair in: " + file); } } finally { file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); } } }
@Override @Nullable public ObjectStubTree readOrBuild( Project project, final VirtualFile vFile, @Nullable PsiFile psiFile) { final ObjectStubTree fromIndices = readFromVFile(project, vFile); if (fromIndices != null) { return fromIndices; } if (!canHaveStub(vFile)) { return null; } try { final FileContent fc = new FileContentImpl(vFile, vFile.contentsToByteArray()); fc.putUserData(IndexingDataKeys.PROJECT, project); if (psiFile != null) { fc.putUserData(IndexingDataKeys.PSI_FILE, psiFile); if (!vFile.getFileType().isBinary()) { fc.putUserData( IndexingDataKeys.FILE_TEXT_CONTENT_KEY, psiFile.getViewProvider().getContents()); } psiFile.putUserData(PsiFileImpl.BUILDING_STUB, true); } Stub element; try { element = StubTreeBuilder.buildStubTree(fc); } finally { if (psiFile != null) { psiFile.putUserData(PsiFileImpl.BUILDING_STUB, null); } } if (element instanceof PsiFileStub) { StubTree tree = new StubTree((PsiFileStub) element); tree.setDebugInfo("created from file content, timestamp=" + vFile.getTimeStamp()); return tree; } } catch (IOException e) { LOG.info( e); // content can be not cached yet, and the file can be deleted on disk already, without // refresh } return null; }
/** * @return psiFile associated with the content. If the file was not set on FileContentCreation, it * will be created on the spot */ @NotNull @Override public PsiFile getPsiFile() { PsiFile psi = getUserData(IndexingDataKeys.PSI_FILE); if (psi == null) { psi = getUserData(CACHED_PSI); } if (psi == null) { psi = createFileFromText(getContentAsText()); psi.putUserData(IndexingDataKeys.VIRTUAL_FILE, getFile()); putUserData(CACHED_PSI, psi); } return psi; }
@Override public PsiElement getMirror() { TreeElement mirrorTreeElement = myMirrorFileElement; if (mirrorTreeElement == null) { synchronized (myMirrorLock) { mirrorTreeElement = myMirrorFileElement; if (mirrorTreeElement == null) { VirtualFile file = getVirtualFile(); PsiClass[] classes = getClasses(); String fileName = (classes.length > 0 ? classes[0].getName() : file.getNameWithoutExtension()) + JavaFileType.DOT_DEFAULT_EXTENSION; final Document document = FileDocumentManager.getInstance().getDocument(file); assert document != null : file.getUrl(); CharSequence mirrorText = document.getImmutableCharSequence(); boolean internalDecompiler = StringUtil.startsWith(mirrorText, BANNER); PsiFileFactory factory = PsiFileFactory.getInstance(getManager().getProject()); PsiFile mirror = factory.createFileFromText(fileName, JavaLanguage.INSTANCE, mirrorText, false, false); mirror.putUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY, getLanguageLevel()); mirrorTreeElement = SourceTreeToPsiMap.psiToTreeNotNull(mirror); try { final TreeElement finalMirrorTreeElement = mirrorTreeElement; ProgressManager.getInstance() .executeNonCancelableSection( new Runnable() { @Override public void run() { setMirror(finalMirrorTreeElement); putUserData(CLS_DOCUMENT_LINK_KEY, document); } }); } catch (InvalidMirrorException e) { //noinspection ThrowableResultOfMethodCallIgnored LOG.error(file.getUrl(), internalDecompiler ? e : wrapException(e, file)); } ((PsiFileImpl) mirror).setOriginalFile(this); myMirrorFileElement = mirrorTreeElement; } } } return mirrorTreeElement.getPsi(); }
@Nullable @Override public Document getDocument(@NotNull PsiFile file) { if (file instanceof PsiBinaryFile) return null; Document document = getCachedDocument(file); if (document != null) { if (!file.getViewProvider().isPhysical() && document.getUserData(HARD_REF_TO_PSI) == null) { PsiUtilCore.ensureValid(file); cachePsi(document, file); } return document; } FileViewProvider viewProvider = file.getViewProvider(); if (!viewProvider.isEventSystemEnabled()) return null; document = FileDocumentManager.getInstance().getDocument(viewProvider.getVirtualFile()); if (document != null) { if (document.getTextLength() != file.getTextLength()) { String message = "Document/PSI mismatch: " + file + " (" + file.getClass() + "); physical=" + viewProvider.isPhysical(); if (document.getTextLength() + file.getTextLength() < 8096) { message += "\n=== document ===\n" + document.getText() + "\n=== PSI ===\n" + file.getText(); } throw new AssertionError(message); } if (!viewProvider.isPhysical()) { PsiUtilCore.ensureValid(file); cachePsi(document, file); file.putUserData(HARD_REF_TO_DOCUMENT, document); } } return document; }
@Override public void customize(@NotNull EditorEx editor) { boolean apply = isEnabled(); if (!READY) { return; } Project project = editor.getProject(); if (project == null) { return; } PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); if (file == null) { return; } Function<InspectionProfileImpl, InspectionProfileWrapper> strategy = file.getUserData(InspectionProfileWrapper.CUSTOMIZATION_KEY); if (strategy == null) { file.putUserData( InspectionProfileWrapper.CUSTOMIZATION_KEY, strategy = new MyInspectionProfileStrategy()); } if (!(strategy instanceof MyInspectionProfileStrategy)) { return; } ((MyInspectionProfileStrategy) strategy).setUseSpellCheck(apply); if (apply) { editor.putUserData(IntentionManager.SHOW_INTENTION_OPTIONS_KEY, false); } // Update representation. DaemonCodeAnalyzer analyzer = DaemonCodeAnalyzer.getInstance(project); if (analyzer != null) { analyzer.restart(file); } }
private static Collection<String> suggestKeywords(PsiElement position) { TextRange posRange = position.getTextRange(); BnfFile posFile = (BnfFile) position.getContainingFile(); BnfRule statement = PsiTreeUtil.getTopmostParentOfType(position, BnfRule.class); final TextRange range; if (statement != null) { range = new TextRange(statement.getTextRange().getStartOffset(), posRange.getStartOffset()); } else { int offset = posRange.getStartOffset(); for (PsiElement cur = GrammarUtil.getDummyAwarePrevSibling(position); cur != null; cur = GrammarUtil.getDummyAwarePrevSibling(cur)) { if (cur instanceof BnfAttrs) offset = cur.getTextRange().getEndOffset(); else if (cur instanceof BnfRule) offset = cur.getTextRange().getStartOffset(); else continue; break; } range = new TextRange(offset, posRange.getStartOffset()); } final String text = range.isEmpty() ? CompletionInitializationContext.DUMMY_IDENTIFIER : range.substring(posFile.getText()); PsiFile file = PsiFileFactory.getInstance(posFile.getProject()) .createFileFromText("a.bnf", BnfLanguage.INSTANCE, text, true, false); int completionOffset = posRange.getStartOffset() - range.getStartOffset(); GeneratedParserUtilBase.CompletionState state = new GeneratedParserUtilBase.CompletionState(completionOffset) { @Override public String convertItem(Object o) { // we do not have other keywords return o instanceof String ? (String) o : null; } }; file.putUserData(GeneratedParserUtilBase.COMPLETION_STATE_KEY, state); TreeUtil.ensureParsed(file.getNode()); return state.items; }
private XmlNSDescriptor getDefaultNSDescriptorInner( final String namespace, final boolean strict) { final XmlFile containingFile = XmlUtil.getContainingFile(this); if (containingFile == null) return null; final XmlProlog prolog = getProlog(); final XmlDoctype doctype = prolog != null ? prolog.getDoctype() : null; boolean dtdUriFromDocTypeIsNamespace = false; if (XmlUtil.HTML_URI.equals(namespace)) { XmlNSDescriptor nsDescriptor = doctype != null ? getNsDescriptorFormDocType(doctype, containingFile, true) : null; if (doctype != null) { LOG.debug( "Descriptor from doctype " + doctype + " is " + (nsDescriptor != null ? nsDescriptor.getClass().getCanonicalName() : "NULL")); } if (nsDescriptor == null) { String htmlns = ExternalResourceManagerEx.getInstanceEx().getDefaultHtmlDoctype(getProject()); if (htmlns.isEmpty()) { htmlns = Html5SchemaProvider.getHtml5SchemaLocation(); } nsDescriptor = getDefaultNSDescriptor(htmlns, false); } return new HtmlNSDescriptorImpl(nsDescriptor); } else if (XmlUtil.XHTML_URI.equals(namespace)) { String xhtmlNamespace = XmlUtil.getDefaultXhtmlNamespace(getProject()); if (xhtmlNamespace == null || xhtmlNamespace.isEmpty()) { xhtmlNamespace = Html5SchemaProvider.getXhtml5SchemaLocation(); } return getDefaultNSDescriptor(xhtmlNamespace, false); } else if (namespace != null && namespace != XmlUtil.EMPTY_URI) { if (doctype == null || !namespace.equals(XmlUtil.getDtdUri(doctype))) { boolean documentIsSchemaThatDefinesNs = namespace.equals(XmlUtil.getTargetSchemaNsFromTag(getRootTag())); final XmlFile xmlFile = documentIsSchemaThatDefinesNs ? containingFile : XmlUtil.findNamespace(containingFile, namespace); if (xmlFile != null) { final XmlDocument document = xmlFile.getDocument(); if (document != null) { return (XmlNSDescriptor) document.getMetaData(); } } } else { dtdUriFromDocTypeIsNamespace = true; } } if (strict && !dtdUriFromDocTypeIsNamespace) return null; if (doctype != null) { XmlNSDescriptor descr = getNsDescriptorFormDocType(doctype, containingFile, false); if (descr != null) { return XmlExtension.getExtension(containingFile) .getDescriptorFromDoctype(containingFile, descr); } } if (strict) return null; if (namespace == XmlUtil.EMPTY_URI) { final XmlFile xmlFile = XmlUtil.findNamespace(containingFile, namespace); if (xmlFile != null) { return (XmlNSDescriptor) xmlFile.getDocument().getMetaData(); } } try { final PsiFile fileFromText = PsiFileFactory.getInstance(getProject()) .createFileFromText( containingFile.getName() + ".dtd", DTDLanguage.INSTANCE, XmlUtil.generateDocumentDTD(this, false), false, false); if (fileFromText instanceof XmlFile) { fileFromText.putUserData(AUTO_GENERATED, Boolean.TRUE); return (XmlNSDescriptor) ((XmlFile) fileFromText).getDocument().getMetaData(); } } catch (ProcessCanceledException ex) { throw ex; } catch (RuntimeException ignored) { } // e.g. dtd isn't mapped to xml type return null; }
@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; } }; }
public static void clearCachedInjectedFragmentsForFile(@NotNull PsiFile file) { file.putUserData(INJECTED_DOCS_KEY, null); }
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 static void installProvider( @NotNull PsiFile psiFile, @NotNull TextCompletionProvider provider, boolean autoPopup) { psiFile.putUserData(COMPLETING_TEXT_FIELD_KEY, provider); psiFile.putUserData(AUTO_POPUP_KEY, autoPopup); }
public final void invokeCompletion( @NotNull final Project project, @NotNull final Editor editor, int time, boolean hasModifiers, boolean restarted) { final PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project); assert psiFile != null : "no PSI file: " + FileDocumentManager.getInstance().getFile(editor.getDocument()); checkNoWriteAccess(); CompletionAssertions.checkEditorValid(editor); if (editor.isViewer()) { editor.getDocument().fireReadOnlyModificationAttempt(); return; } if (invokedExplicitly) { CompletionLookupArranger.applyLastCompletionStatisticsUpdate(); } if (!CodeInsightUtilBase.prepareEditorForWrite(editor) || !FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project)) { return; } psiFile.putUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING, Boolean.TRUE); CompletionPhase phase = CompletionServiceImpl.getCompletionPhase(); boolean repeated = phase.indicator != null && phase.indicator.isRepeatedInvocation(myCompletionType, editor); /* if (repeated && isAutocompleteCommonPrefixOnInvocation() && phase.fillInCommonPrefix()) { return; } */ int newTime = phase.newCompletionStarted(time, repeated); if (invokedExplicitly) { time = newTime; } if (CompletionServiceImpl.isPhase(CompletionPhase.InsertedSingleItem.class)) { CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); } CompletionServiceImpl.assertPhase( CompletionPhase.NoCompletion.getClass(), CompletionPhase.CommittingDocuments.class); if (time > 1) { if (myCompletionType == CompletionType.BASIC) { FeatureUsageTracker.getInstance() .triggerFeatureUsed(CodeCompletionFeatures.SECOND_BASIC_COMPLETION); } } final CompletionInitializationContext[] initializationContext = {null}; Runnable initCmd = new Runnable() { @Override public void run() { Runnable runnable = new Runnable() { @Override public void run() { EditorUtil.fillVirtualSpaceUntilCaret(editor); PsiDocumentManager.getInstance(project).commitAllDocuments(); CompletionAssertions.assertCommitSuccessful(editor, psiFile); CompletionAssertions.checkEditorValid(editor); initializationContext[0] = runContributorsBeforeCompletion(editor, psiFile); } }; ApplicationManager.getApplication().runWriteAction(runnable); } }; if (autopopup) { CommandProcessor.getInstance().runUndoTransparentAction(initCmd); if (!restarted && shouldSkipAutoPopup(editor, psiFile)) { CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); return; } } else { CommandProcessor.getInstance().executeCommand(project, initCmd, null, null); } insertDummyIdentifier(initializationContext[0], hasModifiers, time); }