Skip to content

Commit 48ed5ef

Browse files
committed
Refactor ProgressService
1 parent 64f22c2 commit 48ed5ef

File tree

10 files changed

+667
-108
lines changed

10 files changed

+667
-108
lines changed

headless-services/commons/commons-language-server/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@
3939
<artifactId>spring-boot-configuration-processor</artifactId>
4040
<optional>true</optional>
4141
</dependency>
42+
43+
<!-- Test dependencies -->
44+
<dependency>
45+
<groupId>org.mockito</groupId>
46+
<artifactId>mockito-core</artifactId>
47+
<scope>test</scope>
48+
</dependency>
4249
</dependencies>
4350

4451
</project>

headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/AbstractProgressTask.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,30 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.commons.languageserver;
1212

13+
import org.eclipse.lsp4j.WorkDoneProgressEnd;
14+
1315
/**
14-
* Writes progress messages to the same progress task.
16+
* Base class for progress tasks that report progress to the LSP client.
1517
*
16-
* This handler can be used for long-running progress that requires message updates to the same task.
18+
* <p>This handler can be used for long-running progress that requires message updates to the same task.</p>
1719
*
1820
*/
1921
public abstract class AbstractProgressTask {
2022

2123
private static long progress_counter = 0;
2224

2325
protected final String taskId;
24-
protected final ProgressService service;
26+
protected final ProgressClient client;
2527

2628

27-
public AbstractProgressTask(String taskId, ProgressService service) {
29+
public AbstractProgressTask(String taskId, ProgressClient client) {
2830
this.taskId = taskId + "-" + (progress_counter++);
29-
this.service = service;
31+
this.client = client;
3032
}
3133

3234
public void done() {
33-
this.service.progressDone(taskId);
35+
WorkDoneProgressEnd endReport = new WorkDoneProgressEnd();
36+
this.client.end(taskId, endReport);
3437
}
3538

3639
}

headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/IndefiniteProgressTask.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
public class IndefiniteProgressTask extends AbstractProgressTask {
1717

18-
public IndefiniteProgressTask(String taskId, ProgressService service, String title, String message) {
19-
super(taskId, service);
18+
public IndefiniteProgressTask(String taskId, ProgressClient client, String title, String message) {
19+
super(taskId, client);
2020
progressBegin(title, message);
2121
}
2222

@@ -27,13 +27,13 @@ private void progressBegin(String title, String message) {
2727
if (message != null && !message.isEmpty()) {
2828
report.setMessage(message);
2929
}
30-
service.progressBegin(taskId, report);
30+
client.begin(taskId, report);
3131
}
3232

3333
public void progressEvent(String statusMsg) {
3434
WorkDoneProgressReport report = new WorkDoneProgressReport();
3535
report.setMessage(statusMsg);
36-
service.progressEvent(taskId, report);
36+
client.report(taskId, report);
3737
}
3838

3939

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
12+
package org.springframework.ide.vscode.commons.languageserver;
13+
14+
import java.util.concurrent.ConcurrentHashMap;
15+
16+
import org.eclipse.lsp4j.ProgressParams;
17+
import org.eclipse.lsp4j.WorkDoneProgressBegin;
18+
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
19+
import org.eclipse.lsp4j.WorkDoneProgressEnd;
20+
import org.eclipse.lsp4j.WorkDoneProgressReport;
21+
import org.eclipse.lsp4j.jsonrpc.messages.Either;
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
24+
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
25+
26+
/**
27+
* Package-private implementation of {@link ProgressClient} that communicates with an LSP client
28+
* using the Language Server Protocol progress notifications.
29+
*
30+
* <p>This class manages the lifecycle of progress tokens, ensuring proper
31+
* creation and cleanup of progress indicators.</p>
32+
*
33+
* <p>This class is package-private as it's an internal implementation detail.
34+
* Users should only interact with {@link ProgressService} which creates instances of this class.</p>
35+
*/
36+
class LspProgressClient implements ProgressClient {
37+
38+
private static final Logger log = LoggerFactory.getLogger(LspProgressClient.class);
39+
40+
private final STS4LanguageClient client;
41+
private final ConcurrentHashMap<String, Boolean> activeTaskIDs = new ConcurrentHashMap<>();
42+
43+
/**
44+
* Package-private constructor. Creates a new LspProgressClient with the specified LSP client.
45+
*
46+
* @param client the LSP client to use for sending notifications
47+
* @throws IllegalArgumentException if client is null
48+
*/
49+
LspProgressClient(STS4LanguageClient client) {
50+
if (client == null) {
51+
throw new IllegalArgumentException("STS4LanguageClient cannot be null");
52+
}
53+
this.client = client;
54+
}
55+
56+
@Override
57+
public void begin(String taskId, WorkDoneProgressBegin report) {
58+
if (client == null) {
59+
return;
60+
}
61+
62+
boolean isNew = activeTaskIDs.put(taskId, true) == null;
63+
if (!isNew) {
64+
log.error("Progress for task id '{}' already exists", taskId);
65+
}
66+
67+
// First create the progress token
68+
WorkDoneProgressCreateParams params = new WorkDoneProgressCreateParams();
69+
params.setToken(taskId);
70+
client.createProgress(params).thenAcceptAsync((p) -> {
71+
// Then send the begin notification
72+
ProgressParams progressParams = new ProgressParams();
73+
progressParams.setToken(taskId);
74+
progressParams.setValue(Either.forLeft(report));
75+
client.notifyProgress(progressParams);
76+
});
77+
}
78+
79+
@Override
80+
public void report(String taskId, WorkDoneProgressReport report) {
81+
if (client == null) {
82+
return;
83+
}
84+
85+
if (!activeTaskIDs.containsKey(taskId)) {
86+
log.error("Progress for task id '{}' does NOT exist!", taskId);
87+
return;
88+
}
89+
90+
ProgressParams progressParams = new ProgressParams();
91+
progressParams.setToken(taskId);
92+
progressParams.setValue(Either.forLeft(report));
93+
client.notifyProgress(progressParams);
94+
}
95+
96+
@Override
97+
public void end(String taskId, WorkDoneProgressEnd report) {
98+
if (client == null || activeTaskIDs.remove(taskId) == null) {
99+
return;
100+
}
101+
102+
ProgressParams progressParams = new ProgressParams();
103+
progressParams.setToken(taskId);
104+
progressParams.setValue(Either.forLeft(report));
105+
client.notifyProgress(progressParams);
106+
}
107+
108+
}
109+

headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/PercentageProgressTask.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public class PercentageProgressTask extends AbstractProgressTask {
2828

2929
private int currentPercentage;
3030

31-
public PercentageProgressTask(String taskId, ProgressService service, int total, String title) {
32-
super(taskId, service);
31+
public PercentageProgressTask(String taskId, ProgressClient client, int total, String title) {
32+
super(taskId, client);
3333
this.total = total;
3434
this.current = 0;
3535
begin(title);
@@ -40,7 +40,7 @@ private void begin(String title) {
4040
progressBegin.setPercentage(0);
4141
progressBegin.setCancellable(false);
4242
progressBegin.setTitle(title);
43-
service.progressBegin(taskId, progressBegin);
43+
client.begin(taskId, progressBegin);
4444
}
4545

4646
public int getTotal() {
@@ -64,7 +64,7 @@ private void reportPercent(int percent) {
6464
currentPercentage = percent;
6565
WorkDoneProgressReport r = new WorkDoneProgressReport();
6666
r.setPercentage(percent);
67-
service.progressEvent(taskId, r);
67+
client.report(taskId, r);
6868
}
6969
}
7070

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
12+
package org.springframework.ide.vscode.commons.languageserver;
13+
14+
import org.eclipse.lsp4j.WorkDoneProgressBegin;
15+
import org.eclipse.lsp4j.WorkDoneProgressEnd;
16+
import org.eclipse.lsp4j.WorkDoneProgressReport;
17+
18+
/**
19+
* Low-level interface responsible for sending progress notifications to the LSP client.
20+
* Implementations handle the actual protocol-level communication.
21+
*/
22+
public interface ProgressClient {
23+
24+
/**
25+
* No-op implementation that discards all progress events.
26+
* Useful for testing or when progress reporting is disabled.
27+
*/
28+
public static final ProgressClient NO_PROGRESS = new ProgressClient() {
29+
30+
@Override
31+
public void begin(String taskId, WorkDoneProgressBegin report) {
32+
}
33+
34+
@Override
35+
public void report(String taskId, WorkDoneProgressReport report) {
36+
}
37+
38+
@Override
39+
public void end(String taskId, WorkDoneProgressEnd report) {
40+
}
41+
42+
};
43+
44+
/**
45+
* Sends a begin progress notification to the LSP client.
46+
*
47+
* @param taskId unique identifier for the progress task
48+
* @param report the initial progress report containing title, message, and optional percentage
49+
*/
50+
void begin(String taskId, WorkDoneProgressBegin report);
51+
52+
/**
53+
* Sends a progress update notification to the LSP client.
54+
* Each event updates the message shown to the user, replacing the previous one.
55+
* Multiple progress indicators may be shown simultaneously if they have different taskIds.
56+
*
57+
* @param taskId unique identifier for the progress task
58+
* @param report the progress update containing message and optional percentage
59+
*/
60+
void report(String taskId, WorkDoneProgressReport report);
61+
62+
/**
63+
* Sends an end progress notification to the LSP client.
64+
*
65+
* @param taskId unique identifier for the progress task
66+
* @param report the final progress report containing an optional message
67+
*/
68+
void end(String taskId, WorkDoneProgressEnd report);
69+
70+
}
71+

0 commit comments

Comments
 (0)