lspz 0.10.10

AI-friendly LSP compression proxy
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# lspz 编码约定

**版本**: v0.1.0
**最后更新**: 2026-05-09

> **注意**: lspz 项目的通用编码规范定义在项目根目录的 [AGENTS.md]https://github.com/straydragon/lspz/blob/main/AGENTS.md(SSOT)。本文档仅包含 lspz 特定的约定和补充。

---

## 快速参考

### 通用规范(详见 AGENTS.md)

- [Rust Edition & Toolchain]https://github.com/straydragon/lspz/blob/main/AGENTS.md#rust
- [命名约定]https://github.com/straydragon/lspz/blob/main/AGENTS.md#命名约定
- [错误处理]https://github.com/straydragon/lspz/blob/main/AGENTS.md#错误处理
- [异步代码]https://github.com/straydragon/lspz/blob/main/AGENTS.md#异步代码
- [日志规范]https://github.com/straydragon/lspz/blob/main/AGENTS.md#日志规范
- [测试规范]https://github.com/straydragon/lspz/blob/main/AGENTS.md#测试规范
- [文档规范]https://github.com/straydragon/lspz/blob/main/AGENTS.md#文档规范
- [安全考虑]https://github.com/straydragon/lspz/blob/main/AGENTS.md#安全考虑
- [性能考虑]https://github.com/straydragon/lspz/blob/main/AGENTS.md#性能考虑

**请先阅读 AGENTS.md 了解通用规范。**

---

## lspz 特定约定

### 项目结构约定

```
lspz/
├── Cargo.toml           # single crate, feature flags
├── src/
│   ├── lib.rs           # 公共 API + 模块声明
│   ├── main.rs          # CLI 入口 (feature = "cli")
│   ├── proxy.rs         # Proxy 核心
│   ├── interceptors/    # 所有拦截器实现
│   ├── codec/           # 编解码层(JSON-RPC, 紧凑格式)
│   ├── transport/       # 传输层实现(stdio, TCP, WS)
│   ├── mcp/             # MCP 服务器 (feature = "mcp")
│   ├── agent_sdk/       # Agent SDK (feature = "agent-sdk")
│   ├── config.rs
│   └── error.rs
└── examples/            # 示例代码
```

### 模块组织

- **lib.rs**: 仅重新导出公共 API,不包含实现
- **proxy.rs**: Proxy 核心逻辑,依赖其他模块
- **interceptors/**: 所有拦截器实现
- **codec/**: 编解码层(JSON-RPC, 紧凑格式)
- **transport/**: 传输层实现(stdio, 未来 TCP/WS)

### 公共 API 设计

**原则**: 核心库部分公共 API 必须稳定且易用。

```rust
// ✅ 好: 清晰的构建器模式
let config = Config::builder()
    .backend_cmd("rust-analyzer")
    .enable_diag_compress(true)
    .build();

let proxy = Proxy::new(config).await?;

// ❌ 不好: 复杂的嵌套结构
let proxy = Proxy::new(ProxyConfig {
    backend: BackendConfig {
        command: "rust-analyzer".to_string(),
        args: vec![],
    },
    compression: CompressionConfig {
        enable_diagnostics: true,
        // ...
    },
})?;
```

### 拦截器约定

**拦截器 trait 定义**:

```rust
#[async_trait]
pub trait Interceptor: Send + Sync + AsAny {
    /// 拦截器名称(用于日志和调试)
    fn name(&self) -> &str;

    /// 拦截处理
    async fn intercept(
        &self,
        message: &JsonRpcMessage,
        direction: Direction,
    ) -> Result<Option<JsonRpcMessage>, LspzError>;

    /// 优先级(越小越先执行)
    fn priority(&self) -> i32 {
        0
    }
}
```

**实现拦截器**:

```rust
pub struct DiagnosticsCompressor {
    config: CompressionConfig,
}

#[async_trait]
impl Interceptor for DiagnosticsCompressor {
    fn name(&self) -> &str {
        "diagnostics_compressor"
    }

    fn priority(&self) -> i32 {
        100  // 高优先级
    }

    async fn intercept(
        &self,
        message: &JsonRpcMessage,
        direction: Direction,
    ) -> Result<Option<JsonRpcMessage>, LspzError> {
        // 只处理 Server→Client 的诊断
        if direction != Direction::ServerToClient {
            return Ok(Some(message.clone()));
        }

        if message.method == "textDocument/publishDiagnostics" {
            self.compress_diagnostics(message).await
        } else {
            Ok(Some(message.clone()))
        }
    }
}
```

### 传输层约定

**Transport trait 定义**:

```rust
#[async_trait]
pub trait Transport: Send + Sync {
    /// 发送消息
    async fn send(&self, message: JsonRpcMessage) -> Result<(), LspzError>;

    /// 接收消息
    async fn receive(&self) -> Result<JsonRpcMessage, LspzError>;

    /// 关闭传输
    async fn close(&self) -> Result<(), LspzError>;
}
```

**实现传输层**:

```rust
pub struct StdioTransport {
    reader: BufReader<ChildStdout>,
    writer: BufWriter<ChildStdin>,
}

#[async_trait]
impl Transport for StdioTransport {
    async fn send(&self, message: JsonRpcMessage) -> Result<(), LspzError> {
        let json = serde_json::to_string(&message)?;
        writeln!(self.writer, "Content-Length: {}", json.len())?;
        writeln!(self.writer)?;
        writeln!(self.writer, "{}", json)?;
        self.writer.flush().await?;
        Ok(())
    }

    async fn receive(&self) -> Result<JsonRpcMessage, LspzError> {
        // 解析 Content-Length
        // 读取 JSON 消息
        // ...
    }

    async fn close(&self) -> Result<(), LspzError> {
        // 清理资源
    }
}
```

### 配置约定

**配置结构体**:

```rust
#[derive(Clone, Debug, Builder)]
pub struct Config {
    /// 后端 LSP 命令
    #[builder(default = "default_backend_cmd")]
    backend_cmd: String,

    /// 是否启用诊断压缩
    #[builder(default)]
    enable_diag_compress: bool,

    /// 日志级别
    #[builder(default = "default_log_level")]
    log_level: Level,
}
```

**从环境变量加载**:

```rust
impl Config {
    pub fn from_env() -> Result<Self, LspzError> {
        let backend_cmd = env::var("LSPZ_BACKEND_CMD")
            .unwrap_or_else(|_| "rust-analyzer".to_string());

        let enable_diag_compress = env::var("LSPZ_ENABLE_DIAG_COMPRESS")
            .map(|s| s.eq_ignore_ascii_case("true"))
            .unwrap_or(false);

        // ...
    }
}
```

### 错误处理约定

**统一错误类型**:

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum LspzError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("JSON parse error: {0}")]
    JsonParse(#[from] serde_json::Error),

    #[error("LSP protocol error: {0}")]
    LspProtocol(String),

    #[error("Compression error: {0}")]
    Compression(String),

    #[error("Transport error: {0}")]
    Transport(String),

    #[error("Configuration error: {0}")]
    Config(String),
}
```

**压缩失败降级**:

```rust
// 在 Proxy 核心中
match self.intercept(message, direction) {
    Ok(Some(compressed)) => send(compressed),
    Ok(None) => { /* 消息被丢弃 */ }
    Err(e) => {
        warn!("Compression failed: {}, falling back to transparent forward", e);
        send_original(message);
    }
}
```

### LSP 兼容性约定

**透明转发原则**:

- `textDocument/publishDiagnostics` 消息必须透明转发
- 不得修改 LSP 初始化握手
- 不得修改 server/client capabilities

**能力协商**:

```rust
// lspz 不修改 capabilities,直接转发
async fn handle_initialize(&self, req: Request) -> Result<Response, LspzError> {
    let server_response = self.send_to_backend(req).await?;
    Ok(server_response)  // 直接返回,不修改
}
```

---

## 扩展点预留

### 为未来扩展预留代码

**拦截器动态注册** (v0.3):

```rust
// 当前: 固定列表
let interceptors: Vec<Box<dyn Interceptor>> = vec![
    Box::new(DiagnosticsCompressor::new(config)),
];

