package com.dmarcotte.handlebars.file; import com.dmarcotte.handlebars.HbLanguage; import com.intellij.lang.Language; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.lang.ParserDefinition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.LanguageSubstitutors; import com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.templateLanguages.ConfigurableTemplateLanguageFileViewProvider; import com.intellij.psi.templateLanguages.TemplateDataElementType; import com.intellij.psi.templateLanguages.TemplateDataLanguageMappings; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.Set; import java.util.concurrent.ConcurrentMap; import static com.dmarcotte.handlebars.parsing.HbTokenTypes.CONTENT; import static com.dmarcotte.handlebars.parsing.HbTokenTypes.OUTER_ELEMENT_TYPE; public class HbFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements ConfigurableTemplateLanguageFileViewProvider { private final Language myBaseLanguage; private final Language myTemplateLanguage; private static final ConcurrentMap<String, TemplateDataElementType> TEMPLATE_DATA_TO_LANG = ContainerUtil.newConcurrentMap(); private static TemplateDataElementType getTemplateDataElementType(Language lang) { TemplateDataElementType result = TEMPLATE_DATA_TO_LANG.get(lang.getID()); if (result != null) return result; TemplateDataElementType created = new TemplateDataElementType("HB_TEMPLATE_DATA", lang, CONTENT, OUTER_ELEMENT_TYPE); TemplateDataElementType prevValue = TEMPLATE_DATA_TO_LANG.putIfAbsent(lang.getID(), created); return prevValue == null ? created : prevValue; } public HbFileViewProvider(PsiManager manager, VirtualFile file, boolean physical, Language baseLanguage) { this(manager, file, physical, baseLanguage, getTemplateDataLanguage(manager, file)); } public HbFileViewProvider(PsiManager manager, VirtualFile file, boolean physical, Language baseLanguage, Language templateLanguage) { super(manager, file, physical); myBaseLanguage = baseLanguage; myTemplateLanguage = templateLanguage; } @Override public boolean supportsIncrementalReparse(@NotNull Language rootLanguage) { return false; } @NotNull private static Language getTemplateDataLanguage(PsiManager manager, VirtualFile file) { Language dataLang = TemplateDataLanguageMappings.getInstance(manager.getProject()).getMapping(file); if (dataLang == null) { dataLang = HbLanguage.getDefaultTemplateLang().getLanguage(); } Language substituteLang = LanguageSubstitutors.INSTANCE.substituteLanguage(dataLang, file, manager.getProject()); // only use a substituted language if it's templateable if (TemplateDataLanguageMappings.getTemplateableLanguages().contains(substituteLang)) { dataLang = substituteLang; } return dataLang; } @NotNull @Override public Language getBaseLanguage() { return myBaseLanguage; } @NotNull @Override public Language getTemplateDataLanguage() { return myTemplateLanguage; } @NotNull @Override public Set<Language> getLanguages() { return new THashSet<>(Arrays.asList(new Language[]{myBaseLanguage, getTemplateDataLanguage()})); } @Override protected MultiplePsiFilesPerDocumentFileViewProvider cloneInner(VirtualFile virtualFile) { return new HbFileViewProvider(getManager(), virtualFile, false, myBaseLanguage, myTemplateLanguage); } @Override protected PsiFile createFile(@NotNull Language lang) { ParserDefinition parserDefinition = getDefinition(lang); if (parserDefinition == null) { return null; } if (lang.is(getTemplateDataLanguage())) { PsiFileImpl file = (PsiFileImpl)parserDefinition.createFile(this); file.setContentElementType(getTemplateDataElementType(getBaseLanguage())); return file; } else if (lang.isKindOf(getBaseLanguage())) { return parserDefinition.createFile(this); } else { return null; } } private ParserDefinition getDefinition(Language lang) { ParserDefinition parserDefinition; if (lang.isKindOf(getBaseLanguage())) { parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(lang.is(getBaseLanguage()) ? lang : getBaseLanguage()); } else { parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(lang); } return parserDefinition; } }