oxc-zed 0.4.3

Oxc Zed Extension
Documentation
use crate::lsp::ZedLspSupport;
use log::debug;
use std::path::{Path, PathBuf};
use zed_extension_api::serde_json::Value;
use zed_extension_api::settings::LspSettings;
use zed_extension_api::{Command, EnvVars, LanguageServerId, Result, Worktree, node_binary_path};

pub struct ZedOxlintLsp {}

impl ZedOxlintLsp {
    pub fn new() -> Self {
        ZedOxlintLsp {}
    }
}

impl ZedLspSupport for ZedOxlintLsp {
    fn get_package_name(&self) -> String {
        "oxlint".to_string()
    }

    fn language_server_command(
        &self,
        language_server_id: &LanguageServerId,
        worktree: &Worktree,
    ) -> Result<Command> {
        let settings = LspSettings::for_worktree(language_server_id.as_ref(), worktree)?;
        debug!("Oxlint language_server_command LspSettings: {settings:?}");

        let mut args = vec![
            self.get_resolved_exe_path(worktree)?
                .to_string_lossy()
                .to_string(),
            "--lsp".to_string(),
        ];
        let mut command = node_binary_path()?;
        let mut env = EnvVars::default();
        if let Some(binary) = settings.binary {
            if (binary.path.is_some() && binary.arguments.is_none())
                || (binary.path.is_none() && binary.arguments.is_some())
            {
                return Err(
                    "When supplying binary.arguments, binary.path must be supplied (or vice-versa).".to_string(),
                );
            }

            if binary.arguments.is_some() {
                args = binary.arguments.unwrap();
            }
            if binary.path.is_some() {
                command = binary.path.unwrap();
            }
            env = normalize_env(binary.env, worktree)?;
        }

        Ok(Command { command, args, env })
    }

    fn language_server_initialization_options(
        &self,
        language_server_id: &LanguageServerId,
        worktree: &Worktree,
    ) -> Result<Option<Value>> {
        let settings = LspSettings::for_worktree(language_server_id.as_ref(), worktree)?;
        debug!("Oxlint language_server_initialization_options LspSettings: {settings:?}");

        Ok(settings.initialization_options)
    }

    fn language_server_workspace_configuration(
        &self,
        language_server_id: &LanguageServerId,
        worktree: &Worktree,
    ) -> Result<Option<Value>> {
        let settings = LspSettings::for_worktree(language_server_id.as_ref(), worktree)?;
        debug!("Oxfmt language_server_workspace_configuration LspSettings: {settings:?}");

        Ok(settings
            .initialization_options
            .as_ref()
            .and_then(|v| v.get("settings").cloned()))
    }
}

#[expect(clippy::disallowed_types, reason = "HashMap comes from Zed API")]
fn normalize_env(
    env_map: Option<std::collections::HashMap<String, String>>,
    worktree: &Worktree,
) -> Result<EnvVars> {
    let mut env = env_map.unwrap_or_default();
    if let Some(tsgolint_path) = env.get_mut("OXLINT_TSGOLINT_PATH") {
        let path_buf = PathBuf::from(tsgolint_path.as_str());

        let resolved = if path_buf.is_absolute() {
            path_buf
        } else {
            let root_path = worktree.root_path();
            let worktree_root = Path::new(root_path.as_str());
            worktree_root.join(&path_buf)
        };

        *tsgolint_path = resolved.to_string_lossy().to_string();
    }

    Ok(env.into_iter().collect())
}