pub mod copilot_chat;mod copilot_completion_provider;pub mod request;mod sign_in; use ::fs::Fs;use anyhow::{anyhow, Context as _, Result};use async_compression::futures::bufread::GzipDecoder;use async_tar::Archive;use collections::{HashMap, HashSet};use command_palette_hooks::CommandPaletteFilter;use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};use gpui::{ actions, App, AppContext as _, AsyncApp, Context, Entity, EntityId, EventEmitter, Global, Task, WeakEntity,};use http_client::github::get_release_by_tag_name;use http_client::HttpClient;use language::language_settings::CopilotSettings;use language::{ language_settings::{all_language_settings, language_settings, EditPredictionProvider}, point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16, ToPointUtf16,};use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId, LanguageServerName};use node_runtime::NodeRuntime;use parking_lot::Mutex;use request::StatusNotification;use settings::SettingsStore;use smol::{fs, io::BufReader, stream::StreamExt};use std::{ any::TypeId, env, ffi::OsString, mem, ops::Range, path::{Path, PathBuf}, sync::Arc,};use util::{fs::remove_matching, maybe, ResultExt}; pub use crate::copilot_completion_provider::CopilotCompletionProvider;pub use crate::sign_in::{initiate_sign_in, CopilotCodeVerification}; actions!( copilot, [ Suggest, NextSuggestion, PreviousSuggestion, Reinstall, SignIn, SignOut ]); pub fn init( new_server_id: LanguageServerId, fs: Arc<dyn Fs>, http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, cx: &mut App,) { copilot_chat::init(fs, http.clone(), cx); let copilot = cx.new({ let node_runtime = node_runtime.clone(); move |cx| Copilot::start(new_server_id, http, node_runtime, cx) }); Copilot::set_global(copilot.clone(), cx); cx.observe(&copilot, |handle, cx| { let copilot_action_types = [ TypeId::of::<Suggest>(), TypeId::of::<NextSuggestion>(), TypeId::of::<PreviousSuggestion>(), TypeId::of::<Reinstall>(), ]; let copilot_auth_action_types = [TypeId::of::<SignOut>()]; let copilot_no_auth_action_types = [TypeId::of::<SignIn>()]; let status = handle.read(cx).status(); let filter = CommandPaletteFilter::global_mut(cx); match status { Status::Disabled => { filter.hide_action_types(&copilot_action_types); filter.hide_action_types(&copilot_auth_action_types); filter.hide_action_types(&copilot_no_auth_action_types); } Status::Authorized => { filter.hide_action_types(&copilot_no_auth_action_types); filter.show_action_types( copilot_action_types .iter() .chain(&copilot_auth_action_types), ); } _ => { filter.hide_action_types(&copilot_action_types); filter.hide_action_types(&copilot_auth_action_types); filter.show_action_types(copilot_no_auth_action_types.iter()); } } }) .detach(); cx.on_action(|_: &SignIn, cx| { if let Some(copilot) = Copilot::global(cx) { copilot .update(cx, |copilot, cx| copilot.sign_in(cx)) .detach_and_log_err(cx); } }); cx.on_action(|_: &SignOut, cx| { if let Some(copilot) = Copilot::global(cx) { copilot .update(cx, |copilot, cx| copilot.sign_out(cx)) .detach_and_log_err(cx); } }); cx.on_action(|_: &Reinstall, cx| { if let Some(copilot) = Copilot::global(cx) { copilot .update(cx, |copilot, cx| copilot.reinstall(cx)) .detach(); } });} enum CopilotServer { Disabled, Starting { task: Shared<Task<()>> }, Error(Arc<str>), Running(RunningCopilotServer),} impl CopilotServer { fn as_authenticated(&mut self) -> Result<&mut RunningCopilotServer> { let server = self.as_running()?; if matches!(server.sign_in_status, SignInStatus::Authorized { .. }) { Ok(server) } else { Err(anyhow!("must sign in before using copilot")) } } fn as_running(&mut self) -> Result<&mut RunningCopilotServer> { match self { CopilotServer::Starting { .. } => Err(anyhow!("copilot is still starting")), CopilotServer::Disabled => Err(anyhow!("copilot is disabled")), CopilotServer::Error(error) => Err(anyhow!( "copilot was not started because of an error: {}", error )), CopilotServer::Running(server) => Ok(server), } }} struct RunningCopilotServer { lsp: Arc<LanguageServer>, sign_in_status: SignInStatus, registered_buffers: HashMap<EntityId, RegisteredBuffer>,} #[derive(Clone, Debug)]enum SignInStatus { Authorized, Unauthorized, SigningIn { prompt: Option<request::PromptUserDeviceFlow>, task: Shared<Task<Result<(), Arc<anyhow::Error>>>>, }, SignedOut,} #[derive(Debug, Clone)]pub enum Status { Starting { task: Shared<Task<()>>, }, Error(Arc<str>), Disabled, SignedOut, SigningIn { prompt: Option<request::PromptUserDeviceFlow>, }, Unauthorized, Authorized,} impl Status { pub fn is_authorized(&self) -> bool { matches!(self, Status::Authorized) } pub fn is_disabled(&self) -> bool { matches!(self, Status::Disabled) }} struct RegisteredBuffer { uri: lsp::Url, language_id: String, snapshot: BufferSnapshot, snapshot_version: i32, _subscriptions: [gpui::Subscription; 2], pending_buffer_change: Task<Option<()>>,} impl RegisteredBuffer { fn report_changes( &mut self, buffer: &Entity<Buffer>, cx: &mut Context<Copilot>, ) -> oneshot::Receiver<(i32, BufferSnapshot)> { let (done_tx, done_rx) = oneshot::channel(); if buffer.read(cx).version() == self.snapshot.version { let _ = done_tx.send((self.snapshot_version, self.snapshot.clone())); } else { let buffer = buffer.downgrade(); let id = buffer.entity_id(); let prev_pending_change = mem::replace(&mut self.pending_buffer_change, Task::ready(None)); self.pending_buffer_change = cx.spawn(move |copilot, mut cx| async move { prev_pending_change.await; let old_version = copilot .update(&mut cx, |copilot, _| { let server = copilot.server.as_authenticated().log_err()?; let buffer = server.registered_buffers.get_mut(&id)?; Some(buffer.snapshot.version.clone()) }) .ok()??; let new_snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot()).ok()?; let content_changes = cx .background_spawn({ let new_snapshot = new_snapshot.clone(); async move { new_snapshot .edits_since::<(PointUtf16, usize)>(&old_version) .map(|edit| { let edit_start = edit.new.start.0; let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0); let new_text = new_snapshot .text_for_range(edit.new.start.1..edit.new.end.1) .collect(); lsp::TextDocumentContentChangeEvent { range: Some(lsp::Range::new( point_to_lsp(edit_start), point_to_lsp(edit_end), )), range_length: None, text: new_text, } }) .collect::<Vec<_>>() } }) .await; copilot .update(&mut cx, |copilot, _| { let server = copilot.server.as_authenticated().log_err()?; let buffer = server.registered_buffers.get_mut(&id)?; if !content_changes.is_empty() { buffer.snapshot_version += 1; buffer.snapshot = new_snapshot; server .lsp .notify::<lsp::notification::DidChangeTextDocument>( &lsp::DidChangeTextDocumentParams { text_document: lsp::VersionedTextDocumentIdentifier::new( buffer.uri.clone(), buffer.snapshot_version, ), content_changes, }, ) .log_err(); } let _ = done_tx.send((buffer.snapshot_version, buffer.snapshot.clone())); Some(()) }) .ok()?; Some(()) }); } done_rx }} #[derive(Debug)]pub struct Completion { pub uuid: String, pub range: Range<Anchor>, pub text: String,} pub struct Copilot { http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, server: CopilotServer, buffers: HashSet<WeakEntity<Buffer>>, server_id: LanguageServerId, _subscription: gpui::Subscription,} pub enum Event { CopilotLanguageServerStarted, CopilotAuthSignedIn, CopilotAuthSignedOut,} impl EventEmitter<Event> for Copilot {} struct GlobalCopilot(Entity<Copilot>); impl Global for GlobalCopilot {} impl Copilot { pub fn global(cx: &App) -> Option<Entity<Self>> { cx.try_global::<GlobalCopilot>() .map(|model| model.0.clone()) } pub fn set_global(copilot: Entity<Self>, cx: &mut App) { cx.set_global(GlobalCopilot(copilot)); } fn start( new_server_id: LanguageServerId, http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, cx: &mut Context<Self>, ) -> Self { let mut this = Self { server_id: new_server_id, http, node_runtime, server: CopilotServer::Disabled, buffers: Default::default(), _subscription: cx.on_app_quit(Self::shutdown_language_server), }; this.enable_or_disable_copilot(cx); cx.observe_global::<SettingsStore>(move |this, cx| this.enable_or_disable_copilot(cx)) .detach(); this } fn shutdown_language_server(&mut self, _cx: &mut Context<Self>) -> impl Future<Output = ()> { let shutdown = match mem::replace(&mut self.server, CopilotServer::Disabled) { CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })), _ => None, }; async move { if let Some(shutdown) = shutdown { shutdown.await; } } } fn enable_or_disable_copilot(&mut self, cx: &mut Context<Self>) { let server_id = self.server_id; let http = self.http.clone(); let node_runtime = self.node_runtime.clone(); let language_settings = all_language_settings(None, cx); if language_settings.edit_predictions.provider == EditPredictionProvider::Copilot { if matches!(self.server, CopilotServer::Disabled) { let env = self.build_env(&language_settings.edit_predictions.copilot); let start_task = cx .spawn(move |this, cx| { Self::start_language_server(server_id, http, node_runtime, env, this, cx) }) .shared(); self.server = CopilotServer::Starting { task: start_task }; cx.notify(); } } else { self.server = CopilotServer::Disabled; cx.notify(); } } fn build_env(&self, copilot_settings: &CopilotSettings) -> Option<HashMap<String, String>> { let proxy_url = copilot_settings.proxy.clone()?; let no_verify = copilot_settings.proxy_no_verify; let http_or_https_proxy = if proxy_url.starts_with("http:") { "HTTP_PROXY" } else if proxy_url.starts_with("https:") { "HTTPS_PROXY" } else { log::error!( "Unsupported protocol scheme for language server proxy (must be http or https)" ); return None; }; let mut env = HashMap::default(); env.insert(http_or_https_proxy.to_string(), proxy_url); if let Some(true) = no_verify { env.insert("NODE_TLS_REJECT_UNAUTHORIZED".to_string(), "0".to_string()); }; Some(env) } #[cfg(any(test, feature = "test-support"))] pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) { use lsp::FakeLanguageServer; use node_runtime::NodeRuntime; let (server, fake_server) = FakeLanguageServer::new( LanguageServerId(0), LanguageServerBinary { path: "path/to/copilot".into(), arguments: vec![], env: None, }, "copilot".into(), Default::default(), cx.to_async(), ); let http = http_client::FakeHttpClient::create(|_| async { unreachable!() }); let node_runtime = NodeRuntime::unavailable(); let this = cx.new(|cx| Self { server_id: LanguageServerId(0), http: http.clone(), node_runtime, server: CopilotServer::Running(RunningCopilotServer { lsp: Arc::new(server), sign_in_status: SignInStatus::Authorized, registered_buffers: Default::default(), }), _subscription: cx.on_app_quit(Self::shutdown_language_server), buffers: Default::default(), }); (this, fake_server) } async fn start_language_server( new_server_id: LanguageServerId, http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, env: Option<HashMap<String, String>>, this: WeakEntity<Self>, mut cx: AsyncApp, ) { let start_language_server = async { let server_path = get_copilot_lsp(http).await?; let node_path = node_runtime.binary_path().await?; let arguments: Vec<OsString> = vec![server_path.into(), "--stdio".into()]; let binary = LanguageServerBinary { path: node_path, arguments, env, }; let root_path = if cfg!(target_os = "windows") { Path::new("C:/") } else { Path::new("/") }; let server_name = LanguageServerName("copilot".into()); let server = LanguageServer::new( Arc::new(Mutex::new(None)), new_server_id, server_name, binary, root_path, None, Default::default(), cx.clone(), )?; server .on_notification::<StatusNotification, _>(|_, _| { /* Silence the notification */ }) .detach(); let configuration = lsp::DidChangeConfigurationParams { settings: Default::default(), }; let server = cx .update(|cx| { let params = server.default_initialize_params(cx); server.initialize(params, configuration.into(), cx) })? .await?; let status = server .request::<request::CheckStatus>(request::CheckStatusParams { local_checks_only: false, }) .await?;Lines 1–500 of 1317. Load more
pub mod copilot_chat; mod copilot_completion_provider; pub mod request; mod sign_in; use ::fs::Fs; use anyhow::{anyhow, Context as _, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; use collections::{HashMap, HashSet}; use command_palette_hooks::CommandPaletteFilter; use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt}; use gpui::{ actions, App, AppContext as _, AsyncApp, Context, Entity, EntityId, EventEmitter, Global, Task, WeakEntity, }; use http_client::github::get_release_by_tag_name; use http_client::HttpClient; use language::language_settings::CopilotSettings; use language::{ language_settings::{all_language_settings, language_settings, EditPredictionProvider}, point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16, ToPointUtf16, }; use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId, LanguageServerName}; use node_runtime::NodeRuntime; use parking_lot::Mutex; use request::StatusNotification; use settings::SettingsStore; use smol::{fs, io::BufReader, stream::StreamExt}; use std::{ any::TypeId, env, ffi::OsString, mem, ops::Range, path::{Path, PathBuf}, sync::Arc, }; use util::{fs::remove_matching, maybe, ResultExt}; pub use crate::copilot_completion_provider::CopilotCompletionProvider; pub use crate::sign_in::{initiate_sign_in, CopilotCodeVerification}; actions!( copilot, [ Suggest, NextSuggestion, PreviousSuggestion, Reinstall, SignIn, SignOut ] ); pub fn init( new_server_id: LanguageServerId, fs: Arc<dyn Fs>, http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, cx: &mut App, ) { copilot_chat::init(fs, http.clone(), cx); let copilot = cx.new({ let node_runtime = node_runtime.clone(); move |cx| Copilot::start(new_server_id, http, node_runtime, cx) }); Copilot::set_global(copilot.clone(), cx); cx.observe(&copilot, |handle, cx| { let copilot_action_types = [ TypeId::of::<Suggest>(), TypeId::of::<NextSuggestion>(), TypeId::of::<PreviousSuggestion>(), TypeId::of::<Reinstall>(), ]; let copilot_auth_action_types = [TypeId::of::<SignOut>()]; let copilot_no_auth_action_types = [TypeId::of::<SignIn>()]; let status = handle.read(cx).status(); let filter = CommandPaletteFilter::global_mut(cx); match status { Status::Disabled => { filter.hide_action_types(&copilot_action_types); filter.hide_action_types(&copilot_auth_action_types); filter.hide_action_types(&copilot_no_auth_action_types); } Status::Authorized => { filter.hide_action_types(&copilot_no_auth_action_types); filter.show_action_types( copilot_action_types .iter() .chain(&copilot_auth_action_types), ); } _ => { filter.hide_action_types(&copilot_action_types); filter.hide_action_types(&copilot_auth_action_types); filter.show_action_types(copilot_no_auth_action_types.iter()); } } }) .detach(); cx.on_action(|_: &SignIn, cx| { if let Some(copilot) = Copilot::global(cx) { copilot .update(cx, |copilot, cx| copilot.sign_in(cx)) .detach_and_log_err(cx); } }); cx.on_action(|_: &SignOut, cx| { if let Some(copilot) = Copilot::global(cx) { copilot .update(cx, |copilot, cx| copilot.sign_out(cx)) .detach_and_log_err(cx); } }); cx.on_action(|_: &Reinstall, cx| { if let Some(copilot) = Copilot::global(cx) { copilot .update(cx, |copilot, cx| copilot.reinstall(cx)) .detach(); } }); } enum CopilotServer { Disabled, Starting { task: Shared<Task<()>> }, Error(Arc<str>), Running(RunningCopilotServer), } impl CopilotServer { fn as_authenticated(&mut self) -> Result<&mut RunningCopilotServer> { let server = self.as_running()?; if matches!(server.sign_in_status, SignInStatus::Authorized { .. }) { Ok(server) } else { Err(anyhow!("must sign in before using copilot")) } } fn as_running(&mut self) -> Result<&mut RunningCopilotServer> { match self { CopilotServer::Starting { .. } => Err(anyhow!("copilot is still starting")), CopilotServer::Disabled => Err(anyhow!("copilot is disabled")), CopilotServer::Error(error) => Err(anyhow!( "copilot was not started because of an error: {}", error )), CopilotServer::Running(server) => Ok(server), } } } struct RunningCopilotServer { lsp: Arc<LanguageServer>, sign_in_status: SignInStatus, registered_buffers: HashMap<EntityId, RegisteredBuffer>, } #[derive(Clone, Debug)] enum SignInStatus { Authorized, Unauthorized, SigningIn { prompt: Option<request::PromptUserDeviceFlow>, task: Shared<Task<Result<(), Arc<anyhow::Error>>>>, }, SignedOut, } #[derive(Debug, Clone)] pub enum Status { Starting { task: Shared<Task<()>>, }, Error(Arc<str>), Disabled, SignedOut, SigningIn { prompt: Option<request::PromptUserDeviceFlow>, }, Unauthorized, Authorized, } impl Status { pub fn is_authorized(&self) -> bool { matches!(self, Status::Authorized) } pub fn is_disabled(&self) -> bool { matches!(self, Status::Disabled) } } struct RegisteredBuffer { uri: lsp::Url, language_id: String, snapshot: BufferSnapshot, snapshot_version: i32, _subscriptions: [gpui::Subscription; 2], pending_buffer_change: Task<Option<()>>, } impl RegisteredBuffer { fn report_changes( &mut self, buffer: &Entity<Buffer>, cx: &mut Context<Copilot>, ) -> oneshot::Receiver<(i32, BufferSnapshot)> { let (done_tx, done_rx) = oneshot::channel(); if buffer.read(cx).version() == self.snapshot.version { let _ = done_tx.send((self.snapshot_version, self.snapshot.clone())); } else { let buffer = buffer.downgrade(); let id = buffer.entity_id(); let prev_pending_change = mem::replace(&mut self.pending_buffer_change, Task::ready(None)); self.pending_buffer_change = cx.spawn(move |copilot, mut cx| async move { prev_pending_change.await; let old_version = copilot .update(&mut cx, |copilot, _| { let server = copilot.server.as_authenticated().log_err()?; let buffer = server.registered_buffers.get_mut(&id)?; Some(buffer.snapshot.version.clone()) }) .ok()??; let new_snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot()).ok()?; let content_changes = cx .background_spawn({ let new_snapshot = new_snapshot.clone(); async move { new_snapshot .edits_since::<(PointUtf16, usize)>(&old_version) .map(|edit| { let edit_start = edit.new.start.0; let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0); let new_text = new_snapshot .text_for_range(edit.new.start.1..edit.new.end.1) .collect(); lsp::TextDocumentContentChangeEvent { range: Some(lsp::Range::new( point_to_lsp(edit_start), point_to_lsp(edit_end), )), range_length: None, text: new_text, } }) .collect::<Vec<_>>() } }) .await; copilot .update(&mut cx, |copilot, _| { let server = copilot.server.as_authenticated().log_err()?; let buffer = server.registered_buffers.get_mut(&id)?; if !content_changes.is_empty() { buffer.snapshot_version += 1; buffer.snapshot = new_snapshot; server .lsp .notify::<lsp::notification::DidChangeTextDocument>( &lsp::DidChangeTextDocumentParams { text_document: lsp::VersionedTextDocumentIdentifier::new( buffer.uri.clone(), buffer.snapshot_version, ), content_changes, }, ) .log_err(); } let _ = done_tx.send((buffer.snapshot_version, buffer.snapshot.clone())); Some(()) }) .ok()?; Some(()) }); } done_rx } } #[derive(Debug)] pub struct Completion { pub uuid: String, pub range: Range<Anchor>, pub text: String, } pub struct Copilot { http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, server: CopilotServer, buffers: HashSet<WeakEntity<Buffer>>, server_id: LanguageServerId, _subscription: gpui::Subscription, } pub enum Event { CopilotLanguageServerStarted, CopilotAuthSignedIn, CopilotAuthSignedOut, } impl EventEmitter<Event> for Copilot {} struct GlobalCopilot(Entity<Copilot>); impl Global for GlobalCopilot {} impl Copilot { pub fn global(cx: &App) -> Option<Entity<Self>> { cx.try_global::<GlobalCopilot>() .map(|model| model.0.clone()) } pub fn set_global(copilot: Entity<Self>, cx: &mut App) { cx.set_global(GlobalCopilot(copilot)); } fn start( new_server_id: LanguageServerId, http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, cx: &mut Context<Self>, ) -> Self { let mut this = Self { server_id: new_server_id, http, node_runtime, server: CopilotServer::Disabled, buffers: Default::default(), _subscription: cx.on_app_quit(Self::shutdown_language_server), }; this.enable_or_disable_copilot(cx); cx.observe_global::<SettingsStore>(move |this, cx| this.enable_or_disable_copilot(cx)) .detach(); this } fn shutdown_language_server(&mut self, _cx: &mut Context<Self>) -> impl Future<Output = ()> { let shutdown = match mem::replace(&mut self.server, CopilotServer::Disabled) { CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })), _ => None, }; async move { if let Some(shutdown) = shutdown { shutdown.await; } } } fn enable_or_disable_copilot(&mut self, cx: &mut Context<Self>) { let server_id = self.server_id; let http = self.http.clone(); let node_runtime = self.node_runtime.clone(); let language_settings = all_language_settings(None, cx); if language_settings.edit_predictions.provider == EditPredictionProvider::Copilot { if matches!(self.server, CopilotServer::Disabled) { let env = self.build_env(&language_settings.edit_predictions.copilot); let start_task = cx .spawn(move |this, cx| { Self::start_language_server(server_id, http, node_runtime, env, this, cx) }) .shared(); self.server = CopilotServer::Starting { task: start_task }; cx.notify(); } } else { self.server = CopilotServer::Disabled; cx.notify(); } } fn build_env(&self, copilot_settings: &CopilotSettings) -> Option<HashMap<String, String>> { let proxy_url = copilot_settings.proxy.clone()?; let no_verify = copilot_settings.proxy_no_verify; let http_or_https_proxy = if proxy_url.starts_with("http:") { "HTTP_PROXY" } else if proxy_url.starts_with("https:") { "HTTPS_PROXY" } else { log::error!( "Unsupported protocol scheme for language server proxy (must be http or https)" ); return None; }; let mut env = HashMap::default(); env.insert(http_or_https_proxy.to_string(), proxy_url); if let Some(true) = no_verify { env.insert("NODE_TLS_REJECT_UNAUTHORIZED".to_string(), "0".to_string()); }; Some(env) } #[cfg(any(test, feature = "test-support"))] pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) { use lsp::FakeLanguageServer; use node_runtime::NodeRuntime; let (server, fake_server) = FakeLanguageServer::new( LanguageServerId(0), LanguageServerBinary { path: "path/to/copilot".into(), arguments: vec![], env: None, }, "copilot".into(), Default::default(), cx.to_async(), ); let http = http_client::FakeHttpClient::create(|_| async { unreachable!() }); let node_runtime = NodeRuntime::unavailable(); let this = cx.new(|cx| Self { server_id: LanguageServerId(0), http: http.clone(), node_runtime, server: CopilotServer::Running(RunningCopilotServer { lsp: Arc::new(server), sign_in_status: SignInStatus::Authorized, registered_buffers: Default::default(), }), _subscription: cx.on_app_quit(Self::shutdown_language_server), buffers: Default::default(), }); (this, fake_server) } async fn start_language_server( new_server_id: LanguageServerId, http: Arc<dyn HttpClient>, node_runtime: NodeRuntime, env: Option<HashMap<String, String>>, this: WeakEntity<Self>, mut cx: AsyncApp, ) { let start_language_server = async { let server_path = get_copilot_lsp(http).await?; let node_path = node_runtime.binary_path().await?; let arguments: Vec<OsString> = vec![server_path.into(), "--stdio".into()]; let binary = LanguageServerBinary { path: node_path, arguments, env, }; let root_path = if cfg!(target_os = "windows") { Path::new("C:/") } else { Path::new("/") }; let server_name = LanguageServerName("copilot".into()); let server = LanguageServer::new( Arc::new(Mutex::new(None)), new_server_id, server_name, binary, root_path, None, Default::default(), cx.clone(), )?; server .on_notification::<StatusNotification, _>(|_, _| { /* Silence the notification */ }) .detach(); let configuration = lsp::DidChangeConfigurationParams { settings: Default::default(), }; let server = cx .update(|cx| { let params = server.default_initialize_params(cx); server.initialize(params, configuration.into(), cx) })? .await?; let status = server .request::<request::CheckStatus>(request::CheckStatusParams { local_checks_only: false, }) .await?;