From 42459505b154684df4d9af23b2fe225d90ebce46 Mon Sep 17 00:00:00 2001 From: Piotr Tomiak Date: Fri, 18 Apr 2025 12:40:16 +0200 Subject: [PATCH] [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 --- .../tree/injected/InjectedLanguageUtil.java | 5 +++ .../tree/injected/InjectionRegistrarImpl.java | 43 +++++++++---------- platform/core-api/api-dump-unreviewed.txt | 2 +- .../injection/InjectedLanguageManager.java | 12 ++---- .../lang/injection/MultiHostRegistrar.java | 13 +++++- plugins/IntelliLang/api-dump.txt | 4 ++ .../intelliLang/inject/InjectorUtils.java | 40 ++++++++++++++--- .../intelliLang/inject/InjectorUtils.kt | 7 +++ 8 files changed, 88 insertions(+), 38 deletions(-) create mode 100644 plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.kt diff --git a/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java b/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java index 707b97f3bd81..be0ba1da49a4 100644 --- a/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java +++ b/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageUtil.java @@ -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 void putInjectedFileUserData(@NotNull PsiElement element, @NotNull Language language, @NotNull Key key, diff --git a/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectionRegistrarImpl.java b/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectionRegistrarImpl.java index b76cac6cfca4..1de18ef881d1 100644 --- a/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectionRegistrarImpl.java +++ b/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectionRegistrarImpl.java @@ -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 placeInfos; private boolean cleared = true; private String fileExtension; + @SuppressWarnings("rawtypes") + private Map 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 MultiHostRegistrar putInjectedFileUserData(@NotNull Key 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 userData : myUserData.entrySet()) { + //noinspection unchecked + psiFile.putUserData(userData.getKey(), userData.getValue()); + } + } DocumentWindowImpl retrieved = (DocumentWindowImpl)myDocumentManagerBase.getDocument(psiFile); assertEverythingIsAllright(myDocumentManagerBase, retrieved, psiFile); diff --git a/platform/core-api/api-dump-unreviewed.txt b/platform/core-api/api-dump-unreviewed.txt index 82cfa612796f..77108cb075e5 100644 --- a/platform/core-api/api-dump-unreviewed.txt +++ b/platform/core-api/api-dump-unreviewed.txt @@ -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 - ():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 diff --git a/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java b/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java index 640d4f6482ca..5b46ef69dbc6 100644 --- a/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java +++ b/platform/core-api/src/com/intellij/lang/injection/InjectedLanguageManager.java @@ -25,17 +25,13 @@ public abstract class InjectedLanguageManager { } /** - * Use {@link InjectedLanguageManager#isFrankensteinInjection(PsiElement)} or {@link MultiHostRegistrar#frankensteinInjection(boolean)} instead. - *

- * 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 FRANKENSTEIN_INJECTION = Key.create("FRANKENSTEIN_INJECTION"); - /** - * Use {@link InjectedLanguageManager#shouldInspectionsBeLenient(PsiElement)} or {@link MultiHostRegistrar#makeInspectionsLenient(boolean)} instead. - *

- * Made public to enable custom {@link MultiHostRegistrar} implementations. - */ + @ApiStatus.Internal public static final Key LENIENT_INSPECTIONS = Key.create("LENIENT_INSPECTIONS"); public abstract PsiLanguageInjectionHost getInjectionHost(@NotNull FileViewProvider injectedProvider); diff --git a/platform/core-api/src/com/intellij/lang/injection/MultiHostRegistrar.java b/platform/core-api/src/com/intellij/lang/injection/MultiHostRegistrar.java index 25ac05d31676..30f1bb3f90e6 100644 --- a/platform/core-api/src/com/intellij/lang/injection/MultiHostRegistrar.java +++ b/platform/core-api/src/com/intellij/lang/injection/MultiHostRegistrar.java @@ -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 MultiHostRegistrar putInjectedFileUserData(Key key, T data) { throw new UnsupportedOperationException(); } diff --git a/plugins/IntelliLang/api-dump.txt b/plugins/IntelliLang/api-dump.txt index b33369a9211c..f947cc432268 100644 --- a/plugins/IntelliLang/api-dump.txt +++ b/plugins/IntelliLang/api-dump.txt @@ -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 diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java index c27f2e9b932c..729ff3c0a47b 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.java @@ -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 list, + @NotNull MultiHostRegistrar registrar) { + registerInjection(language, containingFile, list, registrar, null); + } public static void registerInjection(@Nullable Language language, @NotNull PsiFile containingFile, @NotNull List list, - @NotNull MultiHostRegistrar registrar) { + @NotNull MultiHostRegistrar registrar, + @Nullable Consumer 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 void putInjectedFileUserData(@NotNull PsiElement element, @NotNull Language language, @NotNull Key key, @Nullable T value) { InjectedLanguageUtil.putInjectedFileUserData(element, language, key, value); } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.kt b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.kt new file mode 100644 index 000000000000..eedbf0948297 --- /dev/null +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectorUtils.kt @@ -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) +}