You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 17:40:28