VSCode扩展WebSocket连接已运行语言服务器无后续通信问题
问题描述
我正尝试编写一个VSCode扩展,用于连接已运行的语言服务器,目的是在IDE中运行该语言服务器并附加调试器。我尝试通过WebSocket进行连接,扩展代码如下:
import * as vscode from 'vscode'; import WebSocket from 'ws'; import { TextDecoder } from 'util'; // Import the language client, language client options and server options from VSCode language client. import { LanguageClient } from 'vscode-languageclient'; // Name of the launcher class which contains the main. const main: string = 'com.ajanuary.ashlang.lsp.AsLangLanguageServerLauncher'; export function activate(context: vscode.ExtensionContext) { console.log('Congratulations, your extension "ashlang" is now active!'); const ws = new WebSocket(`ws://localhost:5000/`); const connection = WebSocket.createWebSocketStream(ws); connection.on("data", function (chunk) { console.log(new TextDecoder().decode(chunk)); }); let disposable = new LanguageClient('ashLS', 'Ash Language Server', () => Promise.resolve({ reader: connection, writer: connection, }), { documentSelector: [{ scheme: 'file', language: 'ash' }] }).start(); context.subscriptions.push(disposable); } // this method is called when your extension is deactivated export function deactivate() { console.log('Your extension "ashlang" is now deactivated!'); }
数据处理函数输出了如下内容:
{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"textDocumentSync":1,"completionProvider":{}}}}
由此可知扩展能与服务器通信并接收响应,但后续不再发送任何消息。请问如何让扩展正确读取并处理WebSocket消息?或者有没有更好的方式在IDE中开发语言服务器?
解决方案
修复WebSocket连接问题
当前代码的核心问题是WebSocket.createWebSocketStream返回的是二进制流,而LSP依赖基于文本行的JSON-RPC消息格式,二进制流无法正确解析后续的LSP消息结构,导致LanguageClient无法识别处理。
1. 封装WebSocket为文本流
通过自定义可读/可写流,将WebSocket的消息转换为LSP兼容的文本流:
import * as vscode from 'vscode'; import WebSocket from 'ws'; import { Readable, Writable } from 'stream'; import { LanguageClient } from 'vscode-languageclient'; export function activate(context: vscode.ExtensionContext) { console.log('ashlang 扩展已激活'); const ws = new WebSocket('ws://localhost:5000/'); // 自定义可读流,处理WebSocket消息 const reader = new Readable({ read() {} }); // 自定义可写流,发送数据到WebSocket const writer = new Writable({ write(chunk, encoding, callback) { if (ws.readyState === WebSocket.OPEN) { ws.send(chunk.toString(), callback); } else { callback(new Error('WebSocket未建立连接')); } } }); // 收到服务器消息时,推送到可读流(添加换行符保证LSP解析) ws.on('message', (data) => { reader.push(data.toString() + '\n'); }); ws.on('open', () => console.log('已连接到Ash语言服务器')); ws.on('error', (err) => console.error('WebSocket连接错误:', err)); const client = new LanguageClient('ashLS', 'Ash Language Server', () => Promise.resolve({ reader, writer }), { documentSelector: [{ scheme: 'file', language: 'ash' }] } ); context.subscriptions.push(client.start()); } export function deactivate() {}
该实现将WebSocket的message事件数据转为文本并添加换行符,确保LSP的JSON-RPC解析器能正确识别每条消息;写入时则将流数据转为字符串发送给WebSocket。
2. 验证服务器消息格式
确保语言服务器发送的LSP消息符合规范:
- 标准LSP消息需要包含
Content-Length头部,后跟空行和JSON内容 - 部分简易实现会直接发送换行分隔的JSON文本
如果服务器使用带头部的格式,需要在自定义流中先解析头部,再提取JSON内容传递给可读流。
更高效的语言服务器开发调试方式
如果核心需求是在IDE中调试语言服务器,没必要通过WebSocket中转,推荐以下更简便的方式:
1. 直接启动并调试服务器
在语言服务器项目中创建.vscode/launch.json,配置启动调试(以Java服务器为例):
{ "version": "0.2.0", "configurations": [ { "type": "java", "name": "Launch Ash Language Server", "request": "launch", "mainClass": "com.ajanuary.ashlang.lsp.AsLangLanguageServerLauncher", "args": ["--stdio"], "env": { "JAVA_OPTS": "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" } } ] }
通过--stdio参数让服务器使用标准输入输出与扩展通信,这是LSP最常用的稳定通信方式。
2. 扩展中使用stdio连接服务器
扩展直接通过启动进程的方式连接服务器,无需额外WebSocket服务:
import * as vscode from 'vscode'; import { LanguageClient, Executable } from 'vscode-languageclient'; export function activate(context: vscode.ExtensionContext) { const serverOptions: Executable = { command: 'java', args: ['-jar', '/path/to/your/ash-server.jar', '--stdio'] }; const client = new LanguageClient('ashLS', 'Ash Language Server', serverOptions, { documentSelector: [{ scheme: 'file', language: 'ash' }] }); context.subscriptions.push(client.start()); } export function deactivate() {}
3. 附加到远程调试端口
如果服务器已通过远程调试模式启动(如上述launch.json中的5005端口),可以直接在VSCode的调试面板中选择"附加到进程",连接到5005端口即可调试,无需通过扩展中转。
内容的提问来源于stack exchange,提问作者ICR

