From 3743202301c38f3b7577ceb0b9fdfd429262fe43 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Wed, 16 Jul 2025 13:06:13 -0700 Subject: [PATCH 1/2] fix(amazonq): suppress IDE error when editor does not actually contain text --- ...-846e7004-5426-4f6b-970f-2b9437314b98.json | 4 + .../TextDocumentServiceHandler.kt | 144 +++++++++--------- .../TextDocumentServiceHandlerTest.kt | 13 ++ 3 files changed, 92 insertions(+), 69 deletions(-) create mode 100644 .changes/next-release/bugfix-846e7004-5426-4f6b-970f-2b9437314b98.json diff --git a/.changes/next-release/bugfix-846e7004-5426-4f6b-970f-2b9437314b98.json b/.changes/next-release/bugfix-846e7004-5426-4f6b-970f-2b9437314b98.json new file mode 100644 index 00000000000..4c4a9e6db41 --- /dev/null +++ b/.changes/next-release/bugfix-846e7004-5426-4f6b-970f-2b9437314b98.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Suppress IDE error when current editor context is not valid for Amazon Q" +} \ No newline at end of file diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandler.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandler.kt index d41e7ec0a70..b49f412f987 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandler.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandler.kt @@ -33,7 +33,10 @@ import org.eclipse.lsp4j.TextDocumentContentChangeEvent import org.eclipse.lsp4j.TextDocumentIdentifier import org.eclipse.lsp4j.TextDocumentItem import org.eclipse.lsp4j.VersionedTextDocumentIdentifier +import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.tryOrNull +import software.aws.toolkits.core.utils.warn +import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLanguageServer import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ACTIVE_EDITOR_CHANGED_NOTIFICATION import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.LspEditorUtil.getCursorState @@ -98,40 +101,36 @@ class TextDocumentServiceHandler( } } - cs.launch { - AmazonQLspService.executeAsyncIfRunning(project) { languageServer -> - toUriString(file)?.let { uri -> - languageServer.textDocumentService.didOpen( - DidOpenTextDocumentParams().apply { - textDocument = TextDocumentItem().apply { - this.uri = uri - text = file.inputStream.readAllBytes().decodeToString() - languageId = file.fileType.name.lowercase() - version = file.modificationStamp.toInt() - } + trySendIfValid { languageServer -> + toUriString(file)?.let { uri -> + languageServer.textDocumentService.didOpen( + DidOpenTextDocumentParams().apply { + textDocument = TextDocumentItem().apply { + this.uri = uri + text = file.inputStream.readAllBytes().decodeToString() + languageId = file.fileType.name.lowercase() + version = file.modificationStamp.toInt() } - ) - } + } + ) } } } } override fun beforeDocumentSaving(document: Document) { - cs.launch { - AmazonQLspService.executeAsyncIfRunning(project) { languageServer -> - val file = FileDocumentManager.getInstance().getFile(document) ?: return@executeAsyncIfRunning - toUriString(file)?.let { uri -> - languageServer.textDocumentService.didSave( - DidSaveTextDocumentParams().apply { - textDocument = TextDocumentIdentifier().apply { - this.uri = uri - } - // TODO: should respect `textDocumentSync.save.includeText` server capability config - text = document.text + trySendIfValid { languageServer -> + val file = FileDocumentManager.getInstance().getFile(document) ?: return@trySendIfValid + toUriString(file)?.let { uri -> + languageServer.textDocumentService.didSave( + DidSaveTextDocumentParams().apply { + textDocument = TextDocumentIdentifier().apply { + this.uri = uri } - ) - } + // TODO: should respect `textDocumentSync.save.includeText` server capability config + text = document.text + } + ) } } } @@ -141,23 +140,21 @@ class TextDocumentServiceHandler( val document = FileDocumentManager.getInstance().getCachedDocument(event.file) ?: return@forEach handleFileOpened(event.file) - cs.launch { - AmazonQLspService.executeAsyncIfRunning(project) { languageServer -> - toUriString(event.file)?.let { uri -> - languageServer.textDocumentService.didChange( - DidChangeTextDocumentParams().apply { - textDocument = VersionedTextDocumentIdentifier().apply { - this.uri = uri - version = document.modificationStamp.toInt() - } - contentChanges = listOf( - TextDocumentContentChangeEvent().apply { - text = document.text - } - ) + trySendIfValid { languageServer -> + toUriString(event.file)?.let { uri -> + languageServer.textDocumentService.didChange( + DidChangeTextDocumentParams().apply { + textDocument = VersionedTextDocumentIdentifier().apply { + this.uri = uri + version = document.modificationStamp.toInt() } - ) - } + contentChanges = listOf( + TextDocumentContentChangeEvent().apply { + text = document.text + } + ) + } + ) } } } @@ -179,17 +176,15 @@ class TextDocumentServiceHandler( tryOrNull { FileDocumentManager.getInstance().getDocument(file)?.removeDocumentListener(listener) } file.putUserData(KEY_REAL_TIME_EDIT_LISTENER, null) - cs.launch { - AmazonQLspService.executeAsyncIfRunning(project) { languageServer -> - toUriString(file)?.let { uri -> - languageServer.textDocumentService.didClose( - DidCloseTextDocumentParams().apply { - textDocument = TextDocumentIdentifier().apply { - this.uri = uri - } + trySendIfValid { languageServer -> + toUriString(file)?.let { uri -> + languageServer.textDocumentService.didClose( + DidCloseTextDocumentParams().apply { + textDocument = TextDocumentIdentifier().apply { + this.uri = uri } - ) - } + } + ) } } } @@ -221,24 +216,22 @@ class TextDocumentServiceHandler( } private fun realTimeEdit(event: DocumentEvent) { - cs.launch { - AmazonQLspService.executeAsyncIfRunning(project) { languageServer -> - val vFile = FileDocumentManager.getInstance().getFile(event.document) ?: return@executeAsyncIfRunning - toUriString(vFile)?.let { uri -> - languageServer.textDocumentService.didChange( - DidChangeTextDocumentParams().apply { - textDocument = VersionedTextDocumentIdentifier().apply { - this.uri = uri - version = event.document.modificationStamp.toInt() - } - contentChanges = listOf( - TextDocumentContentChangeEvent().apply { - text = event.document.text - } - ) + trySendIfValid { languageServer -> + val vFile = FileDocumentManager.getInstance().getFile(event.document) ?: return@trySendIfValid + toUriString(vFile)?.let { uri -> + languageServer.textDocumentService.didChange( + DidChangeTextDocumentParams().apply { + textDocument = VersionedTextDocumentIdentifier().apply { + this.uri = uri + version = event.document.modificationStamp.toInt() } - ) - } + contentChanges = listOf( + TextDocumentContentChangeEvent().apply { + text = event.document.text + } + ) + } + ) } } // Process document changes here @@ -247,7 +240,20 @@ class TextDocumentServiceHandler( override fun dispose() { } + private fun trySendIfValid(runnable: (AmazonQLanguageServer) -> Unit) { + cs.launch { + AmazonQLspService.executeAsyncIfRunning(project) { languageServer -> + try { + runnable(languageServer) + } catch (e: Exception) { + LOG.warn { "Invalid document: $e" } + } + } + } + } + companion object { private val KEY_REAL_TIME_EDIT_LISTENER = Key.create("amazonq.textdocument.realtimeedit.listener") + private val LOG = getLogger() } } diff --git a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt index a351db5937c..d8b5dc60e1c 100644 --- a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt +++ b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt @@ -188,6 +188,19 @@ class TextDocumentServiceHandlerTest { } } + @Test + fun `fileOpened suppreses document read failure`() = runTest { + sut = TextDocumentServiceHandler(projectRule.project, this) + advanceUntilIdle() + + val document = mockk { + every { inputStream } throws RuntimeException("read failure") + } + sut.fileOpened(mockk(), document) + + verify(exactly = 0) { mockTextDocumentService.didOpen(any()) } + } + @Test fun `didClose runs on fileClosed`() = runTest { sut = TextDocumentServiceHandler(projectRule.project, this) From 0151d71d3c5562e9b7865b5e6ad2405d376b6fcb Mon Sep 17 00:00:00 2001 From: Richard Li Date: Fri, 18 Jul 2025 11:00:51 -0700 Subject: [PATCH 2/2] lint --- .../lsp/textdocument/TextDocumentServiceHandlerTest.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt index d8b5dc60e1c..bde4e2e989e 100644 --- a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt +++ b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/textdocument/TextDocumentServiceHandlerTest.kt @@ -193,10 +193,13 @@ class TextDocumentServiceHandlerTest { sut = TextDocumentServiceHandler(projectRule.project, this) advanceUntilIdle() - val document = mockk { + val file = mockk { every { inputStream } throws RuntimeException("read failure") + every { getUserData(any()) } returns null + every { putUserData(any(), any()) } returns Unit + every { isValid } returns false } - sut.fileOpened(mockk(), document) + sut.fileOpened(mockk(), file) verify(exactly = 0) { mockTextDocumentService.didOpen(any()) } }