// 未来预留: 支持动态注册
// proxy.register_interceptor(Box::new(CustomInterceptor::new()))?;
// proxy.set_interceptor_order(&["diagnostics", "custom"])?;
```

**传输层抽象** (v0.4):

```rust
// 当前: 只有 stdio
let transport = StdioTransport::new(child)?;

// 未来预留: 支持多种传输
// let transport: Box<dyn Transport> = match config.transport_type {
//     TransportType::Stdio => Box::new(StdioTransport::new(child)?),
//     TransportType::Tcp => Box::new(TcpTransport::new(&config.tcp_addr)?),
//     TransportType::WebSocket => Box::new(WebSocketTransport::new(&config.ws_url)?),
// };
```

**配置热加载** (v0.4):

```rust
// 当前: 配置固定
let config = Config::from_env()?;

// 未来预留: 支持热加载
// let hot_reload = HotReloadConfig::new("lspz.toml")?;
// hot_reload.watch(|new_config| {
//     proxy.update_config(new_config);
// });
```

---

## 文档注释模板

### Struct 文档

```rust
/// LSP 代理核心结构体。
///
/// # 示例
///
/// ```no_run
/// use lspz::{Proxy, Config};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let config = Config::builder()
///         .backend_cmd("rust-analyzer")
///         .enable_diag_compress(true)
///         .build();
///
///     let proxy = Proxy::new(config).await?;
///     proxy.initialize().await?;
///
///     // 使用 proxy...
///     Ok(())
/// }
/// ```
///
/// # 错误
///
/// 如果后端 LSP 启动失败,返回 `LspzError::Io`。
/// 如果 LSP 握手失败,返回 `LspzError::LspProtocol`。
pub struct Proxy {
    // ...
}
```

### Trait 文档

```rust
/// LSP 消息拦截器。
///
/// 拦截器可以修改、丢弃或转发 LSP 消息。
///
/// # 示例
///
/// ```
/// use lspz::interceptors::{Interceptor, Direction};
/// use lspz::{JsonRpcMessage, LspzError};
///
/// struct MyInterceptor;
///
/// #[async_trait::async_trait]
/// impl Interceptor for MyInterceptor {
///     async fn intercept(
///         &self,
///         message: &JsonRpcMessage,
///         direction: Direction,
///     ) -> Result<Option<JsonRpcMessage>, LspzError> {
///         // 实现拦截逻辑
///         Ok(Some(message.clone()))
///     }
/// }
/// ```
pub trait Interceptor: Send + Sync {
    // ...
}
```

---

## 参考文档

- [AGENTS.md]https://github.com/straydragon/lspz/blob/main/AGENTS.md - 项目通用规范(SSOT)
- [架构设计]../architecture.md - 三模态架构
- [压缩格式]../specs/compression-format.md - 压缩格式规范
- [LSP 兼容性]../specs/lsp-compatibility.md - LSP 兼容性规范
- [测试指南]testing.md - 测试指南