Eliminate GPUI View, ViewContext, and WindowContext types (#22632)
There's still a bit more work to do on this, but this PR is compiling (with warnings) after eliminating the key types. When the tasks below are complete, this will be the new narrative for GPUI: - `Entity<T>` - This replaces `View<T>`/`Model<T>`. It represents a unit of state, and if `T` implements `Render`, then `Entity<T>` implements `Element`. - `&mut App` This replaces `AppContext` and represents the app. - `&mut Context<T>` This replaces `ModelContext` and derefs to `App`. It is provided by the framework when updating an entity. - `&mut Window` Broken out of `&mut WindowContext` which no longer exists. Every method that once took `&mut WindowContext` now takes `&mut Window, &mut App` and every method that took `&mut ViewContext<T>` now takes `&mut Window, &mut Context<T>` Not pictured here are the two other failed attempts. It's been quite a month! Tasks: - [x] Remove `View`, `ViewContext`, `WindowContext` and thread through `Window` - [x] [@cole-miller @mikayla-maki] Redraw window when entities change - [x] [@cole-miller @mikayla-maki] Get examples and Zed running - [x] [@cole-miller @mikayla-maki] Fix Zed rendering - [x] [@mikayla-maki] Fix todo! macros and comments - [x] Fix a bug where the editor would not be redrawn because of view caching - [x] remove publicness window.notify() and replace with `AppContext::notify` - [x] remove `observe_new_window_models`, replace with `observe_new_models` with an optional window - [x] Fix a bug where the project panel would not be redrawn because of the wrong refresh() call being used - [x] Fix the tests - [x] Fix warnings by eliminating `Window` params or using `_` - [x] Fix conflicts - [x] Simplify generic code where possible - [x] Rename types - [ ] Update docs ### issues post merge - [x] Issues switching between normal and insert mode - [x] Assistant re-rendering failure - [x] Vim test failures - [x] Mac build issue Release Notes: - N/A --------- Co-authored-by: Antonio Scandurra <me@as-cii.com> Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: Mikayla <mikayla@zed.dev> Co-authored-by: Joseph <joseph@zed.dev> Co-authored-by: max <max@zed.dev> Co-authored-by: Michael Sloan <michael@zed.dev> Co-authored-by: Mikayla Maki <mikaylamaki@Mikaylas-MacBook-Pro.local> Co-authored-by: Mikayla <mikayla.c.maki@gmail.com> Co-authored-by: joão <joao@zed.dev>
This commit is contained in:
parent
21b4a0d50e
commit
6fca1d2b0b
648 changed files with 36248 additions and 28208 deletions
|
@ -11,8 +11,8 @@ use collections::{HashMap, HashSet};
|
|||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
|
||||
use gpui::{
|
||||
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Global, Model,
|
||||
ModelContext, Task, WeakModel,
|
||||
actions, App, AppContext as _, AsyncAppContext, Context, Entity, EntityId, EventEmitter,
|
||||
Global, Task, WeakEntity,
|
||||
};
|
||||
use http_client::github::get_release_by_tag_name;
|
||||
use http_client::HttpClient;
|
||||
|
@ -58,11 +58,11 @@ pub fn init(
|
|||
fs: Arc<dyn Fs>,
|
||||
http: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
cx: &mut AppContext,
|
||||
cx: &mut App,
|
||||
) {
|
||||
copilot_chat::init(fs, http.clone(), cx);
|
||||
|
||||
let copilot = cx.new_model({
|
||||
let copilot = cx.new({
|
||||
let node_runtime = node_runtime.clone();
|
||||
move |cx| Copilot::start(new_server_id, http, node_runtime, cx)
|
||||
});
|
||||
|
@ -209,8 +209,8 @@ struct RegisteredBuffer {
|
|||
impl RegisteredBuffer {
|
||||
fn report_changes(
|
||||
&mut self,
|
||||
buffer: &Model<Buffer>,
|
||||
cx: &mut ModelContext<Copilot>,
|
||||
buffer: &Entity<Buffer>,
|
||||
cx: &mut Context<Copilot>,
|
||||
) -> oneshot::Receiver<(i32, BufferSnapshot)> {
|
||||
let (done_tx, done_rx) = oneshot::channel();
|
||||
|
||||
|
@ -304,7 +304,7 @@ pub struct Copilot {
|
|||
http: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
server: CopilotServer,
|
||||
buffers: HashSet<WeakModel<Buffer>>,
|
||||
buffers: HashSet<WeakEntity<Buffer>>,
|
||||
server_id: LanguageServerId,
|
||||
_subscription: gpui::Subscription,
|
||||
}
|
||||
|
@ -317,17 +317,17 @@ pub enum Event {
|
|||
|
||||
impl EventEmitter<Event> for Copilot {}
|
||||
|
||||
struct GlobalCopilot(Model<Copilot>);
|
||||
struct GlobalCopilot(Entity<Copilot>);
|
||||
|
||||
impl Global for GlobalCopilot {}
|
||||
|
||||
impl Copilot {
|
||||
pub fn global(cx: &AppContext) -> Option<Model<Self>> {
|
||||
pub fn global(cx: &App) -> Option<Entity<Self>> {
|
||||
cx.try_global::<GlobalCopilot>()
|
||||
.map(|model| model.0.clone())
|
||||
}
|
||||
|
||||
pub fn set_global(copilot: Model<Self>, cx: &mut AppContext) {
|
||||
pub fn set_global(copilot: Entity<Self>, cx: &mut App) {
|
||||
cx.set_global(GlobalCopilot(copilot));
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ impl Copilot {
|
|||
new_server_id: LanguageServerId,
|
||||
http: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let mut this = Self {
|
||||
server_id: new_server_id,
|
||||
|
@ -351,10 +351,7 @@ impl Copilot {
|
|||
this
|
||||
}
|
||||
|
||||
fn shutdown_language_server(
|
||||
&mut self,
|
||||
_cx: &mut ModelContext<Self>,
|
||||
) -> impl Future<Output = ()> {
|
||||
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,
|
||||
|
@ -367,7 +364,7 @@ impl Copilot {
|
|||
}
|
||||
}
|
||||
|
||||
fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Self>) {
|
||||
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();
|
||||
|
@ -390,7 +387,7 @@ impl Copilot {
|
|||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn fake(cx: &mut gpui::TestAppContext) -> (Model<Self>, lsp::FakeLanguageServer) {
|
||||
pub fn fake(cx: &mut gpui::TestAppContext) -> (Entity<Self>, lsp::FakeLanguageServer) {
|
||||
use lsp::FakeLanguageServer;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
||||
|
@ -407,7 +404,7 @@ impl Copilot {
|
|||
);
|
||||
let http = http_client::FakeHttpClient::create(|_| async { unreachable!() });
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
let this = cx.new_model(|cx| Self {
|
||||
let this = cx.new(|cx| Self {
|
||||
server_id: LanguageServerId(0),
|
||||
http: http.clone(),
|
||||
node_runtime,
|
||||
|
@ -426,7 +423,7 @@ impl Copilot {
|
|||
new_server_id: LanguageServerId,
|
||||
http: Arc<dyn HttpClient>,
|
||||
node_runtime: NodeRuntime,
|
||||
this: WeakModel<Self>,
|
||||
this: WeakEntity<Self>,
|
||||
mut cx: AsyncAppContext,
|
||||
) {
|
||||
let start_language_server = async {
|
||||
|
@ -513,7 +510,7 @@ impl Copilot {
|
|||
.ok();
|
||||
}
|
||||
|
||||
pub fn sign_in(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
pub fn sign_in(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
|
||||
if let CopilotServer::Running(server) = &mut self.server {
|
||||
let task = match &server.sign_in_status {
|
||||
SignInStatus::Authorized { .. } => Task::ready(Ok(())).shared(),
|
||||
|
@ -598,7 +595,7 @@ impl Copilot {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sign_out(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
pub fn sign_out(&mut self, cx: &mut Context<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();
|
||||
|
@ -613,7 +610,7 @@ impl Copilot {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn reinstall(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
|
||||
pub fn reinstall(&mut self, cx: &mut Context<Self>) -> Task<()> {
|
||||
let start_task = cx
|
||||
.spawn({
|
||||
let http = self.http.clone();
|
||||
|
@ -643,7 +640,7 @@ impl Copilot {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn register_buffer(&mut self, buffer: &Model<Buffer>, cx: &mut ModelContext<Self>) {
|
||||
pub fn register_buffer(&mut self, buffer: &Entity<Buffer>, cx: &mut Context<Self>) {
|
||||
let weak_buffer = buffer.downgrade();
|
||||
self.buffers.insert(weak_buffer.clone());
|
||||
|
||||
|
@ -699,9 +696,9 @@ impl Copilot {
|
|||
|
||||
fn handle_buffer_event(
|
||||
&mut self,
|
||||
buffer: Model<Buffer>,
|
||||
buffer: Entity<Buffer>,
|
||||
event: &language::BufferEvent,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Result<()> {
|
||||
if let Ok(server) = self.server.as_running() {
|
||||
if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.entity_id())
|
||||
|
@ -760,7 +757,7 @@ impl Copilot {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister_buffer(&mut self, buffer: &WeakModel<Buffer>) {
|
||||
fn unregister_buffer(&mut self, buffer: &WeakEntity<Buffer>) {
|
||||
if let Ok(server) = self.server.as_running() {
|
||||
if let Some(buffer) = server.registered_buffers.remove(&buffer.entity_id()) {
|
||||
server
|
||||
|
@ -777,9 +774,9 @@ impl Copilot {
|
|||
|
||||
pub fn completions<T>(
|
||||
&mut self,
|
||||
buffer: &Model<Buffer>,
|
||||
buffer: &Entity<Buffer>,
|
||||
position: T,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Vec<Completion>>>
|
||||
where
|
||||
T: ToPointUtf16,
|
||||
|
@ -789,9 +786,9 @@ impl Copilot {
|
|||
|
||||
pub fn completions_cycling<T>(
|
||||
&mut self,
|
||||
buffer: &Model<Buffer>,
|
||||
buffer: &Entity<Buffer>,
|
||||
position: T,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Vec<Completion>>>
|
||||
where
|
||||
T: ToPointUtf16,
|
||||
|
@ -802,7 +799,7 @@ impl Copilot {
|
|||
pub fn accept_completion(
|
||||
&mut self,
|
||||
completion: &Completion,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let server = match self.server.as_authenticated() {
|
||||
Ok(server) => server,
|
||||
|
@ -823,7 +820,7 @@ impl Copilot {
|
|||
pub fn discard_completions(
|
||||
&mut self,
|
||||
completions: &[Completion],
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let server = match self.server.as_authenticated() {
|
||||
Ok(server) => server,
|
||||
|
@ -846,9 +843,9 @@ impl Copilot {
|
|||
|
||||
fn request_completions<R, T>(
|
||||
&mut self,
|
||||
buffer: &Model<Buffer>,
|
||||
buffer: &Entity<Buffer>,
|
||||
position: T,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Vec<Completion>>>
|
||||
where
|
||||
R: 'static
|
||||
|
@ -937,11 +934,7 @@ impl Copilot {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_sign_in_status(
|
||||
&mut self,
|
||||
lsp_status: request::SignInStatus,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
fn update_sign_in_status(&mut self, lsp_status: request::SignInStatus, cx: &mut Context<Self>) {
|
||||
self.buffers.retain(|buffer| buffer.is_upgradable());
|
||||
|
||||
if let Ok(server) = self.server.as_running() {
|
||||
|
@ -983,7 +976,7 @@ fn id_for_language(language: Option<&Arc<Language>>) -> String {
|
|||
.unwrap_or_else(|| "plaintext".to_string())
|
||||
}
|
||||
|
||||
fn uri_for_buffer(buffer: &Model<Buffer>, cx: &AppContext) -> lsp::Url {
|
||||
fn uri_for_buffer(buffer: &Entity<Buffer>, cx: &App) -> lsp::Url {
|
||||
if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
|
||||
lsp::Url::from_file_path(file.abs_path(cx)).unwrap()
|
||||
} else {
|
||||
|
@ -1073,7 +1066,7 @@ mod tests {
|
|||
async fn test_buffer_management(cx: &mut TestAppContext) {
|
||||
let (copilot, mut lsp) = Copilot::fake(cx);
|
||||
|
||||
let buffer_1 = cx.new_model(|cx| Buffer::local("Hello", cx));
|
||||
let buffer_1 = cx.new(|cx| Buffer::local("Hello", cx));
|
||||
let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64())
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
@ -1091,7 +1084,7 @@ mod tests {
|
|||
}
|
||||
);
|
||||
|
||||
let buffer_2 = cx.new_model(|cx| Buffer::local("Goodbye", cx));
|
||||
let buffer_2 = cx.new(|cx| Buffer::local("Goodbye", cx));
|
||||
let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64())
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
@ -1246,11 +1239,11 @@ mod tests {
|
|||
&self.path
|
||||
}
|
||||
|
||||
fn full_path(&self, _: &AppContext) -> PathBuf {
|
||||
fn full_path(&self, _: &App) -> PathBuf {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn file_name<'a>(&'a self, _: &'a AppContext) -> &'a std::ffi::OsStr {
|
||||
fn file_name<'a>(&'a self, _: &'a App) -> &'a std::ffi::OsStr {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -1258,11 +1251,11 @@ mod tests {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_proto(&self, _: &AppContext) -> rpc::proto::File {
|
||||
fn to_proto(&self, _: &App) -> rpc::proto::File {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn worktree_id(&self, _: &AppContext) -> settings::WorktreeId {
|
||||
fn worktree_id(&self, _: &App) -> settings::WorktreeId {
|
||||
settings::WorktreeId::from_usize(0)
|
||||
}
|
||||
|
||||
|
@ -1272,15 +1265,15 @@ mod tests {
|
|||
}
|
||||
|
||||
impl language::LocalFile for File {
|
||||
fn abs_path(&self, _: &AppContext) -> PathBuf {
|
||||
fn abs_path(&self, _: &App) -> PathBuf {
|
||||
self.abs_path.clone()
|
||||
}
|
||||
|
||||
fn load(&self, _: &AppContext) -> Task<Result<String>> {
|
||||
fn load(&self, _: &App) -> Task<Result<String>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _cx: &AppContext) -> Task<Result<Vec<u8>>> {
|
||||
fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use anyhow::{anyhow, Result};
|
|||
use chrono::DateTime;
|
||||
use fs::Fs;
|
||||
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
|
||||
use gpui::{prelude::*, AppContext, AsyncAppContext, Global};
|
||||
use gpui::{prelude::*, App, AsyncAppContext, Global};
|
||||
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
|
||||
use paths::home_dir;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -181,7 +181,7 @@ impl TryFrom<ApiTokenResponse> for ApiToken {
|
|||
}
|
||||
}
|
||||
|
||||
struct GlobalCopilotChat(gpui::Model<CopilotChat>);
|
||||
struct GlobalCopilotChat(gpui::Entity<CopilotChat>);
|
||||
|
||||
impl Global for GlobalCopilotChat {}
|
||||
|
||||
|
@ -191,8 +191,8 @@ pub struct CopilotChat {
|
|||
client: Arc<dyn HttpClient>,
|
||||
}
|
||||
|
||||
pub fn init(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &mut AppContext) {
|
||||
let copilot_chat = cx.new_model(|cx| CopilotChat::new(fs, client, cx));
|
||||
pub fn init(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &mut App) {
|
||||
let copilot_chat = cx.new(|cx| CopilotChat::new(fs, client, cx));
|
||||
cx.set_global(GlobalCopilotChat(copilot_chat));
|
||||
}
|
||||
|
||||
|
@ -215,12 +215,12 @@ fn copilot_chat_config_paths() -> [PathBuf; 2] {
|
|||
}
|
||||
|
||||
impl CopilotChat {
|
||||
pub fn global(cx: &AppContext) -> Option<gpui::Model<Self>> {
|
||||
pub fn global(cx: &App) -> Option<gpui::Entity<Self>> {
|
||||
cx.try_global::<GlobalCopilotChat>()
|
||||
.map(|model| model.0.clone())
|
||||
}
|
||||
|
||||
pub fn new(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &AppContext) -> Self {
|
||||
pub fn new(fs: Arc<dyn Fs>, client: Arc<dyn HttpClient>, cx: &App) -> Self {
|
||||
let config_paths = copilot_chat_config_paths();
|
||||
|
||||
let resolve_config_path = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{Completion, Copilot};
|
||||
use anyhow::Result;
|
||||
use gpui::{AppContext, EntityId, Model, ModelContext, Task};
|
||||
use gpui::{App, Context, Entity, EntityId, Task};
|
||||
use inline_completion::{Direction, InlineCompletion, InlineCompletionProvider};
|
||||
use language::{
|
||||
language_settings::{all_language_settings, AllLanguageSettings},
|
||||
|
@ -19,11 +19,11 @@ pub struct CopilotCompletionProvider {
|
|||
file_extension: Option<String>,
|
||||
pending_refresh: Option<Task<Result<()>>>,
|
||||
pending_cycling_refresh: Option<Task<Result<()>>>,
|
||||
copilot: Model<Copilot>,
|
||||
copilot: Entity<Copilot>,
|
||||
}
|
||||
|
||||
impl CopilotCompletionProvider {
|
||||
pub fn new(copilot: Model<Copilot>) -> Self {
|
||||
pub fn new(copilot: Entity<Copilot>) -> Self {
|
||||
Self {
|
||||
cycled: false,
|
||||
buffer_id: None,
|
||||
|
@ -73,9 +73,9 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
|
|||
|
||||
fn is_enabled(
|
||||
&self,
|
||||
buffer: &Model<Buffer>,
|
||||
buffer: &Entity<Buffer>,
|
||||
cursor_position: language::Anchor,
|
||||
cx: &AppContext,
|
||||
cx: &App,
|
||||
) -> bool {
|
||||
if !self.copilot.read(cx).status().is_authorized() {
|
||||
return false;
|
||||
|
@ -90,10 +90,10 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
|
|||
|
||||
fn refresh(
|
||||
&mut self,
|
||||
buffer: Model<Buffer>,
|
||||
buffer: Entity<Buffer>,
|
||||
cursor_position: language::Anchor,
|
||||
debounce: bool,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let copilot = self.copilot.clone();
|
||||
self.pending_refresh = Some(cx.spawn(|this, mut cx| async move {
|
||||
|
@ -139,10 +139,10 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
|
|||
|
||||
fn cycle(
|
||||
&mut self,
|
||||
buffer: Model<Buffer>,
|
||||
buffer: Entity<Buffer>,
|
||||
cursor_position: language::Anchor,
|
||||
direction: Direction,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if self.cycled {
|
||||
match direction {
|
||||
|
@ -194,7 +194,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
|
|||
}
|
||||
}
|
||||
|
||||
fn accept(&mut self, cx: &mut ModelContext<Self>) {
|
||||
fn accept(&mut self, cx: &mut Context<Self>) {
|
||||
if let Some(completion) = self.active_completion() {
|
||||
self.copilot
|
||||
.update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
|
||||
|
@ -202,7 +202,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
|
|||
}
|
||||
}
|
||||
|
||||
fn discard(&mut self, cx: &mut ModelContext<Self>) {
|
||||
fn discard(&mut self, cx: &mut Context<Self>) {
|
||||
let settings = AllLanguageSettings::get_global(cx);
|
||||
|
||||
let copilot_enabled = settings.inline_completions_enabled(None, None, cx);
|
||||
|
@ -220,9 +220,9 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
|
|||
|
||||
fn suggest(
|
||||
&mut self,
|
||||
buffer: &Model<Buffer>,
|
||||
buffer: &Entity<Buffer>,
|
||||
cursor_position: language::Anchor,
|
||||
cx: &mut ModelContext<Self>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<InlineCompletion> {
|
||||
let buffer_id = buffer.entity_id();
|
||||
let buffer = buffer.read(cx);
|
||||
|
@ -280,7 +280,7 @@ mod tests {
|
|||
};
|
||||
use fs::FakeFs;
|
||||
use futures::StreamExt;
|
||||
use gpui::{BackgroundExecutor, Context, TestAppContext, UpdateGlobal};
|
||||
use gpui::{AppContext as _, BackgroundExecutor, TestAppContext, UpdateGlobal};
|
||||
use indoc::indoc;
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
|
||||
|
@ -309,9 +309,9 @@ mod tests {
|
|||
cx,
|
||||
)
|
||||
.await;
|
||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), cx)
|
||||
let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
|
||||
});
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
|
@ -339,7 +339,7 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert!(editor.context_menu_visible());
|
||||
assert!(!editor.context_menu_contains_inline_completion());
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
|
@ -350,7 +350,7 @@ mod tests {
|
|||
// Confirming a non-copilot completion inserts it and hides the context menu, without showing
|
||||
// the copilot suggestion afterwards.
|
||||
editor
|
||||
.confirm_completion(&Default::default(), cx)
|
||||
.confirm_completion(&Default::default(), window, cx)
|
||||
.unwrap()
|
||||
.detach();
|
||||
assert!(!editor.context_menu_visible());
|
||||
|
@ -386,7 +386,7 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
assert!(!editor.context_menu_visible());
|
||||
assert!(editor.has_active_inline_completion());
|
||||
// Since only the copilot is available, it's shown inline
|
||||
|
@ -397,7 +397,7 @@ mod tests {
|
|||
// Ensure existing inline completion is interpolated when inserting again.
|
||||
cx.simulate_keystroke("c");
|
||||
executor.run_until_parked();
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
assert!(!editor.context_menu_visible());
|
||||
assert!(!editor.context_menu_contains_inline_completion());
|
||||
assert!(editor.has_active_inline_completion());
|
||||
|
@ -416,7 +416,7 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert!(!editor.context_menu_visible());
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert!(!editor.context_menu_contains_inline_completion());
|
||||
|
@ -424,19 +424,19 @@ mod tests {
|
|||
assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
|
||||
|
||||
// Canceling should remove the active Copilot suggestion.
|
||||
editor.cancel(&Default::default(), cx);
|
||||
editor.cancel(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.c\ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
|
||||
|
||||
// After canceling, tabbing shouldn't insert the previously shown suggestion.
|
||||
editor.tab(&Default::default(), cx);
|
||||
editor.tab(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.c \ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.c \ntwo\nthree\n");
|
||||
|
||||
// When undoing the previously active suggestion is shown again.
|
||||
editor.undo(&Default::default(), cx);
|
||||
editor.undo(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.c\ntwo\nthree\n");
|
||||
|
@ -444,25 +444,25 @@ mod tests {
|
|||
|
||||
// If an edit occurs outside of this editor, the suggestion is still correctly interpolated.
|
||||
cx.update_buffer(|buffer, cx| buffer.edit([(5..5, "o")], None, cx));
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
|
||||
|
||||
// AcceptInlineCompletion when there is an active suggestion inserts it.
|
||||
editor.accept_inline_completion(&Default::default(), cx);
|
||||
editor.accept_inline_completion(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.copilot2\ntwo\nthree\n");
|
||||
|
||||
// When undoing the previously active suggestion is shown again.
|
||||
editor.undo(&Default::default(), cx);
|
||||
editor.undo(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.copilot2\ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
|
||||
|
||||
// Hide suggestion.
|
||||
editor.cancel(&Default::default(), cx);
|
||||
editor.cancel(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.co\ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.co\ntwo\nthree\n");
|
||||
|
@ -471,16 +471,16 @@ mod tests {
|
|||
// If an edit occurs outside of this editor but no suggestion is being shown,
|
||||
// we won't make it visible.
|
||||
cx.update_buffer(|buffer, cx| buffer.edit([(6..6, "p")], None, cx));
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one.cop\ntwo\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one.cop\ntwo\nthree\n");
|
||||
});
|
||||
|
||||
// Reset the editor to verify how suggestions behave when tabbing on leading indentation.
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.set_text("fn foo() {\n \n}", cx);
|
||||
editor.change_selections(None, cx, |s| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.set_text("fn foo() {\n \n}", window, cx);
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select_ranges([Point::new(1, 2)..Point::new(1, 2)])
|
||||
});
|
||||
});
|
||||
|
@ -494,21 +494,23 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
|
||||
cx.update_editor(|editor, cx| editor.next_inline_completion(&Default::default(), cx));
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.next_inline_completion(&Default::default(), window, cx)
|
||||
});
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
|
||||
assert_eq!(editor.text(cx), "fn foo() {\n \n}");
|
||||
|
||||
// Tabbing inside of leading whitespace inserts indentation without accepting the suggestion.
|
||||
editor.tab(&Default::default(), cx);
|
||||
editor.tab(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.text(cx), "fn foo() {\n \n}");
|
||||
assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
|
||||
|
||||
// Using AcceptInlineCompletion again accepts the suggestion.
|
||||
editor.accept_inline_completion(&Default::default(), cx);
|
||||
editor.accept_inline_completion(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.text(cx), "fn foo() {\n let x = 4;\n}");
|
||||
assert_eq!(editor.display_text(cx), "fn foo() {\n let x = 4;\n}");
|
||||
|
@ -535,9 +537,9 @@ mod tests {
|
|||
cx,
|
||||
)
|
||||
.await;
|
||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), cx)
|
||||
let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
|
||||
});
|
||||
|
||||
// Setup the editor with a completion request.
|
||||
|
@ -566,17 +568,17 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert!(editor.has_active_inline_completion());
|
||||
|
||||
// Accepting the first word of the suggestion should only accept the first word and still show the rest.
|
||||
editor.accept_partial_inline_completion(&Default::default(), cx);
|
||||
editor.accept_partial_inline_completion(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.text(cx), "one.copilot\ntwo\nthree\n");
|
||||
assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
|
||||
|
||||
// Accepting next word should accept the non-word and copilot suggestion should be gone
|
||||
editor.accept_partial_inline_completion(&Default::default(), cx);
|
||||
editor.accept_partial_inline_completion(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.text(cx), "one.copilot1\ntwo\nthree\n");
|
||||
assert_eq!(editor.display_text(cx), "one.copilot1\ntwo\nthree\n");
|
||||
|
@ -608,11 +610,11 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert!(editor.has_active_inline_completion());
|
||||
|
||||
// Accepting the first word (non-word) of the suggestion should only accept the first word and still show the rest.
|
||||
editor.accept_partial_inline_completion(&Default::default(), cx);
|
||||
editor.accept_partial_inline_completion(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.text(cx), "one.123. \ntwo\nthree\n");
|
||||
assert_eq!(
|
||||
|
@ -621,7 +623,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// Accepting next word should accept the next word and copilot suggestion should still exist
|
||||
editor.accept_partial_inline_completion(&Default::default(), cx);
|
||||
editor.accept_partial_inline_completion(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.text(cx), "one.123. copilot\ntwo\nthree\n");
|
||||
assert_eq!(
|
||||
|
@ -630,7 +632,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// Accepting the whitespace should accept the non-word/whitespaces with newline and copilot suggestion should be gone
|
||||
editor.accept_partial_inline_completion(&Default::default(), cx);
|
||||
editor.accept_partial_inline_completion(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.text(cx), "one.123. copilot\n 456\ntwo\nthree\n");
|
||||
assert_eq!(
|
||||
|
@ -659,9 +661,9 @@ mod tests {
|
|||
cx,
|
||||
)
|
||||
.await;
|
||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), cx)
|
||||
let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
|
||||
});
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
|
@ -679,31 +681,33 @@ mod tests {
|
|||
}],
|
||||
vec![],
|
||||
);
|
||||
cx.update_editor(|editor, cx| editor.next_inline_completion(&Default::default(), cx));
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.next_inline_completion(&Default::default(), window, cx)
|
||||
});
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\ntw\nthree\n");
|
||||
|
||||
editor.backspace(&Default::default(), cx);
|
||||
editor.backspace(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\nt\nthree\n");
|
||||
|
||||
editor.backspace(&Default::default(), cx);
|
||||
editor.backspace(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\n\nthree\n");
|
||||
|
||||
// Deleting across the original suggestion range invalidates it.
|
||||
editor.backspace(&Default::default(), cx);
|
||||
editor.backspace(&Default::default(), window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\nthree\n");
|
||||
|
||||
// Undoing the deletion restores the suggestion.
|
||||
editor.undo(&Default::default(), cx);
|
||||
editor.undo(&Default::default(), window, cx);
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
assert_eq!(editor.text(cx), "one\n\nthree\n");
|
||||
|
@ -716,9 +720,9 @@ mod tests {
|
|||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
|
||||
let buffer_1 = cx.new_model(|cx| Buffer::local("a = 1\nb = 2\n", cx));
|
||||
let buffer_2 = cx.new_model(|cx| Buffer::local("c = 3\nd = 4\n", cx));
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let buffer_1 = cx.new(|cx| Buffer::local("a = 1\nb = 2\n", cx));
|
||||
let buffer_2 = cx.new(|cx| Buffer::local("c = 3\nd = 4\n", cx));
|
||||
let multibuffer = cx.new(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
|
@ -738,12 +742,18 @@ mod tests {
|
|||
);
|
||||
multibuffer
|
||||
});
|
||||
let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, true, cx));
|
||||
editor.update(cx, |editor, cx| editor.focus(cx)).unwrap();
|
||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||
let editor = cx
|
||||
.add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, true, window, cx));
|
||||
editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), cx)
|
||||
.update(cx, |editor, window, cx| {
|
||||
use gpui::Focusable;
|
||||
window.focus(&editor.focus_handle(cx));
|
||||
})
|
||||
.unwrap();
|
||||
let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
|
||||
editor
|
||||
.update(cx, |editor, window, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
|
@ -756,15 +766,15 @@ mod tests {
|
|||
}],
|
||||
vec![],
|
||||
);
|
||||
_ = editor.update(cx, |editor, cx| {
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
// Ensure copilot suggestions are shown for the first excerpt.
|
||||
editor.change_selections(None, cx, |s| {
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
|
||||
});
|
||||
editor.next_inline_completion(&Default::default(), cx);
|
||||
editor.next_inline_completion(&Default::default(), window, cx);
|
||||
});
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
_ = editor.update(cx, |editor, cx| {
|
||||
_ = editor.update(cx, |editor, _, cx| {
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(
|
||||
editor.display_text(cx),
|
||||
|
@ -782,9 +792,9 @@ mod tests {
|
|||
}],
|
||||
vec![],
|
||||
);
|
||||
_ = editor.update(cx, |editor, cx| {
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
// Move to another excerpt, ensuring the suggestion gets cleared.
|
||||
editor.change_selections(None, cx, |s| {
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
|
||||
});
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
|
@ -795,7 +805,7 @@ mod tests {
|
|||
assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
|
||||
|
||||
// Type a character, ensuring we don't even try to interpolate the previous suggestion.
|
||||
editor.handle_input(" ", cx);
|
||||
editor.handle_input(" ", window, cx);
|
||||
assert!(!editor.has_active_inline_completion());
|
||||
assert_eq!(
|
||||
editor.display_text(cx),
|
||||
|
@ -806,7 +816,7 @@ mod tests {
|
|||
|
||||
// Ensure the new suggestion is displayed when the debounce timeout expires.
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
_ = editor.update(cx, |editor, cx| {
|
||||
_ = editor.update(cx, |editor, _, cx| {
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(
|
||||
editor.display_text(cx),
|
||||
|
@ -835,9 +845,9 @@ mod tests {
|
|||
cx,
|
||||
)
|
||||
.await;
|
||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), cx)
|
||||
let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
|
||||
});
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
|
@ -864,9 +874,11 @@ mod tests {
|
|||
}],
|
||||
vec![],
|
||||
);
|
||||
cx.update_editor(|editor, cx| editor.next_inline_completion(&Default::default(), cx));
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.next_inline_completion(&Default::default(), window, cx)
|
||||
});
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
assert!(!editor.context_menu_visible());
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
|
@ -893,7 +905,7 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
assert!(!editor.context_menu_visible());
|
||||
assert!(editor.has_active_inline_completion());
|
||||
assert_eq!(editor.display_text(cx), "one\ntwo.foo()\nthree\n");
|
||||
|
@ -920,7 +932,7 @@ mod tests {
|
|||
vec![],
|
||||
);
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
cx.update_editor(|editor, cx| {
|
||||
cx.update_editor(|editor, _, cx| {
|
||||
assert!(editor.context_menu_visible());
|
||||
assert!(!editor.context_menu_contains_inline_completion());
|
||||
assert!(!editor.has_active_inline_completion(),);
|
||||
|
@ -963,7 +975,7 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let multibuffer = cx.new_model(|cx| {
|
||||
let multibuffer = cx.new(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
|
||||
multibuffer.push_excerpts(
|
||||
private_buffer.clone(),
|
||||
|
@ -983,12 +995,18 @@ mod tests {
|
|||
);
|
||||
multibuffer
|
||||
});
|
||||
let editor = cx.add_window(|cx| Editor::for_multibuffer(multibuffer, None, true, cx));
|
||||
editor.update(cx, |editor, cx| editor.focus(cx)).unwrap();
|
||||
let copilot_provider = cx.new_model(|_| CopilotCompletionProvider::new(copilot));
|
||||
let editor = cx
|
||||
.add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, true, window, cx));
|
||||
editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), cx)
|
||||
.update(cx, |editor, window, cx| {
|
||||
use gpui::Focusable;
|
||||
window.focus(&editor.focus_handle(cx))
|
||||
})
|
||||
.unwrap();
|
||||
let copilot_provider = cx.new(|_| CopilotCompletionProvider::new(copilot));
|
||||
editor
|
||||
.update(cx, |editor, window, cx| {
|
||||
editor.set_inline_completion_provider(Some(copilot_provider), window, cx)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
|
@ -1008,21 +1026,21 @@ mod tests {
|
|||
},
|
||||
);
|
||||
|
||||
_ = editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(None, cx, |selections| {
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(None, window, cx, |selections| {
|
||||
selections.select_ranges([Point::new(0, 0)..Point::new(0, 0)])
|
||||
});
|
||||
editor.refresh_inline_completion(true, false, cx);
|
||||
editor.refresh_inline_completion(true, false, window, cx);
|
||||
});
|
||||
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
assert!(copilot_requests.try_next().is_err());
|
||||
|
||||
_ = editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(None, cx, |s| {
|
||||
_ = editor.update(cx, |editor, window, cx| {
|
||||
editor.change_selections(None, window, cx, |s| {
|
||||
s.select_ranges([Point::new(5, 0)..Point::new(5, 0)])
|
||||
});
|
||||
editor.refresh_inline_completion(true, false, cx);
|
||||
editor.refresh_inline_completion(true, false, window, cx);
|
||||
});
|
||||
|
||||
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{request::PromptUserDeviceFlow, Copilot, Status};
|
||||
use gpui::{
|
||||
div, AppContext, ClipboardItem, DismissEvent, Element, EventEmitter, FocusHandle,
|
||||
FocusableView, InteractiveElement, IntoElement, Model, MouseDownEvent, ParentElement, Render,
|
||||
Styled, Subscription, ViewContext,
|
||||
div, App, ClipboardItem, Context, DismissEvent, Element, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, InteractiveElement, IntoElement, MouseDownEvent, ParentElement, Render, Styled,
|
||||
Subscription, Window,
|
||||
};
|
||||
use ui::{prelude::*, Button, Label, Vector, VectorName};
|
||||
use util::ResultExt as _;
|
||||
|
@ -13,21 +13,21 @@ const COPILOT_SIGN_UP_URL: &str = "https://github.com/features/copilot";
|
|||
|
||||
struct CopilotStartingToast;
|
||||
|
||||
pub fn initiate_sign_in(cx: &mut WindowContext) {
|
||||
pub fn initiate_sign_in(window: &mut Window, cx: &mut App) {
|
||||
let Some(copilot) = Copilot::global(cx) else {
|
||||
return;
|
||||
};
|
||||
let status = copilot.read(cx).status();
|
||||
let Some(workspace) = cx.window_handle().downcast::<Workspace>() else {
|
||||
let Some(workspace) = window.window_handle().downcast::<Workspace>() else {
|
||||
return;
|
||||
};
|
||||
match status {
|
||||
Status::Starting { task } => {
|
||||
let Some(workspace) = cx.window_handle().downcast::<Workspace>() else {
|
||||
let Some(workspace) = window.window_handle().downcast::<Workspace>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(workspace) = workspace.update(cx, |workspace, cx| {
|
||||
let Ok(workspace) = workspace.update(cx, |workspace, _window, cx| {
|
||||
workspace.show_toast(
|
||||
Toast::new(
|
||||
NotificationId::unique::<CopilotStartingToast>(),
|
||||
|
@ -70,8 +70,10 @@ pub fn initiate_sign_in(cx: &mut WindowContext) {
|
|||
_ => {
|
||||
copilot.update(cx, |this, cx| this.sign_in(cx)).detach();
|
||||
workspace
|
||||
.update(cx, |this, cx| {
|
||||
this.toggle_modal(cx, |cx| CopilotCodeVerification::new(&copilot, cx));
|
||||
.update(cx, |this, window, cx| {
|
||||
this.toggle_modal(window, cx, |_, cx| {
|
||||
CopilotCodeVerification::new(&copilot, cx)
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
@ -85,8 +87,8 @@ pub struct CopilotCodeVerification {
|
|||
_subscription: Subscription,
|
||||
}
|
||||
|
||||
impl FocusableView for CopilotCodeVerification {
|
||||
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
||||
impl Focusable for CopilotCodeVerification {
|
||||
fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +97,7 @@ impl EventEmitter<DismissEvent> for CopilotCodeVerification {}
|
|||
impl ModalView for CopilotCodeVerification {}
|
||||
|
||||
impl CopilotCodeVerification {
|
||||
pub fn new(copilot: &Model<Copilot>, cx: &mut ViewContext<Self>) -> Self {
|
||||
pub fn new(copilot: &Entity<Copilot>, cx: &mut Context<Self>) -> Self {
|
||||
let status = copilot.read(cx).status();
|
||||
Self {
|
||||
status,
|
||||
|
@ -113,15 +115,12 @@ impl CopilotCodeVerification {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_status(&mut self, status: Status, cx: &mut ViewContext<Self>) {
|
||||
pub fn set_status(&mut self, status: Status, cx: &mut Context<Self>) {
|
||||
self.status = status;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_device_code(
|
||||
data: &PromptUserDeviceFlow,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
fn render_device_code(data: &PromptUserDeviceFlow, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let copied = cx
|
||||
.read_from_clipboard()
|
||||
.map(|item| item.text().as_ref() == Some(&data.user_code))
|
||||
|
@ -136,9 +135,9 @@ impl CopilotCodeVerification {
|
|||
.justify_between()
|
||||
.on_mouse_down(gpui::MouseButton::Left, {
|
||||
let user_code = data.user_code.clone();
|
||||
move |_, cx| {
|
||||
move |_, window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(user_code.clone()));
|
||||
cx.refresh();
|
||||
window.refresh();
|
||||
}
|
||||
})
|
||||
.child(div().flex_1().child(Label::new(data.user_code.clone())))
|
||||
|
@ -152,7 +151,8 @@ impl CopilotCodeVerification {
|
|||
fn render_prompting_modal(
|
||||
connect_clicked: bool,
|
||||
data: &PromptUserDeviceFlow,
|
||||
cx: &mut ViewContext<Self>,
|
||||
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl Element {
|
||||
let connect_button_label = if connect_clicked {
|
||||
"Waiting for connection..."
|
||||
|
@ -177,7 +177,7 @@ impl CopilotCodeVerification {
|
|||
Button::new("connect-button", connect_button_label)
|
||||
.on_click({
|
||||
let verification_uri = data.verification_uri.clone();
|
||||
cx.listener(move |this, _, cx| {
|
||||
cx.listener(move |this, _, _window, cx| {
|
||||
cx.open_url(&verification_uri);
|
||||
this.connect_clicked = true;
|
||||
})
|
||||
|
@ -188,10 +188,10 @@ impl CopilotCodeVerification {
|
|||
.child(
|
||||
Button::new("copilot-enable-cancel-button", "Cancel")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
|
||||
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
|
||||
)
|
||||
}
|
||||
fn render_enabled_modal(cx: &mut ViewContext<Self>) -> impl Element {
|
||||
fn render_enabled_modal(cx: &mut Context<Self>) -> impl Element {
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.child(Headline::new("Copilot Enabled!").size(HeadlineSize::Large))
|
||||
|
@ -201,11 +201,11 @@ impl CopilotCodeVerification {
|
|||
.child(
|
||||
Button::new("copilot-enabled-done-button", "Done")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
|
||||
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_unauthorized_modal(cx: &mut ViewContext<Self>) -> impl Element {
|
||||
fn render_unauthorized_modal(cx: &mut Context<Self>) -> impl Element {
|
||||
v_flex()
|
||||
.child(Headline::new("You must have an active GitHub Copilot subscription.").size(HeadlineSize::Large))
|
||||
|
||||
|
@ -215,12 +215,12 @@ impl CopilotCodeVerification {
|
|||
.child(
|
||||
Button::new("copilot-subscribe-button", "Subscribe on GitHub")
|
||||
.full_width()
|
||||
.on_click(|_, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
|
||||
.on_click(|_, _, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
|
||||
)
|
||||
.child(
|
||||
Button::new("copilot-subscribe-cancel-button", "Cancel")
|
||||
.full_width()
|
||||
.on_click(cx.listener(|_, _, cx| cx.emit(DismissEvent))),
|
||||
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -232,7 +232,7 @@ impl CopilotCodeVerification {
|
|||
}
|
||||
|
||||
impl Render for CopilotCodeVerification {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let prompt = match &self.status {
|
||||
Status::SigningIn {
|
||||
prompt: Some(prompt),
|
||||
|
@ -260,11 +260,11 @@ impl Render for CopilotCodeVerification {
|
|||
.items_center()
|
||||
.p_4()
|
||||
.gap_2()
|
||||
.on_action(cx.listener(|_, _: &menu::Cancel, cx| {
|
||||
.on_action(cx.listener(|_, _: &menu::Cancel, _, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
}))
|
||||
.on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, cx| {
|
||||
cx.focus(&this.focus_handle);
|
||||
.on_any_mouse_down(cx.listener(|this, _: &MouseDownEvent, window, _| {
|
||||
window.focus(&this.focus_handle);
|
||||
}))
|
||||
.child(
|
||||
Vector::new(VectorName::ZedXCopilot, rems(8.), rems(4.))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue