Skip to content

Commit 8de4581

Browse files
committed
fix: Use a Gson singleton which knows about the types from LSP4J
1 parent 400ae3c commit 8de4581

8 files changed

Lines changed: 92 additions & 19 deletions

File tree

org.eclipse.lsp4e.test/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: Tests for language server bundle (Incubation)
44
Bundle-SymbolicName: org.eclipse.lsp4e.test;singleton:=true
5-
Bundle-Version: 0.16.1.qualifier
5+
Bundle-Version: 0.16.2.qualifier
66
Fragment-Host: org.eclipse.lsp4e
77
Bundle-Vendor: Eclipse LSP4E
88
Bundle-RequiredExecutionEnvironment: JavaSE-21

org.eclipse.lsp4e.test/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
</parent>
99
<artifactId>org.eclipse.lsp4e.test</artifactId>
1010
<packaging>eclipse-test-plugin</packaging>
11-
<version>0.16.1-SNAPSHOT</version>
11+
<version>0.16.2-SNAPSHOT</version>
1212

1313
<properties>
1414
<os-jvm-flags /> <!-- for the default case -->

org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/commands/DynamicRegistrationTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@
4545
import org.junit.jupiter.api.BeforeEach;
4646
import org.junit.jupiter.api.Test;
4747

48-
import com.google.gson.Gson;
49-
5048
public class DynamicRegistrationTest extends AbstractTestWithProject {
5149

5250
private static final String WORKSPACE_EXECUTE_COMMAND = "workspace/executeCommand";
@@ -143,7 +141,7 @@ private UUID registerWatchedFiles() throws Exception {
143141
final var watcher = new org.eclipse.lsp4j.FileSystemWatcher(
144142
org.eclipse.lsp4j.jsonrpc.messages.Either.forLeft("**/*.txt"), null);
145143
options.setWatchers(List.of(watcher));
146-
registration.setRegisterOptions(new Gson().toJsonTree(options));
144+
registration.setRegisterOptions(options);
147145
client.registerCapability(new RegistrationParams(List.of(registration))).get(1, TimeUnit.SECONDS);
148146
return id;
149147
}
@@ -165,7 +163,7 @@ private UUID registerCommands(String... command) throws Exception {
165163
final var registration = new Registration();
166164
registration.setId(id.toString());
167165
registration.setMethod(WORKSPACE_EXECUTE_COMMAND);
168-
registration.setRegisterOptions(new Gson().toJsonTree(new ExecuteCommandOptions(List.of(command))));
166+
registration.setRegisterOptions(new ExecuteCommandOptions(List.of(command)));
169167
client.registerCapability(new RegistrationParams(List.of(registration))).get(1, TimeUnit.SECONDS);
170168
return id;
171169
}

org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/completion/DynamicCompletionRegistrationTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@
4141
import org.junit.jupiter.api.BeforeEach;
4242
import org.junit.jupiter.api.Test;
4343

44-
import com.google.gson.Gson;
45-
4644
/**
4745
* Verifies that dynamic registration of completion updates LSP4E server
4846
* capabilities and enables content assist proposals.
@@ -82,7 +80,7 @@ public void testDynamicCompletionRegistrationProvidesProposalsAndTriggers() thro
8280
registration.setMethod("textDocument/completion");
8381
var opts = new CompletionOptions();
8482
opts.setTriggerCharacters(List.of(".", "/", "#"));
85-
registration.setRegisterOptions(new Gson().toJsonTree(opts));
83+
registration.setRegisterOptions(opts);
8684
client.registerCapability(new RegistrationParams(List.of(registration))).get(2, TimeUnit.SECONDS);
8785

8886
// Compute proposals (manual invocation). Should return the mock item.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation.
3+
* This program and the accompanying materials are made
4+
* available under the terms of the Eclipse Public License 2.0
5+
* which is available at https://www.eclipse.org/legal/epl-2.0/
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* See git history
11+
*******************************************************************************/
12+
package org.eclipse.lsp4e.test.internal;
13+
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
16+
import org.eclipse.lsp4e.internal.JsonUtil;
17+
import org.eclipse.lsp4j.FileSystemWatcher;
18+
import org.eclipse.lsp4j.WatchKind;
19+
import org.junit.jupiter.api.Test;
20+
21+
public class JsonUtilTest {
22+
23+
@Test
24+
void testRoundtrip() throws Exception {
25+
// Setup an object which uses Either internally.
26+
// This can only be properly de/serialized with Gson instance which knows about LSP4J types.
27+
var original = new FileSystemWatcher();
28+
original.setGlobPattern("**");
29+
original.setKind(WatchKind.Create | WatchKind.Change | WatchKind.Delete);
30+
31+
String json = JsonUtil.getLSP4J_GSON().toJson(original);
32+
assertEquals("""
33+
{"globPattern":"**","kind":7}""", json);
34+
35+
FileSystemWatcher copy = JsonUtil.getLSP4J_GSON().fromJson(json, FileSystemWatcher.class);
36+
assertEquals(original, copy);
37+
}
38+
39+
}

org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*******************************************************************************/
1919
package org.eclipse.lsp4e;
2020

21-
import static org.eclipse.lsp4e.internal.NullSafetyHelper.*;
21+
import static org.eclipse.lsp4e.internal.NullSafetyHelper.castNonNull;
2222

2323
import java.io.BufferedReader;
2424
import java.io.File;
@@ -80,6 +80,7 @@
8080
import org.eclipse.lsp4e.internal.ArrayUtil;
8181
import org.eclipse.lsp4e.internal.CancellationUtil;
8282
import org.eclipse.lsp4e.internal.FileBufferListenerAdapter;
83+
import org.eclipse.lsp4e.internal.JsonUtil;
8384
import org.eclipse.lsp4e.internal.SupportedFeatures;
8485
import org.eclipse.lsp4e.internal.files.FileSystemWatcherManager;
8586
import org.eclipse.lsp4e.server.StreamConnectionProvider;
@@ -129,7 +130,6 @@
129130

130131
import com.google.common.base.Functions;
131132
import com.google.common.util.concurrent.ThreadFactoryBuilder;
132-
import com.google.gson.Gson;
133133
import com.google.gson.JsonObject;
134134

135135
public class LanguageServerWrapper {
@@ -1173,7 +1173,7 @@ public void registerCapability(RegistrationParams params) {
11731173
break;
11741174
case "workspace/executeCommand": //$NON-NLS-1$
11751175
try {
1176-
ExecuteCommandOptions executeCommandOptions = castNonNull(new Gson().fromJson((JsonObject) reg.getRegisterOptions(),
1176+
ExecuteCommandOptions executeCommandOptions = castNonNull(JsonUtil.getLSP4J_GSON().fromJson((JsonObject) reg.getRegisterOptions(),
11771177
ExecuteCommandOptions.class));
11781178
List<String> newCommands = executeCommandOptions.getCommands();
11791179
if (!newCommands.isEmpty()) {
@@ -1212,7 +1212,7 @@ public void registerCapability(RegistrationParams params) {
12121212
case "textDocument/completion": { //$NON-NLS-1$
12131213
CompletionOptions previous = serverCapabilities.getCompletionProvider();
12141214
try {
1215-
final var completionOpts = new Gson().fromJson((JsonObject) reg.getRegisterOptions(),
1215+
final var completionOpts = JsonUtil.getLSP4J_GSON().fromJson((JsonObject) reg.getRegisterOptions(),
12161216
CompletionOptions.class);
12171217
serverCapabilities.setCompletionProvider(completionOpts);
12181218
addRegistration(reg, () -> serverCapabilities.setCompletionProvider(previous));
@@ -1255,8 +1255,9 @@ public void registerCapability(RegistrationParams params) {
12551255
return null;
12561256
if (registerOptions instanceof DidChangeWatchedFilesRegistrationOptions direct)
12571257
return direct;
1258-
if (registerOptions instanceof JsonObject jsonObject)
1259-
return new Gson().fromJson(jsonObject, DidChangeWatchedFilesRegistrationOptions.class);
1258+
if (registerOptions instanceof JsonObject jsonObject) {
1259+
return JsonUtil.getLSP4J_GSON().fromJson(jsonObject, DidChangeWatchedFilesRegistrationOptions.class);
1260+
}
12601261
return null;
12611262
}
12621263

org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/CommandExecutor.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
*******************************************************************************/
99
package org.eclipse.lsp4e.command;
1010

11-
import static org.eclipse.lsp4e.command.LSPCommandHandler.*;
11+
import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_COMMAND_PARAMETER_ID;
12+
import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_PATH_PARAMETER_ID;
1213

1314
import java.net.URI;
1415
import java.util.ArrayList;
@@ -33,6 +34,7 @@
3334
import org.eclipse.lsp4e.LSPEclipseUtils;
3435
import org.eclipse.lsp4e.LanguageServerPlugin;
3536
import org.eclipse.lsp4e.command.internal.CommandEventParameter;
37+
import org.eclipse.lsp4e.internal.JsonUtil;
3638
import org.eclipse.lsp4j.Command;
3739
import org.eclipse.lsp4j.TextEdit;
3840
import org.eclipse.lsp4j.WorkspaceEdit;
@@ -192,7 +194,7 @@ private static WorkspaceEdit createWorkspaceEdit(List<Object> commandArguments,
192194
} else if (arg instanceof TextEdit textEdit) {
193195
currentEntry.value.add(textEdit);
194196
} else if (arg instanceof Map) {
195-
final var gson = new Gson(); // TODO? retrieve the GSon used by LS
197+
Gson gson = JsonUtil.getLSP4J_GSON();
196198
TextEdit edit = gson.fromJson(gson.toJson(arg), TextEdit.class);
197199
if (edit != null) {
198200
currentEntry.value.add(edit);
@@ -210,15 +212,15 @@ private static WorkspaceEdit createWorkspaceEdit(List<Object> commandArguments,
210212
}
211213
}
212214
} else if (arg instanceof JsonArray jsonArray) {
213-
final var gson = new Gson(); // TODO? retrieve the GSon used by LS
215+
Gson gson = JsonUtil.getLSP4J_GSON();
214216
jsonArray.forEach(elt -> {
215217
TextEdit edit = gson.fromJson(gson.toJson(elt), TextEdit.class);
216218
if (edit != null) {
217219
currentEntry.value.add(edit);
218220
}
219221
});
220222
} else if (arg instanceof JsonObject jsonObject) {
221-
final var gson = new Gson(); // TODO? retrieve the GSon used by LS
223+
Gson gson = JsonUtil.getLSP4J_GSON();
222224
WorkspaceEdit wEdit = gson.fromJson(jsonObject, WorkspaceEdit.class);
223225
if (wEdit != null) {
224226
Map<String, List<TextEdit>> entries = wEdit.getChanges();
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation.
3+
* This program and the accompanying materials are made
4+
* available under the terms of the Eclipse Public License 2.0
5+
* which is available at https://www.eclipse.org/legal/epl-2.0/
6+
*
7+
* SPDX-License-Identifier: EPL-2.0
8+
*
9+
* Contributors:
10+
* See git history
11+
*******************************************************************************/
12+
package org.eclipse.lsp4e.internal;
13+
14+
import java.util.Map;
15+
import java.util.Objects;
16+
17+
import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler;
18+
19+
import com.google.gson.Gson;
20+
21+
/**
22+
* Provides a {@link Gson} instance which can properly serialize and deserialize LSP4J JSON-RPC objects
23+
*/
24+
public class JsonUtil {
25+
26+
private static final Gson LSP4J_GSON = Objects.requireNonNull(new MessageJsonHandler(Map.of()).getGson());
27+
28+
/**
29+
* Obtain a singleton Gson instance to work with LSP4J JSON objects.
30+
*/
31+
public static Gson getLSP4J_GSON() {
32+
return LSP4J_GSON;
33+
}
34+
35+
}

0 commit comments

Comments
 (0)