mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[grazie] IJPL-232354 Exception when trying to fix a typo in Kotlin code
Merge-request: IJ-MR-189452 Merged-by: Ilia Permiashkin <ilia.permiashkin@jetbrains.com> GitOrigin-RevId: d0cbdb76d0b4a6e402a8c1a3a914d55d1668bdef
This commit is contained in:
committed by
intellij-monorepo-bot
parent
46cfb3792a
commit
224cbb3ef8
@@ -23,6 +23,7 @@ jvm_library(
|
||||
"//plugins/grazie/src/test:grazie-tests_test_lib",
|
||||
"//plugins/kotlin/base/test:test_test_lib",
|
||||
"//platform/core-api:core",
|
||||
"//platform/analysis-api:analysis",
|
||||
"//plugins/kotlin/test-framework:test-framework_test_lib",
|
||||
],
|
||||
runtime_deps = [":kotlin-grazie-tests"]
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<orderEntry type="module" module-name="intellij.grazie.tests" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlin.base.test" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.analysis" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.kotlin.testFramework" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
+18
@@ -54,4 +54,22 @@ class KotlinGrazieSupportTest28 : GrazieTestBase(), ExpectedPluginModeProvider {
|
||||
val content = TextExtractor.findTextAt(file, 10, TextContent.TextDomain.ALL)
|
||||
assertEquals("foo | bar", TextContentTest.unknownOffsets(content))
|
||||
}
|
||||
|
||||
fun `test meaningful single suggestion in RenameTo action`() {
|
||||
myFixture.configureByText("a.kt", """
|
||||
class A {
|
||||
// <TYPO descr="Typo: In word 'tagret'">tagret</TYPO>
|
||||
val <TYPO descr="Typo: In word 'tagret'">tag<caret>ret</TYPO> = 1
|
||||
}
|
||||
""")
|
||||
myFixture.checkHighlighting()
|
||||
val intention = myFixture.findSingleIntention("Typo: Rename to 'target'")
|
||||
myFixture.launchAction(intention)
|
||||
myFixture.checkResult("""
|
||||
class A {
|
||||
// target
|
||||
val target = 1
|
||||
}
|
||||
""")
|
||||
}
|
||||
}
|
||||
+18
@@ -48,4 +48,22 @@ class KotlinGrazieSupportTest28 : GrazieTestBase(), ExpectedPluginModeProvider {
|
||||
val content = TextExtractor.findTextAt(file, 10, TextContent.TextDomain.ALL)
|
||||
assertEquals("foo | bar", TextContentTest.unknownOffsets(content))
|
||||
}
|
||||
|
||||
fun `test meaningful single suggestion in RenameTo action`() {
|
||||
myFixture.configureByText("a.kt", """
|
||||
class A {
|
||||
// <TYPO descr="Typo: In word 'tagret'">tagret</TYPO>
|
||||
val <TYPO descr="Typo: In word 'tagret'">tag<caret>ret</TYPO> = 1
|
||||
}
|
||||
""")
|
||||
myFixture.checkHighlighting()
|
||||
val intention = myFixture.findSingleIntention("Typo: Rename to 'target'")
|
||||
myFixture.launchAction(intention)
|
||||
myFixture.checkResult("""
|
||||
class A {
|
||||
// target
|
||||
val target = 1
|
||||
}
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
package ide.language.kotlin
|
||||
|
||||
interface A {
|
||||
val <TYPO descr="Typo: In word 'typpo'">typpo</TYPO>: Int
|
||||
fun <TYPO descr="Typo: In word 'typpo'">typpo</TYPO>(<TYPO descr="Typo: In word 'typpo'">typpo</TYPO>: Int)
|
||||
}
|
||||
|
||||
class B : A {
|
||||
// typos are ignored because of `override` keyword
|
||||
override val typpo: Int = 0
|
||||
override fun typpo(typpo: Int) { }
|
||||
}
|
||||
|
||||
val variableWith<TYPO descr="Typo: In word 'Eror'">Eror</TYPO> = "error"
|
||||
|
||||
fun <TYPO descr="Typo: In word 'eror'">eror</TYPO>Function(<TYPO descr="Typo: In word 'eror'">eror</TYPO>: Int) {}
|
||||
|
||||
+26
-20
@@ -6,51 +6,57 @@ import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.spellchecker.inspections.PlainTextSplitter
|
||||
import com.intellij.spellchecker.inspections.Splitter
|
||||
import com.intellij.spellchecker.tokenizer.SpellcheckingStrategy
|
||||
import com.intellij.spellchecker.tokenizer.TokenConsumer
|
||||
import com.intellij.spellchecker.tokenizer.Tokenizer
|
||||
import com.intellij.spellchecker.tokenizer.TokenizerBase
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.psi.KtParameterList
|
||||
|
||||
internal class KotlinSpellcheckingStrategy : SpellcheckingStrategy(), DumbAware {
|
||||
private val plainTextTokenizer: Tokenizer<PsiElement> = TokenizerBase(PlainTextSplitter.getInstance())
|
||||
private val codeTokenizer: Tokenizer<PsiElement> = object : TokenizerBase<PsiElement>(PlainTextSplitter.getInstance()) {
|
||||
override fun consumeToken(element: PsiElement, consumer: TokenConsumer, splitter: Splitter) {
|
||||
consumer.consumeToken(element, true, splitter)
|
||||
}
|
||||
}
|
||||
private val emptyTokenizer: Tokenizer<PsiElement> = EMPTY_TOKENIZER
|
||||
|
||||
override fun getTokenizer(element: PsiElement): Tokenizer<out PsiElement?> {
|
||||
if (useTextLevelSpellchecking() && (element is PsiComment || element is KtLiteralStringTemplateEntry)) {
|
||||
// use [KotlinTextExtractor] and [GrazieTextLevelSpellCheckingExtension] if enabled for non-code constructs
|
||||
// use [KotlinTextExtractor] and [SpellingTextChecker] if enabled for non-code constructs
|
||||
return emptyTokenizer
|
||||
}
|
||||
return when (element) {
|
||||
is PsiComment -> super.getTokenizer(element)
|
||||
is KtParameter -> {
|
||||
val function = (element.parent as? KtParameterList)?.parent as? KtNamedFunction
|
||||
when {
|
||||
function?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true -> emptyTokenizer
|
||||
else -> super.getTokenizer(element)
|
||||
}
|
||||
}
|
||||
|
||||
is PsiNameIdentifierOwner -> {
|
||||
when {
|
||||
element is KtModifierListOwner && element.hasModifier(KtTokens.OVERRIDE_KEYWORD) -> emptyTokenizer
|
||||
else -> super.getTokenizer(element)
|
||||
}
|
||||
}
|
||||
|
||||
is KtLiteralStringTemplateEntry if !isInjectedLanguageFragment(element.parent) -> plainTextTokenizer
|
||||
|
||||
is LeafPsiElement -> {
|
||||
if (element.elementType != KtTokens.IDENTIFIER) return emptyTokenizer
|
||||
val parent = element.parent
|
||||
if (parent is KtNameReferenceExpression) return emptyTokenizer
|
||||
if (parent is KtParameter) {
|
||||
val function = (parent.parent as? KtParameterList)?.parent as? KtNamedFunction
|
||||
if (function is KtNamedFunction) return getTokenizer(function)
|
||||
}
|
||||
if (parent is KtModifierListOwner) return getTokenizer(parent)
|
||||
codeTokenizer
|
||||
}
|
||||
else -> emptyTokenizer
|
||||
}
|
||||
}
|
||||
|
||||
override fun useTextLevelSpellchecking(): Boolean {
|
||||
return Registry.`is`("spellchecker.grazie.enabled", false)
|
||||
private fun getTokenizer(parent: KtModifierListOwner): Tokenizer<PsiElement> = when {
|
||||
parent.hasModifier(KtTokens.OVERRIDE_KEYWORD) -> emptyTokenizer
|
||||
else -> codeTokenizer
|
||||
}
|
||||
|
||||
override fun useTextLevelSpellchecking(): Boolean = Registry.`is`("spellchecker.grazie.enabled", false)
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ public class RenameTo extends IntentionAndQuickFixAction implements Iconable, Ev
|
||||
|
||||
private void generateSuggestions(String name, PsiElement element) {
|
||||
if (suggestions == null) {
|
||||
TextRange range = restoreRange(name);
|
||||
TextRange range = restoreRange();
|
||||
if (range == null) return;
|
||||
this.suggestions = SpellCheckerManager.getInstance(pointer.getProject()).getSuggestions(typo)
|
||||
.stream()
|
||||
@@ -147,15 +147,13 @@ public class RenameTo extends IntentionAndQuickFixAction implements Iconable, Ev
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable TextRange restoreRange(String currentElementName) {
|
||||
private @Nullable TextRange restoreRange() {
|
||||
PsiElement element = pointer.getElement();
|
||||
Segment rangeRelativeToFile = this.rangeRelativeToFile.getRange();
|
||||
if (element == null || rangeRelativeToFile == null) return null;
|
||||
|
||||
int offset = element.getText().indexOf(currentElementName);
|
||||
return TextRange.create(rangeRelativeToFile)
|
||||
.shiftLeft(element.getTextRange().getStartOffset())
|
||||
.shiftLeft(offset);
|
||||
.shiftLeft(element.getTextRange().getStartOffset());
|
||||
}
|
||||
|
||||
private void runRenamer(PsiElement element, String suggestion) {
|
||||
|
||||
Reference in New Issue
Block a user