magi-code 0.96.2

Repository-aware CLI coding agent for terminal work
Documentation
use crate::{
    config::{LspServerConfig, LspSettings},
    lsp::DocumentVersion,
};
use std::{collections::BTreeMap, path::Path};

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct LanguageRoute {
    pub(crate) server_id: String,
    pub(crate) language_id: String,
    pub(crate) config: LspServerConfig,
}

#[derive(Debug, Default, Clone)]
pub(crate) struct DocumentVersions {
    by_path: BTreeMap<std::path::PathBuf, DocumentVersion>,
}

impl DocumentVersions {
    pub(crate) fn next_for(&mut self, path: &Path) -> DocumentVersion {
        let entry = self
            .by_path
            .entry(path.to_path_buf())
            .or_insert(DocumentVersion(0));
        entry.0 += 1;
        *entry
    }
}

pub(crate) fn default_servers() -> BTreeMap<String, LspServerConfig> {
    BTreeMap::from([
        (
            "rust-analyzer".to_string(),
            LspServerConfig {
                command: "rust-analyzer".to_string(),
                args: Vec::new(),
                enabled: true,
            },
        ),
        (
            "typescript-language-server".to_string(),
            LspServerConfig {
                command: "typescript-language-server".to_string(),
                args: vec!["--stdio".to_string()],
                enabled: true,
            },
        ),
        (
            "pyright-langserver".to_string(),
            LspServerConfig {
                command: "pyright-langserver".to_string(),
                args: vec!["--stdio".to_string()],
                enabled: true,
            },
        ),
        (
            "gopls".to_string(),
            LspServerConfig {
                command: "gopls".to_string(),
                args: Vec::new(),
                enabled: true,
            },
        ),
    ])
}

pub(crate) fn route_for_path(path: &Path, settings: &LspSettings) -> Option<LanguageRoute> {
    let language_id = language_id_for_path(path)?.to_string();
    let server_id = server_id_for_language(&language_id)?.to_string();
    let mut servers = default_servers();
    for (key, config) in &settings.servers {
        if servers.contains_key(key) {
            servers.insert(key.clone(), config.clone());
        }
    }
    let config = servers.remove(&server_id)?;
    config.enabled.then_some(LanguageRoute {
        server_id,
        language_id,
        config,
    })
}

pub(crate) fn language_id_for_path(path: &Path) -> Option<&'static str> {
    match path.extension().and_then(|extension| extension.to_str())? {
        "rs" => Some("rust"),
        "ts" | "tsx" => Some("typescript"),
        "js" | "jsx" => Some("javascript"),
        "py" => Some("python"),
        "go" => Some("go"),
        _ => None,
    }
}

fn server_id_for_language(language_id: &str) -> Option<&'static str> {
    match language_id {
        "rust" => Some("rust-analyzer"),
        "typescript" | "javascript" => Some("typescript-language-server"),
        "python" => Some("pyright-langserver"),
        "go" => Some("gopls"),
        _ => None,
    }
}