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

Neovim LSP客户端未发送initialized方法通知问题求助

问题解决:Neovim LSP未发送initialized通知的原因及修复

核心问题分析

你的语言服务器代码存在几个关键问题,导致Neovim在收到initialize响应后无法继续通信:

  • 头部解析逻辑缺陷:代码中每次循环都会覆盖header变量,仅保留最后一行头部信息。如果客户端发送多个头部字段(例如Content-Type),会导致无法正确提取Content-Length,后续请求处理直接崩溃。
  • Content-Length计算错误:使用len(json.dumps(response))得到的是字符串字符数,而非实际发送的字节数。LSP要求Content-Length是字节长度,不匹配会导致客户端无法正确解析响应,进而终止通信。
  • 未处理无ID的通知请求:initialized是LSP的通知类型(无id字段),当前代码默认所有请求都有id,遇到通知时会抛出KeyError,导致服务器终止。
  • 文本模式IO的潜在问题:默认的文本模式会自动转换换行符,可能破坏LSP规定的\r\n分隔格式。

修正后的代码

import sys
import logging
import json

logging.basicConfig(
    filename='server.log',
    level=logging.DEBUG,
    filemode='w',
    format='%(asctime)s %(levelname)s - %(message)s'
)

logging.info('Server logging initiated')

# 切换到二进制模式处理IO,避免换行符自动转换
stdin = sys.stdin.buffer
stdout = sys.stdout.buffer

def read_header():
    """正确读取并解析LSP头部,提取Content-Length"""
    content_length = None
    while True:
        line = stdin.readline()
        if not line:
            # 连接关闭
            return None
        line = line.decode('utf-8').strip()
        if not line:
            # 头部结束,空行
            break
        if line.startswith('Content-Length:'):
            content_length = int(line.split(':')[1].strip())
    return content_length

while True:
    content_length = read_header()
    if content_length is None:
        break
    
    content = stdin.read(content_length).decode('utf-8')
    request = json.loads(content)
    
    logging.info(f'Received method: {request["method"]}')
    if 'params' in request and 'clientInfo' in request['params']:
        client_name = request['params']['clientInfo']['name']
        client_version = request['params']['clientInfo']['version']
        logging.info(f'Connected to client: {client_name} {client_version}')
    
    response = None
    if request['method'] == 'initialize':
        # 处理initialize请求
        response = {
            'jsonrpc': '2.0',
            'id': request['id'],
            'result': {
                'serverInfo': {
                    'name': 'my-ls',
                    'version': '0.1.0'
                },
                'capabilities': {}
            }
        }
    elif request['method'] == 'initialized':
        # 处理initialized通知,无需响应
        logging.info('Received initialized notification')
        continue
    
    # 发送响应(如果有)
    if response:
        response_bytes = json.dumps(response).encode('utf-8')
        header = f'Content-Length: {len(response_bytes)}\r\n\r\n'.encode('utf-8')
        stdout.write(header)
        stdout.write(response_bytes)
        stdout.flush()
        logging.info(f'Sent response for {request["method"]}')

关键修改说明

  1. 二进制模式IO:使用sys.stdin.buffer和sys.stdout.buffer以二进制模式处理输入输出,避免换行符自动转换,严格遵循LSP的通信格式。
  2. 正确的头部解析:新增read_header函数,遍历所有头部行,专门提取Content-Length,忽略其他无关头部字段,保证后续内容读取正确。
  3. 字节长度计算:将响应转为字节后再计算长度,确保Content-Length是正确的字节数,客户端可以准确解析响应。
  4. 处理通知类型:区分请求(带id)和通知(无id),比如initialized通知不需要返回响应,直接跳过即可。

验证效果

修改后重启服务器,Neovim会在收到initialize响应后发送initialized通知,你的日志中会新增Received initialized notification的记录,后续可以继续处理其他LSP请求/通知。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:52:05