From 6b928611afc42959e430f6fbbb552ae269c7d050 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 08:42:24 +0000 Subject: [PATCH 1/4] Initial plan From e2c80cafe07ec52793a72ea850ee333a14238ac6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 08:57:42 +0000 Subject: [PATCH 2/4] Add logging and extract timeout constant for document close termination Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../BSLTextDocumentService.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java index 689c624dc25..d8c4594460b 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java @@ -140,6 +140,7 @@ public class BSLTextDocumentService implements TextDocumentService, ProtocolExtension { private static final long AWAIT_CLOSE = 30; + private static final long AWAIT_FORCE_TERMINATION = 1; private final ServerContext context; private final LanguageServerConfiguration configuration; @@ -532,15 +533,31 @@ public void didClose(DidCloseTextDocumentParams params) { docExecutor.shutdownNow(); // Must wait for worker thread to finish even after shutdownNow, // because finally block in worker may still be executing flushPendingChanges - docExecutor.awaitTermination(1, TimeUnit.SECONDS); + boolean terminated = docExecutor.awaitTermination(AWAIT_FORCE_TERMINATION, TimeUnit.SECONDS); + if (!terminated) { + LOGGER.warn( + "Document executor for URI {} did not terminate within the additional timeout after shutdownNow()", + uri + ); + } } } catch (InterruptedException e) { docExecutor.shutdownNow(); // Wait briefly for worker to finish after interrupt try { - docExecutor.awaitTermination(1, TimeUnit.SECONDS); + boolean terminated = docExecutor.awaitTermination(AWAIT_FORCE_TERMINATION, TimeUnit.SECONDS); + if (!terminated) { + LOGGER.warn( + "Document executor for URI {} did not terminate within {} second after interrupt during document close", + uri, + AWAIT_FORCE_TERMINATION + ); + } } catch (InterruptedException ignored) { - // Already interrupted, just restore flag + LOGGER.warn( + "Interrupted again while waiting for document executor for URI {} to terminate after shutdownNow", + uri + ); } Thread.currentThread().interrupt(); } From fb345fbd4206683672c63de3d515e08c66ae6cbc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 09:02:59 +0000 Subject: [PATCH 3/4] Add test coverage for document close race conditions Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../BSLTextDocumentServiceTest.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentServiceTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentServiceTest.java index 82377608092..c43d819523e 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentServiceTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentServiceTest.java @@ -186,6 +186,93 @@ void didClose() { textDocumentService.didClose(params); } + @Test + void didCloseWithPendingChanges() throws IOException { + // given - open a document and make changes + var textDocumentItem = getTextDocumentItem(); + var didOpenParams = new DidOpenTextDocumentParams(textDocumentItem); + textDocumentService.didOpen(didOpenParams); + + var documentContext = serverContext.getDocumentUnsafe(textDocumentItem.getUri()); + assertThat(documentContext).isNotNull(); + assertThat(serverContext.isDocumentOpened(documentContext)).isTrue(); + + // when - submit multiple changes rapidly and then close immediately + var params = new DidChangeTextDocumentParams(); + var uri = textDocumentItem.getUri(); + + for (int i = 0; i < 5; i++) { + params.setTextDocument(new VersionedTextDocumentIdentifier(uri, 2 + i)); + var range = Ranges.create(0, 0, 0, 0); + var changeEvent = new TextDocumentContentChangeEvent(range, "// Change " + i + "\n"); + List contentChanges = new ArrayList<>(); + contentChanges.add(changeEvent); + params.setContentChanges(contentChanges); + textDocumentService.didChange(params); + } + + // then - close should wait for pending changes to complete + var closeParams = new DidCloseTextDocumentParams(); + closeParams.setTextDocument(new TextDocumentIdentifier(uri)); + textDocumentService.didClose(closeParams); + + // verify the document is closed + assertThat(serverContext.isDocumentOpened(documentContext)).isFalse(); + } + + @Test + void didCloseDuringActiveChange() throws IOException { + // given - open a document + var textDocumentItem = getTextDocumentItem(); + var didOpenParams = new DidOpenTextDocumentParams(textDocumentItem); + textDocumentService.didOpen(didOpenParams); + + var documentContext = serverContext.getDocumentUnsafe(textDocumentItem.getUri()); + assertThat(documentContext).isNotNull(); + assertThat(serverContext.isDocumentOpened(documentContext)).isTrue(); + + // when - submit a change + var params = new DidChangeTextDocumentParams(); + var uri = textDocumentItem.getUri(); + params.setTextDocument(new VersionedTextDocumentIdentifier(uri, 2)); + var range = Ranges.create(0, 0, 0, 0); + var changeEvent = new TextDocumentContentChangeEvent(range, "// New content\n"); + List contentChanges = new ArrayList<>(); + contentChanges.add(changeEvent); + params.setContentChanges(contentChanges); + textDocumentService.didChange(params); + + // then - close immediately while change may still be processing + var closeParams = new DidCloseTextDocumentParams(); + closeParams.setTextDocument(new TextDocumentIdentifier(uri)); + textDocumentService.didClose(closeParams); + + // verify the document is closed + assertThat(serverContext.isDocumentOpened(documentContext)).isFalse(); + } + + @Test + void didCloseAwaitTerminationCompletes() throws IOException { + // given - open a document + var textDocumentItem = getTextDocumentItem(); + var didOpenParams = new DidOpenTextDocumentParams(textDocumentItem); + textDocumentService.didOpen(didOpenParams); + + var uri = textDocumentItem.getUri(); + var documentContext = serverContext.getDocumentUnsafe(uri); + assertThat(documentContext).isNotNull(); + assertThat(serverContext.isDocumentOpened(documentContext)).isTrue(); + + // when - close the document (which should wait for executor to terminate) + var closeParams = new DidCloseTextDocumentParams(); + closeParams.setTextDocument(new TextDocumentIdentifier(uri)); + textDocumentService.didClose(closeParams); + + // then - verify the document is properly closed + // The close should complete successfully even if executor needs time to terminate + assertThat(serverContext.isDocumentOpened(documentContext)).isFalse(); + } + @Test void didSave() { DidSaveTextDocumentParams params = new DidSaveTextDocumentParams(); From ff642f42ac7626ed5ef48a5611fe7cedb25fe649 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 09:04:41 +0000 Subject: [PATCH 4/4] Fix grammar in log message: 'second' -> 'seconds' Co-authored-by: nixel2007 <1132840+nixel2007@users.noreply.github.com> --- .../_1c_syntax/bsl/languageserver/BSLTextDocumentService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java index d8c4594460b..2e7d32a62ca 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java @@ -548,7 +548,7 @@ public void didClose(DidCloseTextDocumentParams params) { boolean terminated = docExecutor.awaitTermination(AWAIT_FORCE_TERMINATION, TimeUnit.SECONDS); if (!terminated) { LOGGER.warn( - "Document executor for URI {} did not terminate within {} second after interrupt during document close", + "Document executor for URI {} did not terminate within {} seconds after interrupt during document close", uri, AWAIT_FORCE_TERMINATION );