Skip to main content

aster/lsp/
config.rs

1//! LSP 服务器配置
2//!
3//! 定义 LSP 服务器配置结构和默认配置
4
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::path::Path;
8
9/// LSP 服务器配置
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct LSPServerConfig {
12    /// 服务器名称
13    pub name: String,
14    /// 可执行文件路径或命令
15    pub command: String,
16    /// 命令行参数
17    #[serde(default)]
18    pub args: Vec<String>,
19    /// 支持的文件扩展名
20    pub file_extensions: Vec<String>,
21
22    /// 文件扩展名到语言ID的映射
23    #[serde(default)]
24    pub extension_to_language: HashMap<String, String>,
25    /// 初始化选项
26    #[serde(default)]
27    pub initialization_options: Option<serde_json::Value>,
28    /// 服务器设置
29    #[serde(default)]
30    pub settings: Option<serde_json::Value>,
31    /// 环境变量
32    #[serde(default)]
33    pub env: HashMap<String, String>,
34    /// 工作区文件夹路径
35    pub workspace_folder: Option<String>,
36    /// 启动超时时间 (毫秒)
37    #[serde(default = "default_startup_timeout")]
38    pub startup_timeout: u64,
39    /// 崩溃后是否自动重启
40    #[serde(default = "default_restart_on_crash")]
41    pub restart_on_crash: bool,
42    /// 最大重启次数
43    #[serde(default = "default_max_restarts")]
44    pub max_restarts: u32,
45    /// 配置来源
46    pub source: Option<String>,
47}
48
49fn default_startup_timeout() -> u64 {
50    30000
51}
52fn default_restart_on_crash() -> bool {
53    true
54}
55fn default_max_restarts() -> u32 {
56    3
57}
58
59impl Default for LSPServerConfig {
60    fn default() -> Self {
61        Self {
62            name: String::new(),
63            command: String::new(),
64            args: Vec::new(),
65            file_extensions: Vec::new(),
66            extension_to_language: HashMap::new(),
67            initialization_options: None,
68            settings: None,
69            env: HashMap::new(),
70            workspace_folder: None,
71            startup_timeout: default_startup_timeout(),
72            restart_on_crash: default_restart_on_crash(),
73            max_restarts: default_max_restarts(),
74            source: None,
75        }
76    }
77}
78
79/// .lsp.json 配置文件格式
80pub type LSPConfigFile = HashMap<String, LSPServerConfig>;
81
82/// 加载 .lsp.json 配置文件
83pub fn load_lsp_config_file(workspace_root: &Path) -> Vec<LSPServerConfig> {
84    let search_paths = [
85        workspace_root.join(".lsp.json"),
86        workspace_root.join(".claude/lsp.json"),
87        dirs::home_dir()
88            .map(|h| h.join(".claude/lsp.json"))
89            .unwrap_or_default(),
90    ];
91
92    let mut configs = Vec::new();
93
94    for config_path in &search_paths {
95        if !config_path.exists() {
96            continue;
97        }
98
99        match std::fs::read_to_string(config_path) {
100            Ok(content) => match serde_json::from_str::<LSPConfigFile>(&content) {
101                Ok(config_file) => {
102                    for (name, mut config) in config_file {
103                        config.name = name;
104                        config.source = Some(config_path.display().to_string());
105                        configs.push(config);
106                    }
107                }
108                Err(e) => {
109                    tracing::warn!("解析 LSP 配置文件失败 {}: {}", config_path.display(), e);
110                }
111            },
112            Err(e) => {
113                tracing::warn!("读取 LSP 配置文件失败 {}: {}", config_path.display(), e);
114            }
115        }
116    }
117
118    configs
119}
120
121/// 默认 LSP 服务器配置
122pub fn default_lsp_configs() -> Vec<LSPServerConfig> {
123    vec![
124        LSPServerConfig {
125            name: "typescript-language-server".to_string(),
126            command: "typescript-language-server".to_string(),
127            args: vec!["--stdio".to_string()],
128            file_extensions: vec![
129                ".ts".to_string(),
130                ".tsx".to_string(),
131                ".js".to_string(),
132                ".jsx".to_string(),
133            ],
134            extension_to_language: [
135                (".ts".to_string(), "typescript".to_string()),
136                (".tsx".to_string(), "typescriptreact".to_string()),
137                (".js".to_string(), "javascript".to_string()),
138                (".jsx".to_string(), "javascriptreact".to_string()),
139            ]
140            .into_iter()
141            .collect(),
142            restart_on_crash: true,
143            max_restarts: 3,
144            ..Default::default()
145        },
146        LSPServerConfig {
147            name: "pyright".to_string(),
148            command: "pyright-langserver".to_string(),
149            args: vec!["--stdio".to_string()],
150            file_extensions: vec![".py".to_string()],
151            extension_to_language: [(".py".to_string(), "python".to_string())]
152                .into_iter()
153                .collect(),
154            restart_on_crash: true,
155            max_restarts: 3,
156            ..Default::default()
157        },
158        LSPServerConfig {
159            name: "rust-analyzer".to_string(),
160            command: "rust-analyzer".to_string(),
161            args: vec![],
162            file_extensions: vec![".rs".to_string()],
163            extension_to_language: [(".rs".to_string(), "rust".to_string())]
164                .into_iter()
165                .collect(),
166            restart_on_crash: true,
167            max_restarts: 3,
168            ..Default::default()
169        },
170    ]
171}