mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[command-completion] IDEA-370458 make command completion faster
GitOrigin-RevId: dcc5c8c5fde38d30ce962d59cfacdc26b06b3f22
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4ff346caa2
commit
4e2fe7893e
+4
-2
@@ -95,8 +95,10 @@ public final class DefineParamsDefaultValueAction extends PsiBasedModCommandActi
|
||||
PsiParameterList parameterList = method.getParameterList();
|
||||
PsiParameter[] parameters = parameterList.getParameters();
|
||||
if (parameters.length == 1) {
|
||||
return ModCommand.psiUpdate(method, (m, updater) -> invoke(context.project(), m, updater,
|
||||
updater.getWritable(m).getParameterList().getParameters()));
|
||||
return ModCommand.psiUpdate(method, (m, updater) -> {
|
||||
PsiMethod writableMethod = updater.getWritable(m);
|
||||
invoke(context.project(), writableMethod, updater, writableMethod.getParameterList().getParameters());
|
||||
});
|
||||
}
|
||||
List<ParameterClassMember> members = ContainerUtil.map(parameters, ParameterClassMember::new);
|
||||
int idx = getSelectedIndex(element);
|
||||
|
||||
+17
@@ -421,6 +421,23 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
fun testRedCodeImport() {
|
||||
Registry.get("ide.completion.command.force.enabled").setValue(true, getTestRootDisposable())
|
||||
runBlocking {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, """
|
||||
class A {
|
||||
void foo() {
|
||||
List<String> y = new ArrayList<caret><String>;
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.doHighlighting()
|
||||
myFixture.type(".")
|
||||
val elements = myFixture.completeBasic()
|
||||
assertTrue(elements.any { element -> element.lookupString.contains("Import", ignoreCase = true) })
|
||||
}
|
||||
}
|
||||
|
||||
fun testChangeSignature() {
|
||||
Registry.get("ide.completion.command.force.enabled").setValue(true, getTestRootDisposable())
|
||||
var text = """
|
||||
|
||||
@@ -298,7 +298,7 @@ public class HighlightInfo implements Segment {
|
||||
}
|
||||
else {
|
||||
LOG.error(new IllegalStateException("fileLevelComponents=" + fileLevelComponentsStorage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -1117,7 +1117,12 @@ public class HighlightInfo implements Segment {
|
||||
myIntentionActionDescriptors = List.copyOf(result);
|
||||
updateFields(getIntentionActionDescriptors(), document);
|
||||
}
|
||||
synchronized void updateLazyFixesPsiTimeStamp(long psiTimeStamp) {
|
||||
|
||||
/**
|
||||
* only for internal usages
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public synchronized void updateLazyFixesPsiTimeStamp(long psiTimeStamp) {
|
||||
List<LazyFixDescription> newFixes = ContainerUtil.map(myLazyQuickFixes,
|
||||
d -> d.psiModificationStamp() == 0
|
||||
? new LazyFixDescription(d.fixesComputer(), psiTimeStamp, d.future())
|
||||
|
||||
+36
-29
@@ -262,35 +262,7 @@ internal class CommandCompletionProvider : CompletionProvider<CompletionParamete
|
||||
TextRange(hostOffset, originalDocument.textLength))
|
||||
|
||||
|
||||
var file =
|
||||
if (isNonWritten) {
|
||||
val createdFile = originalFile.copy() as PsiFile
|
||||
if (createdFile is PsiFileImpl) {
|
||||
createdFile.setOriginalFile(topFile)
|
||||
}
|
||||
createdFile
|
||||
}
|
||||
else {
|
||||
if (topFile == originalFile) {
|
||||
var createdFile = commandCompletionFactory.createFile(originalFile, adjustedText)
|
||||
if (createdFile == null) {
|
||||
createdFile = PsiFileFactory.getInstance(topEditor.project)
|
||||
.createFileFromText(topFile.getName(), topFile.getLanguage(), adjustedText, true, true, false, topFile.virtualFile)
|
||||
if (createdFile is PsiFileImpl) {
|
||||
createdFile.setOriginalFile(topFile)
|
||||
}
|
||||
}
|
||||
createdFile
|
||||
}
|
||||
else {
|
||||
val createdFile = PsiFileFactory.getInstance(topEditor.project)
|
||||
.createFileFromText(topFile.getName(), topFile.getLanguage(), adjustedText, true, true, false, topFile.virtualFile)
|
||||
if (createdFile is PsiFileImpl) {
|
||||
createdFile.setOriginalFile(topFile)
|
||||
}
|
||||
createdFile
|
||||
}
|
||||
}
|
||||
var file = createFile(isNonWritten, originalFile, topFile, commandCompletionFactory, adjustedText, topEditor)
|
||||
|
||||
if (file is PsiFileImpl) {
|
||||
file.setOriginalFile(topFile)
|
||||
@@ -302,6 +274,41 @@ internal class CommandCompletionProvider : CompletionProvider<CompletionParamete
|
||||
}
|
||||
return AdjustedCompletionParameters(file, adjustedOffset, hostAdjustedOffset)
|
||||
}
|
||||
|
||||
private fun createFile(
|
||||
isNonWritten: Boolean,
|
||||
originalFile: PsiFile,
|
||||
topFile: PsiFile,
|
||||
commandCompletionFactory: CommandCompletionFactory,
|
||||
adjustedText: String,
|
||||
topEditor: Editor,
|
||||
): PsiFile {
|
||||
if (isNonWritten) {
|
||||
val createdFile = originalFile.copy() as PsiFile
|
||||
if (createdFile is PsiFileImpl) {
|
||||
createdFile.setOriginalFile(topFile)
|
||||
}
|
||||
return createdFile
|
||||
}
|
||||
|
||||
if (topFile == originalFile) {
|
||||
var createdFile = commandCompletionFactory.createFile(originalFile, adjustedText)
|
||||
if (createdFile == null) {
|
||||
createdFile = PsiFileFactory.getInstance(topEditor.project)
|
||||
.createFileFromText(topFile.getName(), topFile.getLanguage(), adjustedText, true, true, false, topFile.virtualFile)
|
||||
if (createdFile is PsiFileImpl) {
|
||||
createdFile.setOriginalFile(topFile)
|
||||
}
|
||||
}
|
||||
return createdFile
|
||||
}
|
||||
val createdFile = PsiFileFactory.getInstance(topEditor.project)
|
||||
.createFileFromText(topFile.getName(), topFile.getLanguage(), adjustedText, true, true, false, topFile.virtualFile)
|
||||
if (createdFile is PsiFileImpl) {
|
||||
createdFile.setOriginalFile(topFile)
|
||||
}
|
||||
return createdFile
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
||||
+25
-2
@@ -12,15 +12,18 @@ import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass.addAvailableFixes
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
|
||||
import com.intellij.codeInsight.quickfix.LazyQuickFixUpdater
|
||||
import com.intellij.concurrency.currentThreadContext
|
||||
import com.intellij.injected.editor.DocumentWindow
|
||||
import com.intellij.lang.injection.InjectedLanguageManager
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.progress.jobToIndicator
|
||||
import com.intellij.openapi.util.ProperTextRange
|
||||
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import kotlinx.coroutines.job
|
||||
import org.jetbrains.annotations.Nls
|
||||
import javax.swing.Icon
|
||||
@@ -54,15 +57,35 @@ internal class DirectErrorFixCompletionCommand(
|
||||
val indicator = DaemonProgressIndicator()
|
||||
readAction {
|
||||
jobToIndicator(currentThreadContext().job, indicator) {
|
||||
val highlightings = runAnnotatorsInGeneralHighlighting(topLevelPsiFile, true, true, true)
|
||||
//todo merge with error finder
|
||||
val fileDocument = topLevelPsiFile.fileDocument
|
||||
val lineNumber = fileDocument.getLineNumber(topLevelOffset)
|
||||
val startLineNumber = maxOf(0, lineNumber - 2)
|
||||
val endLineNumber = minOf(fileDocument.lineCount - 1, lineNumber + 2)
|
||||
val startOffset = fileDocument.getLineStartOffset(startLineNumber)
|
||||
val endOffset = fileDocument.getLineEndOffset(endLineNumber)
|
||||
val highlightings = runAnnotatorsInGeneralHighlighting(topLevelPsiFile,
|
||||
startOffset,
|
||||
endOffset,
|
||||
ProperTextRange(startOffset, endOffset),
|
||||
true, true, true)
|
||||
val lazyQuickFixUpdater = LazyQuickFixUpdater.getInstance(psiFile.project)
|
||||
for (info in highlightings) {
|
||||
if (info.hasLazyQuickFixes()) {
|
||||
info.updateLazyFixesPsiTimeStamp(PsiModificationTracker.getInstance(psiFile.project).modificationCount);
|
||||
lazyQuickFixUpdater.waitQuickFixesSynchronously(psiFile, editor, info)
|
||||
}
|
||||
if (!(info.startOffset == highlightInfo.range.startOffset && info.endOffset == highlightInfo.range.endOffset)) {
|
||||
continue
|
||||
}
|
||||
val fixes: MutableList<IntentionActionDescriptor> = ArrayList()
|
||||
addAvailableFixesForGroups(info, topLevelEditor, topLevelPsiFile, fixes, -1, topLevelOffset, false)
|
||||
for (fix in fixes) {
|
||||
if (fix.action.text == name) {
|
||||
var currentName = fix.action.text
|
||||
if (currentName.startsWith("<html>") && currentName.endsWith("</html>")) {
|
||||
currentName = currentName.substring(6, currentName.length - 7)
|
||||
}
|
||||
if (currentName == name) {
|
||||
return@jobToIndicator fix.action
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -47,6 +47,7 @@ internal class DirectInspectionFixCompletionCommand(
|
||||
|
||||
override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
|
||||
if (editor == null) return
|
||||
//todo merge with error finder
|
||||
val injectedLanguageManager = InjectedLanguageManager.getInstance(psiFile.project)
|
||||
val topLevelFile = injectedLanguageManager.getTopLevelFile(psiFile)
|
||||
val topLevelEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor)
|
||||
|
||||
+194
-149
@@ -1,32 +1,18 @@
|
||||
// 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.codeInsight.completion.command.commands
|
||||
|
||||
import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
|
||||
import com.intellij.codeInsight.completion.command.CommandCompletionUnsupportedOperationException
|
||||
import com.intellij.codeInsight.completion.command.CommandProvider
|
||||
import com.intellij.codeInsight.completion.command.CompletionCommand
|
||||
import com.intellij.codeInsight.completion.command.HighlightInfoLookup
|
||||
import com.intellij.codeInsight.completion.command.MyEditor
|
||||
import com.intellij.codeInsight.completion.command.commandCompletionEnabled
|
||||
import com.intellij.codeInsight.completion.command.*
|
||||
import com.intellij.codeInsight.daemon.HighlightDisplayKey
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.codeInsight.daemon.impl.*
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo.IntentionActionDescriptor
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightVisitorBasedInspection
|
||||
import com.intellij.codeInsight.daemon.impl.IntentionActionFilter
|
||||
import com.intellij.codeInsight.daemon.impl.IntentionMenuContributor
|
||||
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar
|
||||
import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass
|
||||
import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass.IntentionsInfo
|
||||
import com.intellij.codeInsight.intention.CommonIntentionAction
|
||||
import com.intellij.codeInsight.intention.EmptyIntentionAction
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.intention.IntentionActionDelegate
|
||||
import com.intellij.codeInsight.intention.IntentionManager
|
||||
import com.intellij.codeInsight.intention.*
|
||||
import com.intellij.codeInsight.intention.impl.CachedIntentions
|
||||
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
|
||||
import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewComputable
|
||||
import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewUnsupportedOperationException
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
|
||||
import com.intellij.codeInsight.quickfix.LazyQuickFixUpdater
|
||||
import com.intellij.codeInspection.InspectionEngine
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemDescriptorBase
|
||||
@@ -43,6 +29,8 @@ import com.intellij.lang.annotation.HighlightSeverity.INFORMATION
|
||||
import com.intellij.lang.injection.InjectedLanguageManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.diagnostic.ControlFlowException
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||
@@ -59,16 +47,20 @@ import com.intellij.openapi.project.ProjectTypeService
|
||||
import com.intellij.openapi.util.Iconable
|
||||
import com.intellij.openapi.util.Iconable.ICON_FLAG_VISIBILITY
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.ProperTextRange
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager.Companion.getInstance
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.job
|
||||
import java.lang.ref.SoftReference
|
||||
import java.util.concurrent.CancellationException
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
@@ -97,14 +89,14 @@ internal class DirectIntentionCommandProvider : CommandProvider {
|
||||
return runBlockingCancellable {
|
||||
val result: MutableList<CompletionCommand> = ArrayList()
|
||||
|
||||
val errorCache = originalEditor.getUserData(ERROR_CACHE)
|
||||
val errorCache = originalEditor.getUserData(ERROR_CACHE)?.get()
|
||||
val cachedErrorCommand = errorCache?.getCommands(psiFile.fileDocument, offset)
|
||||
cachedErrorCommand?.let {
|
||||
result.addAll(it)
|
||||
}
|
||||
val asyncErrorHighlighting = if (cachedErrorCommand == null) asyncErrorHighlighting(psiFile, editor, offset) else null
|
||||
|
||||
val inspectionCache = originalEditor.getUserData(INSPECTION_CACHE)
|
||||
val inspectionCache = originalEditor.getUserData(INSPECTION_CACHE)?.get()
|
||||
val cachedInspectionCommand = inspectionCache?.getCommands(psiFile.fileDocument, offset)
|
||||
cachedInspectionCommand?.let {
|
||||
result.addAll(it)
|
||||
@@ -120,22 +112,22 @@ internal class DirectIntentionCommandProvider : CommandProvider {
|
||||
val errors = asyncErrorHighlighting?.await()
|
||||
errors?.let {
|
||||
result.addAll(errors)
|
||||
originalEditor.putUserData(ERROR_CACHE, IntentionCache(
|
||||
originalEditor.putUserData(ERROR_CACHE, SoftReference(IntentionCache(
|
||||
commands = errors,
|
||||
offset = offset,
|
||||
hashCode = psiFile.fileDocument.immutableCharSequence.hashCode()
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
|
||||
val inspections = asyncInspections?.await()
|
||||
inspections?.let {
|
||||
result.addAll(inspections)
|
||||
originalEditor.putUserData(INSPECTION_CACHE, IntentionCache(
|
||||
originalEditor.putUserData(INSPECTION_CACHE, SoftReference(IntentionCache(
|
||||
commands = inspections,
|
||||
offset = offset,
|
||||
hashCode = psiFile.fileDocument.immutableCharSequence.hashCode()
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
val intentions = asyncIntentions.await()
|
||||
@@ -154,8 +146,8 @@ internal class DirectIntentionCommandProvider : CommandProvider {
|
||||
return intentionEditor
|
||||
}
|
||||
|
||||
private val ERROR_CACHE: Key<IntentionCache> = Key.create("completion.command.error.cache")
|
||||
private val INSPECTION_CACHE: Key<IntentionCache> = Key.create("completion.command.inspection.cache")
|
||||
private val ERROR_CACHE: Key<SoftReference<IntentionCache>> = Key.create("completion.command.error.cache")
|
||||
private val INSPECTION_CACHE: Key<SoftReference<IntentionCache>> = Key.create("completion.command.inspection.cache")
|
||||
|
||||
private data class IntentionCache(
|
||||
private val commands: List<CompletionCommand>,
|
||||
@@ -183,147 +175,200 @@ internal class DirectIntentionCommandProvider : CommandProvider {
|
||||
val offsetProvider = IntentionCommandOffsetProvider.EP_NAME.forLanguage(language)
|
||||
|
||||
val result: MutableMap<String, CompletionCommand> = mutableMapOf()
|
||||
val intentionCommandSkipper = IntentionCommandSkipper.EP_NAME.forLanguage(psiFile.language)
|
||||
val injectedLanguageManager = InjectedLanguageManager.getInstance(psiFile.project)
|
||||
val allOffsets = offsetProvider?.findOffsets(psiFile, offset) ?: mutableListOf(offset)
|
||||
val fileDocument = psiFile.fileDocument
|
||||
val lineByOffset = mutableMapOf<Int, Int>()
|
||||
for (offset in allOffsets) {
|
||||
val lineNumber = fileDocument.getLineNumber(offset)
|
||||
lineByOffset.compute(lineNumber) { k, v -> if (v == null) offset else maxOf(v, offset) }
|
||||
}
|
||||
for (currentOffset in lineByOffset.values) {
|
||||
editor.caretModel.moveToOffset(currentOffset)
|
||||
val topLevelFile = injectedLanguageManager.getTopLevelFile(psiFile)
|
||||
val topLevelEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor)
|
||||
val topLevelOffset = injectedLanguageManager.injectedToHost(psiFile, currentOffset)
|
||||
val isInjected = topLevelFile != psiFile
|
||||
val profileToUse = getInstance(psiFile.project).getCurrentProfile()
|
||||
val inspectionWrapper = InspectionProfileWrapper(profileToUse)
|
||||
val inspectionTools = getInspectionTools(inspectionWrapper, originalFile)
|
||||
val lineRange = getLineRange(topLevelFile, topLevelOffset)
|
||||
val indicator = EmptyProgressIndicator()
|
||||
val inspectionResult = jobToIndicator(coroutineContext.job, indicator) {
|
||||
if (!isInjected) {
|
||||
InspectionEngine.inspectEx(inspectionTools, topLevelFile, lineRange, lineRange, true, false, true, indicator, fun(_: LocalInspectionToolWrapper, _: ProblemDescriptor): Boolean {
|
||||
return true
|
||||
})
|
||||
}
|
||||
else {
|
||||
val textRange = getLineRange(psiFile, currentOffset)
|
||||
InspectionEngine.inspectElements(inspectionTools, psiFile, textRange, true, true, indicator,
|
||||
PsiTreeUtil.collectElements(psiFile) { it.textRange.intersects(textRange) }.toList(), fun(_: LocalInspectionToolWrapper, _: ProblemDescriptor): Boolean {
|
||||
return true
|
||||
})
|
||||
}
|
||||
try {
|
||||
val intentionCommandSkipper = IntentionCommandSkipper.EP_NAME.forLanguage(psiFile.language)
|
||||
val injectedLanguageManager = InjectedLanguageManager.getInstance(psiFile.project)
|
||||
val allOffsets = offsetProvider?.findOffsets(psiFile, offset) ?: mutableListOf(offset)
|
||||
val fileDocument = psiFile.fileDocument
|
||||
val lineByOffset = mutableMapOf<Int, Int>()
|
||||
for (offset in allOffsets) {
|
||||
val lineNumber = fileDocument.getLineNumber(offset)
|
||||
lineByOffset.compute(lineNumber) { k, v -> if (v == null) offset else maxOf(v, offset) }
|
||||
}
|
||||
for (entry: MutableMap.MutableEntry<LocalInspectionToolWrapper?, List<ProblemDescriptor?>?> in inspectionResult.entries) {
|
||||
val inspectionToolWrapper: LocalInspectionToolWrapper = entry.key ?: continue
|
||||
val toolId = inspectionToolWrapper.shortName
|
||||
val descriptors: List<ProblemDescriptor?> = entry.value ?: continue
|
||||
for (descriptor in descriptors) {
|
||||
if (descriptor == null) continue
|
||||
val fixes = descriptor.fixes ?: continue
|
||||
if (descriptor !is ProblemDescriptorBase) continue
|
||||
var textRange = descriptor.textRange ?: continue
|
||||
if (!lineRange.intersects(textRange)) continue
|
||||
if (isInjected) {
|
||||
textRange = injectedLanguageManager.injectedToHost(psiFile, textRange)
|
||||
for (currentOffset in lineByOffset.values) {
|
||||
editor.caretModel.moveToOffset(currentOffset)
|
||||
val topLevelFile = injectedLanguageManager.getTopLevelFile(psiFile)
|
||||
val topLevelEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor)
|
||||
val topLevelOffset = injectedLanguageManager.injectedToHost(psiFile, currentOffset)
|
||||
val isInjected = topLevelFile != psiFile
|
||||
val profileToUse = getInstance(psiFile.project).getCurrentProfile()
|
||||
val inspectionWrapper = InspectionProfileWrapper(profileToUse)
|
||||
val inspectionTools = getInspectionTools(inspectionWrapper, originalFile)
|
||||
val lineRange = getLineRange(topLevelFile, topLevelOffset)
|
||||
val indicator = EmptyProgressIndicator()
|
||||
val inspectionResult = jobToIndicator(coroutineContext.job, indicator) {
|
||||
if (!isInjected) {
|
||||
InspectionEngine.inspectEx(inspectionTools, topLevelFile, lineRange, lineRange, true, false, true, indicator, fun(_: LocalInspectionToolWrapper, _: ProblemDescriptor): Boolean {
|
||||
return true
|
||||
})
|
||||
}
|
||||
val displayKey: HighlightDisplayKey = inspectionToolWrapper.getDisplayKey() ?: continue
|
||||
val severity: HighlightSeverity = inspectionWrapper.getErrorLevel(displayKey, topLevelFile).severity
|
||||
val severityRegistrar: SeverityRegistrar = inspectionWrapper.profileManager.severityRegistrar
|
||||
val level = ProblemDescriptorUtil.highlightTypeFromDescriptor(descriptor, severity, severityRegistrar)
|
||||
//necessary to be compatible with call site
|
||||
editor.caretModel.moveToOffset(offset)
|
||||
topLevelEditor.caretModel.moveToOffset(offset)
|
||||
for (i in 0..fixes.size - 1) {
|
||||
val action = QuickFixWrapper.wrap(descriptor, i)
|
||||
if (action is EmptyIntentionAction) continue
|
||||
if (intentionCommandSkipper != null && intentionCommandSkipper.skip(action, psiFile, currentOffset)) continue
|
||||
if (!isInjected && !ShowIntentionActionsHandler.availableFor(topLevelFile, topLevelEditor, topLevelOffset, action)) continue
|
||||
if (isInjected && !ShowIntentionActionsHandler.availableFor(psiFile, editor, currentOffset, action)) continue
|
||||
val priority = if (level.getSeverity(null) == INFORMATION) 70 else 80
|
||||
val icon = if (level.getSeverity(null) == INFORMATION) AllIcons.Actions.IntentionBulbGrey else AllIcons.Actions.IntentionBulb
|
||||
else {
|
||||
val textRange = getLineRange(psiFile, currentOffset)
|
||||
InspectionEngine.inspectElements(inspectionTools, psiFile, textRange, true, true, indicator,
|
||||
PsiTreeUtil.collectElements(psiFile) { it.textRange.intersects(textRange) }.toList(), fun(_: LocalInspectionToolWrapper, _: ProblemDescriptor): Boolean {
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
for (entry: MutableMap.MutableEntry<LocalInspectionToolWrapper?, List<ProblemDescriptor?>?> in inspectionResult.entries) {
|
||||
val inspectionToolWrapper: LocalInspectionToolWrapper = entry.key ?: continue
|
||||
val toolId = inspectionToolWrapper.shortName
|
||||
val descriptors: List<ProblemDescriptor?> = entry.value ?: continue
|
||||
for (descriptor in descriptors) {
|
||||
if (descriptor == null) continue
|
||||
val fixes = descriptor.fixes ?: continue
|
||||
if (descriptor !is ProblemDescriptorBase) continue
|
||||
var textRange = descriptor.textRange ?: continue
|
||||
if (!lineRange.intersects(textRange)) continue
|
||||
if (isInjected) {
|
||||
textRange = injectedLanguageManager.injectedToHost(psiFile, textRange)
|
||||
}
|
||||
val displayKey: HighlightDisplayKey = inspectionToolWrapper.getDisplayKey() ?: continue
|
||||
val severity: HighlightSeverity = inspectionWrapper.getErrorLevel(displayKey, topLevelFile).severity
|
||||
val severityRegistrar: SeverityRegistrar = inspectionWrapper.profileManager.severityRegistrar
|
||||
val level = ProblemDescriptorUtil.highlightTypeFromDescriptor(descriptor, severity, severityRegistrar)
|
||||
//necessary to be compatible with call site
|
||||
editor.caretModel.moveToOffset(offset)
|
||||
topLevelEditor.caretModel.moveToOffset(offset)
|
||||
for (i in 0..fixes.size - 1) {
|
||||
val action = QuickFixWrapper.wrap(descriptor, i)
|
||||
if (action is EmptyIntentionAction) continue
|
||||
if (intentionCommandSkipper != null && intentionCommandSkipper.skip(action, psiFile, currentOffset)) continue
|
||||
if (!isInjected && !ShowIntentionActionsHandler.availableFor(topLevelFile, topLevelEditor, topLevelOffset, action)) continue
|
||||
if (isInjected && !ShowIntentionActionsHandler.availableFor(psiFile, editor, currentOffset, action)) continue
|
||||
val priority = if (level.getSeverity(null) == INFORMATION) 70 else 80
|
||||
val icon = if (level.getSeverity(null) == INFORMATION) AllIcons.Actions.IntentionBulbGrey else AllIcons.Actions.IntentionBulb
|
||||
|
||||
result[toolId + ":" + action.text] = (DirectInspectionFixCompletionCommand(
|
||||
inspectionId = toolId,
|
||||
name = action.text,
|
||||
priority = priority,
|
||||
icon = icon,
|
||||
highlightInfo = HighlightInfoLookup(textRange, level.attributesKey, priority),
|
||||
targetOffset = currentOffset,
|
||||
previewProvider = {
|
||||
IntentionPreviewComputable(psiFile.project, action, psiFile, editor, offset).call()
|
||||
}))
|
||||
result[toolId + ":" + action.text] = (DirectInspectionFixCompletionCommand(
|
||||
inspectionId = toolId,
|
||||
name = action.text,
|
||||
priority = priority,
|
||||
icon = icon,
|
||||
highlightInfo = HighlightInfoLookup(textRange, level.attributesKey, priority),
|
||||
targetOffset = currentOffset,
|
||||
previewProvider = {
|
||||
computePreview(psiFile, action, editor, offset)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
if (e is ControlFlowException || e is CancellationException) {
|
||||
throw e
|
||||
}
|
||||
thisLogger().error("Can't collect inspections", e)
|
||||
}
|
||||
|
||||
|
||||
return@readAction result.values.toList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun computePreview(
|
||||
psiFile: PsiFile,
|
||||
action: IntentionAction,
|
||||
editor: Editor,
|
||||
offset: Int,
|
||||
): IntentionPreviewInfo? = try {
|
||||
IntentionPreviewComputable(psiFile.project, action, psiFile, editor, offset).call()
|
||||
}
|
||||
catch (e: Exception) {
|
||||
if (e is ControlFlowException || e is CancellationException) {
|
||||
throw e
|
||||
}
|
||||
thisLogger().error("Can't create preview", e)
|
||||
null
|
||||
}
|
||||
|
||||
private fun CoroutineScope.asyncErrorHighlighting(psiFile: PsiFile, editor: Editor, offset: Int): Deferred<List<CompletionCommand>> {
|
||||
return async {
|
||||
val result: MutableList<CompletionCommand> = mutableListOf()
|
||||
readAction {
|
||||
val injectedLanguageManager = InjectedLanguageManager.getInstance(psiFile.project)
|
||||
val topLevelFile = injectedLanguageManager.getTopLevelFile(psiFile)
|
||||
val topLevelEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor)
|
||||
val topLevelOffset = injectedLanguageManager.injectedToHost(psiFile, offset)
|
||||
val isInjected = topLevelFile != psiFile
|
||||
val indicator = DaemonProgressIndicator()
|
||||
val errorHighlightings: List<HighlightInfo?>? = jobToIndicator(coroutineContext.job, indicator) {
|
||||
ApplicationManager.getApplication().runReadAction<List<HighlightInfo?>?> {
|
||||
HighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting(topLevelFile, true, true, true)
|
||||
try {
|
||||
val injectedLanguageManager = InjectedLanguageManager.getInstance(psiFile.project)
|
||||
val topLevelFile = injectedLanguageManager.getTopLevelFile(psiFile)
|
||||
val topLevelEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor)
|
||||
val topLevelOffset = injectedLanguageManager.injectedToHost(psiFile, offset)
|
||||
|
||||
val fileDocument = topLevelFile.fileDocument
|
||||
val lineNumber = fileDocument.getLineNumber(topLevelOffset)
|
||||
val startLineNumber = maxOf(0, lineNumber - 2)
|
||||
val endLineNumber = minOf(fileDocument.lineCount - 1, lineNumber + 2)
|
||||
val startOffset = fileDocument.getLineStartOffset(startLineNumber)
|
||||
val endOffset = fileDocument.getLineEndOffset(endLineNumber)
|
||||
val isInjected = topLevelFile != psiFile
|
||||
val indicator = DaemonProgressIndicator()
|
||||
val errorHighlightings: List<HighlightInfo?>? = jobToIndicator(coroutineContext.job, indicator) {
|
||||
ApplicationManager.getApplication().runReadAction<List<HighlightInfo?>?> {
|
||||
HighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting(topLevelFile,
|
||||
startOffset,
|
||||
endOffset,
|
||||
ProperTextRange(startOffset, endOffset),
|
||||
true, true, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errorHighlightings == null) return@readAction
|
||||
var insideRange = getLineRange(topLevelFile, topLevelOffset)
|
||||
if (isInjected) {
|
||||
insideRange = insideRange.intersection(
|
||||
TextRange(injectedLanguageManager.injectedToHost(psiFile, 0),
|
||||
injectedLanguageManager.injectedToHost(psiFile, psiFile.fileDocument.textLength)))
|
||||
}
|
||||
var attemptToAddAi = false
|
||||
for (info: HighlightInfo? in errorHighlightings) {
|
||||
if (info == null) continue
|
||||
if (!insideRange.intersects(info.startOffset, info.endOffset) ||
|
||||
(isInjected && !insideRange.contains(TextRange(info.startOffset, info.endOffset)))) continue
|
||||
if (!attemptToAddAi && info.severity == HighlightSeverity.ERROR) {
|
||||
val intentionsInfo: IntentionsInfo? = tryToAddAI(info, psiFile, editor, offset)
|
||||
if (intentionsInfo != null) {
|
||||
val intentionsCache = CachedIntentions(psiFile.project, psiFile, editor)
|
||||
intentionsCache.wrapAndUpdateActions(intentionsInfo, false)
|
||||
for (descriptor in intentionsCache.errorFixes) {
|
||||
val command = IntentionCompletionCommand(descriptor, 50, AllIcons.Actions.QuickfixBulb,
|
||||
HighlightInfoLookup(TextRange(info.startOffset, info.endOffset),
|
||||
CodeInsightColors.ERRORS_ATTRIBUTES, 100), offset) { null }
|
||||
result.add(command)
|
||||
if (errorHighlightings == null) return@readAction
|
||||
var insideRange = getLineRange(topLevelFile, topLevelOffset)
|
||||
if (isInjected) {
|
||||
insideRange = insideRange.intersection(
|
||||
TextRange(injectedLanguageManager.injectedToHost(psiFile, 0),
|
||||
injectedLanguageManager.injectedToHost(psiFile, psiFile.fileDocument.textLength)))
|
||||
}
|
||||
var attemptToAddAi = false
|
||||
val lazyQuickFixUpdater = LazyQuickFixUpdater.getInstance(psiFile.project)
|
||||
for (info: HighlightInfo? in errorHighlightings) {
|
||||
if (info == null) continue
|
||||
if (!insideRange.intersects(info.startOffset, info.endOffset) ||
|
||||
(isInjected && !insideRange.contains(TextRange(info.startOffset, info.endOffset)))) continue
|
||||
if (info.hasLazyQuickFixes()) {
|
||||
info.updateLazyFixesPsiTimeStamp(PsiModificationTracker.getInstance(psiFile.project).modificationCount)
|
||||
lazyQuickFixUpdater.waitQuickFixesSynchronously(psiFile, editor, info)
|
||||
}
|
||||
if (!attemptToAddAi && info.severity == HighlightSeverity.ERROR) {
|
||||
val intentionsInfo: IntentionsInfo? = tryToAddAI(info, psiFile, editor, offset)
|
||||
if (intentionsInfo != null) {
|
||||
val intentionsCache = CachedIntentions(psiFile.project, psiFile, editor)
|
||||
intentionsCache.wrapAndUpdateActions(intentionsInfo, false)
|
||||
for (descriptor in intentionsCache.errorFixes) {
|
||||
val command = IntentionCompletionCommand(descriptor, 50, AllIcons.Actions.QuickfixBulb,
|
||||
HighlightInfoLookup(TextRange(info.startOffset, info.endOffset),
|
||||
CodeInsightColors.ERRORS_ATTRIBUTES, 100), offset) { null }
|
||||
result.add(command)
|
||||
}
|
||||
}
|
||||
attemptToAddAi = true
|
||||
}
|
||||
attemptToAddAi = true
|
||||
}
|
||||
val fixes: MutableList<IntentionActionDescriptor> = ArrayList()
|
||||
ShowIntentionsPass.addAvailableFixesForGroups(info, topLevelEditor, topLevelFile, fixes, -1, offset, false)
|
||||
for (descriptor in fixes) {
|
||||
if (descriptor.action is EmptyIntentionAction) continue
|
||||
var name = descriptor.action.text
|
||||
if (name.startsWith("<html>")) {
|
||||
name = descriptor.action.familyName
|
||||
val fixes: MutableList<IntentionActionDescriptor> = ArrayList()
|
||||
ShowIntentionsPass.addAvailableFixesForGroups(info, topLevelEditor, topLevelFile, fixes, -1, offset, false)
|
||||
for (descriptor in fixes) {
|
||||
if (descriptor.action is EmptyIntentionAction) continue
|
||||
var name = descriptor.action.text
|
||||
val prefix = "<html>"
|
||||
val suffix = "</html>"
|
||||
if (name.startsWith(prefix) && name.endsWith(suffix)) {
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
name = name.substring(prefix.length, name.length - suffix.length)
|
||||
}
|
||||
val command = DirectErrorFixCompletionCommand(name = name,
|
||||
priority = 100,
|
||||
icon = AllIcons.Actions.QuickfixBulb,
|
||||
highlightInfo = HighlightInfoLookup(TextRange(info.startOffset, info.endOffset),
|
||||
CodeInsightColors.ERRORS_ATTRIBUTES, 100),
|
||||
previewProvider = {
|
||||
computePreview(psiFile, descriptor.action, editor, offset)
|
||||
})
|
||||
result.add(command)
|
||||
}
|
||||
val command = DirectErrorFixCompletionCommand(name = name,
|
||||
priority = 100,
|
||||
icon = AllIcons.Actions.QuickfixBulb,
|
||||
highlightInfo = HighlightInfoLookup(TextRange(info.startOffset, info.endOffset),
|
||||
CodeInsightColors.ERRORS_ATTRIBUTES, 100),
|
||||
previewProvider = {
|
||||
IntentionPreviewComputable(psiFile.project, descriptor.action, psiFile, editor, offset).call()
|
||||
})
|
||||
result.add(command)
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
if (e is ControlFlowException || e is CancellationException) {
|
||||
throw e
|
||||
}
|
||||
thisLogger().error("Can't collect errors", e)
|
||||
}
|
||||
}
|
||||
return@async result
|
||||
}
|
||||
@@ -428,9 +473,9 @@ internal class DirectIntentionCommandProvider : CommandProvider {
|
||||
val intentionCommand =
|
||||
IntentionCompletionCommand(intention, 50, intention.icon ?: AllIcons.Actions.IntentionBulbGrey, null, currentOffset) {
|
||||
editor.caretModel.moveToOffset(currentOffset)
|
||||
IntentionPreviewComputable(psiFile.project, intention.action, psiFile, editor, currentOffset).call()
|
||||
computePreview(psiFile, intention.action, editor, currentOffset)
|
||||
}
|
||||
result.put(intention.text, intentionCommand)
|
||||
result[intention.text] = intentionCommand
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-5
@@ -20,6 +20,7 @@ import com.intellij.platform.diagnostic.telemetry.helpers.TraceKt;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -103,6 +104,21 @@ public final class HighlightVisitorBasedInspection extends GlobalSimpleInspectio
|
||||
boolean highlightErrorElements,
|
||||
boolean runAnnotators,
|
||||
boolean runVisitors) {
|
||||
int startOffset = 0;
|
||||
int endOffset = psiFile.getTextLength();
|
||||
ProperTextRange visibleRange = ProperTextRange.create(psiFile.getTextRange());
|
||||
return runAnnotatorsInGeneralHighlighting(psiFile, startOffset, endOffset, visibleRange, highlightErrorElements, runAnnotators, runVisitors);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.Internal
|
||||
public static @NotNull List<HighlightInfo> runAnnotatorsInGeneralHighlighting(@NotNull PsiFile psiFile,
|
||||
int startOffset,
|
||||
int endOffset,
|
||||
@NotNull ProperTextRange visibleRange,
|
||||
boolean highlightErrorElements,
|
||||
boolean runAnnotators,
|
||||
boolean runVisitors) {
|
||||
ApplicationManager.getApplication().assertIsNonDispatchThread();
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
Project project = psiFile.getProject();
|
||||
@@ -112,13 +128,13 @@ public final class HighlightVisitorBasedInspection extends GlobalSimpleInspectio
|
||||
// in case the inspection is running in batch mode
|
||||
// todo IJPL-339 figure out what is the correct context here
|
||||
CodeInsightContext context = FileViewProviderUtil.getCodeInsightContext(psiFile);
|
||||
HighlightingSessionImpl.getOrCreateHighlightingSession(psiFile, context, daemonProgressIndicator, ProperTextRange.create(psiFile.getTextRange()),
|
||||
HighlightingSessionImpl.getOrCreateHighlightingSession(psiFile, context, daemonProgressIndicator, visibleRange,
|
||||
TextRange.EMPTY_RANGE);
|
||||
GeneralHighlightingPass ghp =
|
||||
new GeneralHighlightingPass(psiFile, document, 0, psiFile.getTextLength(), true, ProperTextRange.create(psiFile.getTextRange()), null,
|
||||
runAnnotators, runVisitors, highlightErrorElements, HighlightInfoUpdater.EMPTY);
|
||||
InjectedGeneralHighlightingPass ighp = new InjectedGeneralHighlightingPass(psiFile, document, null, 0, psiFile.getTextLength(), true,
|
||||
ProperTextRange.create(psiFile.getTextRange()), null,
|
||||
new GeneralHighlightingPass(psiFile, document, startOffset, endOffset, true, visibleRange, null,
|
||||
runAnnotators, runVisitors, highlightErrorElements, HighlightInfoUpdater.EMPTY);
|
||||
InjectedGeneralHighlightingPass ighp = new InjectedGeneralHighlightingPass(psiFile, document, null, startOffset, endOffset, true,
|
||||
visibleRange, null,
|
||||
runAnnotators, runVisitors, highlightErrorElements, HighlightInfoUpdater.EMPTY);
|
||||
ighp.setContext(context);
|
||||
String fileName = psiFile.getName();
|
||||
|
||||
Reference in New Issue
Block a user