//! Static language server definitions and core types. //! //! Defines the set of supported LSP servers, their install tiers, and the //! result/enum types used across the provisioner. /// Optional progress callback type (non-owning, caller ensures liveness /// for the duration of the provisioning call). /// Intended to be hooked up to a UI toast / status-bar mechanism. pub type ProgressFn<'a> = Option<&'a dyn Fn(&str)>; /// Result of attempting to make a single language server available. /// /// The caller should switch on this variant: `AlreadyAvailable` and /// Installed both mean the binary can be launched; Failed means we /// gave up and the user needs to install manually (see `manual_instructions`). #[derive(Debug, Clone)] pub enum ProvisionResult { /// Binary was already on PATH — no install was needed. AlreadyAvailable { server_name: String, language: String, binary_path: String, }, /// Provisioner successfully installed the binary during this run. Installed { server_name: String, language: String, binary_path: String, }, /// Every install tier failed. Tells the user how to install by hand. Failed { language: String, server_name: String, reason: String, }, } /// Sentinel command names used by `provision_single` to detect "download" /// tiers (which are dispatched to `download_*` helpers rather than /// `run_command`). Kept as constants so `supported_servers` stays readable. pub(super) const DOWNLOAD_RUST_BIN: &str = "__download_rust_analyzer__"; pub(super) const DOWNLOAD_JDTLS: &str = "__download_jdtls__"; /// Static description of a single language server: how to detect it, /// what file extensions it handles, and how to install it. #[derive(Debug, Clone)] pub struct LanguageServerDef { /// Human-readable server name (e.g. "rust-analyzer"). pub name: String, /// LSP language identifier (e.g. "rust"). pub language: String, /// File extensions this server handles (with leading dot). pub extensions: Vec, /// Candidate binary names — the provisioner accepts whichever appears on PATH. pub binary_names: Vec, /// Install strategies, tried in order until one succeeds. pub install_tiers: Vec, } /// A single install attempt: a command (plus args) gated by a prerequisite. /// /// `requires` lists binaries that must already be on PATH for this tier /// to be considered. If any required binary is missing, the tier is /// skipped (not attempted) so we don't produce misleading failures /// like "rustup: command not found" when the real fix was to install /// rustup first. #[derive(Debug, Clone)] pub struct InstallTier { /// Short human-readable label, e.g. "rustup component". pub label: String, /// Binaries that must be available before this tier is attempted. pub requires: Vec, /// Command to run. pub command: String, /// Arguments to pass to the command. pub args: Vec, } /// Return the static set of supported language servers. /// /// The order is significant: it determines provisioning order and /// the order results appear in `provision_all_with_progress()`. Tier 1 paths are /// the canonical/idiomatic install for each ecosystem; later tiers /// are fallbacks for hosts that lack the primary tooling. /// /// Why hard-coded rather than loaded from settings: the set is small, /// changes rarely, and bundling it lets the provisioner run before any /// user config has been read (e.g. on first launch). pub fn supported_servers() -> Vec { vec![ LanguageServerDef { name: "rust-analyzer".to_string(), language: "rust".to_string(), extensions: vec![".rs".to_string()], binary_names: vec!["rust-analyzer".to_string()], install_tiers: vec![ InstallTier { label: "rustup component".to_string(), requires: vec!["rustup".to_string()], command: "rustup".to_string(), args: vec![ "component".to_string(), "add".to_string(), "rust-analyzer".to_string(), ], }, InstallTier { label: "pacman".to_string(), requires: vec!["pacman".to_string()], command: "pacman".to_string(), args: vec![ "-S".to_string(), "--noconfirm".to_string(), "--needed".to_string(), "rust-analyzer".to_string(), ], }, InstallTier { label: "brew".to_string(), requires: vec!["brew".to_string()], command: "brew".to_string(), args: vec!["install".to_string(), "rust-analyzer".to_string()], }, InstallTier { label: "cargo install".to_string(), requires: vec!["cargo".to_string()], command: "cargo".to_string(), args: vec![ "install".to_string(), "--locked".to_string(), "rust-analyzer".to_string(), ], }, InstallTier { label: "download prebuilt".to_string(), requires: vec!["curl".to_string(), "tar".to_string()], command: DOWNLOAD_RUST_BIN.to_string(), args: vec![], }, ], }, LanguageServerDef { name: "typescript-language-server".to_string(), language: "typescript".to_string(), extensions: vec![ ".ts".to_string(), ".tsx".to_string(), ".js".to_string(), ".jsx".to_string(), ], binary_names: vec!["typescript-language-server".to_string()], install_tiers: vec![InstallTier { label: "npm global".to_string(), requires: vec!["npm".to_string()], command: "npm".to_string(), args: vec![ "install".to_string(), "-g".to_string(), "typescript".to_string(), "typescript-language-server".to_string(), ], }], }, LanguageServerDef { name: "gopls".to_string(), language: "go".to_string(), extensions: vec![".go".to_string()], binary_names: vec!["gopls".to_string()], install_tiers: vec![InstallTier { label: "go install".to_string(), requires: vec!["go".to_string()], command: "go".to_string(), args: vec![ "install".to_string(), "golang.org/x/tools/gopls@latest".to_string(), ], }], }, LanguageServerDef { name: "jdtls".to_string(), language: "java".to_string(), extensions: vec![".java".to_string()], binary_names: vec![ "jdtls".to_string(), "eclipse-jdt-ls".to_string(), "jdtls-launcher".to_string(), ], install_tiers: vec![ InstallTier { label: "pacman".to_string(), requires: vec!["java".to_string(), "pacman".to_string()], command: "pacman".to_string(), args: vec![ "-S".to_string(), "--noconfirm".to_string(), "--needed".to_string(), "eclipse-jdt-ls".to_string(), ], }, InstallTier { label: "apt".to_string(), requires: vec!["java".to_string(), "apt".to_string()], command: "sudo".to_string(), args: vec![ "apt".to_string(), "install".to_string(), "-y".to_string(), "eclipse-jdt-ls".to_string(), ], }, InstallTier { label: "brew".to_string(), requires: vec!["java".to_string(), "brew".to_string()], command: "brew".to_string(), args: vec!["install".to_string(), "jdtls".to_string()], }, InstallTier { label: "download from eclipse".to_string(), requires: vec!["java".to_string(), "curl".to_string(), "tar".to_string()], command: DOWNLOAD_JDTLS.to_string(), args: vec![], }, ], }, ] }