mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 21:32:40 +00:00
add copilot menu
This commit is contained in:
parent
9b8a3e4de5
commit
f16b96cafc
3 changed files with 130 additions and 45 deletions
|
@ -202,7 +202,9 @@ impl Copilot {
|
||||||
.spawn({
|
.spawn({
|
||||||
let http = http.clone();
|
let http = http.clone();
|
||||||
let node_runtime = node_runtime.clone();
|
let node_runtime = node_runtime.clone();
|
||||||
move |this, cx| Self::start_language_server(http, node_runtime, this, cx)
|
move |this, cx| async {
|
||||||
|
Self::start_language_server(http, node_runtime, this, cx).await
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.shared();
|
.shared();
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,18 @@ use crate::{request::PromptUserDeviceFlow, Copilot, Status};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*,
|
elements::*,
|
||||||
geometry::rect::RectF,
|
geometry::rect::RectF,
|
||||||
|
impl_internal_actions,
|
||||||
platform::{WindowBounds, WindowKind, WindowOptions},
|
platform::{WindowBounds, WindowKind, WindowOptions},
|
||||||
AppContext, ClipboardItem, Element, Entity, View, ViewContext, ViewHandle,
|
AppContext, ClipboardItem, Element, Entity, View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use theme::ui::modal;
|
use theme::ui::modal;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
struct ClickedConnect;
|
||||||
|
|
||||||
|
impl_internal_actions!(copilot_verification, [ClickedConnect]);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
struct CopyUserCode;
|
struct CopyUserCode;
|
||||||
|
|
||||||
|
@ -56,6 +62,12 @@ pub fn init(cx: &mut AppContext) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
cx.add_action(
|
||||||
|
|code_verification: &mut CopilotCodeVerification, _: &ClickedConnect, _| {
|
||||||
|
code_verification.connect_clicked = true;
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_copilot_auth_window(
|
fn create_copilot_auth_window(
|
||||||
|
@ -81,11 +93,15 @@ fn create_copilot_auth_window(
|
||||||
|
|
||||||
pub struct CopilotCodeVerification {
|
pub struct CopilotCodeVerification {
|
||||||
status: Status,
|
status: Status,
|
||||||
|
connect_clicked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CopilotCodeVerification {
|
impl CopilotCodeVerification {
|
||||||
pub fn new(status: Status) -> Self {
|
pub fn new(status: Status) -> Self {
|
||||||
Self { status }
|
Self {
|
||||||
|
status,
|
||||||
|
connect_clicked: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_status(&mut self, status: Status, cx: &mut ViewContext<Self>) {
|
pub fn set_status(&mut self, status: Status, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -143,6 +159,7 @@ impl CopilotCodeVerification {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_prompting_modal(
|
fn render_prompting_modal(
|
||||||
|
connect_clicked: bool,
|
||||||
data: &PromptUserDeviceFlow,
|
data: &PromptUserDeviceFlow,
|
||||||
style: &theme::Copilot,
|
style: &theme::Copilot,
|
||||||
cx: &mut gpui::RenderContext<Self>,
|
cx: &mut gpui::RenderContext<Self>,
|
||||||
|
@ -189,13 +206,20 @@ impl CopilotCodeVerification {
|
||||||
.with_style(style.auth.prompting.hint.container.clone())
|
.with_style(style.auth.prompting.hint.container.clone())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
theme::ui::cta_button_with_click(
|
theme::ui::cta_button_with_click(
|
||||||
"Connect to GitHub",
|
if connect_clicked {
|
||||||
|
"Waiting for connection..."
|
||||||
|
} else {
|
||||||
|
"Connect to GitHub"
|
||||||
|
},
|
||||||
style.auth.content_width,
|
style.auth.content_width,
|
||||||
&style.auth.cta_button,
|
&style.auth.cta_button,
|
||||||
cx,
|
cx,
|
||||||
{
|
{
|
||||||
let verification_uri = data.verification_uri.clone();
|
let verification_uri = data.verification_uri.clone();
|
||||||
move |_, cx| cx.platform().open_url(&verification_uri)
|
move |_, cx| {
|
||||||
|
cx.platform().open_url(&verification_uri);
|
||||||
|
cx.dispatch_action(ClickedConnect)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
@ -343,9 +367,20 @@ impl View for CopilotCodeVerification {
|
||||||
match &self.status {
|
match &self.status {
|
||||||
Status::SigningIn {
|
Status::SigningIn {
|
||||||
prompt: Some(prompt),
|
prompt: Some(prompt),
|
||||||
} => Self::render_prompting_modal(&prompt, &style.copilot, cx),
|
} => Self::render_prompting_modal(
|
||||||
Status::Unauthorized => Self::render_unauthorized_modal(&style.copilot, cx),
|
self.connect_clicked,
|
||||||
Status::Authorized => Self::render_enabled_modal(&style.copilot, cx),
|
&prompt,
|
||||||
|
&style.copilot,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
|
Status::Unauthorized => {
|
||||||
|
self.connect_clicked = false;
|
||||||
|
Self::render_unauthorized_modal(&style.copilot, cx)
|
||||||
|
}
|
||||||
|
Status::Authorized => {
|
||||||
|
self.connect_clicked = false;
|
||||||
|
Self::render_enabled_modal(&style.copilot, cx)
|
||||||
|
}
|
||||||
_ => Empty::new().boxed(),
|
_ => Empty::new().boxed(),
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
|
@ -24,6 +24,15 @@ const COPILOT_ERROR_TOAST_ID: usize = 1338;
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct DeployCopilotMenu;
|
pub struct DeployCopilotMenu;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct DeployCopilotStartMenu;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct HideCopilot;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct InitiateSignIn;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct ToggleCopilotForLanguage {
|
pub struct ToggleCopilotForLanguage {
|
||||||
language: Arc<str>,
|
language: Arc<str>,
|
||||||
|
@ -40,6 +49,9 @@ impl_internal_actions!(
|
||||||
copilot,
|
copilot,
|
||||||
[
|
[
|
||||||
DeployCopilotMenu,
|
DeployCopilotMenu,
|
||||||
|
DeployCopilotStartMenu,
|
||||||
|
HideCopilot,
|
||||||
|
InitiateSignIn,
|
||||||
DeployCopilotModal,
|
DeployCopilotModal,
|
||||||
ToggleCopilotForLanguage,
|
ToggleCopilotForLanguage,
|
||||||
ToggleCopilotGlobally,
|
ToggleCopilotGlobally,
|
||||||
|
@ -48,6 +60,7 @@ impl_internal_actions!(
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(CopilotButton::deploy_copilot_menu);
|
cx.add_action(CopilotButton::deploy_copilot_menu);
|
||||||
|
cx.add_action(CopilotButton::deploy_copilot_start_menu);
|
||||||
cx.add_action(
|
cx.add_action(
|
||||||
|_: &mut CopilotButton, action: &ToggleCopilotForLanguage, cx| {
|
|_: &mut CopilotButton, action: &ToggleCopilotForLanguage, cx| {
|
||||||
let language = action.language.clone();
|
let language = action.language.clone();
|
||||||
|
@ -73,6 +86,58 @@ pub fn init(cx: &mut AppContext) {
|
||||||
file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
|
file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cx.add_action(|_: &mut CopilotButton, _: &HideCopilot, cx| {
|
||||||
|
SettingsFile::update(cx, move |file_contents| {
|
||||||
|
file_contents.features.copilot = Some(false)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.add_action(|_: &mut CopilotButton, _: &InitiateSignIn, cx| {
|
||||||
|
let Some(copilot) = Copilot::global(cx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let status = copilot.read(cx).status();
|
||||||
|
|
||||||
|
match status {
|
||||||
|
Status::Starting { task } => {
|
||||||
|
cx.dispatch_action(workspace::Toast::new(
|
||||||
|
COPILOT_STARTING_TOAST_ID,
|
||||||
|
"Copilot is starting...",
|
||||||
|
));
|
||||||
|
let window_id = cx.window_id();
|
||||||
|
let task = task.to_owned();
|
||||||
|
cx.spawn(|handle, mut cx| async move {
|
||||||
|
task.await;
|
||||||
|
cx.update(|cx| {
|
||||||
|
if let Some(copilot) = Copilot::global(cx) {
|
||||||
|
let status = copilot.read(cx).status();
|
||||||
|
match status {
|
||||||
|
Status::Authorized => cx.dispatch_action_at(
|
||||||
|
window_id,
|
||||||
|
handle.id(),
|
||||||
|
workspace::Toast::new(
|
||||||
|
COPILOT_STARTING_TOAST_ID,
|
||||||
|
"Copilot has started!",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
cx.dispatch_action_at(
|
||||||
|
window_id,
|
||||||
|
handle.id(),
|
||||||
|
DismissToast::new(COPILOT_STARTING_TOAST_ID),
|
||||||
|
);
|
||||||
|
cx.dispatch_action_at(window_id, handle.id(), SignIn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
_ => cx.dispatch_action(SignIn),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CopilotButton {
|
pub struct CopilotButton {
|
||||||
|
@ -109,8 +174,6 @@ impl View for CopilotButton {
|
||||||
.editor_enabled
|
.editor_enabled
|
||||||
.unwrap_or(settings.show_copilot_suggestions(None));
|
.unwrap_or(settings.show_copilot_suggestions(None));
|
||||||
|
|
||||||
let view_id = cx.view_id();
|
|
||||||
|
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
MouseEventHandler::<Self>::new(0, cx, {
|
MouseEventHandler::<Self>::new(0, cx, {
|
||||||
|
@ -157,48 +220,13 @@ impl View for CopilotButton {
|
||||||
let status = status.clone();
|
let status = status.clone();
|
||||||
move |_, cx| match status {
|
move |_, cx| match status {
|
||||||
Status::Authorized => cx.dispatch_action(DeployCopilotMenu),
|
Status::Authorized => cx.dispatch_action(DeployCopilotMenu),
|
||||||
Status::Starting { ref task } => {
|
|
||||||
cx.dispatch_action(workspace::Toast::new(
|
|
||||||
COPILOT_STARTING_TOAST_ID,
|
|
||||||
"Copilot is starting...",
|
|
||||||
));
|
|
||||||
let window_id = cx.window_id();
|
|
||||||
let task = task.to_owned();
|
|
||||||
cx.spawn(|mut cx| async move {
|
|
||||||
task.await;
|
|
||||||
cx.update(|cx| {
|
|
||||||
if let Some(copilot) = Copilot::global(cx) {
|
|
||||||
let status = copilot.read(cx).status();
|
|
||||||
match status {
|
|
||||||
Status::Authorized => cx.dispatch_action_at(
|
|
||||||
window_id,
|
|
||||||
view_id,
|
|
||||||
workspace::Toast::new(
|
|
||||||
COPILOT_STARTING_TOAST_ID,
|
|
||||||
"Copilot has started!",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_ => {
|
|
||||||
cx.dispatch_action_at(
|
|
||||||
window_id,
|
|
||||||
view_id,
|
|
||||||
DismissToast::new(COPILOT_STARTING_TOAST_ID),
|
|
||||||
);
|
|
||||||
cx.dispatch_global_action(SignIn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
Status::Error(ref e) => cx.dispatch_action(workspace::Toast::new_action(
|
Status::Error(ref e) => cx.dispatch_action(workspace::Toast::new_action(
|
||||||
COPILOT_ERROR_TOAST_ID,
|
COPILOT_ERROR_TOAST_ID,
|
||||||
format!("Copilot can't be started: {}", e),
|
format!("Copilot can't be started: {}", e),
|
||||||
"Reinstall Copilot",
|
"Reinstall Copilot",
|
||||||
Reinstall,
|
Reinstall,
|
||||||
)),
|
)),
|
||||||
_ => cx.dispatch_action(SignIn),
|
_ => cx.dispatch_action(DeployCopilotStartMenu),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.with_tooltip::<Self, _>(
|
.with_tooltip::<Self, _>(
|
||||||
|
@ -244,6 +272,26 @@ impl CopilotButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deploy_copilot_start_menu(
|
||||||
|
&mut self,
|
||||||
|
_: &DeployCopilotStartMenu,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
let mut menu_options = Vec::with_capacity(2);
|
||||||
|
|
||||||
|
menu_options.push(ContextMenuItem::item("Sign In", InitiateSignIn));
|
||||||
|
menu_options.push(ContextMenuItem::item("Hide Copilot", HideCopilot));
|
||||||
|
|
||||||
|
self.popup_menu.update(cx, |menu, cx| {
|
||||||
|
menu.show(
|
||||||
|
Default::default(),
|
||||||
|
AnchorCorner::BottomRight,
|
||||||
|
menu_options,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deploy_copilot_menu(&mut self, _: &DeployCopilotMenu, cx: &mut ViewContext<Self>) {
|
pub fn deploy_copilot_menu(&mut self, _: &DeployCopilotMenu, cx: &mut ViewContext<Self>) {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue