Start copilot and check sign in status

This commit is contained in:
Antonio Scandurra 2023-03-23 10:29:56 +01:00 committed by Mikayla Maki
parent 5471217089
commit 797bb7d780
2 changed files with 100 additions and 27 deletions

View file

@ -1,9 +1,16 @@
use anyhow::{anyhow, Ok}; mod request;
use anyhow::{anyhow, Result};
use async_compression::futures::bufread::GzipDecoder; use async_compression::futures::bufread::GzipDecoder;
use client::Client; use client::Client;
use gpui::{actions, MutableAppContext}; use gpui::{actions, AppContext, Entity, ModelContext, ModelHandle, MutableAppContext};
use lsp::LanguageServer;
use smol::{fs, io::BufReader, stream::StreamExt}; use smol::{fs, io::BufReader, stream::StreamExt};
use std::{env::consts, path::PathBuf, sync::Arc}; use std::{
env::consts,
path::{Path, PathBuf},
sync::Arc,
};
use util::{ use util::{
fs::remove_matching, github::latest_github_release, http::HttpClient, paths, ResultExt, fs::remove_matching, github::latest_github_release, http::HttpClient, paths, ResultExt,
}; };
@ -11,46 +18,89 @@ use util::{
actions!(copilot, [SignIn]); actions!(copilot, [SignIn]);
pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) { pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) {
cx.add_global_action(move |_: &SignIn, cx: &mut MutableAppContext| { let copilot = cx.add_model(|cx| Copilot::start(client.http_client(), cx));
Copilot::sign_in(client.http_client(), cx) cx.set_global(copilot);
}); }
enum CopilotServer {
Downloading,
Error(String),
Started {
server: Arc<LanguageServer>,
status: SignInStatus,
},
}
enum SignInStatus {
Authorized,
Unauthorized,
SignedOut,
} }
#[derive(Debug)]
struct Copilot { struct Copilot {
copilot_server: PathBuf, server: CopilotServer,
}
impl Entity for Copilot {
type Event = ();
} }
impl Copilot { impl Copilot {
fn sign_in(http: Arc<dyn HttpClient>, cx: &mut MutableAppContext) { fn global(cx: &AppContext) -> Option<ModelHandle<Self>> {
let maybe_copilot = cx.default_global::<Option<Arc<Copilot>>>().clone(); if cx.has_global::<ModelHandle<Self>>() {
Some(cx.global::<ModelHandle<Self>>().clone())
} else {
None
}
}
cx.spawn(|mut cx| async move { fn start(http: Arc<dyn HttpClient>, cx: &mut ModelContext<Self>) -> Self {
// Lazily download / initialize copilot LSP let copilot = Self {
let copilot = if let Some(copilot) = maybe_copilot { server: CopilotServer::Downloading,
copilot };
} else { cx.spawn(|this, mut cx| async move {
let copilot_server = get_lsp_binary(http).await?; // TODO: Make this error user visible let start_language_server = async {
let new_copilot = Arc::new(Copilot { copilot_server }); let server_path = get_lsp_binary(http).await?;
cx.update({ let server =
let new_copilot = new_copilot.clone(); LanguageServer::new(0, &server_path, &["--stdio"], Path::new("/"), cx.clone())?;
move |cx| cx.set_global(Some(new_copilot.clone())) let server = server.initialize(Default::default()).await?;
}); let status = server
new_copilot .request::<request::CheckStatus>(request::CheckStatusParams {
local_checks_only: false,
})
.await?;
let status = match status.status.as_str() {
"OK" | "MaybeOk" => SignInStatus::Authorized,
"NotAuthorized" => SignInStatus::Unauthorized,
_ => SignInStatus::SignedOut,
};
anyhow::Ok((server, status))
}; };
dbg!(copilot); let server = start_language_server.await;
this.update(&mut cx, |this, cx| {
Ok(()) cx.notify();
match server {
Ok((server, status)) => {
this.server = CopilotServer::Started { server, status };
Ok(())
}
Err(error) => {
this.server = CopilotServer::Error(error.to_string());
Err(error)
}
}
})
}) })
.detach(); .detach_and_log_err(cx);
copilot
} }
} }
async fn get_lsp_binary(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> { async fn get_lsp_binary(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
///Check for the latest copilot language server and download it if we haven't already ///Check for the latest copilot language server and download it if we haven't already
async fn fetch_latest(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> { async fn fetch_latest(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
let release = latest_github_release("zed-industries/copilotserver", http.clone()).await?; let release = latest_github_release("zed-industries/copilot", http.clone()).await?;
let asset_name = format!("copilot-darwin-{}.gz", consts::ARCH); let asset_name = format!("copilot-darwin-{}.gz", consts::ARCH);
let asset = release let asset = release
.assets .assets
@ -58,6 +108,7 @@ async fn get_lsp_binary(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
.find(|asset| asset.name == asset_name) .find(|asset| asset.name == asset_name)
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?; .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
fs::create_dir_all(&*paths::COPILOT_DIR).await?;
let destination_path = let destination_path =
paths::COPILOT_DIR.join(format!("copilot-{}-{}", release.name, consts::ARCH)); paths::COPILOT_DIR.join(format!("copilot-{}-{}", release.name, consts::ARCH));

View file

@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
pub enum CheckStatus {}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckStatusParams {
pub local_checks_only: bool,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckStatusResult {
pub status: String,
pub user: Option<String>,
}
impl lsp::request::Request for CheckStatus {
type Params = CheckStatusParams;
type Result = CheckStatusResult;
const METHOD: &'static str = "checkStatus";
}