diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 060425039..ab538df36 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -154,6 +154,7 @@ jobs: --batch-mode \ --show-version \ -Declipse.p2.mirrors=false \ + -Dtycho.p2.baseline.skip=true \ -Dmaven.test.skip=true \ clean verify diff --git a/org.eclipse.lsp4e.debug/META-INF/MANIFEST.MF b/org.eclipse.lsp4e.debug/META-INF/MANIFEST.MF index 8caf3fb2e..a6b57c107 100644 --- a/org.eclipse.lsp4e.debug/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e.debug/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Debug Adapter client for Eclipse IDE (Incubation) Bundle-SymbolicName: org.eclipse.lsp4e.debug;singleton:=true Bundle-Vendor: Eclipse LSP4E -Bundle-Version: 0.16.0.qualifier +Bundle-Version: 0.16.1.qualifier Bundle-Activator: org.eclipse.lsp4e.debug.DSPPlugin Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, diff --git a/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/debugmodel/DSPBreakpointManager.java b/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/debugmodel/DSPBreakpointManager.java index 0eeada50b..76bb1e7a4 100644 --- a/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/debugmodel/DSPBreakpointManager.java +++ b/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/debugmodel/DSPBreakpointManager.java @@ -160,6 +160,12 @@ public void breakpointChanged(IBreakpoint breakpoint, @Nullable IMarkerDelta del private void addBreakpointToMap(IBreakpoint breakpoint) { Assert.isTrue(supportsBreakpoint(breakpoint) && breakpoint instanceof ILineBreakpoint); if (breakpoint instanceof ILineBreakpoint lineBreakpoint) { + // Ensure we do not keep stale breakpoint entries for the same + // location (line/column) when attributes such as conditions change. + // This avoids sending multiple breakpoints for the same source + // location to the debug adapter. + deleteBreakpointFromMap(breakpoint); + IMarker marker = lineBreakpoint.getMarker(); IResource resource = marker.getResource(); IPath location = resource.getLocation(); diff --git a/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/presentation/DSPBreakpointDetailPane.java b/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/presentation/DSPBreakpointDetailPane.java index 036d3c33b..760c79b1e 100644 --- a/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/presentation/DSPBreakpointDetailPane.java +++ b/org.eclipse.lsp4e.debug/src/org/eclipse/lsp4e/debug/presentation/DSPBreakpointDetailPane.java @@ -199,11 +199,13 @@ public void display(final IStructuredSelection selection) { // Apply capability gating final var caps = getDebugAdapterCapabilities(); // is null if no debug session is active - final boolean condSupported = caps == null || caps.getSupportsConditionalBreakpoints(); + final boolean condSupported = caps == null + || caps.getSupportsConditionalBreakpoints() != null && caps.getSupportsConditionalBreakpoints(); enableConditionButton.setEnabled(condSupported); conditionEditor.setEnabled(condSupported); - final boolean hitSupported = caps == null || caps.getSupportsHitConditionalBreakpoints(); + final boolean hitSupported = caps == null || caps.getSupportsHitConditionalBreakpoints() != null + && caps.getSupportsHitConditionalBreakpoints(); enableHitConditionButton.setEnabled(hitSupported); hitConditionText.setEnabled(hitSupported); } else { diff --git a/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF b/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF index e6ca473f2..c70633892 100644 --- a/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF +++ b/org.eclipse.lsp4e.test/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Tests for language server bundle (Incubation) Bundle-SymbolicName: org.eclipse.lsp4e.test;singleton:=true -Bundle-Version: 0.16.0.qualifier +Bundle-Version: 0.16.1.qualifier Fragment-Host: org.eclipse.lsp4e Bundle-Vendor: Eclipse LSP4E Bundle-RequiredExecutionEnvironment: JavaSE-21 diff --git a/org.eclipse.lsp4e.test/pom.xml b/org.eclipse.lsp4e.test/pom.xml index f2dc3dbbc..ef54b04de 100644 --- a/org.eclipse.lsp4e.test/pom.xml +++ b/org.eclipse.lsp4e.test/pom.xml @@ -8,7 +8,7 @@ org.eclipse.lsp4e.test eclipse-test-plugin - 0.16.0-SNAPSHOT + 0.16.1-SNAPSHOT diff --git a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/debug/BreakpointMappingTest.java b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/debug/BreakpointMappingTest.java index 5d2a9ce6b..ed9dcdba4 100644 --- a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/debug/BreakpointMappingTest.java +++ b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/debug/BreakpointMappingTest.java @@ -113,4 +113,51 @@ void breakpoint_conditions_are_sent_to_server() throws Exception { manager.shutdown(); } } + + @Test + void changing_breakpoint_condition_replaces_existing_entry() throws Exception { + IFile file = TestUtils.createUniqueTestFile(project, "txt", "first line\nsecond line\n"); + + var bp = new DSPLineBreakpoint(file, 2); + bp.setCondition("x > 0"); + created.add(bp); + + var server = new CapturingServer(); + var manager = new DSPBreakpointManager(DebugPlugin.getDefault().getBreakpointManager(), server, null); + + try { + // Simulate initial registration of the breakpoint with the manager + manager.initialize().join(); + manager.breakpointAdded(bp); + + // Change the condition and simulate the platform reporting the change + server.calls.clear(); + bp.setCondition("x > 1"); + manager.breakpointChanged(bp, null); + + SetBreakpointsArguments matching = null; + synchronized (server.calls) { + assertTrue(!server.calls.isEmpty(), + "No setBreakpoints() calls captured after breakpoint condition change"); + String path = file.getLocation().toOSString(); + for (SetBreakpointsArguments a : server.calls) { + if (a.getSource() != null && path.equals(a.getSource().getPath())) { + matching = a; + break; + } + } + } + + assertNotNull(matching, + "No setBreakpoints() call for our file was captured after breakpoint condition change"); + SourceBreakpoint[] sent = matching.getBreakpoints(); + assertNotNull(sent); + assertEquals(1, sent.length, "Expected exactly one SourceBreakpoint after condition change"); + + SourceBreakpoint sb = sent[0]; + assertEquals("x > 1", sb.getCondition()); + } finally { + manager.shutdown(); + } + } } diff --git a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/AbstractTestWithProject.java b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/AbstractTestWithProject.java index 35e4e8641..316e8271d 100644 --- a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/AbstractTestWithProject.java +++ b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/utils/AbstractTestWithProject.java @@ -15,6 +15,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; @@ -35,8 +36,38 @@ public void setUpProject(TestInfo testInfo) throws Exception { @AfterEach public void tearDownProject() throws Exception { - if (project != null) { - project.delete(IResource.FORCE, null); + if (project != null && project.exists()) { + deleteProjectWithRetries(project, 10, 500); + } + } + + /** + * Mitigation for potential + * java.nio.file.FileSystemException: The process cannot access the file because it is being used by another process + * when deleting a project. + */ + private static void deleteProjectWithRetries(IProject project, int maxAttempts, long delayMillis) + throws CoreException { + for (int attempt = 1; attempt <= maxAttempts; attempt++) { + try { + if (!project.exists()) { + break; + } + project.close(null); + project.delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, null); + break; + } catch (CoreException ex) { + if (attempt == maxAttempts) { + throw ex; + } + try { + Thread.sleep(delayMillis); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + ex.printStackTrace(); + break; + } + } } } } diff --git a/pom.xml b/pom.xml index 89d75b898..81170b63f 100644 --- a/pom.xml +++ b/pom.xml @@ -142,6 +142,7 @@ compare-version-with-baselines + ${tycho.p2.baseline.skip} https://download.eclipse.org/lsp4e/releases/latest