package com.linkedin.intellij.dust; import com.intellij.lang.Language; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.openapi.fileTypes.PlainTextLanguage; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.templateLanguages.*; import com.intellij.psi.tree.IElementType; import com.intellij.util.IncorrectOperationException; import com.linkedin.intellij.dust.psi.DustPsiUtil; import com.linkedin.intellij.dust.psi.DustTokenType; import com.linkedin.intellij.dust.psi.DustTypes; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Set; /** * Created with IntelliJ IDEA. * User: yzhang * Date: 2/1/13 * Time: 1:15 PM */ public class DustFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements TemplateLanguageFileViewProvider { public static IElementType OUTER_TYPE = new DustTokenType("OUTER"); private static final TemplateDataElementType templateDataElementType = new TemplateDataElementType("DUST_TEMPLATE_DATA", DustLanguage.INSTANCE, DustTypes.HTML, OUTER_TYPE); // main language of the file (HTML) private final Language myTemplateDataLanguage; // default constructor from parent public DustFileViewProvider(PsiManager manager, VirtualFile file, boolean physical) { super(manager, file, physical); // get the main language of the file Language dataLang = TemplateDataLanguageMappings.getInstance(manager.getProject()).getMapping(file); if (dataLang == null) dataLang = StdFileTypes.HTML.getLanguage(); // some magic? if (dataLang instanceof TemplateLanguage) { myTemplateDataLanguage = PlainTextLanguage.INSTANCE; } else { myTemplateDataLanguage = LanguageSubstitutors.INSTANCE.substituteLanguage(dataLang, file, manager.getProject()); } } // constructor to be used by self public DustFileViewProvider(PsiManager psiManager, VirtualFile virtualFile, boolean physical, Language myTemplateDataLanguage) { super(psiManager, virtualFile, physical); this.myTemplateDataLanguage = myTemplateDataLanguage; } @NotNull @Override public Language getBaseLanguage() { return DustLanguage.INSTANCE; } @NotNull @Override public Language getTemplateDataLanguage() { return myTemplateDataLanguage; } @NotNull @Override public Set<Language> getLanguages() { return new THashSet<Language>(Arrays.asList(new Language[]{DustLanguage.INSTANCE, myTemplateDataLanguage})); } @Override protected MultiplePsiFilesPerDocumentFileViewProvider cloneInner(VirtualFile virtualFile) { return new DustFileViewProvider(getManager(), virtualFile, false, myTemplateDataLanguage); } @Override protected PsiFile createFile(Language lang) { // creating file for main lang (HTML) if (lang == myTemplateDataLanguage) { PsiFileImpl file = (PsiFileImpl) LanguageParserDefinitions.INSTANCE.forLanguage(lang).createFile(this); file.setContentElementType(templateDataElementType); return file; } else if (lang == DustLanguage.INSTANCE) { return LanguageParserDefinitions.INSTANCE.forLanguage(lang).createFile(this); } else { return null; } } /** * TODO This method is for just for supporting Intellij 11. Otherwise com.intellij.codeInsight.navigation.actions.GotoDeclarationAction lines 149-151 will throw nullpointerexception or assertion fail. * * @param offset * @return PsiReference */ @Override @Nullable public PsiReference findReferenceAt(int offset) { for (final Language language : getLanguages()) { final PsiElement psiRoot = getPsi(language); if (psiRoot != null) { final PsiElement element = psiRoot.getContainingFile().getViewProvider().findElementAt(offset, language); if (element != null && !(element instanceof OuterLanguageElement) && DustPsiUtil.findParentPartialTagElement(element) != null) { PsiElement[] foundElements = DustGotoDeclarationHandler.gotoReferences(element); PsiReference fakeReference = null; if (foundElements != null && foundElements.length > 0) { fakeReference = new PsiReference() { @Override public PsiElement getElement() { return element; } @Override public TextRange getRangeInElement() { TextRange range = element.getTextRange(); int start = range.getStartOffset(); TextRange newRange = new TextRange(0, range.getEndOffset() - start); return newRange; } @Nullable @Override public PsiElement resolve() { return element; } @NotNull @Override public String getCanonicalText() { return element.getText(); } @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { return null; } @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { return null; } @Override public boolean isReferenceTo(PsiElement element) { return false; } @NotNull @Override public Object[] getVariants() { return new Object[0]; } @Override public boolean isSoft() { return false; } }; } return fakeReference; } } } return super.findReferenceAt(offset); } }