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

使用Rust读取Neovim标准输入时无法获取完整JSON-RPC消息的问题

问题分析

你当前的问题出在按行读取stdin的方式不符合JSON-RPC的消息格式。Neovim发送的JSON-RPC消息结构是:

Content-Length: [字节数]\r\n
\r\n
[对应字节数的JSON内容]

lines()方法会按换行符分割内容,无法处理这种基于长度的消息协议:

  • 读取到Content-Length行后,下一行是空行
  • 后续的JSON内容可能包含换行,或者需要一次性读取指定字节数的完整内容,而不是逐行拆分
解决方案

修改读取逻辑,按照JSON-RPC的协议规范来读取:

  1. 解析Content-Length头获取消息体长度
  2. 跳过头部和消息体之间的空行
  3. 一次性读取对应长度的字节作为完整JSON消息

修改后的main.rs代码:

use std::io::{self, BufRead, BufReader, Read};
use std::str::FromStr;

fn main() -> io::Result<()> {
    let stdin = io::stdin();
    let mut reader = BufReader::new(stdin.lock());

    loop {
        // 读取Content-Length头
        let mut header_line = String::new();
        match reader.read_line(&mut header_line) {
            Ok(0) => break, // stdin关闭,退出循环
            Ok(_) => {},
            Err(e) => {
                logger::print_logs(format!("读取头行失败: {}", e), None)?;
                continue;
            }
        }

        // 解析Content-Length值
        let content_length = match header_line.strip_prefix("Content-Length: ") {
            Some(len_str) => {
                let len_str = len_str.trim();
                usize::from_str(len_str).map_err(|e| {
                    io::Error::new(io::ErrorKind::InvalidData, format!("解析Content-Length失败: {}", e))
                })
            }
            None => {
                logger::print_logs(format!("无效的头行: {}", header_line), None)?;
                continue;
            }
        };

        let content_length = match content_length {
            Ok(val) => val,
            Err(e) => {
                logger::print_logs(e.to_string(), None)?;
                continue;
            }
        };

        logger::print_logs(format!("Content-Length: {}", content_length), None)?;

        // 跳过空行(头和消息体之间的分隔)
        let mut empty_line = String::new();
        if let Err(e) = reader.read_line(&mut empty_line) {
            logger::print_logs(format!("读取空行失败: {}", e), None)?;
            continue;
        }

        // 读取指定长度的JSON消息体
        let mut json_buffer = vec![0; content_length];
        match reader.read_exact(&mut json_buffer) {
            Ok(_) => {
                let json_str = String::from_utf8_lossy(&json_buffer);
                logger::print_logs("完整JSON消息:".to_string(), None)?;
                logger::print_logs(json_str.to_string(), None)?;
            }
            Err(e) => {
                logger::print_logs(format!("读取JSON消息失败: {}", e), None)?;
                continue;
            }
        }
    }

    Ok(())
}
额外优化建议

你的logger.rs每次写入日志都先读取整个文件再重新写入,效率较低且存在并发风险(单线程下虽能运行,但不推荐),可以改为追加模式:

use std::fs::{self, File, OpenOptions};
use std::io::Write;

const LOGS_FOLDER: &str = "./logs";
const DEFAULT_LOGS_FILE: &str = "lsp_logs.txt";

pub fn print_logs(message: String, file_name: Option<&str>) -> std::io::Result<()> {
    fs::create_dir_all(LOGS_FOLDER)?;

    let file_name = file_name.unwrap_or(DEFAULT_LOGS_FILE);
    let file_location = format!("{}/{}", LOGS_FOLDER, file_name);

    // 使用追加模式打开文件,无需先读取原有内容
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(file_location)?;

    writeln!(file, "{}", message)?;

    Ok(())
}

内容的提问来源于stack exchange,提问作者Cristi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 05:48:25