[inline-completion] IJPL-165258: do not destroy Handler state when something unexpected happens to Session

GitOrigin-RevId: 5d2b5aed72da4710271f8bb8ce589e0238c07449
This commit is contained in:
Kirill.Karnaukhov
2024-10-28 15:23:21 +01:00
committed by intellij-monorepo-bot
parent 9ae741194f
commit 1a1f6849ac
3 changed files with 62 additions and 3 deletions

View File

@@ -218,7 +218,16 @@ abstract class InlineCompletionHandler @ApiStatus.Internal constructor(
@RequiresEdt
fun hide(context: InlineCompletionContext, finishType: FinishType = FinishType.OTHER) {
ThreadingAssertions.assertEventDispatchThread()
LOG.assertTrue(!context.isDisposed)
val currentSession = InlineCompletionSession.getOrNull(editor)
if (context !== currentSession?.context) {
LOG.error("[Inline Completion] Cannot hide a session because an invalid context is passed.")
return
}
if (context.isDisposed) {
sessionManager.removeSession()
LOG.error("[Inline Completion] Cannot hide a session because the context is already disposed.")
return
}
doHide(context, finishType)
}

View File

@@ -192,9 +192,14 @@ class InlineCompletionSession private constructor(
@RequiresEdt
internal fun remove(editor: Editor) {
getOrNull(editor)?.apply {
Disposer.dispose(this)
if (!context.isDisposed) {
Disposer.dispose(this)
LOG.trace("[Inline Completion] Remove inline completion session")
}
else {
LOG.warn("[Inline Completion] Cannot dispose session because it's already disposed.")
}
editor.putUserData(INLINE_COMPLETION_SESSION, null)
LOG.trace("Remove inline completion session")
}
}
}

View File

@@ -0,0 +1,45 @@
// 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.codeInsight.inline.completion
import com.intellij.codeInsight.inline.completion.elements.InlineCompletionElement
import com.intellij.codeInsight.inline.completion.elements.InlineCompletionGrayTextElement
import com.intellij.codeInsight.inline.completion.impl.SimpleInlineCompletionProvider
import com.intellij.codeInsight.inline.completion.session.InlineCompletionSession
import com.intellij.openapi.application.EDT
import com.intellij.openapi.fileTypes.PlainTextFileType
import com.intellij.openapi.progress.assertLogThrows
import com.intellij.openapi.util.Disposer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
internal class InlineCompletionExceptionTest : InlineCompletionTestCase() {
private fun registerSuggestion(vararg suggestion: InlineCompletionElement) {
InlineCompletionHandler.registerTestHandler(SimpleInlineCompletionProvider(suggestion.toList()), testRootDisposable)
}
@Test
fun `test manually dispose session and use completion after`() = myFixture.testInlineCompletion {
init(PlainTextFileType.INSTANCE)
registerSuggestion(InlineCompletionGrayTextElement("simple completion"))
typeChar('\n')
delay()
withContext(Dispatchers.EDT) {
val session = assertNotNull(InlineCompletionSession.getOrNull(myFixture.editor))
Disposer.dispose(session)
}
withContext(Dispatchers.EDT) {
assertLogThrows<Throwable> {
escape()
}
}
assertInlineHidden()
typeChar('\n')
delay()
assertInlineRender("simple completion")
}
}