removed ugly dirty unsafe hacks

This commit is contained in:
Alexey Kudravtsev
2017-11-21 15:41:47 +03:00
parent 3040e5095f
commit 95e03ff344
14 changed files with 189 additions and 133 deletions
@@ -107,4 +107,6 @@ public abstract class InjectedLanguageManager {
* (which don't work for uncommitted document).
*/
public abstract boolean mightHaveInjectedFragmentAtOffset(@NotNull Document hostDocument, int hostOffset);
@NotNull
public abstract DocumentWindow freezeWindow(@NotNull DocumentWindow document);
}
@@ -25,6 +25,14 @@ import com.intellij.openapi.util.TextRange;
public interface MultiHostRegistrar {
@NotNull /*this*/ MultiHostRegistrar startInjecting(@NotNull Language language);
/**
* @param extension The injected file name will have this extension. Some parsers require extension. By default the extension is taken from the host file.
*/
@NotNull
default /*this*/ MultiHostRegistrar startInjecting(@NotNull Language language, @Nullable String extension) {
return startInjecting(language);
}
@NotNull /*this*/ MultiHostRegistrar addPlace(@NonNls @Nullable String prefix, @NonNls @Nullable String suffix, @NotNull PsiLanguageInjectionHost host, @NotNull TextRange rangeInsideHost);
void doneInjecting();
}
@@ -19,8 +19,8 @@ package com.intellij.psi.impl;
import com.google.common.annotations.VisibleForTesting;
import com.intellij.AppTopics;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.injected.editor.DocumentWindowImpl;
import com.intellij.injected.editor.EditorWindowImpl;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.TransactionGuard;
@@ -43,7 +43,6 @@ import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl;
import com.intellij.util.FileContentUtil;
import com.intellij.util.Processor;
import com.intellij.util.messages.MessageBus;
@@ -206,6 +205,6 @@ public class PsiDocumentManagerImpl extends PsiDocumentManagerBase implements Se
@NotNull
@Override
protected DocumentWindow freezeWindow(@NotNull DocumentWindow document) {
return MultiHostRegistrarImpl.freezeWindow((DocumentWindowImpl)document);
return InjectedLanguageManager.getInstance(myProject).freezeWindow(document);
}
}
@@ -32,6 +32,7 @@ import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.ExtensionPointListener;
import com.intellij.openapi.extensions.Extensions;
@@ -41,7 +42,10 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDocumentManagerBase;
@@ -400,6 +404,16 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager impleme
return InjectedLanguageUtil.mightHaveInjectedFragmentAtCaret(myProject, hostDocument, hostOffset);
}
@NotNull
@Override
public DocumentWindow freezeWindow(@NotNull DocumentWindow document) {
Place shreds = ((DocumentWindowImpl)document).getShreds();
Project project = shreds.getHostPointer().getProject();
DocumentEx delegate = ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(project)).getLastCommittedDocument(document.getDelegate());
Place place = new Place(ContainerUtil.map(shreds, shred -> ((ShredImpl) shred).withPsiRange()));
return new DocumentWindowImpl(delegate, ((DocumentWindowImpl)document).isOneLine(), place);
}
private static int appendRange(@NotNull List<TextRange> result, int start, int length) {
if (length > 0) {
int lastIndex = result.size() - 1;
@@ -22,6 +22,7 @@ import com.intellij.lang.Language;
import com.intellij.lang.LanguageUtil;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
@@ -52,8 +53,8 @@ import java.util.List;
* @author cdr
*/
public class InjectedLanguageUtil {
static final Key<List<Trinity<IElementType, SmartPsiElementPointer<PsiLanguageInjectionHost>, TextRange>>> HIGHLIGHT_TOKENS =
Key.create("HIGHLIGHT_TOKENS");
private static final Logger LOG = Logger.getInstance(InjectedLanguageUtil.class);
static final Key<List<Trinity<IElementType, SmartPsiElementPointer<PsiLanguageInjectionHost>, TextRange>>> HIGHLIGHT_TOKENS = Key.create("HIGHLIGHT_TOKENS");
public static final Key<IElementType> INJECTED_FRAGMENT_TYPE = Key.create("INJECTED_FRAGMENT_TYPE");
public static final Key<Boolean> FRANKENSTEIN_INJECTION = InjectedLanguageManager.FRANKENSTEIN_INJECTION;
// meaning: injected file text is probably incorrect
@@ -648,13 +649,39 @@ public class InjectedLanguageUtil {
getShreds(((VirtualFileWindow)virtualFile).getDocumentWindow()).getHostPointer().getElement() : null;
}
public static <T> void putInjectedFileUserData(MultiHostRegistrar registrar, Key<T> key, T value) {
PsiFile psiFile = getInjectedFile(registrar);
if (psiFile != null) psiFile.putUserData(key, value);
public static <T> void putInjectedFileUserData(@NotNull PsiElement element,
@NotNull Language language,
@NotNull Key<T> key,
T value) {
PsiFile file = getCachedInjectedFileWithLanguage(element, language);
if (file != null) {
file.putUserData(key, value);
}
}
public static PsiFile getInjectedFile(MultiHostRegistrar registrar) {
/**
* @deprecated use {@link #putInjectedFileUserData(com.intellij.psi.PsiElement, com.intellij.lang.Language, com.intellij.openapi.util.Key, java.lang.Object)} instead
*/
public static <T> void putInjectedFileUserData(MultiHostRegistrar registrar, Key<T> key, T value) {
LOG.warn("use #putInjectedFileUserData(com.intellij.psi.PsiElement, com.intellij.lang.Language, com.intellij.openapi.util.Key, java.lang.Object)} instead");
final List<Pair<Place,PsiFile>> result = ((MultiHostRegistrarImpl)registrar).getResult();
return result == null || result.isEmpty() ? null : result.get(result.size() - 1).second;
if (result != null && !result.isEmpty()) {
PsiFile file = result.get(result.size() - 1).getSecond();
file.putUserData(key, value);
}
}
@Nullable
public static PsiFile getCachedInjectedFileWithLanguage(@NotNull PsiElement element, @NotNull Language language) {
if (!element.isValid()) return null;
PsiFile containingFile = element.getContainingFile();
if (containingFile == null || !containingFile.isValid()) return null;
return InjectedLanguageManager.getInstance(containingFile.getProject())
.getCachedInjectedDocumentsInRange(containingFile, element.getTextRange())
.stream()
.map(documentWindow -> PsiDocumentManager.getInstance(containingFile.getProject()).getPsiFile(documentWindow))
.filter(file -> file != null && file.getLanguage() == language)
.findFirst()
.orElse(null);
}
}
@@ -57,7 +57,6 @@ import com.intellij.psi.util.PsiUtilCore;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.PathUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -66,10 +65,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author cdr
*/
public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationTracker {
class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationTracker {
private List<Pair<Place, PsiFile>> result;
private Language myLanguage;
private List<LiteralTextEscaper<? extends PsiLanguageInjectionHost>> escapers;
@@ -86,9 +82,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
private final PsiFile myHostPsiFile;
private ReferenceInjector myReferenceInjector;
public MultiHostRegistrarImpl(@NotNull Project project,
@NotNull PsiFile hostPsiFile,
@NotNull PsiElement contextElement) {
MultiHostRegistrarImpl(@NotNull Project project, @NotNull PsiFile hostPsiFile, @NotNull PsiElement contextElement) {
myProject = project;
myContextElement = contextElement;
myHostPsiFile = PsiUtilCore.getTemplateLanguageFile(hostPsiFile);
@@ -99,18 +93,21 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
myHostDocument = (DocumentEx)viewProvider.getDocument();
}
public List<Pair<Place, PsiFile>> getResult() {
@Nullable("null means nobody cared to call .doneInjecting()")
List<Pair<Place, PsiFile>> getResult() {
return result;
}
@NotNull
public PsiElement getContextElement() {
return myContextElement;
@Override
public MultiHostRegistrar startInjecting(@NotNull Language language) {
return startInjecting(language, null);
}
@Override
@NotNull
public MultiHostRegistrar startInjecting(@NotNull Language language) {
@Override
public MultiHostRegistrar startInjecting(@NotNull Language language, @Nullable String extension) {
fileExtension = extension;
escapers = new SmartList<>();
shreds = new SmartList<>();
outChars = new StringBuilder();
@@ -129,9 +126,6 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
myReferenceInjector = injector;
}
myLanguage = language;
// todo uncomment
//LanguageFileType fileType = myLanguage.getAssociatedFileType();
//fileExtension = fileType == null ? null : fileType.getDefaultExtension();
return this;
}
@@ -146,10 +140,6 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
cleared = true;
}
public void setFileExtension(@Nullable String fileExtension) {
this.fileExtension = fileExtension;
}
@Override
@NotNull
public MultiHostRegistrar addPlace(@NonNls @Nullable String prefix,
@@ -306,12 +296,10 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
}
}
@NotNull
private static DocumentEx createDocument(@NotNull LightVirtualFile virtualFile) {
private static void createDocument(@NotNull LightVirtualFile virtualFile) {
CharSequence content = virtualFile.getContent();
DocumentImpl document = new DocumentImpl(content, StringUtil.indexOf(content, '\r') >= 0, false);
FileDocumentManagerImpl.registerDocument(document, virtualFile);
return document;
}
// returns true if shreds were set, false if old ones were reused
@@ -388,12 +376,12 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
PsiDocumentManagerBase.checkConsistency(psiFile, frozenWindow);
}
public void addToResults(Place place, PsiFile psiFile, @NotNull MultiHostRegistrarImpl from) {
void addToResults(@NotNull Place place, @Nullable /*WAT?*/PsiFile psiFile, @NotNull MultiHostRegistrarImpl from) {
addToResults(place, psiFile);
myReferenceInjector = from.myReferenceInjector;
}
private void addToResults(Place place, PsiFile psiFile) {
private void addToResults(@NotNull Place place, @Nullable /*WAT?*/PsiFile psiFile) {
if (result == null) {
result = new SmartList<>();
}
@@ -430,12 +418,13 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
TreeUtil.clearCaches((TreeElement)parsedNode);
}
// under com.intellij.psi.PsiLock.LOCK
private static PsiFile registerDocument(final DocumentWindowImpl documentWindow,
final PsiFile injectedPsi,
final Place shreds,
final PsiFile hostPsiFile,
final PsiDocumentManager documentManager) {
// under InjectedLanguageManagerImpl.ourInjectionPsiLock
@NotNull
private static PsiFile registerDocument(@NotNull DocumentWindowImpl newDocumentWindow,
@NotNull PsiFile newInjectedPsi,
@NotNull Place shreds,
@NotNull PsiFile hostPsiFile,
@NotNull PsiDocumentManager documentManager) {
List<DocumentWindow> injected = InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile);
for (int i = injected.size()-1; i>=0; i--) {
@@ -451,44 +440,51 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
Disposer.dispose(oldDocument);
continue;
}
InjectedFileViewProvider oldViewProvider = (InjectedFileViewProvider)viewProvider;
final ASTNode injectedNode = injectedPsi.getNode();
final ASTNode newInjectedNode = newInjectedPsi.getNode();
final ASTNode oldFileNode = oldFile.getNode();
assert injectedNode != null : "New node is null";
if (oldDocument.areRangesEqual(documentWindow)) {
if (oldFile.getFileType() != injectedPsi.getFileType() || oldFile.getLanguage() != injectedPsi.getLanguage()) {
assert newInjectedNode != null : "New node is null";
if (oldDocument.areRangesEqual(newDocumentWindow)) {
if (oldFile.getFileType() != newInjectedPsi.getFileType() || oldFile.getLanguage() != newInjectedPsi.getLanguage()) {
injected.remove(i);
Disposer.dispose(oldDocument);
continue;
}
oldFile.putUserData(FileContextUtil.INJECTED_IN_ELEMENT, injectedPsi.getUserData(FileContextUtil.INJECTED_IN_ELEMENT));
oldFile.putUserData(FileContextUtil.INJECTED_IN_ELEMENT, newInjectedPsi.getUserData(FileContextUtil.INJECTED_IN_ELEMENT));
assert shreds.isValid();
if (!oldFile.textMatches(injectedPsi)) {
oldViewProvider.performNonPhysically(() -> {
DebugUtil.startPsiModification("injected tree diff");
try {
final DiffLog diffLog = BlockSupportImpl.mergeTrees(oldFile, oldFileNode, injectedNode, new DaemonProgressIndicator(),
oldFileNode.getText());
DocumentCommitThread.doActualPsiChange(oldFile, diffLog);
}
finally {
DebugUtil.finishPsiModification();
}
});
}
mergePsi(oldFile, oldFileNode, newInjectedPsi, newInjectedNode);
assert shreds.isValid();
return oldFile;
}
else if (intersect(oldDocument, documentWindow)) {
else if (intersect(oldDocument, newDocumentWindow)) {
injected.remove(i); // injected fragments should not overlap. In the End, there can be only one.
}
}
injected.add(documentWindow);
injected.add(newDocumentWindow);
return injectedPsi;
return newInjectedPsi;
}
private static void mergePsi(@NotNull PsiFileImpl oldFile,
@NotNull ASTNode oldFileNode,
@NotNull PsiFile injectedPsi,
@NotNull ASTNode injectedNode) {
if (!oldFile.textMatches(injectedPsi)) {
InjectedFileViewProvider oldViewProvider = (InjectedFileViewProvider)oldFile.getViewProvider();
oldViewProvider.performNonPhysically(() -> {
DebugUtil.startPsiModification("injected tree diff");
try {
final DiffLog diffLog = BlockSupportImpl.mergeTrees(oldFile, oldFileNode, injectedNode, new DaemonProgressIndicator(),
oldFileNode.getText());
DocumentCommitThread.doActualPsiChange(oldFile, diffLog);
}
finally {
DebugUtil.finishPsiModification();
}
});
}
}
private static boolean intersect(DocumentWindowImpl doc1, DocumentWindowImpl doc2) {
@@ -583,8 +579,9 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
return String.valueOf(result);
}
// performance: avoid context.getContainingFile()
@NotNull
public PsiFile getHostPsiFile() {
PsiFile getHostPsiFile() {
return myHostPsiFile;
}
@@ -592,15 +589,6 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar, ModificationT
return myReferenceInjector;
}
@NotNull
public static DocumentWindow freezeWindow(@NotNull DocumentWindowImpl window) {
Place shreds = window.getShreds();
Project project = shreds.getHostPointer().getProject();
DocumentEx delegate = ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(project)).getLastCommittedDocument(window.getDelegate());
Place place = new Place(ContainerUtil.map(shreds, shred -> ((ShredImpl) shred).withPsiRange()));
return new DocumentWindowImpl(delegate, window.isOneLine(), place);
}
boolean isValid() {
List<Pair<Place, PsiFile>> result = getResult();
if (result == null) return false;
@@ -19,6 +19,7 @@ import com.intellij.lang.Language;
import com.intellij.lang.injection.ConcatenationAwareInjector;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.Trinity;
@@ -31,6 +32,7 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.intellij.plugins.intelliLang.Configuration;
import org.intellij.plugins.intelliLang.inject.InjectedLanguage;
@@ -88,13 +90,15 @@ public class ConcatenationInjector implements ConcatenationAwareInjector {
PsiFile finalContainingFile = containingFile;
InjectionProcessor injectionProcessor = new InjectionProcessor(myConfiguration, mySupport, operands) {
@Override
protected void processInjection(Language language,
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> list,
boolean settingsAvailable,
boolean unparsable) {
protected Pair<PsiLanguageInjectionHost, Language> processInjection(Language language,
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> list,
boolean settingsAvailable,
boolean unparsable) {
InjectorUtils.registerInjection(language, list, finalContainingFile, registrar);
InjectorUtils.registerSupport(mySupport, settingsAvailable, registrar);
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, unparsable ? Boolean.TRUE : null);
InjectorUtils.registerSupport(mySupport, settingsAvailable, list.get(0).getFirst(), language);
PsiLanguageInjectionHost host = list.get(0).getFirst();
InjectorUtils.putInjectedFileUserData(host, language, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, unparsable ? Boolean.TRUE : null);
return Pair.create(host,language);
}
@Override
@@ -112,8 +116,12 @@ public class ConcatenationInjector implements ConcatenationAwareInjector {
if (tempLanguage != null) {
BaseInjection baseInjection = new BaseInjection(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID);
baseInjection.setInjectedLanguageId(tempInjectedLanguage.getID());
injectionProcessor.processInjectionInner(baseInjection, false);
InjectorUtils.putInjectedFileUserData(registrar, LanguageInjectionSupport.TEMPORARY_INJECTED_LANGUAGE, tempInjectedLanguage);
List<Pair<PsiLanguageInjectionHost, Language>> list = injectionProcessor.processInjectionWithContext(baseInjection, false);
for (Pair<PsiLanguageInjectionHost, Language> pair : list) {
PsiLanguageInjectionHost host = pair.getFirst();
Language language = pair.getSecond();
InjectorUtils.putInjectedFileUserData(host, language, LanguageInjectionSupport.TEMPORARY_INJECTED_LANGUAGE, tempInjectedLanguage);
}
}
else {
injectionProcessor.processInjections();
@@ -333,28 +341,24 @@ public class ConcatenationInjector implements ConcatenationAwareInjector {
return true;
}
protected void processInjectionInner(BaseInjection injection, boolean settingsAvailable) {
processInjectionWithContext(injection, settingsAvailable);
}
private void processInjectionWithContext(BaseInjection injection, boolean settingsAvailable) {
@NotNull
List<Pair<PsiLanguageInjectionHost, Language>> processInjectionWithContext(BaseInjection injection, boolean settingsAvailable) {
Language language = InjectorUtils.getLanguage(injection);
if (language == null) return;
if (language == null) return Collections.emptyList();
boolean separateFiles = !injection.isSingleFile() && StringUtil.isNotEmpty(injection.getValuePattern());
Ref<Boolean> unparsableRef = Ref.create(myUnparsable);
List<Object> objects = ContextComputationProcessor.collectOperands(injection.getPrefix(), injection.getSuffix(), unparsableRef, myOperands);
if (objects.isEmpty()) return;
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> result =
new ArrayList<>();
if (objects.isEmpty()) return Collections.emptyList();
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> result = new ArrayList<>();
int len = objects.size();
for (int i = 0; i < len; i++) {
String curPrefix = null;
Object o = objects.get(i);
if (o instanceof String) {
curPrefix = (String)o;
if (i == len - 1) return; // IDEADEV-26751
if (i == len - 1) return Collections.emptyList(); // IDEADEV-26751
o = objects.get(++i);
}
String curSuffix = null;
@@ -393,22 +397,26 @@ public class ConcatenationInjector implements ConcatenationAwareInjector {
}
}
}
if (!result.isEmpty()) {
if (separateFiles) {
for (Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange> trinity : result) {
processInjection(language, Collections.singletonList(trinity), settingsAvailable, false);
}
}
else {
processInjection(language, result, settingsAvailable, unparsableRef.get());
if (result.isEmpty()) {
return Collections.emptyList();
}
List<Pair<PsiLanguageInjectionHost, Language>> res = new ArrayList<>();
if (separateFiles) {
for (Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange> trinity : result) {
ContainerUtil.addIfNotNull(res, processInjection(language, Collections.singletonList(trinity), settingsAvailable, false));
}
}
else {
ContainerUtil.addIfNotNull(res, processInjection(language, result, settingsAvailable, unparsableRef.get()));
}
return res;
}
protected void processInjection(Language language,
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> list,
boolean xmlInjection,
boolean unparsable) {
protected Pair<PsiLanguageInjectionHost, Language> processInjection(Language language,
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> list,
boolean xmlInjection,
boolean unparsable) {
return null;
}
protected boolean areThereInjectionsWithName(String methodName, boolean annoOnly) {
@@ -19,16 +19,19 @@ package org.intellij.plugins.intelliLang.inject;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.lang.Language;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl;
import com.intellij.psi.injection.ReferenceInjector;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
@@ -55,6 +58,7 @@ import java.util.regex.Pattern;
* @author Gregory.Shrago
*/
public class InjectorUtils {
private static final Logger LOG = Logger.getInstance(InjectorUtils.class);
public static final Comparator<TextRange> RANGE_COMPARATOR = (o1, o2) -> {
if (o1.intersects(o2)) return 0;
return o1.getStartOffset() - o2.getStartOffset();
@@ -99,7 +103,7 @@ public class InjectorUtils {
//}
registerInjection(language, list, host.getContainingFile(), registrar);
if (support != null) {
registerSupport(support, true, registrar);
registerSupport(support, true, host, language);
}
return !ranges.isEmpty();
}
@@ -122,10 +126,12 @@ public class InjectorUtils {
InjectedLanguage injectedLanguage = t.second;
if (!injectionStarted) {
registrar.startInjecting(language);
// TextMate language requires file extension
if (registrar instanceof MultiHostRegistrarImpl && !StringUtil.equalsIgnoreCase(language.getID(), t.second.getID())) {
((MultiHostRegistrarImpl)registrar).setFileExtension(StringUtil.toLowerCase(t.second.getID()));
if (!StringUtil.equalsIgnoreCase(language.getID(), t.second.getID())) {
registrar.startInjecting(language, StringUtil.toLowerCase(t.second.getID()));
}
else {
registrar.startInjecting(language);
}
injectionStarted = true;
}
@@ -219,19 +225,33 @@ public class InjectorUtils {
return false;
}
public static void registerSupport(@NotNull LanguageInjectionSupport support, boolean settingsAvailable, @NotNull PsiElement element, @NotNull Language language) {
putInjectedFileUserData(element, language, LanguageInjectionSupport.INJECTOR_SUPPORT, support);
if (settingsAvailable) {
putInjectedFileUserData(element, language, LanguageInjectionSupport.SETTINGS_EDITOR, support);
}
}
/**
* @deprecated use {@link InjectorUtils#registerSupport(org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport, boolean, com.intellij.psi.PsiElement, com.intellij.lang.Language)} instead
*/
public static void registerSupport(@NotNull LanguageInjectionSupport support, boolean settingsAvailable, @NotNull MultiHostRegistrar registrar) {
LOG.warn("use {@link InjectorUtils#registerSupport(org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport, boolean, com.intellij.psi.PsiElement, com.intellij.lang.Language)} instead");
putInjectedFileUserData(registrar, LanguageInjectionSupport.INJECTOR_SUPPORT, support);
if (settingsAvailable) {
putInjectedFileUserData(registrar, LanguageInjectionSupport.SETTINGS_EDITOR, support);
}
}
public static <T> void putInjectedFileUserData(MultiHostRegistrar registrar, Key<T> key, T value) {
InjectedLanguageUtil.putInjectedFileUserData(registrar, key, value);
public static <T> void putInjectedFileUserData(@NotNull PsiElement element, @NotNull Language language, Key<T> key, T value) {
InjectedLanguageUtil.putInjectedFileUserData(element, language, key, value);
}
public static PsiFile getInjectedFile(MultiHostRegistrar registrar) {
return InjectedLanguageUtil.getInjectedFile(registrar);
/**
* @deprecated use {@link InjectorUtils#putInjectedFileUserData(com.intellij.psi.PsiElement, com.intellij.lang.Language, com.intellij.openapi.util.Key, java.lang.Object)} instead
*/
public static <T> void putInjectedFileUserData(MultiHostRegistrar registrar, Key<T> key, T value) {
InjectedLanguageUtil.putInjectedFileUserData(registrar, key, value);
}
@SuppressWarnings("UnusedParameters")
@@ -60,7 +60,7 @@ public class TemporaryPlacesInjector implements MultiHostInjector {
List<Trinity<PsiLanguageInjectionHost, InjectedLanguage,TextRange>> trinities =
Collections.singletonList(Trinity.create(host, injectedLanguage, manipulator.getRangeInElement(host)));
InjectorUtils.registerInjection(language, trinities, containingFile, registrar);
InjectorUtils.registerSupport(myRegistry.getLanguageInjectionSupport(), false, registrar);
InjectorUtils.registerSupport(myRegistry.getLanguageInjectionSupport(), false, context, language);
}
}
@@ -89,9 +89,9 @@ public final class XmlLanguageInjector implements MultiHostInjector {
ranges.add(textRange.shiftRight(host1.getTextRange().getStartOffset()));
}
InjectorUtils.registerInjection(language, list, containingFile, registrar);
InjectorUtils.registerSupport(mySupport, true, registrar);
InjectorUtils.registerSupport(mySupport, true, list.get(0).getFirst(), language);
if (Boolean.TRUE.equals(unparsableRef.get())) {
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
InjectorUtils.putInjectedFileUserData(host, language, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
}
return true;
});
@@ -84,8 +84,8 @@ public class GrConcatenationAwareInjector implements ConcatenationAwareInjector
boolean settingsAvailable,
boolean unparsable) {
InjectorUtils.registerInjection(language, list, file, registrar);
InjectorUtils.registerSupport(mySupport, settingsAvailable, registrar);
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, unparsable);
InjectorUtils.registerSupport(mySupport, settingsAvailable, list.get(0).getFirst(), language);
InjectorUtils.putInjectedFileUserData(list.get(0).getFirst(), language, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, unparsable);
}
@Override
@@ -37,7 +37,7 @@ public class PyConfigurationInjector extends PyInjectorBase {
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
final PyInjectionUtil.InjectionResult result = registerInjection(registrar, context);
if (!result.isStrict()) {
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
InjectorUtils.putInjectedFileUserData(context, getInjectedLanguage(context), InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
}
}
@@ -38,9 +38,9 @@ public class PyTemporaryInjector extends PyInjectorBase {
final PyInjectionUtil.InjectionResult result = registerInjection(registrar, context);
if (result.isInjected()) {
final TemporaryPlacesRegistry registry = TemporaryPlacesRegistry.getInstance(context.getProject());
InjectorUtils.registerSupport(registry.getLanguageInjectionSupport(), false, registrar);
InjectorUtils.registerSupport(registry.getLanguageInjectionSupport(), false, context, getInjectedLanguage(context));
if (!result.isStrict()) {
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
InjectorUtils.putInjectedFileUserData(context, getInjectedLanguage(context), InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
}
}
}
@@ -18,12 +18,9 @@ package com.jetbrains.python.codeInsight.regexp;
import com.intellij.lang.Language;
import com.intellij.lang.injection.MultiHostInjector;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl;
import com.intellij.psi.impl.source.tree.injected.Place;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.codeInsight.PyInjectionUtil;
@@ -104,7 +101,7 @@ public class PythonRegexpInjector implements MultiHostInjector {
if (result.isInjected()) {
registrar.doneInjecting();
if (!result.isStrict()) {
final PsiFile file = getInjectedFile(registrar);
final PsiFile file = InjectedLanguageUtil.getCachedInjectedFileWithLanguage(context, language);
if (file != null) {
file.putUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
}
@@ -112,13 +109,6 @@ public class PythonRegexpInjector implements MultiHostInjector {
}
}
@Nullable
private static PsiFile getInjectedFile(@NotNull MultiHostRegistrar registrar) {
// Don't add a dependency on IntelliLang here now, but this injector should become IntelliLang-based in the future
final List<Pair<Place, PsiFile>> result = ((MultiHostRegistrarImpl)registrar).getResult();
return result == null || result.isEmpty() ? null : result.get(result.size() - 1).second;
}
@NotNull
@Override
public List<? extends Class<? extends PsiElement>> elementsToInjectIn() {