我有兴趣探索 Salesforce for Apex 的语言服务器。我对这个概念很陌生,想了解如何与之沟通。 因此,我正在寻求帮助来编写可以与 Salesforce 提供的 JAR 进行通信的 Java 代码。附上链接供参考。
我需要一个演示该过程的 Java 代码的工作示例。
经过大量研究,我成功地与 Apex 语言服务器进行了通信。尽管代码并不完美并且存在需要优化的地方,但它目前适用于初始化服务器、发送文档进行分析以及检索完成或类型提示等基本任务。
package com.iishanto;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class LanguageServerRunner {
private final String serverPath;
private final ExecutorService executorService;
private Process languageServerProcess;
private BufferedWriter writer;
public LanguageServerRunner(String serverPath) {
this.serverPath = serverPath;
this.executorService = Executors.newFixedThreadPool(3);
}
public void startServer() throws IOException {
ProcessBuilder processBuilder = new ProcessBuilder(
"java",
"-Xms256m",
"-Xmx512m",
"-Ddebug.internal.errors=true",
"-Ddebug.semantic.errors=true",
"-Ddebug.completion.statistics=true",
"-jar",
serverPath);
languageServerProcess = processBuilder.start();
writer = new BufferedWriter(new OutputStreamWriter(languageServerProcess.getOutputStream()));
handleServerOutput(languageServerProcess.getInputStream(), "Server Response");
handleServerOutput(languageServerProcess.getErrorStream(), "Error");
}
private void handleServerOutput(InputStream inputStream, String label) {
executorService.submit(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(label + ": " + line);
if (label.equals("Server Response") && line.startsWith("Content-Length:")) {
int contentLength = Integer.parseInt(line.substring("Content-Length:".length()).trim());
char[] content = new char[contentLength];
reader.read(content, 0, contentLength);
System.out.println("Server Response Body: " + new String(content));
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
public void sendMessage(String message) throws IOException {
String formattedMessage = "Content-Length: " + message.length() + "\r\n\r\n" + message;
writer.write(formattedMessage);
writer.flush();
}
public void sendInitializeMessage() throws IOException {
String initRequest = """
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"processId": null,
"rootUri": "file:///C:/Users/Example/Desktop/Project",
"rootPath": "C:/Users/Example/Desktop/Project/",
"capabilities": {
"textDocument": {
"synchronization": {
"dynamicRegistration": true,
"willSave": true,
"willSaveWaitUntil": true,
"didSave": true
},
"completion": {
"dynamicRegistration": true,
"completionItem": {
"snippetSupport": true,
"commitCharactersSupport": true
}
}
},
"workspace": {
"applyEdit": true,
"workspaceEdit": {
"documentChanges": true
}
}
}
}
}
""";
sendMessage(initRequest);
}
public void sendDidOpenMessage(String fileUri, String languageId, int version, String text) throws IOException {
String openRequest = """
{
"jsonrpc": "2.0",
"method": "textDocument/didOpen",
"params": {
"textDocument": {
"uri": "%s",
"languageId": "%s",
"version": %d,
"text": "%s"
}
}
}
""".formatted(fileUri, languageId, version, text);
sendMessage(openRequest);
}
public void sendTypeHintingMessage(String fileUri,String content,int line,int column) throws IOException {
String message= """
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/completion",
"params": {
"textDocument": {
"uri": "%s"
},
"position": {
"line": %d,
"character": %d
},
"context": {
"triggerKind": 1,
"triggerCharacter": "."
}
}
}
""".formatted(fileUri,line,column);
System.out.println(message);
sendMessage(message);
}
public void stopServer() throws InterruptedException {
if (languageServerProcess != null) {
int exitCode = languageServerProcess.waitFor();
System.out.println("Language Server exited with code: " + exitCode);
}
executorService.shutdown();
}
public static void main(String[] args) {
String serverPath = "C:\\Users\\Example\\Downloads\\apex-jorje-lsp.jar";
String filePath = "C:\\Users\\Example\\Desktop\\Project\\force-app\\main\\default\\classes\\DynamicFieldFetcher.cls";
try {
LanguageServerRunner runner = new LanguageServerRunner(serverPath);
runner.startServer();
String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
runner.sendInitializeMessage();
runner.sendDidOpenMessage(
"file:///C:/Users/Example/Desktop/Project/force-app/main/default/classes/DynamicFieldFetcher.cls",
"apex",
6,
fileContent
);
TimeUnit.MILLISECONDS.sleep(5000);
runner.sendTypeHintingMessage(
"file:///C:/Users/Example/Desktop/Project/force-app/main/default/classes/DynamicFieldFetcher.cls"
,fileContent,5,34);
BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in));
String inputLine;
while ((inputLine = userInput.readLine()) != null) {
runner.sendMessage(inputLine);
}
runner.stopServer();
} catch (Exception e) {
e.printStackTrace();
}
}
}