mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[grazie] IJPL-200544 Migrate SpellCheckingInspection to grazie module
GitOrigin-RevId: 635bb5187f9652ea920e373fdec86f0f72831b68
This commit is contained in:
committed by
intellij-monorepo-bot
parent
602482036d
commit
15ee76e949
@@ -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, """
|
||||
{
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
+2
-2
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
+2
-2
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
+346
@@ -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
-2
@@ -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()};
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
-3
@@ -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")));
|
||||
|
||||
@@ -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" />
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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
@@ -1 +1 @@
|
||||
// INSPECTION_CLASS: com.intellij.spellchecker.inspections.SpellCheckingInspection
|
||||
// INSPECTION_CLASS: com.intellij.grazie.spellcheck.GrazieSpellCheckingInspection
|
||||
|
||||
+1
-1
@@ -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
-1
@@ -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
-1
@@ -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"
|
||||
|
||||
@@ -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
-2
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
+2
-2
@@ -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",
|
||||
"""
|
||||
|
||||
+2
-2
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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" />
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
-2
@@ -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() {
|
||||
|
||||
+2
-2
@@ -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"/>
|
||||
|
||||
|
||||
+13
-338
@@ -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,
|
||||
|
||||
+2
-47
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user