[spellchecker] extend DictionaryLevel to DictionaryLayer with corresponding extension point

In Rider we want to pass ReSharper settings layers to UI-parts like combobox for choosing default dictionary to save words.

GitOrigin-RevId: b6b395d0108f2f1142fc1ab3656d3493c5b9be9c
This commit is contained in:
Den Mukhametianov
2024-01-11 14:49:21 +01:00
committed by intellij-monorepo-bot
parent 75284da775
commit cabf0773f8
12 changed files with 172 additions and 91 deletions

View File

@@ -8,6 +8,7 @@
<extensionPoint name="spellchecker.dictionary.runtimeDictionaryProvider" interface="com.intellij.spellchecker.dictionary.RuntimeDictionaryProvider" dynamic="true"/>
<extensionPoint name="spellchecker.dictionary.checker" interface="com.intellij.spellchecker.dictionary.DictionaryChecker" dynamic="true"/>
<extensionPoint name="spellchecker.builtInDictionariesProvider" interface="com.intellij.spellchecker.settings.BuiltInDictionariesProvider" dynamic="true"/>
<extensionPoint name="spellchecker.dictionaryLayersProvider" interface="com.intellij.spellchecker.DictionaryLayersProvider" dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
@@ -29,6 +30,7 @@
<spellchecker.support language="TEXT" implementationClass="com.intellij.spellchecker.tokenizer.SpellcheckingStrategy"/>
<spellchecker.bundledDictionaryProvider implementation="com.intellij.spellchecker.DefaultBundledDictionariesProvider"/>
<spellchecker.dictionary.customDictionaryProvider implementation="com.intellij.spellchecker.hunspell.HunspellDictionaryProvider"/>
<spellchecker.dictionaryLayersProvider implementation="com.intellij.spellchecker.PlatformSettingsDictionaryLayersProvider" order="first"/>
<registryKey key="spellchecker.corrections.limit" defaultValue="3" description="Spellchecker corrections limit" restartRequired="false"/>
<registryKey key="spellchecker.inspection.enabled" defaultValue="true" description="Enable spellchecking inspection" restartRequired="false"/>

View File

@@ -0,0 +1,94 @@
// 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
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.spellchecker.dictionary.EditableDictionary
import com.intellij.spellchecker.dictionary.ProjectDictionary
import com.intellij.spellchecker.settings.DictionaryLayerChangesSubscriber
import com.intellij.spellchecker.settings.DictionaryLayersChangesDispatcher
import com.intellij.spellchecker.state.AppDictionaryState
import com.intellij.spellchecker.state.ProjectDictionaryState
import com.intellij.spellchecker.util.SpellCheckerBundle
import org.jetbrains.annotations.Nls
abstract class DictionaryLayersProvider {
abstract fun getLayers(project: Project): List<DictionaryLayer>
open fun startWatchingChanges(project: Project) { }
companion object {
val EP_NAME = ExtensionPointName.create<DictionaryLayersProvider>("com.intellij.spellchecker.dictionaryLayersProvider")
fun getAllLayers(project: Project): List<DictionaryLayer> {
return project.service<PerProjectDictionaryLayersHolder>().getAllLayers()
}
fun getLayer(project: Project, layerName: String): DictionaryLayer? {
return project.service<PerProjectDictionaryLayersHolder>().getLayer(layerName)
}
}
}
@Service(Service.Level.PROJECT)
class PerProjectDictionaryLayersHolder(private val project: Project) {
private lateinit var layersMap: Map<String, DictionaryLayer>
private lateinit var layers: List<DictionaryLayer>
init {
project.service<DictionaryLayersChangesDispatcher>()
.register(object : DictionaryLayerChangesSubscriber {
override fun layersChanged() {
rebuild()
}
})
DictionaryLayersProvider.EP_NAME.extensionList.forEach{
it.startWatchingChanges(project)
}
rebuild()
}
fun rebuild() {
layers = DictionaryLayersProvider.EP_NAME.extensionList.flatMap { it.getLayers(project) }.toList()
layersMap = layers.associateBy { it.name }
}
fun getLayer(layerName: String): DictionaryLayer? {
return layersMap[layerName]
}
fun getAllLayers(): List<DictionaryLayer> {
return layers
}
}
interface DictionaryLayer {
val dictionary: EditableDictionary
@get:Nls
val name: String
}
class PlatformSettingsDictionaryLayersProvider : DictionaryLayersProvider() {
override fun getLayers(project: Project): List<DictionaryLayer> {
return listOf(ApplicationDictionaryLayer.INSTANCE, ProjectDictionaryLayer(project))
}
}
class ProjectDictionaryLayer(val project: Project) : DictionaryLayer {
companion object {
val name = SpellCheckerBundle.message("dictionary.name.project.level")
}
override val name = Companion.name
override val dictionary: ProjectDictionary = project.service<ProjectDictionaryState>().projectDictionary
}
class ApplicationDictionaryLayer : DictionaryLayer {
companion object {
val name = SpellCheckerBundle.message("dictionary.name.application.level")
val INSTANCE = ApplicationDictionaryLayer()
}
override val name = Companion.name
override val dictionary: EditableDictionary = AppDictionaryState.getInstance().dictionary
}

