pub struct LanguageMapping {
pub language: &'static str,
pub extensions: &'static [&'static str],
pub backend: &'static str,
pub spawn_args: &'static [&'static str],
}
pub const DEFAULT_LANGUAGES: &[LanguageMapping] = &[
LanguageMapping {
language: "rust",
extensions: &["rs"],
backend: "rust-analyzer",
spawn_args: &[],
},
LanguageMapping {
language: "go",
extensions: &["go"],
backend: "gopls",
spawn_args: &[],
},
LanguageMapping {
language: "typescript",
extensions: &["ts", "tsx"],
backend: "typescript-language-server",
spawn_args: &["--stdio"],
},
LanguageMapping {
language: "javascript",
extensions: &["js", "jsx"],
backend: "typescript-language-server",
spawn_args: &["--stdio"],
},
LanguageMapping {
language: "python",
extensions: &["py"],
backend: "basedpyright-langserver",
spawn_args: &["--stdio"],
},
LanguageMapping {
language: "c",
extensions: &["c", "h"],
backend: "clangd",
spawn_args: &[],
},
LanguageMapping {
language: "cpp",
extensions: &["cpp", "cc", "cxx", "hpp", "hxx"],
backend: "clangd",
spawn_args: &[],
},
];
pub fn lookup_by_extension(ext: &str) -> Option<(&'static str, String)> {
for mapping in DEFAULT_LANGUAGES {
if mapping.extensions.contains(&ext) {
let cmd = spawn_cmd(mapping);
return Some((mapping.language, cmd));
}
}
None
}
fn spawn_cmd(mapping: &LanguageMapping) -> String {
if mapping.spawn_args.is_empty() {
mapping.backend.to_string()
} else {
format!("{} {}", mapping.backend, mapping.spawn_args.join(" "))
}
}
pub fn generate_language_table() -> String {
let mut entries = Vec::new();
for mapping in DEFAULT_LANGUAGES {
let available = which(mapping.backend);
let exts: Vec<String> = mapping
.extensions
.iter()
.map(|e| format!("`.{e}`"))
.collect();
let status = if available { "" } else { " (not installed)" };
entries.push(format!(
"{} → `{}`{status}",
exts.join(" "),
mapping.backend,
));
}
entries.join(" | ")
}
fn which(cmd: &str) -> bool {
std::process::Command::new("which")
.arg(cmd)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}