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