View File

@@ -29,7 +29,6 @@ import com.intellij.openapi.vfs.*
import com.intellij.project.getProjectStoreDirectory
import com.intellij.spellchecker.SpellCheckerManager.Companion.restartInspections
import com.intellij.spellchecker.dictionary.*
import com.intellij.spellchecker.dictionary.Dictionary
import com.intellij.spellchecker.engine.SpellCheckerEngine
import com.intellij.spellchecker.engine.SuggestionProvider
import com.intellij.spellchecker.grazie.GrazieSpellCheckerEngine
@@ -43,11 +42,8 @@ import com.intellij.util.EventDispatcher
import com.intellij.util.application
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.runBlocking
import org.jetbrains.annotations.Nls
import java.io.File
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
private val LOG = logger<SpellCheckerManager>()
private val BUNDLED_EP_NAME = ExtensionPointName<BundledDictionaryProvider>("com.intellij.spellchecker.bundledDictionaryProvider")
@@ -238,31 +234,31 @@ class SpellCheckerManager(val project: Project) : Disposable {
}
fun acceptWordAsCorrect(word: String, project: Project) {
acceptWordAsCorrect(word = word, file = null, project = project, dictionaryLevel = DictionaryLevel.PROJECT) // TODO: or default
acceptWordAsCorrect(word = word, file = null, project = project, dictionaryLayer = ProjectDictionaryLayer(project)) // TODO: or default
}
internal fun acceptWordAsCorrect(word: String, file: VirtualFile?, project: Project, dictionaryLevel: DictionaryLevel) {
if (DictionaryLevel.NOT_SPECIFIED == dictionaryLevel) {
internal fun acceptWordAsCorrect(word: String, file: VirtualFile?, project: Project, dictionaryLayer: DictionaryLayer?) {
if (dictionaryLayer == null) {
return
}
val transformed = spellChecker!!.transformation.transform(word) ?: return
val dictionary = if (DictionaryLevel.PROJECT == dictionaryLevel) projectDictionary else appDictionary
val dictionary = dictionaryLayer.dictionary
if (file != null) {
WriteCommandAction.writeCommandAction(project)
.run<RuntimeException> {
UndoManager.getInstance(project).undoableActionPerformed(object : BasicUndoableAction(file) {
override fun undo() {
removeWordFromDictionary(dictionary!!, transformed)
removeWordFromDictionary(dictionary, transformed)
}
override fun redo() {
addWordToDictionary(dictionary!!, transformed)
addWordToDictionary(dictionary, transformed)
}
})
}
}
addWordToDictionary(dictionary = dictionary!!, word = transformed)
addWordToDictionary(dictionary = dictionary, word = transformed)
}
private fun addWordToDictionary(dictionary: EditableDictionary, word: String) {
@@ -338,22 +334,6 @@ class SpellCheckerManager(val project: Project) : Disposable {
}
}
internal enum class DictionaryLevel(private val nameSupplier: Supplier<@Nls String>) {
APP(SpellCheckerBundle.messagePointer("dictionary.name.application.level")),
PROJECT(SpellCheckerBundle.messagePointer("dictionary.name.project.level")),
NOT_SPECIFIED(SpellCheckerBundle.messagePointer("dictionary.name.not.specified"));
@Nls
fun getName(): String = nameSupplier.get()
companion object {
private val DICTIONARY_LEVELS = EnumSet.allOf(DictionaryLevel::class.java).associateBy { it.getName() }
@JvmStatic
fun getLevelByName(name: String): DictionaryLevel = DICTIONARY_LEVELS.get(name) ?: NOT_SPECIFIED
}
}
private class CustomDictFileListener(private val project: Project, private val manager: SpellCheckerManager) : VirtualFileListener {
override fun fileDeleted(event: VirtualFileEvent) {
removeCustomDictionaries(event.file.path)

View File

@@ -141,7 +141,7 @@ public final class SpellCheckingInspection extends LocalInspectionTool {
private static void addBatchDescriptor(PsiElement element,
@NotNull TextRange textRange,
@NotNull ProblemsHolder holder) {
SpellCheckerQuickFix[] fixes = SpellcheckingStrategy.getDefaultBatchFixes();
SpellCheckerQuickFix[] fixes = SpellcheckingStrategy.getDefaultBatchFixes(element);
ProblemDescriptor problemDescriptor = createProblemDescriptor(element, textRange, fixes, false);
holder.registerProblem(problemDescriptor);
}

View File

@@ -11,29 +11,27 @@ import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.spellchecker.DictionaryLevel;
import com.intellij.spellchecker.DictionaryLayer;
import com.intellij.spellchecker.DictionaryLayersProvider;
import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.spellchecker.util.SpellCheckerBundle;
import com.intellij.ui.components.JBList;
import com.intellij.util.containers.ContainerUtil;
import icons.SpellcheckerIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.List;
public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
private static final SaveTo SAVE_TO_APP_FIX = new SaveTo(DictionaryLevel.APP);
private static final SaveTo SAVE_TO_PROJECT_FIX = new SaveTo(DictionaryLevel.PROJECT);
private static final String DICTIONARY = " dictionary";
private static final String DOTS = "...";
private DictionaryLevel myLevel = DictionaryLevel.NOT_SPECIFIED;
@Nullable private DictionaryLayer myLevel = null;
private String myWord;
private SaveTo(@NotNull DictionaryLevel level) {
public SaveTo(@NotNull DictionaryLayer level) {
myLevel = level;
}
@@ -41,7 +39,7 @@ public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
myWord = word;
}
public SaveTo(String word, @NotNull DictionaryLevel level) {
public SaveTo(String word, @NotNull DictionaryLayer level) {
myWord = word;
myLevel = level;
}
@@ -53,7 +51,7 @@ public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
@Override
public @NotNull String getFamilyName() {
final String dictionary = myLevel != DictionaryLevel.NOT_SPECIFIED ? myLevel.getName() + DICTIONARY : DOTS;
final String dictionary = myLevel != null ? myLevel.getName() + DICTIONARY : DOTS;
return SpellCheckerBundle.message("save.0.to.1", "", dictionary);
}
@@ -68,10 +66,10 @@ public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
.getDataContextFromFocusAsync()
.onSuccess(context -> {
final String wordToSave = myWord != null ? myWord : ProblemDescriptorUtil.extractHighlightedText(descriptor, descriptor.getPsiElement());
final VirtualFile file = descriptor.getPsiElement().getContainingFile().getVirtualFile();
if (myLevel == DictionaryLevel.NOT_SPECIFIED) {
final List<String> dictionaryList = Arrays.asList(DictionaryLevel.PROJECT.getName(), DictionaryLevel.APP.getName());
final JBList<String> dictList = new JBList<>(dictionaryList);
if (myLevel == null) {
final JBList<String> dictList = new JBList<>(
ContainerUtil.map(DictionaryLayersProvider.Companion.getAllLayers(project), it -> it.getName())
);
JBPopupFactory.getInstance()
.createListPopupBuilder(dictList)
@@ -80,7 +78,7 @@ public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
() ->
CommandProcessor.getInstance().executeCommand(
project,
() -> acceptWord(wordToSave, DictionaryLevel.getLevelByName(dictList.getSelectedValue()), descriptor),
() -> acceptWord(wordToSave, DictionaryLayersProvider.Companion.getLayer(project, dictList.getSelectedValue()), descriptor),
getName(),
null
)
@@ -94,7 +92,7 @@ public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
});
}
private static void acceptWord(String word, DictionaryLevel level, ProblemDescriptor descriptor) {
private static void acceptWord(String word, @Nullable DictionaryLayer level, ProblemDescriptor descriptor) {
SideEffectGuard.checkSideEffectAllowed(SideEffectGuard.EffectType.SETTINGS);
PsiElement psi = descriptor.getPsiElement();
@@ -106,10 +104,6 @@ public final class SaveTo implements SpellCheckerQuickFix, LowPriorityAction {
UpdateHighlightersUtil.removeHighlightersWithExactRange(file.getViewProvider().getDocument(), project, range);
}
public static SaveTo getSaveToLevelFix(DictionaryLevel level) {
return DictionaryLevel.PROJECT == level ? SAVE_TO_PROJECT_FIX : SAVE_TO_APP_FIX;
}
@Override
public Icon getIcon(int flags) {
return SpellcheckerIcons.Spellcheck;

View File

@@ -1,14 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.spellchecker.settings
import com.intellij.util.messages.Topic
// used in Rider
interface CustomDictionaryPathListener {
fun dictionariesChanged(paths: List<String>)
companion object {
@Topic.AppLevel
val TOPIC: Topic<CustomDictionaryPathListener> = Topic(CustomDictionaryPathListener::class.java)
}
}

View File

@@ -0,0 +1,26 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.spellchecker.settings
import com.intellij.openapi.components.Service
import com.intellij.util.application
import com.intellij.util.messages.MessageBusConnection
import com.intellij.util.messages.Topic
@Service(Service.Level.PROJECT)
class DictionaryLayersChangesDispatcher {
@Topic.ProjectLevel
private val topic = Topic(DictionaryLayerChangesSubscriber::class.java)
val publisher: DictionaryLayerChangesSubscriber
get() = application.messageBus.syncPublisher(topic)
fun register(subscriber: DictionaryLayerChangesSubscriber): MessageBusConnection {
val connection = application.messageBus.connect()
connection.subscribe(topic, subscriber)
return connection
}
}
interface DictionaryLayerChangesSubscriber {
fun layersChanged()
}

View File

@@ -3,7 +3,8 @@ package com.intellij.spellchecker.settings
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.spellchecker.DictionaryLevel
import com.intellij.spellchecker.ApplicationDictionaryLayer
import com.intellij.spellchecker.ProjectDictionaryLayer
import com.intellij.spellchecker.state.ProjectDictionaryState
internal class SettingsTransferActivity : ProjectActivity {
@@ -12,9 +13,9 @@ internal class SettingsTransferActivity : ProjectActivity {
if (settings.isSettingsTransferred) {
return
}
if (settings.isUseSingleDictionaryToSave && DictionaryLevel.PROJECT.getName() == settings.dictionaryToSave &&
if (settings.isUseSingleDictionaryToSave && ProjectDictionaryLayer.name == settings.dictionaryToSave &&
project.getService(ProjectDictionaryState::class.java).projectDictionary.words.isEmpty()) {
settings.dictionaryToSave = DictionaryLevel.APP.getName()
settings.dictionaryToSave = ApplicationDictionaryLayer.name
}
settings.isSettingsTransferred = true
}

View File

@@ -1,13 +1,12 @@
// 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.settings;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.spellchecker.DictionaryLevel;
import com.intellij.spellchecker.ProjectDictionaryLayer;
import com.intellij.spellchecker.util.SPFileUtil;
import com.intellij.util.PathUtil;
import org.jdom.Element;
@@ -31,7 +30,7 @@ public final class SpellCheckerSettings implements PersistentStateComponent<Elem
private static final String RUNTIME_DICTIONARY_ATTR_NAME = "RuntimeDictionary";
private static final String DICTIONARY_TO_SAVE_ATTR_NAME = "DefaultDictionary";
private static final String DEFAULT_DICTIONARY_TO_SAVE = DictionaryLevel.PROJECT.getName();
private static final String DEFAULT_DICTIONARY_TO_SAVE = ProjectDictionaryLayer.Companion.getName();
private static final String USE_SINGLE_DICT_ATTR_NAME = "UseSingleDictionary";
private static final boolean DEFAULT_USE_SINGLE_DICT = true;
private static final String SETTINGS_TRANSFERRED = "transferred";
@@ -80,9 +79,6 @@ public final class SpellCheckerSettings implements PersistentStateComponent<Elem
public void setCustomDictionariesPaths(List<String> customDictionariesPaths) {
myCustomDictionariesPaths = customDictionariesPaths;
ApplicationManager.getApplication().getMessageBus()
.syncPublisher(CustomDictionaryPathListener.Companion.getTOPIC())
.dictionariesChanged(customDictionariesPaths);
}
public Set<String> getRuntimeDisabledDictionariesNames() {

View File

@@ -13,7 +13,7 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.profile.codeInspection.ui.ErrorsConfigurable;
import com.intellij.spellchecker.DictionaryLevel;
import com.intellij.spellchecker.DictionaryLayersProvider;
import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.spellchecker.dictionary.CustomDictionaryProvider;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
@@ -77,8 +77,7 @@ public final class SpellCheckerSettingsPane implements Disposable {
myDictionariesComboBox.setEnabled(myUseSingleDictionary.isSelected());
}
});
myDictionariesComboBox.addItem(DictionaryLevel.APP.getName());
myDictionariesComboBox.addItem(DictionaryLevel.PROJECT.getName());
DictionaryLayersProvider.Companion.getAllLayers(project).forEach(it -> myDictionariesComboBox.addItem(it.getName()));
linkContainer.setLayout(new BorderLayout());
linkContainer.add(link);

View File

@@ -14,7 +14,7 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.spellchecker.DictionaryLevel;
import com.intellij.spellchecker.DictionaryLayersProvider;
import com.intellij.spellchecker.inspections.PlainTextSplitter;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.spellchecker.quickfixes.ChangeTo;
@@ -27,6 +27,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
/**
@@ -53,9 +54,6 @@ public class SpellcheckingStrategy {
public static final Tokenizer<PsiElement> TEXT_TOKENIZER = new TokenizerBase<>(PlainTextSplitter.getInstance());
private static final SpellCheckerQuickFix[] BATCH_FIXES =
new SpellCheckerQuickFix[]{SaveTo.getSaveToLevelFix(DictionaryLevel.APP), SaveTo.getSaveToLevelFix(DictionaryLevel.PROJECT)};
public Tokenizer getTokenizer(PsiElement element, Set<SpellCheckingInspection.SpellCheckingScope> scope) {
return getTokenizer(element);
}
@@ -145,7 +143,7 @@ public class SpellcheckingStrategy {
final SpellCheckerSettings settings = SpellCheckerSettings.getInstance(element.getProject());
if (settings.isUseSingleDictionaryToSave()) {
result.add(new SaveTo(typo, DictionaryLevel.getLevelByName(settings.getDictionaryToSave())));
result.add(new SaveTo(typo, Objects.requireNonNull(DictionaryLayersProvider.Companion.getLayer(element.getProject(), settings.getDictionaryToSave()))));
return result.toArray(LocalQuickFix.EMPTY_ARRAY);
}
@@ -153,8 +151,10 @@ public class SpellcheckingStrategy {
return result.toArray(LocalQuickFix.EMPTY_ARRAY);
}
public static SpellCheckerQuickFix[] getDefaultBatchFixes() {
return BATCH_FIXES;
public static SpellCheckerQuickFix[] getDefaultBatchFixes(PsiElement element) {
return DictionaryLayersProvider.Companion.getAllLayers(element.getProject())
.stream().map(it -> new SaveTo(it))
.toArray(SpellCheckerQuickFix[]::new);
}
public boolean isMyContext(@NotNull PsiElement element) {

View File

@@ -6,8 +6,9 @@ import com.intellij.openapi.command.impl.UndoManagerImpl;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.spellchecker.DictionaryLevel;
import com.intellij.spellchecker.ProjectDictionaryLayer;
import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
@@ -20,11 +21,12 @@ public class AcceptWordAsCorrectTest extends BasePlatformTestCase {
public static final String TEST_TXT = "test.txt";
private void doTest(String word, VirtualFile file) {
final SpellCheckerManager manager = SpellCheckerManager.getInstance(getProject());
var project = getProject();
final SpellCheckerManager manager = SpellCheckerManager.getInstance(project);
try {
assertTrue(manager.hasProblem(word));
CommandProcessor.getInstance().executeCommand(getProject(), () -> manager
.acceptWordAsCorrect$intellij_spellchecker(word, file, getProject(), DictionaryLevel.PROJECT), getName(), null);
CommandProcessor.getInstance().executeCommand(project, () -> manager
.acceptWordAsCorrect$intellij_spellchecker(word, file, project, new ProjectDictionaryLayer(project)), getName(), null);
assertFalse(manager.hasProblem(word));
}
finally {
@@ -72,20 +74,21 @@ public class AcceptWordAsCorrectTest extends BasePlatformTestCase {
public void testUndoRedo() {
final VirtualFile file = myFixture.configureByText(TEST_TXT, TYPPO).getVirtualFile();
final UndoManager instance = UndoManager.getInstance(getProject());
final Project project = getProject();
final UndoManager instance = UndoManager.getInstance(project);
((UndoManagerImpl)instance).dropHistoryInTests(); // to make sure it's empty
final SpellCheckerManager manager = SpellCheckerManager.getInstance(getProject());
final SpellCheckerManager manager = SpellCheckerManager.getInstance(project);
assertTrue(manager.hasProblem(TYPPO));
CommandProcessor.getInstance().executeCommand(getProject(), () -> manager
.acceptWordAsCorrect$intellij_spellchecker(TYPPO, file, getProject(), DictionaryLevel.PROJECT), getName(), null);
CommandProcessor.getInstance().executeCommand(project, () -> manager
.acceptWordAsCorrect$intellij_spellchecker(TYPPO, file, project, new ProjectDictionaryLayer(project)), getName(), null);
assertFalse(manager.hasProblem(TYPPO));
instance.undo(FileEditorManager.getInstance(getProject()).getSelectedEditor(file));
instance.undo(FileEditorManager.getInstance(project).getSelectedEditor(file));
assertTrue(manager.hasProblem(TYPPO));
instance.redo(FileEditorManager.getInstance(getProject()).getSelectedEditor(file));
instance.redo(FileEditorManager.getInstance(project).getSelectedEditor(file));
assertFalse(manager.hasProblem(TYPPO));
}
}