Skip to content

Commit ecdf107

Browse files
committed
Migrate Vue language server to 3.x
Signed-off-by: Dawid Pakuła <[email protected]>
1 parent dd90024 commit ecdf107

File tree

10 files changed

+202
-43
lines changed

10 files changed

+202
-43
lines changed

org.eclipse.wildwebdeveloper/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
"astro-vscode" : "2.15.4",
55
"firefox-debugadapter": "2.15.0",
66
"typescript": "5.9.2",
7-
"typescript-language-server": "4.3.4",
7+
"typescript-language-server": "4.4.0",
88
"typescript-lit-html-plugin": "0.9.0",
99
"typescript-plugin-css-modules": "5.2.0",
1010
"yaml-language-server": "1.18.0",
1111
"vscode-css-languageservice": "6.3.7",
1212
"vscode-html-languageservice": "5.5.1",
1313
"vscode-json-languageservice": "5.6.1",
14-
"@vue/language-server" : "2.2.10",
15-
"@vue/typescript-plugin" : "2.2.10",
14+
"@vue/language-server" : "3.0.7",
15+
"@vue/typescript-plugin" : "3.0.7",
1616
"fsevents" : "2.3.3",
1717
"vscode-css-languageserver": "file:target/vscode-css-languageserver-1.0.0.tgz",
1818
"vscode-html-languageserver": "file:target/vscode-html-languageserver-1.0.0.tgz",

org.eclipse.wildwebdeveloper/plugin.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@
377377
<server
378378
class="org.eclipse.wildwebdeveloper.jsts.JSTSLanguageServer"
379379
clientImpl="org.eclipse.wildwebdeveloper.jsts.JSTSLanguageClientImpl"
380+
serverInterface="org.eclipse.wildwebdeveloper.jsts.JSTSLanguageServerAPI"
380381
id="org.eclipse.wildwebdeveloper.jsts"
381382
label="JavaScript-TypeScript Language Server">
382383
</server>
@@ -568,7 +569,6 @@
568569
clientImpl="org.eclipse.wildwebdeveloper.vue.VueClientImpl"
569570
serverInterface="org.eclipse.wildwebdeveloper.vue.VueLanguageServerAPI"
570571
id="org.eclipse.wildwebdeveloper.vue"
571-
singleton="true"
572572
label="VUE Language Server"/>
573573
<contentTypeMapping contentType="org.eclipse.wildwebdeveloper.vue" languageId="vue" id="org.eclipse.wildwebdeveloper.vue"/>
574574
</extension>

