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"]}')
关键修改说明
- 二进制模式IO:使用
sys.stdin.buffer和sys.stdout.buffer以二进制模式处理输入输出,避免换行符自动转换,严格遵循LSP的通信格式。 - 正确的头部解析:新增
read_header函数,遍历所有头部行,专门提取Content-Length,忽略其他无关头部字段,保证后续内容读取正确。 - 字节长度计算:将响应转为字节后再计算长度,确保
Content-Length是正确的字节数,客户端可以准确解析响应。 - 处理通知类型:区分请求(带
id)和通知(无id),比如initialized通知不需要返回响应,直接跳过即可。
验证效果
修改后重启服务器,Neovim会在收到initialize响应后发送initialized通知,你的日志中会新增Received initialized notification的记录,后续可以继续处理其他LSP请求/通知。
内容的提问来源于stack exchange,提问作者Milton Tom
相关产品推荐
相关产品推荐

