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        "rb" => Some("ruby"),
89        "c" | "h" => Some("c"),
90        "cpp" | "cxx" | "cc" | "hpp" => Some("cpp"),
91        "cs" => Some("csharp"),
92        _ => None,
93    }
94}
95
96pub fn find_binary_in_path(binary: &str) -> Option<PathBuf> {
97    let path_var = std::env::var("PATH").ok()?;
98    for dir in std::env::split_paths(&path_var) {
99        let candidate = dir.join(binary);
100        if candidate.is_file() {
101            return Some(candidate);
102        }
103        if cfg!(windows) {
104            let exe = dir.join(format!("{binary}.exe"));
105            if exe.is_file() {
106                return Some(exe);
107            }
108        }
109    }
110    None
111}
112
113pub fn install_hint_for_language(language: &str) -> &'static str {
114    for info in KNOWN_SERVERS {
115        if info.language == language {
116            return info.install_hint;
117        }
118    }
119    "No install instructions available for this language server."
120}
121
122pub fn binary_for_language(language: &str) -> Option<&'static str> {
123    for info in KNOWN_SERVERS {
124        if info.language == language {
125            return Some(info.binary);
126        }
127    }
128    None
129}
130
131pub fn check_server_available(language: &str) -> Result<PathBuf, String> {
132    let servers = default_servers();
133    let config = servers
134        .get(language)
135        .ok_or_else(|| format!("No LSP server configured for '{language}'"))?;
136
137    find_binary_in_path(&config.command).ok_or_else(|| {
138        let hint = install_hint_for_language(language);
139        format!(
140            "Language server '{}' not found in PATH.\n\
141             \n\
142             ctx_refactor requires an external language server for '{}' files.\n\
143             Install it with:\n\
144             \n\
145             \x20   {}\n\
146             \n\
147             Then retry. This is optional — ctx_search and ctx_graph work without it.",
148            config.command, language, hint
149        )
150    })
151}