move dangerous remove* methods from UpdateHighlightingUtil to spellchecker plugin closer to their usages, to avoid abuse

GitOrigin-RevId: 3d8b7ad0aa7307d38287aec692307744c197185f
This commit is contained in:
Alexey Kudravtsev
2024-03-19 13:22:10 +01:00
committed by intellij-monorepo-bot
parent 1dde31ff99
commit f04797bfb5
5 changed files with 87 additions and 36 deletions

View File

@@ -1,16 +1,18 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.spellchecker.quickfixes
import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil
import com.intellij.codeInsight.intention.FileModifier
import com.intellij.codeInsight.intention.HighPriorityAction
import com.intellij.codeInsight.intention.choice.ChoiceTitleIntentionAction
import com.intellij.codeInsight.intention.choice.ChoiceVariantIntentionAction
import com.intellij.codeInsight.intention.choice.DefaultIntentionActionWithChoice
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.impl.DocumentMarkupModel
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.Segment
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
@@ -18,6 +20,7 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.SmartPointerManager
import com.intellij.refactoring.suggested.startOffset
import com.intellij.spellchecker.util.SpellCheckerBundle
import com.intellij.util.concurrency.ThreadingAssertions
class ChangeTo(typo: String, element: PsiElement, private val range: TextRange) : DefaultIntentionActionWithChoice, LazySuggestions(typo) {
private val pointer = SmartPointerManager.getInstance(element.project).createSmartPsiElementPointer(element, element.containingFile)
@@ -60,7 +63,7 @@ class ChangeTo(typo: String, element: PsiElement, private val range: TextRange)
val document = file.viewProvider.document
val myRange = getRange(document) ?: return
UpdateHighlightersUtil.removeHighlightersWithExactRange(document, project, myRange)
removeHighlightersWithExactRange(document, project, myRange)
document.replaceString(myRange.startOffset, myRange.endOffset, suggestion)
}
@@ -88,4 +91,25 @@ class ChangeTo(typo: String, element: PsiElement, private val range: TextRange)
return (0 until limit).map { ChangeToVariantAction(it) }
}
/**
* Remove all highlighters with exactly the given range from [DocumentMarkupModel].
* This might be useful in quick fixes and intention actions to provide immediate feedback.
* Note that all highlighters at the given range are removed, not only the ones produced by your inspection,
* but most likely that will look fine:
* they'll be restored when the new highlighting pass is finished.
* This method currently works in O(total highlighter count in file) time.
*/
fun removeHighlightersWithExactRange(document: Document, project: Project, range: Segment) {
if (IntentionPreviewUtils.isIntentionPreviewActive()) return
ThreadingAssertions.assertEventDispatchThread()
val model = DocumentMarkupModel.forDocument(document, project, false) ?: return
for (highlighter in model.allHighlighters) {
if (TextRange.areSegmentsEqual(range, highlighter!!)) {
model.removeHighlighter(highlighter!!)
}
}
}
}

View File

@@ -1,15 +1,21 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.spellchecker.quickfixes;
import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.LowPriorityAction;
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemDescriptorUtil;
import com.intellij.ide.DataManager;
import com.intellij.model.SideEffectGuard;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.impl.DocumentMarkupModel;
import com.intellij.openapi.editor.markup.MarkupModel;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -19,6 +25,7 @@ import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.spellchecker.util.SpellCheckerBundle;
import com.intellij.ui.components.JBList;
import com.intellij.util.concurrency.ThreadingAssertions;
import com.intellij.util.containers.ContainerUtil;
import icons.SpellcheckerIcons;
import org.jetbrains.annotations.NotNull;
@@ -102,9 +109,32 @@ public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
SpellCheckerManager.getInstance(project).acceptWordAsCorrect$intellij_spellchecker(word, file.getViewProvider().getVirtualFile(), project, layer);
TextRange range = descriptor.getTextRangeInElement().shiftRight(psi.getTextRange().getStartOffset());
UpdateHighlightersUtil.removeHighlightersWithExactRange(file.getViewProvider().getDocument(), project, range, SpellCheckingInspection.SPELL_CHECKING_INSPECTION_TOOL_NAME);
removeHighlightersWithExactRange(file.getViewProvider().getDocument(), project, range, SpellCheckingInspection.SPELL_CHECKING_INSPECTION_TOOL_NAME);
}
/**
* Remove all highlighters with exactly the given range from {@link DocumentMarkupModel} produced by given inspection.
* This might be useful in quick fixes and intention actions to provide immediate feedback.
* This method currently works in O(total highlighter count in file) time.
*/
public static void removeHighlightersWithExactRange(@NotNull Document document, @NotNull Project project, @NotNull Segment range, @NotNull String inspectionToolId) {
if (IntentionPreviewUtils.isIntentionPreviewActive()) return;
ThreadingAssertions.assertEventDispatchThread();
MarkupModel model = DocumentMarkupModel.forDocument(document, project, false);
if (model == null) return;
for (RangeHighlighter highlighter : model.getAllHighlighters()) {
if (TextRange.areSegmentsEqual(range, highlighter)) {
var highlightInfo = HighlightInfo.fromRangeHighlighter(highlighter);
if(highlightInfo == null || !inspectionToolId.equals(highlightInfo.getInspectionToolId())) {
continue;
}
model.removeHighlighter(highlighter);
}
}
}
@Override
public Icon getIcon(int flags) {
return SpellcheckerIcons.Spellcheck;