org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/jsts/JSTSLanguageServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public Object getInitializationOptions(URI rootUri) {
7777
// plugins.add(new TypeScriptPlugin("@angular/language-service"));
7878
plugins.add(new TypeScriptPlugin("typescript-plugin-css-modules"));
7979
plugins.add(new TypeScriptPlugin("typescript-lit-html-plugin"));
80-
plugins.add(new TypeScriptPlugin("@vue/typescript-plugin", new String[] {"vue"}));
80+
plugins.add(new TypeScriptPlugin("@vue/typescript-plugin", "@vue/language-server", new String[] {"vue"}));
8181
options.put("plugins", plugins.stream().map(TypeScriptPlugin::toMap).toArray());
8282

8383
// If the tsserver path is not explicitly specified, tsserver will use the local
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*******************************************************************************
2+
* Copyright (c) Dawid Pakuła and others.
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+
* Dawid Pakuła <[email protected]> - initial implementation
11+
*******************************************************************************/
12+
package org.eclipse.wildwebdeveloper.jsts;
13+
14+
import org.eclipse.lsp4j.services.LanguageServer;
15+
16+
public interface JSTSLanguageServerAPI extends LanguageServer {
17+
18+
public final static String TS_REQUEST_COMMAND = "typescript.tsserverRequest";
19+
}

org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/jsts/TypeScriptPlugin.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ public TypeScriptPlugin(String name) throws IOException {
3030
}
3131

3232
public TypeScriptPlugin(String name, String[] languages) throws IOException {
33+
this(name, name, null);
34+
}
35+
36+
public TypeScriptPlugin(String name, String locationName, String[] languages) throws IOException {
3337
pluginName = name;
34-
URL fileURL = FileLocator.toFileURL(getClass().getResource("/node_modules/" + name));
38+
URL fileURL = FileLocator.toFileURL(getClass().getResource("/node_modules/" + locationName));
3539
pluginProbeLocation = new File(fileURL.getPath()).getAbsolutePath();
3640
pluginLanguages = languages;
3741
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Dawid Pakuła and others.
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+
* Dawid Pakuła <[email protected]> - initial implementation
11+
*******************************************************************************/
12+
package org.eclipse.wildwebdeveloper.jsts.request;
13+
14+
public class ExecuteInfo {
15+
16+
private int executionTarget = 0;
17+
18+
private boolean expectsResult = true;
19+
20+
private boolean isAsync = false;
21+
22+
private boolean lowPriority = true;
23+
24+
public int getExecutionTarget() {
25+
return executionTarget;
26+
}
27+
28+
public void setExecutionTarget(int executionTarget) {
29+
this.executionTarget = executionTarget;
30+
}
31+
32+
public boolean isExpectsResult() {
33+
return expectsResult;
34+
}
35+
36+
public void setExpectsResult(boolean expectsResult) {
37+
this.expectsResult = expectsResult;
38+
}
39+
40+
public boolean isAsync() {
41+
return isAsync;
42+
}
43+
44+
public void setAsync(boolean isAsync) {
45+
this.isAsync = isAsync;
46+
}
47+
48+
public boolean isLowPriority() {
49+
return lowPriority;
50+
}
51+
52+
public void setLowPriority(boolean lowPriority) {
53+
this.lowPriority = lowPriority;
54+
}
55+
}

org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/vue/VueClientImpl.java

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,82 @@
1212
*******************************************************************************/
1313
package org.eclipse.wildwebdeveloper.vue;
1414

15+
import static org.eclipse.wildwebdeveloper.css.ui.preferences.CSSPreferenceServerConstants.isMatchCssSection;
16+
import static org.eclipse.wildwebdeveloper.html.ui.preferences.HTMLPreferenceServerConstants.isMatchHtmlSection;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
1520
import java.util.Map;
21+
import java.util.concurrent.CompletableFuture;
1622

17-
import org.eclipse.lsp4e.LanguageClientImpl;
23+
import org.eclipse.lsp4e.client.DefaultLanguageClient;
24+
import org.eclipse.lsp4j.ConfigurationItem;
25+
import org.eclipse.lsp4j.ConfigurationParams;
1826
import org.eclipse.lsp4j.MessageParams;
1927
import org.eclipse.lsp4j.MessageType;
28+
import org.eclipse.wildwebdeveloper.css.ui.preferences.CSSPreferenceServerConstants;
29+
import org.eclipse.wildwebdeveloper.html.ui.preferences.HTMLPreferenceServerConstants;
30+
import org.eclipse.wildwebdeveloper.ui.preferences.Settings;
2031

21-
public class VueClientImpl extends LanguageClientImpl implements VueLanguageServerExtention {
32+
public class VueClientImpl extends DefaultLanguageClient implements VueLanguageServerExtention {
2233

2334
@Override
2435
public void projectLoadingFinish(Object object) {
2536
// TODO should this set some state because only now stuff will work like hover..
2637
// or maybe even after projectLanguageService "enabled" call
2738
logMessage(new MessageParams(MessageType.Info, "Vue project loading finished"));
2839
}
29-
40+
3041
@Override
3142
public void projectLoadingStart(Object object) {
3243
logMessage(new MessageParams(MessageType.Info, "Vue project loading started"));
3344
}
34-
45+
3546
@Override
36-
public void projectLanguageService(Map<String,Object> data) {
37-
logMessage(new MessageParams(MessageType.Info, "Language Service is " + (((Boolean)data.get("languageServiceEnabled")).booleanValue()?"":"not yet ") + "enabled for project " + data.get("projectName")));
47+
public void projectLanguageService(Map<String, Object> data) {
48+
logMessage(new MessageParams(MessageType.Info,
49+
"Language Service is "
50+
+ (((Boolean) data.get("languageServiceEnabled")).booleanValue() ? "" : "not yet ")
51+
+ "enabled for project " + data.get("projectName")));
52+
}
53+
54+
@Override
55+
public void tsserverRequest(Object[] params) {
56+
logMessage(new MessageParams(MessageType.Info, "Forward TS message " + params[0]));
3857
}
3958

59+
@Override
60+
public CompletableFuture<List<Object>> configuration(ConfigurationParams params) {
61+
return CompletableFuture.supplyAsync(() -> {
62+
// The HTML language server asks for a given uri, the settings for 'css',
63+
// 'javascript', 'html'
64+
// See
65+
// https://github.com/microsoft/vscode/blob/7bd27b4287b49e61a1cb49e18f370260144c8685/extensions/html-language-features/server/src/htmlServer.ts#L123
66+
List<Object> settings = new ArrayList<>();
67+
for (ConfigurationItem item : params.getItems()) {
68+
String section = item.getSection();
69+
if (isMatchHtmlSection(section)) {
70+
// 'html' section, returns the html settings
71+
Settings htmlSettings = HTMLPreferenceServerConstants.getGlobalSettings();
72+
settings.add(htmlSettings.findSettings(section.split("[.]")));
73+
} else if (isMatchCssSection(section)) {
74+
// 'css' section, returns the css settings
75+
Settings cssSettings = CSSPreferenceServerConstants.getGlobalSettings();
76+
settings.add(cssSettings.findSettings(section.split("[.]")));
77+
} else if (section.equals("vue.suggest.defineAssignment")) {
78+
settings.add(true);
79+
} else if (section.equals("vue.suggest.propNameCasing")) {
80+
settings.add("preferKebabCase");
81+
} else if (section.equals("vue.suggest.componentNameCasing")) {
82+
settings.add("preferPascalCase");
83+
} else {
84+
// TODO match javascript section once those preferences will be
85+
// implemented.
86+
settings.add(null);
87+
}
88+
}
89+
return settings;
90+
});
91+
}
92+
4093
}

org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/vue/VueLanguageServer.java

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,41 @@
1616
import java.io.IOException;
1717
import java.net.URI;
1818
import java.net.URL;
19-
import java.nio.file.Paths;
2019
import java.util.ArrayList;
21-
import java.util.Collections;
22-
import java.util.HashMap;
20+
import java.util.Arrays;
2321
import java.util.List;
2422
import java.util.Map;
23+
import java.util.concurrent.CompletableFuture;
2524

2625
import org.eclipse.core.runtime.FileLocator;
2726
import org.eclipse.core.runtime.ILog;
27+
import org.eclipse.lsp4e.LSPEclipseUtils;
28+
import org.eclipse.lsp4e.LanguageServers;
2829
import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider;
30+
import org.eclipse.lsp4j.ExecuteCommandParams;
31+
import org.eclipse.lsp4j.jsonrpc.messages.Message;
32+
import org.eclipse.lsp4j.jsonrpc.messages.NotificationMessage;
33+
import org.eclipse.lsp4j.services.LanguageServer;
2934
import org.eclipse.wildwebdeveloper.embedder.node.NodeJSManager;
35+
import org.eclipse.wildwebdeveloper.jsts.JSTSLanguageServerAPI;
36+
import org.eclipse.wildwebdeveloper.jsts.request.ExecuteInfo;
3037

3138
public class VueLanguageServer extends ProcessStreamConnectionProvider {
32-
private static String tsserverPath = null;
3339
private static String vuePath = null;
40+
private static String TS_REQUEST = "tsserver/request";
3441

3542
public VueLanguageServer() {
3643

3744
List<String> commands = new ArrayList<>();
3845
commands.add(NodeJSManager.getNodeJsLocation().getAbsolutePath());
3946
try {
40-
if (vuePath == null || tsserverPath == null) {
47+
if (vuePath == null) {
4148
resolvePaths();
4249
}
4350
commands.add(vuePath);
4451
commands.add("--stdio");
4552
setCommands(commands);
46-
setWorkingDirectory(System.getProperty("user.dir"));
53+
//setWorkingDirectory(System.getProperty("user.dir"));
4754
} catch (IOException e) {
4855
ILog.get().error(e.getMessage(), e);
4956
}
@@ -53,44 +60,56 @@ private void resolvePaths() throws IOException {
5360
URL url = FileLocator
5461
.toFileURL(getClass().getResource("/node_modules/@vue/language-server/bin/vue-language-server.js"));
5562
vuePath = new File(url.getPath()).getAbsolutePath();
56-
57-
url = FileLocator.toFileURL(getClass().getResource("/node_modules/typescript/lib"));
58-
tsserverPath = new File(url.getPath()).getAbsolutePath();
63+
5964
}
65+
6066

6167
@Override
6268
protected ProcessBuilder createProcessBuilder() {
6369
ProcessBuilder builder = super.createProcessBuilder();
6470
builder.environment().put("VUE_NONPOLLING_WATCHER", Boolean.toString(true));
71+
builder.environment().put("NODE_ENV", "production");
6572
return builder;
6673
}
6774

6875
@Override
69-
public Object getInitializationOptions(URI rootUri) {
70-
Map<String, Object> options = new HashMap<>();
71-
setWorkingDirectory(Paths.get(rootUri).toString());
72-
73-
options.put("typescript", Collections.singletonMap("tsdk", tsserverPath));
74-
options.put("diagnosticModel", 0);
75-
options.put("additionalExtensions", new String[] {});
76-
77-
Map<String, Object> legend = new HashMap<>();
78-
legend.put("tokenTypes", new String[] {"component"} );
79-
legend.put("tokenModifiers", new String[] {} );
80-
options.put("semanticTokensLegend", legend);
81-
82-
Map<String, Object> vue = new HashMap<>();
83-
vue.put("hybridMode", false);
84-
85-
options.put("vue", vue);
86-
87-
return options;
76+
public String toString() {
77+
return "VUE Language Server: " + super.toString();
8878
}
8979

9080
@Override
91-
public String toString() {
92-
return "VUE Language Server: " + super.toString();
81+
public void handleMessage(Message message, LanguageServer languageServer, URI rootURI) {
82+
if (message instanceof NotificationMessage) {
83+
NotificationMessage msg = (NotificationMessage) message;
84+
if (msg.getMethod().equals(TS_REQUEST)) {
85+
forwardTS((Object[]) msg.getParams(), (VueLanguageServerAPI) languageServer, rootURI);
86+
}
87+
}
88+
super.handleMessage(message, languageServer, rootURI);
89+
}
90+
91+
@SuppressWarnings("restriction")
92+
private void forwardTS(Object[] params, VueLanguageServerAPI languageServer, URI rootURI) {
93+
Object requestId = params[0];
94+
String commandId = (String) params[1];
95+
Object args = params.length > 2 ? params[2] : null;
96+
97+
LanguageServers.forProject(LSPEclipseUtils.findResourceFor(rootURI).getProject())
98+
.collectAll((w, ls) -> CompletableFuture.completedFuture(ls)).thenAccept((lss) -> {
99+
lss.stream().filter(JSTSLanguageServerAPI.class::isInstance).map(JSTSLanguageServerAPI.class::cast)
100+
.findAny().ifPresent(jsts -> {
101+
jsts.getWorkspaceService()
102+
.executeCommand(new ExecuteCommandParams(JSTSLanguageServerAPI.TS_REQUEST_COMMAND,
103+
Arrays.asList(new Object[] { commandId, args, new ExecuteInfo() })))
104+
.whenComplete((result, e) -> {
105+
Object body = null;
106+
if (result instanceof Map) {
107+
body = ((Map<?, ?>)result).get("body");
108+
}
109+
languageServer.tsserverResponse(new Object[] {requestId, body});
110+
});
111+
});
112+
});
93113
}
94114

95-
96115
}

org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/vue/VueLanguageServerAPI.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.concurrent.CompletableFuture;
1616

1717
import org.eclipse.lsp4j.jsonrpc.messages.Either;
18+
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
1819
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
1920
import org.eclipse.lsp4j.services.LanguageServer;
2021
import org.eclipse.wildwebdeveloper.vue.autoinsert.AutoInsertParams;
@@ -38,5 +39,9 @@ public interface VueLanguageServerAPI extends LanguageServer {
3839
@JsonRequest("volar/client/autoInsert")
3940
CompletableFuture<Either<String, AutoInsertResponse>> autoInsert(AutoInsertParams params);
4041

42+
@JsonNotification("tsserver/response")
43+
void tsserverResponse(Object any);
44+
45+
4146

4247
}

org.eclipse.wildwebdeveloper/src/org/eclipse/wildwebdeveloper/vue/VueLanguageServerExtention.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ public interface VueLanguageServerExtention {
2626

2727
@JsonNotification(value = "vue/projectLanguageService")
2828
public void projectLanguageService(Map<String,Object> data);
29+
30+
31+
@JsonNotification(value = "tsserver/request")
32+
public void tsserverRequest(Object[] params);
2933
}

0 commit comments

Comments
 (0)