[core] WEB-72084 Add API in MultiHostRegistrar to provide custom user data for the injected files; deprecate InjectorUtils.putInjectedFileUserData.

(cherry picked from commit 7493536cfa1f2e13fdeb2a60d269c4d549ea5f9c)

IJ-CR-160968

GitOrigin-RevId: 813b334a1aba4414dc72f8bc1f576b7b06c895e7
This commit is contained in:
Piotr Tomiak
2025-04-18 12:40:16 +02:00
committed by intellij-monorepo-bot
parent d0485c842c
commit 42459505b1
8 changed files with 88 additions and 38 deletions

View File

@@ -268,6 +268,11 @@ public final class InjectedLanguageUtil extends InjectedLanguageUtilBase {
return ref.get();
}
/**
* Does not work with multiple injections on the same host.
*
* @deprecated Use {@link MultiHostRegistrar#putInjectedFileUserData(Key, Object)} when registering the injection.
*/
public static <T> void putInjectedFileUserData(@NotNull PsiElement element,
@NotNull Language language,
@NotNull Key<T> key,

View File

@@ -6,7 +6,6 @@ import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.lang.*;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments;
@@ -51,9 +50,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.*;
@ApiStatus.Internal
public final class InjectionRegistrarImpl implements MultiHostRegistrar {
@@ -64,13 +61,13 @@ public final class InjectionRegistrarImpl implements MultiHostRegistrar {
private List<PlaceInfo> placeInfos;
private boolean cleared = true;
private String fileExtension;
@SuppressWarnings("rawtypes")
private Map<Key, Object> myUserData;
private final Project myProject;
private final DocumentEx myHostDocument;
private final VirtualFile myHostVirtualFile;
private final PsiElement myContextElement;
private final PsiFile myHostPsiFile;
private boolean isFrankensteinInjection;
private boolean shouldInspectionsBeLenient;
private Thread currentThread;
public InjectionRegistrarImpl(@NotNull Project project,
@@ -123,9 +120,7 @@ public final class InjectionRegistrarImpl implements MultiHostRegistrar {
cleared = true;
placeInfos = null;
currentThread = null;
isFrankensteinInjection = false;
shouldInspectionsBeLenient = false;
myUserData = null;
}
@Override
@@ -162,14 +157,15 @@ public final class InjectionRegistrarImpl implements MultiHostRegistrar {
}
@Override
public @NotNull MultiHostRegistrar makeInspectionsLenient(boolean shouldInspectionsBeLenient) {
this.shouldInspectionsBeLenient = shouldInspectionsBeLenient;
return this;
}
@Override
public @NotNull MultiHostRegistrar frankensteinInjection(boolean isFrankensteinInjection) {
this.isFrankensteinInjection = isFrankensteinInjection;
public @NotNull <T> MultiHostRegistrar putInjectedFileUserData(@NotNull Key<T> key, @Nullable T data) {
if (myUserData == null) {
if (data == null) return this;
myUserData = new HashMap<>();
}
if (data == null)
myUserData.remove(key);
else
myUserData.put(key, data);
return this;
}
@@ -276,12 +272,6 @@ public final class InjectionRegistrarImpl implements MultiHostRegistrar {
placeInfos, decodedChars, fileName, myDocumentManagerBase);
for (ASTNode node : parsedNodes) {
PsiFile psiFile = (PsiFile)node.getPsi();
if (isFrankensteinInjection) {
psiFile.putUserData(InjectedLanguageManager.FRANKENSTEIN_INJECTION, Boolean.TRUE);
}
if (shouldInspectionsBeLenient) {
psiFile.putUserData(InjectedLanguageManagerImpl.LENIENT_INSPECTIONS, Boolean.TRUE);
}
InjectedFileViewProvider viewProvider = (InjectedFileViewProvider)psiFile.getViewProvider();
synchronized (InjectedLanguageManagerImpl.ourInjectionPsiLock) {
if (psiFile.getLanguage() == viewProvider.getBaseLanguage()) {
@@ -291,6 +281,13 @@ public final class InjectionRegistrarImpl implements MultiHostRegistrar {
else {
cacheEverything(place, documentWindow, viewProvider, psiFile);
}
if (myUserData != null && !myUserData.isEmpty()) {
//noinspection rawtypes
for (Map.Entry<Key, Object> userData : myUserData.entrySet()) {
//noinspection unchecked
psiFile.putUserData(userData.getKey(), userData.getValue());
}
}
DocumentWindowImpl retrieved = (DocumentWindowImpl)myDocumentManagerBase.getDocument(psiFile);
assertEverythingIsAllright(myDocumentManagerBase, retrieved, psiFile);

View File

@@ -761,7 +761,6 @@ f:com.intellij.lang.folding.LanguageFolding
- forLanguage(com.intellij.lang.Language):com.intellij.lang.folding.FoldingBuilder
a:com.intellij.lang.injection.InjectedLanguageManager
- sf:FRANKENSTEIN_INJECTION:com.intellij.openapi.util.Key
- sf:LENIENT_INSPECTIONS:com.intellij.openapi.util.Key
- <init>():V
- a:dropFileCaches(com.intellij.psi.PsiFile):V
- a:enumerate(com.intellij.psi.PsiElement,com.intellij.psi.PsiLanguageInjectionHost$InjectedPsiVisitor):V
@@ -799,6 +798,7 @@ com.intellij.lang.injection.MultiHostRegistrar
- a:doneInjecting():V
- frankensteinInjection(Z):com.intellij.lang.injection.MultiHostRegistrar
- makeInspectionsLenient(Z):com.intellij.lang.injection.MultiHostRegistrar
- putInjectedFileUserData(com.intellij.openapi.util.Key,java.lang.Object):com.intellij.lang.injection.MultiHostRegistrar
- a:startInjecting(com.intellij.lang.Language):com.intellij.lang.injection.MultiHostRegistrar
- startInjecting(com.intellij.lang.Language,java.lang.String):com.intellij.lang.injection.MultiHostRegistrar
com.intellij.lang.injection.general.Injection

View File

@@ -25,17 +25,13 @@ public abstract class InjectedLanguageManager {
}
/**
* Use {@link InjectedLanguageManager#isFrankensteinInjection(PsiElement)} or {@link MultiHostRegistrar#frankensteinInjection(boolean)} instead.
* <p>
* Made public to enable custom {@link MultiHostRegistrar} implementations.
* @deprecated use {@link InjectedLanguageManager#isFrankensteinInjection(PsiElement)} or {@link MultiHostRegistrar#frankensteinInjection(boolean)} instead.
*/
@Deprecated
@ApiStatus.ScheduledForRemoval
public static final Key<Boolean> FRANKENSTEIN_INJECTION = Key.create("FRANKENSTEIN_INJECTION");
/**
* Use {@link InjectedLanguageManager#shouldInspectionsBeLenient(PsiElement)} or {@link MultiHostRegistrar#makeInspectionsLenient(boolean)} instead.
* <p>
* Made public to enable custom {@link MultiHostRegistrar} implementations.
*/
@ApiStatus.Internal
public static final Key<Boolean> LENIENT_INSPECTIONS = Key.create("LENIENT_INSPECTIONS");
public abstract PsiLanguageInjectionHost getInjectionHost(@NotNull FileViewProvider injectedProvider);

View File

@@ -2,6 +2,7 @@
package com.intellij.lang.injection;
import com.intellij.lang.Language;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLanguageInjectionHost;
@@ -97,7 +98,7 @@ public interface MultiHostRegistrar {
*/
@NotNull
default MultiHostRegistrar makeInspectionsLenient(boolean shouldInspectionsBeLenient) {
throw new UnsupportedOperationException();
return putInjectedFileUserData(InjectedLanguageManager.LENIENT_INSPECTIONS, shouldInspectionsBeLenient ? true : null);
}
/**
@@ -117,6 +118,16 @@ public interface MultiHostRegistrar {
*/
@NotNull
default MultiHostRegistrar frankensteinInjection(boolean isFrankensteinInjection) {
return putInjectedFileUserData(InjectedLanguageManager.FRANKENSTEIN_INJECTION, isFrankensteinInjection ? true : null);
}
/**
* Allows setting custom user data on the injected file.
*
* @return this
*/
@NotNull
default <T> MultiHostRegistrar putInjectedFileUserData(Key<T> key, T data) {
throw new UnsupportedOperationException();
}

View File

@@ -156,8 +156,10 @@ f:org.intellij.plugins.intelliLang.inject.InjectorUtils
- s:isRegexp(java.lang.String):Z
- s:putInjectedFileUserData(com.intellij.psi.PsiElement,com.intellij.lang.Language,com.intellij.openapi.util.Key,java.lang.Object):V
- s:registerInjection(com.intellij.lang.Language,com.intellij.psi.PsiFile,java.util.List,com.intellij.lang.injection.MultiHostRegistrar):V
- s:registerInjection(com.intellij.lang.Language,com.intellij.psi.PsiFile,java.util.List,com.intellij.lang.injection.MultiHostRegistrar,java.util.function.Consumer):V
- s:registerInjection(com.intellij.lang.Language,java.util.List,com.intellij.psi.PsiFile,com.intellij.lang.injection.MultiHostRegistrar):V
- s:registerInjectionSimple(com.intellij.psi.PsiLanguageInjectionHost,org.intellij.plugins.intelliLang.inject.config.BaseInjection,org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport,com.intellij.lang.injection.MultiHostRegistrar):Z
- s:registerSupport(com.intellij.lang.injection.MultiHostRegistrar,org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport,Z):V
- s:registerSupport(org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport,Z,com.intellij.psi.PsiElement,com.intellij.lang.Language):V
c:org.intellij.plugins.intelliLang.inject.InjectorUtils$CommentInjectionData
- getDisplayName():java.lang.String
@@ -173,6 +175,8 @@ f:org.intellij.plugins.intelliLang.inject.InjectorUtils$InjectionInfo
- host():com.intellij.psi.PsiLanguageInjectionHost
- language():org.intellij.plugins.intelliLang.inject.InjectedLanguage
- range():com.intellij.openapi.util.TextRange
f:org.intellij.plugins.intelliLang.inject.InjectorUtilsKt
- sf:registerSupport(com.intellij.lang.injection.MultiHostRegistrar,org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport,Z):com.intellij.lang.injection.MultiHostRegistrar
a:org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport
- sf:INJECTOR_SUPPORT:com.intellij.openapi.util.Key
- sf:SETTINGS_EDITOR:com.intellij.openapi.util.Key

View File

@@ -34,6 +34,7 @@ import org.jetbrains.annotations.Unmodifiable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -94,10 +95,11 @@ public final class InjectorUtils {
for (TextRange range : ranges) {
list.add(new InjectionInfo(host, injectedLanguage, range));
}
registerInjection(language, host.getContainingFile(), list, registrar);
if (support != null) {
registerSupport(support, true, host, language);
}
registerInjection(language, host.getContainingFile(), list, registrar, it -> {
if (support != null) {
registerSupport(it, support, true);
}
});
return !ranges.isEmpty();
}
@@ -121,12 +123,19 @@ public final class InjectorUtils {
registerInjection(language, containingFile,
ContainerUtil.map(list, trinity -> new InjectionInfo(trinity.first, trinity.second, trinity.third)), registrar);
}
public static void registerInjection(@Nullable Language language,
@NotNull PsiFile containingFile,
@NotNull List<InjectionInfo> list,
@NotNull MultiHostRegistrar registrar) {
registerInjection(language, containingFile, list, registrar, null);
}
public static void registerInjection(@Nullable Language language,
@NotNull PsiFile containingFile,
@NotNull List<InjectionInfo> list,
@NotNull MultiHostRegistrar registrar) {
@NotNull MultiHostRegistrar registrar,
@Nullable Consumer<MultiHostRegistrar> customizeInjection) {
// if language isn't injected when length == 0, subsequent edits will not cause the language to be injected as well.
// Maybe IDEA core is caching a bit too aggressively here?
if (language == null/* && (pair.second.getLength() > 0*/) {
@@ -165,6 +174,9 @@ public final class InjectorUtils {
}
registrar.addPlace(injectedLanguage.getPrefix(), injectedLanguage.getSuffix(), host, textRange);
}
if (customizeInjection != null) {
customizeInjection.accept(registrar);
}
if (injectionStarted) {
registrar.doneInjecting();
}
@@ -249,6 +261,10 @@ public final class InjectorUtils {
return false;
}
/**
* @deprecated Use {@link #registerSupport(MultiHostRegistrar, LanguageInjectionSupport, boolean)} instead
*/
@Deprecated
public static void registerSupport(@NotNull LanguageInjectionSupport support,
boolean settingsAvailable,
@NotNull PsiElement element,
@@ -259,7 +275,21 @@ public final class InjectorUtils {
}
}
public static void registerSupport(@NotNull MultiHostRegistrar registrar,
@NotNull LanguageInjectionSupport support,
boolean settingsAvailable) {
registrar.putInjectedFileUserData(LanguageInjectionSupport.INJECTOR_SUPPORT, support);
if (settingsAvailable) {
registrar.putInjectedFileUserData(LanguageInjectionSupport.SETTINGS_EDITOR, support);
}
}
/**
* Does not work with multiple injections on the same host.
*
* @deprecated Use {@link MultiHostRegistrar#putInjectedFileUserData(Key, Object)} when registering the injection.
*/
@Deprecated
public static <T> void putInjectedFileUserData(@NotNull PsiElement element, @NotNull Language language, @NotNull Key<T> key, @Nullable T value) {
InjectedLanguageUtil.putInjectedFileUserData(element, language, key, value);
}

View File

@@ -0,0 +1,7 @@
package org.intellij.plugins.intelliLang.inject
import com.intellij.lang.injection.MultiHostRegistrar
fun MultiHostRegistrar.registerSupport(support: LanguageInjectionSupport, settingsAvailable: Boolean): MultiHostRegistrar = also {
InjectorUtils.registerSupport(it, support, settingsAvailable)
}