diff --git a/Cargo.lock b/Cargo.lock index e1c7451343..4637613580 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3665,7 +3665,6 @@ dependencies = [ "pretty_assertions", "project", "rpc", - "semver", "serde", "serde_json", "settings", diff --git a/crates/copilot/Cargo.toml b/crates/copilot/Cargo.toml index 4d2ffde10c..0d9d9ed1e6 100644 --- a/crates/copilot/Cargo.toml +++ b/crates/copilot/Cargo.toml @@ -40,7 +40,6 @@ node_runtime.workspace = true parking_lot.workspace = true paths.workspace = true project.workspace = true -semver.workspace = true serde.workspace = true serde_json.workspace = true settings.workspace = true diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 4a08cf2803..4fa41fc8cb 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -27,7 +27,6 @@ use parking_lot::Mutex; use project::project_settings::ProjectSettings; use project::{DisableAiSettings, Project}; use request::DidChangeStatus; -use semver::Version; use serde_json::json; use settings::{Settings, SettingsStore}; use std::{ @@ -567,17 +566,11 @@ impl Copilot { cx: &mut AsyncApp, ) { let start_language_server = async { - let server_path = get_copilot_lsp(fs, node_runtime.clone()).await?; - let node_path = node_runtime.binary_path().await?; - ensure_node_version_for_copilot(&node_path).await?; + let server_path = get_copilot_lsp(fs, node_runtime).await?; - let arguments: Vec = vec![ - "--experimental-sqlite".into(), - server_path.into(), - "--stdio".into(), - ]; + let arguments: Vec = vec!["--stdio".into()]; let binary = LanguageServerBinary { - path: node_path, + path: server_path, arguments, env, }; @@ -1396,44 +1389,6 @@ async fn clear_copilot_config_dir() { remove_matching(copilot_chat::copilot_chat_config_dir(), |_| true).await } -async fn ensure_node_version_for_copilot(node_path: &Path) -> anyhow::Result<()> { - const MIN_COPILOT_NODE_VERSION: Version = Version::new(20, 8, 0); - - log::info!("Checking Node.js version for Copilot at: {:?}", node_path); - - let output = util::command::new_command(node_path) - .arg("--version") - .output() - .await - .with_context(|| format!("checking Node.js version at {:?}", node_path))?; - - if !output.status.success() { - anyhow::bail!( - "failed to run node --version for Copilot. stdout: {}, stderr: {}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr), - ); - } - - let version_str = String::from_utf8_lossy(&output.stdout); - let version = Version::parse(version_str.trim().trim_start_matches('v')) - .with_context(|| format!("parsing Node.js version from '{}'", version_str.trim()))?; - - if version < MIN_COPILOT_NODE_VERSION { - anyhow::bail!( - "GitHub Copilot language server requires Node.js {MIN_COPILOT_NODE_VERSION} or later, but found {version}. \ - Please update your Node.js version or configure a different Node.js path in settings." - ); - } - - log::info!( - "Node.js version {} meets Copilot requirements (>= {})", - version, - MIN_COPILOT_NODE_VERSION - ); - Ok(()) -} - async fn get_copilot_lsp(fs: Arc, node_runtime: NodeRuntime) -> anyhow::Result { const PACKAGE_NAME: &str = "@github/copilot-language-server"; const SERVER_PATH: &str = @@ -1443,17 +1398,19 @@ async fn get_copilot_lsp(fs: Arc, node_runtime: NodeRuntime) -> anyhow:: .npm_package_latest_version(PACKAGE_NAME) .await?; let server_path = paths::copilot_dir().join(SERVER_PATH); + let binary_path = copilot_lsp_native_binary_path()?; fs.create_dir(paths::copilot_dir()).await?; - let should_install = node_runtime - .should_install_npm_package( - PACKAGE_NAME, - &server_path, - paths::copilot_dir(), - VersionStrategy::Latest(&latest_version), - ) - .await; + let should_install = !fs.is_file(&binary_path).await + || node_runtime + .should_install_npm_package( + PACKAGE_NAME, + &server_path, + paths::copilot_dir(), + VersionStrategy::Latest(&latest_version), + ) + .await; if should_install { node_runtime .npm_install_packages( @@ -1463,7 +1420,40 @@ async fn get_copilot_lsp(fs: Arc, node_runtime: NodeRuntime) -> anyhow:: .await?; } - Ok(server_path) + if fs.is_file(&binary_path).await { + return Ok(binary_path); + } + + anyhow::bail!("GitHub Copilot native language server binary was not installed") +} + +fn copilot_lsp_native_binary_path() -> anyhow::Result { + let platform = match env::consts::OS { + "linux" => "linux", + "macos" => "darwin", + "windows" => "win32", + platform => anyhow::bail!("unsupported Copilot language server platform: {platform}"), + }; + let architecture = match env::consts::ARCH { + "aarch64" => "arm64", + "x86_64" => "x64", + architecture => { + anyhow::bail!("unsupported Copilot language server architecture: {architecture}") + } + }; + + let package_name = format!("copilot-language-server-{platform}-{architecture}"); + + let executable_name = if cfg!(target_os = "windows") { + "copilot-language-server.exe" + } else { + "copilot-language-server" + }; + Ok(paths::copilot_dir() + .join("node_modules") + .join("@github") + .join(package_name) + .join(executable_name)) } #[cfg(test)]