[diff] IJPL-190026 Don't call LanguageUtil.getLanguageForPsi on EDT

`DiffLangSpecificProvider.findApplicable` is called from EDT, producing a slow operation error in `LanguageUtil.getLanguageForPsi -> ... -> findModuleForFile`.
Now `DiffUserDataKeys.LANGUAGE` is set on BGT and `DiffLangSpecificProvider.findApplicable` relies on it.

GitOrigin-RevId: dfe87819de3f3782ffcfee1174b0ba3d7dc69ea0
This commit is contained in:
Ilia.Shulgin
2025-07-15 11:09:05 +00:00
committed by intellij-monorepo-bot
parent fbda434679
commit 6e2fa02aa7
8 changed files with 78 additions and 38 deletions
+4 -5
View File
@@ -545,14 +545,14 @@ com.intellij.diff.lang.DiffIgnoredRangeProvider
com.intellij.diff.lang.DiffLangSpecificProvider
- sf:Companion:com.intellij.diff.lang.DiffLangSpecificProvider$Companion
- sf:EP_NAME:com.intellij.openapi.extensions.ExtensionPointName
- s:findApplicable(com.intellij.openapi.project.Project,com.intellij.diff.contents.DiffContent,com.intellij.diff.contents.DiffContent):com.intellij.diff.lang.DiffLangSpecificProvider
- s:findApplicable(com.intellij.diff.contents.DiffContent,com.intellij.diff.contents.DiffContent):com.intellij.diff.lang.DiffLangSpecificProvider
- a:getDescription():java.lang.String
- getLineFragments(com.intellij.openapi.project.Project,java.lang.CharSequence,java.lang.CharSequence,com.intellij.diff.tools.util.base.IgnorePolicy,com.intellij.diff.tools.util.base.HighlightPolicy,com.intellij.openapi.progress.ProgressIndicator):java.util.List
- getPatchedLineFragments(com.intellij.openapi.project.Project,java.util.List,java.lang.CharSequence,java.lang.CharSequence,com.intellij.diff.tools.util.base.IgnorePolicy,com.intellij.diff.tools.util.base.HighlightPolicy,com.intellij.openapi.progress.ProgressIndicator):java.util.List
- a:getShouldPrecalculateLineFragments():Z
- a:isApplicable(com.intellij.lang.Language):Z
f:com.intellij.diff.lang.DiffLangSpecificProvider$Companion
- f:findApplicable(com.intellij.openapi.project.Project,com.intellij.diff.contents.DiffContent,com.intellij.diff.contents.DiffContent):com.intellij.diff.lang.DiffLangSpecificProvider
- f:findApplicable(com.intellij.diff.contents.DiffContent,com.intellij.diff.contents.DiffContent):com.intellij.diff.lang.DiffLangSpecificProvider
a:com.intellij.diff.lang.LangDiffIgnoredRangeProvider
- com.intellij.diff.lang.DiffIgnoredRangeProvider
- <init>():V
@@ -560,15 +560,14 @@ a:com.intellij.diff.lang.LangDiffIgnoredRangeProvider
- pa:accepts(com.intellij.openapi.project.Project,com.intellij.lang.Language):Z
- pa:computeIgnoredRanges(com.intellij.openapi.project.Project,java.lang.CharSequence,com.intellij.lang.Language):java.util.List
- getIgnoredRanges(com.intellij.openapi.project.Project,java.lang.CharSequence,com.intellij.diff.contents.DiffContent):java.util.List
- s:getLanguage(com.intellij.openapi.project.Project,com.intellij.diff.contents.DiffContent):com.intellij.lang.Language
com.intellij.diff.merge.LangSpecificMergeConflictResolver
- sf:Companion:com.intellij.diff.merge.LangSpecificMergeConflictResolver$Companion
- sf:EP_NAME:com.intellij.openapi.extensions.ExtensionPointName
- s:findApplicable(com.intellij.openapi.project.Project,java.util.List):com.intellij.diff.merge.LangSpecificMergeConflictResolver
- s:findApplicable(java.util.List):com.intellij.diff.merge.LangSpecificMergeConflictResolver
- a:isApplicable(com.intellij.lang.Language):Z
- a:tryResolveMergeConflicts(com.intellij.diff.merge.LangSpecificMergeContext,kotlin.coroutines.Continuation):java.lang.Object
f:com.intellij.diff.merge.LangSpecificMergeConflictResolver$Companion
- f:findApplicable(com.intellij.openapi.project.Project,java.util.List):com.intellij.diff.merge.LangSpecificMergeConflictResolver
- f:findApplicable(java.util.List):com.intellij.diff.merge.LangSpecificMergeConflictResolver
f:com.intellij.diff.merge.LangSpecificMergeContext
- <init>(com.intellij.openapi.project.Project,java.util.List,java.util.List,java.util.List):V
- f:file(com.intellij.diff.util.ThreeSide):com.intellij.psi.PsiFile
@@ -4,6 +4,7 @@ package com.intellij.diff.impl;
import com.intellij.CommonBundle;
import com.intellij.diff.chains.DiffRequestProducer;
import com.intellij.diff.chains.DiffRequestProducerException;
import com.intellij.diff.lang.DiffLanguage;
import com.intellij.diff.requests.*;
import com.intellij.diff.tools.util.SoftHardCacheMap;
import com.intellij.diff.util.DiffTaskQueue;
@@ -110,6 +111,9 @@ public abstract class CacheDiffRequestProcessor<T> extends DiffRequestProcessor
myQueue.executeAndTryWait(
indicator -> {
final DiffRequest request = doLoadRequest(requestProvider, indicator);
if (request instanceof ContentDiffRequest contentDiffRequest) {
contentDiffRequest.getContents().forEach(content -> DiffLanguage.computeAndCacheLanguage(content, getProject()));
}
return () -> finishRequestLoading(request, force, scrollToChangePolicy, requestProvider);
},
() -> applyRequest(new LoadingDiffRequest(getRequestName(requestProvider)), force, scrollToChangePolicy),
@@ -61,11 +61,9 @@ interface DiffLangSpecificProvider {
val EP_NAME: ExtensionPointName<DiffLangSpecificProvider> = ExtensionPointName.create<DiffLangSpecificProvider>("com.intellij.diff.lang.DiffLangSpecificAdjuster")
@JvmStatic
fun findApplicable(project: Project?, leftContent: DiffContent, rightContent: DiffContent): DiffLangSpecificProvider? {
if (project == null) return null
val leftLanguage = LangDiffIgnoredRangeProvider.getLanguage(project, leftContent)
val rightLanguage = LangDiffIgnoredRangeProvider.getLanguage(project, rightContent)
fun findApplicable(leftContent: DiffContent, rightContent: DiffContent): DiffLangSpecificProvider? {
val leftLanguage = DiffLanguage.getLanguage(leftContent)
val rightLanguage = DiffLanguage.getLanguage(rightContent)
if (leftLanguage == null || rightLanguage == null || leftLanguage != rightLanguage) return null
@@ -0,0 +1,58 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.diff.lang
import com.intellij.diff.contents.DiffContent
import com.intellij.diff.contents.DocumentContent
import com.intellij.diff.lang.DiffLanguage.getLanguageOrCompute
import com.intellij.diff.util.DiffUserDataKeys
import com.intellij.lang.Language
import com.intellij.lang.LanguageUtil
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.project.Project
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
import org.jetbrains.annotations.CalledInAny
internal object DiffLanguage {
/**
* Note that unlike [getLanguageOrCompute] this method ignores [com.intellij.psi.LanguageSubstitutors] as their
* calculation might require a background thread.
*
* @return language saved as user data in [content] or language guessed by [content] file type
*/
@CalledInAny
fun getLanguage(content: DiffContent): Language? =
getLanguageByDataKey(content) ?: getLanguageByFileType(content)
@JvmStatic
@RequiresBackgroundThread
fun getLanguageOrCompute(project: Project, content: DiffContent): Language? {
val language = getLanguageByDataKey(content)
if (language != null) return language
return computeLanguage(content, project)
}
@JvmStatic
@RequiresBackgroundThread
fun computeAndCacheLanguage(content: DiffContent, project: Project?) {
val language = computeLanguage(content, project)
if (language != null) {
content.putUserData(DiffUserDataKeys.LANGUAGE, language)
}
}
private fun computeLanguage(content: DiffContent, project: Project?): Language? {
if (project == null) return getLanguageByFileType(content)
val fileType = content.getContentType()
val languageForPsi = (content as? DocumentContent)?.highlightFile?.let { highlightingFile ->
runReadAction { LanguageUtil.getLanguageForPsi(project, highlightingFile, fileType) }
}
return languageForPsi ?: getLanguageByFileType(content)
}
private fun getLanguageByDataKey(content: DiffContent): Language? = content.getUserData(DiffUserDataKeys.LANGUAGE)
private fun getLanguageByFileType(content: DiffContent): Language? = content.getContentType()?.let(LanguageUtil::getFileTypeLanguage)
}
@@ -2,15 +2,9 @@
package com.intellij.diff.lang;
import com.intellij.diff.contents.DiffContent;
import com.intellij.diff.contents.DocumentContent;
import com.intellij.diff.util.DiffUserDataKeys;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageUtil;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -24,7 +18,7 @@ public abstract class LangDiffIgnoredRangeProvider implements DiffIgnoredRangePr
@Override
public final boolean accepts(@Nullable Project project, @NotNull DiffContent content) {
if (project == null) return false;
Language language = getLanguage(project, content);
Language language = DiffLanguage.getLanguageOrCompute(project, content);
if (language == null) return false;
return accepts(project, language);
}
@@ -32,21 +26,9 @@ public abstract class LangDiffIgnoredRangeProvider implements DiffIgnoredRangePr
@Override
public @NotNull List<TextRange> getIgnoredRanges(@Nullable Project project, @NotNull CharSequence text, @NotNull DiffContent content) {
assert project != null;
Language language = getLanguage(project, content);
Language language = DiffLanguage.getLanguageOrCompute(project, content);
assert language != null;
return computeIgnoredRanges(project, text, language);
}
public static @Nullable Language getLanguage(@NotNull Project project, @NotNull DiffContent content) {
Language language = content.getUserData(DiffUserDataKeys.LANGUAGE);
if (language != null) return language;
FileType fileType = content.getContentType();
VirtualFile file = content instanceof DocumentContent ? ((DocumentContent)content).getHighlightFile() : null;
if (file != null) {
return ReadAction.compute(() -> LanguageUtil.getLanguageForPsi(project, file, fileType));
}
return fileType == null ? null : LanguageUtil.getFileTypeLanguage(fileType);
}
}
@@ -2,11 +2,10 @@
package com.intellij.diff.merge
import com.intellij.diff.contents.DocumentContent
import com.intellij.diff.lang.LangDiffIgnoredRangeProvider
import com.intellij.diff.lang.DiffLanguage
import com.intellij.diff.util.ThreeSide
import com.intellij.lang.Language
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
/**
* An interface for resolving merge conflicts in a way that requires semantic of some language.
@@ -31,10 +30,10 @@ interface LangSpecificMergeConflictResolver {
val EP_NAME: ExtensionPointName<LangSpecificMergeConflictResolver> = ExtensionPointName.create("com.intellij.diff.merge.conflict.semantic.resolver")
@JvmStatic
fun findApplicable(project: Project?, contentList: List<DocumentContent>): LangSpecificMergeConflictResolver? {
if (project == null || contentList.size != ThreeSide.entries.size) return null
fun findApplicable(contentList: List<DocumentContent>): LangSpecificMergeConflictResolver? {
if (contentList.size != ThreeSide.entries.size) return null
val languageList = contentList.mapNotNull { LangDiffIgnoredRangeProvider.getLanguage(project, it) }
val languageList = contentList.mapNotNull { DiffLanguage.getLanguage(it) }
if (languageList.size != ThreeSide.entries.size || languageList.distinct().size != 1) return null
@@ -28,7 +28,7 @@ import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
class LangSpecificMergeConflictResolverWrapper(private val project: Project?, contentList: List<DocumentContent>) {
private val resolver: LangSpecificMergeConflictResolver? = if (`is`("semantic.merge.conflict.resolution", false)) {
LangSpecificMergeConflictResolver.findApplicable(project, contentList)
LangSpecificMergeConflictResolver.findApplicable(contentList)
}
else {
null
@@ -49,7 +49,7 @@ public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implement
DiffContent content1 = Side.LEFT.select(request.getContents());
DiffContent content2 = Side.RIGHT.select(request.getContents());
DiffIgnoredRangeProvider ignoredRangeProvider = getIgnoredRangeProvider(project, content1, content2);
DiffLangSpecificProvider diffProvider = DiffLangSpecificProvider.findApplicable(project, content1, content2);
DiffLangSpecificProvider diffProvider = DiffLangSpecificProvider.findApplicable(content1, content2);
IgnorePolicy[] ignorePolicies = getIgnorePolicies();
return new SmartTextDiffProvider(project, content1, content2, settings, rediff, disposable, ignoredRangeProvider, diffProvider,
ignorePolicies);
@@ -63,7 +63,7 @@ public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implement
DiffContent content1 = Side.LEFT.select(request.getContents());
DiffContent content2 = Side.RIGHT.select(request.getContents());
DiffIgnoredRangeProvider ignoredRangeProvider = getIgnoredRangeProvider(project, content1, content2);
DiffLangSpecificProvider diffAdjuster = DiffLangSpecificProvider.findApplicable(project, content1, content2);
DiffLangSpecificProvider diffAdjuster = DiffLangSpecificProvider.findApplicable(content1, content2);
return new SmartTextDiffProvider.NoIgnore(project, content1, content2, settings, rediff, disposable, ignoredRangeProvider, diffAdjuster);
}