[grazie] IJPL-200544 Migrate SpellCheckingInspection to grazie module

GitOrigin-RevId: 635bb5187f9652ea920e373fdec86f0f72831b68
This commit is contained in:
Ilia Permiashkin
2025-09-19 22:48:59 +00:00
committed by intellij-monorepo-bot
parent 602482036d
commit 15ee76e949
59 changed files with 460 additions and 450 deletions
@@ -1,6 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.json;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.json.json5.Json5FileType;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.IdeActions;
@@ -8,7 +9,6 @@ import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.testFramework.ServiceContainerUtil;
import com.intellij.testFramework.fixtures.InjectionTestFixture;
import com.jetbrains.jsonSchema.JsonSchemaTestProvider;
@@ -24,7 +24,7 @@ import java.util.function.Predicate;
public class JsonSpellcheckerTest extends JsonTestCase {
private void doTest() {
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
myFixture.configureByFile(getTestName(false) + ".json");
myFixture.checkHighlighting(true, false, true);
}
@@ -52,14 +52,14 @@ public class JsonSpellcheckerTest extends JsonTestCase {
JsonSchemaTestServiceImpl.setProvider(null);
}
});
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
myFixture.checkHighlighting(true, false, true);
}
// WEB-31894 EA-117068
public void testAfterModificationOfStringLiteralWithEscaping() {
myFixture.configureByFile(getTestName(false) + ".json");
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
myFixture.checkHighlighting();
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_BACKSPACE);
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_BACKSPACE);
@@ -67,7 +67,7 @@ public class JsonSpellcheckerTest extends JsonTestCase {
}
public void testHashesQuotedSpelling() {
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
myFixture.configureByText("hashes.json", """
{
"typo": "<TYPO>hereistheerror</TYPO>",
@@ -83,7 +83,7 @@ public class JsonSpellcheckerTest extends JsonTestCase {
}
public void testInjectedFragments() {
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
myFixture.configureByText(Json5FileType.INSTANCE, """
{
@@ -17,13 +17,13 @@ com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection
com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection
com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection
com.intellij.codeInspection.visibility.VisibilityInspection
com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
com.intellij.lang.javascript.inspections.JSMismatchedCollectionQueryUpdateInspection
com.intellij.lang.javascript.inspections.JSPotentiallyInvalidConstructorUsageInspection
com.intellij.lang.javascript.inspections.JSSuspiciousNameCombinationInspection
com.intellij.lang.javascript.inspections.UnterminatedStatementJSInspection
com.intellij.psi.css.inspections.CssUnknownPropertyInspection
com.intellij.restructuredtext.python.inspections.RestRoleInspection
com.intellij.spellchecker.inspections.SpellCheckingInspection
com.jetbrains.php.lang.inspections.PhpUndefinedClassInspection
com.jetbrains.php.lang.inspections.PhpUndefinedVariableInspection
com.jetbrains.php.lang.inspections.PhpUnusedParameterInspection
+1
View File
@@ -63,6 +63,7 @@ jvm_library(
"//platform/vcs-impl:vcs-impl_test_lib",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"@lib//:http-client",
"//jps/model-impl",
"@lib//:gson",
@@ -51,6 +51,7 @@
<orderEntry type="module" module-name="intellij.xml.dom.impl" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.relaxng" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.spellchecker" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.debugger.impl" scope="RUNTIME" />
<orderEntry type="library" scope="TEST" name="http-client" level="project" />
<orderEntry type="module" module-name="intellij.platform.jps.model.impl" scope="TEST" />
@@ -1,7 +1,7 @@
// 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.codeHighlighting
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.testFramework.fixtures.BasePlatformTestCase
class CustomFileTypeInspectionTest : BasePlatformTestCase() {
@@ -15,7 +15,7 @@ infixl -- keyword
-- <TYPO descr="Typo: In word 'infixl'">infixl</TYPO> in line comment
{- <TYPO descr="Typo: In word 'infixl'">infixl</TYPO> in block comment -}
""")
myFixture.enableInspections(SpellCheckingInspection())
myFixture.enableInspections(GrazieSpellCheckingInspection())
myFixture.checkHighlighting()
}
+1
View File
@@ -35,6 +35,7 @@ jvm_library(
"//xml/xml-psi-api:psi",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
]
)
### auto-generated section `build intellij.devkit.testFramework` end
@@ -26,6 +26,7 @@
<orderEntry type="module" module-name="intellij.xml.analysis.impl" scope="TEST" />
<orderEntry type="module" module-name="intellij.xml.psi" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" scope="TEST" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
</component>
<component name="TestModuleProperties" production-module="intellij.devkit.core" />
</module>
@@ -1,10 +1,10 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
@@ -27,7 +27,7 @@ internal class DevKitMnemonicsSpellingTest : LightJavaCodeInsightFixtureTestCase
@Throws(Exception::class)
override fun setUp() {
super.setUp()
myFixture.enableInspections(SpellCheckingInspection::class.java)
myFixture.enableInspections(GrazieSpellCheckingInspection::class.java)
}
fun testMnemonics() {
@@ -69,6 +69,7 @@ jvm_library(
"//java/testFramework",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//platform/core-ui",
],
runtime_deps = [
@@ -30,6 +30,7 @@
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.java.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" scope="TEST" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
</component>
</module>
@@ -2,8 +2,8 @@
package org.jetbrains.idea.devkit.i18n;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.openapi.project.IntelliJProjectUtil;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.testFramework.TestDataPath;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
@@ -79,7 +79,7 @@ public class PluginXmlCapitalizationInspectionTest extends LightJavaCodeInsightF
" enum Capitalization {NotSpecified,Title,Sentence}" +
" Capitalization capitalization() default Capitalization.NotSpecified;" +
"}");
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
myFixture.testHighlighting("pluginXmlCapitalization_extensionPoint.xml", "MyExtensionPoint.java");
}
}
@@ -67,6 +67,11 @@
implementationClass="com.intellij.grazie.ide.inspection.detection.LanguageDetectionInspection"
level="WEAK WARNING" key="grazie.detection.inspection.text" language=""/>
<localInspection shortName="SpellCheckingInspection" bundle="messages.SpellCheckerBundle"
key="spellchecking.inspection.name" groupKey="proofread" enabledByDefault="true" level="TYPO"
language=""
implementationClass="com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection"/>
<applicationConfigurable parentId="editor" instance="com.intellij.grazie.ide.ui.proofreading.ProofreadConfigurable" id="proofread"
key="configurable.group.proofread.settings.display.name" bundle="messages.OptionsBundle"/>
+1
View File
@@ -27,6 +27,7 @@ jvm_library(
"//platform/editor-ui-api:editor-ui",
"//platform/indexing-api:indexing",
"//platform/projectModel-api:projectModel",
"//platform/refactoring",
"//platform/vcs-api:vcs",
"//platform/editor-ui-ex:editor-ex",
"//platform/lang-api:lang",
@@ -22,6 +22,7 @@
<orderEntry type="module" module-name="intellij.platform.editor.ui" />
<orderEntry type="module" module-name="intellij.platform.indexing" />
<orderEntry type="module" module-name="intellij.platform.projectModel" />
<orderEntry type="module" module-name="intellij.platform.refactoring" />
<orderEntry type="module" module-name="intellij.platform.vcs" />
<orderEntry type="module" module-name="intellij.platform.editor.ex" />
<orderEntry type="module" module-name="intellij.platform.lang" />
@@ -0,0 +1,346 @@
// 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.grazie.spellcheck;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.options.OptPane;
import com.intellij.lang.LanguageNamesValidation;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.refactoring.NamesValidator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.refactoring.rename.RenameUtil;
import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider;
import com.intellij.spellchecker.grazie.diacritic.Diacritics;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.spellchecker.inspections.SpellcheckingExtension;
import com.intellij.spellchecker.inspections.SpellcheckingExtension.SpellCheckingResult;
import com.intellij.spellchecker.inspections.SpellcheckingExtension.SpellingTypo;
import com.intellij.spellchecker.inspections.Splitter;
import com.intellij.spellchecker.tokenizer.SpellcheckingStrategy;
import com.intellij.spellchecker.tokenizer.SuppressibleSpellcheckingStrategy;
import com.intellij.spellchecker.tokenizer.TokenConsumer;
import com.intellij.spellchecker.util.SpellCheckerBundle;
import com.intellij.util.Consumer;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.text.StringSearcher;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.intellij.codeInspection.options.OptPane.checkbox;
import static com.intellij.codeInspection.options.OptPane.pane;
import static com.intellij.spellchecker.tokenizer.SpellcheckingStrategy.getSpellcheckingStrategy;
public final class GrazieSpellCheckingInspection extends SpellCheckingInspection {
@Override
public SuppressQuickFix @NotNull [] getBatchSuppressActions(@Nullable PsiElement element) {
if (element != null) {
SpellcheckingStrategy strategy = getSpellcheckingStrategy(element);
if (strategy instanceof SuppressibleSpellcheckingStrategy) {
return ((SuppressibleSpellcheckingStrategy)strategy).getSuppressActions(element, getShortName());
}
}
return super.getBatchSuppressActions(element);
}
@Override
public boolean isSuppressedFor(@NotNull PsiElement element) {
SpellcheckingStrategy strategy = getSpellcheckingStrategy(element);
if (strategy instanceof SuppressibleSpellcheckingStrategy) {
return ((SuppressibleSpellcheckingStrategy)strategy).isSuppressedFor(element, getShortName());
}
return super.isSuppressedFor(element);
}
@Override
public @NonNls @NotNull String getShortName() {
return SPELL_CHECKING_INSPECTION_TOOL_NAME;
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return super.buildVisitor(holder, isOnTheFly);
}
@Override
public @NotNull HighlightDisplayLevel getDefaultLevel() {
return Objects.requireNonNull(HighlightDisplayLevel.find(SpellCheckerSeveritiesProvider.TYPO));
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
boolean isOnTheFly,
@NotNull LocalInspectionToolSession session) {
if (!Registry.is("spellchecker.inspection.enabled", true) ||
InspectionProfileManager.hasTooLowSeverity(session, this) ||
InjectedLanguageManager.getInstance(holder.getProject()).isFrankensteinInjection(holder.getFile())) {
return PsiElementVisitor.EMPTY_VISITOR;
}
var scopes = buildAllowedScopes();
SpellCheckerManager manager = SpellCheckerManager.getInstance(holder.getProject());
return new PsiElementVisitor() {
@Override
public void visitWhiteSpace(@NotNull PsiWhiteSpace space) { }
@Override
public void visitElement(final @NotNull PsiElement element) {
if (holder.getResultCount() > 1000 || element.getNode() == null) return;
var strategy = getSpellcheckingStrategy(element);
if (strategy == null || !strategy.elementFitsScope(element, scopes) || isCopyrightComment(strategy, element)) return;
SpellCheckingResult result = SpellcheckingExtension.Companion.spellcheck(
element, strategy, session,
typo -> {
if (hasSameNamedReferenceInFile(typo.getWord(), element, strategy)) return;
registerProblem(typo, holder);
}
);
if (result == SpellCheckingResult.Checked) return;
tokenize(
strategy, element,
new MyTokenConsumer(manager, strategy, holder, LanguageNamesValidation.INSTANCE.forLanguage(element.getLanguage())),
scopes
);
}
};
}
private Set<SpellCheckingScope> buildAllowedScopes() {
var result = new HashSet<SpellCheckingScope>();
if (processLiterals) {
result.add(SpellCheckingScope.Literals);
}
if (processComments) {
result.add(SpellCheckingScope.Comments);
}
if (processCode) {
result.add(SpellCheckingScope.Code);
}
return result;
}
private static void addBatchDescriptor(@NotNull PsiElement element,
@NotNull TextRange textRange,
@NotNull String word,
@NotNull ProblemsHolder holder) {
var fixes = SpellcheckingStrategy.getDefaultBatchFixes(element, textRange, word);
ProblemDescriptor problemDescriptor = createProblemDescriptor(element, textRange, fixes, false);
holder.registerProblem(problemDescriptor);
}
private static void addRegularDescriptor(@NotNull PsiElement element, @NotNull TextRange textRange, @NotNull ProblemsHolder holder,
boolean useRename, String wordWithTypo) {
SpellcheckingStrategy strategy = getSpellcheckingStrategy(element);
LocalQuickFix[] fixes = strategy != null
? strategy.getRegularFixes(element, textRange, useRename, wordWithTypo)
: SpellcheckingStrategy.getDefaultRegularFixes(useRename, wordWithTypo, element, textRange);
final ProblemDescriptor problemDescriptor = createProblemDescriptor(element, textRange, fixes, true);
holder.registerProblem(problemDescriptor);
}
private static ProblemDescriptor createProblemDescriptor(PsiElement element, TextRange textRange,
LocalQuickFix[] fixes,
boolean onTheFly) {
final String description = SpellCheckerBundle.message("typo.in.word.ref");
return new ProblemDescriptorBase(element, element, description, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
false, textRange, onTheFly, onTheFly);
}
@SuppressWarnings("PublicField")
public boolean processCode = true;
public boolean processLiterals = true;
public boolean processComments = true;
@Override
public @NotNull OptPane getOptionsPane() {
return pane(
checkbox("processCode", SpellCheckerBundle.message("process.code")),
checkbox("processLiterals", SpellCheckerBundle.message("process.literals")),
checkbox("processComments", SpellCheckerBundle.message("process.comments"))
);
}
private static final class MyTokenConsumer extends TokenConsumer implements Consumer<TextRange> {
private static final Pattern NON_ENGLISH_LETTERS = Pattern.compile(".*[^a-zA-Z].*");
private final Set<String> myAlreadyChecked = CollectionFactory.createSmallMemoryFootprintSet();
private final SpellCheckerManager myManager;
private final ProblemsHolder myHolder;
private final NamesValidator myNamesValidator;
private final SpellcheckingStrategy myStrategy;
private boolean myCodeLike;
private PsiElement myElement;
private String myText;
private boolean myUseRename;
private int myOffset;
MyTokenConsumer(SpellCheckerManager manager, SpellcheckingStrategy strategy, ProblemsHolder holder, NamesValidator namesValidator) {
myManager = manager;
myStrategy = strategy;
myHolder = holder;
myNamesValidator = namesValidator;
}
@Override
public void consumeToken(final PsiElement element,
final String text,
final boolean useRename,
final int offset,
TextRange rangeToCheck,
Splitter splitter) {
myElement = element;
myText = text;
myUseRename = useRename;
myOffset = offset;
myCodeLike = myStrategy.elementFitsScope(myElement, Set.of(SpellCheckingScope.Code));
splitter.split(text, rangeToCheck, this);
}
@Override
public void consume(TextRange range) {
// Tokenization of large texts can produce a lot of tokens, but we are inside RA
ProgressManager.checkCanceled();
String word = range.substring(myText);
if (!myHolder.isOnTheFly() && myAlreadyChecked.contains(word)) {
return;
}
boolean keyword = myNamesValidator.isKeyword(word, myElement.getProject());
if (keyword || !hasProblem(word, range) || hasSameNamedReferenceInFile(word, myElement, myStrategy)) {
return;
}
range = myStrategy.getTokenizer(myElement)
.getHighlightingRange(myElement, myOffset, range);
assert range.getStartOffset() >= 0;
if (!myHolder.isOnTheFly()) {
myAlreadyChecked.add(word);
}
registerProblem(myHolder, myElement, range, myUseRename, word);
}
private boolean hasCamelCaseMatch(String word, TextRange range) {
Set<String> camelCaseWords = myManager.getUserCamelCaseWords()
.stream()
.filter(camelCaseWord -> camelCaseWord.contains(word))
.collect(Collectors.toSet());
if (camelCaseWords.isEmpty()) {
return false;
}
String text = myElement.getText();
for (String camelCaseWord : camelCaseWords) {
ProgressManager.checkCanceled();
int[] indexes = new StringSearcher(camelCaseWord, false, true).findAllOccurrences(text);
for (int index : indexes) {
TextRange hitRange = new TextRange(index, index + camelCaseWord.length());
if (range.intersectsStrict(hitRange)) {
return true;
}
}
}
return false;
}
private boolean hasProblem(String word, TextRange range) {
if (!myManager.hasProblem(word)) {
return false;
}
// Check if a user has added CamelCase word to the project / app dictionary
if (hasCamelCaseMatch(word, range)) {
return false;
}
// If the word isn't "code" or contains letters outside the English alphabet,
// then diacritic check should be skipped
if (!myCodeLike || NON_ENGLISH_LETTERS.matcher(word).matches()) {
return true;
}
Project project = myElement.getProject();
return SpellCheckerManager.getInstance(project).getSuggestions(word)
.stream()
.filter(suggestion -> RenameUtil.isValidName(project, myElement, suggestion))
.noneMatch(suggestion -> Diacritics.equalsIgnoringDiacritics(word, suggestion));
}
}
private static boolean hasSameNamedReferenceInFile(String word, PsiElement element, SpellcheckingStrategy strategy) {
if (!strategy.elementFitsScope(element, Set.of(SpellCheckingScope.Comments))) {
return false;
}
PsiFile file = element.getContainingFile();
Map<String, Boolean> references = CachedValuesManager.getProjectPsiDependentCache(file, (psi) -> new ConcurrentHashMap<>());
return references.computeIfAbsent(word, key -> hasSameNamedReferencesInFile(key, file));
}
private static boolean hasSameNamedReferencesInFile(String word, PsiFile file) {
int[] occurrences = new StringSearcher(word, true, true).findAllOccurrences(file.getText());
if (occurrences.length <= 1) {
return false;
}
for (int occurrence : occurrences) {
PsiReference reference = file.findReferenceAt(occurrence);
PsiElement resolvedReference = reference != null ? reference.resolve() : null;
if (reference != null && resolvedReference != null && reference.getElement() != resolvedReference) {
return true;
}
}
return false;
}
private static boolean isCopyrightComment(SpellcheckingStrategy strategy, PsiElement psi) {
return strategy.elementFitsScope(psi, Set.of(SpellCheckingScope.Comments))
&& StringUtil.containsIgnoreCase(psi.getText(), "Copyright")
&& isAtFileStart(psi);
}
private static boolean isAtFileStart(PsiElement psi) {
PsiFile file = psi.getContainingFile();
int textStart = psi.getTextRange().getStartOffset();
return file.getViewProvider().getContents().subSequence(0, textStart).chars().noneMatch(Character::isLetterOrDigit);
}
private static void registerProblem(@NotNull SpellingTypo typo, @NotNull ProblemsHolder holder) {
registerProblem(holder, typo.getElement(), typo.getRange(), false, typo.getWord());
}
private static void registerProblem(@NotNull ProblemsHolder holder,
@NotNull PsiElement element,
@NotNull TextRange range,
boolean useRename,
String word) {
if (holder.isOnTheFly()) {
addRegularDescriptor(element, range, holder, useRename, word);
}
else {
addBatchDescriptor(element, range, word, holder);
}
}
}
@@ -8,6 +8,7 @@ import com.intellij.grazie.ide.inspection.grammar.GrazieInspection
import com.intellij.grazie.jlanguage.Lang
import com.intellij.grazie.remote.HunspellDescriptor
import com.intellij.grazie.spellcheck.GrazieCheckers
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.grazie.style.StyleInspection
import com.intellij.grazie.text.TextChecker
import com.intellij.grazie.text.TextContent
@@ -25,7 +26,6 @@ import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiPlainText
import com.intellij.spellchecker.SpellCheckerManager.Companion.getInstance
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.intellij.testFramework.ExtensionTestUtil
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.fixtures.BasePlatformTestCase
@@ -37,7 +37,7 @@ import kotlin.io.path.Path
abstract class GrazieTestBase : BasePlatformTestCase() {
companion object {
val inspectionTools by lazy {
arrayOf<LocalInspectionTool>(GrazieInspection(), SpellCheckingInspection(), StyleInspection())
arrayOf<LocalInspectionTool>(GrazieInspection(), GrazieSpellCheckingInspection(), StyleInspection())
}
/**
@@ -2,7 +2,7 @@
package com.intellij.grazie.spellchecker.inspection;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
public abstract class SpellcheckerInspectionTestCase extends BasePlatformTestCase {
@@ -22,6 +22,6 @@ public abstract class SpellcheckerInspectionTestCase extends BasePlatformTestCas
}
public static LocalInspectionTool[] getInspectionTools() {
return new LocalInspectionTool[]{new SpellCheckingInspection()};
return new LocalInspectionTool[]{new GrazieSpellCheckingInspection()};
}
}
@@ -16,8 +16,8 @@
package com.intellij.grazie.spellchecker.inspection.quickfixes;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.grazie.spellchecker.inspection.SpellcheckerInspectionTestCase;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.spellchecker.quickfixes.ChangeTo;
import com.intellij.util.containers.ContainerUtil;
@@ -33,7 +33,7 @@ public abstract class AbstractSpellCheckerFixesTest extends SpellcheckerInspecti
private void doChangeToTest(int toSelect) {
myFixture.configureByFile(getBeforeFile());
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
final IntentionAction intention = ContainerUtil.filter(
myFixture.getAvailableIntentions(), (it) -> it.getFamilyName().equals(ChangeTo.getFixName())
).get(toSelect + 1);
@@ -44,7 +44,7 @@ public abstract class AbstractSpellCheckerFixesTest extends SpellcheckerInspecti
protected void doNoQuickFixTest(String quickfix) {
myFixture.configureByFile(getBeforeFile());
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
assertNull(myFixture.getAvailableIntention(quickfix));
}
@@ -16,13 +16,13 @@
package com.intellij.grazie.spellchecker.ui;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.SpellCheckingEditorCustomizationProvider;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.fileTypes.PlainTextLanguage;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
import com.intellij.ui.EditorCustomization;
import com.intellij.ui.EditorTextFieldProvider;
@@ -48,7 +48,7 @@ public class SpellCheckingEditorCustomizationTest extends BasePlatformTestCase {
InspectionProfileImpl.INIT_INSPECTIONS = true;
try {
myFixture.configureByText(PlainTextFileType.INSTANCE, document);
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
EditorCustomization customization = SpellCheckingEditorCustomizationProvider.getInstance().getCustomization(enabled);
assertNotNull(customization);
@@ -62,7 +62,7 @@ public class SpellCheckingEditorCustomizationTest extends BasePlatformTestCase {
}
public void testHighlightingWorksInEditorTextFieldWithCustomization() {
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
var customization = SpellCheckingEditorCustomizationProvider.getInstance().getEnabledCustomization();
assertNotNull(customization);
+1
View File
@@ -146,6 +146,7 @@ jvm_library(
"//jps/jps-builders:build",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//plugins/junit",
"//plugins/junit_rt:junit-rt",
"//java/java-indexing-api:indexing",
+1
View File
@@ -53,6 +53,7 @@
<orderEntry type="module" module-name="intellij.xml.langInjection" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.jps.build" />
<orderEntry type="module" module-name="intellij.spellchecker" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.junit" />
<orderEntry type="module" module-name="intellij.junit.rt" />
<orderEntry type="module" module-name="intellij.java.indexing" />
@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.groovy.lang;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
public class GroovySpellcheckerTest extends LightJavaCodeInsightFixtureTestCase {
@@ -14,7 +14,7 @@ public class GroovySpellcheckerTest extends LightJavaCodeInsightFixtureTestCase
}
private void checkTypos() {
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
myFixture.checkHighlighting(false, false, true);
}
@@ -2,8 +2,7 @@
package org.jetbrains.plugins.groovy.lang.highlighting;
import com.intellij.analysis.AnalysisBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import org.jetbrains.plugins.groovy.util.GroovyLatestTest;
import org.jetbrains.plugins.groovy.util.HighlightingTest;
@@ -12,7 +11,7 @@ import org.junit.Test;
public class GrSuppressionTest extends GroovyLatestTest implements HighlightingTest {
private void doTest(String before, String after) {
JavaCodeInsightTestFixture fixture = getFixture();
fixture.enableInspections(SpellCheckingInspection.class);
fixture.enableInspections(GrazieSpellCheckingInspection.class);
configureByText(before);
fixture.checkHighlighting();
fixture.launchAction(fixture.getAvailableIntention(AnalysisBundle.message("suppress.inspection.file")));
+1
View File
@@ -56,6 +56,7 @@ jvm_library(
"//platform/testFramework:testFramework_test_lib",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//xml/xml-psi-api:psi",
"//xml/tests:tests_test_lib",
"//platform/core-ui",
@@ -16,6 +16,7 @@
<orderEntry type="module" module-name="intellij.xml.impl" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" scope="TEST" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.xml.psi" scope="TEST" />
<orderEntry type="module" module-name="intellij.xml.tests" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
@@ -12,6 +12,7 @@ import com.intellij.codeInsight.intention.impl.CachedIntentions;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.htmlInspections.*;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.htmltools.HtmlToolsTestsUtil;
import com.intellij.htmltools.codeInspection.htmlInspections.HtmlDeprecatedAttributeInspection;
import com.intellij.htmltools.codeInspection.htmlInspections.HtmlDeprecatedTagInspection;
@@ -31,7 +32,6 @@ import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.testFramework.HighlightTestInfo;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
import com.intellij.xml.Html5SchemaProvider;
@@ -841,7 +841,7 @@ public class HtmlHighlightingTest extends BasePlatformTestCase {
}
public void testSpelling() {
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
doTest();
}
@@ -3,12 +3,12 @@
package org.jetbrains.kotlin.checkers;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.testFramework.core.FileComparisonFailedError;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.testFramework.ExpectedHighlightingData;
import com.intellij.testFramework.fixtures.impl.JavaCodeInsightTestFixtureImpl;
import kotlin.Unit;
@@ -60,7 +60,7 @@ public abstract class AbstractKotlinHighlightVisitorTest extends KotlinLightCode
void doTestWithInfos(@NotNull String __) {
myFixture.configureByFile(fileName());
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
try {
// TODO fix duplicate highlighting of some symbols
@@ -1 +1 @@
// INSPECTION_CLASS: com.intellij.spellchecker.inspections.SpellCheckingInspection
// INSPECTION_CLASS: com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
@@ -1,5 +1,5 @@
// "Typo: Change to..." "false"
// TOOL: com.intellij.spellchecker.inspections.SpellCheckingInspection
// TOOL: com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
// ACTION: Convert property initializer to getter
// ACTION: Convert to 'buildString' call
// ACTION: Convert to lazy property
@@ -1,5 +1,5 @@
// "Suppress 'SpellCheckingInspection' for val str" "true"
// TOOL: com.intellij.spellchecker.inspections.SpellCheckingInspection
// TOOL: com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
val str = "<caret>kjsghkjshtiurhuig"
// FUS_K2_QUICKFIX_NAME: com.intellij.codeInspection.SuppressIntentionActionFromFix
@@ -1,5 +1,5 @@
// "Suppress 'SpellCheckingInspection' for val str" "true"
// TOOL: com.intellij.spellchecker.inspections.SpellCheckingInspection
// TOOL: com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
@Suppress("SpellCheckingInspection")
val str = "kjsghkjshtiurhuig"
+1
View File
@@ -38,6 +38,7 @@ jvm_library(
"//platform/usageView",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//xml/xml-psi-api:psi",
"//plugins/yaml",
"//plugins/yaml:yaml_test_lib",
@@ -26,6 +26,7 @@
<orderEntry type="module" module-name="intellij.platform.projectModel" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.usageView" />
<orderEntry type="module" module-name="intellij.spellchecker" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.xml.psi" scope="TEST" />
<orderEntry type="module" module-name="intellij.yaml" />
<orderEntry type="module" module-name="intellij.xml.psi.impl" />
@@ -2,7 +2,7 @@ package org.intellij.plugins.markdown.spellchecking;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
import org.intellij.plugins.markdown.MarkdownTestingUtil;
@@ -11,7 +11,7 @@ public class MarkdownSpellcheckerTest extends BasePlatformTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
}
public void testAll() {
+1
View File
@@ -45,6 +45,7 @@ jvm_library(
"//plugins/properties/properties-psi-api:psi",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//platform/vcs-api:vcs",
"//plugins/groovy",
"//plugins/groovy:groovy_test_lib",
@@ -31,6 +31,7 @@
<orderEntry type="module" module-name="intellij.platform.externalSystem.tests" scope="TEST" />
<orderEntry type="module" module-name="intellij.properties.psi" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" scope="TEST" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.vcs" />
<orderEntry type="module" module-name="intellij.groovy" scope="TEST" />
<orderEntry type="module" module-name="intellij.groovy.jps" scope="TEST" />
@@ -15,15 +15,15 @@
*/
package org.jetbrains.idea.maven.dom
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.maven.testFramework.MavenDomTestCase
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import kotlinx.coroutines.runBlocking
import org.junit.Test
class MavenSpellcheckerTest : MavenDomTestCase() {
@Test
fun testSpell() = runBlocking {
fixture.enableInspections(SpellCheckingInspection::class.java)
fixture.enableInspections(GrazieSpellCheckingInspection::class.java)
createProjectPom("""
<groupId>test</groupId>
+1
View File
@@ -34,6 +34,7 @@ jvm_library(
"//platform/util/diff",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//java/openapi:java",
"//xml/impl",
"//plugins/properties:properties-backend",
@@ -25,6 +25,7 @@
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.util.diff" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" scope="TEST" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.java" scope="TEST" />
<orderEntry type="module" module-name="intellij.xml.impl" scope="TEST" />
<orderEntry type="module" module-name="intellij.properties.backend" exported="" scope="TEST" />
@@ -15,13 +15,13 @@
*/
package com.intellij.lang.properties;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
public class PropertiesSpellcheckingTest extends LightJavaCodeInsightFixtureTestCase {
public void testPropertiesSpellcheckingStrategy() {
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
myFixture.configureByText("test.properties",
"""
@@ -1,7 +1,7 @@
package com.intellij.lang.properties.rename
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.lang.properties.psi.impl.PropertyKeyImpl
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.intellij.util.ProcessingContext
@@ -21,7 +21,7 @@ class PropertyRenameToTest : BasePlatformTestCase() {
fun `test rename action is enabled and visible`() {
myFixture.configureByText("a.properties", "<TYPO descr=\"Typo: In word 'helloworld'\">hellow<caret>orld</TYPO>=value")
myFixture.enableInspections(SpellCheckingInspection())
myFixture.enableInspections(GrazieSpellCheckingInspection())
myFixture.checkHighlighting()
myFixture.getAvailableIntention("Typo: Rename to…") ?: error("RenameTo intention is not available")
}
+1
View File
@@ -55,6 +55,7 @@ jvm_library(
"//plugins/sh/plugin:plugin-main",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//platform/testFramework",
"//platform/testFramework:testFramework_test_lib",
"//platform/analysis-impl",
+1
View File
@@ -16,6 +16,7 @@
<orderEntry type="module" module-name="intellij.sh.backend" scope="TEST" />
<orderEntry type="module" module-name="intellij.sh.plugin.main" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" scope="TEST" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.analysis.impl" />
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
@@ -1,8 +1,8 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.sh.spellchecker;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
public class ShSpellcheckerTest extends BasePlatformTestCase {
@@ -14,7 +14,7 @@ public class ShSpellcheckerTest extends BasePlatformTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
myFixture.enableInspections(new SpellCheckingInspection());
myFixture.enableInspections(new GrazieSpellCheckingInspection());
}
public void testTypoInString() { doTest(); }
+1
View File
@@ -41,6 +41,7 @@ jvm_library(
"//platform/editor-ui-api:editor-ui",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//platform/projectModel-api:projectModel",
"//plugins/textmate/plugin",
"//plugins/textmate/backend",
@@ -27,6 +27,7 @@
<orderEntry type="module" module-name="intellij.platform.analysis" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.editor.ui" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" scope="TEST" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.projectModel" scope="TEST" />
<orderEntry type="module" module-name="intellij.textmate.plugin" scope="TEST" />
<orderEntry type="module" module-name="intellij.textmate.backend" scope="TEST" />
@@ -1,11 +1,11 @@
package org.jetbrains.plugins.textmate.spellchecker;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import org.jetbrains.plugins.textmate.TextMateAcceptanceTestCase;
public class TextMateSpellcheckingTest extends TextMateAcceptanceTestCase {
public void testSimple() {
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
myFixture.configureByText("test.md_hack", "some <TYPO>typooo</TYPO>");
myFixture.checkHighlighting();
}
+1
View File
@@ -73,6 +73,7 @@ jvm_library(
"//platform/testFramework:testFramework_test_lib",
"//spellchecker",
"//spellchecker:spellchecker_test_lib",
"//plugins/grazie/src:core",
"//platform/platform-impl:ide-impl",
"//json/backend",
"//json/backend/tests:tests_test_lib",
@@ -24,6 +24,7 @@
<orderEntry type="module" module-name="intellij.platform.structureView.impl" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.spellchecker" />
<orderEntry type="module" module-name="intellij.grazie.core" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
<orderEntry type="module" module-name="intellij.json.backend" />
<orderEntry type="module" module-name="intellij.json.tests" scope="TEST" />
@@ -1,7 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.yaml.highlighting
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.intellij.testFramework.fixtures.InjectionTestFixture
import org.intellij.lang.regexp.RegExpLanguage
@@ -10,7 +10,7 @@ class YAMLSpellCheckerTest : BasePlatformTestCase() {
override fun setUp() {
super.setUp()
myFixture.enableInspections(SpellCheckingInspection::class.java)
myFixture.enableInspections(GrazieSpellCheckingInspection::class.java)
}
fun testSpellChecking() {
@@ -2,10 +2,10 @@
package org.jetbrains.yaml.schema
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.openapi.application.ex.PathManagerEx
import com.intellij.openapi.fileTypes.LanguageFileType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.jetbrains.jsonSchema.JsonSchemaHighlightingTestBase
import org.jetbrains.yaml.YAMLLanguage
import java.util.function.Predicate
@@ -31,7 +31,7 @@ class YamlWithSchemaSpellcheckingTest : JsonSchemaHighlightingTestBase() {
}
fun testYamlSpellcheckingWithSchema() {
myFixture.enableInspections(SpellCheckingInspection::class.java)
myFixture.enableInspections(GrazieSpellCheckingInspection::class.java)
val schema = """
{
"properties": {
@@ -1,7 +1,7 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.LanguageLevel;
@@ -70,7 +70,7 @@ public class PySpellCheckerTest extends PyTestCase {
}
private void doTest() {
myFixture.enableInspections(SpellCheckingInspection.class);
myFixture.enableInspections(GrazieSpellCheckingInspection.class);
myFixture.configureByFile("inspections/spelling/" + getTestName(true) + ".py");
myFixture.checkHighlighting(true, false, true);
}
@@ -2,8 +2,8 @@
package com.jetbrains.python.inspections.flake8;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection;
import com.intellij.psi.PsiFile;
import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.PyComparisonWithNoneInspection;
@@ -39,7 +39,7 @@ public class Flake8InspectionSuppressorTest extends PyTestCase {
public void testSuppressingNonPythonSpecificInspections() {
doTestByText("s = 'ipaddress' # noqa",
SpellCheckingInspection.class);
GrazieSpellCheckingInspection.class);
}
public void testTopLevelCommentsIgnored() {
@@ -1,8 +1,8 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.inspections.typeignore
import com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
import com.intellij.psi.PsiFile
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.jetbrains.python.PythonFileType
import com.jetbrains.python.fixtures.PyTestCase
import com.jetbrains.python.inspections.*
@@ -53,7 +53,7 @@ class TypeIgnoreInspectionSuppressorTest : PyTestCase() {
}
fun testWithNoqa() {
myFixture.enableInspections(SpellCheckingInspection::class.java)
myFixture.enableInspections(GrazieSpellCheckingInspection::class.java)
doTestByText("""
def foo(x: str):
print(x.bar + 'ajsd') # type: ignore # noqa
@@ -28,11 +28,6 @@
<fileType name="Dictionary" implementationClass="com.intellij.spellchecker.DictionaryFileType" extensions="dic"/>
<postStartupActivity implementation="com.intellij.spellchecker.grazie.GrazieSpellCheckerEngine$SpellerLoadActivity"/>
<localInspection shortName="SpellCheckingInspection" bundle="messages.SpellCheckerBundle"
key="spellchecking.inspection.name" groupKey="proofread" enabledByDefault="true" level="TYPO"
language=""
implementationClass="com.intellij.spellchecker.inspections.SpellCheckingInspection"/>
<nameSuggestionProvider id="SpellcheckingSuggestionProvider" implementation="com.intellij.spellchecker.quickfixes.SpellcheckingNameSuggestionProvider" order="first"/>
<severitiesProvider implementation="com.intellij.spellchecker.SpellCheckerSeveritiesProvider"/>
@@ -2,145 +2,25 @@
package com.intellij.spellchecker.inspections;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.options.OptPane;
import com.intellij.lang.LanguageNamesValidation;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.refactoring.NamesValidator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.refactoring.rename.RenameUtil;
import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.spellchecker.grazie.diacritic.Diacritics;
import com.intellij.spellchecker.inspections.SpellcheckingExtension.SpellCheckingResult;
import com.intellij.spellchecker.inspections.SpellcheckingExtension.SpellingTypo;
import com.intellij.spellchecker.tokenizer.LanguageSpellchecking;
import com.intellij.psi.PsiElement;
import com.intellij.spellchecker.tokenizer.SpellcheckingStrategy;
import com.intellij.spellchecker.tokenizer.SuppressibleSpellcheckingStrategy;
import com.intellij.spellchecker.tokenizer.TokenConsumer;
import com.intellij.spellchecker.util.SpellCheckerBundle;
import com.intellij.util.Consumer;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.text.StringSearcher;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.intellij.codeInspection.options.OptPane.checkbox;
import static com.intellij.codeInspection.options.OptPane.pane;
import static com.intellij.spellchecker.tokenizer.SpellcheckingStrategy.getSpellcheckingStrategy;
public final class SpellCheckingInspection extends LocalInspectionTool implements DumbAware {
/**
* Please use {@link com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection} instead.
* The class is not marked as deprecated even though it is not used as an inspection anymore
* because {@link SpellCheckingScope} and both {@link SpellCheckingInspection#tokenize} methods are not deprecated and will not be in foreseeable future.
* They should be present here to preserve backward compatibility.
*/
public abstract class SpellCheckingInspection extends LocalInspectionTool implements DumbAware {
public static final String SPELL_CHECKING_INSPECTION_TOOL_NAME = "SpellCheckingInspection";
@Override
public SuppressQuickFix @NotNull [] getBatchSuppressActions(@Nullable PsiElement element) {
if (element != null) {
SpellcheckingStrategy strategy = getSpellcheckingStrategy(element);
if (strategy instanceof SuppressibleSpellcheckingStrategy) {
return ((SuppressibleSpellcheckingStrategy)strategy).getSuppressActions(element, getShortName());
}
}
return super.getBatchSuppressActions(element);
}
public static SpellcheckingStrategy getSpellcheckingStrategy(@NotNull PsiElement element) {
DumbService dumbService = DumbService.getInstance(element.getProject());
for (SpellcheckingStrategy strategy : LanguageSpellchecking.INSTANCE.allForLanguage(element.getLanguage())) {
if (dumbService.isUsableInCurrentContext(strategy) && strategy.isMyContext(element)) {
return strategy;
}
}
return null;
}
@Override
public boolean isSuppressedFor(@NotNull PsiElement element) {
SpellcheckingStrategy strategy = getSpellcheckingStrategy(element);
if (strategy instanceof SuppressibleSpellcheckingStrategy) {
return ((SuppressibleSpellcheckingStrategy)strategy).isSuppressedFor(element, getShortName());
}
return super.isSuppressedFor(element);
}
@Override
public @NonNls @NotNull String getShortName() {
return SPELL_CHECKING_INSPECTION_TOOL_NAME;
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return super.buildVisitor(holder, isOnTheFly);
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
boolean isOnTheFly,
@NotNull LocalInspectionToolSession session) {
if (!Registry.is("spellchecker.inspection.enabled", true) ||
InspectionProfileManager.hasTooLowSeverity(session, this) ||
InjectedLanguageManager.getInstance(holder.getProject()).isFrankensteinInjection(holder.getFile())) {
return PsiElementVisitor.EMPTY_VISITOR;
}
var scopes = buildAllowedScopes();
SpellCheckerManager manager = SpellCheckerManager.getInstance(holder.getProject());
return new PsiElementVisitor() {
@Override
public void visitWhiteSpace(@NotNull PsiWhiteSpace space) { }
@Override
public void visitElement(final @NotNull PsiElement element) {
if (holder.getResultCount() > 1000 || element.getNode() == null) return;
var strategy = getSpellcheckingStrategy(element);
if (strategy == null || !strategy.elementFitsScope(element, scopes) || isCopyrightComment(strategy, element)) return;
SpellCheckingResult result = SpellcheckingExtension.Companion.spellcheck(
element, strategy, session,
typo -> {
if (hasSameNamedReferenceInFile(typo.getWord(), element, strategy)) return;
registerProblem(typo, holder);
}
);
if (result == SpellCheckingResult.Checked) return;
tokenize(
strategy, element,
new MyTokenConsumer(manager, strategy, holder, LanguageNamesValidation.INSTANCE.forLanguage(element.getLanguage())),
scopes
);
}
};
}
private Set<SpellCheckingScope> buildAllowedScopes() {
var result = new HashSet<SpellCheckingScope>();
if (processLiterals) {
result.add(SpellCheckingScope.Literals);
}
if (processComments) {
result.add(SpellCheckingScope.Comments);
}
if (processCode) {
result.add(SpellCheckingScope.Code);
}
return result;
}
/**
* Splits element text in tokens according to spell checker strategy of given language
*
@@ -156,220 +36,15 @@ public final class SpellCheckingInspection extends LocalInspectionTool implement
tokenize(factoryByLanguage, element, consumer, allowedScopes);
}
private static void tokenize(SpellcheckingStrategy strategy,
PsiElement element,
TokenConsumer consumer,
Set<SpellCheckingScope> allowedScopes) {
public static void tokenize(SpellcheckingStrategy strategy,
PsiElement element,
TokenConsumer consumer,
Set<SpellCheckingScope> allowedScopes) {
var tokenizer = strategy.getTokenizer(element, allowedScopes);
//noinspection unchecked
tokenizer.tokenize(element, consumer);
}
private static void addBatchDescriptor(@NotNull PsiElement element,
@NotNull TextRange textRange,
@NotNull String word,
@NotNull ProblemsHolder holder) {
var fixes = SpellcheckingStrategy.getDefaultBatchFixes(element, textRange, word);
ProblemDescriptor problemDescriptor = createProblemDescriptor(element, textRange, fixes, false);
holder.registerProblem(problemDescriptor);
}
private static void addRegularDescriptor(@NotNull PsiElement element, @NotNull TextRange textRange, @NotNull ProblemsHolder holder,
boolean useRename, String wordWithTypo) {
SpellcheckingStrategy strategy = getSpellcheckingStrategy(element);
LocalQuickFix[] fixes = strategy != null
? strategy.getRegularFixes(element, textRange, useRename, wordWithTypo)
: SpellcheckingStrategy.getDefaultRegularFixes(useRename, wordWithTypo, element, textRange);
final ProblemDescriptor problemDescriptor = createProblemDescriptor(element, textRange, fixes, true);
holder.registerProblem(problemDescriptor);
}
private static ProblemDescriptor createProblemDescriptor(PsiElement element, TextRange textRange,
LocalQuickFix[] fixes,
boolean onTheFly) {
final String description = SpellCheckerBundle.message("typo.in.word.ref");
return new ProblemDescriptorBase(element, element, description, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
false, textRange, onTheFly, onTheFly);
}
@SuppressWarnings("PublicField")
public boolean processCode = true;
public boolean processLiterals = true;
public boolean processComments = true;
@Override
public @NotNull OptPane getOptionsPane() {
return pane(
checkbox("processCode", SpellCheckerBundle.message("process.code")),
checkbox("processLiterals", SpellCheckerBundle.message("process.literals")),
checkbox("processComments", SpellCheckerBundle.message("process.comments"))
);
}
private static final class MyTokenConsumer extends TokenConsumer implements Consumer<TextRange> {
private static final Pattern NON_ENGLISH_LETTERS = Pattern.compile(".*[^a-zA-Z].*");
private final Set<String> myAlreadyChecked = CollectionFactory.createSmallMemoryFootprintSet();
private final SpellCheckerManager myManager;
private final ProblemsHolder myHolder;
private final NamesValidator myNamesValidator;
private final SpellcheckingStrategy myStrategy;
private boolean myCodeLike;
private PsiElement myElement;
private String myText;
private boolean myUseRename;
private int myOffset;
MyTokenConsumer(SpellCheckerManager manager, SpellcheckingStrategy strategy, ProblemsHolder holder, NamesValidator namesValidator) {
myManager = manager;
myStrategy = strategy;
myHolder = holder;
myNamesValidator = namesValidator;
}
@Override
public void consumeToken(final PsiElement element,
final String text,
final boolean useRename,
final int offset,
TextRange rangeToCheck,
Splitter splitter) {
myElement = element;
myText = text;
myUseRename = useRename;
myOffset = offset;
myCodeLike = myStrategy.elementFitsScope(myElement, Set.of(SpellCheckingScope.Code));
splitter.split(text, rangeToCheck, this);
}
@Override
public void consume(TextRange range) {
// Tokenization of large texts can produce a lot of tokens, but we are inside RA
ProgressManager.checkCanceled();
String word = range.substring(myText);
if (!myHolder.isOnTheFly() && myAlreadyChecked.contains(word)) {
return;
}
boolean keyword = myNamesValidator.isKeyword(word, myElement.getProject());
if (keyword || !hasProblem(word, range) || hasSameNamedReferenceInFile(word, myElement, myStrategy)) {
return;
}
range = myStrategy.getTokenizer(myElement)
.getHighlightingRange(myElement, myOffset, range);
assert range.getStartOffset() >= 0;
if (!myHolder.isOnTheFly()) {
myAlreadyChecked.add(word);
}
registerProblem(myHolder, myElement, range, myUseRename, word);
}
private boolean hasCamelCaseMatch(String word, TextRange range) {
Set<String> camelCaseWords = myManager.getUserCamelCaseWords()
.stream()
.filter(camelCaseWord -> camelCaseWord.contains(word))
.collect(Collectors.toSet());
if (camelCaseWords.isEmpty()) {
return false;
}
String text = myElement.getText();
for (String camelCaseWord : camelCaseWords) {
ProgressManager.checkCanceled();
int[] indexes = new StringSearcher(camelCaseWord, false, true).findAllOccurrences(text);
for (int index : indexes) {
TextRange hitRange = new TextRange(index, index + camelCaseWord.length());
if (range.intersectsStrict(hitRange)) {
return true;
}
}
}
return false;
}
private boolean hasProblem(String word, TextRange range) {
if (!myManager.hasProblem(word)) {
return false;
}
// Check if a user has added CamelCase word to the project / app dictionary
if (hasCamelCaseMatch(word, range)) {
return false;
}
// If the word isn't "code" or contains letters outside the English alphabet,
// then diacritic check should be skipped
if (!myCodeLike || NON_ENGLISH_LETTERS.matcher(word).matches()) {
return true;
}
Project project = myElement.getProject();
return SpellCheckerManager.getInstance(project).getSuggestions(word)
.stream()
.filter(suggestion -> RenameUtil.isValidName(project, myElement, suggestion))
.noneMatch(suggestion -> Diacritics.equalsIgnoringDiacritics(word, suggestion));
}
}
private static boolean hasSameNamedReferenceInFile(String word, PsiElement element, SpellcheckingStrategy strategy) {
if (!strategy.elementFitsScope(element, Set.of(SpellCheckingScope.Comments))) {
return false;
}
PsiFile file = element.getContainingFile();
Map<String, Boolean> references = CachedValuesManager.getProjectPsiDependentCache(file, (psi) -> new ConcurrentHashMap<>());
return references.computeIfAbsent(word, key -> hasSameNamedReferencesInFile(key, file));
}
private static boolean hasSameNamedReferencesInFile(String word, PsiFile file) {
int[] occurrences = new StringSearcher(word, true, true).findAllOccurrences(file.getText());
if (occurrences.length <= 1) {
return false;
}
for (int occurrence : occurrences) {
PsiReference reference = file.findReferenceAt(occurrence);
PsiElement resolvedReference = reference != null ? reference.resolve() : null;
if (reference != null && resolvedReference != null && reference.getElement() != resolvedReference) {
return true;
}
}
return false;
}
private static boolean isCopyrightComment(SpellcheckingStrategy strategy, PsiElement psi) {
return strategy.elementFitsScope(psi, Set.of(SpellCheckingScope.Comments))
&& StringUtil.containsIgnoreCase(psi.getText(), "Copyright")
&& isAtFileStart(psi);
}
private static boolean isAtFileStart(PsiElement psi) {
PsiFile file = psi.getContainingFile();
int textStart = psi.getTextRange().getStartOffset();
return file.getViewProvider().getContents().subSequence(0, textStart).chars().noneMatch(Character::isLetterOrDigit);
}
private static void registerProblem(@NotNull SpellingTypo typo, @NotNull ProblemsHolder holder) {
registerProblem(holder, typo.getElement(), typo.getRange(), false, typo.getWord());
}
private static void registerProblem(@NotNull ProblemsHolder holder,
@NotNull PsiElement element,
@NotNull TextRange range,
boolean useRename,
String word) {
if (holder.isOnTheFly()) {
addRegularDescriptor(element, range, holder, useRename, word);
}
else {
addBatchDescriptor(element, range, word, holder);
}
}
public enum SpellCheckingScope {
Comments,
Literals,
@@ -5,8 +5,8 @@ import com.intellij.lang.Language
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.ui.CommitMessage
import com.intellij.psi.PsiElement
import com.intellij.spellchecker.inspections.SpellCheckingInspection
import com.intellij.spellchecker.inspections.SpellCheckingInspection.SpellCheckingScope
import com.intellij.spellchecker.tokenizer.SpellcheckingStrategy.getSpellcheckingStrategy
import kotlin.concurrent.atomics.AtomicBoolean
import kotlin.concurrent.atomics.ExperimentalAtomicApi
@@ -30,7 +30,7 @@ class SpellcheckerRateTracker private constructor(
companion object {
private fun determineDomain(element: PsiElement): String {
val strategy = SpellCheckingInspection.getSpellcheckingStrategy(element)
val strategy = getSpellcheckingStrategy(element)
if (CommitMessage.isCommitMessage(element)) {
return "commit"
}
@@ -9,6 +9,7 @@ import com.intellij.lang.ParserDefinition;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.impl.CustomSyntaxTableFileType;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.PossiblyDumbAware;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
@@ -150,6 +151,16 @@ public class SpellcheckingStrategy implements PossiblyDumbAware {
return getDefaultRegularFixes(useRename, typo, element, textRange);
}
public static SpellcheckingStrategy getSpellcheckingStrategy(@NotNull PsiElement element) {
DumbService dumbService = DumbService.getInstance(element.getProject());
for (SpellcheckingStrategy strategy : LanguageSpellchecking.INSTANCE.allForLanguage(element.getLanguage())) {
if (dumbService.isUsableInCurrentContext(strategy) && strategy.isMyContext(element)) {
return strategy;
}
}
return null;
}
public static LocalQuickFix[] getDefaultRegularFixes(boolean useRename,
String typo,
@NotNull PsiElement element,
@@ -5,10 +5,8 @@ import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.InspectionProfileWrapper;
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.Project;
@@ -21,9 +19,6 @@ import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.containers.CollectionFactory;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
@@ -34,51 +29,14 @@ import java.util.function.Function;
* Thread-safe.
*/
public class SpellCheckingEditorCustomization extends SimpleEditorCustomization {
private static volatile Map<String, LocalInspectionToolWrapper> SPELL_CHECK_TOOLS;
private static volatile boolean READY;
SpellCheckingEditorCustomization(boolean enabled) {
super(enabled);
}
private static @NotNull Map<String, LocalInspectionToolWrapper> getSpellCheckTools() {
Map<String, LocalInspectionToolWrapper> tools = SPELL_CHECK_TOOLS;
if (tools == null) {
tools = new HashMap<>();
// It's assumed that default spell checking inspection settings are just fine for processing all types of data.
// Please perform corresponding settings tuning if that assumption is broken in the future.
Class<LocalInspectionTool>[] inspectionClasses = (Class<LocalInspectionTool>[])new Class<?>[]{SpellCheckingInspection.class};
for (Class<LocalInspectionTool> inspectionClass : inspectionClasses) {
try {
LocalInspectionTool tool = inspectionClass.newInstance();
tools.put(tool.getShortName(), new LocalInspectionToolWrapper(tool));
}
catch (Throwable e) {
READY = false;
return Map.of();
}
}
SPELL_CHECK_TOOLS = tools;
READY = true;
}
return tools;
}
private static boolean isReady() {
if (SPELL_CHECK_TOOLS == null) {
getSpellCheckTools();
}
return READY;
}
@Override
public void customize(@NotNull EditorEx editor) {
boolean apply = isEnabled();
if (!isReady()) {
return;
}
Project project = editor.getProject();
if (project == null) {
return;
@@ -119,7 +77,7 @@ public class SpellCheckingEditorCustomization extends SimpleEditorCustomization
}
static Set<String> getSpellCheckingToolNames() {
return Collections.unmodifiableSet(getSpellCheckTools().keySet());
return Set.of(SpellCheckingInspection.SPELL_CHECKING_INSPECTION_TOOL_NAME);
}
private static class MyInspectionProfileStrategy implements Function<InspectionProfile, InspectionProfileWrapper> {
@@ -129,9 +87,6 @@ public class SpellCheckingEditorCustomization extends SimpleEditorCustomization
@Override
public @NotNull InspectionProfileWrapper apply(@NotNull InspectionProfile profile) {
if (!isReady()) {
return new InspectionProfileWrapper((InspectionProfileImpl)profile);
}
MyInspectionProfileWrapper wrapper = myWrappers.get(profile);
return wrapper == null
? ConcurrencyUtil.cacheOrGet(myWrappers, profile, new MyInspectionProfileWrapper(profile, myUseSpellCheck))
@@ -153,7 +108,7 @@ public class SpellCheckingEditorCustomization extends SimpleEditorCustomization
@Override
public boolean isToolEnabled(HighlightDisplayKey key, PsiElement element) {
return (key != null && getSpellCheckTools().containsKey(key.getShortName()) ? myUseSpellCheck : super.isToolEnabled(key, element));
return (key != null && getSpellCheckingToolNames().contains(key.getShortName()) ? myUseSpellCheck : super.isToolEnabled(key, element));
}
}
}