Skip to main content

lean_ctx/lsp/
config.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3
4#[derive(Debug, Clone)]
5pub struct LspServerConfig {
6    pub command: String,
7    pub args: Vec<String>,
8}
9
10#[derive(Debug, Clone)]
11pub struct LspServerInfo {
12    pub language: &'static str,
13    pub binary: &'static str,
14    pub install_hint: &'static str,
15}
16
17pub const KNOWN_SERVERS: &[LspServerInfo] = &[
18    LspServerInfo {
19        language: "rust",
20        binary: "rust-analyzer",
21        install_hint: "rustup component add rust-analyzer",
22    },
23    LspServerInfo {
24        language: "typescript",
25        binary: "typescript-language-server",
26        install_hint: "npm install -g typescript-language-server typescript",
27    },
28    LspServerInfo {
29        language: "python",
30        binary: "pylsp",
31        install_hint: "pip install python-lsp-server",
32    },
33    LspServerInfo {
34        language: "go",
35        binary: "gopls",
36        install_hint: "go install golang.org/x/tools/gopls@latest",
37    },
38];
39
40pub fn default_servers() -> HashMap<&'static str, LspServerConfig> {
41    let mut m = HashMap::new();
42    m.insert(
43        "rust",
44        LspServerConfig {
45            command: "rust-analyzer".into(),
46            args: vec![],
47        },
48    );
49    m.insert(
50        "typescript",
51        LspServerConfig {
52            command: "typescript-language-server".into(),
53            args: vec!["--stdio".into()],
54        },
55    );
56    m.insert(
57        "javascript",
58        LspServerConfig {
59            command: "typescript-language-server".into(),
60            args: vec!["--stdio".into()],
61        },
62    );
63    m.insert(
64        "python",
65        LspServerConfig {
66            command: "pylsp".into(),
67            args: vec![],
68        },
69    );
70    m.insert(
71        "go",
72        LspServerConfig {
73            command: "gopls".into(),
74            args: vec!["serve".into()],
75        },
76    );
77    m
78}
79
80pub fn language_for_extension(ext: &str) -> Option<&'static str> {
81    match ext {
82        "rs" => Some("rust"),
83        "ts" | "tsx" => Some("typescript"),
84        "js" | "jsx" | "mjs" | "cjs" => Some("javascript"),
85        "py" | "pyi" => Some("python"),
86        "go" => Some("go"),
87        "java" => Some("java"),
88        "kt" | "kts" => Some("kotlin"),
89        "rb" => Some("ruby"),
90        "c" | "h" => Some("c"),
91        "cpp" | "cxx" | "cc" | "hpp" => Some("cpp"),
92        "cs" => Some("csharp"),
93        _ => None,
94    }
95}
96
97pub fn find_binary_in_path(binary: &str) -> Option<PathBuf> {
98    let path_var = std::env::var("PATH").ok()?;
99    for dir in std::env::split_paths(&path_var) {
100        let candidate = dir.join(binary);
101        if candidate.is_file() {
102            return Some(candidate);
103        }
104        if cfg!(windows) {
105            let exe = dir.join(format!("{binary}.exe"));
106            if exe.is_file() {
107                return Some(exe);
108            }
109        }
110    }
111    None
112}
113
114pub fn install_hint_for_language(language: &str) -> &'static str {
115    for info in KNOWN_SERVERS {
116        if info.language == language {
117            return info.install_hint;
118        }
119    }
120    "No install instructions available for this language server."
121}
122
123pub fn binary_for_language(language: &str) -> Option<&'static str> {
124    for info in KNOWN_SERVERS {
125        if info.language == language {
126            return Some(info.binary);
127        }
128    }
129    None
130}
131
132pub fn check_server_available(language: &str) -> Result<PathBuf, String> {
133    let servers = default_servers();
134    let config = servers
135        .get(language)
136        .ok_or_else(|| format!("No LSP server configured for '{language}'"))?;
137
138    find_binary_in_path(&config.command).ok_or_else(|| {
139        let hint = install_hint_for_language(language);
140        format!(
141            "Language server '{}' not found in PATH.\n\
142             \n\
143             ctx_refactor requires an external language server for '{}' files.\n\
144             Install it with:\n\
145             \n\
146             \x20   {}\n\
147             \n\
148             Then retry. This is optional — ctx_search and ctx_graph work without it.",
149            config.command, language, hint
150        )
151    })
152}