Skip to content

Commit faecb29

Browse files
committed
Filter duplicate registrations from servers.
1 parent 2e4a43b commit faecb29

2 files changed

Lines changed: 84 additions & 6 deletions

File tree

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/routing/ActualRoutingLanguageServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public CompletableFuture<IBaseLanguageServerExtensions> route(ISourceLocation lo
173173
@Override
174174
public void connect(LanguageClient client) {
175175
super.connect(client); // first let the super class proxy the client
176-
this.remoteClient = new MultipleClientProxy(availableClient());
176+
this.remoteClient = new MultipleClientProxy(availableClient(), getExecutor());
177177
}
178178

179179
private static String extension(ISourceLocation doc) {

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/routing/MultipleClientProxy.java

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@
2828

2929
import java.net.URI;
3030
import java.util.List;
31+
import java.util.Map;
32+
import java.util.Objects;
33+
import java.util.Set;
3134
import java.util.concurrent.CompletableFuture;
35+
import java.util.concurrent.ConcurrentHashMap;
36+
import java.util.concurrent.CopyOnWriteArraySet;
37+
import java.util.concurrent.ExecutorService;
38+
import java.util.function.Predicate;
39+
import java.util.stream.Collectors;
3240
import org.apache.logging.log4j.LogManager;
3341
import org.apache.logging.log4j.Logger;
3442
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -41,18 +49,21 @@
4149
import org.eclipse.lsp4j.ProgressParams;
4250
import org.eclipse.lsp4j.PublishDiagnosticsParams;
4351
import org.eclipse.lsp4j.Range;
52+
import org.eclipse.lsp4j.Registration;
4453
import org.eclipse.lsp4j.RegistrationParams;
4554
import org.eclipse.lsp4j.ShowDocumentParams;
4655
import org.eclipse.lsp4j.ShowDocumentResult;
4756
import org.eclipse.lsp4j.ShowMessageRequestParams;
4857
import org.eclipse.lsp4j.TextDocumentContentRefreshParams;
58+
import org.eclipse.lsp4j.Unregistration;
4959
import org.eclipse.lsp4j.UnregistrationParams;
5060
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
5161
import org.eclipse.lsp4j.WorkspaceFolder;
5262
import org.eclipse.lsp4j.services.LanguageClient;
5363
import org.rascalmpl.uri.remote.jsonrpc.ISourceLocationChanged;
5464
import org.rascalmpl.vscode.lsp.IBaseLanguageClient;
5565
import org.rascalmpl.vscode.lsp.parametric.LanguageRegistry.LanguageParameter;
66+
import org.rascalmpl.vscode.lsp.util.concurrent.CompletableFutureUtils;
5667

5768
import io.usethesource.vallang.IInteger;
5869
import io.usethesource.vallang.IString;
@@ -65,9 +76,16 @@ public class MultipleClientProxy implements IBaseLanguageClient {
6576
private static final Logger logger = LogManager.getLogger(MultipleClientProxy.class);
6677

6778
private final IBaseLanguageClient client;
79+
private final ExecutorService exec;
80+
private final CompletableFuture<Void> noop;
6881

69-
protected MultipleClientProxy(LanguageClient client) {
82+
private final Map<String, Set<Registration>> currentRegistrations = new ConcurrentHashMap<>();
83+
84+
85+
protected MultipleClientProxy(LanguageClient client, ExecutorService exec) {
7086
this.client = (IBaseLanguageClient) client;
87+
this.exec = exec;
88+
this.noop = CompletableFutureUtils.completedFuture(null, exec);
7189
}
7290

7391
@Override
@@ -194,14 +212,74 @@ public CompletableFuture<ShowDocumentResult> showDocument(ShowDocumentParams par
194212

195213
@Override
196214
public CompletableFuture<Void> registerCapability(RegistrationParams params) {
197-
// TODO Collect/maintain capabilities of all delegate servers, combine, and unregister capabilities if necessary based on that.
198-
return client.registerCapability(params);
215+
return CompletableFutureUtils
216+
.reduce(params
217+
.getRegistrations()
218+
.parallelStream()
219+
.map(this::registerCapability), exec)
220+
.thenAccept(v -> {}); // convert to Void
221+
}
222+
223+
private CompletableFuture<Void> registerCapability(Registration r) {
224+
var c = currentRegistrations.computeIfAbsent(r.getMethod(), k -> new CopyOnWriteArraySet<>());
225+
// Lock on the registrations for this method for a moment
226+
synchronized (c) {
227+
var similarRegOpt = c.stream().filter(rr -> Objects.equals(rr.getRegisterOptions(), r.getRegisterOptions())).findAny();
228+
if (similarRegOpt.isPresent()) {
229+
logger.trace("We already have a registration for {} with the same options; ignoring this one", r.getMethod());
230+
return noop;
231+
}
232+
233+
c.add(r);
234+
return client.registerCapability(new RegistrationParams(List.of(r)))
235+
.exceptionally(t -> {
236+
logger.error("Exception while registering {}", r, t);
237+
c.remove(r);
238+
return null;
239+
});
240+
}
199241
}
200242

201243
@Override
202244
public CompletableFuture<Void> unregisterCapability(UnregistrationParams params) {
203-
// TODO Collect/maintain capabilities of all delegate servers, combine, and unregister capabilities if necessary based on that.
204-
return client.unregisterCapability(params);
245+
return CompletableFutureUtils
246+
.reduce(params
247+
.getUnregisterations()
248+
.parallelStream()
249+
.map(this::unregisterCapability), exec)
250+
.thenAccept(v -> {}); // convert to Void
251+
}
252+
253+
private boolean matches(Registration r, Unregistration u) {
254+
return r.getId().equals(u.getId())
255+
&& r.getMethod().equals(u.getMethod());
256+
}
257+
258+
private Predicate<Registration> matches(Unregistration u) {
259+
return r -> matches(r, u);
260+
}
261+
262+
private CompletableFuture<Void> unregisterCapability(Unregistration u) {
263+
var c = currentRegistrations.get(u.getMethod());
264+
synchronized (c) {
265+
if (c == null) {
266+
return noop;
267+
}
268+
269+
var cs = c.stream().filter(matches(u)).collect(Collectors.toSet());
270+
if (cs.isEmpty()) {
271+
// No registrations => nothing to unregister
272+
return noop;
273+
}
274+
275+
c.removeAll(cs);
276+
return client.unregisterCapability(new UnregistrationParams(List.of(u)))
277+
.exceptionally(t -> {
278+
logger.error("Exception while unregistering {} ({})", u.getMethod(), u, t);
279+
c.addAll(cs);
280+
return null;
281+
});
282+
}
205283
}
206284

207285
@Override

0 commit comments

Comments
 (0)