mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 06:33:52 +03:00
Merge pull request #2386 from zed-industries/copilot-shipping
Get copilot ready to ship
This commit is contained in:
commit
dd73233973
@ -178,7 +178,7 @@
|
|||||||
"focus": false
|
"focus": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"alt-\\": "copilot::NextSuggestion",
|
"alt-\\": "copilot::Suggest",
|
||||||
"alt-]": "copilot::NextSuggestion",
|
"alt-]": "copilot::NextSuggestion",
|
||||||
"alt-[": "copilot::PreviousSuggestion"
|
"alt-[": "copilot::PreviousSuggestion"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
// The name of the Zed theme to use for the UI
|
// The name of the Zed theme to use for the UI
|
||||||
"theme": "One Dark",
|
"theme": "One Dark",
|
||||||
|
// Features that can be globally enabled or disabled
|
||||||
|
"features": {
|
||||||
|
// Show Copilot icon in status bar
|
||||||
|
"copilot": true
|
||||||
|
},
|
||||||
// The name of a font to use for rendering text in the editor
|
// The name of a font to use for rendering text in the editor
|
||||||
"buffer_font_family": "Zed Mono",
|
"buffer_font_family": "Zed Mono",
|
||||||
// The OpenType features to enable for text in the editor.
|
// The OpenType features to enable for text in the editor.
|
||||||
@ -13,11 +18,6 @@
|
|||||||
// The factor to grow the active pane by. Defaults to 1.0
|
// The factor to grow the active pane by. Defaults to 1.0
|
||||||
// which gives the same size as all other panes.
|
// which gives the same size as all other panes.
|
||||||
"active_pane_magnification": 1.0,
|
"active_pane_magnification": 1.0,
|
||||||
// Enable / disable copilot integration.
|
|
||||||
"enable_copilot_integration": true,
|
|
||||||
// Controls whether copilot provides suggestion immediately
|
|
||||||
// or waits for a `copilot::Toggle`
|
|
||||||
"copilot": "on",
|
|
||||||
// Whether to enable vim modes and key bindings
|
// Whether to enable vim modes and key bindings
|
||||||
"vim_mode": false,
|
"vim_mode": false,
|
||||||
// Whether to show the informational hover box when moving the mouse
|
// Whether to show the informational hover box when moving the mouse
|
||||||
@ -30,6 +30,9 @@
|
|||||||
// Whether to pop the completions menu while typing in an editor without
|
// Whether to pop the completions menu while typing in an editor without
|
||||||
// explicitly requesting it.
|
// explicitly requesting it.
|
||||||
"show_completions_on_input": true,
|
"show_completions_on_input": true,
|
||||||
|
// Controls whether copilot provides suggestion immediately
|
||||||
|
// or waits for a `copilot::Toggle`
|
||||||
|
"show_copilot_suggestions": true,
|
||||||
// Whether the screen sharing icon is shown in the os status bar.
|
// Whether the screen sharing icon is shown in the os status bar.
|
||||||
"show_call_status_icon": true,
|
"show_call_status_icon": true,
|
||||||
// Whether to use language servers to provide code intelligence.
|
// Whether to use language servers to provide code intelligence.
|
||||||
|
@ -29,7 +29,10 @@ const COPILOT_AUTH_NAMESPACE: &'static str = "copilot_auth";
|
|||||||
actions!(copilot_auth, [SignIn, SignOut]);
|
actions!(copilot_auth, [SignIn, SignOut]);
|
||||||
|
|
||||||
const COPILOT_NAMESPACE: &'static str = "copilot";
|
const COPILOT_NAMESPACE: &'static str = "copilot";
|
||||||
actions!(copilot, [NextSuggestion, PreviousSuggestion, Reinstall]);
|
actions!(
|
||||||
|
copilot,
|
||||||
|
[Suggest, NextSuggestion, PreviousSuggestion, Reinstall]
|
||||||
|
);
|
||||||
|
|
||||||
pub fn init(http: Arc<dyn HttpClient>, node_runtime: Arc<NodeRuntime>, cx: &mut AppContext) {
|
pub fn init(http: Arc<dyn HttpClient>, node_runtime: Arc<NodeRuntime>, cx: &mut AppContext) {
|
||||||
// Disable Copilot for stable releases.
|
// Disable Copilot for stable releases.
|
||||||
@ -172,7 +175,7 @@ impl Copilot {
|
|||||||
let http = http.clone();
|
let http = http.clone();
|
||||||
let node_runtime = node_runtime.clone();
|
let node_runtime = node_runtime.clone();
|
||||||
move |this, cx| {
|
move |this, cx| {
|
||||||
if cx.global::<Settings>().enable_copilot_integration {
|
if cx.global::<Settings>().features.copilot {
|
||||||
if matches!(this.server, CopilotServer::Disabled) {
|
if matches!(this.server, CopilotServer::Disabled) {
|
||||||
let start_task = cx
|
let start_task = cx
|
||||||
.spawn({
|
.spawn({
|
||||||
@ -194,12 +197,14 @@ impl Copilot {
|
|||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
if cx.global::<Settings>().enable_copilot_integration {
|
if cx.global::<Settings>().features.copilot {
|
||||||
let start_task = cx
|
let start_task = cx
|
||||||
.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,17 +60,19 @@ 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.to_owned();
|
let language = action.language.clone();
|
||||||
|
let show_copilot_suggestions = cx
|
||||||
let current_langauge = cx.global::<Settings>().copilot_on(Some(&language));
|
.global::<Settings>()
|
||||||
|
.show_copilot_suggestions(Some(&language));
|
||||||
|
|
||||||
SettingsFile::update(cx, move |file_contents| {
|
SettingsFile::update(cx, move |file_contents| {
|
||||||
file_contents.languages.insert(
|
file_contents.languages.insert(
|
||||||
language.to_owned(),
|
language,
|
||||||
settings::EditorSettings {
|
settings::EditorSettings {
|
||||||
copilot: Some((!current_langauge).into()),
|
show_copilot_suggestions: Some((!show_copilot_suggestions).into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -67,12 +81,63 @@ pub fn init(cx: &mut AppContext) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
cx.add_action(|_: &mut CopilotButton, _: &ToggleCopilotGlobally, cx| {
|
cx.add_action(|_: &mut CopilotButton, _: &ToggleCopilotGlobally, cx| {
|
||||||
let copilot_on = cx.global::<Settings>().copilot_on(None);
|
let show_copilot_suggestions = cx.global::<Settings>().show_copilot_suggestions(None);
|
||||||
|
|
||||||
SettingsFile::update(cx, move |file_contents| {
|
SettingsFile::update(cx, move |file_contents| {
|
||||||
file_contents.editor.copilot = Some((!copilot_on).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 {
|
||||||
@ -94,7 +159,7 @@ impl View for CopilotButton {
|
|||||||
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox {
|
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
|
|
||||||
if !settings.enable_copilot_integration {
|
if !settings.features.copilot {
|
||||||
return Empty::new().boxed();
|
return Empty::new().boxed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,9 +170,9 @@ impl View for CopilotButton {
|
|||||||
};
|
};
|
||||||
let status = copilot.read(cx).status();
|
let status = copilot.read(cx).status();
|
||||||
|
|
||||||
let enabled = self.editor_enabled.unwrap_or(settings.copilot_on(None));
|
let enabled = self
|
||||||
|
.editor_enabled
|
||||||
let view_id = cx.view_id();
|
.unwrap_or(settings.show_copilot_suggestions(None));
|
||||||
|
|
||||||
Stack::new()
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
@ -155,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, _>(
|
||||||
@ -242,22 +272,38 @@ 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>();
|
||||||
|
|
||||||
let mut menu_options = Vec::with_capacity(6);
|
let mut menu_options = Vec::with_capacity(6);
|
||||||
|
|
||||||
if let Some(language) = &self.language {
|
if let Some(language) = &self.language {
|
||||||
let language_enabled = settings.copilot_on(Some(language.as_ref()));
|
let language_enabled = settings.show_copilot_suggestions(Some(language.as_ref()));
|
||||||
|
|
||||||
menu_options.push(ContextMenuItem::item(
|
menu_options.push(ContextMenuItem::item(
|
||||||
format!(
|
format!(
|
||||||
"{} Copilot for {}",
|
"{} Suggestions for {}",
|
||||||
if language_enabled {
|
if language_enabled { "Hide" } else { "Show" },
|
||||||
"Disable"
|
|
||||||
} else {
|
|
||||||
"Enable"
|
|
||||||
},
|
|
||||||
language
|
language
|
||||||
),
|
),
|
||||||
ToggleCopilotForLanguage {
|
ToggleCopilotForLanguage {
|
||||||
@ -266,12 +312,12 @@ impl CopilotButton {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let globally_enabled = cx.global::<Settings>().copilot_on(None);
|
let globally_enabled = cx.global::<Settings>().show_copilot_suggestions(None);
|
||||||
menu_options.push(ContextMenuItem::item(
|
menu_options.push(ContextMenuItem::item(
|
||||||
if globally_enabled {
|
if globally_enabled {
|
||||||
"Disable Copilot Globally"
|
"Hide Suggestions for All Files"
|
||||||
} else {
|
} else {
|
||||||
"Enable Copilot Globally"
|
"Show Suggestions for All Files"
|
||||||
},
|
},
|
||||||
ToggleCopilotGlobally,
|
ToggleCopilotGlobally,
|
||||||
));
|
));
|
||||||
@ -319,7 +365,7 @@ impl CopilotButton {
|
|||||||
|
|
||||||
self.language = language_name.clone();
|
self.language = language_name.clone();
|
||||||
|
|
||||||
self.editor_enabled = Some(settings.copilot_on(language_name.as_deref()));
|
self.editor_enabled = Some(settings.show_copilot_suggestions(language_name.as_deref()));
|
||||||
|
|
||||||
cx.notify()
|
cx.notify()
|
||||||
}
|
}
|
||||||
|
@ -397,6 +397,7 @@ pub fn init(cx: &mut AppContext) {
|
|||||||
cx.add_async_action(Editor::find_all_references);
|
cx.add_async_action(Editor::find_all_references);
|
||||||
cx.add_action(Editor::next_copilot_suggestion);
|
cx.add_action(Editor::next_copilot_suggestion);
|
||||||
cx.add_action(Editor::previous_copilot_suggestion);
|
cx.add_action(Editor::previous_copilot_suggestion);
|
||||||
|
cx.add_action(Editor::copilot_suggest);
|
||||||
|
|
||||||
hover_popover::init(cx);
|
hover_popover::init(cx);
|
||||||
link_go_to_definition::init(cx);
|
link_go_to_definition::init(cx);
|
||||||
@ -1016,6 +1017,8 @@ impl CodeActionsMenu {
|
|||||||
pub struct CopilotState {
|
pub struct CopilotState {
|
||||||
excerpt_id: Option<ExcerptId>,
|
excerpt_id: Option<ExcerptId>,
|
||||||
pending_refresh: Task<Option<()>>,
|
pending_refresh: Task<Option<()>>,
|
||||||
|
pending_cycling_refresh: Task<Option<()>>,
|
||||||
|
cycled: bool,
|
||||||
completions: Vec<copilot::Completion>,
|
completions: Vec<copilot::Completion>,
|
||||||
active_completion_index: usize,
|
active_completion_index: usize,
|
||||||
}
|
}
|
||||||
@ -1024,9 +1027,11 @@ impl Default for CopilotState {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
excerpt_id: None,
|
excerpt_id: None,
|
||||||
|
pending_cycling_refresh: Task::ready(Some(())),
|
||||||
pending_refresh: Task::ready(Some(())),
|
pending_refresh: Task::ready(Some(())),
|
||||||
completions: Default::default(),
|
completions: Default::default(),
|
||||||
active_completion_index: 0,
|
active_completion_index: 0,
|
||||||
|
cycled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1070,6 +1075,26 @@ impl CopilotState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cycle_completions(&mut self, direction: Direction) {
|
||||||
|
match direction {
|
||||||
|
Direction::Prev => {
|
||||||
|
self.active_completion_index = if self.active_completion_index == 0 {
|
||||||
|
self.completions.len() - 1
|
||||||
|
} else {
|
||||||
|
self.active_completion_index - 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Direction::Next => {
|
||||||
|
if self.completions.len() == 0 {
|
||||||
|
self.active_completion_index = 0
|
||||||
|
} else {
|
||||||
|
self.active_completion_index =
|
||||||
|
(self.active_completion_index + 1) % self.completions.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn push_completion(&mut self, new_completion: copilot::Completion) {
|
fn push_completion(&mut self, new_completion: copilot::Completion) {
|
||||||
for completion in &self.completions {
|
for completion in &self.completions {
|
||||||
if *completion == new_completion {
|
if *completion == new_completion {
|
||||||
@ -1267,7 +1292,7 @@ impl Editor {
|
|||||||
cx.subscribe(&buffer, Self::on_buffer_event),
|
cx.subscribe(&buffer, Self::on_buffer_event),
|
||||||
cx.observe(&display_map, Self::on_display_map_changed),
|
cx.observe(&display_map, Self::on_display_map_changed),
|
||||||
cx.observe(&blink_manager, |_, _, cx| cx.notify()),
|
cx.observe(&blink_manager, |_, _, cx| cx.notify()),
|
||||||
cx.observe_global::<Settings, _>(Self::on_settings_changed),
|
cx.observe_global::<Settings, _>(Self::settings_changed),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
this.end_selection(cx);
|
this.end_selection(cx);
|
||||||
@ -2028,13 +2053,13 @@ impl Editor {
|
|||||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
|
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
|
||||||
|
|
||||||
if had_active_copilot_suggestion {
|
if had_active_copilot_suggestion {
|
||||||
this.refresh_copilot_suggestions(cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
if !this.has_active_copilot_suggestion(cx) {
|
if !this.has_active_copilot_suggestion(cx) {
|
||||||
this.trigger_completion_on_input(&text, cx);
|
this.trigger_completion_on_input(&text, cx);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.trigger_completion_on_input(&text, cx);
|
this.trigger_completion_on_input(&text, cx);
|
||||||
this.refresh_copilot_suggestions(cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2116,7 +2141,7 @@ impl Editor {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
|
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
|
||||||
this.refresh_copilot_suggestions(cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2591,7 +2616,7 @@ impl Editor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refresh_copilot_suggestions(cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let project = self.project.clone()?;
|
let project = self.project.clone()?;
|
||||||
@ -2884,10 +2909,14 @@ impl Editor {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
|
fn refresh_copilot_suggestions(
|
||||||
|
&mut self,
|
||||||
|
debounce: bool,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Option<()> {
|
||||||
let copilot = Copilot::global(cx)?;
|
let copilot = Copilot::global(cx)?;
|
||||||
if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
|
if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
|
||||||
self.hide_copilot_suggestion(cx);
|
self.clear_copilot_suggestions(cx);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.update_visible_copilot_suggestion(cx);
|
self.update_visible_copilot_suggestion(cx);
|
||||||
@ -2895,28 +2924,35 @@ impl Editor {
|
|||||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||||
let cursor = self.selections.newest_anchor().head();
|
let cursor = self.selections.newest_anchor().head();
|
||||||
let language_name = snapshot.language_at(cursor).map(|language| language.name());
|
let language_name = snapshot.language_at(cursor).map(|language| language.name());
|
||||||
if !cx.global::<Settings>().copilot_on(language_name.as_deref()) {
|
if !cx
|
||||||
self.hide_copilot_suggestion(cx);
|
.global::<Settings>()
|
||||||
|
.show_copilot_suggestions(language_name.as_deref())
|
||||||
|
{
|
||||||
|
self.clear_copilot_suggestions(cx);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (buffer, buffer_position) =
|
let (buffer, buffer_position) =
|
||||||
self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
|
self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
|
||||||
self.copilot_state.pending_refresh = cx.spawn_weak(|this, mut cx| async move {
|
self.copilot_state.pending_refresh = cx.spawn_weak(|this, mut cx| async move {
|
||||||
|
if debounce {
|
||||||
cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
|
cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
|
||||||
let (completion, completions_cycling) = copilot.update(&mut cx, |copilot, cx| {
|
}
|
||||||
(
|
|
||||||
copilot.completions(&buffer, buffer_position, cx),
|
let completions = copilot
|
||||||
copilot.completions_cycling(&buffer, buffer_position, cx),
|
.update(&mut cx, |copilot, cx| {
|
||||||
)
|
copilot.completions(&buffer, buffer_position, cx)
|
||||||
});
|
})
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
let (completion, completions_cycling) = futures::join!(completion, completions_cycling);
|
|
||||||
let mut completions = Vec::new();
|
|
||||||
completions.extend(completion.log_err().into_iter().flatten());
|
|
||||||
completions.extend(completions_cycling.log_err().into_iter().flatten());
|
|
||||||
this.upgrade(&cx)?.update(&mut cx, |this, cx| {
|
this.upgrade(&cx)?.update(&mut cx, |this, cx| {
|
||||||
if !completions.is_empty() {
|
if !completions.is_empty() {
|
||||||
|
this.copilot_state.cycled = false;
|
||||||
|
this.copilot_state.pending_cycling_refresh = Task::ready(None);
|
||||||
this.copilot_state.completions.clear();
|
this.copilot_state.completions.clear();
|
||||||
this.copilot_state.active_completion_index = 0;
|
this.copilot_state.active_completion_index = 0;
|
||||||
this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
|
this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
|
||||||
@ -2933,34 +2969,73 @@ impl Editor {
|
|||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
|
fn cycle_suggestions(
|
||||||
|
&mut self,
|
||||||
|
direction: Direction,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let copilot = Copilot::global(cx)?;
|
||||||
|
if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.copilot_state.cycled {
|
||||||
|
self.copilot_state.cycle_completions(direction);
|
||||||
|
self.update_visible_copilot_suggestion(cx);
|
||||||
|
} else {
|
||||||
|
let cursor = self.selections.newest_anchor().head();
|
||||||
|
let (buffer, buffer_position) =
|
||||||
|
self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
|
||||||
|
self.copilot_state.pending_cycling_refresh = cx.spawn_weak(|this, mut cx| async move {
|
||||||
|
let completions = copilot
|
||||||
|
.update(&mut cx, |copilot, cx| {
|
||||||
|
copilot.completions_cycling(&buffer, buffer_position, cx)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
this.upgrade(&cx)?.update(&mut cx, |this, cx| {
|
||||||
|
this.copilot_state.cycled = true;
|
||||||
|
for completion in completions.log_err().into_iter().flatten() {
|
||||||
|
this.copilot_state.push_completion(completion);
|
||||||
|
}
|
||||||
|
this.copilot_state.cycle_completions(direction);
|
||||||
|
this.update_visible_copilot_suggestion(cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
|
||||||
if !self.has_active_copilot_suggestion(cx) {
|
if !self.has_active_copilot_suggestion(cx) {
|
||||||
self.refresh_copilot_suggestions(cx);
|
self.refresh_copilot_suggestions(false, cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.copilot_state.active_completion_index =
|
|
||||||
(self.copilot_state.active_completion_index + 1) % self.copilot_state.completions.len();
|
|
||||||
self.update_visible_copilot_suggestion(cx);
|
self.update_visible_copilot_suggestion(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
|
||||||
|
if self.has_active_copilot_suggestion(cx) {
|
||||||
|
self.cycle_suggestions(Direction::Next, cx);
|
||||||
|
} else {
|
||||||
|
self.refresh_copilot_suggestions(false, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn previous_copilot_suggestion(
|
fn previous_copilot_suggestion(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &copilot::PreviousSuggestion,
|
_: &copilot::PreviousSuggestion,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
if !self.has_active_copilot_suggestion(cx) {
|
if self.has_active_copilot_suggestion(cx) {
|
||||||
self.refresh_copilot_suggestions(cx);
|
self.cycle_suggestions(Direction::Prev, cx);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.copilot_state.active_completion_index =
|
|
||||||
if self.copilot_state.active_completion_index == 0 {
|
|
||||||
self.copilot_state.completions.len() - 1
|
|
||||||
} else {
|
} else {
|
||||||
self.copilot_state.active_completion_index - 1
|
self.refresh_copilot_suggestions(false, cx);
|
||||||
};
|
}
|
||||||
self.update_visible_copilot_suggestion(cx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||||
@ -3002,11 +3077,11 @@ impl Editor {
|
|||||||
.copilot_state
|
.copilot_state
|
||||||
.text_for_active_completion(cursor, &snapshot)
|
.text_for_active_completion(cursor, &snapshot)
|
||||||
{
|
{
|
||||||
self.display_map.update(cx, |map, cx| {
|
self.display_map.update(cx, move |map, cx| {
|
||||||
map.replace_suggestion(
|
map.replace_suggestion(
|
||||||
Some(Suggestion {
|
Some(Suggestion {
|
||||||
position: cursor,
|
position: cursor,
|
||||||
text: text.into(),
|
text: text.trim_end().into(),
|
||||||
}),
|
}),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
@ -3017,6 +3092,11 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
self.copilot_state = Default::default();
|
||||||
|
self.hide_copilot_suggestion(cx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_code_actions_indicator(
|
pub fn render_code_actions_indicator(
|
||||||
&self,
|
&self,
|
||||||
style: &EditorStyle,
|
style: &EditorStyle,
|
||||||
@ -3302,7 +3382,7 @@ impl Editor {
|
|||||||
|
|
||||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
|
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
|
||||||
this.insert("", cx);
|
this.insert("", cx);
|
||||||
this.refresh_copilot_suggestions(cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3318,7 +3398,7 @@ impl Editor {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
this.insert("", cx);
|
this.insert("", cx);
|
||||||
this.refresh_copilot_suggestions(cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3414,7 +3494,7 @@ impl Editor {
|
|||||||
self.transact(cx, |this, cx| {
|
self.transact(cx, |this, cx| {
|
||||||
this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
|
this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
|
||||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
|
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
|
||||||
this.refresh_copilot_suggestions(cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4094,7 +4174,7 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||||
self.unmark_text(cx);
|
self.unmark_text(cx);
|
||||||
self.refresh_copilot_suggestions(cx);
|
self.refresh_copilot_suggestions(true, cx);
|
||||||
cx.emit(Event::Edited);
|
cx.emit(Event::Edited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4109,7 +4189,7 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
self.request_autoscroll(Autoscroll::fit(), cx);
|
self.request_autoscroll(Autoscroll::fit(), cx);
|
||||||
self.unmark_text(cx);
|
self.unmark_text(cx);
|
||||||
self.refresh_copilot_suggestions(cx);
|
self.refresh_copilot_suggestions(true, cx);
|
||||||
cx.emit(Event::Edited);
|
cx.emit(Event::Edited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6570,8 +6650,8 @@ impl Editor {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_settings_changed(&mut self, cx: &mut ViewContext<Self>) {
|
fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
self.refresh_copilot_suggestions(cx);
|
self.refresh_copilot_suggestions(true, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_searchable(&mut self, searchable: bool) {
|
pub fn set_searchable(&mut self, searchable: bool) {
|
||||||
|
@ -28,11 +28,11 @@ pub use watched_json::watch_files;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
pub features: Features,
|
||||||
pub buffer_font_family_name: String,
|
pub buffer_font_family_name: String,
|
||||||
pub buffer_font_features: fonts::Features,
|
pub buffer_font_features: fonts::Features,
|
||||||
pub buffer_font_family: FamilyId,
|
pub buffer_font_family: FamilyId,
|
||||||
pub default_buffer_font_size: f32,
|
pub default_buffer_font_size: f32,
|
||||||
pub enable_copilot_integration: bool,
|
|
||||||
pub buffer_font_size: f32,
|
pub buffer_font_size: f32,
|
||||||
pub active_pane_magnification: f32,
|
pub active_pane_magnification: f32,
|
||||||
pub cursor_blink: bool,
|
pub cursor_blink: bool,
|
||||||
@ -177,43 +177,7 @@ pub struct EditorSettings {
|
|||||||
pub ensure_final_newline_on_save: Option<bool>,
|
pub ensure_final_newline_on_save: Option<bool>,
|
||||||
pub formatter: Option<Formatter>,
|
pub formatter: Option<Formatter>,
|
||||||
pub enable_language_server: Option<bool>,
|
pub enable_language_server: Option<bool>,
|
||||||
#[schemars(skip)]
|
pub show_copilot_suggestions: Option<bool>,
|
||||||
pub copilot: Option<OnOff>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum OnOff {
|
|
||||||
On,
|
|
||||||
Off,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OnOff {
|
|
||||||
pub fn as_bool(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
OnOff::On => true,
|
|
||||||
OnOff::Off => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bool(value: bool) -> OnOff {
|
|
||||||
match value {
|
|
||||||
true => OnOff::On,
|
|
||||||
false => OnOff::Off,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<OnOff> for bool {
|
|
||||||
fn from(value: OnOff) -> bool {
|
|
||||||
value.as_bool()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for OnOff {
|
|
||||||
fn from(value: bool) -> OnOff {
|
|
||||||
OnOff::from_bool(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
@ -437,8 +401,7 @@ pub struct SettingsFileContent {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub base_keymap: Option<BaseKeymap>,
|
pub base_keymap: Option<BaseKeymap>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[schemars(skip)]
|
pub features: FeaturesContent,
|
||||||
pub enable_copilot_integration: Option<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
@ -447,6 +410,18 @@ pub struct LspSettings {
|
|||||||
pub initialization_options: Option<Value>,
|
pub initialization_options: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub struct Features {
|
||||||
|
pub copilot: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub struct FeaturesContent {
|
||||||
|
pub copilot: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
/// Fill out the settings corresponding to the default.json file, overrides will be set later
|
/// Fill out the settings corresponding to the default.json file, overrides will be set later
|
||||||
pub fn defaults(
|
pub fn defaults(
|
||||||
@ -500,7 +475,7 @@ impl Settings {
|
|||||||
format_on_save: required(defaults.editor.format_on_save),
|
format_on_save: required(defaults.editor.format_on_save),
|
||||||
formatter: required(defaults.editor.formatter),
|
formatter: required(defaults.editor.formatter),
|
||||||
enable_language_server: required(defaults.editor.enable_language_server),
|
enable_language_server: required(defaults.editor.enable_language_server),
|
||||||
copilot: required(defaults.editor.copilot),
|
show_copilot_suggestions: required(defaults.editor.show_copilot_suggestions),
|
||||||
},
|
},
|
||||||
editor_overrides: Default::default(),
|
editor_overrides: Default::default(),
|
||||||
git: defaults.git.unwrap(),
|
git: defaults.git.unwrap(),
|
||||||
@ -517,7 +492,9 @@ impl Settings {
|
|||||||
telemetry_overrides: Default::default(),
|
telemetry_overrides: Default::default(),
|
||||||
auto_update: defaults.auto_update.unwrap(),
|
auto_update: defaults.auto_update.unwrap(),
|
||||||
base_keymap: Default::default(),
|
base_keymap: Default::default(),
|
||||||
enable_copilot_integration: defaults.enable_copilot_integration.unwrap(),
|
features: Features {
|
||||||
|
copilot: defaults.features.copilot.unwrap(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,10 +546,7 @@ impl Settings {
|
|||||||
merge(&mut self.autosave, data.autosave);
|
merge(&mut self.autosave, data.autosave);
|
||||||
merge(&mut self.default_dock_anchor, data.default_dock_anchor);
|
merge(&mut self.default_dock_anchor, data.default_dock_anchor);
|
||||||
merge(&mut self.base_keymap, data.base_keymap);
|
merge(&mut self.base_keymap, data.base_keymap);
|
||||||
merge(
|
merge(&mut self.features.copilot, data.features.copilot);
|
||||||
&mut self.enable_copilot_integration,
|
|
||||||
data.enable_copilot_integration,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.editor_overrides = data.editor;
|
self.editor_overrides = data.editor;
|
||||||
self.git_overrides = data.git.unwrap_or_default();
|
self.git_overrides = data.git.unwrap_or_default();
|
||||||
@ -596,12 +570,15 @@ impl Settings {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copilot_on(&self, language: Option<&str>) -> bool {
|
pub fn features(&self) -> &Features {
|
||||||
if self.enable_copilot_integration {
|
&self.features
|
||||||
self.language_setting(language, |settings| settings.copilot.map(Into::into))
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn show_copilot_suggestions(&self, language: Option<&str>) -> bool {
|
||||||
|
self.features.copilot
|
||||||
|
&& self.language_setting(language, |settings| {
|
||||||
|
settings.show_copilot_suggestions.map(Into::into)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 {
|
pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 {
|
||||||
@ -740,7 +717,7 @@ impl Settings {
|
|||||||
format_on_save: Some(FormatOnSave::On),
|
format_on_save: Some(FormatOnSave::On),
|
||||||
formatter: Some(Formatter::LanguageServer),
|
formatter: Some(Formatter::LanguageServer),
|
||||||
enable_language_server: Some(true),
|
enable_language_server: Some(true),
|
||||||
copilot: Some(OnOff::On),
|
show_copilot_suggestions: Some(true),
|
||||||
},
|
},
|
||||||
editor_overrides: Default::default(),
|
editor_overrides: Default::default(),
|
||||||
journal_defaults: Default::default(),
|
journal_defaults: Default::default(),
|
||||||
@ -760,7 +737,7 @@ impl Settings {
|
|||||||
telemetry_overrides: Default::default(),
|
telemetry_overrides: Default::default(),
|
||||||
auto_update: true,
|
auto_update: true,
|
||||||
base_keymap: Default::default(),
|
base_keymap: Default::default(),
|
||||||
enable_copilot_integration: true,
|
features: Features { copilot: true },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1125,7 +1102,7 @@ mod tests {
|
|||||||
{
|
{
|
||||||
"language_overrides": {
|
"language_overrides": {
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"copilot": "off"
|
"show_copilot_suggestions": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1135,7 +1112,7 @@ mod tests {
|
|||||||
settings.languages.insert(
|
settings.languages.insert(
|
||||||
"Rust".into(),
|
"Rust".into(),
|
||||||
EditorSettings {
|
EditorSettings {
|
||||||
copilot: Some(OnOff::On),
|
show_copilot_suggestions: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -1144,10 +1121,10 @@ mod tests {
|
|||||||
{
|
{
|
||||||
"language_overrides": {
|
"language_overrides": {
|
||||||
"Rust": {
|
"Rust": {
|
||||||
"copilot": "on"
|
"show_copilot_suggestions": true
|
||||||
},
|
},
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"copilot": "off"
|
"show_copilot_suggestions": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1163,21 +1140,21 @@ mod tests {
|
|||||||
{
|
{
|
||||||
"languages": {
|
"languages": {
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"copilot": "off"
|
"show_copilot_suggestions": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
.unindent(),
|
.unindent(),
|
||||||
|settings| {
|
|settings| {
|
||||||
settings.editor.copilot = Some(OnOff::On);
|
settings.editor.show_copilot_suggestions = Some(true);
|
||||||
},
|
},
|
||||||
r#"
|
r#"
|
||||||
{
|
{
|
||||||
"copilot": "on",
|
"show_copilot_suggestions": true,
|
||||||
"languages": {
|
"languages": {
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"copilot": "off"
|
"show_copilot_suggestions": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1187,13 +1164,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_update_langauge_copilot() {
|
fn test_update_language_copilot() {
|
||||||
assert_new_settings(
|
assert_new_settings(
|
||||||
r#"
|
r#"
|
||||||
{
|
{
|
||||||
"languages": {
|
"languages": {
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"copilot": "off"
|
"show_copilot_suggestions": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1203,7 +1180,7 @@ mod tests {
|
|||||||
settings.languages.insert(
|
settings.languages.insert(
|
||||||
"Rust".into(),
|
"Rust".into(),
|
||||||
EditorSettings {
|
EditorSettings {
|
||||||
copilot: Some(OnOff::On),
|
show_copilot_suggestions: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -1212,10 +1189,10 @@ mod tests {
|
|||||||
{
|
{
|
||||||
"languages": {
|
"languages": {
|
||||||
"Rust": {
|
"Rust": {
|
||||||
"copilot": "on"
|
"show_copilot_suggestions": true
|
||||||
},
|
},
|
||||||
"JSON": {
|
"JSON": {
|
||||||
"copilot": "off"
|
"show_copilot_suggestions": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,7 @@ export default function editor(colorScheme: ColorScheme) {
|
|||||||
activeLineBackground: withOpacity(background(layer, "on"), 0.75),
|
activeLineBackground: withOpacity(background(layer, "on"), 0.75),
|
||||||
highlightedLineBackground: background(layer, "on"),
|
highlightedLineBackground: background(layer, "on"),
|
||||||
// Inline autocomplete suggestions, Co-pilot suggestions, etc.
|
// Inline autocomplete suggestions, Co-pilot suggestions, etc.
|
||||||
suggestion: {
|
suggestion: syntax.predictive,
|
||||||
color: syntax.predictive.color,
|
|
||||||
},
|
|
||||||
codeActions: {
|
codeActions: {
|
||||||
indicator: {
|
indicator: {
|
||||||
color: foreground(layer, "variant"),
|
color: foreground(layer, "variant"),
|
||||||
|
@ -181,6 +181,7 @@ function buildDefaultSyntax(colorScheme: ColorScheme): Syntax {
|
|||||||
},
|
},
|
||||||
predictive: {
|
predictive: {
|
||||||
color: color.predictive,
|
color: color.predictive,
|
||||||
|
italic: true,
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
color: color.emphasis,
|
color: color.emphasis,
|
||||||
|
Loading…
Reference in New Issue
Block a user