public static String getUnescapedText( PsiFile file, @Nullable final PsiElement startElement, @Nullable final PsiElement endElement) { final InjectedLanguageManager manager = InjectedLanguageManager.getInstance(file.getProject()); if (manager.getInjectionHost(file) == null) { return file.getText() .substring( startElement == null ? 0 : startElement.getTextRange().getStartOffset(), endElement == null ? file.getTextLength() : endElement.getTextRange().getStartOffset()); } final StringBuilder sb = new StringBuilder(); file.accept( new PsiRecursiveElementWalkingVisitor() { Boolean myState = startElement == null ? Boolean.TRUE : null; @Override public void visitElement(PsiElement element) { if (element == startElement) myState = Boolean.TRUE; if (element == endElement) myState = Boolean.FALSE; if (Boolean.FALSE == myState) return; if (Boolean.TRUE == myState && element.getFirstChild() == null) { sb.append(getUnescapedLeafText(element, false)); } else { super.visitElement(element); } } }); return sb.toString(); }
public static void clearCaches( @NotNull PsiFile injected, @NotNull DocumentWindowImpl documentWindow) { VirtualFileWindowImpl virtualFile = (VirtualFileWindowImpl) injected.getVirtualFile(); PsiManagerEx psiManagerEx = (PsiManagerEx) injected.getManager(); if (psiManagerEx.getProject().isDisposed()) return; psiManagerEx.getFileManager().setViewProvider(virtualFile, null); PsiElement context = InjectedLanguageManager.getInstance(injected.getProject()).getInjectionHost(injected); PsiFile hostFile; if (context != null) { hostFile = context.getContainingFile(); } else { VirtualFile delegate = virtualFile.getDelegate(); hostFile = delegate.isValid() ? psiManagerEx.findFile(delegate) : null; } if (hostFile != null) { // modification of cachedInjectedDocuments must be under PsiLock synchronized (PsiLock.LOCK) { List<DocumentWindow> cachedInjectedDocuments = getCachedInjectedDocuments(hostFile); for (int i = cachedInjectedDocuments.size() - 1; i >= 0; i--) { DocumentWindow cachedInjectedDocument = cachedInjectedDocuments.get(i); if (cachedInjectedDocument == documentWindow) { cachedInjectedDocuments.remove(i); } } } } }
@Nullable public static DocumentWindow getDocumentWindow(@NotNull PsiElement element) { PsiFile file = element.getContainingFile(); if (file == null) return null; VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile instanceof VirtualFileWindow) return ((VirtualFileWindow) virtualFile).getDocumentWindow(); return null; }
public static PsiFile getTopLevelFile(@NotNull PsiElement element) { PsiFile containingFile = element.getContainingFile(); if (containingFile == null) return null; Document document = PsiDocumentManager.getInstance(element.getProject()).getCachedDocument(containingFile); if (document instanceof DocumentWindow) { PsiElement host = InjectedLanguageManager.getInstance(containingFile.getProject()) .getInjectionHost(containingFile); if (host != null) containingFile = host.getContainingFile(); } return containingFile; }
public static boolean isInInjectedLanguagePrefixSuffix(@NotNull final PsiElement element) { PsiFile injectedFile = element.getContainingFile(); if (injectedFile == null) return false; Project project = injectedFile.getProject(); InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(project); if (!languageManager.isInjectedFragment(injectedFile)) return false; TextRange elementRange = element.getTextRange(); List<TextRange> editables = languageManager.intersectWithAllEditableFragments(injectedFile, elementRange); int combinedEdiablesLength = 0; for (TextRange editable : editables) { combinedEdiablesLength += editable.getLength(); } return combinedEdiablesLength != elementRange.getLength(); }
// returns (injected psi, leaf element at the offset, language of the leaf element) // since findElementAt() is expensive, we trying to reuse its result @NotNull private static Trinity<PsiElement, PsiElement, Language> tryOffset( @NotNull PsiFile hostFile, final int offset, @NotNull PsiDocumentManager documentManager) { FileViewProvider provider = hostFile.getViewProvider(); Language leafLanguage = null; PsiElement leafElement = null; for (Language language : provider.getLanguages()) { PsiElement element = provider.findElementAt(offset, language); if (element != null) { if (leafLanguage == null) { leafLanguage = language; leafElement = element; } PsiElement injected = findInside(element, hostFile, offset, documentManager); if (injected != null) return Trinity.create(injected, element, language); } // maybe we are at the border between two psi elements, then try to find injection at the end // of the left element if (offset != 0 && (element == null || element.getTextRange().getStartOffset() == offset)) { PsiElement leftElement = provider.findElementAt(offset - 1, language); if (leftElement != null && leftElement.getTextRange().getEndOffset() == offset) { PsiElement injected = findInside(leftElement, hostFile, offset, documentManager); if (injected != null) return Trinity.create(injected, element, language); } } } return Trinity.create(null, leafElement, leafLanguage); }
// consider injected elements public static PsiElement findElementAtNoCommit(@NotNull PsiFile file, int offset) { FileViewProvider viewProvider = file.getViewProvider(); Trinity<PsiElement, PsiElement, Language> result = null; if (!(viewProvider instanceof InjectedFileViewProvider)) { PsiDocumentManager documentManager = PsiDocumentManager.getInstance(file.getProject()); result = tryOffset(file, offset, documentManager); PsiElement injected = result.first; if (injected != null) { return injected; } } Language baseLanguage = viewProvider.getBaseLanguage(); if (result != null && baseLanguage == result.third) { return result.second; // already queried } return viewProvider.findElementAt(offset, baseLanguage); }
public static void enumerate( @NotNull PsiElement host, @NotNull PsiFile containingFile, boolean probeUp, @NotNull PsiLanguageInjectionHost.InjectedPsiVisitor visitor) { // do not inject into nonphysical files except during completion if (!containingFile.isPhysical() && containingFile.getOriginalFile() == containingFile) { final PsiElement context = InjectedLanguageManager.getInstance(containingFile.getProject()) .getInjectionHost(containingFile); if (context == null) return; final PsiFile file = context.getContainingFile(); if (file == null || !file.isPhysical() && file.getOriginalFile() == file) return; } if (containingFile.getViewProvider() instanceof InjectedFileViewProvider) return; // no injection inside injection PsiElement inTree = loadTree(host, containingFile); if (inTree != host) { host = inTree; containingFile = host.getContainingFile(); } MultiHostRegistrarImpl registrar = probeElementsUp(host, containingFile, probeUp); if (registrar == null) { return; } List<Pair<Place, PsiFile>> places = registrar.getResult(); for (Pair<Place, PsiFile> pair : places) { PsiFile injectedPsi = pair.second; visitor.visit(injectedPsi, pair.first); } }
public static PsiElement findInjectedElementNoCommit( @NotNull PsiFile hostFile, final int offset) { if (hostFile instanceof PsiCompiledElement) return null; Project project = hostFile.getProject(); if (InjectedLanguageManager.getInstance(project).isInjectedFragment(hostFile)) return null; final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); Trinity<PsiElement, PsiElement, Language> result = tryOffset(hostFile, offset, documentManager); PsiElement injected = result.first; return injected; }
@NotNull static PsiElement loadTree(@NotNull PsiElement host, @NotNull PsiFile containingFile) { if (containingFile instanceof DummyHolder) { PsiElement context = containingFile.getContext(); if (context != null) { PsiFile topFile = context.getContainingFile(); topFile.getNode(); // load tree TextRange textRange = host.getTextRange().shiftRight(context.getTextRange().getStartOffset()); PsiElement inLoadedTree = PsiTreeUtil.findElementOfClassAtRange( topFile, textRange.getStartOffset(), textRange.getEndOffset(), host.getClass()); if (inLoadedTree != null) { host = inLoadedTree; } } } return host; }
@NotNull public static List<DocumentWindow> getCachedInjectedDocuments(@NotNull PsiFile hostPsiFile) { // modification of cachedInjectedDocuments must be under PsiLock only List<DocumentWindow> injected = hostPsiFile.getUserData(INJECTED_DOCS_KEY); if (injected == null) { injected = ((UserDataHolderEx) hostPsiFile) .putUserDataIfAbsent( INJECTED_DOCS_KEY, ContainerUtil.<DocumentWindow>createEmptyCOWList()); } return injected; }
public static Editor openEditorFor(@NotNull PsiFile file, @NotNull Project project) { Document document = PsiDocumentManager.getInstance(project).getDocument(file); // may return editor injected in current selection in the host editor, not for the file passed // as argument VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile == null) { return null; } if (virtualFile instanceof VirtualFileWindow) { virtualFile = ((VirtualFileWindow) virtualFile).getDelegate(); } Editor editor = FileEditorManager.getInstance(project) .openTextEditor(new OpenFileDescriptor(project, virtualFile, -1), false); if (editor == null || editor instanceof EditorWindow || editor.isDisposed()) return editor; if (document instanceof DocumentWindowImpl) { return EditorWindow.create((DocumentWindowImpl) document, (EditorImpl) editor, file); } return editor; }
@NotNull public static Editor getInjectedEditorForInjectedFile( @NotNull Editor hostEditor, @Nullable final PsiFile injectedFile) { if (injectedFile == null || hostEditor instanceof EditorWindow || hostEditor.isDisposed()) return hostEditor; Project project = hostEditor.getProject(); if (project == null) project = injectedFile.getProject(); Document document = PsiDocumentManager.getInstance(project).getDocument(injectedFile); if (!(document instanceof DocumentWindowImpl)) return hostEditor; DocumentWindowImpl documentWindow = (DocumentWindowImpl) document; SelectionModel selectionModel = hostEditor.getSelectionModel(); if (selectionModel.hasSelection()) { int selstart = selectionModel.getSelectionStart(); int selend = selectionModel.getSelectionEnd(); if (!documentWindow.containsRange(selstart, selend)) { // selection spreads out the injected editor range return hostEditor; } } if (!documentWindow.isValid()) return hostEditor; // since the moment we got hold of injectedFile and this moment call, // document may have been dirtied return EditorWindow.create(documentWindow, (EditorImpl) hostEditor, injectedFile); }
public static Place getShreds(@NotNull PsiFile injectedFile) { FileViewProvider viewProvider = injectedFile.getViewProvider(); return getShreds(viewProvider); }
public static List< Trinity<IElementType, SmartPsiElementPointer<PsiLanguageInjectionHost>, TextRange>> getHighlightTokens(@NotNull PsiFile file) { return file.getUserData(HIGHLIGHT_TOKENS); }
private static MultiHostRegistrarImpl probeElementsUp( @NotNull PsiElement element, @NotNull PsiFile hostPsiFile, boolean probeUp) { PsiManager psiManager = hostPsiFile.getManager(); final Project project = psiManager.getProject(); InjectedLanguageManagerImpl injectedManager = InjectedLanguageManagerImpl.getInstanceImpl(project); if (injectedManager == null) { return null; // for tests } MultiHostRegistrarImpl registrar = null; PsiElement current = element; nextParent: while (current != null && current != hostPsiFile) { ProgressManager.checkCanceled(); if ("EL".equals(current.getLanguage().getID())) break; ParameterizedCachedValue<MultiHostRegistrarImpl, PsiElement> data = current.getUserData(INJECTED_PSI); if (data == null) { registrar = InjectedPsiCachedValueProvider.doCompute( current, injectedManager, project, hostPsiFile); } else { registrar = data.getValue(current); } current = current.getParent(); // cache no injection for current if (registrar != null) { List<Pair<Place, PsiFile>> places = registrar.getResult(); // check that injections found intersect with queried element TextRange elementRange = element.getTextRange(); for (Pair<Place, PsiFile> pair : places) { Place place = pair.first; for (PsiLanguageInjectionHost.Shred shred : place) { if (shred.getHost().getTextRange().intersects(elementRange)) { if (place.isValid()) break nextParent; } } } } if (!probeUp) { break; } } if (probeUp) { // cache only if we walked all parents for (PsiElement e = element; e != current && e != null && e != hostPsiFile; e = e.getParent()) { ProgressManager.checkCanceled(); if (registrar == null) { e.putUserData(INJECTED_PSI, null); } else { ParameterizedCachedValue<MultiHostRegistrarImpl, PsiElement> cachedValue = CachedValuesManager.getManager(project) .createParameterizedCachedValue(INJECTED_PSI_PROVIDER, false); CachedValueProvider.Result<MultiHostRegistrarImpl> result = CachedValueProvider.Result.create( registrar, PsiModificationTracker.MODIFICATION_COUNT, registrar); ((PsiParameterizedCachedValue<MultiHostRegistrarImpl, PsiElement>) cachedValue) .setValue(result); e.putUserData(INJECTED_PSI, cachedValue); } } } return registrar; }
public static void clearCachedInjectedFragmentsForFile(@NotNull PsiFile file) { file.putUserData(INJECTED_DOCS_KEY, null); }