Add GitHub Copilot Chat Support (#14842)

# Summary

This commit implements Github Copilot Chat support within the existing
Assistant panel/framework. It required a little bit of trickery and
internal API modification, as Copilot doesn't use the same
authentication-style as all of the existing providers, opting to use
OAuth and a short lived API key instead of a straight API key. All
existing Assistant features should work.

Release Notes:
- Added Github Copilot Chat support
([#4673](https://github.com/zed-industries/zed/issues/4673)).

## Screenshots
<img width="1552" alt="A screenshot showing a conversation between a
user and Github Copilot Chat within the Zed editor."
src="https://github.com/user-attachments/assets/73eaf6a2-792b-4c40-a7fe-f763bd6417d7">

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
This commit is contained in:
Ryan Hawkins 2024-07-30 01:32:58 -06:00 committed by GitHub
parent d93891ba63
commit 6f0655810e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 808 additions and 14 deletions

View file

@ -1,7 +1,9 @@
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;
@ -27,6 +29,7 @@ use settings::SettingsStore;
use smol::{fs, io::BufReader, stream::StreamExt};
use std::{
any::TypeId,
env,
ffi::OsString,
mem,
ops::Range,
@ -52,10 +55,13 @@ actions!(
pub fn init(
new_server_id: LanguageServerId,
fs: Arc<dyn Fs>,
http: Arc<dyn HttpClient>,
node_runtime: Arc<dyn NodeRuntime>,
cx: &mut AppContext,
) {
copilot_chat::init(fs, http.clone(), cx);
let copilot = cx.new_model({
let node_runtime = node_runtime.clone();
move |cx| Copilot::start(new_server_id, http, node_runtime, cx)
@ -185,6 +191,10 @@ impl Status {
pub fn is_authorized(&self) -> bool {
matches!(self, Status::Authorized)
}
pub fn is_disabled(&self) -> bool {
matches!(self, Status::Disabled)
}
}
struct RegisteredBuffer {
@ -301,6 +311,8 @@ pub struct Copilot {
pub enum Event {
CopilotLanguageServerStarted,
CopilotAuthSignedIn,
CopilotAuthSignedOut,
}
impl EventEmitter<Event> for Copilot {}
@ -581,7 +593,7 @@ impl Copilot {
}
}
fn sign_out(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
pub fn sign_out(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
self.update_sign_in_status(request::SignInStatus::NotSignedIn, cx);
if let CopilotServer::Running(RunningCopilotServer { lsp: server, .. }) = &self.server {
let server = server.clone();
@ -928,6 +940,7 @@ impl Copilot {
| request::SignInStatus::MaybeOk { .. }
| request::SignInStatus::AlreadySignedIn { .. } => {
server.sign_in_status = SignInStatus::Authorized;
cx.emit(Event::CopilotAuthSignedIn);
for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
if let Some(buffer) = buffer.upgrade() {
self.register_buffer(&buffer, cx);
@ -942,6 +955,7 @@ impl Copilot {
}
request::SignInStatus::Ok { user: None } | request::SignInStatus::NotSignedIn => {
server.sign_in_status = SignInStatus::SignedOut;
cx.emit(Event::CopilotAuthSignedOut);
for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
self.unregister_buffer(&buffer);
}